diff --git a/src/bun.js/api/bun/socket/Listener.zig b/src/bun.js/api/bun/socket/Listener.zig index 46f5582c0c..49928762ee 100644 --- a/src/bun.js/api/bun/socket/Listener.zig +++ b/src/bun.js/api/bun/socket/Listener.zig @@ -437,7 +437,7 @@ pub fn stop(this: *Listener, _: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) fn doStop(this: *Listener, force_close: bool) void { if (this.listener == .none) return; const listener = this.listener; - + // Unlink Unix socket file BEFORE closing the socket to avoid race conditions // (same approach as libuv - see uv__pipe_close in deps/uv/src/unix/pipe.c) if (!Environment.isWindows) { @@ -453,7 +453,7 @@ fn doStop(this: *Listener, force_close: bool) void { } } } - + defer switch (listener) { .uws => |socket| socket.close(this.ssl), .namedPipe => |namedPipe| if (Environment.isWindows) namedPipe.closePipeAndDeinit(), diff --git a/test/js/bun/net/unix-socket-cleanup.test.ts b/test/js/bun/net/unix-socket-cleanup.test.ts index d0ba5b11f0..e71980e783 100644 --- a/test/js/bun/net/unix-socket-cleanup.test.ts +++ b/test/js/bun/net/unix-socket-cleanup.test.ts @@ -1,19 +1,19 @@ -import { test, expect } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; -import { existsSync, unlinkSync } from "node:fs"; +import { expect, test } from "bun:test"; +import { isWindows } from "harness"; import { randomBytes } from "node:crypto"; -import { join } from "node:path"; +import { existsSync, unlinkSync } from "node:fs"; import { tmpdir } from "node:os"; +import { join } from "node:path"; test.skipIf(isWindows)("Unix domain socket file should be cleaned up when listener.stop() is called", () => { // Generate a random socket path to avoid conflicts const socketPath = join(tmpdir(), `bun_test_${randomBytes(8).toString("hex")}.sock`); - + // Clean up any existing socket file if (existsSync(socketPath)) { unlinkSync(socketPath); } - + // Create a Unix socket listener const listener = Bun.listen({ unix: socketPath, @@ -23,13 +23,13 @@ test.skipIf(isWindows)("Unix domain socket file should be cleaned up when listen close(socket) {}, }, }); - + // Verify the socket file was created expect(existsSync(socketPath)).toBe(true); - + // Stop the listener listener.stop(); - + // Verify the socket file was cleaned up expect(existsSync(socketPath)).toBe(false); }); @@ -37,12 +37,12 @@ test.skipIf(isWindows)("Unix domain socket file should be cleaned up when listen test.skipIf(isWindows)("Unix domain socket file should be cleaned up when listener.stop(true) is called", () => { // Generate a random socket path const socketPath = join(tmpdir(), `bun_test_${randomBytes(8).toString("hex")}.sock`); - + // Clean up any existing socket file if (existsSync(socketPath)) { unlinkSync(socketPath); } - + // Create a Unix socket listener const listener = Bun.listen({ unix: socketPath, @@ -52,13 +52,13 @@ test.skipIf(isWindows)("Unix domain socket file should be cleaned up when listen close(socket) {}, }, }); - + // Verify the socket file was created expect(existsSync(socketPath)).toBe(true); - + // Stop the listener with force=true listener.stop(true); - + // Verify the socket file was cleaned up expect(existsSync(socketPath)).toBe(false); }); @@ -66,7 +66,7 @@ test.skipIf(isWindows)("Unix domain socket file should be cleaned up when listen test.skipIf(isWindows)("Abstract Unix domain sockets should not leave files (start with null byte)", () => { // Abstract sockets start with a null byte and don't create filesystem entries const abstractPath = "\0bun_test_abstract_" + randomBytes(8).toString("hex"); - + // Create an abstract Unix socket listener const listener = Bun.listen({ unix: abstractPath, @@ -76,11 +76,11 @@ test.skipIf(isWindows)("Abstract Unix domain sockets should not leave files (sta close(socket) {}, }, }); - + // Abstract sockets shouldn't create a file in the filesystem // We can't really check this, but we can verify stop() doesn't crash listener.stop(); - + // Test passes if no crash occurs expect(true).toBe(true); }); @@ -88,16 +88,16 @@ test.skipIf(isWindows)("Abstract Unix domain sockets should not leave files (sta test.skipIf(isWindows)("Multiple Unix sockets cleanup", () => { const sockets = []; const paths = []; - + // Create multiple Unix socket listeners for (let i = 0; i < 3; i++) { const socketPath = join(tmpdir(), `bun_test_multi_${i}_${randomBytes(4).toString("hex")}.sock`); - + // Clean up any existing socket file if (existsSync(socketPath)) { unlinkSync(socketPath); } - + paths.push(socketPath); sockets.push( Bun.listen({ @@ -107,18 +107,18 @@ test.skipIf(isWindows)("Multiple Unix sockets cleanup", () => { data(socket, data) {}, close(socket) {}, }, - }) + }), ); - + // Verify the socket file was created expect(existsSync(socketPath)).toBe(true); } - + // Stop all listeners for (const listener of sockets) { listener.stop(); } - + // Verify all socket files were cleaned up for (const path of paths) { expect(existsSync(path)).toBe(false); @@ -127,15 +127,15 @@ test.skipIf(isWindows)("Multiple Unix sockets cleanup", () => { test.skipIf(isWindows)("Unix socket cleanup with active connections", async () => { const socketPath = join(tmpdir(), `bun_test_active_${randomBytes(8).toString("hex")}.sock`); - + // Clean up any existing socket file if (existsSync(socketPath)) { unlinkSync(socketPath); } - + let serverSocket = null; let connectionReceived = false; - + // Create a Unix socket listener const listener = Bun.listen({ unix: socketPath, @@ -150,10 +150,10 @@ test.skipIf(isWindows)("Unix socket cleanup with active connections", async () = close(socket) {}, }, }); - + // Verify the socket file was created expect(existsSync(socketPath)).toBe(true); - + // Connect to the socket const client = await Bun.connect({ unix: socketPath, @@ -163,17 +163,17 @@ test.skipIf(isWindows)("Unix socket cleanup with active connections", async () = close(socket) {}, }, }); - + // Wait for connection to be established await Bun.sleep(10); expect(connectionReceived).toBe(true); - + // Stop the listener with force=true (should close all connections) listener.stop(true); - + // Give some time for cleanup await Bun.sleep(10); - + // Verify the socket file was cleaned up even with active connections expect(existsSync(socketPath)).toBe(false); -}); \ No newline at end of file +});