Skip to content
Open
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
7f82a21
node:worker_threads: add 5 upstream Node worker tests that already pass
cirospaciari Jul 16, 2026
f69da29
node:worker_threads: add 3 more upstream Node worker tests that alrea…
cirospaciari Jul 17, 2026
356f936
node:worker_threads: drop test-worker-dns-terminate, exempt fshandles…
cirospaciari Jul 17, 2026
f57c53b
node:worker_threads: exempt both fshandles tests from LeakSanitizer
cirospaciari Jul 17, 2026
2872f8f
node:worker_threads: drop the two fshandles tests instead of exemptin…
cirospaciari Jul 17, 2026
c8a6e90
worker_threads: publish node's 'worker_threads' diagnostics channel
cirospaciari Jul 17, 2026
10fe341
worker_threads: async_hooks WORKER resource, error-clone stack, share…
cirospaciari Jul 17, 2026
c308af4
BroadcastChannel: ref() should return the channel
cirospaciari Jul 17, 2026
f1384cd
worker_threads: fire 'online' before the entry point runs, as node does
cirospaciari Jul 17, 2026
5056973
Merge origin/main into ciro/worker-threads-node-tests
cirospaciari Jul 17, 2026
d7e1659
worker_threads: address review feedback and drop test-worker-memory
robobun Jul 17, 2026
c7d3e81
cli: support node's -pe alias
cirospaciari Jul 17, 2026
80aae50
worker_threads: profile worker threads under --cpu-prof
cirospaciari Jul 17, 2026
e61bfa4
cli: scope the -pe alias to the bun/node entry points in execArgv
robobun Jul 17, 2026
4f599cf
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 17, 2026
fb8b3d3
cpu profiler: clamp the sampling interval to a usable range
cirospaciari Jul 17, 2026
dbe27a0
tls: make --use-system-ca a per-Environment option, add --no-use-syst…
cirospaciari Jul 17, 2026
0fe453a
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 17, 2026
e20129c
cpu profiler: do not inherit --cpu-prof-name into workers
robobun Jul 17, 2026
3a09c2e
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 17, 2026
fc01f2f
worker_threads: keep error.code when the thrown value cannot be cloned
cirospaciari Jul 17, 2026
57cf856
perf_hooks: implement eventLoopUtilization()
cirospaciari Jul 17, 2026
dfe31cd
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 17, 2026
aba81dd
cpu profiler: let workers inherit --cpu-prof-name, as node does
cirospaciari Jul 17, 2026
74c1ebf
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 17, 2026
f17365c
fix lint and clippy fallout from the eventLoopUtilization work
cirospaciari Jul 17, 2026
997ce14
address review: once() this-binding, init sites, nested cpu-prof, lint
robobun Jul 17, 2026
e19fa93
Merge branch 'main' into ciro/worker-threads-node-tests
cirospaciari Jul 17, 2026
1fbc0d1
eventLoopUtilization: fix two cross-thread races
robobun Jul 18, 2026
19b36ff
eventLoopUtilization: use seq_cst for the idle_ns/idle_entry_ns pair
robobun Jul 18, 2026
c7cfb4e
web_worker: fix two aliasing/race hazards in the cross-thread ELU/cpu…
robobun Jul 18, 2026
f0f5e8e
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 18, 2026
de70866
ci: retrigger
robobun Jul 18, 2026
31fb9fc
epoll_kqueue: hoist the post-park clock read before zeroing idle_entr…
robobun Jul 18, 2026
a757b03
Worker.cpp: guard errorCodeOf against a pending TerminationException
robobun Jul 18, 2026
46f9f32
Merge branch 'main' into ciro/worker-threads-node-tests
cirospaciari Jul 19, 2026
1efb86f
Merge remote-tracking branch 'origin/main' into ciro/worker-threads-n…
robobun Jul 22, 2026
89e5f6e
Merge remote-tracking branch 'origin/main' into ciro/worker-threads-n…
robobun Aug 3, 2026
747cbbb
trim comments to <=3 lines, cite spec/node source
robobun Aug 3, 2026
79b3862
Worker.cpp: guard WebWorker__dispatchError/dispatchErrorWithValue aga…
robobun Aug 3, 2026
388af0e
bun_get_loop_elu: use raw us_loop_idle_ns, drop the &self idle_ns wra…
robobun Aug 3, 2026
134c6b0
Merge remote-tracking branch 'origin/main' into ciro/worker-threads-n…
robobun Aug 4, 2026
5e6f3ac
Merge branch 'main' into ciro/worker-threads-node-tests
cirospaciari Aug 5, 2026
fb72372
web_worker: don't report a terminate()-rejected entry promise as unca…
robobun Aug 5, 2026
f99b7c0
Add missing SAFETY comment on us_loop_idle_ns call
cirospaciari Aug 5, 2026
20f5734
web_worker: close the Bun-side termination-exception holes in the ter…
robobun Aug 5, 2026
04dca3c
worker: keep flush_logs and teardown termination-safe on the remainin…
cirospaciari Aug 5, 2026
a4993b9
Merge remote-tracking branch 'origin/main' into ciro/worker-threads-n…
robobun Aug 7, 2026
5d77db6
Merge branch 'main' into ciro/worker-threads-node-tests
cirospaciari Aug 7, 2026
3b28311
execArgv: drop the unconditional -pe insertion the merge reintroduced
robobun Aug 7, 2026
49f1a4f
Trim comments to node-source/spec references
robobun Aug 7, 2026
7d70027
Skip worker_destruction under ASAN while the terminate-during-load as…
robobun Aug 7, 2026
c6045a8
worker_destruction: terminate when the worker reports its action started
robobun Aug 8, 2026
3288369
Merge remote-tracking branch 'origin/main' into ciro/worker-threads-n…
dylan-conway Aug 8, 2026
bc4eab3
worker_threads: read a Worker's hasRef / loop utilization through pri…
dylan-conway Aug 8, 2026
f3c28b8
Reattach release_parent_poll_ref doc comment to its function
robobun Aug 8, 2026
8c4567d
Drop unreachable keep-alive guard in WorkerMessagingProxy::hasRef
robobun Aug 8, 2026
94db9ae
worker_threads: honour --cpu-prof-dir, --cpu-prof-name and --cpu-prof…
dylan-conway Aug 8, 2026
fec0b60
perf_hooks: eventLoopUtilization needs no null-loop guard
dylan-conway Aug 8, 2026
47cceef
tls: a Worker's --use-system-ca / --no-use-system-ca governs the root…
dylan-conway Aug 9, 2026
e5e73b0
Merge remote-tracking branch 'origin/main' into ciro/worker-threads-n…
dylan-conway Aug 9, 2026
153e900
Fix two review findings in the per-worker use-system-ca plumbing
robobun Aug 9, 2026
e7f89fd
Only install OpenSSL default cert paths in the system-CA store variant
robobun Aug 9, 2026
ab623d0
Merge branch 'main' into ciro/worker-threads-node-tests
cirospaciari Aug 10, 2026
dc6b8bb
tls.getCACertificates('default') reports the decision connections use
robobun Aug 10, 2026
6222f4d
root_certs: cite node's NewRootCertStore for the default-paths gating
robobun Aug 10, 2026
6ab78d8
Restore per-thread NODE_USE_SYSTEM_CA fallback in getCACertificates
robobun Aug 10, 2026
28ea0a8
worker_threads: apply the thread's --use-system-ca to the default cli…
cirospaciari Aug 10, 2026
bb88250
worker_threads: resolve a flagless thread's CA option from its env, r…
cirospaciari Aug 10, 2026
6fc1c9d
tls/worker_threads: keep OpenSSL's default lookups in the flagless st…
cirospaciari Aug 10, 2026
efc5781
tls: back to node's root-store rules; make --use-openssl-ca exclusive
cirospaciari Aug 10, 2026
8eb5f3e
uws: safety comment on us_get_shared_default_ca_store matches its arg…
robobun Aug 10, 2026
9d5b413
tls.getCACertificates('default') leaves bundled and system roots out …
robobun Aug 10, 2026
ef0dc09
tls/worker_threads: system store is the OS store alone, openssl-ca re…
cirospaciari Aug 10, 2026
b6f0c62
Pin the web Worker 'open' event to thread start, ahead of the entry p…
robobun Aug 10, 2026
a24ad08
web_worker: borrow the ELU atomics directly (clippy deref_addrof)
cirospaciari Aug 11, 2026
3fcfde3
Point the use_system_ca_flag docs at InitOptions; arm the ELU test's …
robobun Aug 11, 2026
da612ae
Merge branch 'main' into ciro/worker-threads-node-tests
alii Aug 11, 2026
09f3e89
bake: init the production VM as the main thread like its siblings
robobun Aug 11, 2026
7e37ca0
bake: pass the CA flag explicitly instead of initializing as the main…
robobun Aug 11, 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
24 changes: 16 additions & 8 deletions packages/bun-usockets/src/crypto/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ static int us_ctx_cache_ex_idx = -1;
* ca/caFile options or a later addCACert): the per-socket client attach must
* not replace such a store with the process-shared default roots. */
static int us_ctx_user_ca_ex_idx = -1;
/* The resolved system-CA decision the context was built with (stored as value+1 so 0 = unset). */
static int us_ctx_use_system_ca_ex_idx = -1;
static int us_ssl_reneg_state_idx = -1;
/* Per-connection async-SNI suspension state (select_certificate_cb retry). */
static int us_ssl_sni_pending_idx = -1;
Expand Down Expand Up @@ -443,6 +445,7 @@ static void us_ex_idx_init(void) {
us_sni_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
us_ctx_cache_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, bun_ssl_ctx_cache_on_free);
us_ctx_user_ca_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
us_ctx_use_system_ca_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
us_ctx_sni_policy_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
us_ssl_reneg_state_idx = SSL_get_ex_new_index(0, NULL, NULL, NULL, us_ssl_reneg_state_free);
us_ssl_sni_pending_idx = SSL_get_ex_new_index(0, NULL, NULL, NULL, us_ssl_sni_pending_free);
Expand Down Expand Up @@ -903,13 +906,15 @@ static int us_ssl_ctx_use_privatekey_content(SSL_CTX *ctx, const char *content,
* the still-empty SSL_CTX_new() store are first replaced by a private full
* default-root copy, and the context is marked so the per-socket attach keeps
* it. https://github.com/nodejs/node/blob/v26.3.0/src/crypto/crypto_context.cc#L1831 */
int us_ssl_ctx_use_system_ca(SSL_CTX *ctx) {
us_ex_idx_ensure();
intptr_t stored = (intptr_t)SSL_CTX_get_ex_data(ctx, us_ctx_use_system_ca_ex_idx);
return stored ? (int)(stored - 1) : us_default_use_system_ca();
}

static X509_STORE *us_ssl_ctx_get_own_cert_store(SSL_CTX *ctx) {
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
/* us_get_shared_default_ca_store() up-refs before returning, so release
* the reference taken just for this comparison. */
X509_STORE *shared = us_get_shared_default_ca_store();
int store_is_shared = store != NULL && store == shared;
X509_STORE_free(shared);
int store_is_shared = us_is_shared_default_ca_store(store);
us_ex_idx_ensure();
int store_is_empty = 0;
if (store != NULL && !store_is_shared) {
Expand All @@ -921,7 +926,7 @@ static X509_STORE *us_ssl_ctx_get_own_cert_store(SSL_CTX *ctx) {
* no `ca` configured at all may be seeded with the default roots here. */
int user_ca = SSL_CTX_get_ex_data(ctx, us_ctx_user_ca_ex_idx) != NULL;
if (store == NULL || store_is_shared || (store_is_empty && !user_ca)) {
X509_STORE *own = us_get_default_ca_store();
X509_STORE *own = us_get_default_ca_store(us_ssl_ctx_use_system_ca(ctx));
if (own == NULL) {
return NULL;
}
Expand Down Expand Up @@ -1106,6 +1111,9 @@ SSL_CTX *us_ssl_ctx_build_raw(struct us_bun_socket_context_options_t options,
/* Register the live-count free_func first thing so every exit (including
* build_fail) balances. The packed reneg policy reuses the same slot. */
SSL_CTX_set_ex_data(ssl_context, us_ssl_ctx_ex_idx(), NULL);
const int use_system_ca = us_resolve_use_system_ca(options.use_system_ca);
us_ex_idx_ensure();
SSL_CTX_set_ex_data(ssl_context, us_ctx_use_system_ca_ex_idx, (void *)(intptr_t)(use_system_ca + 1));

/* Default options we rely on — changing these breaks the BIO logic. */
SSL_CTX_set_read_ahead(ssl_context, 1);
Expand Down Expand Up @@ -1233,7 +1241,7 @@ SSL_CTX *us_ssl_ctx_build_raw(struct us_bun_socket_context_options_t options,
* addRootCerts() when `ca` is absent - the handshake-time auto-chain and
* (for requestCert) client verification both read it. The getter up-refs,
* so set_cert_store owns exactly one reference per context. */
SSL_CTX_set_cert_store(ssl_context, us_get_shared_default_ca_store());
SSL_CTX_set_cert_store(ssl_context, us_get_shared_default_ca_store(use_system_ca));
if (options.request_cert) {
SSL_CTX_set_verify(ssl_context,
options.reject_unauthorized ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
Expand Down Expand Up @@ -1574,7 +1582,7 @@ void us_internal_ssl_attach(struct us_socket_t *s, SSL_CTX *ctx,
* A context whose store holds user-provided CAs (ca/caFile options or
* addCACert) keeps using its own store - overriding it here would
* hide those CAs from chain verification. */
X509_STORE *roots = us_get_shared_default_ca_store();
X509_STORE *roots = us_get_shared_default_ca_store(us_ssl_ctx_use_system_ca(ctx));
if (roots) SSL_set0_verify_cert_store(ssl, roots);
}
}
Expand Down
74 changes: 51 additions & 23 deletions packages/bun-usockets/src/crypto/root_certs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "./root_certs_header.h"
#include "./internal/internal.h"
#include <mutex>
#include <atomic>
#include <string.h>
#include "./default_ciphers.h"

Expand All @@ -23,22 +24,34 @@ extern "C" void BUN__warn__extra_ca_load_failed(const char* filename, const char
// Forward declarations for platform-specific functions
// (Actual implementations are in platform-specific files)

// External variable from Zig CLI arguments
// External variables from the CLI arguments
extern "C" bool Bun__Node__UseSystemCA;

// Helper function to check if system CA should be used
// Checks both CLI flag (--use-system-ca) and environment variable (NODE_USE_SYSTEM_CA=1)
static bool us_should_use_system_ca() {
// Check CLI flag first
extern "C" bool Bun__Node__NoUseSystemCA;
// BunCAStore discriminant (Arguments.rs): 1 == --use-openssl-ca.
extern "C" uint8_t Bun__Node__CAStore;
static const uint8_t BUN_CA_STORE_OPENSSL = 1;

// The process-wide default: --no-use-system-ca beats everything, then --use-system-ca, then
// NODE_USE_SYSTEM_CA=1. A thread (node: Environment) started with its own flag overrides this for
// the contexts it creates — see us_bun_socket_context_options_t.use_system_ca.
extern "C" int us_default_use_system_ca() {
if (Bun__Node__NoUseSystemCA) {
return 0;
}
if (Bun__Node__UseSystemCA) {
return true;
return 1;
}

// Check environment variable
const char *use_system_ca = getenv("NODE_USE_SYSTEM_CA");
return use_system_ca && strcmp(use_system_ca, "1") == 0;
}

// Resolve an options-struct tri-state (0: process default, >0: include system roots, <0: exclude).
extern "C" int us_resolve_use_system_ca(int requested) {
if (requested > 0) return 1;
if (requested < 0) return 0;
return us_default_use_system_ca();
}

// Platform-specific system certificate loading implementations are separated:
// - macOS: root_certs_darwin.cpp (Security framework with dynamic loading)
// - Windows: root_certs_windows.cpp (Windows CryptoAPI)
Expand Down Expand Up @@ -207,13 +220,19 @@ STACK_OF(X509) *us_get_root_system_cert_instances() {
return system_certs;
}

extern "C" X509_STORE *us_get_default_ca_store() {
extern "C" X509_STORE *us_get_default_ca_store(int use_system_ca) {
Comment thread
robobun marked this conversation as resolved.
X509_STORE *store = X509_STORE_new();
if (store == NULL) {
return NULL;
}

if (!X509_STORE_set_default_paths(store)) {
// Node's NewRootCertStore: --use-openssl-ca means OpenSSL's default lookups *instead of* the
// bundled roots (system roots ignored); otherwise the bundled roots, plus the system roots when
// asked. The default lookups double as the system store's SSL_CERT_FILE / SSL_CERT_DIR overrides,
// which node's Linux system loader honours too, so they also come along with use_system_ca.
// https://github.com/nodejs/node/blob/v26.3.0/src/crypto/crypto_context.cc#L1099-L1109
const int openssl_ca = Bun__Node__CAStore == BUN_CA_STORE_OPENSSL;
if ((use_system_ca || openssl_ca) && !X509_STORE_set_default_paths(store)) {
X509_STORE_free(store);
return NULL;
}
Expand All @@ -223,7 +242,7 @@ extern "C" X509_STORE *us_get_default_ca_store() {
STACK_OF(X509) *root_extra_cert_instances = default_ca_certificates->root_extra_cert_instances;

// load all root_cert_instances on the default ca store
for (size_t i = 0; i < root_certs_size; i++) {
for (size_t i = 0; !openssl_ca && i < root_certs_size; i++) {
X509 *cert = root_cert_instances[i];
if (cert == NULL)
continue;
Expand All @@ -239,7 +258,7 @@ extern "C" X509_STORE *us_get_default_ca_store() {
}
}

if (us_should_use_system_ca()) {
if (use_system_ca && !openssl_ca) {
STACK_OF(X509) *root_system_cert_instances = us_get_root_system_cert_instances();
if (root_system_cert_instances) {
for (int i = 0; i < sk_X509_num(root_system_cert_instances); i++) {
Expand All @@ -253,20 +272,29 @@ extern "C" X509_STORE *us_get_default_ca_store() {
return store;
}

// Process-wide immutable default store. Safe to share across SSL_CTXs that
// don't add per-config CAs (the user-`ca` path in build_raw populates the
// SSL_CTX's own private, initially-empty store instead). This makes the
// ~150-root build a once-per-process cost instead of once-per-SSL_CTX, which
// is what kept Bun.connect({tls:true}) under the node-tls-server.test.ts
// 100ms cold-path budget in debug+ASAN.
extern "C" X509_STORE *us_get_shared_default_ca_store() {
static X509_STORE *shared = nullptr;
static std::once_flag once;
std::call_once(once, []() { shared = us_get_default_ca_store(); });
// Process-wide immutable default stores, one per system-CA decision. Safe to share across SSL_CTXs
// that don't add per-config CAs (the user-`ca` path in build_raw populates the SSL_CTX's own
// private, initially-empty store instead). This makes the ~150-root build a once-per-process cost
// (per variant actually used) instead of once-per-SSL_CTX, which is what kept
// Bun.connect({tls:true}) under the node-tls-server.test.ts 100ms cold-path budget in debug+ASAN.
static std::atomic<X509_STORE *> shared_default_ca_store[2] = { nullptr, nullptr };

extern "C" X509_STORE *us_get_shared_default_ca_store(int use_system_ca) {
static std::once_flag once[2];
int i = use_system_ca ? 1 : 0;
std::call_once(once[i], [i]() { shared_default_ca_store[i].store(us_get_default_ca_store(i)); });
X509_STORE *shared = shared_default_ca_store[i].load();
if (shared) X509_STORE_up_ref(shared);
return shared;
}

// Whether `store` is one of the process-shared default stores (as opposed to a context's own).
// Compares against whatever has been built so far; builds nothing.
extern "C" int us_is_shared_default_ca_store(X509_STORE *store) {
return store != nullptr
&& (store == shared_default_ca_store[0].load() || store == shared_default_ca_store[1].load());
}

extern "C" const char *us_get_default_ciphers() {
return DEFAULT_CIPHER_LIST;
}
Expand Down
9 changes: 7 additions & 2 deletions packages/bun-usockets/src/crypto/root_certs_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ STACK_OF(X509) *us_get_root_system_cert_instances();
#define CPPDECL extern
#endif

CPPDECL X509_STORE *us_get_default_ca_store();
CPPDECL X509_STORE *us_get_shared_default_ca_store();
CPPDECL int us_default_use_system_ca();
CPPDECL int us_resolve_use_system_ca(int requested);
CPPDECL X509_STORE *us_get_default_ca_store(int use_system_ca);
CPPDECL X509_STORE *us_get_shared_default_ca_store(int use_system_ca);
CPPDECL int us_is_shared_default_ca_store(X509_STORE *store);
/* The resolved system-CA decision an SSL_CTX built by us_ssl_ctx_build_raw was created with. */
CPPDECL int us_ssl_ctx_use_system_ca(SSL_CTX *ctx);
16 changes: 16 additions & 0 deletions packages/bun-usockets/src/eventing/epoll_kqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,10 @@ void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout
}
}

const uint64_t idle_start_ns = will_idle_inside_event_loop ? us_internal_monotonic_ns() : 0;
if (will_idle_inside_event_loop)
__atomic_store_n(&loop->data.idle_entry_ns, idle_start_ns, __ATOMIC_SEQ_CST);

/* Fetch ready polls */
#ifdef LIBUS_USE_EPOLL
/* A zero timespec already has a fast path in ep_poll (fs/eventpoll.c):
Expand All @@ -533,6 +537,18 @@ void us_loop_run_bun_tick(struct us_loop_t *loop, const struct timespec* timeout
timeout);
#endif

if (will_idle_inside_event_loop) {
/* us_loop_idle_ns (another thread) retries while idle_seq is odd or changed underneath it, so
* it never observes the entry cleared without the park added (a non-monotonic sample). */
__atomic_add_fetch(&loop->data.idle_seq, 1, __ATOMIC_SEQ_CST);
/* Clock read inside the odd window: a reader's own clock read (taken before it validated an
* even seq) is then never later than the park length we record, so samples stay monotonic. */
uint64_t now = us_internal_monotonic_ns();
__atomic_store_n(&loop->data.idle_entry_ns, 0, __ATOMIC_SEQ_CST);
__atomic_add_fetch(&loop->data.idle_ns, now - idle_start_ns, __ATOMIC_SEQ_CST);
__atomic_add_fetch(&loop->data.idle_seq, 1, __ATOMIC_SEQ_CST);
}
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.
Comment thread
robobun marked this conversation as resolved.

/* Before anything can allocate again. */
if (handed_off)
mi_on_thread_idle_end();
Expand Down
1 change: 1 addition & 0 deletions packages/bun-usockets/src/eventing/libuv.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ struct us_loop_t *us_create_loop(void *hint,

loop->uv_loop = hint ? hint : uv_loop_new();
loop->is_default = hint != 0;
uv_loop_configure(loop->uv_loop, UV_METRICS_IDLE_TIME);

loop->uv_pre = us_malloc(sizeof(uv_prepare_t));
uv_prepare_init(loop->uv_loop, loop->uv_pre);
Expand Down
3 changes: 3 additions & 0 deletions packages/bun-usockets/src/internal/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ uint64_t us_internal_monotonic_ns(void);
long long us_internal_sweep_timeout_ns(struct us_loop_t *loop);
void us_internal_sweep_if_due(struct us_loop_t *loop);
#endif
/* Nanoseconds this loop has spent parked, including a park in progress. Safe
* from another thread. Both platforms: Rust calls it ungated. */
uint64_t us_loop_idle_ns(struct us_loop_t *loop);
void us_internal_free_closed_sockets(us_loop_r loop);
void us_internal_loop_link_group(struct us_loop_t *loop, struct us_socket_group_t *group);
void us_internal_loop_unlink_group(struct us_loop_t *loop, struct us_socket_group_t *group);
Expand Down
9 changes: 9 additions & 0 deletions packages/bun-usockets/src/internal/loop_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ struct us_internal_loop_data_t {
* for lsquic's time-driven state. POSIX folds the deadline into the
* epoll_pwait2 timeout via getTimeout() instead. */
struct us_timer_t *quic_timer;
#endif
#ifndef LIBUS_USE_LIBUV
/* Nanoseconds parked, for eventLoopUtilization(). Read cross-thread —
* __atomic_* only. MIRRORED in src/uws_sys/InternalLoopData.rs: this struct
* is us_loop_t's first member, so a field here shifts num_polls. */
unsigned long long idle_ns;
unsigned long long idle_entry_ns;
/* Seqlock over the park-exit update of the two fields above (odd while in progress). */
unsigned long long idle_seq;
#endif
struct us_socket_group_t *iterator;
char *recv_buf;
Expand Down
4 changes: 4 additions & 0 deletions packages/bun-usockets/src/libusockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,10 @@ struct us_bun_socket_context_options_t {
const char *sigalgs;
/* Colon-separated named-group list applied via SSL_CTX_set1_groups_list. */
const char *ecdh_curve;
/* Whether the default root store of this context includes the system's trusted CAs (node's
* per-Environment --use-system-ca): 0 = the process default (CLI flags / NODE_USE_SYSTEM_CA),
* 1 = include, -1 = exclude. Only matters when no `ca`/`ca_file_name` is given. */
int use_system_ca;
};

enum create_bun_socket_error_t {
Expand Down
30 changes: 30 additions & 0 deletions packages/bun-usockets/src/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,36 @@ void us_internal_sweep_if_due(struct us_loop_t *loop) {
#endif


/* The clock us_loop_idle_ns accumulates in, so eventLoopUtilization's elapsed and idle share one
* time base (they diverge across system sleep otherwise: CLOCK_MONOTONIC keeps counting on macOS,
* the uptime clock std::time::Instant uses does not). */
uint64_t us_loop_idle_clock_ns(void) {
#ifdef LIBUS_USE_LIBUV
return uv_hrtime();
#else
return us_internal_monotonic_ns();
#endif
}

uint64_t us_loop_idle_ns(struct us_loop_t *loop) {
#ifdef LIBUS_USE_LIBUV
return uv_metrics_idle_time(loop->uv_loop);
#else
uint64_t idle, entry, now;
for (;;) {
uint64_t seq = __atomic_load_n(&loop->data.idle_seq, __ATOMIC_SEQ_CST);
if (seq & 1) continue;
idle = __atomic_load_n(&loop->data.idle_ns, __ATOMIC_SEQ_CST);
entry = __atomic_load_n(&loop->data.idle_entry_ns, __ATOMIC_SEQ_CST);
now = entry > 0 ? us_internal_monotonic_ns() : 0;
if (__atomic_load_n(&loop->data.idle_seq, __ATOMIC_SEQ_CST) == seq) break;
}
if (entry > 0 && now > entry)
idle += now - entry;
return idle;
#endif
}

void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct us_loop_t *loop),
void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop)) {
// We allocate with calloc, so we only need to initialize the specific fields in use.
Expand Down
8 changes: 5 additions & 3 deletions packages/bun-usockets/src/quic.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
#include <ws2tcpip.h>
#endif

#include "crypto/root_certs_header.h"

extern SSL_CTX *us_ssl_ctx_build_raw(
struct us_bun_socket_context_options_t options,
enum create_bun_socket_error_t *err);
extern X509_STORE *us_get_default_ca_store(void);

#define US_QUIC_READ_BUF (16 * 1024)

Expand Down Expand Up @@ -1156,8 +1157,9 @@ us_quic_socket_context_t *us_create_quic_client_context(
SSL_CTX_set_max_proto_version(ssl, TLS1_3_VERSION);
/* Same root store the H1/H2 client uses (bundled Mozilla roots + platform
* CAs + NODE_EXTRA_CA_CERTS); set_default_verify_paths alone doesn't find
* the system store on macOS/Windows. */
SSL_CTX_set_cert_store(ssl, us_get_default_ca_store());
* the system store on macOS/Windows. The shared store is never mutated on
* this path, and set_cert_store takes the getter's up-ref. */
SSL_CTX_set_cert_store(ssl, us_get_shared_default_ca_store(us_default_use_system_ca()));
SSL_CTX_set_custom_verify(ssl, SSL_VERIFY_PEER, us_quic_client_verify);

us_quic_socket_context_t *ctx = (us_quic_socket_context_t *)
Expand Down
2 changes: 2 additions & 0 deletions packages/bun-uws/src/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ namespace uWS {
int allow_partial_trust_chain = 0;
const char *sigalgs = nullptr;
const char *ecdh_curve = nullptr;
/* 0 = process default, 1 = include system CAs, -1 = exclude (see libusockets.h) */
int use_system_ca = 0;

/* Conversion operator used internally */
operator struct us_bun_socket_context_options_t() const {
Expand Down
Loading
Loading