node:net: fix handle leak (#22913)

This commit is contained in:
Meghan Denny
2025-09-23 21:02:34 -08:00
committed by GitHub
parent 1a23797e82
commit ebe2e9da14
3 changed files with 79 additions and 2 deletions

View File

@@ -80,7 +80,7 @@ function getNodeParallelTestTimeout(testPath) {
if (testPath.includes("test-dns")) {
return 90_000;
}
return 10_000;
return 20_000;
}
process.on("SIGTRAP", () => {

View File

@@ -111,7 +111,7 @@ pub fn NewSocket(comptime ssl: bool) type {
pub fn doConnect(this: *This, connection: Listener.UnixOrHost) !void {
bun.assert(this.socket_context != null);
this.ref();
errdefer this.deref();
defer this.deref();
switch (connection) {
.host => |c| {

View File

@@ -0,0 +1,77 @@
import { expect } from "bun:test";
import { isASAN, isWindows } from "harness";
import * as net from "node:net";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { setTimeout } from "node:timers/promises";
const listen_path = join(tmpdir(), "test-net-successful-connection-handle-leak.sock");
const { promise, resolve } = Promise.withResolvers();
const server = net
.createServer()
.listen(listen_path)
.on("listening", () => resolve());
await promise;
const address = server.address();
console.log("server address", address);
let started;
started = 0;
while (started < 50_000) {
const promises: Promise<void>[] = [];
for (let i = 0; i < 100; i++) {
const { promise, resolve, reject } = Promise.withResolvers<void>();
const socket = net
.connect({ path: listen_path })
.on("connect", () => {
socket.on("close", () => resolve());
socket.end();
})
.on("error", e => {
reject(e);
});
promises.push(promise);
started++;
}
await Promise.all(promises);
await setTimeout(1);
console.log(`Completed ${started} connections. RSS: ${(process.memoryUsage.rss() / 1024 / 1024) | 0} MB`);
}
Bun.gc(true);
const warmup_rss = process.memoryUsage.rss();
started = 0;
while (started < 100_000) {
const promises: Promise<void>[] = [];
for (let i = 0; i < 100; i++) {
const { promise, resolve, reject } = Promise.withResolvers<void>();
const socket = net
.connect({ path: listen_path })
.on("connect", () => {
socket.on("close", () => resolve());
socket.end();
})
.on("error", e => {
reject(e);
});
promises.push(promise);
started++;
}
await Promise.all(promises);
await setTimeout(1);
console.log(`Completed ${started} connections. RSS: ${(process.memoryUsage.rss() / 1024 / 1024) | 0} MB`);
}
const post_rss = process.memoryUsage.rss();
server.close();
let margin = 1024 * 1024 * 15;
if (isWindows) margin = 1024 * 1024 * 20;
if (isASAN) margin = 1024 * 1024 * 60;
expect(post_rss - warmup_rss).toBeLessThan(margin);