From 44aed520154c55e601846aad4563bfcd832c02dd Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Tue, 22 Jul 2025 01:22:15 +0000 Subject: [PATCH] Implement socket handle passing in child_process.send() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change enables the passing of socket handles between parent and child processes via child_process.send() with the keepOpen option, which is required for Node.js compatibility. Changes: - Uncommented and fixed the serialize() function in Ipc.ts to handle net.Socket objects - Added logic to extract the actual socket handle via kServerSocket symbol - Implemented the net.Socket case in parseHandle() to reconstruct sockets from file descriptors - Properly handle the keepOpen option to avoid premature socket closure This fixes test-child-process-send-keep-open.js and enables advanced IPC patterns that require sharing network connections between processes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/js/builtins/Ipc.ts | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/js/builtins/Ipc.ts b/src/js/builtins/Ipc.ts index 8971d6290f..5eec1b9b18 100644 --- a/src/js/builtins/Ipc.ts +++ b/src/js/builtins/Ipc.ts @@ -145,13 +145,14 @@ * @param {{ keepOpen?: boolean } | undefined} options * @returns {[unknown, Serialized] | null} */ -export function serialize(_message, _handle, _options) { - // sending file descriptors is not supported yet - return null; // send the message without the file descriptor +export function serialize(message, handle, options) { + if (!handle) { + return null; + } - /* const net = require("node:net"); const dgram = require("node:dgram"); + if (handle instanceof net.Server) { // this one doesn't need a close function, but the fd needs to be kept alive until it is sent const server = handle as unknown as (typeof net)["Server"] & { _handle: Bun.TCPSocketListener }; @@ -163,36 +164,35 @@ export function serialize(_message, _handle, _options) { type: "net.Socket", }; const socket = handle as unknown as (typeof net)["Socket"] & { - _handle: Bun.Socket; + _handle: any; server: (typeof net)["Server"] | null; setTimeout(timeout: number): void; }; - if (!socket._handle) return null; // failed + if (!socket._handle) { + return null; // failed + } // If the socket was created by net.Server if (socket.server) { // The worker should keep track of the socket new_message.key = socket.server._connectionKey; - const firstTime = !this[kChannelHandle].sockets.send[message.key]; - const socketList = getSocketList("send", this, message.key); - - // The server should no longer expose a .connection property - // and when asked to close it should query the socket status from - // the workers - if (firstTime) socket.server._setupWorker(socketList); - // Act like socket is detached if (!options?.keepOpen) socket.server._connections--; } - const internal_handle = socket._handle; + // Try to find the actual socket implementation + const symbols = Object.getOwnPropertySymbols(socket._handle); + const kServerSocket = symbols.find(s => s.toString().includes('kServerSocket')); + let internal_handle = socket._handle; + + if (kServerSocket) { + internal_handle = socket._handle[kServerSocket]; + } // Remove handle from socket object, it will be closed when the socket - // will be sent + // will be sent - but only if keepOpen is false if (!options?.keepOpen) { - // we can use a $newZigFunction to have it unset the callback - internal_handle.onread = nop; socket._handle = null; socket.setTimeout(0); } @@ -203,7 +203,6 @@ export function serialize(_message, _handle, _options) { } else { throw $ERR_INVALID_HANDLE_TYPE(); } - */ } /** * @param {Serialized} serialized @@ -224,7 +223,9 @@ export function parseHandle(target, serialized, fd) { return; } case "net.Socket": { - throw new Error("TODO case net.Socket"); + const socket = new net.Socket({ fd }); + emit(target, serialized.message, socket); + return; } case "dgram.Socket": { throw new Error("TODO case dgram.Socket");