From 8fc3da2fc31a04281b0ba8dc0cdc1e1650067da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Fri, 7 Feb 2020 11:32:15 +0100 Subject: Fix HTTP/1.1 bug when a flow command is returned after fin This resulted in a badarith error due to the current flow being set to infinity when the body has been fully read. A test case has been added reproducing the issue. --- src/cowboy_http.erl | 3 +++ test/handlers/stream_handler_h.erl | 4 ++++ test/stream_handler_SUITE.erl | 13 +++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index 9f0b865..13d07b7 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -970,6 +970,9 @@ commands(State=#state{out_streamid=Current, streams=Streams0}, StreamID, Command Streams = lists:keyreplace(StreamID, #stream.id, Streams0, Stream#stream{queue=Queue ++ Commands}), State#state{streams=Streams}; +%% When we have finished reading the request body, do nothing. +commands(State=#state{flow=infinity}, StreamID, [{flow, _}|Tail]) -> + commands(State, StreamID, Tail); %% Read the request body. commands(State0=#state{flow=Flow0}, StreamID, [{flow, Size}|Tail]) -> %% We must read *at least* Size of data otherwise functions diff --git a/test/handlers/stream_handler_h.erl b/test/handlers/stream_handler_h.erl index 3761e11..370d15a 100644 --- a/test/handlers/stream_handler_h.erl +++ b/test/handlers/stream_handler_h.erl @@ -34,6 +34,8 @@ init_commands(_, _, #state{test=crash_in_terminate}) -> [{response, 200, #{<<"content-length">> => <<"12">>}, <<"Hello world!">>}, stop]; init_commands(_, _, #state{test=crash_in_early_error}) -> error(crash); +init_commands(_, _, #state{test=flow_after_body_fully_read}) -> + []; init_commands(_, _, #state{test=set_options_ignore_unknown}) -> [ {set_options, #{unknown_options => true}}, @@ -81,6 +83,8 @@ init_process(TrapExit, #state{pid=Pid}) -> data(_, _, _, #state{test=crash_in_data}) -> error(crash); +data(_, fin, <<"Hello world!">>, State=#state{test=flow_after_body_fully_read}) -> + {[{flow, 12}, {response, 200, #{}, <<"{}">>}], State}; data(StreamID, IsFin, Data, State=#state{pid=Pid}) -> Pid ! {Pid, self(), data, StreamID, IsFin, Data, State}, {[], State}. diff --git a/test/stream_handler_SUITE.erl b/test/stream_handler_SUITE.erl index 1d5bd27..46a05b2 100644 --- a/test/stream_handler_SUITE.erl +++ b/test/stream_handler_SUITE.erl @@ -282,6 +282,19 @@ early_error_stream_error_reason(Config) -> {response, fin, Status, _} = gun:await(ConnPid, Ref), ok. +flow_after_body_fully_read(Config) -> + doc("A flow command may be returned even after the body was read fully."), + Self = self(), + ConnPid = gun_open(Config), + Ref = gun:post(ConnPid, "/long_polling", [ + {<<"x-test-case">>, <<"flow_after_body_fully_read">>}, + {<<"x-test-pid">>, pid_to_list(Self)} + ], <<"Hello world!">>), + %% Receive a 200 response, sent after the second flow command, + %% confirming that the flow command was accepted. + {response, _, 200, _} = gun:await(ConnPid, Ref), + ok. + set_options_ignore_unknown(Config) -> doc("Confirm that unknown options are ignored when using the set_options commands."), Self = self(), -- cgit v1.2.3