Skip to content

Commit

Permalink
Replace terminate/2 with terminate/3, adding a Reason
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
essen committed Jan 22, 2013
1 parent 297ae32 commit 647e95a
Show file tree
Hide file tree
Showing 27 changed files with 76 additions and 99 deletions.
4 changes: 2 additions & 2 deletions examples/chunked_hello_world/src/toppage_handler.erl
Expand Up @@ -5,7 +5,7 @@

-export([init/3]).
-export([handle/2]).
-export([terminate/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
{ok, Req, undefined}.
Expand All @@ -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.
4 changes: 2 additions & 2 deletions examples/compress_response/src/toppage_handler.erl
Expand Up @@ -5,7 +5,7 @@

-export([init/3]).
-export([handle/2]).
-export([terminate/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
{ok, Req, undefined}.
Expand All @@ -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.
4 changes: 2 additions & 2 deletions examples/cookie/src/toppage_handler.erl
Expand Up @@ -5,7 +5,7 @@

-export([init/3]).
-export([handle/2]).
-export([terminate/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
{ok, Req, undefined}.
Expand All @@ -25,5 +25,5 @@ handle(Req, State) ->
Body, Req4),
{ok, Req5, State}.

terminate(_Req, _State) ->
terminate(_Reason, _Req, _State) ->
ok.
4 changes: 2 additions & 2 deletions examples/echo_get/src/toppage_handler.erl
Expand Up @@ -5,7 +5,7 @@

-export([init/3]).
-export([handle/2]).
-export([terminate/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
{ok, Req, undefined}.
Expand All @@ -25,5 +25,5 @@ echo(_, _, Req) ->
%% Method not allowed.
cowboy_req:reply(405, Req).

terminate(_Req, _State) ->
terminate(_Reason, _Req, _State) ->
ok.
4 changes: 2 additions & 2 deletions examples/echo_post/src/toppage_handler.erl
Expand Up @@ -5,7 +5,7 @@

-export([init/3]).
-export([handle/2]).
-export([terminate/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
{ok, Req, undefined}.
Expand All @@ -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.
4 changes: 2 additions & 2 deletions examples/hello_world/src/toppage_handler.erl
Expand Up @@ -5,7 +5,7 @@

-export([init/3]).
-export([handle/2]).
-export([terminate/2]).
-export([terminate/3]).

init(_Transport, Req, []) ->
{ok, Req, undefined}.
Expand All @@ -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.
2 changes: 1 addition & 1 deletion guide/http_handlers.md
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions guide/introduction.md
Expand Up @@ -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
Expand All @@ -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}.
Expand All @@ -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.
```

Expand Down
6 changes: 3 additions & 3 deletions guide/loop_handlers.md
Expand Up @@ -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
Expand All @@ -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}.
Expand All @@ -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.
```
34 changes: 20 additions & 14 deletions src/cowboy_handler.erl
Expand Up @@ -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);
Expand Down Expand Up @@ -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"
Expand All @@ -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.

Expand Down Expand Up @@ -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 ->
Expand All @@ -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} ->
Expand All @@ -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.
9 changes: 6 additions & 3 deletions src/cowboy_http_handler.erl
Expand Up @@ -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
Expand All @@ -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()}
Expand All @@ -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.
9 changes: 6 additions & 3 deletions src/cowboy_loop_handler.erl
Expand Up @@ -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
Expand All @@ -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()}
Expand All @@ -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.
4 changes: 2 additions & 2 deletions test/chunked_handler.erl
Expand Up @@ -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}.
Expand All @@ -13,5 +13,5 @@ handle(Req, State) ->
cowboy_req:chunk("works fine!", Req2),
{ok, Req2, State}.

terminate(_Req, _State) ->
terminate(_, _, _) ->
ok.
4 changes: 2 additions & 2 deletions test/http_handler.erl
Expand Up @@ -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}).

Expand All @@ -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.
4 changes: 2 additions & 2 deletions test/http_handler_echo_body.erl
Expand Up @@ -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}.
Expand All @@ -15,5 +15,5 @@ handle(Req, State) ->
{ok, Req4} = cowboy_req:reply(200, [], Body, Req3),
{ok, Req4, State}.

terminate(_, _) ->
terminate(_, _, _) ->
ok.
4 changes: 2 additions & 2 deletions test/http_handler_errors.erl
Expand Up @@ -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),
Expand Down Expand Up @@ -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.
4 changes: 2 additions & 2 deletions test/http_handler_init_shutdown.erl
Expand Up @@ -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">>,
Expand All @@ -13,5 +13,5 @@ handle(Req, State) ->
{ok, Req2} = cowboy_req:reply(200, [], "Hello world!", Req),
{ok, Req2, State}.

terminate(_Req, _State) ->
terminate(_, _, _) ->
ok.
4 changes: 2 additions & 2 deletions test/http_handler_long_polling.erl
Expand Up @@ -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),
Expand All @@ -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.

0 comments on commit 647e95a

Please sign in to comment.