mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Let the stream handler take care of crash handling and logging
This commit is contained in:
parent
e30d120bd8
commit
9966df9ad4
5 changed files with 22 additions and 57 deletions
|
@ -36,7 +36,7 @@
|
||||||
-spec execute(Req, Env) -> {ok, Req, Env}
|
-spec execute(Req, Env) -> {ok, Req, Env}
|
||||||
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
when Req::cowboy_req:req(), Env::cowboy_middleware:env().
|
||||||
execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) ->
|
execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) ->
|
||||||
case Handler:init(Req, HandlerOpts) of
|
try Handler:init(Req, HandlerOpts) of
|
||||||
{ok, Req2, State} ->
|
{ok, Req2, State} ->
|
||||||
Result = terminate(normal, Req2, State, Handler),
|
Result = terminate(normal, Req2, State, Handler),
|
||||||
{ok, Req2, [{result, Result}|Env]};
|
{ok, Req2, [{result, Result}|Env]};
|
||||||
|
@ -48,6 +48,9 @@ execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) ->
|
||||||
Mod:upgrade(Req2, Env, Handler, State, Timeout, run);
|
Mod:upgrade(Req2, Env, Handler, State, Timeout, run);
|
||||||
{Mod, Req2, State, Timeout, hibernate} ->
|
{Mod, Req2, State, Timeout, hibernate} ->
|
||||||
Mod:upgrade(Req2, Env, Handler, State, Timeout, hibernate)
|
Mod:upgrade(Req2, Env, Handler, State, Timeout, hibernate)
|
||||||
|
catch Class:Reason ->
|
||||||
|
terminate({crash, Class, Reason}, Req, HandlerOpts, Handler),
|
||||||
|
erlang:raise(Class, Reason, erlang:get_stacktrace())
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec terminate(any(), Req, any(), module()) -> ok when Req::cowboy_req:req().
|
-spec terminate(any(), Req, any(), module()) -> ok when Req::cowboy_req:req().
|
||||||
|
|
|
@ -115,16 +115,8 @@ call(Req, State, Handler, HandlerState, Message) ->
|
||||||
{stop, Req2, HandlerState2} ->
|
{stop, Req2, HandlerState2} ->
|
||||||
terminate(Req2, State, Handler, HandlerState2, stop)
|
terminate(Req2, State, Handler, HandlerState2, stop)
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
Stacktrace = erlang:get_stacktrace(),
|
|
||||||
cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler),
|
cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler),
|
||||||
exit({cowboy_handler, [
|
erlang:raise(Class, Reason, erlang:get_stacktrace())
|
||||||
{class, Class},
|
|
||||||
{reason, Reason},
|
|
||||||
{mfa, {Handler, info, 3}},
|
|
||||||
{stacktrace, Stacktrace},
|
|
||||||
{req, Req},
|
|
||||||
{state, HandlerState}
|
|
||||||
]})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
terminate(Req, #state{env=Env, timeout_ref=TRef},
|
terminate(Req, #state{env=Env, timeout_ref=TRef},
|
||||||
|
|
|
@ -654,7 +654,7 @@ variances(Req, State=#state{content_types_p=CTP,
|
||||||
resource_exists(Req3, State2)
|
resource_exists(Req3, State2)
|
||||||
end
|
end
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, variances)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
variances(Req, State, Variances) ->
|
variances(Req, State, Variances) ->
|
||||||
|
@ -693,7 +693,7 @@ if_match(Req, State, EtagsList) ->
|
||||||
false -> precondition_failed(Req2, State2)
|
false -> precondition_failed(Req2, State2)
|
||||||
end
|
end
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, generate_etag)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
if_match_must_not_exist(Req, State) ->
|
if_match_must_not_exist(Req, State) ->
|
||||||
|
@ -721,7 +721,7 @@ if_unmodified_since(Req, State, IfUnmodifiedSince) ->
|
||||||
false -> if_none_match_exists(Req2, State2)
|
false -> if_none_match_exists(Req2, State2)
|
||||||
end
|
end
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, last_modified)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
if_none_match_exists(Req, State) ->
|
if_none_match_exists(Req, State) ->
|
||||||
|
@ -747,7 +747,7 @@ if_none_match(Req, State, EtagsList) ->
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, generate_etag)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Weak Etag comparison: only check the opaque tag.
|
%% Weak Etag comparison: only check the opaque tag.
|
||||||
|
@ -790,7 +790,7 @@ if_modified_since(Req, State, IfModifiedSince) ->
|
||||||
false -> not_modified(Req2, State2)
|
false -> not_modified(Req2, State2)
|
||||||
end
|
end
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, last_modified)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
not_modified(Req, State) ->
|
not_modified(Req, State) ->
|
||||||
|
@ -801,10 +801,10 @@ not_modified(Req, State) ->
|
||||||
{Req4, State3} ->
|
{Req4, State3} ->
|
||||||
respond(Req4, State3, 304)
|
respond(Req4, State3, 304)
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State2, Class, Reason, expires)
|
error_terminate(Req, State2, Class, Reason)
|
||||||
end
|
end
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, generate_etag)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
precondition_failed(Req, State) ->
|
precondition_failed(Req, State) ->
|
||||||
|
@ -951,7 +951,7 @@ process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) ->
|
||||||
true -> respond(Req3, State2, 201)
|
true -> respond(Req3, State2, 201)
|
||||||
end
|
end
|
||||||
end catch Class:Reason = {case_clause, no_call} ->
|
end catch Class:Reason = {case_clause, no_call} ->
|
||||||
error_terminate(Req, State, Class, Reason, Fun)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% If PUT was used then the resource has been created at the current URL.
|
%% If PUT was used then the resource has been created at the current URL.
|
||||||
|
@ -978,7 +978,7 @@ set_resp_body_etag(Req, State) ->
|
||||||
{Req2, State2} ->
|
{Req2, State2} ->
|
||||||
set_resp_body_last_modified(Req2, State2)
|
set_resp_body_last_modified(Req2, State2)
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, generate_etag)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Set the Last-Modified header if any for the response provided.
|
%% Set the Last-Modified header if any for the response provided.
|
||||||
|
@ -995,7 +995,7 @@ set_resp_body_last_modified(Req, State) ->
|
||||||
set_resp_body_expires(Req3, State2)
|
set_resp_body_expires(Req3, State2)
|
||||||
end
|
end
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, last_modified)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Set the Expires header if any for the response provided.
|
%% Set the Expires header if any for the response provided.
|
||||||
|
@ -1004,7 +1004,7 @@ set_resp_body_expires(Req, State) ->
|
||||||
{Req2, State2} ->
|
{Req2, State2} ->
|
||||||
set_resp_body(Req2, State2)
|
set_resp_body(Req2, State2)
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, expires)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Set the response headers and call the callback found using
|
%% Set the response headers and call the callback found using
|
||||||
|
@ -1028,7 +1028,7 @@ set_resp_body(Req, State=#state{content_type_a={_, Callback}}) ->
|
||||||
end,
|
end,
|
||||||
multiple_choices(Req3, State2)
|
multiple_choices(Req3, State2)
|
||||||
end catch Class:Reason = {case_clause, no_call} ->
|
end catch Class:Reason = {case_clause, no_call} ->
|
||||||
error_terminate(Req, State, Class, Reason, Callback)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
multiple_choices(Req, State) ->
|
multiple_choices(Req, State) ->
|
||||||
|
@ -1131,7 +1131,7 @@ call(Req, State=#state{handler=Handler, handler_state=HandlerState},
|
||||||
try
|
try
|
||||||
Handler:Callback(Req, HandlerState)
|
Handler:Callback(Req, HandlerState)
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
error_terminate(Req, State, Class, Reason, Callback)
|
error_terminate(Req, State, Class, Reason)
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
no_call
|
no_call
|
||||||
|
@ -1152,20 +1152,10 @@ next(Req, State, StatusCode) when is_integer(StatusCode) ->
|
||||||
respond(Req, State, StatusCode) ->
|
respond(Req, State, StatusCode) ->
|
||||||
terminate(cowboy_req:reply(StatusCode, Req), State).
|
terminate(cowboy_req:reply(StatusCode, Req), State).
|
||||||
|
|
||||||
-spec error_terminate(cowboy_req:req(), #state{}, atom(), any(), atom()) -> no_return().
|
-spec error_terminate(cowboy_req:req(), #state{}, atom(), any()) -> no_return().
|
||||||
error_terminate(Req, #state{handler=Handler, handler_state=HandlerState},
|
error_terminate(Req, #state{handler=Handler, handler_state=HandlerState}, Class, Reason) ->
|
||||||
Class, Reason, Callback) ->
|
|
||||||
Stacktrace = erlang:get_stacktrace(),
|
|
||||||
cowboy_req:maybe_reply(Stacktrace, Req),
|
|
||||||
cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler),
|
cowboy_handler:terminate({crash, Class, Reason}, Req, HandlerState, Handler),
|
||||||
exit({cowboy_handler, [
|
erlang:raise(Class, Reason, erlang:get_stacktrace()).
|
||||||
{class, Class},
|
|
||||||
{reason, Reason},
|
|
||||||
{mfa, {Handler, Callback, 2}},
|
|
||||||
{stacktrace, Stacktrace},
|
|
||||||
{req, Req},
|
|
||||||
{state, HandlerState}
|
|
||||||
]}).
|
|
||||||
|
|
||||||
terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
|
terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
|
||||||
Result = cowboy_handler:terminate(normal, Req, HandlerState, Handler),
|
Result = cowboy_handler:terminate(normal, Req, HandlerState, Handler),
|
||||||
|
|
|
@ -69,18 +69,6 @@ data(_StreamID, IsFin, Data, State=#state{pid=Pid, read_body_ref=Ref,
|
||||||
-spec info(_,_,_) -> _.
|
-spec info(_,_,_) -> _.
|
||||||
info(_StreamID, {'EXIT', Pid, normal}, State=#state{pid=Pid}) ->
|
info(_StreamID, {'EXIT', Pid, normal}, State=#state{pid=Pid}) ->
|
||||||
{[stop], State};
|
{[stop], State};
|
||||||
%% @todo Transition.
|
|
||||||
%% In the future it would be better to simplify things
|
|
||||||
%% and only catch this at the stream level.
|
|
||||||
%%
|
|
||||||
%% Maybe we don't need specific error messages
|
|
||||||
%% for every single callbacks anymore?
|
|
||||||
info(_StreamID, Exit = {'EXIT', Pid, {cowboy_handler, _}}, State=#state{pid=Pid}) ->
|
|
||||||
%% No crash report; one has already been sent.
|
|
||||||
{[
|
|
||||||
{error_response, 500, #{<<"content-length">> => <<"0">>}, <<>>},
|
|
||||||
{internal_error, Exit, 'Stream process crashed.'}
|
|
||||||
], State};
|
|
||||||
info(_StreamID, {'EXIT', Pid, {_Reason, [_, {cow_http_hd, _, _, _}|_]}}, State=#state{pid=Pid}) ->
|
info(_StreamID, {'EXIT', Pid, {_Reason, [_, {cow_http_hd, _, _, _}|_]}}, State=#state{pid=Pid}) ->
|
||||||
%% @todo Have an option to enable/disable this specific crash report?
|
%% @todo Have an option to enable/disable this specific crash report?
|
||||||
%%report_crash(Ref, StreamID, Pid, Reason, Stacktrace),
|
%%report_crash(Ref, StreamID, Pid, Reason, Stacktrace),
|
||||||
|
|
|
@ -369,15 +369,7 @@ handler_call(State=#state{handler=Handler}, Req, HandlerState,
|
||||||
websocket_close(State, Req2, HandlerState2, stop)
|
websocket_close(State, Req2, HandlerState2, stop)
|
||||||
catch Class:Reason ->
|
catch Class:Reason ->
|
||||||
_ = websocket_close(State, Req, HandlerState, {crash, Class, Reason}),
|
_ = websocket_close(State, Req, HandlerState, {crash, Class, Reason}),
|
||||||
exit({cowboy_handler, [
|
erlang:raise(Class, Reason, erlang:get_stacktrace())
|
||||||
{class, Class},
|
|
||||||
{reason, Reason},
|
|
||||||
{mfa, {Handler, Callback, 3}},
|
|
||||||
{stacktrace, erlang:get_stacktrace()},
|
|
||||||
{msg, Message},
|
|
||||||
{req, Req},
|
|
||||||
{state, HandlerState}
|
|
||||||
]})
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec websocket_send(cow_ws:frame(), #state{}) -> ok | stop | {error, atom()}.
|
-spec websocket_send(cow_ws:frame(), #state{}) -> ok | stop | {error, atom()}.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue