Browse Source

Centralize stream handler error reporting in cowboy_stream

Loïc Hoguin 7 years ago
parent
commit
a6126306a2
3 changed files with 93 additions and 44 deletions
  1. 28 25
      src/cowboy_http.erl
  2. 19 19
      src/cowboy_http2.erl
  3. 46 0
      src/cowboy_stream.erl

+ 28 - 25
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().

+ 19 - 19
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.

+ 46 - 0
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]).