Skip to content

Commit

Permalink
Fix request_timeout triggering when a request was in the buffer
Browse files Browse the repository at this point in the history
The problem was that when a request immediately following another
request with a large enough body, the data for the new request
(including headers) would be buffered waiting for more data,
instead of being processed immediately. If no more data came
in on the socket the request_timeout would eventually trigger.
Now the buffer is processed immediately.
  • Loading branch information
essen committed Feb 6, 2025
1 parent 9d49122 commit 8e121d1
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/cowboy_http.erl
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,8 @@ after_parse({data, StreamID, IsFin, Data, State0=#state{opts=Opts, buffer=Buffer
end;
%% No corresponding stream. We must skip the body of the previous request
%% in order to process the next one.
after_parse({data, _, IsFin, _, State}) ->
loop(set_timeout(State, case IsFin of
after_parse({data, StreamID, IsFin, _, State=#state{buffer=Buffer}}) ->
parse(Buffer, set_timeout(State, case IsFin of
fin -> request_timeout;
nofin -> idle_timeout
end));
Expand Down
21 changes: 21 additions & 0 deletions test/http_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ end_per_group(Name, _) ->
init_dispatch(_) ->
cowboy_router:compile([{"localhost", [
{"/", hello_h, []},
{"/delay_hello", delay_hello_h, #{delay => 1000, notify_received => self()}},
{"/echo/:key", echo_h, []},
{"/resp/:key[/:arg]", resp_h, []},
{"/set_options/:key", set_options_h, []},
Expand Down Expand Up @@ -454,6 +455,26 @@ request_timeout_pipeline(Config) ->
cowboy:stop_listener(?FUNCTION_NAME)
end.

request_timeout_pipeline_delay(Config) ->
doc("Ensure the request_timeout does not trigger on requests "
"coming in after a large request body."),
{ok, _} = cowboy:start_clear(?FUNCTION_NAME, [{port, 0}], #{
env => #{dispatch => init_dispatch(Config)},
request_timeout => 500
}),
Port = ranch:get_port(?FUNCTION_NAME),
try
ConnPid = gun_open([{type, tcp}, {protocol, http}, {port, Port}|Config]),
{ok, http} = gun:await_up(ConnPid),
StreamRef1 = gun:post(ConnPid, "/", #{}, <<0:8000000>>),
StreamRef2 = gun:get(ConnPid, "/delay_hello"),
{response, nofin, 200, _} = gun:await(ConnPid, StreamRef1),
{response, nofin, 200, _} = gun:await(ConnPid, StreamRef2),
{error, {down, {shutdown, closed}}} = gun:await(ConnPid, undefined, 1000)
after
cowboy:stop_listener(?FUNCTION_NAME)
end.

request_timeout_skip_body(Config) ->
doc("Ensure the request_timeout drops connections when requests "
"fail to come in fast enough after skipping a request body."),
Expand Down

0 comments on commit 8e121d1

Please sign in to comment.