diff --git a/spec/std/http/client/client_spec.cr b/spec/std/http/client/client_spec.cr index cd7631c305ef..56e3ff1509a8 100644 --- a/spec/std/http/client/client_spec.cr +++ b/spec/std/http/client/client_spec.cr @@ -121,6 +121,16 @@ module HTTP end end + it "sends the host header ipv6 with brackets" do + server = HTTP::Server.new do |context| + context.response.print context.request.headers["Host"] + end + address = server.bind_unused_port "::1" + spawn { server.listen } + + HTTP::Client.get("http://[::1]:#{address.port}/").body.should eq("[::1]:#{address.port}") + end + it "doesn't read the body if request was HEAD" do resp_get = TestServer.open("localhost", 0, 0) do |server| client = Client.new("localhost", server.local_address.port) diff --git a/spec/std/uri_spec.cr b/spec/std/uri_spec.cr index 7d34065911d3..dbbb6fb3dbf6 100644 --- a/spec/std/uri_spec.cr +++ b/spec/std/uri_spec.cr @@ -19,6 +19,7 @@ end describe "URI" do assert_uri("http://www.example.com", scheme: "http", host: "www.example.com") assert_uri("http://www.example.com:81", scheme: "http", host: "www.example.com", port: 81) + assert_uri("http://[::1]:81", scheme: "http", host: "[::1]", port: 81) assert_uri("http://www.example.com/foo", scheme: "http", host: "www.example.com", path: "/foo") assert_uri("http://www.example.com/foo?q=1", scheme: "http", host: "www.example.com", path: "/foo", query: "q=1") assert_uri("http://www.example.com?q=1", scheme: "http", host: "www.example.com", query: "q=1") @@ -32,6 +33,12 @@ describe "URI" do assert_uri("/foo?q=1", path: "/foo", query: "q=1") assert_uri("mailto:foo@example.org", scheme: "mailto", path: nil, opaque: "foo@example.org") + describe "hostname" do + it { URI.parse("http://www.example.com/foo").hostname.should eq("www.example.com") } + it { URI.parse("http://[::1]/foo").hostname.should eq("::1") } + it { URI.parse("/foo").hostname.should be_nil } + end + describe "full_path" do it { URI.parse("http://www.example.com/foo").full_path.should eq("/foo") } it { URI.parse("http://www.example.com").full_path.should eq("/") } diff --git a/src/http/client.cr b/src/http/client.cr index 3fa83b18565e..a3aeb1de977e 100644 --- a/src/http/client.cr +++ b/src/http/client.cr @@ -651,7 +651,8 @@ class HTTP::Client socket = @socket return socket if socket - socket = TCPSocket.new @host, @port, @dns_timeout, @connect_timeout + hostname = @host.starts_with?('[') && @host.ends_with?(']') ? @host[1..-2] : @host + socket = TCPSocket.new hostname, @port, @dns_timeout, @connect_timeout socket.read_timeout = @read_timeout if @read_timeout socket.sync = false @socket = socket diff --git a/src/uri.cr b/src/uri.cr index c6992e993d80..47ef85aba1b2 100644 --- a/src/uri.cr +++ b/src/uri.cr @@ -119,6 +119,16 @@ class URI def initialize(@scheme = nil, @host = nil, @port = nil, @path = nil, @query = nil, @user = nil, @password = nil, @fragment = nil, @opaque = nil) end + # Returns the host part of the URI and unwrap brackets for IPv6 addresses. + # + # ``` + # URI.parse("http://[::1]/bar").hostname # => "::1" + # URI.parse("http://[::1]/bar").host # => "[::1]" + # ``` + def hostname + host.try { |host| host.starts_with?('[') && host.ends_with?(']') ? host[1..-2] : host } + end + # Returns the full path of this URI. # # ```