Notable changes: deps: * (SEMVER-MINOR) update OpenSSL build config to support compression (Tim Perry) https://github.com/nodejs/node/pull/62217 doc: * (SEMVER-MINOR) update `blockList` stability status to release candidate (alphaleadership) https://github.com/nodejs/node/pull/63050 fs: * (SEMVER-MINOR) support caller-supplied `readFile()` buffers (Matteo Collina) https://github.com/nodejs/node/pull/63634 http: * (SEMVER-MINOR) close pre-request sockets in `closeIdleConnections` (semimikoh) https://github.com/nodejs/node/pull/63470 loader: * (SEMVER-MINOR) implement package maps (Maël Nison) https://github.com/nodejs/node/pull/62239 net: * (SEMVER-MINOR) support `TCP_KEEPINTVL` and `TCP_KEEPCNT` in `setKeepAlive` (Guy Bedford) https://github.com/nodejs/node/pull/63825 tls: * (SEMVER-MINOR) add `certificateCompression` option (Tim Perry) https://github.com/nodejs/node/pull/62217 vfs: * (SEMVER-MINOR) dispatch `node:fs/promises` to mounted VFS instances (Matteo Collina) https://github.com/nodejs/node/pull/63537 * (SEMVER-MINOR) add minimal `node:vfs` subsystem (Matteo Collina) https://github.com/nodejs/node/pull/63115 PR-URL: https://github.com/nodejs/node/pull/64058
4520 lines
128 KiB
Markdown
4520 lines
128 KiB
Markdown
# QUIC
|
||
|
||
<!-- introduced_in=v23.8.0-->
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
> Stability: 1.0 - Early development
|
||
|
||
<!-- source_link=lib/quic.js -->
|
||
|
||
The 'node:quic' module provides an implementation of the QUIC protocol.
|
||
To access it, start Node.js with the `--experimental-quic` option and:
|
||
|
||
```mjs
|
||
import quic from 'node:quic';
|
||
```
|
||
|
||
```cjs
|
||
const quic = require('node:quic');
|
||
```
|
||
|
||
The module is only available under the `node:` scheme.
|
||
|
||
## Overview
|
||
|
||
The `quic` module provides APIs for creating QUIC clients and servers.
|
||
|
||
### Relevant RFCs and specifications
|
||
|
||
The QUIC and HTTP/3 protocols are defined by a collection of RFCs produced
|
||
primarily by the IETF QUIC Working Group. A familiarity with these documents
|
||
is strongly recommended for users of this module.
|
||
|
||
**Core QUIC transport:**
|
||
|
||
* [RFC 8999][] — Version-Independent Properties of QUIC
|
||
* [RFC 9000][] — QUIC: A UDP-Based Multiplexed and Secure Transport
|
||
* [RFC 9001][] — Using TLS to Secure QUIC
|
||
* [RFC 9002][] — QUIC Loss Detection and Congestion Control
|
||
|
||
**Core HTTP/3:**
|
||
|
||
* [RFC 9114][] — HTTP/3
|
||
* [RFC 9204][] — QPACK: Field Compression for HTTP/3
|
||
|
||
**QUIC extensions:**
|
||
|
||
* [RFC 9221][] — An Unreliable Datagram Extension to QUIC
|
||
* [RFC 9287][] — Greasing the QUIC Bit
|
||
* [RFC 9368][] — Compatible Version Negotiation for QUIC
|
||
* [RFC 9369][] — QUIC Version 2
|
||
* [RFC 9443][] — Multiplexing Scheme Updates for QUIC
|
||
|
||
**HTTP/3 extensions:**
|
||
|
||
* [RFC 9218][] — Extensible Prioritization Scheme for HTTP
|
||
* [RFC 9220][] — Bootstrapping WebSockets with HTTP/3
|
||
* [RFC 9297][] — HTTP Datagrams and the Capsule Protocol
|
||
* [RFC 9412][] — The ORIGIN Extension in HTTP/3
|
||
|
||
**Operational and informational:**
|
||
|
||
* [RFC 9308][] — Applicability of the QUIC Transport Protocol
|
||
* [RFC 9312][] — Manageability of the QUIC Transport Protocol
|
||
|
||
## Architecture
|
||
|
||
The `quic` module is built around three core abstractions:
|
||
|
||
* `QuicEndpoint`: represents the local UDP socket binding for QUIC. It is
|
||
used to send and receive QUIC packets and can be shared across multiple
|
||
sessions. A single endpoint can be used as both a client and a server
|
||
simultaneously.
|
||
|
||
* `QuicSession`: represents a QUIC connection between the local endpoint and
|
||
a remote peer. A session is created either by initiating a connection to a
|
||
remote peer using `quic.connect()` or by accepting an incoming connection
|
||
from a remote peer via `quic.listen()`.
|
||
|
||
* `QuicStream`: represents a QUIC stream within a session. Streams are
|
||
created by either local or remote peers and can be bidirectional or
|
||
unidirectional.
|
||
|
||
Unlike traditional TCP-based protocols, QUIC "connections" are not inherently
|
||
tied to a specific local port / remote port pair. A session is initiated via
|
||
a QUIC endpoint but may be migrated to a different local or remote address
|
||
over its lifetime, outlive the endpoint that created it, and may even be
|
||
associated with multiple endpoints simultaneously. This flexibility allows for
|
||
advanced use cases such as connection migration, multi-homing, and load balancing.
|
||
Most often, however, a simple one-to-one relationship between endpoint and session
|
||
is sufficient.
|
||
|
||
### Integrated TLS 1.3
|
||
|
||
The QUIC protocol integrates TLS 1.3 directly into the protocol for connection
|
||
establishment and security. The `quic` module's API reflects this integration
|
||
by exposing TLS-related information and configuration options. It is currently
|
||
not possible to use QUIC without TLS or to use a different version of TLS.
|
||
|
||
Every QUIC session starts with the client and server performing a TLS handshake
|
||
to negotiate the application protocol (via ALPN), authenticate the server (and
|
||
optionally the client), exchange transport parameters, and establish shared keys
|
||
for encryption.
|
||
|
||
#### Certificate size and handshake performance
|
||
|
||
QUIC includes an anti-amplification limit ([RFC 9000 Section 8.1][]) that
|
||
restricts the server to sending at most three times the data received from
|
||
the client before the client's address is validated. Because the client's
|
||
Initial packet is typically around 1200 bytes, the server can send at most
|
||
approximately 3600 bytes before it must wait for the client to acknowledge.
|
||
|
||
The server's initial response is dominated by its TLS certificate chain. If
|
||
the certificate chain exceeds the amplification limit, the handshake requires
|
||
an additional round trip — the server must pause, wait for the client's
|
||
acknowledgement, and then continue sending the remainder of the certificate.
|
||
This eliminates QUIC's 1-RTT handshake advantage over TCP+TLS and can add
|
||
50–100 ms or more of latency on the first connection, depending on the network
|
||
path.
|
||
|
||
To avoid this, servers should use compact certificate chains:
|
||
|
||
* **Use ECDSA certificates** (P-256 or P-384) rather than RSA. ECDSA keys and
|
||
signatures are significantly smaller. A typical ECDSA P-256 certificate chain
|
||
with one intermediate is approximately 1.5–2 KB, well within the amplification
|
||
limit. An equivalent RSA-2048 chain is often 3–5 KB, which may exceed it.
|
||
|
||
* **Minimize the certificate chain.** Include only the leaf certificate and
|
||
the necessary intermediate(s). Do not include the root certificate (clients
|
||
already have it in their trust store). Avoid cross-signed intermediates when
|
||
the self-signed root is already widely trusted.
|
||
|
||
* **Prefer certificate authorities with short chains.** Some CAs issue
|
||
certificates with a single small intermediate, while others require multiple
|
||
large RSA intermediates. The choice of CA directly affects handshake latency.
|
||
|
||
Certificate compression ([RFC 8879][]) can also address this issue by
|
||
compressing the certificate chain during the handshake. However, Node.js does
|
||
not currently support TLS certificate compression.
|
||
|
||
### Rate limiting
|
||
|
||
QUIC endpoints include built-in rate limiting to protect against
|
||
denial-of-service attacks. There are two layers of defense:
|
||
|
||
**Global rate limits** cap the total rate of stateless responses that the
|
||
endpoint will send, regardless of the source address. These protect against
|
||
floods from spoofed source IP addresses, where an attacker rotates through
|
||
many fake source addresses to bypass per-host limits. Four types of stateless
|
||
responses are independently rate-limited:
|
||
|
||
* **Retry packets** — sent to validate a client's address during connection
|
||
setup. Configurable via [`endpointOptions.retryRate`][] and
|
||
[`endpointOptions.retryBurst`][].
|
||
* **Stateless reset packets** — sent when the endpoint receives a packet for an
|
||
unknown session. Configurable via [`endpointOptions.statelessResetRate`][]
|
||
and [`endpointOptions.statelessResetBurst`][].
|
||
* **Version negotiation packets** — sent when a client uses an unsupported QUIC
|
||
version. Configurable via [`endpointOptions.versionNegotiationRate`][] and
|
||
[`endpointOptions.versionNegotiationBurst`][].
|
||
* **Immediate connection close packets** — sent when the server is busy or a
|
||
token is invalid. Configurable via [`endpointOptions.immediateCloseRate`][]
|
||
and [`endpointOptions.immediateCloseBurst`][].
|
||
|
||
Each rate limit uses a token bucket: the endpoint can send up to the burst
|
||
capacity instantly, and tokens refill at the configured rate per second. When
|
||
the bucket is empty, additional responses of that type are silently dropped.
|
||
The defaults (100 per second, burst of 200) are suitable for most deployments.
|
||
|
||
**Per-host session creation rate limits** cap how fast a single remote address
|
||
can create new sessions. This is tracked per validated remote address and
|
||
prevents a single client from churning through sessions (rapidly connecting and
|
||
disconnecting) to consume server resources. Configurable via
|
||
[`endpointOptions.sessionCreationRate`][] and
|
||
[`endpointOptions.sessionCreationBurst`][]. The defaults (50 per second, burst
|
||
of 100) are generous enough for legitimate traffic patterns. For benchmarking
|
||
scenarios where traffic comes from a single source, increase these values.
|
||
|
||
In addition to rate limiting, the endpoint supports **concurrent connection
|
||
limits** via `maxConnectionsPerHost` and `maxConnectionsTotal`, and a
|
||
**busy mode** via [`endpoint.busy`][] that rejects all new connections.
|
||
|
||
Rate limiting activity can be monitored through the endpoint's statistics
|
||
object. Each rate limiter has a corresponding counter
|
||
(e.g., `endpoint.stats.retryRateLimited`,
|
||
`endpoint.stats.sessionCreationRateLimited`) that tracks how many responses
|
||
were dropped. A non-zero value indicates the rate limiter is actively
|
||
protecting the endpoint.
|
||
|
||
#### Block lists
|
||
|
||
Endpoints can filter incoming packets by source address using a
|
||
[`net.BlockList`][]. The block list is checked before any QUIC processing
|
||
occurs, so blocked packets consume no resources beyond the check itself.
|
||
|
||
In **deny** mode (the default), packets from addresses in the list are dropped:
|
||
|
||
```mjs
|
||
import { BlockList } from 'node:net';
|
||
import { listen } from 'node:quic';
|
||
|
||
const blocked = new BlockList();
|
||
blocked.addSubnet('192.168.1.0', 24); // Block an entire subnet
|
||
blocked.addAddress('10.0.0.5'); // Block a specific address
|
||
|
||
const endpoint = await listen(onSession, {
|
||
endpoint: {
|
||
blockList: blocked,
|
||
blockListPolicy: 'deny',
|
||
},
|
||
// ...
|
||
});
|
||
```
|
||
|
||
In **allow** mode, only packets from addresses in the list are accepted:
|
||
|
||
```mjs
|
||
const trusted = new BlockList();
|
||
trusted.addSubnet('10.0.0.0', 8);
|
||
|
||
const endpoint = await listen(onSession, {
|
||
endpoint: {
|
||
blockList: trusted,
|
||
blockListPolicy: 'allow',
|
||
},
|
||
// ...
|
||
});
|
||
```
|
||
|
||
The block list is evaluated live — rules added or removed after the endpoint
|
||
is created take effect immediately. The `endpoint.stats.packetsBlocked`
|
||
counter tracks how many packets have been dropped by the filter.
|
||
|
||
### Applications
|
||
|
||
Every `QuicSession` is associated with a single application protocol, negotiated
|
||
via ALPN during the TLS handshake. The `quic` module is designed to be
|
||
application-agnostic in general but includes built-in support for HTTP/3 as a
|
||
specific application protocol. When using HTTP/3, the `quic` module provides
|
||
additional APIs for handling HTTP/3-specific features such as headers, trailers,
|
||
and prioritization. For other application protocols, users can implement their
|
||
own message framing and multiplexing on top of the core QUIC transport features.
|
||
|
||
When initiating a TLS handshake, the client will include a list of supported
|
||
ALPN protocols in the `ClientHello`. The server selects one of these protocols
|
||
(if any) and includes it in the `ServerHello`. The negotiated protocol determines
|
||
how the `QuicSession` and `QuicStream` APIs behave. For example, when the `h3`
|
||
protocol is negotiated for HTTP/3, the `QuicSession` and `QuicStream` will support
|
||
HTTP/3-specific features.
|
||
|
||
Currently, the `quic` module only supports HTTP/3 as a built-in application protocol.
|
||
All other protocols must be implemented by the user on top of the provided JavaScript
|
||
API.
|
||
|
||
### Configuration
|
||
|
||
The QUIC API is designed to be flexible and highly configurable to support a wide
|
||
range of use cases. Users can configure various aspects of the QUIC transport,
|
||
TLS handshake, and application behavior via options passed to the `quic.connect()`
|
||
and `quic.listen()` functions, as well as dynamically on `QuicEndpoint` and
|
||
`QuicSession` instances. The API also provides access to detailed statistics and
|
||
events for monitoring and debugging.
|
||
|
||
QUIC transport parameters are exchanged during the TLS handshake to negotiate
|
||
various transport-level settings such as maximum stream counts, idle timeouts,
|
||
and datagram support. The `quic` module allows users to configure the transport
|
||
parameters their endpoint advertises to peers, as well as access the transport
|
||
parameters advertised by peers. These configure the capabilities and limits of
|
||
the QUIC connection in coordination with the peer.
|
||
|
||
A rich set of local settings is also available for configuring the behavior of
|
||
the local endpoint and sessions. These include settings for connection limits,
|
||
congestion control, stream prioritization, and more.
|
||
|
||
### Callbacks and Promises
|
||
|
||
The `quic` module uses a combination of callbacks and promises for asynchronous
|
||
operations. For example, initiating a connection with `quic.connect()` returns
|
||
a promise for the established session, while incoming sessions on the server
|
||
side are handled via a callback passed to `quic.listen()`. Within a session,
|
||
events such as incoming streams, datagrams, and session state changes are handled
|
||
via callbacks on the `QuicSession` instance. Promises are used for operations
|
||
that have a clear completion point, such as completion of the TLS handshake or
|
||
graceful closure of a session.
|
||
|
||
All callbacks are invoked synchronously and may either return synchronously or
|
||
return a promise. If a callback returns a promise that rejects, or throws an error,
|
||
the object will be destroyed with the error as the reason if an `onerror` callback
|
||
is not specified.
|
||
|
||
### Streams
|
||
|
||
Streams are the primary data-carrying abstraction in QUIC. A stream can be
|
||
initiated by either the local endpoint or the remote peer once a session is
|
||
established.
|
||
|
||
Streams can be either bidirectional (data flows in both directions) or
|
||
unidirectional (data flows in only one direction). The `quic` module provides
|
||
separate APIs for creating each kind:
|
||
[`session.createBidirectionalStream()`][] and
|
||
[`session.createUnidirectionalStream()`][]. Streams initiated by a remote
|
||
peer are delivered via the [`session.onstream`][] callback.
|
||
|
||
There are two ways to write data to a stream:
|
||
|
||
* **Body source** — pass a `body` option when creating the stream (or call
|
||
[`stream.setBody()`][]). The body can be a string, `ArrayBuffer`,
|
||
`ArrayBufferView`, `Blob`, `FileHandle`, `AsyncIterable`, sync `Iterable`,
|
||
or `Promise` resolving to any of these. A `null` body closes the writable
|
||
side immediately. This is the simplest approach when the data is available
|
||
up front or can be expressed as an iterable.
|
||
* **Writer** — access [`stream.writer`][] to push data incrementally. The
|
||
writer exposes synchronous methods (`writeSync()`, `writevSync()`,
|
||
`endSync()`) that return immediately, as well as async equivalents
|
||
(`write()`, `writev()`, `end()`) that wait for drain when backpressured.
|
||
`writeSync()` returns `false` when the write buffer is full; the caller
|
||
should wait for drain before retrying.
|
||
|
||
These two approaches are mutually exclusive for a given stream.
|
||
|
||
Reading is done by iterating the stream as an async iterable. Each iteration
|
||
yields a batch of `Uint8Array` chunks:
|
||
|
||
```mjs
|
||
for await (const chunks of stream) {
|
||
for (const chunk of chunks) {
|
||
// Process each Uint8Array chunk
|
||
}
|
||
}
|
||
```
|
||
|
||
Only one async iterator can be obtained per stream. The stream is also
|
||
compatible with `node:stream/iter` utilities such as `Stream.bytes()`,
|
||
`Stream.text()`, and `Stream.pipeTo()`.
|
||
|
||
### Datagrams
|
||
|
||
In addition to streams, QUIC supports unreliable datagrams ([RFC 9221][]) for
|
||
use cases that require low-latency, best-effort messaging.
|
||
|
||
Datagram support is enabled at two levels. At the QUIC transport level, both
|
||
peers must advertise a non-zero [`maxDatagramFrameSize`][] transport parameter
|
||
during the handshake. For HTTP/3 sessions, both peers must additionally set
|
||
[`application.enableDatagrams`][] to `true`, which exchanges the
|
||
`SETTINGS_H3_DATAGRAM` setting on the HTTP/3 control stream.
|
||
|
||
A datagram is sent with a single call to [`session.sendDatagram()`][]. Each
|
||
datagram must fit within a single QUIC packet — datagrams cannot be
|
||
fragmented. The maximum payload size is determined by the peer's
|
||
`maxDatagramFrameSize` and the path MTU. If a datagram is too large or the
|
||
peer does not support datagrams, `sendDatagram()` returns `0n` rather than
|
||
throwing an error.
|
||
|
||
There is no guarantee of delivery. Datagrams may be lost, duplicated, or
|
||
delivered out of order. The [`session.ondatagramstatus`][] callback reports
|
||
whether each sent datagram was `'acknowledged'`, `'lost'`, or `'abandoned'`
|
||
(never sent on the wire).
|
||
|
||
### 0-RTT early data and session resumption
|
||
|
||
QUIC supports 0-RTT early data, allowing a client that has previously connected
|
||
to a server to send application data with its very first packet, without waiting
|
||
for the handshake to complete. This can eliminate a full round-trip of latency on
|
||
reconnection.
|
||
|
||
Two pieces of state from a prior connection make this possible:
|
||
|
||
* A **session ticket**, received via the [`session.onsessionticket`][] callback,
|
||
enables TLS session resumption and 0-RTT encryption. Pass it as the
|
||
[`sessionOptions.sessionTicket`][] option on a subsequent connection to the
|
||
same server.
|
||
* An **address validation token**, received via the [`session.onnewtoken`][]
|
||
callback, allows the client to skip the server's address validation step
|
||
(avoiding a Retry round-trip). Pass it as the [`sessionOptions.token`][]
|
||
option.
|
||
|
||
If the server accepts the session ticket, any data sent before the handshake
|
||
completes is 0-RTT early data. On the server side, `stream.early` is `true`
|
||
for streams carrying early data. The server can reject the 0-RTT attempt
|
||
(for example, if its configuration has changed since the ticket was issued).
|
||
When this happens, all streams opened during the 0-RTT phase are destroyed and
|
||
the client's [`session.onearlyrejected`][] callback fires. The connection
|
||
falls back to a normal 1-RTT handshake and the application can reopen streams.
|
||
|
||
Early data is less secure than data sent after the handshake completes — it
|
||
can potentially be replayed by an attacker. Applications should treat 0-RTT
|
||
data with appropriate caution and avoid performing non-idempotent operations
|
||
during the early data phase.
|
||
|
||
### Connection lifecycle
|
||
|
||
A typical client session progresses through these stages:
|
||
|
||
1. Call [`quic.connect()`][] with a server address and options. This returns a
|
||
`QuicSession`.
|
||
2. The TLS handshake runs automatically. `session.opened` resolves when the
|
||
handshake completes, providing the negotiated ALPN, cipher, and certificate
|
||
validation results.
|
||
3. Open streams, send datagrams, and exchange data.
|
||
4. Call [`session.close()`][] to initiate a graceful shutdown. Existing streams
|
||
are allowed to finish, then the session is destroyed. The returned promise
|
||
(also available as `session.closed`) resolves when teardown is complete.
|
||
|
||
On the server side, call [`quic.listen()`][] with a callback. The callback
|
||
fires for each incoming session after the TLS handshake begins. Incoming
|
||
streams arrive via the [`session.onstream`][] callback.
|
||
|
||
[`session.destroy()`][] is available for immediate teardown — all open streams
|
||
are destroyed and the session is closed without waiting for them to finish.
|
||
|
||
`QuicEndpoint` and `QuicSession` support `Symbol.asyncDispose`, so they can
|
||
be used with `await using` for automatic cleanup.
|
||
|
||
### Error handling
|
||
|
||
Errors in the `quic` module are communicated through two complementary
|
||
mechanisms: the `onerror` callback and the `closed` promise.
|
||
|
||
Both `QuicSession` and `QuicStream` expose an optional `onerror` callback.
|
||
When a session or stream is destroyed with an error — including errors thrown
|
||
by other user callbacks — the `onerror` callback is invoked with the error
|
||
before the object is torn down. Setting `onerror` also marks the `closed`
|
||
promise as handled, preventing unhandled rejection warnings. If `onerror`
|
||
is not set, the error is delivered solely through the rejection of the
|
||
`closed` promise.
|
||
|
||
The [`QuicError`][] class carries an explicit numeric QUIC error code
|
||
([`error.errorCode`][]) alongside the usual `message` and `code` properties.
|
||
When a `QuicError` is passed to [`stream.destroy()`][] or
|
||
[`writer.fail()`][], its `errorCode` is used in the `RESET_STREAM` or
|
||
`STOP_SENDING` frame sent to the peer. Any other error type falls back to
|
||
the negotiated protocol's generic internal error code.
|
||
|
||
### Permission model
|
||
|
||
When using the [Permission Model][], the `--allow-net` flag must be passed to
|
||
allow QUIC network operations. Without it, calling [`quic.connect()`][] or
|
||
[`quic.listen()`][] will throw an `ERR_ACCESS_DENIED` error.
|
||
|
||
```console
|
||
$ node --permission --allow-fs-read=* --experimental-quic index.mjs
|
||
Error: Access to this API has been restricted. Use --allow-net to manage permissions.
|
||
code: 'ERR_ACCESS_DENIED',
|
||
permission: 'Net',
|
||
}
|
||
```
|
||
|
||
Creating a [`QuicEndpoint`][] instance without connecting or listening
|
||
is permitted even without `--allow-net`, since no network I/O occurs until
|
||
[`quic.connect()`][] or [`quic.listen()`][] is called.
|
||
|
||
## `quic.connect(address[, options])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `address` {string|net.SocketAddress}
|
||
* `options` {quic.SessionOptions}
|
||
* Returns: {Promise} a promise for a {quic.QuicSession}
|
||
|
||
Initiate a new client-side session.
|
||
|
||
```mjs
|
||
import { connect } from 'node:quic';
|
||
import { Buffer } from 'node:buffer';
|
||
|
||
const enc = new TextEncoder();
|
||
const alpn = 'foo';
|
||
const client = await connect('123.123.123.123:8888', { alpn });
|
||
await client.createUnidirectionalStream({
|
||
body: enc.encode('hello world'),
|
||
});
|
||
```
|
||
|
||
By default, every call to `connect(...)` will create a new local
|
||
`QuicEndpoint` instance bound to a new random local IP port. To
|
||
specify the exact local address to use, or to multiplex multiple
|
||
QUIC sessions over a single local port, pass the `endpoint` option
|
||
with either a `QuicEndpoint` or `EndpointOptions` as the argument.
|
||
|
||
```mjs
|
||
import { QuicEndpoint, connect } from 'node:quic';
|
||
|
||
const endpoint = new QuicEndpoint({
|
||
address: '127.0.0.1:1234',
|
||
});
|
||
|
||
const client = await connect('123.123.123.123:8888', { endpoint });
|
||
```
|
||
|
||
## `quic.listen(onsession[, options])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `onsession` {quic.OnSessionCallback}
|
||
* `options` {quic.SessionOptions}
|
||
* Returns: {Promise} a promise for a {quic.QuicEndpoint}
|
||
|
||
Configures the endpoint to listen as a server. When a new session is initiated by
|
||
a remote peer, the given `onsession` callback will be invoked with the created
|
||
session.
|
||
|
||
```mjs
|
||
import { listen } from 'node:quic';
|
||
|
||
const endpoint = await listen((session) => {
|
||
// ... handle the session
|
||
});
|
||
|
||
// Closing the endpoint allows any sessions open when close is called
|
||
// to complete naturally while preventing new sessions from being
|
||
// initiated. Once all existing sessions have finished, the endpoint
|
||
// will be destroyed. The call returns a promise that is resolved once
|
||
// the endpoint is destroyed.
|
||
await endpoint.close();
|
||
```
|
||
|
||
By default, every call to `listen(...)` will create a new local
|
||
`QuicEndpoint` instance bound to a new random local IP port. To
|
||
specify the exact local address to use, or to multiplex multiple
|
||
QUIC sessions over a single local port, pass the `endpoint` option
|
||
with either a `QuicEndpoint` or `EndpointOptions` as the argument.
|
||
|
||
At most, any single `QuicEndpoint` can only be configured to listen as
|
||
a server once.
|
||
|
||
## `quic.listEndpoints([options])`
|
||
|
||
<!-- YAML
|
||
added: v26.4.0
|
||
-->
|
||
|
||
* `options` {object}
|
||
* `active` {boolean} If `true` (the default), only returns endpoints that are
|
||
active (not destroyed, not closing, and not busy). If `false` returns all
|
||
endpoints.
|
||
* Returns: {quic.QuicEndpoint\[]}
|
||
|
||
Returns the list of all `QuicEndpoint` instances. By default, only active
|
||
endpoints are returned.
|
||
|
||
## `quic.constants`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* {Object}
|
||
|
||
An object containing commonly used constants for QUIC configuration.
|
||
|
||
### `quic.constants.cc`
|
||
|
||
* {Object}
|
||
|
||
Congestion control algorithm identifiers, for use with the
|
||
[`sessionOptions.cc`][] option:
|
||
|
||
* `quic.constants.cc.RENO` — Reno congestion control.
|
||
* `quic.constants.cc.CUBIC` — CUBIC congestion control.
|
||
* `quic.constants.cc.BBR` — BBR congestion control.
|
||
|
||
### `quic.constants.DEFAULT_CIPHERS`
|
||
|
||
* {string}
|
||
|
||
The default TLS 1.3 cipher suite list used when [`sessionOptions.ciphers`][]
|
||
is not specified.
|
||
|
||
### `quic.constants.DEFAULT_GROUPS`
|
||
|
||
* {string}
|
||
|
||
The default TLS 1.3 key-exchange group list used when
|
||
[`sessionOptions.groups`][] is not specified.
|
||
|
||
## Class: `QuicEndpoint`
|
||
|
||
A `QuicEndpoint` encapsulates the local UDP-port binding for QUIC. It can be
|
||
used as both a client and a server.
|
||
|
||
### `new QuicEndpoint([options])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `options` {quic.EndpointOptions}
|
||
|
||
### `endpoint.address`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {net.SocketAddress|undefined}
|
||
|
||
The local UDP socket address to which the endpoint is bound, if any.
|
||
|
||
If the endpoint is not currently bound then the value will be `undefined`. Read only.
|
||
|
||
### `endpoint.busy`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
When `endpoint.busy` is set to true, the endpoint will temporarily reject
|
||
new sessions from being created. Read/write.
|
||
|
||
```mjs
|
||
// Mark the endpoint busy. New sessions will be prevented.
|
||
endpoint.busy = true;
|
||
|
||
// Mark the endpoint free. New session will be allowed.
|
||
endpoint.busy = false;
|
||
```
|
||
|
||
The `busy` property is useful when the endpoint is under heavy load and needs to
|
||
temporarily reject new sessions while it catches up.
|
||
|
||
### `endpoint.close()`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Returns: {Promise}
|
||
|
||
Gracefully close the endpoint. The endpoint will close and destroy itself when
|
||
all currently open sessions close. Once called, new sessions will be rejected.
|
||
|
||
Returns a promise that is fulfilled when the endpoint is destroyed.
|
||
|
||
### `endpoint.closed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {Promise}
|
||
|
||
A promise that is fulfilled when the endpoint is destroyed. This will be the same promise that is
|
||
returned by the `endpoint.close()` function. Read only.
|
||
|
||
### `endpoint.closing`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True if `endpoint.close()` has been called and closing the endpoint has not yet completed.
|
||
Read only.
|
||
|
||
### `endpoint.destroy([error])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `error` {any}
|
||
|
||
Forcefully closes the endpoint by forcing all open sessions to be immediately
|
||
closed.
|
||
|
||
### `endpoint.destroyed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True if `endpoint.destroy()` has been called. Read only.
|
||
|
||
### `endpoint.listening`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True if the endpoint is actively listening for incoming connections. Read only.
|
||
|
||
### `endpoint.maxConnectionsPerHost`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
The maximum number of concurrent connections allowed per remote IP address.
|
||
`0` means unlimited (default). Can be set at construction time via the
|
||
`maxConnectionsPerHost` option and changed dynamically at any time.
|
||
The valid range is `0` to `65535`.
|
||
|
||
### `endpoint.maxConnectionsTotal`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
The maximum total number of concurrent connections across all remote
|
||
addresses. `0` means unlimited (default). Can be set at construction time via
|
||
the `maxConnectionsTotal` option and changed dynamically at any time.
|
||
The valid range is `0` to `65535`.
|
||
|
||
### `endpoint.setSNIContexts(entries[, options])`
|
||
|
||
<!-- YAML
|
||
added:
|
||
- v26.1.0
|
||
- v24.16.0
|
||
-->
|
||
|
||
* `entries` {object} An object mapping host names to TLS identity options.
|
||
Each entry must include `keys` and `certs`.
|
||
* `options` {object}
|
||
* `replace` {boolean} If `true`, replaces the entire SNI map. If `false`
|
||
(the default), merges the entries into the existing map.
|
||
|
||
Replaces or updates the SNI TLS contexts for this endpoint. This allows
|
||
changing the TLS identity (key/certificate) used for specific host names
|
||
without restarting the endpoint. Existing sessions are unaffected — only
|
||
new sessions will use the updated contexts.
|
||
|
||
```mjs
|
||
endpoint.setSNIContexts({
|
||
'api.example.com': { keys: [newApiKey], certs: [newApiCert] },
|
||
});
|
||
|
||
// Replace the entire SNI map
|
||
endpoint.setSNIContexts({
|
||
'api.example.com': { keys: [newApiKey], certs: [newApiCert] },
|
||
}, { replace: true });
|
||
```
|
||
|
||
### `endpoint.stats`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.QuicEndpoint.Stats}
|
||
|
||
The statistics collected for an active endpoint. Read only.
|
||
|
||
### `endpoint[Symbol.asyncDispose]()`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
Calls `endpoint.close()` and returns a promise that fulfills when the
|
||
endpoint has closed.
|
||
|
||
## Class: `QuicEndpoint.Stats`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
A view of the collected statistics for an endpoint.
|
||
|
||
### `endpointStats.createdAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} A timestamp indicating the moment the endpoint was created. Read only.
|
||
|
||
### `endpointStats.destroyedAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} A timestamp indicating the moment the endpoint was destroyed. Read only.
|
||
|
||
### `endpointStats.bytesReceived`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of bytes received by this endpoint. Read only.
|
||
|
||
### `endpointStats.bytesSent`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of bytes sent by this endpoint. Read only.
|
||
|
||
### `endpointStats.packetsReceived`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of QUIC packets successfully received by this endpoint. Read only.
|
||
|
||
### `endpointStats.packetsSent`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of QUIC packets successfully sent by this endpoint. Read only.
|
||
|
||
### `endpointStats.serverSessions`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of peer-initiated sessions received by this endpoint. Read only.
|
||
|
||
### `endpointStats.clientSessions`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of sessions initiated by this endpoint. Read only.
|
||
|
||
### `endpointStats.serverBusyCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of times an initial packet was rejected due to the
|
||
endpoint being marked busy. Read only.
|
||
|
||
### `endpointStats.retryCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of retry packets sent by this endpoint. Read only.
|
||
|
||
### `endpointStats.retryRateLimited`
|
||
|
||
* Type: {bigint} The total number of retry packets dropped by the global rate
|
||
limiter. Read only. A non-zero value indicates the endpoint is under retry
|
||
flood pressure.
|
||
|
||
### `endpointStats.versionNegotiationCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of version negotiation packets sent by this
|
||
endpoint. Read only.
|
||
|
||
### `endpointStats.versionNegotiationRateLimited`
|
||
|
||
* Type: {bigint} The total number of version negotiation packets dropped by
|
||
the global rate limiter. Read only.
|
||
|
||
### `endpointStats.statelessResetCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of stateless reset packets sent by this
|
||
endpoint. Read only.
|
||
|
||
### `endpointStats.statelessResetRateLimited`
|
||
|
||
* Type: {bigint} The total number of stateless reset packets dropped by the
|
||
global rate limiter. Read only.
|
||
|
||
### `endpointStats.immediateCloseCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint} The total number of immediate connection close packets sent
|
||
by this endpoint. Read only.
|
||
|
||
### `endpointStats.immediateCloseRateLimited`
|
||
|
||
* Type: {bigint} The total number of immediate connection close packets
|
||
dropped by the global rate limiter. Read only.
|
||
|
||
### `endpointStats.sessionCreationRateLimited`
|
||
|
||
* Type: {bigint} The total number of session creation attempts dropped by the
|
||
per-host rate limiter. Read only. A non-zero value indicates one or more
|
||
remote addresses are creating sessions faster than the configured rate allows.
|
||
|
||
### `endpointStats.packetsBlocked`
|
||
|
||
* Type: {bigint} The total number of incoming packets dropped by the
|
||
block list filter. Read only.
|
||
|
||
## Class: `QuicSession`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
A `QuicSession` represents the local side of a QUIC connection.
|
||
|
||
### `session.applicationOptions`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {quic.ApplicationOptions}
|
||
|
||
The current application-level options for this session. These include settings
|
||
that are specific to the negotiated application protocol (e.g. HTTP/3) and may
|
||
be negotiated separately from the transport parameters. Read only.
|
||
You can use the callback [`session.onapplication`][] to be informed, when settings
|
||
from the remote arrive.
|
||
|
||
### `session.close([options])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `options` {Object}
|
||
* `code` {bigint|number} The error code to include in the `CONNECTION_CLOSE`
|
||
frame sent to the peer. **Default:** `0` (no error).
|
||
* `type` {string} Either `'transport'` or `'application'`. Determines the
|
||
error code namespace used in the `CONNECTION_CLOSE` frame. When `'transport'`
|
||
(the default), the frame type is `0x1c` and the code is interpreted as a QUIC
|
||
transport error. When `'application'`, the frame type is `0x1d` and the code
|
||
is application-specific. **Default:** `'transport'`.
|
||
* `reason` {string} An optional human-readable reason string included in
|
||
the `CONNECTION_CLOSE` frame. Per RFC 9000, this is for diagnostic purposes
|
||
only and should not be used for machine-readable error descriptions.
|
||
* Returns: {Promise}
|
||
|
||
Initiate a graceful close of the session. Existing streams will be allowed
|
||
to complete but no new streams will be opened. Once all streams have closed,
|
||
the session will be destroyed. The returned promise will be fulfilled once
|
||
the session has been destroyed. If a non-zero `code` is specified, the
|
||
promise will reject with an `ERR_QUIC_TRANSPORT_ERROR` or
|
||
`ERR_QUIC_APPLICATION_ERROR` depending on the `type`.
|
||
|
||
### `session.opened`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Promise} for an {Object}
|
||
* `local` {net.SocketAddress} The local socket address.
|
||
* `remote` {net.SocketAddress} The remote socket address.
|
||
* `servername` {string} The SNI server name negotiated during the handshake.
|
||
* `protocol` {string} The ALPN protocol negotiated during the handshake.
|
||
* `cipher` {string} The name of the negotiated TLS cipher suite.
|
||
* `cipherVersion` {string} The TLS protocol version of the cipher suite
|
||
(e.g., `'TLSv1.3'`).
|
||
* `validationErrorReason` {string} If certificate validation failed, the
|
||
reason string. Empty string if validation succeeded.
|
||
* `validationErrorCode` {number} If certificate validation failed, the
|
||
error code. `0` if validation succeeded.
|
||
* `earlyDataAttempted` {boolean} Whether 0-RTT early data was attempted.
|
||
* `earlyDataAccepted` {boolean} Whether 0-RTT early data was accepted by
|
||
the server.
|
||
|
||
A promise that is fulfilled once the TLS handshake completes successfully.
|
||
The resolved value contains information about the established session
|
||
including the negotiated protocol, cipher suite, certificate validation
|
||
status, and 0-RTT early data status.
|
||
|
||
If the handshake fails or the session is destroyed before the handshake
|
||
completes, the promise will be rejected.
|
||
|
||
### `session.closed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {Promise}
|
||
|
||
A promise that is fulfilled once the session is destroyed.
|
||
|
||
### `session.closing`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True if [`session.close()`][] has been called and the session has not yet
|
||
been destroyed. Read only.
|
||
|
||
### `session.destroy([error[, options]])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `error` {any}
|
||
* `options` {Object}
|
||
* `code` {bigint|number} The error code to include in the `CONNECTION_CLOSE`
|
||
frame sent to the peer. **Default:** `0`.
|
||
* `type` {string} Either `'transport'` or `'application'`. **Default:**
|
||
`'transport'`.
|
||
* `reason` {string} An optional human-readable reason string included in
|
||
the `CONNECTION_CLOSE` frame.
|
||
|
||
Immediately destroy the session. All streams will be destroyed and the
|
||
session will be closed. If `error` is provided and [`session.onerror`][] is
|
||
set, the `onerror` callback is invoked before destruction. The
|
||
`session.closed` promise will reject with the error. If `options` is
|
||
provided, the `CONNECTION_CLOSE` frame sent to the peer will include the
|
||
specified error code, type, and reason.
|
||
|
||
### `session.destroyed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True if `session.destroy()` has been called. Read only.
|
||
|
||
### `session.localTransportParams`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {quic.TransportParams|null}
|
||
|
||
The transport parameters advertised by the local endpoint during the handshake.
|
||
Returns `null` if the session has been destroyed. Read only.
|
||
|
||
### `session.endpoint`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.QuicEndpoint|null}
|
||
|
||
The endpoint that created this session. Returns `null` if the session
|
||
has been destroyed. Read only.
|
||
|
||
### `session.onapplication`
|
||
|
||
<!-- YAML
|
||
added: v26.4.0
|
||
-->
|
||
|
||
* Type: {quic.OnApplicationCallback}
|
||
|
||
The callback to invoke when new application options, e.g. HTTP/3 settings arrived.
|
||
|
||
### `session.onerror`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function|undefined}
|
||
|
||
An optional callback invoked when the session is destroyed with an error.
|
||
This includes errors caused by user callbacks that throw or reject (see
|
||
[Callback error handling][]). The callback receives a single argument: the
|
||
error that triggered the destruction. If the `onerror` callback itself throws
|
||
or returns a promise that rejects, the error is surfaced as an uncaught
|
||
exception. Read/write.
|
||
|
||
Can also be set via the `onerror` option in [`quic.connect()`][] or
|
||
[`quic.listen()`][].
|
||
|
||
### `session.onstream`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnStreamCallback}
|
||
|
||
The callback to invoke when a new stream is initiated by a remote peer. Read/write.
|
||
|
||
### `session.ondatagram`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnDatagramCallback}
|
||
|
||
The callback to invoke when a new datagram is received from a remote peer. Read/write.
|
||
|
||
### `session.ondatagramstatus`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnDatagramStatusCallback}
|
||
|
||
The callback to invoke when the status of a datagram is updated. Read/write.
|
||
|
||
### `session.onearlyrejected`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function|undefined}
|
||
|
||
The callback to invoke when the server rejects 0-RTT early data. When
|
||
this fires, all streams that were opened during the 0-RTT phase have
|
||
been destroyed. The application should re-open streams if needed.
|
||
Read/write.
|
||
|
||
This callback only fires on the client side when the server rejects
|
||
the client's 0-RTT attempt. The connection falls back to 1-RTT and
|
||
continues normally.
|
||
|
||
### `session.onpathvalidation`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnPathValidationCallback}
|
||
|
||
The callback to invoke when the path validation is updated. Read/write.
|
||
|
||
### `session.onsessionticket`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnSessionTicketCallback}
|
||
|
||
The callback to invoke when a new session ticket is received. Read/write.
|
||
|
||
### `session.onversionnegotiation`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnVersionNegotiationCallback}
|
||
|
||
The callback to invoke when a version negotiation is initiated. Read/write.
|
||
|
||
### `session.onhandshake`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnHandshakeCallback}
|
||
|
||
The callback to invoke when the TLS handshake is completed. Read/write.
|
||
|
||
### `session.onnewtoken`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {quic.OnNewTokenCallback}
|
||
|
||
The callback to invoke when a NEW\_TOKEN token is received from the server.
|
||
The token can be passed as the `token` option on a future connection to
|
||
the same server to skip address validation. Read/write.
|
||
|
||
### `session.onorigin`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {quic.OnOriginCallback}
|
||
|
||
The callback to invoke when an ORIGIN frame (RFC 9412) is received from
|
||
the server, indicating which origins the server is authoritative for.
|
||
Read/write.
|
||
|
||
### `session.ongoaway`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function}
|
||
|
||
The callback to invoke when the peer sends an HTTP/3 GOAWAY frame,
|
||
indicating it is initiating a graceful shutdown. The callback receives
|
||
`(lastStreamId)` where `lastStreamId` is a `{bigint}`:
|
||
|
||
* When `lastStreamId` is `-1n`, the peer sent a shutdown notice (intent
|
||
to close) without specifying a stream boundary. All existing streams
|
||
may still be processed.
|
||
* When `lastStreamId` is `>= 0n`, it is the highest stream ID the peer
|
||
may have processed. Streams with IDs above this value were NOT
|
||
processed and can be safely retried on a new connection.
|
||
|
||
After GOAWAY is received, `session.createBidirectionalStream()` will
|
||
throw `ERR_INVALID_STATE`. Existing streams continue until they
|
||
complete or the session closes.
|
||
|
||
This callback is only relevant for HTTP/3 sessions. Read/write.
|
||
|
||
### `session.onkeylog`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {quic.OnKeylogCallback}
|
||
|
||
The callback to invoke when TLS key material is available. Requires
|
||
[`sessionOptions.keylog`][] to be `true`. Each invocation receives a single
|
||
line of [NSS Key Log Format][] text (including a trailing newline). This is
|
||
useful for decrypting packet captures with tools like Wireshark. Read/write.
|
||
|
||
Can also be set via the `onkeylog` option in [`quic.connect()`][] or
|
||
[`quic.listen()`][].
|
||
|
||
### `session.onqlog`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {quic.OnQlogCallback}
|
||
|
||
The callback to invoke when qlog data is available. Requires
|
||
[`sessionOptions.qlog`][] to be `true`. The callback receives a string
|
||
chunk of [JSON-SEQ][] formatted qlog data and a boolean `fin` flag. When
|
||
`fin` is `true`, the chunk is the final qlog output for this session and
|
||
the concatenated chunks form a complete qlog trace. Read/write.
|
||
|
||
Qlog data arrives during the connection lifecycle. The first chunk contains
|
||
the qlog header with format metadata. Subsequent chunks contain trace
|
||
events. The final chunk (with `fin` set to `true`) is emitted during
|
||
session destruction and completes the JSON-SEQ output.
|
||
|
||
Can also be set via the `onqlog` option in [`quic.connect()`][] or
|
||
[`quic.listen()`][].
|
||
|
||
### `session.createBidirectionalStream([options])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `options` {Object}
|
||
* `body` {string | ArrayBuffer | SharedArrayBuffer | ArrayBufferView |
|
||
Blob | FileHandle | AsyncIterable | Iterable | Promise | null}
|
||
The outbound body source. See [`stream.setBody()`][] for details on
|
||
supported types. When omitted, the stream starts half-closed (writable
|
||
side open, no body queued).
|
||
* `headers` {Object} Initial request or response headers to send. Only
|
||
used when the session supports headers (e.g. HTTP/3). If `body` is not
|
||
specified and `headers` is provided, the stream is treated as
|
||
headers-only (terminal).
|
||
* `priority` {string} The priority level of the stream. One of `'high'`,
|
||
`'default'`, or `'low'`. **Default:** `'default'`.
|
||
* `incremental` {boolean} When `true`, data from this stream may be
|
||
interleaved with data from other streams of the same priority level.
|
||
When `false`, the stream should be completed before same-priority peers.
|
||
**Default:** `false`.
|
||
* `highWaterMark` {number} The maximum number of bytes that the writer
|
||
will buffer before `writeSync()` returns `false`. When the buffered
|
||
data exceeds this limit, the caller should wait for drain before
|
||
writing more. **Default:** `65536` (64 KB).
|
||
* `onheaders` {Function} Callback for received initial response headers.
|
||
Called with `(headers)`.
|
||
* `ontrailers` {Function} Callback for received trailing headers.
|
||
Called with `(trailers)`.
|
||
* `oninfo` {Function} Callback for received informational (1xx) headers.
|
||
Called with `(headers)`.
|
||
* `onwanttrailers` {Function} Callback when trailers should be sent.
|
||
Called with no arguments; use [`stream.sendTrailers()`][] within the
|
||
callback.
|
||
* Returns: {Promise} for a {quic.QuicStream}
|
||
|
||
Open a new bidirectional stream. If the `body` option is not specified,
|
||
the outgoing stream will be half-closed. The `priority` and `incremental`
|
||
options are only used when the session supports priority (e.g. HTTP/3).
|
||
The `headers`, `onheaders`, `ontrailers`, `oninfo`, and `onwanttrailers`
|
||
options are only used when the session supports headers (e.g. HTTP/3).
|
||
|
||
### `session.createUnidirectionalStream([options])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `options` {Object}
|
||
* `body` {string | ArrayBuffer | SharedArrayBuffer | ArrayBufferView |
|
||
Blob | FileHandle | AsyncIterable | Iterable | Promise | null}
|
||
The outbound body source. See [`stream.setBody()`][] for details on
|
||
supported types. When omitted, the stream is closed immediately.
|
||
* `headers` {Object} Initial request headers to send.
|
||
* `priority` {string} The priority level of the stream. One of `'high'`,
|
||
`'default'`, or `'low'`. **Default:** `'default'`.
|
||
* `incremental` {boolean} When `true`, data from this stream may be
|
||
interleaved with data from other streams of the same priority level.
|
||
When `false`, the stream should be completed before same-priority peers.
|
||
**Default:** `false`.
|
||
* `highWaterMark` {number} The maximum number of bytes that the writer
|
||
will buffer before `writeSync()` returns `false`. When the buffered
|
||
data exceeds this limit, the caller should wait for drain before
|
||
writing more. **Default:** `65536` (64 KB).
|
||
* `onheaders` {Function} Callback for received initial response headers.
|
||
Called with `(headers)`.
|
||
* `ontrailers` {Function} Callback for received trailing headers.
|
||
Called with `(trailers)`.
|
||
* `oninfo` {Function} Callback for received informational (1xx) headers.
|
||
Called with `(headers)`.
|
||
* `onwanttrailers` {Function} Callback when trailers should be sent.
|
||
* Returns: {Promise} for a {quic.QuicStream}
|
||
|
||
Open a new unidirectional stream. If the `body` option is not specified,
|
||
the outgoing stream will be closed. The `priority` and `incremental`
|
||
options are only used when the session supports priority (e.g. HTTP/3).
|
||
|
||
### `session.path`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {Object|undefined}
|
||
* `local` {net.SocketAddress}
|
||
* `remote` {net.SocketAddress}
|
||
|
||
The local and remote socket addresses associated with the session. Read only.
|
||
|
||
### `session.remoteTransportParams`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {quic.TransportParams|null|undefined}
|
||
|
||
The transport parameters advertised by the remote peer during the handshake.
|
||
Returns `null` if the session has been destroyed, `undefined` if the handshake
|
||
has not yet completed and the remote parameters are not yet available. Read
|
||
only.
|
||
|
||
### `session.sendDatagram(datagram[, encoding])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `datagram` {string|ArrayBufferView|Promise}
|
||
* `encoding` {string} The encoding to use if `datagram` is a string.
|
||
**Default:** `'utf8'`.
|
||
* Returns: {Promise} for a {bigint} datagram ID.
|
||
|
||
Sends an unreliable datagram to the remote peer, returning a promise for
|
||
the datagram ID.
|
||
|
||
If `datagram` is a string, it will be encoded using the specified `encoding`.
|
||
|
||
If `datagram` is an `ArrayBufferView`, the bytes are copied into an
|
||
internal buffer; the caller's source buffer is unchanged and may be reused
|
||
or mutated immediately after the call returns. Callers that want to ensure
|
||
their source cannot be mutated after the call (for example, when handing
|
||
the buffer off to another async consumer) can call
|
||
`ArrayBuffer.prototype.transfer()` themselves before passing the buffer.
|
||
|
||
If `datagram` is a `Promise`, it will be awaited before sending. If the
|
||
session closes while awaiting, `0n` is returned silently (datagrams are
|
||
inherently unreliable).
|
||
|
||
If the datagram payload is zero-length (empty string after encoding, detached
|
||
buffer, or zero-length view), `0n` is returned and no datagram is sent.
|
||
|
||
For HTTP/3 sessions, the peer must advertise `SETTINGS_H3_DATAGRAM=1`
|
||
(via `application: { enableDatagrams: true }`) for datagrams to be sent.
|
||
If the peer's setting is `0`, `sendDatagram()` returns `0n` (per RFC 9297
|
||
§3, an endpoint MUST NOT send HTTP Datagrams unless the peer indicated
|
||
support).
|
||
|
||
Datagrams cannot be fragmented — each must fit within a single QUIC packet.
|
||
The maximum datagram size is determined by the peer's
|
||
`maxDatagramFrameSize` transport parameter (which the peer advertises during
|
||
the handshake). If the peer sets this to `0`, datagrams are not supported
|
||
and `0n` will be returned. If the datagram exceeds the peer's limit, it
|
||
will be silently dropped and `0n` returned. The local
|
||
`maxDatagramFrameSize` transport parameter (default: `1200` bytes) controls
|
||
what this endpoint advertises to the peer as its own maximum.
|
||
|
||
### `session.certificate`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {crypto.X509Certificate|undefined}
|
||
|
||
The local certificate as a [`crypto.X509Certificate`][] instance. Server
|
||
sessions return the certificate configured for the negotiated SNI host.
|
||
Client sessions return `undefined` unless a client certificate was sent.
|
||
Returns `undefined` if the session is destroyed.
|
||
|
||
### `session.peerCertificate`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {crypto.X509Certificate|undefined}
|
||
|
||
The peer's certificate as a [`crypto.X509Certificate`][] instance. Returns
|
||
`undefined` if the peer did not present a certificate or the session is
|
||
destroyed.
|
||
|
||
### `session.ephemeralKeyInfo`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Object|undefined}
|
||
|
||
The ephemeral key information for the session, with properties such as
|
||
`type`, `name`, and `size`. Only available on client sessions. Returns
|
||
`undefined` for server sessions or if the session is destroyed.
|
||
|
||
### `session.maxDatagramSize`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
The maximum datagram payload size in bytes that the peer will accept.
|
||
This is derived from the peer's `maxDatagramFrameSize` transport
|
||
parameter minus the DATAGRAM frame overhead (type byte and variable-length
|
||
integer encoding). Returns `0` if the peer does not support datagrams or
|
||
if the handshake has not yet completed. Datagrams larger than this value
|
||
will not be sent.
|
||
|
||
### `session.maxPendingDatagrams`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
* **Default:** `128`
|
||
|
||
The maximum number of datagrams that can be queued for sending. Datagrams
|
||
are queued when `sendDatagram()` is called and sent opportunistically
|
||
alongside stream data by the packet serialization loop. When the queue
|
||
is full, the [`sessionOptions.datagramDropPolicy`][] determines whether
|
||
the oldest or newest datagram is dropped. Dropped datagrams are reported
|
||
as lost via the `ondatagramstatus` callback.
|
||
|
||
This property can be changed dynamically to adjust queue capacity
|
||
based on application activity or memory pressure. The valid range
|
||
is `0` to `65535`.
|
||
|
||
### `session.stats`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.QuicSession.Stats}
|
||
|
||
Return the current statistics for the session. Read only.
|
||
|
||
### `session.updateKey()`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
Initiate a key update for the session.
|
||
|
||
### `session[Symbol.asyncDispose]()`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
Calls `session.close()` and returns a promise that fulfills when the
|
||
session has closed.
|
||
|
||
## Class: `QuicSession.Stats`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
### `sessionStats.createdAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.closingAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.handshakeCompletedAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.handshakeConfirmedAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.bytesReceived`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.bytesSent`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.bidiInStreamCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.bidiOutStreamCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.uniInStreamCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.uniOutStreamCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.maxBytesInFlight`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.bytesInFlight`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.blockCount`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.cwnd`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.latestRtt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.minRtt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.rttVar`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.smoothedRtt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.ssthresh`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.datagramsReceived`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.datagramsSent`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.datagramsAcknowledged`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.datagramsLost`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `sessionStats.streamsIdleTimedOut`
|
||
|
||
* Type: {bigint} The total number of peer-initiated streams destroyed by the
|
||
stream idle timeout. Read only.
|
||
|
||
## Class: `QuicError`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
> Stability: 1 - Experimental
|
||
|
||
A `QuicError` is an `Error` subclass that carries an explicit numeric
|
||
QUIC error code. Use it to abort a QUIC stream or session with a
|
||
specific application-protocol-defined error code rather than letting
|
||
the implementation pick a generic fallback.
|
||
|
||
The class is exported from `node:quic`:
|
||
|
||
```mjs
|
||
import { QuicError } from 'node:quic';
|
||
```
|
||
|
||
```cjs
|
||
const { QuicError } = require('node:quic');
|
||
```
|
||
|
||
When a `QuicError` is supplied to APIs that emit a wire frame
|
||
([`writer.fail()`][], [`stream.destroy()`][]), the QUIC stack uses
|
||
[`error.errorCode`][] as the wire code for the resulting frame.
|
||
When any other value is supplied (for example a plain `Error`), the
|
||
implementation falls back to the negotiated application protocol's
|
||
"internal error" code (`H3_INTERNAL_ERROR` (`0x102`) for HTTP/3, or
|
||
the QUIC transport-layer `INTERNAL_ERROR` (`0x1`) for raw QUIC).
|
||
|
||
The Node.js error code (`error.code`) defaults to
|
||
`'ERR_QUIC_STREAM_ABORTED'`. Callers who need a more specific code
|
||
string can override it via `options.code` — the numeric QUIC code
|
||
is unaffected.
|
||
|
||
The Node.js error code is fixed at `'ERR_QUIC_STREAM_ABORTED'` so that
|
||
catch blocks can distinguish a `QuicError` from other Node.js errors
|
||
without checking the prototype chain. The numeric QUIC code lives on
|
||
the separate [`error.errorCode`][] property to avoid colliding with
|
||
the Node.js convention that `error.code` is a string.
|
||
|
||
### `new QuicError(message, options)`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `message` {string} A human-readable description of the error.
|
||
* `options` {Object}
|
||
* `errorCode` {bigint | number} The numeric QUIC error code. Numbers
|
||
are coerced to `BigInt`. Must be a non-negative 62-bit unsigned
|
||
varint (`0n <= errorCode <= 2n ** 62n - 1n`).
|
||
* `code` {string} The Node.js-style error code string assigned to
|
||
`error.code`. Defaults to `'ERR_QUIC_STREAM_ABORTED'`.
|
||
* `type` {string} Either `'application'` (default) or `'transport'`.
|
||
Indicates whether the code is defined by the negotiated
|
||
application protocol (e.g. RFC 9114 for HTTP/3) or by the QUIC
|
||
transport layer (RFC 9000). Stream resets always carry application
|
||
codes, so the default is `'application'`.
|
||
|
||
```mjs
|
||
import { QuicError } from 'node:quic';
|
||
|
||
const err = new QuicError('rejecting stream', { errorCode: 0x10cn });
|
||
console.log(err.code); // 'ERR_QUIC_STREAM_ABORTED'
|
||
console.log(err.errorCode); // 268n
|
||
console.log(err.type); // 'application'
|
||
|
||
const custom = new QuicError('custom failure', {
|
||
errorCode: 0x10cn,
|
||
code: 'ERR_MY_QUIC_FAILURE',
|
||
});
|
||
console.log(custom.code); // 'ERR_MY_QUIC_FAILURE'
|
||
```
|
||
|
||
### `error.errorCode`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
The numeric QUIC error code carried by this error.
|
||
|
||
### `error.type`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
Either `'application'` or `'transport'`. Indicates the namespace of
|
||
[`error.errorCode`][].
|
||
|
||
## Class: `QuicStream`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
### `stream.closed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {Promise}
|
||
|
||
A promise that is fulfilled when the stream is fully closed. It resolves
|
||
when the stream closes cleanly (including idle timeout). It rejects with
|
||
an `ERR_QUIC_APPLICATION_ERROR` or `ERR_QUIC_TRANSPORT_ERROR` when the
|
||
stream is closed due to a QUIC error (e.g., stream reset by the peer,
|
||
CONNECTION\_CLOSE with a non-zero error code).
|
||
|
||
### `stream.destroy([error[, options]])`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
changes:
|
||
- version: v26.2.0
|
||
pr-url: https://github.com/nodejs/node/pull/62876
|
||
description: Added the `options` parameter accepting `code` and `reason`.
|
||
-->
|
||
|
||
* `error` {any}
|
||
* `options` {Object}
|
||
* `code` {bigint|number} The application error code to include in the
|
||
`RESET_STREAM` and `STOP_SENDING` frames sent to the peer. Numbers are
|
||
coerced to `BigInt`. When omitted, the wire code is derived from `error`
|
||
(see below).
|
||
* `reason` {string} An optional human-readable reason string. Accepted for
|
||
symmetry with [`session.close()`][] and [`session.destroy()`][], but
|
||
**not transmitted on the wire** — neither `RESET_STREAM` nor
|
||
`STOP_SENDING` carry a reason field. Provided for application logging
|
||
and for use by the [`stream.onerror`][] callback.
|
||
|
||
Immediately and abruptly destroys the stream. If `error` is provided and
|
||
[`stream.onerror`][] is set, the `onerror` callback is invoked before
|
||
destruction. The `stream.closed` promise rejects with the error.
|
||
|
||
When the stream is destroyed with an `error` (or with an explicit
|
||
`options.code`), the QUIC stack signals the abort to the peer:
|
||
|
||
* If the writable side is still open, a `RESET_STREAM` frame is sent.
|
||
* If the readable side is still open (a bidirectional stream, or a
|
||
remote-initiated unidirectional stream), a `STOP_SENDING` frame is sent.
|
||
|
||
Both frames carry the same wire code, resolved with the following
|
||
precedence:
|
||
|
||
1. `options.code`, when explicitly provided.
|
||
2. [`error.errorCode`][], when `error` is a [`QuicError`][].
|
||
3. The negotiated application protocol's "internal error" code
|
||
(`H3_INTERNAL_ERROR` (`0x102`) for HTTP/3, or the QUIC transport-layer
|
||
`INTERNAL_ERROR` (`0x1`) for raw QUIC).
|
||
|
||
A clean destroy — no `error` and no `options.code` — does not emit
|
||
`RESET_STREAM` or `STOP_SENDING`; the stream's existing close machinery
|
||
handles teardown.
|
||
|
||
See [Aborting a stream][] for an overview of the available stream-abort
|
||
APIs.
|
||
|
||
### `stream.destroyed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True if `stream.destroy()` has been called.
|
||
|
||
### Aborting a stream
|
||
|
||
A QuicStream can be aborted in three ways, each producing different
|
||
wire-frame side effects:
|
||
|
||
* [`writer.fail(reason)`][] — Aborts only the writable side. Sends
|
||
`RESET_STREAM` to the peer. The readable side is unaffected; any data
|
||
already buffered for read remains available.
|
||
* [`stream.destroy()`][] with an `error` argument — Tears the stream
|
||
down completely. Sends `RESET_STREAM` on any still-open writable side
|
||
**and** `STOP_SENDING` on any still-open readable side. The wire code
|
||
is derived from `error` (see [`stream.destroy()`][] for the precedence
|
||
rules).
|
||
* [`stream.destroy()`][] with an explicit `options.code` — Same as the
|
||
previous form but with a caller-supplied wire code, which takes
|
||
precedence over any code carried by `error`.
|
||
|
||
When `error` is a [`QuicError`][], its [`error.errorCode`][] is used as
|
||
the wire code for both `writer.fail()` and `stream.destroy()`. Otherwise
|
||
the implementation falls back to the negotiated application protocol's
|
||
"internal error" code (see [`QuicError`][]).
|
||
|
||
### `stream.early`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True if any data on this stream was received as 0-RTT (early data)
|
||
before the TLS handshake completed. Early data is less secure and
|
||
could potentially be replayed by an attacker. Applications should
|
||
treat early data with appropriate caution.
|
||
|
||
This property is only meaningful on the server side. On the client
|
||
side, it is always `false`.
|
||
|
||
### `stream.direction`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {string|null} One of `'bidi'`, `'uni'`, or `null`.
|
||
|
||
The directionality of the stream, or `null` if the stream has been destroyed
|
||
or is still pending. Read only.
|
||
|
||
### `stream.highWaterMark`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
The maximum number of bytes that the writer will buffer before
|
||
`writeSync()` returns `false`. When the buffered data exceeds this limit,
|
||
the caller should wait for drain before writing more.
|
||
|
||
The value can be changed dynamically at any time. This is particularly
|
||
useful for streams received via the `onstream` callback, where the
|
||
default (65536) may need to be adjusted based on application needs.
|
||
The valid range is `0` to `4294967295`.
|
||
|
||
### `stream.id`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|null}
|
||
|
||
The stream ID, or `null` if the stream has been destroyed or is still
|
||
pending. Read only.
|
||
|
||
### `stream.onerror`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function|undefined}
|
||
|
||
An optional callback invoked when the stream is destroyed with an error.
|
||
This includes errors caused by user callbacks that throw or reject (see
|
||
[Callback error handling][]). The callback receives a single argument: the
|
||
error that triggered the destruction. If the `onerror` callback itself throws
|
||
or returns a promise that rejects, the error is surfaced as an uncaught
|
||
exception. Read/write.
|
||
|
||
### `stream.onblocked`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnBlockedCallback}
|
||
|
||
The callback to invoke when the stream is blocked. Read/write.
|
||
|
||
### `stream.onreset`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.OnStreamErrorCallback}
|
||
|
||
The callback to invoke when the peer aborts a direction of the stream by
|
||
sending a `RESET_STREAM` frame (the peer abandons their writable side, so
|
||
no further data will arrive on our readable side) or a `STOP_SENDING`
|
||
frame (the peer asks us to stop writing on our writable side).
|
||
|
||
The callback receives a Node.js error whose `errorCode` (`bigint`)
|
||
property carries the application error code from the wire frame.
|
||
|
||
The stream is **not** automatically destroyed when this callback fires —
|
||
the application chooses how to react. Common patterns are: ignore (and
|
||
continue using the still-active direction on a bidirectional stream),
|
||
abort the other direction with [`writer.fail()`][], or tear down the
|
||
whole stream with [`stream.destroy()`][]. Read/write.
|
||
|
||
### `stream.headers`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Object|undefined}
|
||
|
||
The buffered initial headers received on this stream, or `undefined` if the
|
||
application does not support headers or no headers have been received yet.
|
||
For server-side streams, this contains the request headers (e.g., `:method`,
|
||
`:path`, `:scheme`). For client-side streams, this contains the response
|
||
headers (e.g., `:status`).
|
||
|
||
Header names are lowercase strings. Multi-value headers are represented as
|
||
arrays. The object has `__proto__: null`.
|
||
|
||
### `stream.onheaders`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function}
|
||
|
||
The callback to invoke when initial headers are received on the stream. The
|
||
callback receives `(headers)` where `headers` is an object (same format as
|
||
`stream.headers`). For HTTP/3, this delivers request pseudo-headers on the
|
||
server side and response headers on the client side. Throws
|
||
`ERR_INVALID_STATE` if set on a session that does not support headers.
|
||
Read/write.
|
||
|
||
### `stream.ontrailers`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function}
|
||
|
||
The callback to invoke when trailing headers are received from the peer.
|
||
The callback receives `(trailers)` where `trailers` is an object in the
|
||
same format as `stream.headers`. Throws `ERR_INVALID_STATE` if set on a
|
||
session that does not support headers. Read/write.
|
||
|
||
### `stream.oninfo`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function}
|
||
|
||
The callback to invoke when informational (1xx) headers are received from
|
||
the server. The callback receives `(headers)` where `headers` is an object
|
||
in the same format as `stream.headers`. Informational headers are sent
|
||
before the final response (e.g., 103 Early Hints). Throws
|
||
`ERR_INVALID_STATE` if set on a session that does not support headers.
|
||
Read/write.
|
||
|
||
### `stream.onwanttrailers`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Function}
|
||
|
||
The callback to invoke when the application is ready for trailing headers
|
||
to be sent. This is called synchronously — the user must call
|
||
[`stream.sendTrailers()`][] within this callback. Throws
|
||
`ERR_INVALID_STATE` if set on a session that does not support headers.
|
||
Read/write.
|
||
|
||
### `stream.pendingTrailers`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Object|undefined}
|
||
|
||
Set trailing headers to be sent automatically when the application requests
|
||
them. This is an alternative to the [`stream.onwanttrailers`][] callback
|
||
for cases where the trailers are known before the body completes. Throws
|
||
`ERR_INVALID_STATE` if set on a session that does not support headers.
|
||
Read/write.
|
||
|
||
### `stream.sendHeaders(headers[, options])`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `headers` {Object} Header object with string keys and string or
|
||
string-array values. Pseudo-headers (`:method`, `:path`, etc.) must
|
||
appear before regular headers.
|
||
* `options` {Object}
|
||
* `terminal` {boolean} If `true`, the stream is closed for sending
|
||
after the headers (no body will follow). **Default:** `false`.
|
||
* Returns: {boolean}
|
||
|
||
Sends initial or response headers on the stream. For client-side streams,
|
||
this sends request headers. For server-side streams, this sends response
|
||
headers. Throws `ERR_INVALID_STATE` if the session does not support headers.
|
||
|
||
### `stream.sendInformationalHeaders(headers)`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `headers` {Object} Header object. Must include `:status` with a 1xx
|
||
value (e.g., `{ ':status': '103', 'link': '</style.css>; rel=preload' }`).
|
||
* Returns: {boolean}
|
||
|
||
Sends informational (1xx) response headers. Server only. Throws
|
||
`ERR_INVALID_STATE` if the session does not support headers.
|
||
|
||
### `stream.sendTrailers(headers)`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `headers` {Object} Trailing header object. Pseudo-headers must not be
|
||
included in trailers.
|
||
* Returns: {boolean}
|
||
|
||
Sends trailing headers on the stream. Must be called synchronously during
|
||
the [`stream.onwanttrailers`][] callback, or set ahead of time via
|
||
[`stream.pendingTrailers`][]. Throws `ERR_INVALID_STATE` if the session
|
||
does not support headers.
|
||
|
||
### `stream.priority`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Object|null}
|
||
* `level` {string} One of `'high'`, `'default'`, or `'low'`.
|
||
* `incremental` {boolean} Whether the stream data should be interleaved
|
||
with other streams of the same priority level.
|
||
|
||
The current priority of the stream. Returns `null` if the session does not
|
||
support priority (e.g. non-HTTP/3) or if the stream has been destroyed.
|
||
Read only. Use [`stream.setPriority()`][] to change the priority.
|
||
|
||
On client-side HTTP/3 sessions, the value reflects what was set via
|
||
[`stream.setPriority()`][]. On server-side HTTP/3 sessions, the value
|
||
reflects the peer's requested priority (e.g., from `PRIORITY_UPDATE` frames).
|
||
|
||
### `stream.setPriority([options])`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `options` {Object}
|
||
* `level` {string} The priority level. One of `'high'`, `'default'`, or
|
||
`'low'`. **Default:** `'default'`.
|
||
* `incremental` {boolean} When `true`, data from this stream may be
|
||
interleaved with data from other streams of the same priority level.
|
||
**Default:** `false`.
|
||
|
||
Sets the priority of the stream. Throws `ERR_INVALID_STATE` if the session
|
||
does not support priority (e.g. non-HTTP/3). Has no effect if the stream
|
||
has been destroyed.
|
||
|
||
### `stream[Symbol.asyncIterator]()`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Returns: {AsyncIterableIterator} yielding {Uint8Array\[]}
|
||
|
||
The stream implements `Symbol.asyncIterator`, making it directly usable
|
||
in `for await...of` loops. Each iteration yields a batch of `Uint8Array`
|
||
chunks.
|
||
|
||
Only one async iterator can be obtained per stream. A second call throws
|
||
`ERR_INVALID_STATE`. Non-readable streams (outbound-only unidirectional
|
||
or closed) return an immediately-finished iterator.
|
||
|
||
```mjs
|
||
for await (const chunks of stream) {
|
||
for (const chunk of chunks) {
|
||
// Process each Uint8Array chunk
|
||
}
|
||
}
|
||
```
|
||
|
||
Compatible with stream/iter utilities:
|
||
|
||
```mjs
|
||
import Stream from 'node:stream/iter';
|
||
const body = await Stream.bytes(stream);
|
||
const text = await Stream.text(stream);
|
||
await Stream.pipeTo(stream, someWriter);
|
||
```
|
||
|
||
### `stream.writer`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {Object}
|
||
|
||
Returns a Writer object for pushing data to the stream incrementally.
|
||
The Writer implements the stream/iter Writer interface with the
|
||
try-sync-fallback-to-async pattern.
|
||
|
||
Only available when no `body` source was provided at creation time or via
|
||
[`stream.setBody()`][]. Non-writable streams return an already-closed
|
||
Writer. Throws `ERR_INVALID_STATE` if the outbound is already configured.
|
||
|
||
The Writer has the following methods:
|
||
|
||
* `writeSync(chunk)` — Synchronous write. Returns `true` if accepted,
|
||
`false` if flow-controlled. Data is NOT accepted on `false`.
|
||
* `write(chunk[, options])` — Async write with drain wait. `options.signal`
|
||
is checked at entry but not observed during the write.
|
||
* `writevSync(chunks)` — Synchronous vectored write. All-or-nothing.
|
||
* `writev(chunks[, options])` — Async vectored write.
|
||
* `endSync()` — Synchronous close. Returns total bytes or `-1`.
|
||
* `end([options])` — Async close.
|
||
* `fail(reason)` — Errors the stream (sends `RESET_STREAM` to peer).
|
||
When `reason` is a [`QuicError`][], its [`error.errorCode`][] is used
|
||
as the wire code on the resulting `RESET_STREAM` frame; otherwise
|
||
the wire code falls back to the negotiated application protocol's
|
||
"internal error" code (`H3_INTERNAL_ERROR` (`0x102`) for HTTP/3, or
|
||
the QUIC transport-layer `INTERNAL_ERROR` (`0x1`) for raw QUIC).
|
||
See [`stream.destroy()`][] for a full-stream abort that also resets
|
||
the readable side via `STOP_SENDING`.
|
||
* `desiredSize` — Available capacity in bytes, or `null` if closed/errored.
|
||
|
||
The bytes from each `writeSync()` / `writevSync()` / `write()` / `writev()`
|
||
input chunk are copied into an internal buffer, so the caller's source
|
||
buffer is unchanged and may be reused or mutated immediately after the
|
||
call returns. Callers that want to ensure a source buffer cannot be
|
||
mutated after handing it off can call `ArrayBuffer.prototype.transfer()`
|
||
themselves before passing the buffer.
|
||
|
||
### `stream.setBody(body)`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `body` {string | ArrayBuffer | SharedArrayBuffer | ArrayBufferView |
|
||
Blob | FileHandle | AsyncIterable | Iterable | Promise | null}
|
||
|
||
Sets the outbound body source for the stream. Can only be called once.
|
||
Mutually exclusive with [`stream.writer`][].
|
||
|
||
The following body source types are supported:
|
||
|
||
* `null` — The writable side is closed immediately (FIN sent with no data).
|
||
* `string` — UTF-8 encoded and sent as a single chunk.
|
||
* `ArrayBuffer`, `SharedArrayBuffer`, `ArrayBufferView` — Sent as a single
|
||
chunk. The bytes are copied into an internal buffer, so the caller's
|
||
source buffer is unchanged and may be reused or mutated immediately
|
||
after the call returns. Callers wanting to ensure their source cannot
|
||
be mutated after handing it off can call
|
||
`ArrayBuffer.prototype.transfer()` themselves before passing the buffer.
|
||
* `Blob` — Sent from the Blob's underlying data queue.
|
||
* {FileHandle} — The file contents are read asynchronously via an
|
||
fd-backed data source. The `FileHandle` must be opened for reading
|
||
(e.g. via [`fs.promises.open(path, 'r')`][]). Once passed as a body, the
|
||
`FileHandle` is locked and cannot be used as a body for another stream.
|
||
The `FileHandle` is automatically closed when the stream finishes.
|
||
* `AsyncIterable`, `Iterable` — Each yielded chunk (string or
|
||
`Uint8Array`) is written incrementally in streaming mode.
|
||
* `Promise` — Awaited; the resolved value is used as the body (subject
|
||
to the same type rules).
|
||
|
||
Throws `ERR_INVALID_STATE` if the outbound is already configured or if
|
||
the writer has been accessed.
|
||
|
||
### `stream.session`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.QuicSession|null}
|
||
|
||
The session that created this stream, or `null` if the stream has been
|
||
destroyed. Read only.
|
||
|
||
### `stream.stats`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.QuicStream.Stats}
|
||
|
||
The current statistics for the stream. Read only.
|
||
|
||
## Class: `QuicStream.Stats`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
### `streamStats.ackedAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.bytesAccumulated`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
The current number of bytes sitting in the stream's receive accumulation
|
||
buffer, awaiting delivery to the application. A value near zero indicates
|
||
the reader is keeping up with incoming data. A value near the stream's
|
||
flow control window indicates the application is not consuming data fast
|
||
enough.
|
||
|
||
### `streamStats.bytesReceived`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.bytesSent`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.createdAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.destroyedAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.finalSize`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.isConnected`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.maxBytesAccumulated`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
The peak number of bytes that were accumulated in the stream's receive
|
||
buffer at any point during the stream's lifetime. This value only
|
||
increases monotonically. It is useful for diagnosing whether a stream
|
||
experienced backpressure episodes and whether the accumulation buffer
|
||
sizing is appropriate for the workload.
|
||
|
||
### `streamStats.maxOffset`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.maxOffsetAcknowledged`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.maxOffsetReceived`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.openedAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
### `streamStats.receivedAt`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint}
|
||
|
||
## Types
|
||
|
||
### type: `ApplicationOptions`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {Object}
|
||
|
||
The application specific options.
|
||
|
||
#### `applicationOptions.maxHeaderPairs`
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Maximum number of header name-value pairs accepted per header block.
|
||
Headers beyond this limit are silently dropped. **Default:** `128`
|
||
|
||
#### `applicationOptions.maxHeaderLength`
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Maximum total byte length of all header names and values combined per header
|
||
block. Headers that would push the total over this limit are silently
|
||
dropped. **Default:** `8192`
|
||
|
||
#### `applicationOptions.maxFieldSectionSize`
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Maximum size of a compressed header field section (QPACK). `0` means
|
||
unlimited. **Default:** `0`
|
||
|
||
#### `applicationOptions.qpackMaxDTableCapacity`
|
||
|
||
* Type: {bigint|number}
|
||
|
||
QPACK dynamic table capacity in bytes. Set to `0` to disable the dynamic
|
||
table. **Default:** `4096`
|
||
|
||
#### `applicationOptions.qpackEncoderMaxDTableCapacity`
|
||
|
||
* Type: {bigint|number}
|
||
|
||
QPACK encoder maximum dynamic table capacity. **Default:** `4096`
|
||
|
||
#### `applicationOptions.qpackBlockedStreams`
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Maximum number of streams that can e blocked waiting for QPACK dynamic table
|
||
updates. **Default:** `100`
|
||
|
||
#### `applicationOptions.enableConnectProtocol`
|
||
|
||
* Type: {boolean}
|
||
|
||
Enable the extended CONNECT protocol (RFC 9220). **Default:** `false`
|
||
|
||
#### `applicationOptions.enableDatagrams`
|
||
|
||
* Type: {boolean}
|
||
|
||
Enable HTTP/3 datagrams (RFC 9297). **Default:** `false`
|
||
|
||
### Type: `EndpointOptions`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {Object}
|
||
|
||
The endpoint configuration options passed when constructing a new `QuicEndpoint` instance.
|
||
|
||
#### `endpointOptions.address`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {net.SocketAddress | string} The local UDP address and port the endpoint should bind to.
|
||
|
||
If not specified the endpoint will bind to IPv4 `localhost` on a random port.
|
||
|
||
#### `endpointOptions.blockList`
|
||
|
||
* Type: {net.BlockList}
|
||
|
||
An optional [`net.BlockList`][] instance for filtering incoming packets by
|
||
source address. When configured, every received UDP packet is checked against
|
||
the block list before any QUIC processing occurs, minimizing resource
|
||
expenditure on blocked sources. The block list is evaluated live — rules
|
||
added to the `BlockList` object after the endpoint is created take effect
|
||
immediately.
|
||
|
||
See [`endpointOptions.blockListPolicy`][] for how matches are interpreted.
|
||
|
||
#### `endpointOptions.blockListPolicy`
|
||
|
||
* Type: {string} One of `'deny'` or `'allow'`.
|
||
* **Default:** `'deny'`
|
||
|
||
Controls how the [`endpointOptions.blockList`][] is interpreted:
|
||
|
||
* `'deny'` — Packets from addresses matching the block list are dropped.
|
||
All other addresses are accepted. This is the typical blocklist mode.
|
||
* `'allow'` — Only packets from addresses matching the block list are
|
||
accepted. All other addresses are dropped. This is an allowlist mode
|
||
for restricting access to known clients.
|
||
|
||
If no block list is configured, this option has no effect.
|
||
|
||
#### `endpointOptions.addressLRUSize`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
The endpoint maintains an internal cache of validated socket addresses as a
|
||
performance optimization. This option sets the maximum number of addresses
|
||
that are cached. This is an advanced option that users typically won't have
|
||
need to specify.
|
||
|
||
#### `endpointOptions.disableStatelessReset`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
When `true`, the endpoint will not send stateless reset packets in response
|
||
to packets from unknown connections. Stateless resets allow a peer to detect
|
||
that a connection has been lost even when the server has no state for it.
|
||
Disabling them may be useful in testing or when stateless resets are handled
|
||
at a different layer.
|
||
|
||
#### `endpointOptions.idleTimeout`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
* Default: `0`
|
||
|
||
The number of seconds an endpoint will remain alive after all sessions have
|
||
closed and it is no longer listening. A value of `0` (default) means the
|
||
endpoint is only destroyed when explicitly closed via `endpoint.close()` or
|
||
`endpoint.destroy()`. A positive value starts an idle timer when the endpoint
|
||
becomes idle; if no new sessions are created before the timer fires, the
|
||
endpoint is automatically destroyed. This is useful for connection pooling
|
||
where endpoints should linger briefly for reuse by future `connect()` calls.
|
||
|
||
#### `endpointOptions.ipv6Only`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
When `true`, indicates that the endpoint should bind only to IPv6 addresses.
|
||
|
||
#### `endpointOptions.reusePort`
|
||
|
||
<!-- YAML
|
||
added:
|
||
- v26.3.0
|
||
- v24.18.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
* Default: `false`
|
||
|
||
When `true`, allows multiple endpoints (across separate processes) to bind to
|
||
the same address and port. The kernel will load-balance incoming UDP datagrams
|
||
across all sockets bound with this option. This enables horizontal scaling of
|
||
QUIC servers by running multiple Node.js processes on the same port.
|
||
|
||
Supported on Linux 3.9+ and DragonFlyBSD 3.6+. On unsupported platforms, the
|
||
bind will fail with an error.
|
||
|
||
#### `endpointOptions.maxConnectionsPerHost`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
* Default: `0` (unlimited)
|
||
|
||
Specifies the maximum number of concurrent sessions allowed per remote IP
|
||
address (ignoring port). When the limit is reached, new connections from the
|
||
same IP are refused with `CONNECTION_REFUSED`. A value of `0` disables the
|
||
limit. The maximum value is `65535`.
|
||
|
||
This limit can also be changed dynamically after construction via
|
||
[`endpoint.maxConnectionsPerHost`][].
|
||
|
||
#### `endpointOptions.maxConnectionsTotal`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
* Default: `0` (unlimited)
|
||
|
||
Specifies the maximum total number of concurrent sessions across all remote
|
||
addresses. When the limit is reached, new connections are refused with
|
||
`CONNECTION_REFUSED`. A value of `0` disables the limit. The maximum value is
|
||
`65535`.
|
||
|
||
This limit can also be changed dynamically after construction via
|
||
[`endpoint.maxConnectionsTotal`][].
|
||
|
||
#### `endpointOptions.retryRate`
|
||
|
||
* Type: {number}
|
||
* **Default:** `100`
|
||
|
||
The maximum number of QUIC retry packets the endpoint will send per second.
|
||
This is a global rate limit (not per-host) that caps the total server-wide
|
||
retry response rate, preventing spoofed-source floods from consuming unbounded
|
||
resources.
|
||
|
||
#### `endpointOptions.retryBurst`
|
||
|
||
* Type: {number}
|
||
* **Default:** `200`
|
||
|
||
The maximum burst of retry packets allowed before rate limiting takes effect.
|
||
|
||
#### `endpointOptions.statelessResetRate`
|
||
|
||
* Type: {number}
|
||
* **Default:** `100`
|
||
|
||
The maximum number of stateless reset packets the endpoint will send per second.
|
||
|
||
#### `endpointOptions.statelessResetBurst`
|
||
|
||
* Type: {number}
|
||
* **Default:** `200`
|
||
|
||
The maximum burst of stateless reset packets allowed before rate limiting
|
||
takes effect.
|
||
|
||
#### `endpointOptions.versionNegotiationRate`
|
||
|
||
* Type: {number}
|
||
* **Default:** `100`
|
||
|
||
The maximum number of version negotiation packets the endpoint will send per
|
||
second.
|
||
|
||
#### `endpointOptions.versionNegotiationBurst`
|
||
|
||
* Type: {number}
|
||
* **Default:** `200`
|
||
|
||
The maximum burst of version negotiation packets allowed before rate limiting
|
||
takes effect.
|
||
|
||
#### `endpointOptions.immediateCloseRate`
|
||
|
||
* Type: {number}
|
||
* **Default:** `100`
|
||
|
||
The maximum number of immediate connection close packets the endpoint will
|
||
send per second.
|
||
|
||
#### `endpointOptions.immediateCloseBurst`
|
||
|
||
* Type: {number}
|
||
* **Default:** `200`
|
||
|
||
The maximum burst of immediate connection close packets allowed before rate
|
||
limiting takes effect.
|
||
|
||
#### `endpointOptions.sessionCreationRate`
|
||
|
||
* Type: {number}
|
||
* **Default:** `50`
|
||
|
||
The maximum number of new sessions that a single remote address can create per
|
||
second. This is a per-host rate limit tracked in the address validation LRU
|
||
cache. It prevents a validated remote address from churning through sessions
|
||
(rapidly opening and abandoning connections) faster than the server can handle.
|
||
For benchmarking where traffic comes from a single source, set this to a high
|
||
value.
|
||
|
||
#### `endpointOptions.sessionCreationBurst`
|
||
|
||
* Type: {number}
|
||
* **Default:** `100`
|
||
|
||
The maximum burst of new session creations allowed from a single remote address
|
||
before rate limiting takes effect.
|
||
|
||
#### `endpointOptions.retryTokenExpiration`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Specifies the length of time a QUIC retry token is considered valid.
|
||
|
||
#### `endpointOptions.resetTokenSecret`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {ArrayBufferView}
|
||
|
||
Specifies the 16-byte secret used to generate QUIC retry tokens.
|
||
|
||
#### `endpointOptions.tokenExpiration`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Specifies the length of time a QUIC token is considered valid.
|
||
|
||
#### `endpointOptions.tokenSecret`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {ArrayBufferView}
|
||
|
||
Specifies the 16-byte secret used to generate QUIC tokens.
|
||
|
||
#### `endpointOptions.udpReceiveBufferSize`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
#### `endpointOptions.udpSendBufferSize`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
#### `endpointOptions.udpTTL`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
#### `endpointOptions.validateAddress`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
When `true`, requires that the endpoint validate peer addresses using retry packets
|
||
while establishing a new connection.
|
||
|
||
### Type: `SessionOptions`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
#### `sessionOptions.alpn`
|
||
|
||
<!-- YAML
|
||
added:
|
||
- v26.1.0
|
||
- v24.16.0
|
||
-->
|
||
|
||
* Type: {string} (client) | {string\[]} (server)
|
||
|
||
The ALPN (Application-Layer Protocol Negotiation) identifier(s).
|
||
|
||
For **client** sessions, this is a single string specifying the protocol
|
||
the client wants to use (e.g. `'h3'`).
|
||
|
||
For **server** sessions, this is an array of protocol names in preference
|
||
order that the server supports (e.g. `['h3', 'h3-29']`). During the TLS
|
||
handshake, the server selects the first protocol from its list that the
|
||
client also supports.
|
||
|
||
The negotiated ALPN determines which Application implementation is used
|
||
for the session. `'h3'` and `'h3-*'` variants select the HTTP/3
|
||
application; all other values select the default application.
|
||
|
||
Default: `'h3'`
|
||
|
||
#### `sessionOptions.application`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {quic.ApplicationOptions}
|
||
|
||
Application-specific options.
|
||
|
||
```mjs
|
||
const { listen } = await import('node:quic');
|
||
|
||
await listen((session) => { /* ... */ }, {
|
||
application: {
|
||
maxHeaderPairs: 64,
|
||
qpackMaxDTableCapacity: 8192,
|
||
enableDatagrams: true,
|
||
},
|
||
// ... other session options
|
||
});
|
||
```
|
||
|
||
#### `sessionOptions.ca` (client only)
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]}
|
||
|
||
The CA certificates to use for client sessions. For server sessions, CA
|
||
certificates are specified per-identity in the [`sessionOptions.sni`][] map.
|
||
|
||
#### `sessionOptions.cc`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
Specifies the congestion control algorithm that will be used.
|
||
Must be set to one of either `'reno'`, `'cubic'`, or `'bbr'`.
|
||
|
||
This is an advanced option that users typically won't have need to specify.
|
||
|
||
#### `sessionOptions.certs` (client only)
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]}
|
||
|
||
The TLS certificates to use for client sessions. For server sessions,
|
||
certificates are specified per-identity in the [`sessionOptions.sni`][] map.
|
||
|
||
#### `sessionOptions.ciphers`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
The list of supported TLS 1.3 cipher algorithms.
|
||
|
||
#### `sessionOptions.crl` (client only)
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]}
|
||
|
||
The CRL to use for client sessions. For server sessions, CRLs are specified
|
||
per-identity in the [`sessionOptions.sni`][] map.
|
||
|
||
#### `sessionOptions.enableEarlyData`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {boolean} **Default:** `true`
|
||
|
||
When `true`, enables TLS 0-RTT early data for this session. Early data
|
||
allows the client to send application data before the TLS handshake
|
||
completes, reducing latency on reconnection when a valid session ticket
|
||
is available. Set to `false` to disable early data support.
|
||
|
||
#### `sessionOptions.groups`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
The list of supported TLS 1.3 cipher groups.
|
||
|
||
#### `sessionOptions.keylog`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
When `true`, enables TLS key logging for the session. Key material is
|
||
delivered to the [`session.onkeylog`][] callback in [NSS Key Log Format][].
|
||
Each callback invocation receives a single line of key material. The output
|
||
can be used with tools such as Wireshark to decrypt captured QUIC traffic.
|
||
|
||
#### `sessionOptions.keys` (client only)
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
changes:
|
||
- version:
|
||
- v25.9.0
|
||
- v24.15.0
|
||
pr-url: https://github.com/nodejs/node/pull/62335
|
||
description: CryptoKey is no longer accepted.
|
||
-->
|
||
|
||
* Type: {KeyObject|KeyObject\[]}
|
||
|
||
The TLS crypto keys to use for client sessions. For server sessions,
|
||
keys are specified per-identity in the [`sessionOptions.sni`][] map.
|
||
|
||
#### `sessionOptions.maxPayloadSize`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Specifies the maximum UDP packet payload size.
|
||
|
||
#### `sessionOptions.maxStreamWindow`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Specifies the maximum stream flow-control window size.
|
||
|
||
#### `sessionOptions.maxWindow`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Specifies the maximum session flow-control window size.
|
||
|
||
#### `sessionOptions.minVersion`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
The minimum QUIC version number to allow. This is an advanced option that users
|
||
typically won't have need to specify.
|
||
|
||
#### `sessionOptions.preferredAddressPolicy`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {string} One of `'use'`, `'ignore'`, or `'default'`.
|
||
* **Default:** `'ignore'`
|
||
|
||
When the remote peer advertises a preferred address, this option specifies whether
|
||
to use it or ignore it. The default is `'ignore'` because honoring a server's
|
||
preferred address causes the client to migrate its connection to a different IP
|
||
address, which can be exploited for data exfiltration attacks that are
|
||
indistinguishable from legitimate QUIC connection migration at the network level.
|
||
Set to `'use'` only when connecting to trusted servers that require preferred
|
||
address migration.
|
||
|
||
#### `sessionOptions.qlog`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
When `true`, enables [qlog][] diagnostic output for the session. Qlog data
|
||
is delivered to the [`session.onqlog`][] callback as chunks of [JSON-SEQ][]
|
||
formatted text. The output can be analyzed with qlog visualization tools
|
||
such as [qvis][].
|
||
|
||
#### `sessionOptions.sessionTicket`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {ArrayBufferView} A session ticket to use for 0RTT session resumption.
|
||
|
||
#### `sessionOptions.datagramDropPolicy`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
* **Default:** `'drop-oldest'`
|
||
|
||
Controls which datagram to drop when the pending datagram queue
|
||
(sized by [`session.maxPendingDatagrams`][]) is full. Must be one of
|
||
`'drop-oldest'` (discard the oldest queued datagram to make room) or
|
||
`'drop-newest'` (reject the incoming datagram). Dropped datagrams are
|
||
reported as lost via the `ondatagramstatus` callback.
|
||
|
||
This option is immutable after session creation.
|
||
|
||
#### `sessionOptions.streamIdleTimeout`
|
||
|
||
* Type: {bigint|number}
|
||
* **Default:** `30000` (30 seconds)
|
||
|
||
The maximum time in milliseconds that a peer-initiated stream can be idle
|
||
(no data received) before it is automatically destroyed. This protects
|
||
against slowloris-style attacks where a remote peer opens streams but never
|
||
sends data, holding server resources indefinitely. Only peer-initiated
|
||
streams are checked — locally-initiated streams are the application's
|
||
responsibility. Set to `0` to disable.
|
||
|
||
The idle check runs as part of the normal send processing loop, so it adds
|
||
no additional timers or event loop overhead. The
|
||
`session.stats.streamsIdleTimedOut` counter tracks how many streams have been
|
||
destroyed by this mechanism.
|
||
|
||
#### `sessionOptions.maxDatagramSendAttempts`
|
||
|
||
* Type: {number}
|
||
* **Default:** `5`
|
||
|
||
The maximum number of `SendPendingData` cycles a datagram can survive
|
||
without being sent before it is abandoned. When a datagram cannot be
|
||
sent due to congestion control or packet size constraints, it remains
|
||
in the queue and the attempt counter increments. Once the limit is
|
||
reached, the datagram is dropped and reported as `'abandoned'` via the
|
||
`ondatagramstatus` callback. Valid range: `1` to `255`.
|
||
|
||
#### `sessionOptions.drainingPeriodMultiplier`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
* **Default:** `3`
|
||
|
||
A multiplier applied to the Probe Timeout (PTO) to compute the draining
|
||
period duration after receiving a `CONNECTION_CLOSE` frame from the peer.
|
||
RFC 9000 Section 10.2 requires the draining period to persist for at least
|
||
three times the current PTO. The valid range is `3` to `255`. Values below
|
||
`3` are clamped to `3`.
|
||
|
||
#### `sessionOptions.handshakeTimeout`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Specifies the maximum number of milliseconds a TLS handshake is permitted to take
|
||
to complete before timing out.
|
||
|
||
#### `sessionOptions.initialRtt`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
* **Default:** `0` (use ngtcp2 default of 333ms)
|
||
|
||
Specifies the initial round-trip time estimate in milliseconds. This value is
|
||
used for probe timeout (PTO) computation, initial pacing, and early loss
|
||
detection before the first actual RTT sample is collected from the connection.
|
||
The default of 333ms is appropriate for the general internet. For low-latency
|
||
environments such as loopback or same-rack deployments, setting a value closer
|
||
to the actual RTT (e.g., `1`) avoids unnecessarily conservative initial
|
||
behavior.
|
||
|
||
#### `sessionOptions.keepAlive`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
* **Default:** `0` (disabled)
|
||
|
||
Specifies the keep-alive timeout in milliseconds. When set to a non-zero
|
||
value, PING frames will be sent automatically to keep the connection alive
|
||
before the idle timeout fires. The value should be less than the effective
|
||
idle timeout (`maxIdleTimeout` transport parameter) to be useful.
|
||
|
||
#### `sessionOptions.verifyPeer` (client only)
|
||
|
||
* Type: {string} One of `'strict'`, `'auto'`, or `'manual'`.
|
||
* **Default:** `'auto'`
|
||
|
||
Controls how the client handles server certificate validation:
|
||
|
||
* `'strict'` — OpenSSL aborts the TLS handshake immediately if the server's
|
||
certificate fails validation. The `session.opened` promise rejects with a
|
||
TLS error. The application cannot inspect the certificate or the error
|
||
details. This is the most secure mode.
|
||
|
||
* `'auto'` — The TLS handshake completes regardless of validation result.
|
||
If validation fails, the `session.opened` promise is rejected with an error
|
||
containing the validation reason, and the session is destroyed. The
|
||
`onhandshake` callback (if set) fires before rejection, allowing diagnostic
|
||
logging. This is the default and matches the behavior of `tls.connect()`
|
||
with `rejectUnauthorized: true`.
|
||
|
||
* `'manual'` — The TLS handshake completes regardless of validation result.
|
||
The `session.opened` promise resolves with the handshake info, which includes
|
||
`validationErrorReason` and `validationErrorCode` if validation failed. The
|
||
application is responsible for checking these values and deciding whether to
|
||
continue. Use this mode for custom validation logic, certificate pinning, or
|
||
intentionally accepting self-signed certificates.
|
||
|
||
#### `sessionOptions.servername` (client only)
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
The peer server name to target (SNI). Defaults to `'localhost'`.
|
||
|
||
#### `sessionOptions.sni` (server only)
|
||
|
||
<!-- YAML
|
||
added:
|
||
- v26.1.0
|
||
- v24.16.0
|
||
-->
|
||
|
||
* Type: {Object}
|
||
|
||
An object mapping host names to TLS identity options for Server Name
|
||
Indication (SNI) support. This is required for server sessions and must
|
||
contain at least one entry. The special key `'*'` specifies the optional
|
||
default/fallback identity used when no other host name matches. If no
|
||
wildcard entry is provided, connections with unrecognized server names
|
||
will be rejected with a TLS `unrecognized_name` alert. Each entry may
|
||
contain:
|
||
|
||
* `keys` {KeyObject|KeyObject\[]} The TLS private keys. **Required.**
|
||
* `certs` {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]}
|
||
The TLS certificates. **Required.**
|
||
Optional certificate revocation lists.
|
||
* `verifyPrivateKey` {boolean} Verify the private key. Default: `false`.
|
||
* `port` {number} The port to advertise in ORIGIN frames (RFC 9412) for
|
||
this host name. **Default:** `443`. Only used for HTTP/3 sessions.
|
||
* `authoritative` {boolean} Whether to include this host name in ORIGIN
|
||
frames. **Default:** `true`. Set to `false` to exclude a host name
|
||
from ORIGIN advertisements. Wildcard (`'*'`) entries are always
|
||
excluded regardless of this setting.
|
||
|
||
```mjs
|
||
const endpoint = await listen(callback, {
|
||
sni: {
|
||
'*': { keys: [defaultKey], certs: [defaultCert] },
|
||
'api.example.com': { keys: [apiKey], certs: [apiCert], port: 8443 },
|
||
'www.example.com': { keys: [wwwKey], certs: [wwwCert], ca: [customCA] },
|
||
'internal.example.com': { keys: [intKey], certs: [intCert], authoritative: false },
|
||
},
|
||
});
|
||
```
|
||
|
||
Shared TLS options (such as `ciphers`, `groups`, `keylog`, and `verifyClient`)
|
||
are specified at the top level of the session options and apply to all
|
||
identities. Each SNI entry overrides only the per-identity certificate
|
||
fields.
|
||
|
||
The SNI map can be replaced at runtime using `endpoint.setSNIContexts()`,
|
||
which atomically swaps the map for new sessions while existing sessions
|
||
continue to use their original identity.
|
||
|
||
#### `sessionOptions.tlsTrace`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True to enable TLS tracing output.
|
||
|
||
#### `sessionOptions.token` (client only)
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {ArrayBufferView}
|
||
|
||
An opaque address validation token previously received from the server
|
||
via the [`session.onnewtoken`][] callback. Providing a valid token on
|
||
reconnection allows the client to skip the server's address validation,
|
||
reducing handshake latency.
|
||
|
||
#### `sessionOptions.transportParams`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {quic.TransportParams}
|
||
|
||
The QUIC transport parameters to use for the session.
|
||
|
||
#### `sessionOptions.unacknowledgedPacketThreshold`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
Specifies the maximum number of unacknowledged packets a session should allow.
|
||
|
||
#### `sessionOptions.rejectUnauthorized`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {boolean} **Default:** `true`
|
||
|
||
If `true`, the peer certificate is verified against the list of supplied CAs.
|
||
An error is emitted if verification fails; the error can be inspected via
|
||
the `validationErrorReason` and `validationErrorCode` fields in the
|
||
handshake callback. If `false`, peer certificate verification errors are
|
||
ignored.
|
||
|
||
#### `sessionOptions.reuseEndpoint`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
* Default: `true`
|
||
|
||
When `true` (the default), `connect()` will attempt to reuse an existing
|
||
endpoint rather than creating a new one for each session. This provides
|
||
connection pooling behavior — multiple sessions can share a single UDP
|
||
socket. The reuse logic will not return an endpoint that is listening on
|
||
the same address as the connect target (to prevent CID routing conflicts).
|
||
|
||
Set to `false` to force creation of a new endpoint for the session. This
|
||
is useful when endpoint isolation is required (e.g., testing stateless
|
||
reset behavior where source port identity matters).
|
||
|
||
#### `sessionOptions.verifyClient`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True to require verification of TLS client certificate.
|
||
|
||
#### `sessionOptions.verifyPrivateKey` (client only)
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {boolean}
|
||
|
||
True to require private key verification for client sessions. For server
|
||
sessions, this option is specified per-identity in the
|
||
[`sessionOptions.sni`][] map.
|
||
|
||
#### `sessionOptions.version`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {number}
|
||
|
||
The QUIC version number to use. This is an advanced option that users typically
|
||
won't have need to specify.
|
||
|
||
### Type: `TransportParams`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
The `TransportParams` type represents the QUIC transport parameters that are
|
||
negotiated during session establishment. These parameters are used when
|
||
creating a session. The negotiated values can be observed via the
|
||
`session.localTransportParams` and `session.remoteTransportParams` properties.
|
||
|
||
#### `transportParams.initialSCID`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
The initial source connection ID (SCID) specified. This field is ignored on
|
||
creation of the session and is provided for informational purposes only when
|
||
available in the `session.localTransportParams` and
|
||
`session.remoteTransportParams` properties.
|
||
|
||
#### `transportParams.originalDCID`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
The original destination connection ID (DCID) specified. This field is
|
||
ignored on creation of the session and is provided for informational
|
||
purposes only when available in the `session.localTransportParams` and
|
||
`session.remoteTransportParams` properties.
|
||
|
||
#### `transportParams.preferredAddressIpv4`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {net.SocketAddress} The preferred IPv4 address to advertise (only
|
||
used by servers).
|
||
|
||
#### `transportParams.preferredAddressIpv6`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {net.SocketAddress} The preferred IPv6 address to advertise (only
|
||
used by servers)
|
||
|
||
#### `transportParams.initialMaxStreamDataBidiLocal`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.initialMaxStreamDataBidiRemote`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.initialMaxStreamDataUni`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.initialMaxData`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.initialMaxStreamsBidi`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.initialMaxStreamsUni`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.maxIdleTimeout`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.activeConnectionIDLimit`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.ackDelayExponent`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.maxAckDelay`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
|
||
#### `transportParams.maxDatagramFrameSize`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* Type: {bigint|number}
|
||
* **Default:** `1200`
|
||
|
||
The maximum size in bytes of a DATAGRAM frame payload that this endpoint
|
||
is willing to receive. Set to `0` to disable datagram support. The peer
|
||
will not send datagrams larger than this value. The actual maximum size of
|
||
a datagram that can be _sent_ is determined by the peer's
|
||
`maxDatagramFrameSize`, not this endpoint's value.
|
||
|
||
#### `transportParams.retrySCID`
|
||
|
||
<!-- YAML
|
||
added: v26.3.0
|
||
-->
|
||
|
||
* Type: {string}
|
||
|
||
The retry connection ID specified. This field is ignored on creation
|
||
of the session and is provided for informational purposes only when
|
||
available in the `session.localTransportParams` and
|
||
`session.remoteTransportParams` properties.
|
||
|
||
## Callbacks
|
||
|
||
### Callback error handling
|
||
|
||
All session and stream callbacks may be synchronous functions or async
|
||
functions. If a callback throws synchronously or returns a promise that
|
||
rejects, the error is caught and the owning session or stream is destroyed
|
||
with that error:
|
||
|
||
* Stream callbacks (`onblocked`, `onreset`, `onheaders`, `ontrailers`,
|
||
`oninfo`, `onwanttrailers`): the stream is destroyed.
|
||
* Session callbacks (`onapplication`, `onstream`, `ondatagram`,
|
||
`ondatagramstatus`, `onpathvalidation`, `onsessionticket`,
|
||
`onnewtoken`, `onversionnegotiation`, `onorigin`, `ongoaway`,
|
||
`onhandshake`, `onkeylog`, `onqlog`): the session is destroyed along
|
||
with all of its streams.
|
||
|
||
Before destruction, the optional [`session.onerror`][] or
|
||
[`stream.onerror`][] callback is invoked (if set), giving the application a
|
||
chance to observe or log the error. The `session.closed` or `stream.closed`
|
||
promise will reject with the error.
|
||
|
||
If the `onerror` callback itself throws or returns a promise that rejects,
|
||
the error from `onerror` is surfaced as an uncaught exception.
|
||
|
||
### Callback: `OnSessionCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicEndpoint}
|
||
* `session` {quic.QuicSession}
|
||
|
||
The callback function that is invoked when a new session is initiated by a remote peer.
|
||
|
||
### Callback: `OnStreamCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `stream` {quic.QuicStream}
|
||
|
||
### Callback: `OnDatagramCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `datagram` {Uint8Array}
|
||
* `early` {boolean}
|
||
|
||
### Callback: `OnDatagramStatusCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `id` {bigint}
|
||
* `status` {string} One of `'acknowledged'`, `'lost'`, or `'abandoned'`.
|
||
`'acknowledged'` means the peer confirmed receipt. `'lost'` means the
|
||
datagram was sent but the network lost it. `'abandoned'` means the
|
||
datagram was never sent on the wire (dropped due to queue overflow,
|
||
send attempt limit exceeded, or frame size rejection).
|
||
|
||
### Callback: `OnApplicationCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `applicationoption` {quic.QuicSession}
|
||
|
||
The callback function that is invoked when application options change.
|
||
E.g. for http/3 settings are included in applications options and
|
||
may arrive after the connection is established.
|
||
|
||
### Callback: `OnPathValidationCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `result` {string} One of either `'success'`, `'failure'`, or `'aborted'`.
|
||
* `newLocalAddress` {net.SocketAddress} The local address of the validated path.
|
||
* `newRemoteAddress` {net.SocketAddress} The remote address of the validated path.
|
||
* `oldLocalAddress` {net.SocketAddress | null} The local address of the previous
|
||
path, or `null` if this is the first path validation (e.g., preferred address
|
||
migration from the client's perspective).
|
||
* `oldRemoteAddress` {net.SocketAddress | null} The remote address of the previous
|
||
path, or `null`.
|
||
* `preferredAddress` {boolean} `true` if the path validation was triggered by
|
||
a preferred address migration on the client side. `undefined` on the server side.
|
||
|
||
### Callback: `OnSessionTicketCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `ticket` {Object}
|
||
|
||
### Callback: `OnVersionNegotiationCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `version` {number} The QUIC version that was configured for this session
|
||
(the version that the server did not support).
|
||
* `requestedVersions` {number\[]} The versions advertised by the server in
|
||
the Version Negotiation packet. These are the versions the server supports.
|
||
* `supportedVersions` {number\[]} The versions supported locally, expressed
|
||
as a two-element array `[minVersion, maxVersion]`.
|
||
|
||
Called when the server responds to the client's Initial packet with a
|
||
Version Negotiation packet, indicating that the version used by the client
|
||
is not supported. The session is always destroyed immediately after this
|
||
callback returns.
|
||
|
||
### Callback: `OnHandshakeCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `info` {Object} The same object that `session.opened` resolves with.
|
||
* `local` {net.SocketAddress} The local socket address.
|
||
* `remote` {net.SocketAddress} The remote socket address.
|
||
* `servername` {string} The SNI server name negotiated during the handshake.
|
||
* `protocol` {string} The ALPN protocol negotiated during the handshake.
|
||
* `cipher` {string} The name of the negotiated TLS cipher suite.
|
||
* `cipherVersion` {string} The TLS protocol version of the cipher suite.
|
||
* `validationErrorReason` {string} If certificate validation failed, the
|
||
reason string. Empty string if validation succeeded.
|
||
* `validationErrorCode` {number} If certificate validation failed, the
|
||
error code. `0` if validation succeeded.
|
||
* `earlyDataAttempted` {boolean} Whether 0-RTT early data was attempted.
|
||
* `earlyDataAccepted` {boolean} Whether 0-RTT early data was accepted.
|
||
|
||
### Callback: `OnNewTokenCallback`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `token` {Buffer} The NEW\_TOKEN token data.
|
||
* `address` {SocketAddress} The remote address the token is associated with.
|
||
|
||
### Callback: `OnOriginCallback`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `origins` {string\[]} The list of origins the server is authoritative for.
|
||
|
||
### Callback: `OnKeylogCallback`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `line` {string} A single line of [NSS Key Log Format][] text, including
|
||
a trailing newline character.
|
||
|
||
Called when TLS key material is available. Only fires when
|
||
[`sessionOptions.keylog`][] is `true`. Multiple lines are emitted during the
|
||
TLS 1.3 handshake, each containing a secret label, the client random, and
|
||
the secret value.
|
||
|
||
### Callback: `OnQlogCallback`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `this` {quic.QuicSession}
|
||
* `data` {string} A chunk of [JSON-SEQ][] formatted [qlog][] data.
|
||
* `fin` {boolean} `true` if this is the final qlog chunk for the session.
|
||
|
||
Called when qlog diagnostic data is available. Only fires when
|
||
[`sessionOptions.qlog`][] is `true`. The `data` chunks should be
|
||
concatenated in order to produce the complete qlog output. When `fin` is
|
||
`true`, no more chunks will be emitted and the concatenated result is a
|
||
complete JSON-SEQ document.
|
||
|
||
### Callback: `OnBlockedCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicStream}
|
||
|
||
### Callback: `OnStreamErrorCallback`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `this` {quic.QuicStream}
|
||
* `error` {any}
|
||
|
||
### Callback: `OnHeadersCallback`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `this` {quic.QuicStream}
|
||
* `headers` {Object} Header object with lowercase string keys and
|
||
string or string-array values.
|
||
|
||
Called when initial request or response headers are received. For HTTP/3,
|
||
this delivers request pseudo-headers on the server and response headers
|
||
on the client.
|
||
|
||
### Callback: `OnTrailersCallback`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `this` {quic.QuicStream}
|
||
* `trailers` {Object} Trailing header object.
|
||
|
||
Called when trailing headers are received from the peer.
|
||
|
||
### Callback: `OnInfoCallback`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `this` {quic.QuicStream}
|
||
* `headers` {Object} Informational header object.
|
||
|
||
Called when informational (1xx) headers are received from the server
|
||
(e.g., 103 Early Hints).
|
||
|
||
## HTTP/3 support
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
When the negotiated ALPN identifier is `'h3'` (or one of the `'h3-*'`
|
||
draft variants), the QUIC session runs the HTTP/3 application backed
|
||
by `nghttp3`. `'h3'` is the default ALPN for `quic.connect()` and
|
||
`quic.listen()`, so HTTP/3 is what you get unless you select a
|
||
different ALPN explicitly.
|
||
|
||
Selecting the HTTP/3 application enables a number of stream- and
|
||
session-level capabilities that are not available to non-HTTP/3
|
||
applications:
|
||
|
||
* **Headers and trailers** — request and response header blocks
|
||
(including pseudo-headers such as `:method`, `:path`, `:scheme`,
|
||
`:authority`, and `:status`), trailing headers, and informational
|
||
(`1xx`) responses. See [`stream.sendHeaders()`][],
|
||
[`stream.sendTrailers()`][], and
|
||
[`stream.sendInformationalHeaders()`][].
|
||
* **Stream priority (RFC 9218)** — per-stream urgency and
|
||
incremental flags. See [`stream.priority`][] and
|
||
[`stream.setPriority()`][].
|
||
* **HTTP/3 datagrams (RFC 9297)** — unreliable application-layer
|
||
datagrams. The peer must advertise `SETTINGS_H3_DATAGRAM=1`, which
|
||
is enabled by setting [`application.enableDatagrams`][] to `true`
|
||
on both peers. See [`session.sendDatagram()`][] and
|
||
[`session.ondatagram`][].
|
||
* **ORIGIN frame (RFC 9412)** — servers automatically advertise the
|
||
hostnames in their [`sessionOptions.sni`][] map (entries with
|
||
`authoritative: true`); clients receive the list via
|
||
[`session.onorigin`][].
|
||
* **GOAWAY** — graceful shutdown. The server emits `GOAWAY` as part
|
||
of [`session.close()`][]; the client observes it via
|
||
[`session.ongoaway`][] and stops opening new bidirectional streams.
|
||
* **Extended CONNECT settings (RFC 9220)** — the
|
||
`SETTINGS_ENABLE_CONNECT_PROTOCOL` setting can be enabled via
|
||
[`application.enableConnectProtocol`][]. The setting is exchanged
|
||
but the application is responsible for handling the `:protocol`
|
||
pseudo-header and any payload framing on top.
|
||
* **QPACK tuning** — dynamic-table size and blocked-streams limits
|
||
via [`application.qpackMaxDTableCapacity`][] and friends.
|
||
|
||
### Minimal HTTP/3 client
|
||
|
||
```mjs
|
||
import { connect } from 'node:quic';
|
||
import process from 'node:process';
|
||
|
||
const session = await connect('example.com:443', {
|
||
// ALPN defaults to 'h3'.
|
||
servername: 'example.com',
|
||
});
|
||
await session.opened;
|
||
|
||
const stream = await session.createBidirectionalStream({
|
||
headers: {
|
||
':method': 'GET',
|
||
':path': '/',
|
||
':scheme': 'https',
|
||
':authority': 'example.com',
|
||
},
|
||
onheaders(headers) {
|
||
console.log('status:', headers[':status']);
|
||
},
|
||
});
|
||
|
||
const decoder = new TextDecoder();
|
||
for await (const chunks of stream) {
|
||
for (const chunk of chunks) {
|
||
process.stdout.write(decoder.decode(chunk, { stream: true }));
|
||
}
|
||
}
|
||
|
||
await session.close();
|
||
```
|
||
|
||
A few things to note:
|
||
|
||
* `session.createBidirectionalStream({ headers })` automatically
|
||
marks the HEADERS frame as terminal when no `body` is provided —
|
||
the request is `HEADERS` followed by `END_STREAM`.
|
||
* The `onheaders` callback receives the response pseudo-headers and
|
||
regular headers in a single object with lowercase string keys.
|
||
After the callback returns, the same object is also accessible
|
||
via [`stream.headers`][].
|
||
* Reading `for await (const chunks of stream)` consumes the response
|
||
body. Each iteration yields a `Uint8Array[]` batch of chunks.
|
||
* HTTP semantic helpers (URL parsing, method/status validation,
|
||
redirects, content negotiation, and so on) are intentionally not
|
||
built in. The caller is responsible for any HTTP-level handling
|
||
beyond the wire framing.
|
||
|
||
### Minimal HTTP/3 server
|
||
|
||
```mjs
|
||
import { listen } from 'node:quic';
|
||
|
||
const encoder = new TextEncoder();
|
||
|
||
const endpoint = await listen((session) => {
|
||
// The session.onstream callback fires for each new client-initiated stream.
|
||
}, {
|
||
sni: { '*': { keys: [defaultKey], certs: [defaultCert] } },
|
||
// ALPN defaults to 'h3'.
|
||
onheaders(headers) {
|
||
// `this` is the QuicStream. Pseudo-headers are available on the
|
||
// request header block (`:method`, `:path`, `:scheme`,
|
||
// `:authority`).
|
||
if (headers[':path'] === '/health') {
|
||
this.sendHeaders({ ':status': '200', 'content-type': 'text/plain' });
|
||
const w = this.writer;
|
||
w.writeSync(encoder.encode('ok\n'));
|
||
w.endSync();
|
||
} else {
|
||
this.sendHeaders({ ':status': '404' }, { terminal: true });
|
||
}
|
||
},
|
||
});
|
||
|
||
console.log('listening on', endpoint.address);
|
||
```
|
||
|
||
Server-side notes:
|
||
|
||
* Setting `onheaders` at the [`listen()`][`quic.listen()`] level
|
||
applies it to every incoming stream (it is wired up before
|
||
`onstream` fires). Setting it inside `onstream` is too late for
|
||
HTTP/3, where the request HEADERS frame is the first thing that
|
||
arrives on the stream.
|
||
* `this.sendHeaders(headers, { terminal: true })` marks the
|
||
response HEADERS frame as terminal (no body follows).
|
||
* For body responses, send headers first, then write to
|
||
`this.writer` and call `endSync()` to send the body and close the
|
||
stream cleanly.
|
||
|
||
### What is not implemented
|
||
|
||
* **Server push** — `PUSH_PROMISE` and the related push-stream
|
||
machinery are not implemented and are not on the near-term
|
||
roadmap. Server push has limited deployment in practice, and most
|
||
use cases are better served by Early Hints (`103`) or by direct
|
||
fetches from the client.
|
||
* **WebTransport / extended-CONNECT helpers** — the
|
||
`SETTINGS_ENABLE_CONNECT_PROTOCOL` setting can be negotiated but
|
||
there is no built-in support for the `:protocol` pseudo-header,
|
||
WebTransport datagram demultiplexing, or capsule framing.
|
||
* **Higher-level HTTP semantics** — there is no built-in
|
||
request/response router, URL parsing, content-encoding
|
||
negotiation, body-type coercion, redirect following, or
|
||
cookie handling. These are deliberately left to higher-level
|
||
libraries built on top of `node:quic`.
|
||
|
||
## Performance measurement
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
QUIC sessions, streams, and endpoints emit [`PerformanceEntry`][] objects
|
||
with `entryType` set to `'quic'`. These entries are only created when a
|
||
[`PerformanceObserver`][] is observing the `'quic'` entry type, ensuring
|
||
zero overhead when not in use.
|
||
|
||
Each entry provides:
|
||
|
||
* `name` {string} One of `'QuicEndpoint'`, `'QuicSession'`, or `'QuicStream'`.
|
||
* `entryType` {string} Always `'quic'`.
|
||
* `startTime` {number} High-resolution timestamp (ms) when the object was created.
|
||
* `duration` {number} Lifetime in milliseconds from creation to destruction.
|
||
* `detail` {Object} Entry-specific metadata (see below).
|
||
|
||
### `QuicEndpoint` entries
|
||
|
||
* `detail.stats` {QuicEndpointStats} The endpoint's statistics object
|
||
(frozen at destruction time).
|
||
|
||
### `QuicSession` entries
|
||
|
||
* `detail.stats` {QuicSessionStats} The session's statistics object
|
||
(frozen at destruction time). Includes bytes sent/received, RTT
|
||
measurements, congestion window, packet counts, and more.
|
||
* `detail.handshake` {Object|undefined} Timing-relevant handshake metadata,
|
||
or `undefined` if the handshake did not complete before destruction.
|
||
* `servername` {string} The negotiated SNI server name.
|
||
* `protocol` {string} The negotiated ALPN protocol.
|
||
* `earlyDataAttempted` {boolean} Whether 0-RTT early data was attempted.
|
||
* `earlyDataAccepted` {boolean} Whether 0-RTT early data was accepted.
|
||
* `detail.path` {Object|undefined} The session's network path, or
|
||
`undefined` if not yet established.
|
||
* `local` {net.SocketAddress}
|
||
* `remote` {net.SocketAddress}
|
||
|
||
### `QuicStream` entries
|
||
|
||
* `detail.stats` {QuicStreamStats} The stream's statistics object
|
||
(frozen at destruction time). Includes bytes sent/received, timing
|
||
timestamps, and offset tracking.
|
||
* `detail.direction` {string} Either `'bidi'` or `'uni'`.
|
||
|
||
### Example
|
||
|
||
```mjs
|
||
import { PerformanceObserver } from 'node:perf_hooks';
|
||
|
||
const obs = new PerformanceObserver((list) => {
|
||
for (const entry of list.getEntries()) {
|
||
console.log(`${entry.name}: ${entry.duration.toFixed(1)}ms`);
|
||
if (entry.name === 'QuicSession') {
|
||
const { stats, handshake } = entry.detail;
|
||
console.log(` protocol: ${handshake?.protocol}`);
|
||
console.log(` bytes sent: ${stats.bytesSent}`);
|
||
console.log(` smoothed RTT: ${stats.smoothedRtt}ns`);
|
||
}
|
||
}
|
||
});
|
||
obs.observe({ entryTypes: ['quic'] });
|
||
```
|
||
|
||
## Diagnostic Channels
|
||
|
||
### Channel: `quic.endpoint.created`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `config` {quic.EndpointOptions}
|
||
|
||
Published when a new endpoint is created.
|
||
|
||
### Channel: `quic.endpoint.listen`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `options` {quic.SessionOptions}
|
||
|
||
Published when an endpoint begins listening for incoming connections.
|
||
|
||
### Channel: `quic.endpoint.connect`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `address` {net.SocketAddress} The target server address.
|
||
* `options` {quic.SessionOptions}
|
||
|
||
Published when [`quic.connect()`][] is about to create a client session.
|
||
Fires before the ngtcp2 connection is established, allowing diagnostic
|
||
subscribers to observe the connection intent.
|
||
|
||
### Channel: `quic.endpoint.closing`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `hasPendingError` {boolean}
|
||
|
||
Published when an endpoint begins gracefully closing.
|
||
|
||
### Channel: `quic.endpoint.closed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `stats` {quic.QuicEndpoint.Stats} Final endpoint statistics.
|
||
|
||
Published when an endpoint has finished closing and is destroyed.
|
||
|
||
### Channel: `quic.endpoint.error`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `error` {any}
|
||
|
||
Published when an endpoint encounters an error that causes it to close.
|
||
|
||
### Channel: `quic.endpoint.busy.change`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `busy` {boolean}
|
||
|
||
Published when an endpoint's busy state changes.
|
||
|
||
### Channel: `quic.session.application`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `applicationoptions` {quic.ApplicationOptions} Current application options.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a locally-initiated stream is opened.
|
||
|
||
### Channel: `quic.session.created.client`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `session` {quic.QuicSession}
|
||
* `address` {net.SocketAddress} The remote server address.
|
||
* `options` {quic.SessionOptions}
|
||
|
||
Published when a client-initiated session is created.
|
||
|
||
### Channel: `quic.session.created.server`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `endpoint` {quic.QuicEndpoint}
|
||
* `session` {quic.QuicSession}
|
||
* `address` {net.SocketAddress|undefined} The remote peer address.
|
||
|
||
Published when a server-side session is created for an incoming connection.
|
||
|
||
### Channel: `quic.session.open.stream`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
* `direction` {string} Either `'bidi'` or `'uni'`.
|
||
|
||
Published when a locally-initiated stream is opened.
|
||
|
||
### Channel: `quic.session.received.stream`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
* `direction` {string} Either `'bidi'` or `'uni'`.
|
||
|
||
Published when a remotely-initiated stream is received.
|
||
|
||
### Channel: `quic.session.send.datagram`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `id` {bigint} The datagram ID.
|
||
* `length` {number} The datagram payload size in bytes.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a datagram is queued for sending.
|
||
|
||
### Channel: `quic.session.update.key`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a TLS key update is initiated.
|
||
|
||
### Channel: `quic.session.closing`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a session begins gracefully closing (including when a
|
||
GOAWAY frame is received from the peer).
|
||
|
||
### Channel: `quic.session.closed`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `session` {quic.QuicSession}
|
||
* `error` {any} The error that caused the close, or `undefined` if clean.
|
||
* `stats` {quic.QuicSession.Stats} Final session statistics.
|
||
|
||
Published when a session is destroyed. The `stats` object is a snapshot
|
||
of the final statistics at the time of destruction.
|
||
|
||
### Channel: `quic.session.error`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `session` {quic.QuicSession}
|
||
* `error` {any} The error that caused the session to be destroyed.
|
||
|
||
Published when a session is destroyed due to an error. Fires before the
|
||
`onerror` callback and before streams are torn down. Unlike
|
||
`quic.session.closed` (which fires for both clean and error closes), this
|
||
channel fires only when an error is present, making it suitable for
|
||
error-only alerting.
|
||
|
||
### Channel: `quic.session.receive.datagram`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `length` {number} The datagram payload size in bytes.
|
||
* `early` {boolean} Whether the datagram was received as 0-RTT early data.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a datagram is received from the remote peer.
|
||
|
||
### Channel: `quic.session.receive.datagram.status`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `id` {bigint} The datagram ID.
|
||
* `status` {string} One of `'acknowledged'`, `'lost'`, or `'abandoned'`.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when the delivery status of a sent datagram is updated.
|
||
|
||
### Channel: `quic.session.path.validation`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `result` {string} One of `'success'`, `'failure'`, or `'aborted'`.
|
||
* `newLocalAddress` {net.SocketAddress}
|
||
* `newRemoteAddress` {net.SocketAddress}
|
||
* `oldLocalAddress` {net.SocketAddress|null}
|
||
* `oldRemoteAddress` {net.SocketAddress|null}
|
||
* `preferredAddress` {boolean}
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a path validation attempt completes.
|
||
|
||
### Channel: `quic.session.new.token`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `token` {Buffer} The NEW\_TOKEN token data.
|
||
* `address` {net.SocketAddress} The remote server address.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a client session receives a NEW\_TOKEN frame from the
|
||
server.
|
||
|
||
### Channel: `quic.session.ticket`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `ticket` {Object} The opaque session ticket.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a new TLS session ticket is received.
|
||
|
||
### Channel: `quic.session.version.negotiation`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `version` {number} The QUIC version that was configured for this session.
|
||
* `requestedVersions` {number\[]} The versions advertised by the server.
|
||
* `supportedVersions` {number\[]} The versions supported locally.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when the client receives a Version Negotiation packet from the
|
||
server. The session is always destroyed immediately after.
|
||
|
||
### Channel: `quic.session.receive.origin`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `origins` {string\[]} The list of origins the server is authoritative for.
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when the session receives an ORIGIN frame (RFC 9412) from
|
||
the peer.
|
||
|
||
### Channel: `quic.session.handshake`
|
||
|
||
<!-- YAML
|
||
added: v23.8.0
|
||
-->
|
||
|
||
* `session` {quic.QuicSession}
|
||
* `servername` {string}
|
||
* `protocol` {string}
|
||
* `cipher` {string}
|
||
* `cipherVersion` {string}
|
||
* `validationErrorReason` {string}
|
||
* `validationErrorCode` {number}
|
||
* `earlyDataAttempted` {boolean}
|
||
* `earlyDataAccepted` {boolean}
|
||
|
||
Published when the TLS handshake completes.
|
||
|
||
### Channel: `quic.session.goaway`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `session` {quic.QuicSession}
|
||
* `lastStreamId` {bigint} The highest stream ID the peer may have processed.
|
||
|
||
Published when the peer sends an HTTP/3 GOAWAY frame. Streams with IDs
|
||
above `lastStreamId` were not processed and can be retried on a new
|
||
connection. A `lastStreamId` of `-1n` indicates a shutdown notice without
|
||
a stream boundary.
|
||
|
||
### Channel: `quic.session.early.rejected`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when the server rejects 0-RTT early data. All streams that were
|
||
opened during the 0-RTT phase have been destroyed. Useful for diagnosing
|
||
latency regressions when 0-RTT is expected to succeed.
|
||
|
||
### Channel: `quic.stream.closed`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
* `error` {any} The error that caused the close, or `undefined` if clean.
|
||
* `stats` {quic.QuicStream.Stats} Final stream statistics.
|
||
|
||
Published when a stream is destroyed. The `stats` object is a snapshot
|
||
of the final statistics at the time of destruction.
|
||
|
||
### Channel: `quic.stream.headers`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
* `headers` {Object} The initial request or response headers.
|
||
|
||
Published when initial headers are received on a stream. For HTTP/3
|
||
server-side streams, this contains request pseudo-headers (`:method`,
|
||
`:path`, etc.). For client-side streams, this contains response headers
|
||
(`:status`, etc.).
|
||
|
||
### Channel: `quic.stream.trailers`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
* `trailers` {Object} The trailing headers.
|
||
|
||
Published when trailing headers are received on a stream.
|
||
|
||
### Channel: `quic.stream.info`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
* `headers` {Object} The informational headers.
|
||
|
||
Published when informational (1xx) headers are received on a stream
|
||
(e.g., 103 Early Hints).
|
||
|
||
### Channel: `quic.stream.reset`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
* `error` {any} The QUIC error associated with the reset.
|
||
|
||
Published when a stream receives a STOP\_SENDING or RESET\_STREAM frame
|
||
from the peer, indicating the peer has aborted the stream. This is a
|
||
key signal for diagnosing application-level issues such as cancelled
|
||
requests.
|
||
|
||
### Channel: `quic.stream.blocked`
|
||
|
||
<!-- YAML
|
||
added: v26.2.0
|
||
-->
|
||
|
||
* `stream` {quic.QuicStream}
|
||
* `session` {quic.QuicSession}
|
||
|
||
Published when a stream is flow-control blocked and cannot send data
|
||
until the peer increases the flow control window. Useful for diagnosing
|
||
throughput issues caused by flow control.
|
||
|
||
[Aborting a stream]: #aborting-a-stream
|
||
[Callback error handling]: #callback-error-handling
|
||
[JSON-SEQ]: https://www.rfc-editor.org/rfc/rfc7464
|
||
[NSS Key Log Format]: https://udn.realityripple.com/docs/Mozilla/Projects/NSS/Key_Log_Format
|
||
[Permission Model]: permissions.md#permission-model
|
||
[RFC 8879]: https://www.rfc-editor.org/rfc/rfc8879
|
||
[RFC 8999]: https://www.rfc-editor.org/rfc/rfc8999
|
||
[RFC 9000]: https://www.rfc-editor.org/rfc/rfc9000
|
||
[RFC 9000 Section 8.1]: https://www.rfc-editor.org/rfc/rfc9000#section-8.1
|
||
[RFC 9001]: https://www.rfc-editor.org/rfc/rfc9001
|
||
[RFC 9002]: https://www.rfc-editor.org/rfc/rfc9002
|
||
[RFC 9114]: https://www.rfc-editor.org/rfc/rfc9114
|
||
[RFC 9204]: https://www.rfc-editor.org/rfc/rfc9204
|
||
[RFC 9218]: https://www.rfc-editor.org/rfc/rfc9218
|
||
[RFC 9220]: https://www.rfc-editor.org/rfc/rfc9220
|
||
[RFC 9221]: https://www.rfc-editor.org/rfc/rfc9221
|
||
[RFC 9287]: https://www.rfc-editor.org/rfc/rfc9287
|
||
[RFC 9297]: https://www.rfc-editor.org/rfc/rfc9297
|
||
[RFC 9308]: https://www.rfc-editor.org/rfc/rfc9308
|
||
[RFC 9312]: https://www.rfc-editor.org/rfc/rfc9312
|
||
[RFC 9368]: https://www.rfc-editor.org/rfc/rfc9368
|
||
[RFC 9369]: https://www.rfc-editor.org/rfc/rfc9369
|
||
[RFC 9412]: https://www.rfc-editor.org/rfc/rfc9412
|
||
[RFC 9443]: https://www.rfc-editor.org/rfc/rfc9443
|
||
[`PerformanceEntry`]: perf_hooks.md#class-performanceentry
|
||
[`PerformanceObserver`]: perf_hooks.md#class-performanceobserver
|
||
[`QuicEndpoint`]: #class-quicendpoint
|
||
[`QuicError`]: #class-quicerror
|
||
[`application.enableConnectProtocol`]: #sessionoptionsapplication
|
||
[`application.enableDatagrams`]: #sessionoptionsapplication
|
||
[`application.qpackMaxDTableCapacity`]: #sessionoptionsapplication
|
||
[`crypto.X509Certificate`]: crypto.md#class-x509certificate
|
||
[`endpoint.busy`]: #endpointbusy
|
||
[`endpoint.maxConnectionsPerHost`]: #endpointmaxconnectionsperhost
|
||
[`endpoint.maxConnectionsTotal`]: #endpointmaxconnectionstotal
|
||
[`endpointOptions.blockListPolicy`]: #endpointoptionsblocklistpolicy
|
||
[`endpointOptions.blockList`]: #endpointoptionsblocklist
|
||
[`endpointOptions.immediateCloseBurst`]: #endpointoptionsimmediatecloseburst
|
||
[`endpointOptions.immediateCloseRate`]: #endpointoptionsimmediatecloserate
|
||
[`endpointOptions.retryBurst`]: #endpointoptionsretryburst
|
||
[`endpointOptions.retryRate`]: #endpointoptionsretryrate
|
||
[`endpointOptions.sessionCreationBurst`]: #endpointoptionssessioncreationburst
|
||
[`endpointOptions.sessionCreationRate`]: #endpointoptionssessioncreationrate
|
||
[`endpointOptions.statelessResetBurst`]: #endpointoptionsstatelessresetburst
|
||
[`endpointOptions.statelessResetRate`]: #endpointoptionsstatelessresetrate
|
||
[`endpointOptions.versionNegotiationBurst`]: #endpointoptionsversionnegotiationburst
|
||
[`endpointOptions.versionNegotiationRate`]: #endpointoptionsversionnegotiationrate
|
||
[`error.errorCode`]: #errorerrorcode
|
||
[`fs.promises.open(path, 'r')`]: fs.md#fspromisesopenpath-flags-mode
|
||
[`maxDatagramFrameSize`]: #transportparamsmaxdatagramframesize
|
||
[`net.BlockList`]: net.md#class-netblocklist
|
||
[`quic.connect()`]: #quicconnectaddress-options
|
||
[`quic.listen()`]: #quiclistenonsession-options
|
||
[`session.close()`]: #sessioncloseoptions
|
||
[`session.createBidirectionalStream()`]: #sessioncreatebidirectionalstreamoptions
|
||
[`session.createUnidirectionalStream()`]: #sessioncreateunidirectionalstreamoptions
|
||
[`session.destroy()`]: #sessiondestroyerror-options
|
||
[`session.maxPendingDatagrams`]: #sessionmaxpendingdatagrams
|
||
[`session.onapplication`]: #sessiononapplication
|
||
[`session.ondatagram`]: #sessionondatagram
|
||
[`session.ondatagramstatus`]: #sessionondatagramstatus
|
||
[`session.onearlyrejected`]: #sessiononearlyrejected
|
||
[`session.onerror`]: #sessiononerror
|
||
[`session.ongoaway`]: #sessionongoaway
|
||
[`session.onkeylog`]: #sessiononkeylog
|
||
[`session.onnewtoken`]: #sessiononnewtoken
|
||
[`session.onorigin`]: #sessiononorigin
|
||
[`session.onqlog`]: #sessiononqlog
|
||
[`session.onsessionticket`]: #sessiononsessionticket
|
||
[`session.onstream`]: #sessiononstream
|
||
[`session.sendDatagram()`]: #sessionsenddatagramdatagram-encoding
|
||
[`sessionOptions.cc`]: #sessionoptionscc
|
||
[`sessionOptions.ciphers`]: #sessionoptionsciphers
|
||
[`sessionOptions.datagramDropPolicy`]: #sessionoptionsdatagramdroppolicy
|
||
[`sessionOptions.groups`]: #sessionoptionsgroups
|
||
[`sessionOptions.keylog`]: #sessionoptionskeylog
|
||
[`sessionOptions.qlog`]: #sessionoptionsqlog
|
||
[`sessionOptions.sessionTicket`]: #sessionoptionssessionticket
|
||
[`sessionOptions.sni`]: #sessionoptionssni-server-only
|
||
[`sessionOptions.token`]: #sessionoptionstoken-client-only
|
||
[`stream.destroy()`]: #streamdestroyerror-options
|
||
[`stream.headers`]: #streamheaders
|
||
[`stream.onerror`]: #streamonerror
|
||
[`stream.onwanttrailers`]: #streamonwanttrailers
|
||
[`stream.pendingTrailers`]: #streampendingtrailers
|
||
[`stream.priority`]: #streampriority
|
||
[`stream.sendHeaders()`]: #streamsendheadersheaders-options
|
||
[`stream.sendInformationalHeaders()`]: #streamsendinformationalheadersheaders
|
||
[`stream.sendTrailers()`]: #streamsendtrailersheaders
|
||
[`stream.setBody()`]: #streamsetbodybody
|
||
[`stream.setPriority()`]: #streamsetpriorityoptions
|
||
[`stream.writer`]: #streamwriter
|
||
[`writer.fail()`]: #streamwriter
|
||
[`writer.fail(reason)`]: #streamwriter
|
||
[qlog]: https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-main-schema/
|
||
[qvis]: https://qvis.quictools.info/
|