diff --git a/lib/protocol/rack/adapter/generic.rb b/lib/protocol/rack/adapter/generic.rb index 5334972..dda95a4 100644 --- a/lib/protocol/rack/adapter/generic.rb +++ b/lib/protocol/rack/adapter/generic.rb @@ -117,8 +117,11 @@ def unwrap_request(request, env) env[RACK_HIJACK] = proc{request.hijack!.io} end - # HTTP/2 prefers `:authority` over `host`, so we do this for backwards compatibility. - env[CGI::HTTP_HOST] ||= request.authority + # HTTP/2 prefers `:authority` over `host`: + if authority = request.authority + # Note that we also already have SERVER_NAME and SERVER_PORT which are based on the authority. + env[CGI::HTTP_HOST] = authority + end if peer = request.peer env[CGI::REMOTE_ADDR] = peer.ip_address diff --git a/test/protocol/rack/request.rb b/test/protocol/rack/request.rb index 9795293..8244bb4 100644 --- a/test/protocol/rack/request.rb +++ b/test/protocol/rack/request.rb @@ -59,4 +59,25 @@ ) end end + + with "incoming request with both host header and authority" do + let(:headers) {Protocol::HTTP::Headers[{"host" => "header.example.com"}]} + let(:request) {Protocol::HTTP::Request.new("https", "authority.example.com", "GET", "/", "HTTP/1.1", headers, body)} + + it "correctly sets HTTP_HOST to the authority instead of host header" do + # According to HTTP/2 semantics, :authority should take precedence over the host header when both are present: + expect(env).to have_keys( + "HTTP_HOST" => be == "authority.example.com" + ) + end + end + + with "incoming request with trailing host header" do + let(:headers) {Protocol::HTTP::Headers.new([["host", "header.example.com"]], 0)} + let(:request) {Protocol::HTTP::Request.new("https", nil, "GET", "/", "HTTP/1.1", headers, body)} + + it "rejects the request" do + expect(env).not.to have_keys("HTTP_HOST") + end + end end