diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cowboy_handler.erl | 86 | ||||
-rw-r--r-- | src/cowboy_rest.erl | 184 | ||||
-rw-r--r-- | src/cowboy_websocket.erl | 82 |
3 files changed, 126 insertions, 226 deletions
diff --git a/src/cowboy_handler.erl b/src/cowboy_handler.erl index 2074b4e..fcbfe55 100644 --- a/src/cowboy_handler.erl +++ b/src/cowboy_handler.erl @@ -90,15 +90,14 @@ handler_init(Req, State, Handler, HandlerOpts) -> {upgrade, protocol, Module, Req2, HandlerOpts2} -> upgrade_protocol(Req2, State, Handler, HandlerOpts2, Module) catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n" - "** Options were ~p~n" - "** Request was ~p~n" - "** Stacktrace: ~p~n~n", - [Handler, init, 3, Class, Reason, HandlerOpts, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + cowboy_req:maybe_reply(500, Req), + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, init, 3}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {opts, HandlerOpts} + ]) end. -spec upgrade_protocol(Req, #state{}, module(), any(), module()) @@ -121,16 +120,15 @@ handler_handle(Req, State, Handler, HandlerState) -> terminate_request(Req2, State, Handler, HandlerState2, {normal, shutdown}) catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n" - "** Handler state was ~p~n" - "** Request was ~p~n" - "** Stacktrace: ~p~n~n", - [Handler, handle, 2, Class, Reason, HandlerState, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), + cowboy_req:maybe_reply(500, Req), handler_terminate(Req, Handler, HandlerState, Reason), - error_terminate(Req, State) + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, handle, 2}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {state, HandlerState} + ]) end. %% Update the state if the response was sent in the callback. @@ -195,7 +193,8 @@ handler_loop(Req, State=#state{loop_buffer_size=NbBytes, if NbBytes2 > Threshold -> _ = handler_terminate(Req, Handler, HandlerState, {error, overflow}), - error_terminate(Req, State); + cowboy_req:maybe_reply(500, Req), + exit(normal); true -> Req2 = cowboy_req:append_buffer(Data, Req), State2 = handler_loop_timeout(State#state{ @@ -232,7 +231,8 @@ handler_loop(Req, State=#state{loop_buffer_size=NbBytes, -> {ok, Req, cowboy_middleware:env()} | {error, 500, Req} | {suspend, module(), atom(), [any()]} when Req::cowboy_req:req(). -handler_call(Req, State, Handler, HandlerState, Message) -> +handler_call(Req, State=#state{resp_sent=RespSent}, + Handler, HandlerState, Message) -> try Handler:info(Message, Req, HandlerState) of {ok, Req2, HandlerState2} -> handler_after_loop(Req2, State, Handler, HandlerState2, @@ -243,16 +243,19 @@ handler_call(Req, State, Handler, HandlerState, Message) -> handler_after_callback(Req2, State#state{hibernate=true}, Handler, HandlerState2) catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n" - "** Handler state was ~p~n" - "** Request was ~p~n" - "** Stacktrace: ~p~n~n", - [Handler, info, 3, Class, Reason, HandlerState, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), + if RespSent -> + ok; + true -> + cowboy_req:maybe_reply(500, Req) + end, handler_terminate(Req, Handler, HandlerState, Reason), - error_terminate(Req, State) + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, info, 3}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {state, HandlerState} + ]) end. %% It is sometimes important to make a socket passive as it was initially @@ -287,21 +290,12 @@ handler_terminate(Req, Handler, HandlerState, Reason) -> try Handler:terminate(Reason, cowboy_req:lock(Req), HandlerState) catch Class:Reason2 -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n" - "** Handler state was ~p~n" - "** Request was ~p~n" - "** Stacktrace: ~p~n~n", - [Handler, terminate, 3, Class, Reason2, HandlerState, - cowboy_req:to_list(Req), erlang:get_stacktrace()]) + erlang:Class([ + {reason, Reason2}, + {mfa, {Handler, terminate, 3}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {state, HandlerState}, + {terminate_reason, Reason} + ]) 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_rest.erl b/src/cowboy_rest.erl index 34bfce1..862ebbf 100644 --- a/src/cowboy_rest.erl +++ b/src/cowboy_rest.erl @@ -66,30 +66,26 @@ -> {ok, Req, Env} | {error, 500, Req} when Req::cowboy_req:req(), Env::cowboy_middleware:env(). upgrade(Req, Env, Handler, HandlerOpts) -> - try - Method = cowboy_req:get(method, Req), - case erlang:function_exported(Handler, rest_init, 2) of - true -> - try Handler:rest_init(Req, HandlerOpts) of - {ok, Req2, HandlerState} -> - service_available(Req2, #state{env=Env, method=Method, - handler=Handler, handler_state=HandlerState}) - catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Options were ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [Handler, rest_init, 2, Class, Reason, HandlerOpts, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - {error, 500, Req} - end; - false -> - service_available(Req, #state{env=Env, method=Method, - handler=Handler}) - end - catch - throw:{?MODULE, error} -> - {error, 500, Req} + Method = cowboy_req:get(method, Req), + case erlang:function_exported(Handler, rest_init, 2) of + true -> + try Handler:rest_init(Req, HandlerOpts) of + {ok, Req2, HandlerState} -> + service_available(Req2, #state{env=Env, method=Method, + handler=Handler, handler_state=HandlerState}) + catch Class:Reason -> + cowboy_req:maybe_reply(500, Req), + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, rest_init, 2}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {opts, HandlerOpts} + ]) + end; + false -> + service_available(Req, #state{env=Env, method=Method, + handler=Handler}) end. service_available(Req, State) -> @@ -516,14 +512,7 @@ variances(Req, State=#state{content_types_p=CTP, resource_exists(Req3, State2) end catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, variances, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, variances) end. variances(Req, State, Variances) -> @@ -559,14 +548,7 @@ if_match(Req, State, EtagsList) -> false -> precondition_failed(Req2, State2) end catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, generate_etag, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, generate_etag) end. if_match_must_not_exist(Req, State) -> @@ -594,14 +576,7 @@ if_unmodified_since(Req, State, IfUnmodifiedSince) -> false -> if_none_match_exists(Req2, State2) end catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, last_modified, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, last_modified) end. if_none_match_exists(Req, State) -> @@ -627,14 +602,7 @@ if_none_match(Req, State, EtagsList) -> end end catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, generate_etag, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, generate_etag) end. precondition_is_head_get(Req, State=#state{method=Method}) @@ -669,14 +637,7 @@ if_modified_since(Req, State, IfModifiedSince) -> false -> not_modified(Req2, State2) end catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, last_modified, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, last_modified) end. not_modified(Req, State) -> @@ -687,24 +648,10 @@ not_modified(Req, State) -> {Req4, State3} -> respond(Req4, State3, 304) catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, expires, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req2, State) + error_terminate(Req, State, Class, Reason, expires) end catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, generate_etag, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req2, State) + error_terminate(Req, State, Class, Reason, generate_etag) end. precondition_failed(Req, State) -> @@ -829,17 +776,8 @@ choose_content_type(Req, State, {Type, SubType, Param}, choose_content_type(Req, State, ContentType, [_Any|Tail]) -> choose_content_type(Req, State, ContentType, Tail). -process_content_type(Req, State=#state{method=Method, - handler=Handler, handler_state=HandlerState, - exists=Exists}, Fun) -> - case call(Req, State, Fun) of - no_call -> - error_logger:error_msg( - "** Cowboy handler ~p terminating; " - "function ~p/~p was not exported~n" - "** Request was ~p~n** State was ~p~n~n", - [Handler, Fun, 2, cowboy_req:to_list(Req), HandlerState]), - {error, 500, Req}; +process_content_type(Req, State=#state{method=Method, exists=Exists}, Fun) -> + try case call(Req, State, Fun) of {halt, Req2, HandlerState2} -> terminate(Req2, State#state{handler_state=HandlerState2}); {true, Req2, HandlerState2} when Exists -> @@ -859,6 +797,8 @@ process_content_type(Req, State=#state{method=Method, Exists -> respond(Req3, State2, 303); true -> respond(Req3, State2, 201) end + end catch Class:Reason = {case_clause, no_call} -> + error_terminate(Req, State, Class, Reason, Fun) end. %% If the resource is new and has been created at another location @@ -881,14 +821,7 @@ set_resp_body_etag(Req, State) -> {Req2, State2} -> set_resp_body_last_modified(Req2, State2) catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, generate_etag, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, generate_etag) end. %% Set the Last-Modified header if any for the response provided. @@ -905,14 +838,7 @@ set_resp_body_last_modified(Req, State) -> set_resp_body_expires(Req3, State2) end catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, last_modified, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, last_modified) end. %% Set the Expires header if any for the response provided. @@ -921,29 +847,14 @@ set_resp_body_expires(Req, State) -> {Req2, State2} -> set_resp_body(Req2, State2) catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [State#state.handler, expires, 2, - Class, Reason, State#state.handler_state, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, expires) end. %% Set the response headers and call the callback found using %% content_types_provided/2 to obtain the request body and add %% it to the response. -set_resp_body(Req, State=#state{handler=Handler, handler_state=HandlerState, - content_type_a={_Type, Callback}}) -> - case call(Req, State, Callback) of - no_call -> - error_logger:error_msg( - "** Cowboy handler ~p terminating; " - "function ~p/~p was not exported~n" - "** Request was ~p~n** State was ~p~n~n", - [Handler, Callback, 2, cowboy_req:to_list(Req), HandlerState]), - {error, 500, Req}; +set_resp_body(Req, State=#state{content_type_a={_, Callback}}) -> + try case call(Req, State, Callback) of {halt, Req2, HandlerState2} -> terminate(Req2, State#state{handler_state=HandlerState2}); {Body, Req2, HandlerState2} -> @@ -959,6 +870,8 @@ set_resp_body(Req, State=#state{handler=Handler, handler_state=HandlerState, cowboy_req:set_resp_body(Body, Req2) end, multiple_choices(Req3, State2) + end catch Class:Reason = {case_clause, no_call} -> + error_terminate(Req, State, Class, Reason, Callback) end. multiple_choices(Req, State) -> @@ -1057,13 +970,7 @@ call(Req, State=#state{handler=Handler, handler_state=HandlerState}, try Handler:Callback(Req, HandlerState) catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [Handler, Callback, 2, Class, Reason, HandlerState, - cowboy_req:to_list(Req), erlang:get_stacktrace()]), - error_terminate(Req, State) + error_terminate(Req, State, Class, Reason, Callback) end; false -> no_call @@ -1089,10 +996,17 @@ terminate(Req, State=#state{env=Env}) -> rest_terminate(Req, State), {ok, Req, [{result, ok}|Env]}. --spec error_terminate(cowboy_req:req(), #state{}) -> no_return(). -error_terminate(Req, State) -> +error_terminate(Req, State=#state{handler=Handler, handler_state=HandlerState}, + Class, Reason, Callback) -> rest_terminate(Req, State), - erlang:raise(throw, {?MODULE, error}, erlang:get_stacktrace()). + cowboy_req:maybe_reply(500, Req), + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, Callback, 2}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {state, HandlerState} + ]). rest_terminate(Req, #state{handler=Handler, handler_state=HandlerState}) -> case erlang:function_exported(Handler, rest_terminate, 2) of diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl index 073d7c6..88afbd0 100644 --- a/src/cowboy_websocket.erl +++ b/src/cowboy_websocket.erl @@ -48,7 +48,6 @@ socket = undefined :: inet:socket(), transport = undefined :: module(), handler :: module(), - handler_opts :: any(), key = undefined :: undefined | binary(), timeout = infinity :: timeout(), timeout_ref = undefined :: undefined | reference(), @@ -75,10 +74,13 @@ upgrade(Req, Env, Handler, HandlerOpts) -> ranch:remove_connection(Ref), [Socket, Transport] = cowboy_req:get([socket, transport], Req), State = #state{env=Env, socket=Socket, transport=Transport, - handler=Handler, handler_opts=HandlerOpts}, - case catch websocket_upgrade(State, Req) of - {ok, State2, Req2} -> handler_init(State2, Req2); - {'EXIT', _Reason} -> upgrade_error(Req, Env) + handler=Handler}, + try websocket_upgrade(State, Req) of + {ok, State2, Req2} -> + handler_init(State2, Req2, HandlerOpts) + catch _:_ -> + cowboy_req:maybe_reply(400, Req), + exit(normal) end. -spec websocket_upgrade(#state{}, Req) @@ -129,12 +131,12 @@ websocket_extensions(State, Req) -> {ok, State, Req} end. --spec handler_init(#state{}, Req) +-spec handler_init(#state{}, Req, any()) -> {ok, Req, cowboy_middleware:env()} | {error, 400, Req} | {suspend, module(), atom(), [any()]} when Req::cowboy_req:req(). handler_init(State=#state{env=Env, transport=Transport, - handler=Handler, handler_opts=HandlerOpts}, Req) -> + handler=Handler}, Req, HandlerOpts) -> try Handler:websocket_init(Transport:name(), Req, HandlerOpts) of {ok, Req2, HandlerState} -> websocket_handshake(State, Req2, HandlerState); @@ -151,24 +153,14 @@ handler_init(State=#state{env=Env, transport=Transport, cowboy_req:ensure_response(Req2, 400), {ok, Req2, [{result, closed}|Env]} catch Class:Reason -> - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Options were ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [Handler, websocket_init, 3, Class, Reason, HandlerOpts, - cowboy_req:to_list(Req),erlang:get_stacktrace()]), - 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) -> - receive - {cowboy_req, resp_sent} -> - {ok, Req, [{result, closed}|Env]} - after 0 -> - {error, 400, Req} + cowboy_req:maybe_reply(400, Req), + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, websocket_init, 3}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {opts, HandlerOpts} + ]) end. -spec websocket_handshake(#state{}, Req, any()) @@ -601,8 +593,8 @@ websocket_dispatch(State, Req, HandlerState, RemainingData, 10, Payload) -> -> {ok, Req, cowboy_middleware:env()} | {suspend, module(), atom(), [any()]} when Req::cowboy_req:req(). -handler_call(State=#state{handler=Handler, handler_opts=HandlerOpts}, Req, - HandlerState, RemainingData, Callback, Message, NextState) -> +handler_call(State=#state{handler=Handler}, Req, HandlerState, + RemainingData, Callback, Message, NextState) -> try Handler:Callback(Message, Req, HandlerState) of {ok, Req2, HandlerState2} -> NextState(State, Req2, HandlerState2, RemainingData); @@ -658,15 +650,15 @@ handler_call(State=#state{handler=Handler, handler_opts=HandlerOpts}, Req, {shutdown, Req2, HandlerState2} -> websocket_close(State, Req2, HandlerState2, {normal, shutdown}) catch Class:Reason -> - PLReq = cowboy_req:to_list(Req), - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Message was ~p~n" - "** Options were ~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [Handler, Callback, 3, Class, Reason, Message, HandlerOpts, - HandlerState, PLReq, erlang:get_stacktrace()]), - websocket_close(State, Req, HandlerState, {error, handler}) + _ = websocket_close(State, Req, HandlerState, {error, handler}), + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, Callback, 3}}, + {stacktrace, erlang:get_stacktrace()}, + {msg, Message}, + {req, cowboy_req:to_list(Req)}, + {state, HandlerState} + ]) end. websocket_opcode(text) -> 1; @@ -765,19 +757,19 @@ websocket_close(State=#state{socket=Socket, transport=Transport}, -spec handler_terminate(#state{}, Req, any(), atom() | {atom(), atom()}) -> {ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req(). -handler_terminate(#state{env=Env, handler=Handler, handler_opts=HandlerOpts}, +handler_terminate(#state{env=Env, handler=Handler}, Req, HandlerState, TerminateReason) -> try Handler:websocket_terminate(TerminateReason, Req, HandlerState) catch Class:Reason -> - PLReq = cowboy_req:to_list(Req), - error_logger:error_msg( - "** Cowboy handler ~p terminating in ~p/~p~n" - " for the reason ~p:~p~n** Initial reason was ~p~n" - "** Options were ~p~n** Handler state was ~p~n" - "** Request was ~p~n** Stacktrace: ~p~n~n", - [Handler, websocket_terminate, 3, Class, Reason, TerminateReason, - HandlerOpts, HandlerState, PLReq, erlang:get_stacktrace()]) + erlang:Class([ + {reason, Reason}, + {mfa, {Handler, websocket_terminate, 3}}, + {stacktrace, erlang:get_stacktrace()}, + {req, cowboy_req:to_list(Req)}, + {state, HandlerState}, + {terminate_reason, TerminateReason} + ]) end, {ok, Req, [{result, closed}|Env]}. |