diff --git a/lib/puma/request.rb b/lib/puma/request.rb index 66c9884cca..08b0354819 100644 --- a/lib/puma/request.rb +++ b/lib/puma/request.rb @@ -231,7 +231,11 @@ def fetch_status_code(status) # def normalize_env(env, client) if host = env[HTTP_HOST] - if colon = host.index(":") + # host can be a hostname, ipv4 or bracketed ipv6. Followed by an optional port. + if colon = host.rindex("]:") # IPV6 with port + env[SERVER_NAME] = host[0, colon+1] + env[SERVER_PORT] = host[colon+2, host.bytesize] + elsif !host.start_with?("[") && colon = host.index(":") # not hostname or IPV4 with port env[SERVER_NAME] = host[0, colon] env[SERVER_PORT] = host[colon+1, host.bytesize] else diff --git a/test/test_puma_server.rb b/test/test_puma_server.rb index 226e85167c..c64ab16099 100644 --- a/test/test_puma_server.rb +++ b/test/test_puma_server.rb @@ -53,6 +53,54 @@ def new_connection TCPSocket.new(@host, @port).tap {|sock| @ios << sock} end + def test_normalize_host_header_missing + server_run app: ->(env) do + [200, {}, [env["SERVER_NAME"], "\n", env["SERVER_PORT"]]] + end + + data = send_http_and_read "GET / HTTP/1.0\r\n\r\n" + assert_equal "localhost\n80", data.split("\r\n").last + end + + def test_normalize_host_header_hostname + server_run app: ->(env) do + [200, {}, [env["SERVER_NAME"], "\n", env["SERVER_PORT"]]] + end + + data = send_http_and_read "GET / HTTP/1.0\r\nHost: example.com:456\r\n\r\n" + assert_equal "example.com\n456", data.split("\r\n").last + + data = send_http_and_read "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n" + assert_equal "example.com\n80", data.split("\r\n").last + end + + def test_normalize_host_header_ipv4 + server_run app: ->(env) do + [200, {}, [env["SERVER_NAME"], "\n", env["SERVER_PORT"]]] + end + + data = send_http_and_read "GET / HTTP/1.0\r\nHost: 123.123.123.123:456\r\n\r\n" + assert_equal "123.123.123.123\n456", data.split("\r\n").last + + data = send_http_and_read "GET / HTTP/1.0\r\nHost: 123.123.123.123\r\n\r\n" + assert_equal "123.123.123.123\n80", data.split("\r\n").last + end + + def test_normalize_host_header_ipv6 + server_run app: ->(env) do + [200, {}, [env["SERVER_NAME"], "\n", env["SERVER_PORT"]]] + end + + data = send_http_and_read "GET / HTTP/1.0\r\nHost: [::ffff:127.0.0.1]:9292\r\n\r\n" + assert_equal "[::ffff:127.0.0.1]\n9292", data.split("\r\n").last + + data = send_http_and_read "GET / HTTP/1.0\r\nHost: [::1]:9292\r\n\r\n" + assert_equal "[::1]\n9292", data.split("\r\n").last + + data = send_http_and_read "GET / HTTP/1.0\r\nHost: [::1]\r\n\r\n" + assert_equal "[::1]\n80", data.split("\r\n").last + end + def test_proper_stringio_body data = nil