0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Add option linger_timeout to cowboy_http

This commit is contained in:
Loïc Hoguin 2018-05-16 16:01:30 +02:00
parent 827bd8c1c9
commit 0e629f4799
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
2 changed files with 48 additions and 1 deletions

View file

@ -21,6 +21,7 @@ opts() :: #{
env => cowboy_middleware:env(), env => cowboy_middleware:env(),
idle_timeout => timeout(), idle_timeout => timeout(),
inactivity_timeout => timeout(), inactivity_timeout => timeout(),
linger_timeout => timeout(),
max_empty_lines => non_neg_integer(), max_empty_lines => non_neg_integer(),
max_header_name_length => non_neg_integer(), max_header_name_length => non_neg_integer(),
max_header_value_length => non_neg_integer(), max_header_value_length => non_neg_integer(),
@ -59,6 +60,11 @@ idle_timeout (60000)::
inactivity_timeout (300000):: inactivity_timeout (300000)::
Time in ms with nothing received at all before Cowboy closes the connection. Time in ms with nothing received at all before Cowboy closes the connection.
linger_timeout (1000)::
Time in ms that Cowboy will wait when closing the connection. This is
necessary to avoid the TCP reset problem as described in the
https://tools.ietf.org/html/rfc7230#section-6.6[section 6.6 of RFC7230].
max_empty_lines (5):: max_empty_lines (5)::
Maximum number of empty lines before a request. Maximum number of empty lines before a request.
@ -98,6 +104,7 @@ stream_handlers ([cowboy_stream_h])::
== Changelog == Changelog
* *2.5*: The `linger_timeout` option was added.
* *2.2*: The `max_skip_body_length` option was added. * *2.2*: The `max_skip_body_length` option was added.
* *2.0*: The `timeout` option was renamed `request_timeout`. * *2.0*: The `timeout` option was renamed `request_timeout`.
* *2.0*: The `idle_timeout`, `inactivity_timeout` and `shutdown_timeout` options were added. * *2.0*: The `idle_timeout`, `inactivity_timeout` and `shutdown_timeout` options were added.

View file

@ -25,6 +25,7 @@
env => cowboy_middleware:env(), env => cowboy_middleware:env(),
idle_timeout => timeout(), idle_timeout => timeout(),
inactivity_timeout => timeout(), inactivity_timeout => timeout(),
linger_timeout => timeout(),
max_empty_lines => non_neg_integer(), max_empty_lines => non_neg_integer(),
max_header_name_length => non_neg_integer(), max_header_name_length => non_neg_integer(),
max_header_value_length => non_neg_integer(), max_header_value_length => non_neg_integer(),
@ -1236,9 +1237,10 @@ early_error(StatusCode0, #state{socket=Socket, transport=Transport,
-spec terminate(_, _) -> no_return(). -spec terminate(_, _) -> no_return().
terminate(undefined, Reason) -> terminate(undefined, Reason) ->
exit({shutdown, Reason}); exit({shutdown, Reason});
terminate(#state{streams=Streams, children=Children}, Reason) -> terminate(State=#state{streams=Streams, children=Children}, Reason) ->
terminate_all_streams(Streams, Reason), terminate_all_streams(Streams, Reason),
cowboy_children:terminate(Children), cowboy_children:terminate(Children),
terminate_linger(State),
exit({shutdown, Reason}). exit({shutdown, Reason}).
terminate_all_streams([], _) -> terminate_all_streams([], _) ->
@ -1247,6 +1249,44 @@ terminate_all_streams([#stream{id=StreamID, state=StreamState}|Tail], Reason) ->
stream_call_terminate(StreamID, Reason, StreamState), stream_call_terminate(StreamID, Reason, StreamState),
terminate_all_streams(Tail, Reason). terminate_all_streams(Tail, Reason).
terminate_linger(State=#state{socket=Socket, transport=Transport, opts=Opts}) ->
case Transport:shutdown(Socket, write) of
ok ->
case maps:get(linger_timeout, Opts, 1000) of
0 ->
ok;
infinity ->
terminate_linger_loop(State, undefined);
Timeout ->
TimerRef = erlang:start_timer(Timeout, self(), linger_timeout),
terminate_linger_loop(State, TimerRef)
end;
{error, _} ->
ok
end.
terminate_linger_loop(State=#state{socket=Socket, transport=Transport}, TimerRef) ->
{OK, Closed, Error} = Transport:messages(),
%% We may already have a message in the mailbox when we do this
%% but it's OK because we are shutting down anyway.
case Transport:setopts(Socket, [{active, once}]) of
ok ->
receive
{OK, Socket, _} ->
terminate_linger_loop(State, TimerRef);
{Closed, Socket} ->
ok;
{Error, Socket, _} ->
ok;
{timeout, TimerRef, linger_timeout} ->
ok;
_ ->
terminate_linger_loop(State, TimerRef)
end;
{error, _} ->
ok
end.
%% System callbacks. %% System callbacks.
-spec system_continue(_, _, {#state{}, binary()}) -> ok. -spec system_continue(_, _, {#state{}, binary()}) -> ok.