fdimuccio / play2-sockjs

A SockJS server implementation for Play Framework.
Apache License 2.0
62 stars 11 forks source link

Detect ungraceful client disconnects #18

Open TheChifer opened 8 years ago

TheChifer commented 8 years ago

Play2 websockets do not detect ungracefully disconnected clients, i.e pull the network cable in the client machine server is not notified on the connection loss.

https://github.com/playframework/playframework/issues/1388 http://stackoverflow.com/questions/21654421/websocket-connection-status-on-server http://stackoverflow.com/questions/12681565/websocket-hard-disconnect-not-apparent

Solutions that are recommended talks about using heartbeats to detect stale connections. As we are using Sockjs api, is this a feature play2-sockjs can support?. Undetected failures results is stale connection & a major problem in implementing a reconnect.

fdimuccio commented 8 years ago

Yes, as stated in the link that you posted the only way to detect a broken TCP connection is trying to send some bytes. In SockJSSettings there is an option with which you can tune the heartbeat interval, it serves the purpose but there is an issue.

I tested it on Play 2.5 but it doesn't work, Play just keep sending data without detecting that the other end went away, I reproduced it by writing this simple action that returns a chunked response:

def test = Action {
    val source = 
      Source.tick(200.millis, 200.millis, "tick")
        .map(s => {println(System.currentTimeMillis()); s})
        .watchTermination() { (_, r) =>
          r.onComplete(println)
        }
    Ok.chunked(source)
  }

If you run it and disconnect the cable the source will never ends. Since play2-sockjs relies on the underlying Play implementation there is not much that I can do. I smell a bug here, but before reporting it I want to do further investigation.

fdimuccio commented 8 years ago

By the way it could be fixed by implementing a ping/pong style heartbeat, but unluckily SockJS protocol does not support it natively, so you need to implement it at higher level.

fdimuccio commented 8 years ago

I tested it further and it does detect disconnect, but it does after enough data has been transferred, so I guess there is some internal buffer and timeout that control this.

As I said before one simple way to implement it reliably is by sending an ping message and waiting for the pong, if the response doesn't come in time then you detected an unreliable connection.

I'll keep the issue open for further investigations.