When OutgoingMessage transitions from pre-socket buffering (Path B) to
socket-connected writing (Path A), the backpressure domain changes —
subsequent writes go directly to the socket, which enforces its own
backpressure via socket.write() return values. The OM should emit
drain at this transition point to signal that its buffer is clear and
the caller can resume writing under the socket backpressure regime.
Previously, _flush() gated drain emission on writableLength === 0
which included socket.writableLength. This conflated two independent
backpressure domains: the OM pre-socket buffer and the socket kernel
write queue. When the socket had a higher writableHighWaterMark than
the OM (e.g. agent-reused socket from a prior request), the socket
was never backpressured and never emitted drain, causing a permanent
deadlock.
Additionally, avoid reusing a pooled socket in http.Agent when its
writableHighWaterMark differs from the request highWaterMark, so that
the user backpressure threshold is respected for the common case of
the built-in Agent.
Signed-off-by: Naman Trivedi <trivenay@amazon.com>
Fixes: https://github.com/nodejs/node/issues/64680
Refs: https://github.com/nodejs/node/pull/64653
Refs: https://github.com/nodejs/node/pull/62936
PR-URL: https://github.com/nodejs/node/pull/64991
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Previously if you used TLS 1.3 and the server requested a client cert,
but the client didn't send one, and you used rejectUnauthorized:false
the resumed session would report authorized=true. This doesn't match TLS
1.2 behaviour or make any sense, and was purely an artifact of our
internal logic for handling TLS 1.3 resumption details. We now correctly
report the authorization state and/or error from the original connection
in all cases, with a matrix test that fully checks the invariant:
authorized state after resume should always match the initial state.
Signed-off-by: Tim Perry <pimterry@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/64677
Reviewed-By: James M Snell <jasnell@gmail.com>
* fix duplicate address insertion in SocketAddressBlockList
* fix BlockList rule listing order to match apply
* add minor bound check in BlockList
* improve performance of BlockList apply
* eliminating shared_ptr
* check fast api path
* add clear method to BlockList
* general storage improvements to BlockList
* use shared locks for BlockList reads
* add bulk address adding to BlockList
* add BlockList benchmark
* add remove range/subnet to BlockList
* add cidr notation parsing to BlockList
* add additional apis to BlockList
* add private subnet presets to BlockList
Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-by: OpenCode/Opus
PR-URL: https://github.com/nodejs/node/pull/64974
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
ZIP archives can contain more than one structurally plausible EOCD
record. Selecting based on whether a comment reaches EOF can make the
chosen central directory depend on trailing padding.
Inspect all candidates in the common tail window and reject archives
with multiple plausible interpretations.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/65007
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Add an optional `alignment` argument to `Buffer.allocUnsafe()` and
`Buffer.allocUnsafeSlow()`, which guarantees that the memory backing the
returned buffer starts at an address that is a multiple of `alignment`.
Some operating system interfaces refuse to work with unaligned memory.
The motivating case is unbuffered ("direct") file I/O: a read or write
on a descriptor opened with `O_DIRECT` fails with `EINVAL` unless the
buffer address is a multiple of the logical block size of the underlying
device. Until now there was no way to obtain such a buffer from JS,
since the address of a backing store can neither be observed nor chosen.
Alignment is also worth having purely for performance, for instance to
keep a hot buffer from straddling one more cache line than its size
requires.
V8 does not allow picking the address of a backing store, so alignment
is instead achieved by over-allocating `alignment - 1` bytes and
positioning the buffer at the first suitably aligned byte within them.
The new `arrayBufferAlignedOffset()` binding computes that offset.
Addresses are not stable across snapshot serialization, so the binding
reports no padding while a snapshot is being built. Otherwise the
snapshot would capture where this particular process happened to
allocate and stop being reproducible. Buffers restored from a snapshot
are consequently not aligned; the pool works around this by recreating
itself in a deserialize callback.
The `Buffer.allocUnsafe()` pool is now aligned to a cache line itself,
which lets pooled allocations satisfy any alignment up to 64 bytes by
padding their offset into the pool rather than allocating separately.
Assisted-by: Claude/Opus 5
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Robert Nagy <ronagy@icloud.com>
PR-URL: https://github.com/nodejs/node/pull/65003
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
This existed for 'resumeSession', which needed to do an async lookup
though SSL_CTX_sess_set_get_cb is sync-only. Nowadays both OpenSSL &
BoringSSL have an early ClientHello callback for suspend/resume to
handle this properly, so it was redundant, in addition to being
complicated and generally a bit fragile & scary.
This PR switches to use the modern OpenSSL/BoringSSL mechanisms for this
and drops the client hello parser & related infrastructure completely.
In addition, there's a new test here, covering a fixed bug: the hello
parser silently dropped fragmented hellos, which we now do handle
correctly.
Signed-off-by: Tim Perry <pimterry@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/64827
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
readableStreamPipeTo allocated, for every chunk written to the
destination, a { promise, resolve, reject } write request record that
it immediately marked as handled, and drove its loop with an async
step()/run() pair whose implicit promises cost one allocation and one
reaction per iteration. The parked-read path additionally allocated a
read request object, a PromiseWithResolvers record, and a microtask
closure per chunk; this is the steady state for pipeThrough, since a
TransformStream's readable side has a high water mark of zero.
Replace the per-write records with a single per-pipe tracker that the
write request queue holds once per pending write and whose
resolve()/reject() methods maintain a pending-write count, drive the
pump loop with plain callbacks instead of async functions, and reuse
one read request and one forwarding function across all chunks, the
same pattern tee uses since c543cfb72b.
Benchmark results (benchmark/compare.js --runs 20):
webstreams/pipe-to.js +29.9% to +35.8% across all 16 configurations
(all 99.9% confidence); a pipeThrough(TransformStream) passthrough
loop improves ~17%; every other webstreams benchmark is unchanged.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/64890
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Only emit 'finish' and set writableFinished once all data has actually
been flushed successfully. end() callbacks now report the outcome like
stream.Writable: called with null on finish, or with the error that
prevented the flush.
Signed-off-by: Tim Perry <pimterry@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/64847
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Zero-byte writes append entries to the broadcast buffer without
increasing bufferedBytes. This allows write('') and writev([]) to
bypass backpressure and grow the buffer without bound.
Treat zero-byte batches as successful no-ops, matching push streams,
and add coverage for the synchronous and asynchronous writer methods.
Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com>
Assisted-by: codex:gpt-5.6-sol
PR-URL: https://github.com/nodejs/node/pull/64772
Fixes: https://github.com/nodejs/node/issues/64771
Reviewed-By: Aviv Keller <me@aviv.sh>
Accept WHATWG URL objects in private-key inputs and load referenced keys
through OpenSSL STORE loaders. Pass optional property queries and
passphrases while preserving provider-owned EVP_PKEY objects for
ordinary KeyObject and CryptoKey operations.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/63949
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Optimized Fast API calls bypass InvokeFunction and can jump directly to
a symbol after DynamicLibrary::close() unloads its library.
Check the function's closed state in the AArch64 and SysV x64
trampolines before entering the target. If the library is closed,
schedule ERR_FFI_LIBRARY_CLOSED and return without calling the symbol.
Keep the JavaScript guard on platforms without a native trampoline guard
and for signatures that already require argument conversion or
validation. This keeps raw scalar fast calls close to their original
performance on supported platforms.
Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com>
Assisted-by: codex:gpt-5.6-sol
PR-URL: https://github.com/nodejs/node/pull/64860
Fixes: https://github.com/nodejs/node/issues/64854
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Promote DEP0195 from documentation-only to a runtime deprecation.
Calling node:http constructors without `new` now emits DEP0195 via
deprecateInstantiation. This covers Agent, Server, OutgoingMessage,
IncomingMessage, ServerResponse, and ClientRequest.
Update in-tree tests that called Server/Agent without `new` to use
the keyword, and add a dedicated DEP0195 coverage test.
Refs: https://github.com/nodejs/node/pull/58518
Assisted-by: Grok
Signed-off-by: Yagiz Nizipli <yagiz@nizipli.com>
PR-URL: https://github.com/nodejs/node/pull/64853
Reviewed-By: Aviv Keller <me@aviv.sh>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Add ZIP archive support to the node:zlib module through three classes
and a set of helpers:
- ZipEntry: a single archive member, with buffered reads (content()),
bounded-memory streaming reads (contentIterator()), and
create()/createStream() for building members.
- ZipFile: random access to an archive backed by a file descriptor,
reading members lazily without retaining their content and writing
new members in place; opened with open()/openSync().
- ZipBuffer: a zero-copy, in-memory view over an archive already held
in a Buffer.
createZipArchive() serializes a sequence of entries into an archive
byte stream, and setMaxZipContentSize() bounds the default in-memory
decompression size. Every operation has both an asynchronous and a
synchronous form.
Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/64339
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
The byte controller's [[pendingPullIntos]] list was still a plain array
consumed with ArrayPrototypeShift, while every other per-chunk queue in
the WHATWG streams implementation has moved to the Queue ring buffer.
BYOB reads push and shift one descriptor per read, and Array.prototype
shift has real per-call cost even at length 1.
Back the descriptor list with the same lazily materialized Queue used
for the request queues, so constructing a byte stream still allocates
no descriptor storage.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/64818
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Aviv Keller <me@aviv.sh>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
IncomingMessage 'close' is emitted when request handling completes, not
only when the underlying connection is closed. Using that event to abort
IncomingMessage.signal makes the signal abort after a request body is
read normally.
Track the underlying socket close instead, and detach the listener when
the request or response completes normally. This keeps the signal from
being aborted by normal stream completion or later keep-alive socket
closure, while still aborting it when the connection closes during the
active request lifecycle.
Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com>
PR-URL: https://github.com/nodejs/node/pull/64392
Fixes: https://github.com/nodejs/node/issues/64390
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
The `trackPromises` check duplicated `validateBoolean()` exactly: same
error code, same expected type string, same argument order. Replace it
with the validator, matching how this file already validates `type` and
`fn` via `validateString()`/`validateFunction()`.
`ERR_INVALID_ARG_TYPE` had no other use in this file, so drop the
import. Since `validateBoolean` is wrapped in `hideStackFrames()`, the
thrown error is unchanged from a caller's perspective.
Signed-off-by: Soul Lee <alus20x@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/64731
Refs: https://github.com/nodejs/node/pull/61415
Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
An observed composite signal (one with an `abort` listener) is added to
gcPersistentSignals so it stays alive long enough to fire. When one of
its sources aborted, the composite was marked aborted but never removed
from that set, so it was retained forever and its WeakRef was never
pruned from a long-lived source's kDependantSignals. That set therefore
grew without bound.
Drop each transitively-aborted dependent from gcPersistentSignals, the
same way the directly-aborted signal is already removed, so it can be
collected and its dependant entries pruned.
Fixes: https://github.com/nodejs/node/issues/64476
Signed-off-by: Paul Bouchon <mail@bitpshr.net>
PR-URL: https://github.com/nodejs/node/pull/64481
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Edy Silva <edigleyssonsilva@gmail.com>