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

Experiment with a linger_timeout for HTTP/2

This is mostly to ensure that the GOAWAY frame is properly
received on Windows in some tests, but should be benefitial
also in production in particular when clients are slower.
This commit is contained in:
Loïc Hoguin 2020-04-06 14:50:35 +02:00
parent 5203ee6a84
commit 775091134d
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764

View file

@ -35,6 +35,7 @@
inactivity_timeout => timeout(),
initial_connection_window_size => 65535..16#7fffffff,
initial_stream_window_size => 0..16#7fffffff,
linger_timeout => timeout(),
logger => module(),
max_concurrent_streams => non_neg_integer() | infinity,
max_connection_buffer_size => non_neg_integer(),
@ -956,7 +957,7 @@ terminate(State=#state{socket=Socket, transport=Transport, http2_status=Status,
end,
terminate_all_streams(State, maps:to_list(Streams), Reason),
cowboy_children:terminate(Children),
Transport:close(Socket),
terminate_linger(State),
exit({shutdown, Reason});
terminate(#state{socket=Socket, transport=Transport}, Reason) ->
Transport:close(Socket),
@ -973,6 +974,49 @@ terminate_all_streams(State, [{StreamID, #stream{state=StreamState}}|Tail], Reas
terminate_stream_handler(State, StreamID, Reason, StreamState),
terminate_all_streams(State, Tail, Reason).
%% This code is copied from cowboy_http.
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_before_loop(State, undefined, Transport:messages());
Timeout ->
TimerRef = erlang:start_timer(Timeout, self(), linger_timeout),
terminate_linger_before_loop(State, TimerRef, Transport:messages())
end;
{error, _} ->
ok
end.
terminate_linger_before_loop(State, TimerRef, Messages) ->
%% We may already be in active mode when we do this
%% but it's OK because we are shutting down anyway.
case setopts_active(State) of
ok ->
terminate_linger_loop(State, TimerRef, Messages);
{error, _} ->
ok
end.
terminate_linger_loop(State=#state{socket=Socket}, TimerRef, Messages) ->
receive
{OK, Socket, _} when OK =:= element(1, Messages) ->
terminate_linger_loop(State, TimerRef, Messages);
{Closed, Socket} when Closed =:= element(2, Messages) ->
ok;
{Error, Socket, _} when Error =:= element(3, Messages) ->
ok;
{Passive, Socket} when Passive =:= tcp_passive; Passive =:= ssl_passive ->
terminate_linger_before_loop(State, TimerRef, Messages);
{timeout, TimerRef, linger_timeout} ->
ok;
_ ->
terminate_linger_loop(State, TimerRef, Messages)
end.
%% @todo Don't send an RST_STREAM if one was already sent.
reset_stream(State0=#state{socket=Socket, transport=Transport,
http2_machine=HTTP2Machine0}, StreamID, Error) ->