[erlang-questions] uncatched cowboy websocket handler termination

pablo platt pablo.platt@REDACTED
Mon May 6 20:28:51 CEST 2013


Hi,

I'm using a cowboy websocket handler as a simple chat transport.
The websocket handler pass messages to a session gen_server.
The session gen_server traps exits and links to the websocket pid
so if the websocket pid dies, the session gen_server should terminate as
well.

I sometimes see sessions that thinks that the websocket pid is still alive
although checking with is_process_alive/1 shows it is not. That leads to
zombie sessions that accumulate over time.

Is there a race condition in my code or something else I'm missing in
catching the termination of the websocket pid?

Please see a simplified version of the websocket handler and the session
gen_server below.

Thanks

-module(my_session).
-behaviour(gen_server).

-export([handle_message/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).

-record(state, {ws}).

handle_message(undefined, Msg) ->
    case supervisor:start_child(my_session_sup, [Msg, self()]) of
        {error, Reason} -> {close, 1000, <<"">>};
        {ok, Pid} ->
            gen_server:call(Pid, {websocket, self()}),
            self() ! {session, Pid},
            {text, <<"ok">>}
    end;
handle_message(Pid, Msg) ->
    gen_server:call(Pid, {msg, Msg}).

init([Msg, WS]) ->
    case auth(Msg) of
        false -> {stop, normal};
        true ->
            process_flag(trap_exit,true),
            {ok, #state{})
    end.

handle_call({websocket, Pid}, _, State) ->
  link(Pid),
  {reply, ok, State#state{ws=Pid}};
handle_call({msg, Msg}, _, State) ->
    Resp = msg(Msg, State),
   {reply, Resp, State}.

handle_info({'EXIT', _Pid, _Reason}, State) ->
    {stop, normal, State}.

...


-module(ws_handler).
-behaviour(cowboy_websocket_handler).

-export([init/3]).
-export([websocket_init/3]).
-export([websocket_handle/3]).
-export([websocket_info/3]).
-export([websocket_terminate/3]).

-record(state, {tref, session}).

init({tcp, http}, _Req, _Opts) ->
    {upgrade, protocol, cowboy_websocket}.

websocket_init(_TransportName, Req, _Opts) ->
    Tref = erlang:send_after(10000, self(), timeout),
    {ok, Req, #state{tref=Tref}}.

websocket_handle({text, ""}, Req, State) ->
    erlang:cancel_timer(State#state.tref),
    Tref = erlang:send_after(10000, self(), timeout),
    {ok, Req, State#state{tref=Tref}};
websocket_handle({text, Msg}, Req, State) ->
    Tref = erlang:send_after(10000, self(), timeout),
    Resp = my_session:handle_message(State#state.session, Msg),
    {reply, Resp, Req, State#state{tref=Tref}};
websocket_handle(_Data, Req, State) ->
    {ok, Req, State}.

websocket_info({session, Pid}, Req, State) ->
    {ok, Req, State#state{session=Pid}};
websocket_info({terminate}, Req, State) ->
    {reply, {close, 1000, <<"">>}, Req, State};
websocket_info({timeout, _Ref, Msg}, Req, State) ->
    {shutdown, Req, State};
websocket_info(_Info, Req, State) ->
    {ok, Req, State}.

websocket_terminate(_Reason, _Req, _State) ->
    ok.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20130506/37088e54/attachment.htm>


More information about the erlang-questions mailing list