mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
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>
This commit is contained in:
@@ -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();
|
||||
|
||||
58
test/regression/issue/14697.test.ts
Normal file
58
test/regression/issue/14697.test.ts
Normal 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");
|
||||
});
|
||||
Reference in New Issue
Block a user