aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_http2.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-12-04 18:21:10 +0100
committerLoïc Hoguin <[email protected]>2017-12-04 18:21:10 +0100
commit10dc2c2ef0ea4f89f7c9cbe7b886ce6327196115 (patch)
treeb6931d34d70b53342fd35b13bbcbc44a83335c5a /src/cowboy_http2.erl
parent2185198deee93ce3c6f92467ccf0c8ab50c8f356 (diff)
downloadcowboy-10dc2c2ef0ea4f89f7c9cbe7b886ce6327196115.tar.gz
cowboy-10dc2c2ef0ea4f89f7c9cbe7b886ce6327196115.tar.bz2
cowboy-10dc2c2ef0ea4f89f7c9cbe7b886ce6327196115.zip
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.
Diffstat (limited to 'src/cowboy_http2.erl')
-rw-r--r--src/cowboy_http2.erl31
1 files changed, 20 insertions, 11 deletions
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)