From 8169ebf4fef8ff4618c25b585fce0aaa4d2d3bf1 Mon Sep 17 00:00:00 2001 From: Mika Kuns Date: Wed, 20 May 2026 16:23:48 +0200 Subject: [PATCH] refactor(server): clean up close listener and avoid writing 499 to dead socket Wrap the long-poll handler in try/finally to detach the req.raw close listener on every resolution path, and use reply.hijack() on the aborted branch so Fastify does not attempt to write to a socket that's already closed (which would otherwise emit per-disconnect log noise once the watcher relaunch loop is busy). Behavior on the wire is unchanged for the four documented status codes. Co-Authored-By: Claude Opus 4.7 (1M context) --- node/src/server.ts | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/node/src/server.ts b/node/src/server.ts index 2d59aba..c63c2e3 100644 --- a/node/src/server.ts +++ b/node/src/server.ts @@ -145,26 +145,30 @@ export async function buildServer(cfg: DaemonConfig, store: MailboxStore): Promi } const ac = new AbortController(); - req.raw.on("close", () => ac.abort()); + const onClose = (): void => ac.abort(); + req.raw.once("close", onClose); + try { + const result = await store.waitForMessage(name, timeoutS * 1000, ac.signal); - const result = await store.waitForMessage(name, timeoutS * 1000, ac.signal); - - if (result.kind === "message") { - const msg = rowToMessage(result.message); - reply.code(200); - return { ...msg, sentAt: msg.sentAt.toISOString() }; + if (result.kind === "message") { + const msg = rowToMessage(result.message); + reply.code(200); + return { ...msg, sentAt: msg.sentAt.toISOString() }; + } + if (result.kind === "renamed") { + reply.code(409); + return { reason: "renamed", to: result.to }; + } + if (result.kind === "timeout") { + reply.code(204); + return reply.send(); + } + // aborted — client gone; hand control to Fastify without writing to the dead socket. + reply.hijack(); + return; + } finally { + req.raw.off("close", onClose); } - if (result.kind === "renamed") { - reply.code(409); - return { reason: "renamed", to: result.to }; - } - if (result.kind === "timeout") { - reply.code(204); - return reply.send(); - } - // aborted — client gone, no response needed (Fastify will swallow). - reply.code(499); - return reply.send(); }, );