diff options
author | Loïc Hoguin <[email protected]> | 2013-01-29 14:35:26 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2013-01-29 14:35:26 +0100 |
commit | 638638a841f22eacbebd827c0fb40002358bf1ac (patch) | |
tree | 7e59e6efc641d4b4a91e4cf2654885e230eace55 | |
parent | 8050f2e0fab45cd623d2044c94350b6b1ea43c71 (diff) | |
download | cowboy-638638a841f22eacbebd827c0fb40002358bf1ac.tar.gz cowboy-638638a841f22eacbebd827c0fb40002358bf1ac.tar.bz2 cowboy-638638a841f22eacbebd827c0fb40002358bf1ac.zip |
Fix {cowboy_req, resp_sent} potentially leaking in loop handlers
-rw-r--r-- | src/cowboy_handler.erl | 21 | ||||
-rw-r--r-- | src/cowboy_websocket.erl | 1 |
2 files changed, 18 insertions, 4 deletions
diff --git a/src/cowboy_handler.erl b/src/cowboy_handler.erl index 4acb876..7ed7db3 100644 --- a/src/cowboy_handler.erl +++ b/src/cowboy_handler.erl @@ -29,7 +29,8 @@ env :: cowboy_middleware:env(), hibernate = false :: boolean(), loop_timeout = infinity :: timeout(), - loop_timeout_ref :: undefined | reference() + loop_timeout_ref :: undefined | reference(), + resp_sent = false :: boolean() }). %% @private @@ -80,7 +81,7 @@ handler_init(Req, State, Handler, HandlerOpts) -> "** Stacktrace: ~p~n~n", [Handler, init, 3, Class, Reason, HandlerOpts, cowboy_req:to_list(Req), erlang:get_stacktrace()]), - {error, 500, Req} + error_terminate(Req, State) end. -spec upgrade_protocol(Req, #state{}, module(), any(), module()) @@ -112,7 +113,7 @@ handler_handle(Req, State, Handler, HandlerState) -> [Handler, handle, 2, Class, Reason, HandlerState, cowboy_req:to_list(Req), erlang:get_stacktrace()]), handler_terminate(Req, Handler, HandlerState, Reason), - {error, 500, Req} + error_terminate(Req, State) end. %% We don't listen for Transport closes because that would force us @@ -147,6 +148,9 @@ handler_loop_timeout(State=#state{loop_timeout=Timeout, when Req::cowboy_req:req(). handler_loop(Req, State=#state{loop_timeout_ref=TRef}, Handler, HandlerState) -> receive + {cowboy_req, resp_sent} -> + handler_loop(Req, State#state{resp_sent=true}, + Handler, HandlerState); {timeout, TRef, ?MODULE} -> terminate_request(Req, State, Handler, HandlerState, {normal, timeout}); @@ -180,7 +184,7 @@ handler_call(Req, State, Handler, HandlerState, Message) -> [Handler, info, 3, Class, Reason, HandlerState, cowboy_req:to_list(Req), erlang:get_stacktrace()]), handler_terminate(Req, Handler, HandlerState, Reason), - {error, 500, Req} + error_terminate(Req, State) end. -spec terminate_request(Req, #state{}, module(), any(), @@ -205,3 +209,12 @@ handler_terminate(Req, Handler, HandlerState, Reason) -> [Handler, terminate, 3, Class, Reason2, HandlerState, cowboy_req:to_list(Req), erlang:get_stacktrace()]) end. + +%% Only send an error reply if there is no resp_sent message. +-spec error_terminate(Req, #state{}) + -> {error, 500, Req} | {halt, Req} when Req::cowboy_req:req(). +error_terminate(Req, #state{resp_sent=true}) -> + %% Close the connection, but do not attempt sending a reply. + {halt, cowboy_req:set([{connection, close}, {resp_state, done}], Req)}; +error_terminate(Req, _) -> + {error, 500, Req}. diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl index 1b2ad3b..debb69f 100644 --- a/src/cowboy_websocket.erl +++ b/src/cowboy_websocket.erl @@ -121,6 +121,7 @@ handler_init(State=#state{env=Env, transport=Transport, upgrade_error(Req, Env) end. +%% Only send an error reply if there is no resp_sent message. -spec upgrade_error(Req, Env) -> {ok, Req, Env} | {error, 400, Req} when Req::cowboy_req:req(), Env::cowboy_middleware:env(). upgrade_error(Req, Env) -> |