-
Notifications
You must be signed in to change notification settings - Fork 566
Description
Describe the bug
When socket.close() is called, if there are pending packets in the writeBuffer, the transport close is postponed until a "drain" event for the socket is emitted. On socket.flush() the pending packages in the writeBuffer are sent via transport.send(wbuf), and immediately after this line, the "drain" event is emitted synchronously and closes the transport. The problem is that in the case of websocket transport, the transport.send function is asynchronous(it sends each packet and waits for confirmation that the packet was sent successfully, before sending the 2nd packet, and so on), therefore if the buffer consists of 4 packets, 1 will be delivered to the client, but after that, the transport will be closed and the rest of the packets won't be sent.
To Reproduce
- Make multiple(more than 2) synchronous calls of socket.write() with some data.
- Call socket.close() synchronously after the previous write calls.
Engine.IO server version: 6.X.X
Server
socket.write("2[\"force_disconnect\",\"multiple_clients_connected\"]"); // initiates a buffer of 1 packet; sent successfully and received by client
socket.write("1,"); // initiates a buffer of 3 packets; sent successfully and received by client
socket.write("1/foo,"); // part of the 3 packets buffer; not sent due to transport close
socket.write("1/bar,"); // part of the 3 packets buffer; not sent due to transport close
socket.close();Expected behavior
The socket waits for all of the messages in the buffer to be sent before closing the transport.
Additional context
This bug is breaking the Socket.IO v4 library. Users can connect to multiple "namespaces". A user's connection can be closed by the server by calling sioSocket.disconnect(true). This essentially sends a disconnect packet to all the "namespaces" a user has been connected to, so that the client knows, that it's being disconnected before the transport is closed, and it's an intentional disconnect by the server. In that case, the client shouldn't attempt to reconnect automatically because it's not an unexpected transport close. The example provided above is the exact order of calls that the Socket.IO library executes when disconnecting a client who was connected to namespaces "foo" and "bar". From these 4 packets in the example, after sending the first 2 messages, the transport is being closed by the socket instance, which prevents the 2 remaining messages from being sent, preventing the sioSocket.disconnect(true) function from working properly, and ultimately causing a lot of clients-unintentionally-reconnecting-over-and-over-again problems with the Socket.IO library.