Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
598012a296 fix(node:http): emit close event on ServerResponse when client disconnects
Fixes #14697

ServerResponse was not emitting the "close" event when the client
disconnected. This was causing resource leaks in long-running connections
like server-sent events and live streaming.

The issue was in the NodeHTTPServerSocket's #onClose() method, which
cleaned up the request but did not call the socket's close callback.
This prevented onServerResponseClose from being invoked, which is
responsible for emitting the close event on the ServerResponse.

The fix adds a call to callCloseCallback(this) at the end of #onClose(),
ensuring that the close event propagates properly to the ServerResponse.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 22:43:59 +00:00
2 changed files with 61 additions and 0 deletions

View File

@@ -901,6 +901,9 @@ const NodeHTTPServerSocket = class Socket extends Duplex {
req.destroy();
}
}
// Emit close event on the socket to trigger onServerResponseClose
callCloseCallback(this);
}
#onCloseForDestroy(closeCallback) {
this.#onClose();

View File

@@ -0,0 +1,58 @@
// https://github.com/oven-sh/bun/issues/14697
// node:http ServerResponse should emit close event when client disconnects
import { expect, test } from "bun:test";
import { createServer } from "node:http";
test("ServerResponse emits close event when client disconnects", async () => {
const events: string[] = [];
let resolveTest: () => void;
const testPromise = new Promise<void>(resolve => {
resolveTest = resolve;
});
const server = createServer((req, res) => {
req.once("close", () => {
events.push("request-close");
});
res.once("close", () => {
events.push("response-close");
// Both events should have fired by now
resolveTest();
});
// Don't send any response, let the client disconnect
});
await new Promise<void>(resolve => {
server.listen(0, () => resolve());
});
const port = (server.address() as any).port;
// Connect and immediately disconnect
const socket = await Bun.connect({
hostname: "localhost",
port,
socket: {
open(socket) {
// Send a minimal HTTP request
socket.write("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
// Immediately close the connection
socket.end();
},
data() {},
error() {},
close() {},
},
});
// Wait for both close events to fire
await testPromise;
server.close();
// Both request and response should have emitted close events
expect(events).toContain("request-close");
expect(events).toContain("response-close");
});