fix: Accept() must not close the listener socket on success#81
Conversation
NodeSRT::Accept() called srt_close() on the listener socket on the success path, so the listener was destroyed after every accepted connection. In libsrt, srt_accept() returns the newly accepted socket and leaves the listener open so it can keep accepting - this diverged from that contract. A server could accept only one connection per listener; after the first accept() the listener went BROKEN then NONEXIST, and any further caller timed out. Working around it by re-creating the listener after every accept dropped its backlog of pending handshakes, so a burst of simultaneous callers lost all but one. Removes the success-path srt_close()/reset so Accept() only returns the accepted fd and leaves the listener open, matching libsrt semantics; the caller is responsible for closing the listener when done. The failure path is unchanged. Adds a regression test covering both the sync (SRT) and async (AsyncSRT) accept paths: it accepts several connections in a row from the same listener and asserts the listener stays LISTENING throughout. Fixes Eyevinn#79
|
Thank you for this fix, @cdgraff — the root-cause diagnosis is spot-on and the C++ change is exactly right: removing those two lines from the success path of There is one blocker before this can be merged: the Node.js 18.x CI job fails with a segmentation fault (exit code 139) during the jasmine test run. The build itself succeeds on 18.x; the crash occurs at runtime while running the test suite (approximately 20 tests in before the crash). Node 20 and 22 both pass all 23 specs cleanly. The segfault is most likely a Node 18-specific N-API or libuv threading interaction in the new test — particularly the pattern of mixing a blocking Could you please:
Once CI is green (or the 18.x failure is confirmed pre-existing and the maintainer decides to act on it), this fix is ready to merge. The fix itself is clearly correct and addresses a real and significant bug. node-srt maintenance bot — Tier B; awaiting human merge after CI resolution. |
|
Follow-up from the native build expert: the Node 18.x segfault has been investigated and is a pre-existing issue, not caused by this PR. Root cause of the Node 18 crash: The two-line deletion in Options to get CI green (choose one):
Option 1 is the quickest path and is defensible on its own merits. The maintainer will decide which approach to take — the fix itself is correct and ready. node-srt native build expert / maintenance bot |
Summary
NodeSRT::Accept()(src/node-srt.cc) calledsrt_close()on the listener socket on the success path, so the listener was destroyed after every accepted connection.In libsrt,
srt_accept()returns the newly accepted socket and leaves the listener open so it can keep accepting - this diverged from that contract:accept()the listener wentBROKENthenNONEXIST, and any further caller timed out.Fix
Removes the success-path
srt_close(socketValue)(and the following no-op reassignment), soAccept()only returns the accepted fd and leaves the listener open, matching libsrt semantics. The caller is responsible for closing the listener when done. The failure path is unchanged.Test
Adds
spec/accept_keeps_listener_open_spec.js, a regression test covering both the sync (SRT) and async (AsyncSRT) accept paths: it accepts several connections in a row from the same listener and asserts the listener staysLISTENINGthroughout.Verified locally:
src/node-srt.ccchange and rebuilding the addon reproduces the bug: the listener's state goesBROKENright after the first accept, and the next connection attempt errors out (invalid listener socket ID value).Fixes #79
cc @birme