From 10dc2c2ef0ea4f89f7c9cbe7b886ce6327196115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Mon, 4 Dec 2017 18:21:10 +0100 Subject: Add an rfc7231 test suite, fix an HTTP/2 bug with HEAD In some cases there could be a body sent as a response to a HEAD request when using HTTP/2. This has been corrected. --- src/cowboy_http2.erl | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'src/cowboy_http2.erl') diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl index 3aa0ad0..fdfef4a 100644 --- a/src/cowboy_http2.erl +++ b/src/cowboy_http2.erl @@ -37,6 +37,8 @@ id = undefined :: cowboy_stream:streamid(), %% Stream handlers and their state. state = undefined :: {module(), any()} | flush, + %% Request method. + method = undefined :: binary(), %% Whether we finished sending data. local = idle :: idle | upgrade | cowboy_stream:fin() | flush, %% Local flow control window (how much we can send). @@ -536,18 +538,19 @@ commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeSta %% @todo Keep IsFin in the state. %% @todo Same two things above apply to DATA, possibly promise too. commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeState0}, - Stream=#stream{id=StreamID, local=idle}, [{response, StatusCode, Headers0, Body}|Tail]) -> + Stream=#stream{id=StreamID, method=Method, local=idle}, + [{response, StatusCode, Headers0, Body}|Tail]) -> Headers = Headers0#{<<":status">> => status(StatusCode)}, {HeaderBlock, EncodeState} = headers_encode(Headers, EncodeState0), - case Body of - <<>> -> + if + Method =:= <<"HEAD">>; Body =:= <<>> -> Transport:send(Socket, cow_http2:headers(StreamID, fin, HeaderBlock)), commands(State#state{encode_state=EncodeState}, Stream#stream{local=fin}, Tail); - {sendfile, O, B, P} -> + element(1, Body) =:= sendfile -> Transport:send(Socket, cow_http2:headers(StreamID, nofin, HeaderBlock)), commands(State#state{encode_state=EncodeState}, Stream#stream{local=nofin}, - [{sendfile, fin, O, B, P}|Tail]); - _ -> + [erlang:insert_element(2, Body, fin)|Tail]); + true -> Transport:send(Socket, cow_http2:headers(StreamID, nofin, HeaderBlock)), {State1, Stream1} = send_data(State, Stream#stream{local=nofin}, fin, Body), commands(State1#state{encode_state=EncodeState}, Stream1, Tail) @@ -555,11 +558,16 @@ commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeSta %% @todo response when local!=idle %% Send response headers. commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeState0}, - Stream=#stream{id=StreamID, local=idle}, [{headers, StatusCode, Headers0}|Tail]) -> + Stream=#stream{id=StreamID, method=Method, local=idle}, + [{headers, StatusCode, Headers0}|Tail]) -> Headers = Headers0#{<<":status">> => status(StatusCode)}, {HeaderBlock, EncodeState} = headers_encode(Headers, EncodeState0), - Transport:send(Socket, cow_http2:headers(StreamID, nofin, HeaderBlock)), - commands(State#state{encode_state=EncodeState}, Stream#stream{local=nofin}, Tail); + IsFin = case Method of + <<"HEAD">> -> fin; + _ -> nofin + end, + Transport:send(Socket, cow_http2:headers(StreamID, IsFin, HeaderBlock)), + commands(State#state{encode_state=EncodeState}, Stream#stream{local=IsFin}, Tail); %% @todo headers when local!=idle %% Send a response body chunk. commands(State0, Stream0=#stream{local=nofin}, [{data, IsFin, Data}|Tail]) -> @@ -974,12 +982,13 @@ stream_malformed(State=#state{socket=Socket, transport=Transport}, StreamID, _) stream_handler_init(State=#state{opts=Opts, local_settings=#{initial_window_size := RemoteWindow}, remote_settings=#{initial_window_size := LocalWindow}}, - StreamID, RemoteIsFin, LocalIsFin, Req=#{headers := Headers}) -> + StreamID, RemoteIsFin, LocalIsFin, + Req=#{method := Method, headers := Headers}) -> try cowboy_stream:init(StreamID, Req, Opts) of {Commands, StreamState} -> commands(State#state{client_streamid=StreamID}, #stream{id=StreamID, state=StreamState, - remote=RemoteIsFin, local=LocalIsFin, + method=Method, remote=RemoteIsFin, local=LocalIsFin, local_window=LocalWindow, remote_window=RemoteWindow, te=maps:get(<<"te">>, Headers, undefined)}, Commands) -- cgit v1.2.3