From 1c474af8ee4c61c9cbbf6ef4e121d1d82af75151 Mon Sep 17 00:00:00 2001 From: James Fish Date: Mon, 18 Nov 2013 20:32:47 +0000 Subject: Fix loop handler keepalive race condition Previously if a loop handler received the timeout message from a previous request on the same connection the socket would be set to {active, once} incorrectly - when a socket packet was already in the message queue. This second packet would not be added to the buffer before a Handler:info/3 call if a user message was in the message queue before both socket packets. --- src/cowboy_handler.erl | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cowboy_handler.erl b/src/cowboy_handler.erl index fcbfe55..e431ba8 100644 --- a/src/cowboy_handler.erl +++ b/src/cowboy_handler.erl @@ -211,7 +211,7 @@ handler_loop(Req, State=#state{loop_buffer_size=NbBytes, handler_after_loop(Req, State, Handler, HandlerState, {normal, timeout}); {timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) -> - handler_before_loop(Req, State, Handler, HandlerState); + handler_loop(Req, State, Handler, HandlerState); Message -> %% We set the socket back to {active, false} mode in case %% the handler is going to call recv. We also flush any @@ -280,8 +280,14 @@ handler_after_loop(Req, State, Handler, HandlerState, Reason) -> -spec terminate_request(Req, #state{}, module(), any(), {normal, timeout | shutdown} | {error, atom()}) -> {ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req(). -terminate_request(Req, #state{env=Env}, Handler, HandlerState, Reason) -> +terminate_request(Req, #state{env=Env, loop_timeout_ref=TRef}, + Handler, HandlerState, Reason) -> HandlerRes = handler_terminate(Req, Handler, HandlerState, Reason), + _ = case TRef of + undefined -> ignore; + TRef -> erlang:cancel_timer(TRef) + end, + flush_timeouts(), {ok, Req, [{result, HandlerRes}|Env]}. -spec handler_terminate(cowboy_req:req(), module(), any(), @@ -299,3 +305,12 @@ handler_terminate(Req, Handler, HandlerState, Reason) -> {terminate_reason, Reason} ]) end. + +-spec flush_timeouts() -> ok. +flush_timeouts() -> + receive + {timeout, TRef, ?MODULE} when is_reference(TRef) -> + flush_timeouts() + after 0 -> + ok + end. -- cgit v1.2.3