diff options
author | Loïc Hoguin <[email protected]> | 2020-04-06 14:50:35 +0200 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2020-04-06 14:50:35 +0200 |
commit | 775091134dc49633595cfc5956612df0c5702c0f (patch) | |
tree | b0a7ff222d2644526dda38c57b58cc2319946a8f /src | |
parent | 5203ee6a847066be0a71cef0a7795f5617434ba3 (diff) | |
download | cowboy-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.erl | 46 |
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) -> |