1

I have implemented a simple appmod that handle WebSockets and echo back the messages. But how do I handle an ws.close(); from the JavaScript client? I have tried with the code below, but handle_message({close, Reason}) is never called and ws.onclose = function(evt) {} is never executed on the JavaScript client.

When I use the same JavaScript client code interacting with a node.js websocket, the client receives an onclose event immediately after ws.close();.

Here is the code for my simple appmod:

-module(mywebsocket).
-export([handle_message/1]).

handle_message({text, Message}) ->
    {reply, {text, <<Message/binary>>}};

handle_message({close, Reason}) ->
    io:format("User closed websocket.~n", []),
    {close, normal}.
Jonas
  • 121,568
  • 97
  • 310
  • 388

1 Answers1

2

Updated answer:

As of github commit 16834c, which will eventually be part of Yaws 1.93, Yaws passes a new callback to your WebSockets callback module when the client sends a close message. The callback is:

{close, Status, Reason}

where Status is either the close status sent by the client, or the numerical value 1000 (specified by RFC 6455 for a normal close) if the client didn't include a status value. Reason is a binary holding any optional reason string passed from the client; it will be an empty binary if the client sent no reason.

Your callback handler for a close message MUST return {close, CloseReason} where CloseReason is either the atom normal for a normal close (which results in the status code 1000 being returned to the client) or another legal numerical status code allowed by RFC 6455. Note that CloseReason is unrelated to any Reason value passed by the client. Technically CloseReason can also be any other Erlang term, in which case Yaws returns status 1000 and passes the term to erlang:exit/1 to exit the Erlang process handling the web socket, but based on RFC 6455 we suggest simply returning the atom normal for CloseReason in all cases.

Original answer, obsoleted by Yaws github commit 16834c:

Yaws never passes a {close, Reason} message to your callback module. Rather, {close, Reason} is a valid return value from handle_message/1 should your callback module decide it wants to close the ws socket.

I modified the websockets_example.yaws file shipped with Yaws (version 1.92) to call this._ws.close() in the client if the user enters the "bye" message on the web page, and added an alert to the _onclose function to show that the onclose event is triggered. In this case the alert occurred, I believe because the "bye" message causes the server to close the ws socket explicitly. But I then modified the example to call this._ws.close() in the client no matter what message the user enters, and in that case no alert for onclose occurred. In this case, a check with lsof showed the ws connection from the browser to Yaws was still present.

So, for now I believe you've hit a bug where the Yaws websockets support isn't detecting the client close and closing its end. I'll see if I can fix it.

Steve Vinoski
  • 19,847
  • 3
  • 31
  • 46
  • Thanks, this was informative. I use the client code I posted on [How to handle WebSocket messages in an appmod using Yaws?](http://stackoverflow.com/questions/9187809/how-to-handle-websocket-messages-in-an-appmod-using-yaws) where I have a button "disconnect". – Jonas Feb 10 '12 at 19:14
  • Using Chrome 17.0.963.46 I don't see any `close` message going from the browser to Yaws when `ws.close()` is called. I also tried a version 16 of Chrome and didn't see one with it either. What browser are you using? – Steve Vinoski Feb 10 '12 at 19:57
  • I use Chrome 16. The same client code worked with a node.js server. – Jonas Feb 10 '12 at 20:13
  • Please disregard my previous comment, the lack of the `close` message was just operator error on my part. – Steve Vinoski Feb 10 '12 at 20:15
  • 1
    I've verified it's a bug in Yaws. Fixing. – Steve Vinoski Feb 10 '12 at 20:51
  • Interestingly in Chrome 17.0.963.46 (and maybe other versions too) if you send call `ws.send()` followed by `ws.close()`, the `close` message goes across the wire before the sent message. – Steve Vinoski Feb 10 '12 at 22:23
  • Thankfully I was wrong about Chrome too, there is no message ordering problem. I missed the original message from browser to Yaws in tcpdump because Chrome sent it as a masked message, which is perfectly allowable by RFC 6455, but which made it such that I didn't see the text I was expecting to see in tcpdump. – Steve Vinoski Feb 12 '12 at 02:39
  • I can't find any websocket example in Yaws 1.95. And I can't load the module with `handle_message({close, Status, Reason})`. That is not documented on http://yaws.hyber.org/websockets.yaws either. Is this still an issue? I can't get this working with Yaws 1.95. – Jonas Nov 07 '12 at 11:41
  • There are several websocket examples in Yaws. [Here's one](http://yaws.hyber.org/websockets_example.yaws) you can run right from yaws.hyber.org, for example, and [here's the source code](https://github.com/klacke/yaws/blob/master/www/websockets_example.yaws) for that example. – Steve Vinoski Nov 08 '12 at 00:34
  • Also, see the [Yaws PDF documentation](http://yaws.hyber.org/yaws.pdf) for more information about websocket support in Yaws. And yes, it all works perfectly fine in Yaws 1.95. – Steve Vinoski Nov 08 '12 at 00:38