Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve parsing of HTTP_HOST header #2605

Merged
merged 4 commits into from Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/puma/request.rb
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good comments 馃憤

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
Expand Down
48 changes: 48 additions & 0 deletions test/test_puma_server.rb
Expand Up @@ -53,6 +53,54 @@ def new_connection
TCPSocket.new(@host, @port).tap {|sock| @ios << sock}
end

def test_normalize_host_header_missing
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is probably a smarter way to check those headers?

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

Expand Down