[erlang-questions] uncatched cowboy websocket handler termination

pablo platt pablo.platt@REDACTED
Sun May 12 18:17:17 CEST 2013


In my tests the session thinks that the websocket pid is alive although it
is not.
How come it didn't get the exit message?

I'll use websocket_terminate/3 as a last resort but I want to understand
why I'm not getting the exit message first.
Is it possible that cowboy somehow prevent sending this message?


On Sun, May 12, 2013 at 6:42 PM, fxmy wang <fxmywc@REDACTED> wrote:

> >I don't know enough in websocket_init/3 to create the session. I need the
> userid and auth token which are sent with the first websocket packet.
> The *Req* in *init({tcp, http}, Req, Opts)* and*websocket_init(_TransportName, Req, _Opts)
> *contains a lot of things including User-Agents , Cookies, RequestedUrls
> etc..
> You might find things you need there.
> And if unlucky, you can just let the browser to send informations
> explicitely after websocket connection is set up , then handled in *websocket_handle({text,
> Msg}, Req, State)*
> *
> *
> > If the session is linked to the websocket pid, Is it possible that
> websocket_terminate/3 will be called but the session won't get an exit
> message? *
> *
> Once the websocket connection shuts down ,  *websocket_terminate/3* will
> be called , according to here<http://ninenines.eu/docs/en/cowboy/HEAD/guide/ws_handlers>
> .
> And maybe cowboy just keeps this pid alive after called *
> websocket_terminate/3* for some other use.  I can`t really be sure cause
> I`m kind of lost in cowboy`s source code :(   .    This is one possibility
> that your linked session process does not get a EXIT message.
>
> And , have you considering add a call in *websocket_terminate/3* like *my_session:kill_session(
> self(), ...) * to explicitly end your session?
>
> On Sunday, May 12, 2013 11:08:48 PM UTC+8, pablo platt wrote:
>
>> I don't know enough in websocket_init/3 to create the session. I need the
>> userid and auth token which are sent with the first websocket packet.
>>
>> If the session is linked to the websocket pid, Is it possible that
>> websocket_terminate/3 will be called but the session won't get an exit
>> message?
>>
>> From my code, I thought that the only options are:
>> - The websocket pid is dead when trying to link to it so the session will
>> crash.
>> - A link is created. If the websocket pid dies, the session will get an
>> exit message.
>>
>>
>> Is there a race condition in my implementation?
>>
>>
>> On Sun, May 12, 2013 at 6:01 PM, fxmy wang <fxm...@REDACTED> wrote:
>>
>>> hi pablo,
>>>     When a websocket connection shuts down, the function *ws_handler:websocket_terminate(_Reason,
>>> _Req, _State) *will be called.
>>>     So you probably should handle you session-ending through this
>>> function call
>>>
>>>     And more generally, it's a good way to initialize your session
>>> through *websocket_init(_TransportName, Req, _Opts) *and do the clean
>>> up work in  *ws_handler:websocket_terminate(_Reason, _Req, _State) *
>>>
>>> On Tuesday, May 7, 2013 2:28:51 AM UTC+8, pablo platt wrote:
>>>>
>>>> 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/20130512/13c59c5f/attachment.htm>


More information about the erlang-questions mailing list