Should WebSocket.onclose be triggered by user navigation or refresh?

leggetter picture leggetter · Jun 10, 2012 · Viewed 15.4k times · Source

Part 1: Expected behaviour?

I'm seeing some inconsistent browser behaviour between Firefox and Chrome in relation to the onclose handler being called.

It seems that Chrome does not trigger an onclose if it was caused by a user page navigation/refresh. However, Firefox does trigger the onclose.

It seems to me that Firefox may be behaving correctly here:

When the WebSocket connection is closed, possibly cleanly, the user agent must create an event that uses the CloseEvent interface, with the event name close, which does not bubble, is not cancelable, has no default action, whose wasClean attribute is set to true if the connection closed cleanly and false otherwise, whose code attribute is set to the WebSocket connection close code, and whose reason attribute is set to the WebSocket connection close reason; and queue a task to first change the readyState attribute's value to CLOSED (3), and then dispatch the event at the WebSocket object.

Source: http://www.w3.org/TR/2011/WD-websockets-20110419/#closeWebSocket

Even though it can lead to some sneaky code/unexpected behaviour.

Can anybody confirm the expected behaviour?

Part 2: How to implement auto-reconnect?

If you have a library that auto-reconnects for the user how do you know if you should try to reconnect? Do you check the CloseEvent.wasClean property? I'm having to assume that 'clean' means that the close was supposed to happen through either an API call to WebSocket.close() or the server sending a close frame? If a network error causes the close I'm guessing the wasClean would be false?

In the Pusher JavaScript library we assumed (onclose -> waiting -> connecting) that a close should trigger a reconnect unless we are in a closing state - the developer has chosen to close the connection. It would appear that the socket.io client library makes the same assumption.

Based on this the Firefox onclose event caused by user navigation/refresh triggers an unwanted reconnection because neither library check the CloseEvent.wasClean property.

Example and Video

Here's an example that you can use to demonstrate the inconsistency: http://jsbin.com/awonod/7

Here's a video of me demonstrating the problem: http://www.screenr.com/vHn8 (it's late, ignore the couple of slip-ups :))

One point to note is that my hitting the Escape key could also be causing the WebSocket connection to close. However, if you watch closely or try for yourself you will see the close event being logged just before the page refreshes.

Answer

Optox picture Optox · Jun 18, 2013

The unexpected behavior is due to the way in which Firefox and Chrome handle the closing of a Websocket. When the page is refreshed, both browsers close the connection, however, Firefox will execute your onclose code, while chrome closes the connection and skips straight to re-loading the new page. So yes, I confirm this strange behavior.
Even stranger is the fact that, from my observations, calling websocket.close() in chrome will immediately close the connection and call the onclose function, while Firefox waits for a close message back from the server.

The wasClean property will be true if a close message was received from the server

If your library is auto-reconnecting without checking the wasClean property then this could cause a problem, as it tries to re-establish the connection as the page refreshes. You should consider not using the library for this and doing it manually, it should'nt be very hard, just call connect in the onclose function with an if statement making sure the onclean property is true. Or to be even more safe set a variable in onbeforeunload that prevents any new connection.

hope this helps!