Check the normalized SHA algorithm instead of reading the caller hash
name before conversion. This prevents changing getters from bypassing
the supported hash check.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
Reject non-ASCII equivalents of registered Web Crypto algorithm names.
Skip the character scan when the input already uses the canonical name.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
Read buffer lengths and element sizes from internal slots in
randomFillSync() and randomFill(). Shadowed properties can otherwise
skip filling, change the selected range, or fail a native bounds check.
Use the intrinsic byte length for the getRandomValues() quota check and
keep its delegation to randomFillSync().
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66237
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
The reserved root `${os.devNull}/vfs`, which holds the mount points of
all virtual file systems, could not be read: fs calls on it fell through
to the real file system, so nothing could list what was mounted.
While any file system is mounted, serve the root as a read-only
directory. It lists every mount point by the last segment of its path, a
recursive listing descends into each mounted file system, and paths
under it that no mount serves report ENOENT. Creating, removing or
changing entries in it fails with EROFS. When nothing is mounted it does
not exist, as before.
Add vfs.vfsBase(), which returns the path of that directory, so that a
program can read it without spelling the path out.
A mount point cannot be removed or renamed, nor replaced by a rename:
rmdir() and rename() fail with EBUSY, and a recursive rm() empties the
file system and then fails the same way. Before, rmdir() of an empty
mount point reported success without doing anything.
The callback and promise forms of readdir() with `withFileTypes` now
report each Dirent's parentPath as a host path, as readdirSync() did,
instead of the provider-relative one, and split recursive names such as
`dir/file.txt` into their directory and base name. A recursive listing
joins subdirectories with the host separator rather than `/`, which
mixed separators on Windows. realpath() of a mount point no longer
returns it with a trailing separator.
Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/66140
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Getting the values recorded during an interval requires calling
`reset()`, which removes them for every other user of the histogram,
or copying and subtracting histograms, which silently produces a wrong
result when the source was reset in between.
Add `histogram.diff(other)`, which returns a new read-only `Histogram`
containing the values recorded after `other`, an earlier snapshot of
the histogram, was taken. Neither histogram is changed. Unlike
`subtract()`, it verifies that both histograms have the same layout,
and it throws instead of clamping when `other` contains values that
the histogram does not.
Add `histogram.resetCount`, the number of calls to `reset()` and
`subtract()`, which `snapshot()` copies. `diff()` throws when the
counts differ, so a reset between two snapshots is detected even when
every bucket has since grown past its previous count.
Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-by: OpenCode
PR-URL: https://github.com/nodejs/node/pull/66099
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Cloning a histogram with `structuredClone()` or `postMessage()` shares
the native histogram instead of copying it. Capturing the state of a
histogram at a point in time, while other code keeps recording into
it, requires serializing it with `export()` and parsing the result
with `importHistogram()`.
Add `histogram.snapshot()`, which returns a new, independent
`Histogram` containing a copy of the histogram's configuration,
recorded values, `exceeds` count, and EWMA state, without the
serialization round trip. Values cannot be recorded into the returned
histogram. The method is available on all histograms, including
`RecordableHistogram` and `ELDHistogram` instances.
Signed-off-by: James M Snell <jasnell@gmail.com>
Assisted-by: OpenCode
PR-URL: https://github.com/nodejs/node/pull/66099
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
--vfs-mount mounted a source without running it, and shared one ordered
list of sources with --vfs-load, so neither option could say which entry
it had contributed: the entry point was recovered from the position of
--vfs-load among the mounts, in a list NODE_OPTIONS could prepend to.
Nothing needs more than one mount from the command line: a program that
wants more can mount them itself through node:vfs, where it also gets
the instance.
Remove --vfs-mount, leaving --vfs-load with the single source it mounts
and runs, and reserve layer 0 for that source, numbering the file
systems a program mounts itself from 1. The source is then at the same
mount point in every thread, whatever else that thread mounts -
including a thread where a --require preload mounted a file system of
its own first - so a path into it stays valid in a worker.
A worker still does not run that entry point: it inherits the source but
not the decision to load from it. A worker created with its own execArgv
inherits neither, so the documentation now says that such a worker must
be given --experimental-vfs and --vfs-load again to run a script from
the mount, and that --experimental-vfs is also what makes node:vfs
available to the worker's own code.
ERR_VFS_INVALID_TARGET now names --vfs-load as the source's origin, and
the startup test moves to test-vfs-load.js, with the cases that covered
mounting without loading removed and cases for the reserved mount point
added.
Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/66162
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Refresh conditional algorithm registration on FIPS changes and use the
current state for parameter restrictions.
Use per-algorithm availability checks in normalization and native named
key generation. Remove getPqcKeyTypes().
Enable supports() tests under FIPS and cover state transitions in warmed
workers.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66160
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
ZipProvider threw ERR_METHOD_NOT_IMPLEMENTED for readdir() with
`{ recursive: true }`, although an archive already stores every member
under its full path, so a recursive listing is the same scan of the
entry names as a flat one, keeping the whole path below the directory
instead of only its first segment.
List every member below the directory, and every directory their paths
pass through, whether the archive holds an entry for it or only implies
it. Names are relative to the listed directory and joined with `/`, as
MemoryProvider returns them, so VirtualFileSystem reports the right
parentPath for each Dirent. A name that is both a member and a
directory is listed once, as a directory, as a flat listing already
did.
The entries are now collected in a map rather than looked up in an
array, so listing a directory no longer takes time quadratic in the
number of its children.
Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/66127
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
An object literal with computed symbol keys is rebuilt through the
runtime every time it is evaluated, close to a microsecond each. pipeTo,
tee and the byte tee materialized their internal read request as such
a literal once per pipe or tee, and the byte tee's BYOB path did so on
every read. The requests are now instances of one small class holding
the three step functions.
A source without pull() ran the pull bookkeeping anyway: two reaction
closures and a microtask per read whose only effect was to clear the
pulling flag. Push-style sources now skip it.
The default tee's pull algorithm was an async function and the byte
tee's returned a fresh resolved promise; both now return nothing, which
reaches the controller's pull-fulfilled step at the same microtask
position without the promise.
ReadableStreamBYOBReader.prototype.read() was an async method, so the
read request's promise was adopted through a wrapper (an extra promise
and two microtask hops per read). It now returns the request's promise
directly, as the spec does; argument errors still become rejections.
A TransformStream without start() no longer allocates the start promise
record: the sides adopt a promise that is already resolved either way.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66154
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Mattias Buelens <mattias@buelens.com>
util.inspect() (and console.log()) silently dropped a SuppressedError's
`error` and `suppressed` properties, the two pieces of information
needed to actually debug a disposal failure. Both properties are
non-enumerable, so they never showed up without this.
formatError() already special-cases two other non-enumerable
"container" properties for the same reason: Error's `cause` (#41002)
and AggregateError's `errors` (#43646). Handle SuppressedError's
`error` and `suppressed` the same way; nested SuppressedErrors (from
multiple failed disposals) are shown recursively since inspect()
already recurses into any Error-valued property.
Fixes: https://github.com/nodejs/node/issues/66033
Signed-off-by: agape1225 <49804691+agape1225@users.noreply.github.com>
PR-URL: https://github.com/nodejs/node/pull/66159
Fixes: https://github.com/nodejs/node/issues/66033
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Jordan Harband <ljharb@gmail.com>
V8 GC callbacks do not survive a snapshot, so a `'gc'`
PerformanceObserver that was active while building a user-land
snapshot received no entries after deserialization, unless another
`'gc'` observer was created.
When `'gc'` is observed while building a snapshot, add a deserialize
callback that registers the GC callbacks again if `'gc'` observers are
still active.
Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66097
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Whether the V8 GC callbacks used for `'gc'` performance entries were
installed was tracked by a boolean in JavaScript, separately from the
native state, and the two could get out of sync. After deserializing a
user-land snapshot built while a `'gc'` PerformanceObserver was active,
the boolean claimed the callbacks were installed, although V8 GC
callbacks do not survive a snapshot. Observing `'gc'` again did not
install them, and disconnecting removed callbacks that had never been
registered, crashing the process.
Track the installation state in `PerformanceState` instead, and replace
the install and remove bindings with a single idempotent
`updateGarbageCollectionTracking()` binding, which registers the
callbacks if and only if there are `'gc'` observers.
Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66097
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Observing the same entry type more than once on a PerformanceObserver,
through repeated `observe({ type })` calls or duplicates in
`entryTypes`, incremented the internal observer count each time, while
`disconnect()` decremented it only once per observed type. The counts
never returned to zero, so GC tracking stayed installed, and entries
for types such as `'http'` and `'dns'` kept being created after all
observers had disconnected.
Count each entry type at most once per observer.
Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66097
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Pass group names through EC key generation and report provider names
in key details without requiring an OpenSSL NID. Preserve established
curve aliases and synchronous invalid-curve errors.
Filter built-in curves through EC parameter generation and refresh
getCurves() results when FIPS properties change.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/66108
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Short-lived streams (create, a few chunks, close) pay a fixed cost per
stream that dominates once the per-chunk path is lean.
Streams created internally (transform stream sides, tee branches,
ReadableStream.from, transferred streams) were built by wrapper
constructors that swapped the prototype of every instance and then
assigned an own, enumerable `constructor` property to look like a
public stream. Each internal stream therefore had its own hidden class
and `Object.keys(stream)` reported `['constructor']`. The public
constructors now accept the internal construction sentinel and leave
controller setup to the caller, so every ReadableStream and
WritableStream shares one hidden class and no per-instance prototype
swap or own property is needed.
The queue ring buffer grew after a push filled it, so the initial 8-slot
ring held only three (value, size) pairs and a four-chunk stream
reallocated every time. Growing before the push lets the ring hold four
pairs.
pipeTo observed the source's closed promise with two reactions and, on
teardown, let the reader and writer release paths probe and reject
promise records that only the pipe could have observed. One reaction
pair now watches the source, and finalize drops the records before
release.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66052
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
`performance.nodeTiming.uvMetricsInfo` returns the libuv event loop
metrics as numbers, which are only exact up to
`Number.MAX_SAFE_INTEGER`. Add `uvMetricsInfoBigInt`, which returns
the same metrics as bigints backed by `uint64_t` storage, carrying the
full 64-bit range reported by libuv.
A single native call fills both a `Float64Array` and a
`BigUint64Array`, so `uvMetricsInfo` does not pay for bigint
allocation and conversion. The new property is omitted from
`toJSON()`, as `JSON.stringify()` cannot serialize bigints.
Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66094
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
When "vfsArchive" names a ZIP archive in the SEA configuration (with
"useVfs": true), --build-sea embeds the archive verbatim as one
reserved asset, and at runtime the SEA virtual file system mounts the
existing ZipProvider over a zero-copy view of the embedded archive
instead of the SEAProvider. The main script is injected into the
in-memory archive index as a stored entry, so the mounted tree looks
the same as with "assets".
The archive can be produced with any ZIP tool or with the ZIP support
in node:zlib; no ZIP serialization logic is added to the build side.
This trades asset read speed (entries are inflated when opened) for a
substantially smaller executable when the assets are compressible.
Also make FindSingleExecutableResource() return the deserialized
SeaResource by reference instead of by value: the copy included the
whole assets map, and getAsset() calls it once per asset read, so
every fs operation served by the SEA virtual file system paid a cost
linear in the number of bundled assets - with 8192 assets, reading
each of them took seconds instead of milliseconds.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/65810
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Remove allocations and repeated work performed for every request:
- reuse a per-connection updateOutgoingData closure and a shared
'finish' listener instead of binding two functions per request,
reaching resOnFinish through the connection state stored on the
response
- cache the ServerResponse options object per server (custom response
classes keep receiving a fresh object)
- check for Host, Expect, Content-Length and Transfer-Encoding by
scanning rawHeaders instead of materializing req.headers, which was
built (with per-name toLowerCase calls) for every request even when
the application never reads it
- cache the rendered status line per status code when the reason
phrase is the default, skipping its character validation
- cache the complete 'Date: ...' header line in the utcDate cache and
the keep-alive header pair for the current server settings
- compute the lenient-validation option chain once per message
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/65802
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Async iteration left the writable side of `allowHalfOpen` duplexes open
after clean EOF. For `net.Socket` instances, this retained a live handle
after the peer disconnected.
Restore default iterator destruction. Callers that need to continue
writing can use `iterator({ destroyOnReturn: false })`.
Assisted-by: Codex
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/65986
Fixes: https://github.com/nodejs/node/issues/65110
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Mounting a virtual file system requires calling vfs.mount() from inside
the program, so a program cannot itself be served from one: something
already running has to mount the VFS first.
Add two startup flags. --vfs-mount=<source> mounts a directory or an
archive as a virtual file system, and may be repeated.
--vfs-load=<source> mounts that source the same way and additionally
runs the entry point and all subsequent require()/import resolution
against it rather than against the real file system, so an application
can be run straight out of a directory or a ZIP archive:
node --experimental-vfs --vfs-load=my-app.zip
Both options append to one list, so mounts happen in the order written:
node --experimental-vfs --vfs-mount=a --vfs-load=b --vfs-mount=c
mounts a, b and c in that order and runs b. Mounting the same source
twice mounts it twice, at two mount points, and the entry point comes
from the mount --vfs-load contributed rather than the earlier one.
The provider backing a source is chosen from the source itself rather
than from its name: a directory is served by RealFSProvider and a file
whose bytes are a ZIP archive by ZipProvider, so an archive can carry
any extension. vfs.registerProvider() registers a provider for formats
there is no built-in for; selection is deferred until -r and --import
preloads have run, so a preloaded module can register one before its
source is claimed.
The entry point comes from the mount the way `node <directory>` takes
one, from package.json "main" or index.js. Nothing is consumed as an
entry point argument, so every positional reaches the program unchanged
from argv[2] on, and argv[1] reports the named source rather than the
reserved mount point, which is an opaque implementation detail.
--vfs-load may be given at most once, and is not permitted in
NODE_OPTIONS: which entry point runs is the command line's decision, and
NODE_OPTIONS is parsed first, so an environment variable could otherwise
redirect any invocation. --vfs-mount is permitted there. Workers inherit
the same mounts in the same order but not the loading: a worker mounts
what the parent mounted and runs its own entry point.
Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/65748
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Provider-backed keys can have no numeric OpenSSL ID. Identify and
construct asymmetric keys by algorithm name, replacing the custom PQC
name-to-NID substitution with provider-aware matching in ncrypto.
Centralize known algorithm names, public key-type names, capabilities,
and backend compatibility in ncrypto. Use named key generation jobs and
remove asymmetric EVP_PKEY constants from the internal JavaScript
binding.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/65966
Reviewed-By: James M Snell <jasnell@gmail.com>