Просмотр исходного кода

Replace terminate/2 with terminate/3, adding a Reason

This should have been done a *long* time ago, back when I initially
added Websocket support. This is the first part of two in improving
loop handler support with regards to socket closure.

Reason may include: {normal, shutdown} for the most normal shutdown,
{normal, timeout} for a loop handler timeout shutdown, or {error, _}
if an error occured.
Loïc Hoguin 12 лет назад
Родитель
Сommit
647e95aed1

+ 2 - 2
examples/chunked_hello_world/src/toppage_handler.erl

@@ -5,7 +5,7 @@
 
 -export([init/3]).
 -export([handle/2]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init(_Transport, Req, []) ->
 	{ok, Req, undefined}.
@@ -19,5 +19,5 @@ handle(Req, State) ->
 	ok = cowboy_req:chunk("Chunked!\r\n", Req2),
 	{ok, Req2, State}.
 
-terminate(_Req, _State) ->
+terminate(_Reason, _Req, _State) ->
 	ok.

+ 2 - 2
examples/compress_response/src/toppage_handler.erl

@@ -5,7 +5,7 @@
 
 -export([init/3]).
 -export([handle/2]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init(_Transport, Req, []) ->
 	{ok, Req, undefined}.
@@ -27,5 +27,5 @@ who perform work similar to the cowboy in their respective nations.\n">>,
 	{ok, Req2} = cowboy_req:reply(200, [], BigBody, Req),
 	{ok, Req2, State}.
 
-terminate(_Req, _State) ->
+terminate(_Reason, _Req, _State) ->
 	ok.

+ 2 - 2
examples/cookie/src/toppage_handler.erl

@@ -5,7 +5,7 @@
 
 -export([init/3]).
 -export([handle/2]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init(_Transport, Req, []) ->
 	{ok, Req, undefined}.
@@ -25,5 +25,5 @@ handle(Req, State) ->
 		Body, Req4),
 	{ok, Req5, State}.
 
-terminate(_Req, _State) ->
+terminate(_Reason, _Req, _State) ->
 	ok.

+ 2 - 2
examples/echo_get/src/toppage_handler.erl

@@ -5,7 +5,7 @@
 
 -export([init/3]).
 -export([handle/2]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init(_Transport, Req, []) ->
 	{ok, Req, undefined}.
@@ -25,5 +25,5 @@ echo(_, _, Req) ->
 	%% Method not allowed.
 	cowboy_req:reply(405, Req).
 
-terminate(_Req, _State) ->
+terminate(_Reason, _Req, _State) ->
 	ok.

+ 2 - 2
examples/echo_post/src/toppage_handler.erl

@@ -5,7 +5,7 @@
 
 -export([init/3]).
 -export([handle/2]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init(_Transport, Req, []) ->
 	{ok, Req, undefined}.
@@ -32,5 +32,5 @@ echo(Echo, Req) ->
 	cowboy_req:reply(200,
 		[{<<"content-encoding">>, <<"utf-8">>}], Echo, Req).
 
-terminate(_Req, _State) ->
+terminate(_Reason, _Req, _State) ->
 	ok.

+ 2 - 2
examples/hello_world/src/toppage_handler.erl

@@ -5,7 +5,7 @@
 
 -export([init/3]).
 -export([handle/2]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init(_Transport, Req, []) ->
 	{ok, Req, undefined}.
@@ -14,5 +14,5 @@ handle(Req, State) ->
 	{ok, Req2} = cowboy_req:reply(200, [], <<"Hello world!">>, Req),
 	{ok, Req2, State}.
 
-terminate(_Req, _State) ->
+terminate(_Reason, _Req, _State) ->
 	ok.

+ 1 - 1
guide/http_handlers.md

@@ -16,7 +16,7 @@ this should be used for any initialization needs.
 The second callback, `handle/2`, is where most of your code should
 be. As the name explains, this is where you handle the request.
 
-The last callback, `terminate/2`, will be empty most of the time.
+The last callback, `terminate/3`, will be empty most of the time.
 It's used for any needed cleanup. If you used the process dictionary,
 timers, monitors then you most likely want to stop them in this
 callback, as Cowboy might end up reusing this process for subsequent

+ 3 - 3
guide/introduction.md

@@ -87,7 +87,7 @@ handlers, Websocket handlers, REST handlers and static handlers. Their
 usage is documented in the respective sections of the guide.
 
 Most applications use the plain HTTP handler, which has three callback
-functions: init/3, handle/2 and terminate/2. Following is an example of
+functions: init/3, handle/2 and terminate/3. Following is an example of
 a simple handler module.
 
 ``` erlang
@@ -96,7 +96,7 @@ a simple handler module.
 
 -export([init/3]).
 -export([handle/2]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init({tcp, http}, Req, Opts) ->
     {ok, Req, undefined_state}.
@@ -105,7 +105,7 @@ handle(Req, State) ->
     {ok, Req2} = cowboy_req:reply(200, [], <<"Hello World!">>, Req),
     {ok, Req2, State}.
 
-terminate(Req, State) ->
+terminate(Reason, Req, State) ->
     ok.
 ```
 

+ 3 - 3
guide/loop_handlers.md

@@ -31,7 +31,7 @@ practice is known as server-sent events.
 
 Loop handlers essentially wait for one or more Erlang messages
 and feed these messages to the `info/3` callback. It also features
-the `init/3` and `terminate/2` callbacks which work the same as
+the `init/3` and `terminate/3` callbacks which work the same as
 for plain HTTP handlers.
 
 The following handler waits for a message `{reply, Body}` before
@@ -46,7 +46,7 @@ this message.
 
 -export([init/3]).
 -export([info/3]).
--export([terminate/2]).
+-export([terminate/3]).
 
 init({tcp, http}, Req, Opts) ->
     {loop, Req, undefined_state, 60000, hibernate}.
@@ -57,6 +57,6 @@ info({reply, Body}, Req, State) ->
 info(Message, Req, State) ->
     {loop, Req, State, hibernate}.
 
-terminate(Req, State) ->
+terminate(Reason, Req, State) ->
     ok.
 ```

+ 20 - 14
src/cowboy_handler.erl

@@ -64,7 +64,8 @@ handler_init(Req, State, Handler, HandlerOpts) ->
 			handler_before_loop(Req2, State#state{
 				hibernate=true, loop_timeout=Timeout}, Handler, HandlerState);
 		{shutdown, Req2, HandlerState} ->
-			terminate_request(Req2, State, Handler, HandlerState);
+			terminate_request(Req2, State, Handler, HandlerState,
+				{normal, shutdown});
 		%% @todo {upgrade, transport, Module}
 		{upgrade, protocol, Module} ->
 			upgrade_protocol(Req, State, Handler, HandlerOpts, Module);
@@ -99,7 +100,8 @@ upgrade_protocol(Req, #state{env=Env},
 handler_handle(Req, State, Handler, HandlerState) ->
 	try Handler:handle(Req, HandlerState) of
 		{ok, Req2, HandlerState2} ->
-			terminate_request(Req2, State, Handler, HandlerState2)
+			terminate_request(Req2, State, Handler, HandlerState2,
+				{normal, shutdown})
 	catch Class:Reason ->
 		error_logger:error_msg(
 			"** Cowboy handler ~p terminating in ~p/~p~n"
@@ -109,7 +111,7 @@ handler_handle(Req, State, Handler, HandlerState) ->
 			"** Stacktrace: ~p~n~n",
 			[Handler, handle, 2, Class, Reason, HandlerState,
 				cowboy_req:to_list(Req), erlang:get_stacktrace()]),
-		handler_terminate(Req, Handler, HandlerState),
+		handler_terminate(Req, Handler, HandlerState, Reason),
 		{error, 500, Req}
 	end.
 
@@ -146,7 +148,8 @@ handler_loop_timeout(State=#state{loop_timeout=Timeout,
 handler_loop(Req, State=#state{loop_timeout_ref=TRef}, Handler, HandlerState) ->
 	receive
 		{timeout, TRef, ?MODULE} ->
-			terminate_request(Req, State, Handler, HandlerState);
+			terminate_request(Req, State, Handler, HandlerState,
+				{normal, timeout});
 		{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
 			handler_loop(Req, State, Handler, HandlerState);
 		Message ->
@@ -160,7 +163,8 @@ handler_loop(Req, State=#state{loop_timeout_ref=TRef}, Handler, HandlerState) ->
 handler_call(Req, State, Handler, HandlerState, Message) ->
 	try Handler:info(Message, Req, HandlerState) of
 		{ok, Req2, HandlerState2} ->
-			terminate_request(Req2, State, Handler, HandlerState2);
+			terminate_request(Req2, State, Handler, HandlerState2,
+				{normal, shutdown});
 		{loop, Req2, HandlerState2} ->
 			handler_before_loop(Req2, State, Handler, HandlerState2);
 		{loop, Req2, HandlerState2, hibernate} ->
@@ -175,27 +179,29 @@ handler_call(Req, State, Handler, HandlerState, Message) ->
 			"** Stacktrace: ~p~n~n",
 			[Handler, info, 3, Class, Reason, HandlerState,
 				cowboy_req:to_list(Req), erlang:get_stacktrace()]),
-		handler_terminate(Req, Handler, HandlerState),
+		handler_terminate(Req, Handler, HandlerState, Reason),
 		{error, 500, Req}
 	end.
 
--spec terminate_request(Req, #state{}, module(), any()) ->
+-spec terminate_request(Req, #state{}, module(), any(),
+	{normal, timeout | shutdown} | {error, atom()}) ->
 	{ok, Req, cowboy_middleware:env()} when Req::cowboy_req:req().
-terminate_request(Req, #state{env=Env}, Handler, HandlerState) ->
-	HandlerRes = handler_terminate(Req, Handler, HandlerState),
+terminate_request(Req, #state{env=Env}, Handler, HandlerState, Reason) ->
+	HandlerRes = handler_terminate(Req, Handler, HandlerState, Reason),
 	{ok, Req, [{result, HandlerRes}|Env]}.
 
--spec handler_terminate(cowboy_req:req(), module(), any()) -> ok.
-handler_terminate(Req, Handler, HandlerState) ->
+-spec handler_terminate(cowboy_req:req(), module(), any(),
+	{normal, timeout | shutdown} | {error, atom()}) -> ok.
+handler_terminate(Req, Handler, HandlerState, Reason) ->
 	try
-		Handler:terminate(cowboy_req:lock(Req), HandlerState)
-	catch Class:Reason ->
+		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, 2, Class, Reason, HandlerState,
+			[Handler, terminate, 3, Class, Reason2, HandlerState,
 				cowboy_req:to_list(Req), erlang:get_stacktrace()])
 	end.

+ 6 - 3
src/cowboy_http_handler.erl

@@ -22,8 +22,8 @@
 %% <em>handle/2</em> allows you to handle the request. It receives the
 %% state previously defined.
 %%
-%% <em>terminate/2</em> allows you to clean up. It receives the state
-%% previously defined.
+%% <em>terminate/3</em> allows you to clean up. It receives the
+%% termination reason and the state previously defined.
 %%
 %% There is no required operation to perform in any of these callbacks
 %% other than returning the proper values. Make sure you always return
@@ -33,6 +33,9 @@
 
 -type opts() :: any().
 -type state() :: any().
+-type terminate_reason() :: {normal, shutdown}
+	| {normal, timeout} %% Only occurs in loop handlers.
+	| {error, atom()}.
 
 -callback init({atom(), http}, Req, opts())
 	-> {ok, Req, state()}
@@ -45,4 +48,4 @@
 	when Req::cowboy_req:req().
 -callback handle(Req, State) -> {ok, Req, State}
 	when Req::cowboy_req:req(), State::state().
--callback terminate(cowboy_req:req(), state()) -> ok.
+-callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.

+ 6 - 3
src/cowboy_loop_handler.erl

@@ -23,8 +23,8 @@
 %% receive. It receives the message and the state previously defined.
 %% It can decide to stop the receive loop or continue receiving.
 %%
-%% <em>terminate/2</em> allows you to clean up. It receives the state
-%% previously defined.
+%% <em>terminate/3</em> allows you to clean up. It receives the
+%% termination reason and the state previously defined.
 %%
 %% There is no required operation to perform in any of these callbacks
 %% other than returning the proper values. Make sure you always return
@@ -39,6 +39,9 @@
 
 -type opts() :: any().
 -type state() :: any().
+-type terminate_reason() :: {normal, shutdown}
+	| {normal, timeout}
+	| {error, atom()}.
 
 -callback init({atom(), http}, Req, opts())
 	-> {ok, Req, state()}
@@ -54,4 +57,4 @@
 	| {loop, Req, State}
 	| {loop, Req, State, hibernate}
 	when Req::cowboy_req:req(), State::state().
--callback terminate(cowboy_req:req(), state()) -> ok.
+-callback terminate(terminate_reason(), cowboy_req:req(), state()) -> ok.

+ 2 - 2
test/chunked_handler.erl

@@ -2,7 +2,7 @@
 
 -module(chunked_handler).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 init({_Transport, http}, Req, _Opts) ->
 	{ok, Req, undefined}.
@@ -13,5 +13,5 @@ handle(Req, State) ->
 	cowboy_req:chunk("works fine!", Req2),
 	{ok, Req2, State}.
 
-terminate(_Req, _State) ->
+terminate(_, _, _) ->
 	ok.

+ 2 - 2
test/http_handler.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 -record(state, {headers, body}).
 
@@ -15,5 +15,5 @@ handle(Req, State=#state{headers=Headers, body=Body}) ->
 	{ok, Req2} = cowboy_req:reply(200, Headers, Body, Req),
 	{ok, Req2, State}.
 
-terminate(_Req, _State) ->
+terminate(_, _, _) ->
 	ok.

+ 2 - 2
test/http_handler_echo_body.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_echo_body).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 init({_, http}, Req, _) ->
 	{ok, Req, undefined}.
@@ -15,5 +15,5 @@ handle(Req, State) ->
 	{ok, Req4} = cowboy_req:reply(200, [], Body, Req3),
 	{ok, Req4, State}.
 
-terminate(_, _) ->
+terminate(_, _, _) ->
 	ok.

+ 2 - 2
test/http_handler_errors.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_errors).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 init({_Transport, http}, Req, _Opts) ->
     {Case, Req1} = cowboy_req:qs_val(<<"case">>, Req),
@@ -36,5 +36,5 @@ handle(Req, <<"handle_after_reply">> = Case) ->
     {ok, _Req1} = cowboy_req:reply(200, [], "http_handler_crashes", Req),
     erlang:error(Case).
 
-terminate(_Req, _State) ->
+terminate(_, _, _) ->
 	ok.

+ 2 - 2
test/http_handler_init_shutdown.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_init_shutdown).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 init({_Transport, http}, Req, _Opts) ->
 	{ok, Req2} = cowboy_req:reply(<<"666 Init Shutdown Testing">>,
@@ -13,5 +13,5 @@ handle(Req, State) ->
 	{ok, Req2} = cowboy_req:reply(200, [], "Hello world!", Req),
 	{ok, Req2, State}.
 
-terminate(_Req, _State) ->
+terminate(_, _, _) ->
 	ok.

+ 2 - 2
test/http_handler_long_polling.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_long_polling).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, info/3, terminate/2]).
+-export([init/3, handle/2, info/3, terminate/3]).
 
 init({_Transport, http}, Req, _Opts) ->
 	erlang:send_after(500, self(), timeout),
@@ -18,5 +18,5 @@ info(timeout, Req, State) ->
 	erlang:send_after(500, self(), timeout),
 	{loop, Req, State - 1, hibernate}.
 
-terminate(_Req, _State) ->
+terminate({normal, shutdown}, _, _) ->
 	ok.

+ 2 - 2
test/http_handler_loop_timeout.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_loop_timeout).
 -behaviour(cowboy_loop_handler).
--export([init/3, info/3, terminate/2]).
+-export([init/3, info/3, terminate/3]).
 
 init({_, http}, Req, _) ->
 	erlang:send_after(1000, self(), error_timeout),
@@ -12,5 +12,5 @@ info(error_timeout, Req, State) ->
 	{ok, Req2} = cowboy_req:reply(500, Req),
 	{ok, Req2, State}.
 
-terminate(_, _) ->
+terminate({normal, timeout}, _, _) ->
 	ok.

+ 2 - 2
test/http_handler_multipart.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_multipart).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 init({_Transport, http}, Req, []) ->
 	{ok, Req, {}}.
@@ -12,7 +12,7 @@ handle(Req, State) ->
 	{ok, Req3} = cowboy_req:reply(200, [], term_to_binary(Result), Req2),
 	{ok, Req3, State}.
 
-terminate(_Req, _State) ->
+terminate(_, _, _) ->
 	ok.
 
 acc_multipart(Req) ->

+ 2 - 2
test/http_handler_set_resp.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_set_resp).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 init({_Transport, http}, Req, Opts) ->
 	Headers = proplists:get_value(headers, Opts, []),
@@ -27,5 +27,5 @@ handle(Req, State) ->
 			end
 	end.
 
-terminate(_Req, _State) ->
+terminate(_, _, _) ->
 	ok.

+ 2 - 2
test/http_handler_stream_body.erl

@@ -2,7 +2,7 @@
 
 -module(http_handler_stream_body).
 -behaviour(cowboy_http_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3, handle/2, terminate/3]).
 
 -record(state, {headers, body, reply}).
 
@@ -24,5 +24,5 @@ handle(Req, State=#state{headers=_Headers, body=Body, reply=Reply}) ->
 	{ok, Req3} = cowboy_req:reply(200, Req2),
 	{ok, Req3, State}.
 
-terminate(_Req, _State) ->
+terminate(_, _, _) ->
 	ok.

+ 1 - 8
test/websocket_echo_handler.erl

@@ -1,21 +1,14 @@
 %% Feel free to use, reuse and abuse the code in this file.
 
 -module(websocket_echo_handler).
--behaviour(cowboy_http_handler).
 -behaviour(cowboy_websocket_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3]).
 -export([websocket_init/3, websocket_handle/3,
 	websocket_info/3, websocket_terminate/3]).
 
 init(_Any, _Req, _Opts) ->
 	{upgrade, protocol, cowboy_websocket}.
 
-handle(_Req, _State) ->
-	exit(badarg).
-
-terminate(_Req, _State) ->
-	exit(badarg).
-
 websocket_init(_TransportName, Req, _Opts) ->
 	Req2 = cowboy_req:compact(Req),
 	{ok, Req2, undefined}.

+ 1 - 8
test/websocket_handler.erl

@@ -1,21 +1,14 @@
 %% Feel free to use, reuse and abuse the code in this file.
 
 -module(websocket_handler).
--behaviour(cowboy_http_handler).
 -behaviour(cowboy_websocket_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3]).
 -export([websocket_init/3, websocket_handle/3,
 	websocket_info/3, websocket_terminate/3]).
 
 init(_Any, _Req, _Opts) ->
 	{upgrade, protocol, cowboy_websocket}.
 
-handle(_Req, _State) ->
-	exit(badarg).
-
-terminate(_Req, _State) ->
-	exit(badarg).
-
 websocket_init(_TransportName, Req, _Opts) ->
 	erlang:start_timer(1000, self(), <<"websocket_init">>),
 	Req2 = cowboy_req:compact(Req),

+ 1 - 8
test/websocket_handler_init_shutdown.erl

@@ -1,21 +1,14 @@
 %% Feel free to use, reuse and abuse the code in this file.
 
 -module(websocket_handler_init_shutdown).
--behaviour(cowboy_http_handler).
 -behaviour(cowboy_websocket_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3]).
 -export([websocket_init/3, websocket_handle/3,
 	websocket_info/3, websocket_terminate/3]).
 
 init(_Any, _Req, _Opts) ->
 	{upgrade, protocol, cowboy_websocket}.
 
-handle(_Req, _State) ->
-	exit(badarg).
-
-terminate(_Req, _State) ->
-	exit(badarg).
-
 websocket_init(_TransportName, Req, _Opts) ->
 	{ok, Req2} = cowboy_req:reply(403, Req),
 	{shutdown, Req2}.

+ 1 - 8
test/ws_timeout_cancel_handler.erl

@@ -1,21 +1,14 @@
 %% Feel free to use, reuse and abuse the code in this file.
 
 -module(ws_timeout_cancel_handler).
--behaviour(cowboy_http_handler).
 -behaviour(cowboy_websocket_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3]).
 -export([websocket_init/3, websocket_handle/3,
 	websocket_info/3, websocket_terminate/3]).
 
 init(_Any, _Req, _Opts) ->
 	{upgrade, protocol, cowboy_websocket}.
 
-handle(_Req, _State) ->
-	exit(badarg).
-
-terminate(_Req, _State) ->
-	exit(badarg).
-
 websocket_init(_TransportName, Req, _Opts) ->
 	erlang:start_timer(500, self(), should_not_cancel_timer),
 	{ok, Req, undefined, 1000}.

+ 1 - 8
test/ws_timeout_hibernate_handler.erl

@@ -1,21 +1,14 @@
 %% Feel free to use, reuse and abuse the code in this file.
 
 -module(ws_timeout_hibernate_handler).
--behaviour(cowboy_http_handler).
 -behaviour(cowboy_websocket_handler).
--export([init/3, handle/2, terminate/2]).
+-export([init/3]).
 -export([websocket_init/3, websocket_handle/3,
 	websocket_info/3, websocket_terminate/3]).
 
 init(_Any, _Req, _Opts) ->
 	{upgrade, protocol, cowboy_websocket}.
 
-handle(_Req, _State) ->
-	exit(badarg).
-
-terminate(_Req, _State) ->
-	exit(badarg).
-
 websocket_init(_TransportName, Req, _Opts) ->
 	{ok, Req, undefined, 1000, hibernate}.