Browse Source

Fallback to host header if authority is missing

Fredrik Enestad 6 years ago
parent
commit
7ff9e963b8
2 changed files with 38 additions and 4 deletions
  1. 14 3
      src/cowboy_http2.erl
  2. 24 1
      test/rfc7540_SUITE.erl

+ 14 - 3
src/cowboy_http2.erl

@@ -315,9 +315,20 @@ headers_frame(State, StreamID, IsFin, Headers,
 		PseudoHeaders=#{method := <<"TRACE">>}, _) ->
 	early_error(State, StreamID, IsFin, Headers, PseudoHeaders, 501,
 		'The TRACE method is currently not implemented. (RFC7231 4.3.8)');
-headers_frame(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert, proxy_header=ProxyHeader},
-		StreamID, IsFin, Headers, PseudoHeaders=#{method := Method, scheme := Scheme,
-			authority := Authority, path := PathWithQs}, BodyLen) ->
+headers_frame(State, StreamID, IsFin, Headers, PseudoHeaders=#{authority := Authority}, BodyLen) ->
+	headers_frame_parse_host(State, StreamID, IsFin, Headers, PseudoHeaders, BodyLen, Authority);
+headers_frame(State, StreamID, IsFin, Headers, PseudoHeaders, BodyLen) ->
+	case lists:keyfind(<<"host">>, 1, Headers) of
+		{_, Authority} ->
+			headers_frame_parse_host(State, StreamID, IsFin, Headers, PseudoHeaders, BodyLen, Authority);
+		_ ->
+			reset_stream(State, StreamID, {stream_error, protocol_error,
+				'Requests translated from HTTP/1.1 must include a host header. (RFC7540 8.1.2.3, RFC7230 5.4)'})
+	end.
+
+headers_frame_parse_host(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert, proxy_header=ProxyHeader},
+		StreamID, IsFin, Headers, PseudoHeaders=#{method := Method, scheme := Scheme, path := PathWithQs},
+		BodyLen, Authority) ->
 	try cow_http_hd:parse_host(Authority) of
 		{Host, Port0} ->
 			Port = ensure_port(Scheme, Port0),

+ 24 - 1
test/rfc7540_SUITE.erl

@@ -3746,7 +3746,7 @@ reject_many_pseudo_header_scheme(Config) ->
 	ok.
 
 reject_missing_pseudo_header_authority(Config) ->
-	doc("A request without an authority component must be rejected "
+	doc("A request without an authority or host component must be rejected "
 		"with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),
 	{ok, Socket} = do_handshake(Config),
 	%% Send a HEADERS frame without an :authority pseudo-header.
@@ -3760,6 +3760,29 @@ reject_missing_pseudo_header_authority(Config) ->
 	{ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
 	ok.
 
+accept_host_header_on_missing_pseudo_header_authority(Config) ->
+	doc("A request without an authority but with a host header must be accepted. "
+		"(RFC7540 8.1.2.3, RFC7540 8.1.3)"),
+	{ok, Socket} = do_handshake(Config),
+	%% Send a HEADERS frame with host header and without an :authority pseudo-header.
+	{HeadersBlock, _} = cow_hpack:encode([
+		{<<":method">>, <<"GET">>},
+		{<<":scheme">>, <<"http">>},
+		{<<":path">>, <<"/">>},
+		{<<"host">>, <<"localhost">>}
+	]),
+	ok = gen_tcp:send(Socket, cow_http2:headers(1, fin, HeadersBlock)),
+	%% Receive a 200 response.
+	{ok, << Len:24, 1:8, _:8, _:32 >>} = gen_tcp:recv(Socket, 9, 6000),
+	{ok, RespHeadersBlock} = gen_tcp:recv(Socket, Len, 6000),
+	{RespHeaders, _} = cow_hpack:decode(RespHeadersBlock),
+	{_, <<"200">>} = lists:keyfind(<<":status">>, 1, RespHeaders),
+	ok.
+
+%% When both :authority and host headers are received, the current behavior
+%% is to favor :authority and ignore the host header. The specification does
+%% not describe the correct behavior to follow in that case.
+
 reject_many_pseudo_header_authority(Config) ->
 	doc("A request containing more than one authority component must be rejected "
 		"with a PROTOCOL_ERROR stream error. (RFC7540 8.1.2.3, RFC7540 8.1.2.6)"),