You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(ext/node): support ChildProcess.send with net.Server handles (#34948)
## Summary
`subprocess.send('server', server)` from `node:child_process`, where
`server` is an unlistened `net.createServer()`, threw:
```
error: Uncaught (in promise) Error: Not implemented: ChildProcess.send with non-TCP net.Server handle
```
An unlistened `net.Server` has a `null` underlying handle, and
`getIpcHandleInfo` only accepted TCP handles. This makes
`ChildProcess.send` with `net.Server` / `net.Socket` handles work in the
cases Node supports.
## Changes
- **Null handle → plain message.** An unlistened `net.Server` (or
detached `net.Socket`) has a null underlying handle. `getIpcHandleInfo`
now returns `null` in that case and the message is delivered without a
handle, matching Node's `if (!handle) message = message.msg`. This is
the exact case in the issue's repro.
- **Non-TCP (Pipe / unix-socket) handles.** `net.Server` and
`net.Socket` handles backed by a `Pipe` are now transferable. The IPC
message records `nativeKind` (`"tcp"` | `"pipe"`) so the receiver
reconstructs the correct wrap type.
- **`net.Server.prototype.listen` Pipe branch.** A `Pipe` wrap exposes
an `fd` getter, so without an explicit handle branch `listen(pipe)` fell
into the `options.fd` path and re-opened the already-owned fd, failing
with `EEXIST`.
- **`uv_pipe_open_listener`** (mirrors the existing
`uv_tcp_open_listener`). A pipe opened from an inherited *listening* fd
must not eagerly create an `AsyncFd`; otherwise `uv_pipe_listen`'s
`UnixListener` registration hits `epoll_ctl(EPOLL_CTL_ADD)` `EEXIST` on
the same fd. `PipeWrap::open` dispatches to it for server pipes.
## Tests
Adds `tests/specs/node/child_process_ipc_handle/`:
- `unlistened_server_main.mjs` — the issue's repro (send an unlistened
server; child receives the message with no handle).
- `pipe_server_main.mjs` — transfer a listening unix-socket server;
child reconstructs a working `net.Server`.
Both run under `json` and `advanced` serialization. Full suite (10
tests) passes:
`cargo test --test specs -- node::child_process_ipc_handle`.
Note: a full connect round-trip over a *transferred unix-socket* server
isn't asserted because `closeAfterSend` unlinks the socket file on the
parent's `server.close()`, which is inherent to unix sockets (TCP keeps
working because the bound port stays valid while the child's dup'd fd
listens).
Closes#34921Closesdenoland/divybot#502
Co-authored-by: divybot <divybot@users.noreply.github.com>
Co-authored-by: Divy Srivastava <me@littledivy.com>
0 commit comments