Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
183 commits
Select commit Hold shift + click to select a range
bb794d5
cluster: implement real round-robin + shared-handle semantics; sync t…
cirospaciari Jun 4, 2026
7afbee6
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 4, 2026
1f5f6c5
cluster: fix fd handoff protocol, compile errors, and review feedback
cirospaciari Jun 5, 2026
3b68223
cluster: degrade gracefully on Windows where handles cannot cross pro…
cirospaciari Jun 5, 2026
842c9f1
cluster: fix remaining Windows error-code paths; skip two more handof…
cirospaciari Jun 5, 2026
fde299e
cluster: address review feedback - TLS via shared handles, error fide…
cirospaciari Jun 5, 2026
b717031
cluster: implement cross-process socket handle transfer on Windows
cirospaciari Jun 5, 2026
82e861b
cluster: fix Windows fd tagging in connect and wildcard bind preference
cirospaciari Jun 5, 2026
13726f1
cluster: address review of the Windows handle-transfer code
cirospaciari Jun 5, 2026
0d78455
cluster: don't run the libuv pipe sniffer on raw Windows SOCKETs
cirospaciari Jun 5, 2026
5f3bd71
cluster: fix Windows transfer regressions found by second CI execution
cirospaciari Jun 6, 2026
0ec4eab
cluster: skip inspect-brk test on Windows (extra stdio pipes unsuppor…
cirospaciari Jun 6, 2026
daf8761
cluster: fix fdIsRawSocket flag being treated as set when false
cirospaciari Jun 6, 2026
5c42a71
cluster: address handle-transfer review round and clippy lints
cirospaciari Jun 6, 2026
181458e
cluster: reclaim the in-flight connection when a newconn send fails o…
cirospaciari Jun 6, 2026
28ff117
cluster: handle export failure on the shared-handle reply and return …
cirospaciari Jun 6, 2026
27f0ed1
cluster: emit internalMessage on process for worker control messages
cirospaciari Jun 6, 2026
448a3c8
ipc: make the handle retransmission budget per-message
cirospaciari Jun 6, 2026
fd2b2d1
dgram: restore healthCheck on closed sockets; skip cluster-dgram-1 on…
cirospaciari Jun 6, 2026
049135f
ipc: rename unused serialize() options param to _options to satisfy lint
cirospaciari Jun 6, 2026
08c6344
cluster: reject TLS listens on round-robin keys; drop bogus :-1 in pi…
cirospaciari Jun 6, 2026
ea27d22
cluster: unlink shared pipe paths on teardown; bind :: when no host i…
cirospaciari Jun 6, 2026
d712218
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 6, 2026
faa3f18
test: share the cluster pipe name with the worker through the fork env
cirospaciari Jun 6, 2026
1128d4c
test: skip cluster-dgram-bind-fd on macOS like its dgram-1 sibling
cirospaciari Jun 6, 2026
a9a93fb
Merge branch 'main' into ciro/cluster-tests-v26
robobun Jun 10, 2026
ab6d5dc
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Jun 30, 2026
1844b17
ipc: node wire format + own the in-flight fd for process.send(message…
cirospaciari Jun 30, 2026
4cdc919
cluster: propagate server socket options to RR accepts; chmod SCHED_N…
cirospaciari Jun 30, 2026
3e230b3
cluster: forward readableAll/writableAll to the primary's listen query
cirospaciari Jul 1, 2026
8f30ea4
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Jul 2, 2026
30c4489
ipc: only attach the SCM_RIGHTS fd to a message's first write
cirospaciari Jul 2, 2026
90d9e6c
usockets: document that the UDP recv throttle covers every adopted fd
cirospaciari Jul 2, 2026
505f5ad
net: honor allowHalfOpen for fd-adopted sockets
cirospaciari Jul 2, 2026
54c22e2
ipc: scope insert_message's queue-head assertion to the sender-side c…
cirospaciari Jul 2, 2026
36d1a86
webview: pass the new us_socket_from_fd options argument
cirospaciari Jul 2, 2026
35460f8
http: report node's cluster listening payload for unspecified-host an…
cirospaciari Jul 6, 2026
f6022cc
node:tls: sync the test suite to Node v26.3.0 and fix the gaps it sur…
cirospaciari Jun 29, 2026
b93229c
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 7, 2026
100afc9
node:tls: give the exported SecureContext constructor its own SSL_CTX…
cirospaciari Jul 7, 2026
cc40e66
node:net: defer abandoned-socket EOF teardown; keep close(hadError) N…
alii Jul 7, 2026
613f6e8
node:tls: constructor authorizationError=null; build _sharedCreds onc…
alii Jul 7, 2026
da98a0d
tls: auto-chain regardless of user ca; map invalid_crl to ERR_CRYPTO_…
alii Jul 7, 2026
88610f8
node:tls,node:net: block prototype-pollution of TLS verification; mat…
cirospaciari Jul 7, 2026
8c24159
tls: apply SSL_VERIFY_NONE per socket on a !requestCert server adopt
cirospaciari Jul 7, 2026
d694fa7
cluster: address correctness/race findings from PR review
alii Jul 7, 2026
85073ae
cluster: layering, protocol, and coverage follow-ups from PR review
alii Jul 8, 2026
781ffe4
tls: setMaxSendFragment returns false out of OpenSSL's range instead …
cirospaciari Jul 8, 2026
068eb3c
net: only decrement a server's connection count for sockets it counted
cirospaciari Jul 8, 2026
1445535
ci: re-point the tls SSL_CTX LSan suppression at its post-#29932 symbols
cirospaciari Jul 8, 2026
12282c5
Merge branch 'main' into ciro/cluster-tests-v26
alii Jul 8, 2026
b35afb0
cluster: route child send() through process.send so a monkey-patched …
alii Jul 8, 2026
2561dc1
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 8, 2026
d9dd85a
ci: rebuild — the darwin-26-aarch64 agent pool recovered, previous bu…
cirospaciari Jul 8, 2026
9d11d99
node:net,node:tls: honor onread's false with the true sentinel; fail …
cirospaciari Jul 8, 2026
ee269da
tls: use BoringSSL's handshake-time auto-chain instead of an eager st…
cirospaciari Jul 8, 2026
b122868
tls: pin the mirrored BoringSSL group table with a test and an upgrad…
cirospaciari Jul 8, 2026
2316052
node:net: read() on a paused onread socket still redelivers the decli…
cirospaciari Jul 8, 2026
3d1e593
node:tls: surface a natively-rejected key from the STARTTLS wrap on t…
cirospaciari Jul 8, 2026
b82443b
cluster: make queued RR handle fd live and self-evicting; dup on send
alii Jul 10, 2026
3be0485
cluster: address second-pass review findings
alii Jul 10, 2026
3f30e8e
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 10, 2026
3d3c70f
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 10, 2026
5cb13c4
tls: a bare secureContext server upgrade keeps the context's verify mode
cirospaciari Jul 10, 2026
7c3ede6
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 10, 2026
74d2a9e
node:tls: address review — shared symbols, native CA parsing, minimal…
cirospaciari Jul 11, 2026
9c12148
tls: release the listener's SSL_CTX at close() and drop the LSan supp…
cirospaciari Jul 11, 2026
15a4c4f
Merge commit 'refs/tmp/mainnow' into ciro/tls2630-merge
cirospaciari Jul 11, 2026
893d8aa
test(node/tls): reword three upstream comment markers in vendored v26…
robobun Jul 11, 2026
0dbe84c
Revert "test(node/tls): reword three upstream comment markers in vend…
robobun Jul 11, 2026
19fc7ae
tls: guard setDefaultCACertificates empty-result on snapshot.length
robobun Jul 11, 2026
ae9a524
test(net): widen mongodb-pattern RSS bound to 16MB on release
robobun Jul 11, 2026
4212da6
strip PR-added code comments per maintainer request
robobun Jul 11, 2026
a4dcc15
Merge remote-tracking branch 'origin/main' into claude/node-tls-26-co…
cirospaciari Jul 14, 2026
2166be0
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Jul 14, 2026
a3ba65e
http: port the InvalidCRL error to the per-crate error enum
cirospaciari Jul 14, 2026
70f2152
ipc: cover net.Socket handle receive now that parseHandle implements it
cirospaciari Jul 14, 2026
894ff3f
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 14, 2026
25bcd90
ipc: drop the commented-out serialize() reference block
cirospaciari Jul 14, 2026
8d10f4f
test(ipc): stop the unsent-handle test racing the child's ack
cirospaciari Jul 14, 2026
341a92d
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 14, 2026
c45cf40
Merge origin/main into ciro/cluster-tests-v26
cirospaciari Jul 14, 2026
b840ff8
Merge origin/main into ciro/cluster-tests-v26
cirospaciari Jul 15, 2026
62f801b
cluster: drop narration comments from the cluster/IPC changes
cirospaciari Jul 15, 2026
0ea0301
cluster: let a second worker listen on a shared handle on Windows
cirospaciari Jul 15, 2026
475e825
src: strip PR-added comments not from Node.js source
robobun Jul 15, 2026
f2ef76f
Merge remote-tracking branch 'origin/main' into claude/node-tls-26-co…
robobun Jul 16, 2026
72ccd5c
net: fix ServerHandlers.error TLS detection (this vs data) exposed by…
robobun Jul 16, 2026
1a82a2d
test(node/tls): reword three upstream comment markers in the vendored…
robobun Jul 16, 2026
96081d6
net: ServerHandlers.error TLS branch now destroys instead of no-op de…
robobun Jul 16, 2026
0dfd2dd
Revert "test(node/tls): reword three upstream comment markers in the …
robobun Jul 16, 2026
4c429d6
net: use ErrnoException for the onread zero-length ENOBUFS
robobun Jul 16, 2026
ed5be06
Revert "net: use ErrnoException for the onread zero-length ENOBUFS"
robobun Jul 16, 2026
5bf4147
net: drop stale 'delegation above is a no-op' sentence from the Plain…
robobun Jul 16, 2026
1eeea8e
net: SocketEmitEndNT delegates to finishSocketEnd for the EOF body
robobun Jul 16, 2026
b65ea59
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 16, 2026
b2d4ae7
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 16, 2026
00b8c5f
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 17, 2026
97b68a9
cluster: fix auto-merge picking main's incompatible send() wrapping
robobun Jul 17, 2026
0fc6370
test(net): widen handle-leak RSS margin to 24MB on release
robobun Jul 17, 2026
ed57828
net: kick 'end' after push(null) and resume cluster-accepted sockets
robobun Jul 17, 2026
d7139f8
Merge remote-tracking branch 'origin/main' into claude/node-tls-26-co…
robobun Jul 17, 2026
37e5363
test(tls): bind ssl-ctx-cache servers to 127.0.0.1 explicitly
robobun Jul 17, 2026
f7881db
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 17, 2026
f7b30f4
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 17, 2026
e4dc6ba
test(net,tls): bind PR-added servers and clients to 127.0.0.1 explicitly
robobun Jul 17, 2026
d4b92a1
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 17, 2026
5856176
test(net): add rationale for the handle-leak RSS margin and fix stale…
robobun Jul 17, 2026
1b23722
net: map ENOTSOCK/EBADF to EINVAL for listen({fd}) like Node
robobun Jul 17, 2026
0569ecc
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 17, 2026
44134b9
test(net): widen connect({path}) reused-handle page-count threshold t…
robobun Jul 17, 2026
a298ec3
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Jul 18, 2026
28c2df1
ci: retrigger
robobun Jul 18, 2026
42be7fc
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Jul 19, 2026
ad826fa
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 19, 2026
a810ee1
test(http): socket-end-drain: don't reject the close wait on an expec…
robobun Jul 19, 2026
b0f5a0b
Merge remote-tracking branch 'origin/main' into claude/node-tls-26-co…
robobun Jul 20, 2026
37263b0
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 20, 2026
24d75dd
windows: carry the raw UV errno through ListenPipeError::Sys
robobun Jul 20, 2026
3b440fe
test: assert named-pipe errno resolves to EADDRINUSE, not a literal -98
robobun Jul 20, 2026
faa769d
Merge branch 'main' into ciro/cluster-tests-v26
dylan-conway Jul 21, 2026
a27604f
net,tls: address Jarred's four blocking review items
robobun Jul 21, 2026
bbda763
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 21, 2026
47d53b6
socket: give accepted sockets their own KeepAlive so unref() works
robobun Jul 21, 2026
6fade0d
usockets(windows): stop busy-polling on_end for half-open sockets aft…
robobun Jul 21, 2026
d05993a
test(net): assert half-open accepted sockets do not busy-poll the loop
robobun Jul 21, 2026
d295c21
test(net): await the accepted-socket open event instead of one setImm…
robobun Jul 21, 2026
b13343c
ci: retrigger
robobun Jul 21, 2026
1b88b1b
Merge remote-tracking branch 'origin/main' into claude/node-tls-26-co…
robobun Jul 21, 2026
1b71e7c
Merge branch 'main' into claude/node-tls-26-compat
cirospaciari Jul 21, 2026
bc921ba
test(common/gc): make onGC honor upstream's one-setImmediate contract…
robobun Jul 21, 2026
305a46d
test(common/gc): replace the onGC WeakRef nextTick poll with a gc()-d…
robobun Jul 21, 2026
f4963c7
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 22, 2026
3c1dcdb
child_process: port Node v26.3.0 tests and fix the gaps they surface …
cirospaciari Jul 22, 2026
d93f501
Merge origin/main into ciro/cluster-tests-v26; drop 'TODO:' from fd-l…
robobun Jul 22, 2026
875e98d
cluster: hoist this.workers/this.handle to locals in SharedHandle.remove
robobun Jul 22, 2026
199918b
NodeTLS: two-pass parseCACertificates with RAII BoringSSL + shared th…
robobun Jul 22, 2026
7d0ddbd
Stack on #32630; hoist () => handle.close() and annotate Node-matchin…
robobun Jul 22, 2026
e048413
cluster: drop SCHED_NONE worker-side chmod to match Node v26.3.0
robobun Jul 22, 2026
b76d309
crypto: replace spaces with underscores in ERR_OSSL_* reason like Nod…
robobun Jul 22, 2026
535bfef
NodeTLS: copy ArrayBufferView bytes in parseCACertificates pass 1
robobun Jul 22, 2026
f627e76
NodeTLS: keep ArrayBufferView inputs in MarkedArgumentBuffer instead …
robobun Jul 22, 2026
dac8523
Merge branch 'claude/node-tls-26-compat' into ciro/cluster-tests-v26
cirospaciari Jul 22, 2026
3876df3
net: emit dns.lookup error directly in listenInCluster like Node
robobun Jul 22, 2026
5dfab9a
Merge branch 'ciro/cluster-tests-v26' of https://github.com/oven-sh/b…
robobun Jul 22, 2026
cb057f5
clippy: fold ss_len late-init into an if-expression in wildcard_sockaddr
robobun Jul 22, 2026
1b72786
cluster: pass received fd through the (message, handle) slot like Nod…
robobun Jul 23, 2026
959e41b
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 23, 2026
9aced0f
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 24, 2026
d04e4dc
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 24, 2026
af8da54
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Jul 24, 2026
deec145
windows: listen in the primary's bsd_create_bound_socket before WSADu…
robobun Jul 24, 2026
656da3b
Merge branch 'ciro/cluster-tests-v26' of https://github.com/oven-sh/b…
robobun Jul 24, 2026
f22314a
Merge origin/main into ciro/cluster-tests-v26
robobun Jul 24, 2026
de24c75
Merge origin/main into ciro/cluster-tests-v26
robobun Aug 3, 2026
39abea9
trim flagged multi-line comments to single-line refs or drop narration
robobun Aug 3, 2026
28ed3eb
BunProcess.cpp: collapse the getDirect note to one line
robobun Aug 3, 2026
cd9a5a4
Merge remote-tracking branch 'origin/main' into ciro/cluster-tests-v26
robobun Aug 3, 2026
74a0938
ipc_host: drop dead has_ipc helper
robobun Aug 3, 2026
b7a6441
cluster/ipc: parse received dgram.Socket handles instead of closing t…
robobun Aug 4, 2026
7c8fbbc
trim multi-line comments in dgram IPC path to single-line refs
robobun Aug 4, 2026
6f967fb
spawn.ipc.bun-node: dgram.Socket handles are accepted now, test the a…
robobun Aug 4, 2026
1ac1d7b
Merge remote-tracking branch 'origin/main' into ciro/cluster-tests-v26
robobun Aug 4, 2026
2605a67
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Aug 5, 2026
1a5fb31
Merge remote-tracking branch 'origin/main' into ciro/cluster-tests-v26
robobun Aug 7, 2026
5cce0ff
Trim comments to node-source/spec references
robobun Aug 7, 2026
a1d664b
Merge branch 'main' into ciro/cluster-tests-v26
cirospaciari Aug 10, 2026
e0344f4
cluster: survive peer resets on queued round-robin connections; stop …
cirospaciari Aug 10, 2026
67c47c3
dgram: release the shared cluster fd when adopting it fails
robobun Aug 10, 2026
b659b1c
cluster: release shared handles on close(), report shared unix paths,…
cirospaciari Aug 10, 2026
21eab98
net: point the shared-handle release comment at the node source
robobun Aug 10, 2026
095c60b
cluster/ipc: drop a departing worker's in-flight connection, tolerate…
cirospaciari Aug 10, 2026
8af059c
collapse two flagged comments to one line
robobun Aug 10, 2026
975c26f
cluster.test: pin the new in-flight semantics for a dying worker
robobun Aug 10, 2026
f5468a7
cluster/ipc: follow node for in-flight connections, sent sockets and …
cirospaciari Aug 10, 2026
f45e227
collapse four flagged comments to one line
robobun Aug 10, 2026
efca99d
Ipc.ts: read handle.server once (no-duplicate-conditional-property-ac…
robobun Aug 10, 2026
147eec5
spawn: give a closed inherited stdio slot /dev/null instead of whatev…
cirospaciari Aug 10, 2026
e50529e
ipc/cluster: postpone only user disconnects and report them at once; …
cirospaciari Aug 10, 2026
4f68e28
collapse six flagged comments to one line
robobun Aug 10, 2026
59ef8c0
Merge branch 'main' into ciro/cluster-tests-v26
alii Aug 11, 2026
a97ca4d
cluster: mark internal sends and shared dgram fds with private names;…
robobun Aug 12, 2026
478a1a6
cluster/primary: load the handle classes and node:path on first query…
robobun Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/skills/verify/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ For worker/subprocess-shaped changes, spawn a subprocess (still `-e`) so worker
formula shadows the pinned nightly, and `bun bd` dies with `the option 'Z' is only
accepted on the nightly compiler`. `bun bd` re-runs cargo on every invocation, so
this is needed for follow-up runs too, not just the first build.
- `node:cluster` changes can't be driven with `-e`: `cluster.fork()` re-execs `argv[1]`, so workers need a real file on disk. Write a scratch script and run `./build/debug/bun-debug <file>`.
Comment thread
robobun marked this conversation as resolved.
- Only one `bun bd` per worktree at a time — a second one blocks on the build lock and looks like a runtime hang. Build once, then drive `./build/debug/bun-debug` directly under `timeout`.
- `BUN_DEBUG_QUIET_LOGS=1` suppresses debug-build log spam.
- Debug builds print `[cachefs]`/`[sys]` lines to stdout; filter them before diffing
output against `node`.
Expand Down
145 changes: 140 additions & 5 deletions packages/bun-usockets/src/bsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_sendbuf* sendbuf, int fl
#endif
}

int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int flags) {
int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int flags, int max_packets) {
if (max_packets > LIBUS_UDP_RECV_COUNT) max_packets = LIBUS_UDP_RECV_COUNT;
#if defined(_WIN32)
for (int i = 0; i < LIBUS_UDP_RECV_COUNT; i++) {
while (1) {
Expand All @@ -162,12 +163,12 @@ int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int fl
#elif defined(__APPLE__)
if (Bun__doesMacOSVersionSupportSendRecvMsgX()) {
while (1) {
int ret = recvmsg_x(fd, recvbuf->msgvec, LIBUS_UDP_RECV_COUNT, flags);
int ret = recvmsg_x(fd, recvbuf->msgvec, max_packets, flags);
if (ret >= 0 || errno != EINTR) return ret;
}
}

for (int i = 0; i < LIBUS_UDP_RECV_COUNT; ++i) {
for (int i = 0; i < max_packets; ++i) {
while (1) {
ssize_t ret = recvmsg(fd, &recvbuf->msgvec[i].msg_hdr, flags);
if (ret < 0) {
Expand All @@ -179,10 +180,10 @@ int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int fl
break;
}
}
return LIBUS_UDP_RECV_COUNT;
return max_packets;
#else
while (1) {
int ret = recvmmsg(fd, (struct mmsghdr *)&recvbuf->msgvec, LIBUS_UDP_RECV_COUNT, flags, 0);
int ret = recvmmsg(fd, (struct mmsghdr *)&recvbuf->msgvec, max_packets, flags, 0);
if (ret >= 0 || errno != EINTR) return ret;
}
#endif
Expand Down Expand Up @@ -1216,6 +1217,140 @@ int bsd_set_defer_accept(LIBUS_SOCKET_DESCRIPTOR listenFd) {

// return LIBUS_SOCKET_ERROR or the fd that represents listen socket
// listen both on ipv6 and ipv4
int bsd_socket_export_size(void) {
#ifdef _WIN32
return (int) sizeof(WSAPROTOCOL_INFOW);
#else
return 0;
#endif
}

int bsd_socket_export(LIBUS_SOCKET_DESCRIPTOR fd, unsigned int target_pid, void *info_out) {
#ifdef _WIN32
if (WSADuplicateSocketW(fd, (DWORD) target_pid, (WSAPROTOCOL_INFOW *) info_out) != 0) {
return WSAGetLastError();
}
return 0;
#else
(void) fd; (void) target_pid; (void) info_out;
return ENOTSUP;
#endif
}

LIBUS_SOCKET_DESCRIPTOR bsd_socket_import(void *info, int *err) {
#ifdef _WIN32
SOCKET s = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
(WSAPROTOCOL_INFOW *) info, 0, WSA_FLAG_OVERLAPPED);
if (s == INVALID_SOCKET) {
*err = WSAGetLastError();
return LIBUS_SOCKET_ERROR;
}
return s;
#else
(void) info;
*err = ENOTSUP;
return LIBUS_SOCKET_ERROR;
#endif
}

/* Windows rejects listen() on a duplicate of an already-listening socket, where POSIX
* no-ops it; cluster workers each listen on their own dup of one shared fd. libuv
* sidesteps this by listening before the xfer (UV_HANDLE_SHARED_TCP_SOCKET, win/tcp.c). */
int bsd_socket_listen_error_is_benign(LIBUS_SOCKET_DESCRIPTOR fd) {
#ifdef _WIN32
int listening = 0;
int optlen = (int) sizeof(listening);
if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, (char *) &listening, &optlen) != 0) {
return 0;
}
return listening != 0;
#else
(void) fd;
return 0;
#endif
}

LIBUS_SOCKET_DESCRIPTOR bsd_create_bound_socket(const char *host, int port, int options, int *out_port, int *error) {
struct addrinfo hints, *result;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;

char port_string[16];
snprintf(port_string, 16, "%d", port);

int gai = getaddrinfo(host, port_string, &hints, &result);
if (gai != 0) {
#ifdef _WIN32
*error = gai;
#else
*error = EINVAL;
#endif
return LIBUS_SOCKET_ERROR;
}

LIBUS_SOCKET_DESCRIPTOR fd = LIBUS_SOCKET_ERROR;
for (int family = AF_INET6; fd == LIBUS_SOCKET_ERROR && family >= AF_INET; family -= (AF_INET6 - AF_INET)) {
for (struct addrinfo *a = result; a != NULL; a = a->ai_next) {
if (a->ai_family != family) {
continue;
}
fd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol, NULL);
if (fd == LIBUS_SOCKET_ERROR) {
Comment thread
cirospaciari marked this conversation as resolved.
*error = LIBUS_ERR;
continue;
}
#if defined(SO_REUSEADDR) && !defined(_WIN32)
int one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
#endif
#ifdef IPV6_V6ONLY
if (a->ai_family == AF_INET6) {
int enabled = (options & LIBUS_SOCKET_IPV6_ONLY) != 0;
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &enabled, sizeof(enabled));
}
#endif
int rc;
do
rc = bind(fd, a->ai_addr, (socklen_t) a->ai_addrlen);
while (IS_EINTR(rc));
if (rc != 0) {
*error = LIBUS_ERR;
bsd_close_socket(fd);
fd = LIBUS_SOCKET_ERROR;
continue;
}
break;
}
}
freeaddrinfo(result);
if (fd == LIBUS_SOCKET_ERROR) {
return LIBUS_SOCKET_ERROR;
}
#ifdef _WIN32
/* Windows rejects listen() on a duplicate of an already-listening socket
* only after another duplicate has listened, so two workers racing on the
* same shared fd can observe listen() -> WSAEINVAL while SO_ACCEPTCONN
* still reads 0 (the benign check in us_socket_group_listen_fd then
* fails). libuv's approach (UV_HANDLE_SHARED_TCP_SOCKET, win/tcp.c) is to
* listen in the primary before WSADuplicateSocket; every worker's
* duplicate is then already listening and SO_ACCEPTCONN is reliably set. */
if (listen(fd, 511) != 0) {
*error = LIBUS_ERR;
bsd_close_socket(fd);
return LIBUS_SOCKET_ERROR;
}
#endif
struct bsd_addr_t tmp;
if (bsd_local_addr(fd, &tmp) == 0) {
*out_port = bsd_addr_get_port(&tmp);
} else {
*out_port = port;
}
return fd;
}

LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options, int* error) {
struct addrinfo hints, *result;
memset(&hints, 0, sizeof(struct addrinfo));
Expand Down
34 changes: 34 additions & 0 deletions packages/bun-usockets/src/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,40 @@ struct us_listen_socket_t *us_socket_group_listen(struct us_socket_group_t *grou
return ls;
}

struct us_listen_socket_t *us_socket_group_listen_fd(struct us_socket_group_t *group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
LIBUS_SOCKET_DESCRIPTOR fd, int backlog, int options, int socket_ext_size, int *error) {
/* Validate with listen(2) before touching the descriptor's flags: on failure the caller keeps
* the fd (it may be its stdio), and a non-socket must come back untouched. */
if (listen(fd, backlog > 0 ? backlog : 512)) {
int listen_err = LIBUS_ERR;
if (!bsd_socket_listen_error_is_benign(fd)) {
*error = listen_err;
return 0;
}
}
apple_no_sigpipe(fd);
bsd_set_nonblocking(fd);

struct us_poll_t *p = us_create_poll(group->loop, 0, sizeof(struct us_listen_socket_t));
us_poll_init(p, fd, POLL_TYPE_SEMI_SOCKET);
if (us_poll_start_rc(p, group->loop, LIBUS_SOCKET_READABLE) != 0) {
int saved_errno = LIBUS_ERR;
us_poll_free(p, group->loop);
*error = saved_errno;
return 0;
}

struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p;
us_internal_init_listen_socket(ls, group, kind, ssl_ctx, options, socket_ext_size);

if (options & LIBUS_LISTEN_DEFER_ACCEPT) {
ls->deferred_accept = bsd_set_defer_accept(fd);
}

return ls;
}

struct us_listen_socket_t *us_socket_group_listen_unix(struct us_socket_group_t *group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
const char *path, size_t pathlen, int options, int socket_ext_size, int *error) {
Expand Down
2 changes: 1 addition & 1 deletion packages/bun-usockets/src/eventing/libuv.c
Original file line number Diff line number Diff line change
Expand Up @@ -574,4 +574,4 @@ int us_socket_get_error(struct us_socket_t *s) {
return error;
}

#endif
#endif
1 change: 1 addition & 0 deletions packages/bun-usockets/src/internal/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ struct us_udp_socket_t {
uint16_t port;
uint16_t closed : 1;
uint16_t connected : 1;
uint16_t shared_fd : 1;
struct us_udp_socket_t *next;
};

Expand Down
9 changes: 8 additions & 1 deletion packages/bun-usockets/src/internal/networking/bsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ struct udp_sendbuf {
};

int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_sendbuf* sendbuf, int flags);
int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int flags);
int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, struct udp_recvbuf *recvbuf, int flags, int max_packets);
void bsd_udp_setup_recvbuf(struct udp_recvbuf *recvbuf, void *databuf, size_t databuflen);
int bsd_udp_setup_sendbuf(struct udp_sendbuf *buf, size_t bufsize, void** payloads, size_t* lengths, void** addresses, int num);
int bsd_udp_packet_buffer_payload_length(struct udp_recvbuf *msgvec, int index);
Expand Down Expand Up @@ -243,6 +243,13 @@ LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(struct sockaddr_storage *addr,

LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, size_t pathlen, int options);

int bsd_socket_export_size(void);
int bsd_socket_export(LIBUS_SOCKET_DESCRIPTOR fd, unsigned int target_pid, void *info_out);
LIBUS_SOCKET_DESCRIPTOR bsd_socket_import(void *info, int *err);
int bsd_socket_listen_error_is_benign(LIBUS_SOCKET_DESCRIPTOR fd);

LIBUS_SOCKET_DESCRIPTOR bsd_create_bound_socket(const char *host, int port, int options, int *out_port, int *error);

#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT 0
#endif
Expand Down
9 changes: 7 additions & 2 deletions packages/bun-usockets/src/libusockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ struct us_udp_packet_buffer_t *us_create_udp_packet_buffer();

struct us_udp_socket_t *us_create_udp_socket(us_loop_r loop, void (*data_cb)(struct us_udp_socket_t *, void *, int), void (*drain_cb)(struct us_udp_socket_t *), void (*close_cb)(struct us_udp_socket_t *), void (*recv_error_cb)(struct us_udp_socket_t *, int, int), const char *host, unsigned short port, int flags, int *err, void *user);


void us_udp_socket_close(struct us_udp_socket_t *s);

int us_udp_socket_set_broadcast(struct us_udp_socket_t *s, int enabled);
Expand All @@ -242,7 +243,7 @@ LIBUS_SOCKET_DESCRIPTOR us_udp_socket_fd(struct us_udp_socket_t *s);
/* Adopts an already created (and usually already bound) UDP socket descriptor
* instead of creating a new one. The fd is made non-blocking and the standard
* receive-path options are applied. Returns null with *err set on failure. */
struct us_udp_socket_t *us_create_udp_socket_from_fd(us_loop_r loop, void (*data_cb)(struct us_udp_socket_t *, void *, int), void (*drain_cb)(struct us_udp_socket_t *), void (*close_cb)(struct us_udp_socket_t *), void (*recv_error_cb)(struct us_udp_socket_t *, int, int), LIBUS_SOCKET_DESCRIPTOR fd, int *err, void *user);
struct us_udp_socket_t *us_create_udp_socket_from_fd(us_loop_r loop, void (*data_cb)(struct us_udp_socket_t *, void *, int), void (*drain_cb)(struct us_udp_socket_t *), void (*close_cb)(struct us_udp_socket_t *), void (*recv_error_cb)(struct us_udp_socket_t *, int, int), LIBUS_SOCKET_DESCRIPTOR fd, int shared, int *err, void *user);

/* This one is ugly, should be ext! not user */
void *us_udp_socket_user(struct us_udp_socket_t *s);
Expand Down Expand Up @@ -395,6 +396,10 @@ struct us_listen_socket_t *us_socket_group_listen_unix(us_socket_group_r group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
const char *path, size_t pathlen, int options, int socket_ext_size, int *error)
__attribute__((nonnull(1, 4, 8))); /* ssl_ctx nullable */
struct us_listen_socket_t *us_socket_group_listen_fd(us_socket_group_r group,
unsigned char kind, struct ssl_ctx_st *ssl_ctx,
LIBUS_SOCKET_DESCRIPTOR fd, int backlog, int options, int socket_ext_size, int *error)
__attribute__((nonnull(1, 8))); /* ssl_ctx nullable */
void us_listen_socket_close(struct us_listen_socket_t *ls) nonnull_fn_decl;

/* SNI: tree hangs off the listen socket. ssl_ctx is up_ref'd; user is opaque
Expand Down Expand Up @@ -706,7 +711,7 @@ LIBUS_SOCKET_DESCRIPTOR us_socket_get_fd(us_socket_r s) nonnull_fn_decl;

/* Bun extras */
struct us_socket_t *us_socket_pair(us_socket_group_r group, unsigned char kind, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR *fds) nonnull_fn_decl;
struct us_socket_t *us_socket_from_fd(us_socket_group_r group, unsigned char kind, struct ssl_ctx_st *ssl_ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR fd, int ipc)
struct us_socket_t *us_socket_from_fd(us_socket_group_r group, unsigned char kind, struct ssl_ctx_st *ssl_ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR fd, int options, int ipc)
__attribute__((nonnull(1))); /* ssl_ctx nullable */
struct us_socket_t *us_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length);
int us_raw_root_certs(struct us_cert_string_t **out);
Expand Down
2 changes: 1 addition & 1 deletion packages/bun-usockets/src/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,7 @@ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int eof, in
do {
struct udp_recvbuf recvbuf;
bsd_udp_setup_recvbuf(&recvbuf, u->loop->data.recv_buf, LIBUS_RECV_BUFFER_LENGTH);
int npackets = bsd_recvmmsg(us_poll_fd(p), &recvbuf, MSG_DONTWAIT);
int npackets = bsd_recvmmsg(us_poll_fd(p), &recvbuf, MSG_DONTWAIT, u->shared_fd ? 1 : LIBUS_UDP_RECV_COUNT);
if (npackets > 0) {
u->on_data(u, &recvbuf, npackets);
} else {
Expand Down
21 changes: 13 additions & 8 deletions packages/bun-usockets/src/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ struct us_socket_t *us_socket_pair(struct us_socket_group_t *group, unsigned cha
return 0;
}

return us_socket_from_fd(group, kind, NULL, socket_ext_size, fds[0], 0);
return us_socket_from_fd(group, kind, NULL, socket_ext_size, fds[0], 0, 0);
#endif
}

Expand Down Expand Up @@ -445,10 +445,7 @@ int us_socket_write2(struct us_socket_t *s, const char *header, int header_lengt
return written < 0 ? 0 : written;
}

struct us_socket_t *us_socket_from_fd(struct us_socket_group_t *group, unsigned char kind, struct ssl_ctx_st *ssl_ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR fd, int ipc) {
#if defined(LIBUS_USE_LIBUV) || defined(WIN32)
return 0;
#else
struct us_socket_t *us_socket_from_fd(struct us_socket_group_t *group, unsigned char kind, struct ssl_ctx_st *ssl_ctx, int socket_ext_size, LIBUS_SOCKET_DESCRIPTOR fd, int options, int ipc) {
struct us_poll_t *p1 = us_create_poll(group->loop, 0, sizeof(struct us_socket_t) + socket_ext_size);
us_poll_init(p1, fd, POLL_TYPE_SOCKET);
int rc = us_poll_start_rc(p1, group->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
Expand All @@ -464,7 +461,7 @@ struct us_socket_t *us_socket_from_fd(struct us_socket_group_t *group, unsigned
s->timeout = 255;
s->long_timeout = 255;
s->flags.low_prio_state = 0;
s->flags.allow_half_open = 0;
s->flags.allow_half_open = (options & LIBUS_SOCKET_ALLOW_HALF_OPEN) != 0;
s->flags.is_paused = 0;
s->flags.is_ipc = ipc;
s->flags.is_closed = 0;
Expand All @@ -490,7 +487,6 @@ struct us_socket_t *us_socket_from_fd(struct us_socket_group_t *group, unsigned
}

return s;
#endif
}

void *us_socket_get_native_handle(struct us_socket_t *s) {
Expand Down Expand Up @@ -690,12 +686,21 @@ int us_socket_ipc_write_fd(struct us_socket_t *s, const char *data, int length,

int sent = bsd_sendmsg(us_poll_fd(&s->p), &msg, 0);

if (sent < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) {
s->flags.last_write_failed = 1;
us_poll_change(&s->p, s->group->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
return 0;
}
return -1;
}

if (sent != length) {
s->flags.last_write_failed = 1;
us_internal_rearm_writable(s);
}

return sent < 0 ? 0 : sent;
return sent;
}
#endif

Expand Down
Loading