aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2020-04-06 14:50:35 +0200
committerLoïc Hoguin <[email protected]>2020-04-06 14:50:35 +0200
commit775091134dc49633595cfc5956612df0c5702c0f (patch)
treeb0a7ff222d2644526dda38c57b58cc2319946a8f /src
parent5203ee6a847066be0a71cef0a7795f5617434ba3 (diff)
downloadcowboy-775091134dc49633595cfc5956612df0c5702c0f.tar.gz
cowboy-775091134dc49633595cfc5956612df0c5702c0f.tar.bz2
cowboy-775091134dc49633595cfc5956612df0c5702c0f.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/cowboy_http2.erl46
1 files changed, 45 insertions, 1 deletions
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl
index 5224737..8dc8c3b 100644
--- a/src/cowboy_http2.erl
+++ b/src/cowboy_http2.erl
@@ -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) ->