From a6126306a2b9e956c38b10e8cf1e60acdb4e63fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 21 Sep 2017 12:53:21 +0200 Subject: Centralize stream handler error reporting in cowboy_stream --- src/cowboy_http.erl | 53 +++++++++++++++++++++++++++------------------------ src/cowboy_http2.erl | 38 ++++++++++++++++++------------------ src/cowboy_stream.erl | 46 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index c9c6383..107fd60 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -259,10 +259,10 @@ after_parse({request, Req=#{streamid := StreamID, headers := Headers, version := end, State = set_timeout(State1), parse(Buffer, commands(State, StreamID, Commands)) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:init(~p, ~p, ~p) with reason ~p:~p.", - [StreamID, Req, Opts, Class, Reason]), + catch Class:Exception -> + cowboy_stream:report_error(init, + [StreamID, Req, Opts], + Class, Exception, erlang:get_stacktrace()), ok %% @todo send a proper response, etc. note that terminate must NOT be called %% @todo Status code. % stream_reset(State, StreamID, {internal_error, {Class, Reason}, @@ -277,10 +277,10 @@ after_parse({data, StreamID, IsFin, Data, State=#state{ Streams = lists:keyreplace(StreamID, #stream.id, Streams0, Stream#stream{state=StreamState}), parse(Buffer, commands(State#state{streams=Streams}, StreamID, Commands)) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:data(~p, ~p, ~p, ~p) with reason ~p:~p.", - [StreamID, IsFin, Data, StreamState0, Class, Reason]), + catch Class:Exception -> + cowboy_stream:report_error(data, + [StreamID, IsFin, Data, StreamState0], + Class, Exception, erlang:get_stacktrace()), %% @todo Bad value returned here. Crashes. ok %% @todo @@ -741,10 +741,10 @@ info(State=#state{streams=Streams0}, StreamID, Msg) -> Streams = lists:keyreplace(StreamID, #stream.id, Streams0, Stream#stream{state=StreamState}), commands(State#state{streams=Streams}, StreamID, Commands) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:info(~p, ~p, ~p) with reason ~p:~p.", - [StreamID, Msg, StreamState0, Class, Reason]), + catch Class:Exception -> + cowboy_stream:report_error(info, + [StreamID, Msg, StreamState0], + Class, Exception, erlang:get_stacktrace()), ok %% @todo % stream_reset(State, StreamID, {internal_error, {Class, Reason}, @@ -984,10 +984,10 @@ stream_terminate(State0=#state{socket=Socket, transport=Transport, stream_call_terminate(StreamID, Reason, StreamState) -> try cowboy_stream:terminate(StreamID, Reason, StreamState) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:terminate(~p, ~p, ~p) with reason ~p:~p.", - [StreamID, Reason, StreamState, Class, Reason]) + catch Class:Exception -> + cowboy_stream:report_error(terminate, + [StreamID, Reason, StreamState], + Class, Exception, erlang:get_stacktrace()) end. %% @todo max_reqs also @@ -1051,15 +1051,18 @@ error_terminate(StatusCode0, State=#state{ref=Ref, socket=Socket, transport=Tran end } end, - {response, StatusCode, RespHeaders, RespBody} - = cowboy_stream:early_error(StreamID, Reason, PartialReq, - {response, StatusCode0, #{ - <<"content-length">> => <<"0">> - }, <<>>}, Opts), - Transport:send(Socket, [ - cow_http:response(StatusCode, 'HTTP/1.1', maps:to_list(RespHeaders)), - RespBody - ]), + Resp = {response, StatusCode0, #{<<"content-length">> => <<"0">>}, <<>>}, + try cowboy_stream:early_error(StreamID, Reason, PartialReq, Resp, Opts) of + {response, StatusCode, RespHeaders, RespBody} -> + Transport:send(Socket, [ + cow_http:response(StatusCode, 'HTTP/1.1', maps:to_list(RespHeaders)), + RespBody + ]) + catch Class:Exception -> + cowboy_stream:report_error(early_error, + [StreamID, Reason, PartialReq, Resp, Opts], + Class, Exception, erlang:get_stacktrace()) + end, terminate(State, Reason). -spec terminate(_, _) -> no_return(). diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl index 6cee00e..77359ee 100644 --- a/src/cowboy_http2.erl +++ b/src/cowboy_http2.erl @@ -305,11 +305,11 @@ frame(State0=#state{remote_window=ConnWindow, streams=Streams}, commands(State, Stream#stream{state=StreamState, remote_window=StreamWindow - DataLen, body_length=Len}, Commands) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:data(~p, ~p, ~p, ~p) with reason ~p:~p.", - [StreamID, IsFin0, Data, StreamState0, Class, Reason]), - stream_reset(State, StreamID, {internal_error, {Class, Reason}, + catch Class:Exception -> + cowboy_stream:report_error(data, + [StreamID, IsFin, Data, StreamState0], + Class, Exception, erlang:get_stacktrace()), + stream_reset(State, StreamID, {internal_error, {Class, Exception}, 'Exception occurred in cowboy_stream:data/4.'}) end; #stream{remote=fin} -> @@ -441,11 +441,11 @@ info(State=#state{streams=Streams}, StreamID, Msg) -> try cowboy_stream:info(StreamID, Msg, StreamState0) of {Commands, StreamState} -> commands(State, Stream#stream{state=StreamState}, Commands) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:info(~p, ~p, ~p) with reason ~p:~p.", - [StreamID, Msg, StreamState0, Class, Reason]), - stream_reset(State, StreamID, {internal_error, {Class, Reason}, + catch Class:Exception -> + cowboy_stream:report_error(info, + [StreamID, Msg, StreamState0], + Class, Exception, erlang:get_stacktrace()), + stream_reset(State, StreamID, {internal_error, {Class, Exception}, 'Exception occurred in cowboy_stream:info/3.'}) end; false -> @@ -776,11 +776,11 @@ stream_handler_init(State=#state{opts=Opts, remote=RemoteIsFin, local=LocalIsFin, local_window=LocalWindow, remote_window=RemoteWindow}, Commands) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:init(~p, ~p, ~p) with reason ~p:~p.", - [StreamID, Req, Opts, Class, Reason]), - stream_reset(State, StreamID, {internal_error, {Class, Reason}, + catch Class:Exception -> + cowboy_stream:report_error(init, + [StreamID, Req, Opts], + Class, Exception, erlang:get_stacktrace()), + stream_reset(State, StreamID, {internal_error, {Class, Exception}, 'Exception occurred in cowboy_stream:init/3.'}) end. @@ -832,10 +832,10 @@ stream_terminate(State=#state{socket=Socket, transport=Transport, stream_call_terminate(StreamID, Reason, StreamState) -> try cowboy_stream:terminate(StreamID, Reason, StreamState) - catch Class:Reason -> - error_logger:error_msg("Exception occurred in " - "cowboy_stream:terminate(~p, ~p, ~p) with reason ~p:~p.", - [StreamID, Reason, StreamState, Class, Reason]) + catch Class:Exception -> + cowboy_stream:report_error(terminate, + [StreamID, Reason, StreamState], + Class, Exception, erlang:get_stacktrace()) end. %% Headers encode/decode. diff --git a/src/cowboy_stream.erl b/src/cowboy_stream.erl index fbad569..b0a55f8 100644 --- a/src/cowboy_stream.erl +++ b/src/cowboy_stream.erl @@ -76,6 +76,7 @@ -export([info/3]). -export([terminate/3]). -export([early_error/5]). +-export([report_error/5]). %% Note that this and other functions in this module do NOT catch %% exceptions. We want the exception to go all the way down to the @@ -144,3 +145,48 @@ early_error(StreamID, Reason, PartialReq, Resp, Opts) -> Handler:early_error(StreamID, Reason, PartialReq, Resp, Opts#{stream_handlers => Tail}) end. + +-spec report_error(atom(), list(), error | exit | throw, any(), list()) -> ok. +report_error(init, [StreamID, Req, Opts], Class, Exception, Stacktrace) -> + error_logger:error_msg( + "Unhandled exception ~p:~p in cowboy_stream:init(~p, Req, Opts)~n" + "Stacktrace: ~p~n" + "Req: ~p~n" + "Opts: ~p~n", + [Class, Exception, StreamID, Stacktrace, Req, Opts]); +report_error(data, [StreamID, IsFin, Data, State], Class, Exception, Stacktrace) -> + error_logger:error_msg( + "Unhandled exception ~p:~p in cowboy_stream:data(~p, ~p, Data, State)~n" + "Stacktrace: ~p~n" + "Data: ~p~n" + "State: ~p~n", + [Class, Exception, StreamID, IsFin, Stacktrace, Data, State]); +report_error(info, [StreamID, Msg, State], Class, Exception, Stacktrace) -> + error_logger:error_msg( + "Unhandled exception ~p:~p in cowboy_stream:info(~p, Msg, State)~n" + "Stacktrace: ~p~n" + "Msg: ~p~n" + "State: ~p~n", + [Class, Exception, StreamID, Stacktrace, Msg, State]); +report_error(terminate, [StreamID, Reason, State], Class, Exception, Stacktrace) -> + error_logger:error_msg( + "Unhandled exception ~p:~p in cowboy_stream:terminate(~p, Reason, State)~n" + "Stacktrace: ~p~n" + "Reason: ~p~n" + "State: ~p~n", + [Class, Exception, StreamID, Stacktrace, Reason, State]); +report_error(early_error, [StreamID, Reason, PartialReq, Resp, Opts], Class, Exception, Stacktrace) -> + error_logger:error_msg( + "Unhandled exception ~p:~p in cowboy_stream:early_error(~p, Reason, PartialReq, Resp, Opts)~n" + "Stacktrace: ~p~n" + "Reason: ~p~n" + "PartialReq: ~p~n" + "Resp: ~p~n" + "Opts: ~p~n", + [Class, Exception, StreamID, Stacktrace, Reason, PartialReq, Resp, Opts]); +report_error(Callback, _, Class, Reason, Stacktrace) -> + error_logger:error_msg( + "Exception occurred in unknown callback ~p~n" + "Reason: ~p:~p~n" + "Stacktrace: ~p~n", + [Callback, Class, Reason, Stacktrace]). -- cgit v1.2.3