aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_http.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/cowboy_http.erl')
-rw-r--r--src/cowboy_http.erl42
1 files changed, 41 insertions, 1 deletions
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl
index 440aadd..5890037 100644
--- a/src/cowboy_http.erl
+++ b/src/cowboy_http.erl
@@ -25,6 +25,7 @@
env => cowboy_middleware:env(),
idle_timeout => timeout(),
inactivity_timeout => timeout(),
+ linger_timeout => timeout(),
max_empty_lines => non_neg_integer(),
max_header_name_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().
terminate(undefined, Reason) ->
exit({shutdown, Reason});
-terminate(#state{streams=Streams, children=Children}, Reason) ->
+terminate(State=#state{streams=Streams, children=Children}, Reason) ->
terminate_all_streams(Streams, Reason),
cowboy_children:terminate(Children),
+ terminate_linger(State),
exit({shutdown, Reason}).
terminate_all_streams([], _) ->
@@ -1247,6 +1249,44 @@ terminate_all_streams([#stream{id=StreamID, state=StreamState}|Tail], Reason) ->
stream_call_terminate(StreamID, Reason, StreamState),
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.
-spec system_continue(_, _, {#state{}, binary()}) -> ok.