Repository navigation
fix(sio): send the buffered packets to polling clients when closing the server - #5602
Open
prakhar-tiwari wants to merge 2 commits into
Open
prakhar-tiwari wants to merge 2 commits into
prakhar-tiwari wants to merge 2 commits into
Conversation
Unlike close(), which discards the packets that are still buffered (see 923a12e), gracefulClose() waits for the write buffers to be drained, or for the given delay to expire, before closing the clients. New sessions are rejected in the meantime. With HTTP long-polling, the buffered packets can only be sent when the client sends its next GET request. Related: socketio#5088
…he server Previously, calling io.close() right after socket.disconnect(true) would discard the DISCONNECT packet for clients using HTTP long-polling, since engine.close() discards the write buffers and the HTTP server stops listening right away. The client would then receive a "transport error" and try to reconnect. io.close() now waits for engine.gracefulClose() before closing the HTTP server. Custom engines without this method are closed with close(). Related: socketio#5088
prakhar-tiwari
force-pushed
the
fix/graceful-close
branch
from
November 12, 2026 18:21
84e6614 to
18a33d1
Compare
1 of 2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The kind of change this PR does introduce
Current behavior
With HTTP long-polling, calling
io.close()right aftersocket.disconnect(true)drops the DISCONNECT packet.engine.close()discards the write buffers (on purpose since 923a12e), andio.close()then closes the HTTP server, so the client's next GET is refused. The client getstransport errorinstead ofio server disconnectand keeps trying to reconnect.New behavior
The PR has two commits, one per package:
feat(eio): newgracefulClose(timeout = 1000): Promise<void>onBaseServer. It waits until every client with a non-empty write buffer has emitteddrain(orclose), or untiltimeoutexpires, and then calls the existingclose(). New sessions are rejected in the meantime (BAD_REQUESTwithname: "SERVER_CLOSING").close()itself is unchanged, so the tests added in 923a12e still pass.fix(sio):io.close()awaitsengine.gracefulClose()before closing the HTTP server. Custom engines passed toio.bind()that don't have this method are closed withclose(), as before.If no client has buffered packets,
gracefulClose()doesn't wait at all. Otherwiseio.close()takes at most 1 second longer.Tests:
test/close.ts: polling client,disconnect(true)and thenio.close(). The client now getsio server disconnect(this test fails onmain). Also covers a custom engine withoutgracefulClose().test/server.js: the buffered packets are delivered withgracefulClose(), they are discarded once the timeout expires, and new sessions are rejected while waiting.Other information (e.g. related issues)
Related: #5088
Open questions:
io.close()always use the graceful path (current PR), or should it be opt-in, e.g.io.close({ timeout })?