From 6402a4ff33c6e49f15980a164b2bc896cd745ec7 Mon Sep 17 00:00:00 2001 From: AlejandroF Date: Tue, 14 Jul 2026 06:45:28 -0300 Subject: [PATCH] fix: Accept() must not close the listener socket on success 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 #79 --- spec/accept_keeps_listener_open_spec.js | 68 +++++++++++++++++++++++++ src/node-srt.cc | 2 - 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 spec/accept_keeps_listener_open_spec.js diff --git a/spec/accept_keeps_listener_open_spec.js b/spec/accept_keeps_listener_open_spec.js new file mode 100644 index 0000000..6f1cc7b --- /dev/null +++ b/spec/accept_keeps_listener_open_spec.js @@ -0,0 +1,68 @@ +const { SRT, AsyncSRT } = require('../index.js'); + +// Regression test for https://github.com/Eyevinn/node-srt/issues/79 +// +// NodeSRT::Accept() used to call srt_close() on the listener socket on the +// success path, so the listener could only ever accept a single connection +// before going BROKEN. These tests accept several connections in a row from +// the same listener and assert it stays LISTENING throughout, for both the +// sync (SRT) and async (AsyncSRT) accept paths. + +describe("Accept() must not close the listener socket", () => { + const numConnections = 3; + + it("keeps a sync (SRT) listener open across multiple sequential accepts", async () => { + const srt = new SRT(); + const listenerFd = srt.createSocket(); + srt.bind(listenerFd, "0.0.0.0", 1350); + srt.listen(listenerFd, 8); + + // Drives the connecting side off the main thread so the blocking + // srt.accept() call below can be unblocked by an in-flight connect. + const asyncClient = new AsyncSRT(); + + for (let i = 0; i < numConnections; i++) { + const clientFd = await asyncClient.createSocket(); + const connectPromise = asyncClient.connect(clientFd, "127.0.0.1", 1350); + + const acceptedFd = srt.accept(listenerFd); + + expect(acceptedFd).not.toEqual(SRT.ERROR); + expect(srt.getSockState(listenerFd)).toEqual(SRT.SRTS_LISTENING); + + await connectPromise; + srt.close(acceptedFd); + await asyncClient.close(clientFd); + } + + srt.close(listenerFd); + await asyncClient.dispose(); + }); + + it("keeps an async (AsyncSRT) listener open across multiple sequential accepts", async () => { + const asyncServer = new AsyncSRT(); + const asyncClient = new AsyncSRT(); + + const listenerFd = await asyncServer.createSocket(); + await asyncServer.bind(listenerFd, "0.0.0.0", 1351); + await asyncServer.listen(listenerFd, 8); + + for (let i = 0; i < numConnections; i++) { + const clientFd = await asyncClient.createSocket(); + const connectPromise = asyncClient.connect(clientFd, "127.0.0.1", 1351); + + const acceptedFd = await asyncServer.accept(listenerFd); + + expect(acceptedFd).not.toEqual(SRT.ERROR); + expect(await asyncServer.getSockState(listenerFd)).toEqual(SRT.SRTS_LISTENING); + + await connectPromise; + await asyncServer.close(acceptedFd); + await asyncClient.close(clientFd); + } + + await asyncServer.close(listenerFd); + await asyncServer.dispose(); + await asyncClient.dispose(); + }); +}); diff --git a/src/node-srt.cc b/src/node-srt.cc index 4f8dd4f..019a18b 100644 --- a/src/node-srt.cc +++ b/src/node-srt.cc @@ -177,8 +177,6 @@ Napi::Value NodeSRT::Accept(const Napi::CallbackInfo& info) { Napi::Error::New(env, srt_getlasterror_str()).ThrowAsJavaScriptException(); return Napi::Number::New(env, SRT_ERROR); } - srt_close(socketValue); - socketValue = Napi::Number::New(env, SRT_INVALID_SOCK); return Napi::Number::New(env, their_fd); }