engine.io

Is it appropriate to respond to an EngineIO ping before completing the handshake?


I'm implementing a Socket.IO client to connect to a server running on Heroku, both running Engine.IO v4. Sometimes the server takes a very long time to connect, 20-70 seconds. When this takes longer than the server pingInterval, the server sends an EngineIO PING(2) packet as the first packet, before the CONNECT(0) packet. The EngineIO documentation states "Once the handshake is completed, a heartbeat mechanism is started to check the liveness of the connection", but I'm getting it before the handshake is finished.

Normal flow:

Client to Server: establish websocket connection
Server to Client: EngineIO 0(CONNECT), inform client pingInterval is 25 seconds.
#About 25 seconds later(packets may be exchanged)
Server to Client: EngineIO 2(PING)
Client to Server: EngineIO 3(PONG)

Abnormal flow 1 - slow connection closes:

Client to Server: establish websocket connection
#About 25 seconds delay here
Server to Client: EngineIO 2(PING)
Client to Server: EngineIO 3(PONG)
Server closes connection.

Sometimes, if the CONNECT comes around the same time as the PONG, the server will keep the connection open and function, I suspect this is the server queueing its CONNECT before it receives the PONG, so PONG doesn't appear out of order to the server. We're looking into the delayed server response separately. What is the appropriate Engine.IO protocol response to a PING received before CONNECT? It seems like sending PONG before CONNECT causes the server to close the connection, should I delay the PONG until after CONNECT?


Solution

  • This was an X-Y problem in the client, though I didn't know it.

    I suspected that the server was seeing PING->PONG->CONNECT and that was causing it to close the connection, however, we were able to enable Engine.IO logging on the server to reveal that the server's Socket.IO layer was actually closing the connection because it had not received a namespace from the client.

    I was only sending Socket.IO Connect(EIO ID=4, SIO ID=0) in response to an Engine.IO Open(EIO id=0), which I had not received.

    We added more logging and confirmed that Engine.IO was sending the Open packet on the server, but the client was dropping it due to a bug in the ESP32 websocket transport, which does not support HTTP responses, only WS responses. When we saw this issue, the Engine.IO Open response was coming back over HTTP just before the switchover to WebSocket protocol, so the ESP WS client was dropping it.