Skip to content

fix(client): discard timed-out packets from the Engine.IO buffer - #5560

Open
dyk1454683243-sudo wants to merge 1 commit into
socketio:mainfrom
dyk1454683243-sudo:cursor/fix-timed-out-buffered-event-4eb2
Open

dyk1454683243-sudo wants to merge 1 commit into
socketio:mainfrom
dyk1454683243-sudo:cursor/fix-timed-out-buffered-event-4eb2

Conversation

@dyk1454683243-sudo

Copy link
Copy Markdown

The kind of change this PR does introduce

  • a bug fix
  • a new feature
  • an update to the documentation
  • a code change that improves performance
  • other

Current behavior

An event emitted with timeout() while the connection is still considered open can already be sitting in the Engine.IO write buffer (transport temporarily unwritable, for example after a network disruption that has not closed the socket yet).

When the acknowledgement times out, Socket.IO removes the packet from its own sendBuffer, but the encoded Engine.IO packet remains queued. Restoring the network / making the transport writable again flushes that timed-out event to the server.

This is #4318.

New behavior

On acknowledgement timeout the client also removes every Engine.IO packet created from that Socket.IO event, as long as none of them has been handed to the transport yet.

If any encoded packet is already part of the current flush batch, all related packets are left intact so a binary event cannot be partially transmitted.

Neighboring in-flight events are still flushed normally.

Other information (e.g. related issues)

Fixes #4318.

Regression coverage:

  • Engine.IO write-buffer removal, including the partially-flushed safety boundary
  • Socket.IO timeout: timed-out event is not delivered after the transport becomes writable again (the original one/two/three scenario)
  • Socket.IO timeout: binary events that encode to multiple Engine.IO packets

When an acknowledgement times out, Socket.IO already drops the packet from
its own send buffer. If the socket is still considered connected, the event
has already been written to the Engine.IO write buffer and was later flushed
once the transport became writable again.

Remove those unflushed Engine.IO packets on timeout, but keep a multi-packet
payload intact if any fragment was already handed to the transport.

Fixes socketio#4318

Co-authored-by: David <dyk1454683243-sudo@users.noreply.github.com>

@darrachequesne darrachequesne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Thanks a lot for the pull request, please find my comments below.

return;
}

for (let i = 0; i < this._prevBufferLen; i++) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a edge case here, in case of a Socket.IO binary packet sent as multiple Engine.IO packets:

  • the first packet is sent successfully
  • "drain" is emitted and _prevBufferLen is set to 0
  • the attachments remains in the Engine.IO buffer and are then removed by _removeFromWriteBuffer()
  • the server-side Socket.IO parser is left waiting for attachments that will never arrive

@darrachequesne

Copy link
Copy Markdown
Member

Alternative fix to #4318:

  • Socket.IO assigns discard metadata to the Engine.IO packets generated for a timeout-based emit.
  • All Engine.IO packets belonging to the same logical Socket.IO packet share a discardGroupId.
  • The group has a discardAfter timestamp.
  • Before Engine.IO flushes its write buffer, it drops expired groups atomically, as long as no fragment from that group has already been handed to the transport.

Something like:

interface WriteOptions {
  compress?: boolean;
  discardGroupId?: number;
  discardAfter?: number;
}

Which would keep Engine.IO as the owner of its own write buffer and avoid Socket.IO calling into a new Engine.IO buffer API.

What do you think?

Related: #5546

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Timed out event is still buffered

3 participants