From 7754d1b57642d9311b18059879411cf3469c440c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 17 Dec 2024 20:25:57 -0800 Subject: [PATCH] quic: make multiple fixes, cleanups and simplifications --- doc/api/cli.md | 9 + doc/api/quic.md | 667 +-- lib/internal/quic/quic.js | 830 ++-- lib/internal/quic/state.js | 32 +- lib/internal/quic/stats.js | 23 - lib/internal/quic/symbols.js | 40 +- lib/quic.js | 11 +- src/node_builtins.cc | 1 + src/quic/application.cc | 209 +- src/quic/application.h | 11 +- src/quic/bindingdata.h | 12 +- src/quic/defs.h | 9 + src/quic/endpoint.cc | 322 +- src/quic/endpoint.h | 51 +- src/quic/http3.cc | 134 +- src/quic/http3.h | 33 +- src/quic/session.cc | 3826 +++++++++-------- src/quic/session.h | 292 +- src/quic/sessionticket.cc | 5 +- src/quic/streams.cc | 40 +- src/quic/streams.h | 36 +- src/quic/tlscontext.cc | 43 +- src/quic/tlscontext.h | 7 +- src/quic/transportparams.cc | 24 +- src/quic/transportparams.h | 5 +- test/parallel/test-process-get-builtin.mjs | 2 + test/parallel/test-quic-handshake.js | 87 + ...-quic-internal-endpoint-listen-defaults.js | 32 +- .../test-quic-internal-endpoint-options.js | 18 +- ...test-quic-internal-endpoint-stats-state.js | 33 +- test/parallel/test-require-resolve.js | 2 + 31 files changed, 3612 insertions(+), 3234 deletions(-) create mode 100644 test/parallel/test-quic-handshake.js diff --git a/doc/api/cli.md b/doc/api/cli.md index 87eda0deb66b46..4290f5461c6538 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -966,6 +966,14 @@ If the ES module being `require()`'d contains top-level `await`, this flag allows Node.js to evaluate the module, try to locate the top-level awaits, and print their location to help users find them. +### `--experimental-quic` + + + +Enables the experimental `node:quic` built-in module. + ### `--experimental-require-module` -```mjs -import { QuicEndpoint } from 'node:quic'; +* `address` {string|net.SocketAddress} +* `options` {quic.SessionOptions} +* Returns: {quic.QuicSession} -const endpoint = new QuicEndpoint(); +Initiate a new client-side session using this endpoint. -// Server... -endpoint.listen((session) => { - session.onstream = (stream) => { - // Handle the stream.... - }; +```mjs +import { connect } from 'node:quic'; +import { Buffer } from 'node:buffer'; + +const enc = new TextEncoder(); +const alpn = 'foo'; +const client = endpoint.connect('123.123.123.123:8888', { alpn }); +client.openUnidirectionalStream({ + body: enc.encode('hello world'), }); +``` + +## `quic.listen(onsession,[options])` -// Client... -const client = endpoint.connect('123.123.123.123:8888'); -const stream = client.openBidirectionalStream(); + + +* `onsession` {quic.OnSessionCallback} +* `options` {quic.SessionOptions} + +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'; + +listen((session) => { + // ... handle the session +}); ``` +## 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])` -* `address` {string|net.SocketAddress} -* `options` {quic.SessionOptions} -* Returns: {quic.QuicSession} +* {boolean} -Initiate a new client-side session using this endpoint. +True if `endpoint.close()` has been called and closing the endpoint has not yet completed. +Read only. ### `endpoint.destroy([error])` @@ -130,10 +157,9 @@ added: REPLACEME --> * `error` {any} -* Returns: {Promise} Forcefully closes the endpoint by forcing all open sessions to be immediately -closed. Returns `endpoint.closed`. +closed. ### `endpoint.destroyed` @@ -145,49 +171,6 @@ added: REPLACEME True if `endpoint.destroy()` has been called. Read only. -### `endpoint.listen([onsession,][options])` - - - -* `onsession` {quic.OnSessionCallback} -* `options` {quic.SessionOptions} - -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. - -The `onsession` callback must be specified either here or by setting the `onsession` -property or an error will be thrown. - -### `endpoint.onsession` - -* {quic.OnSessionCallback} - -The callback function that is invoked when a new session is initiated by a remote peer. -Read/write. - -### `endpoint.sessions` - - - -* {Iterator} of {quic.QuicSession}. - -An iterator over all sessions associated with this endpoint. Read only. - -### `endpoint.state` - - - -* {quic.QuicEndpointState} - -The state associated with an active session. Read only. - ### `endpoint.stats` - -A view of the internal state of an endpoint. - -### `endpointState.isBound` - - - -* {boolean} True if the endpoint is bound to a local UDP port. - -### `endpointState.isReceiving` - - - -* {boolean} True if the endpoint is bound to a local UDP port and actively listening - for packets. - -### `endpointState.isListening` - - - -* {boolean} True if the endpoint is listening as a server. - -### `endpointState.isClosing` - - - -* {boolean} True if the endpoint is in the process of closing down. - -### `endpointState.isBusy` - - - -* {boolean} True if the endpoint has been marked busy. - -### `endpointState.pendingCallbacks` - - - -* {bigint} The total number of pending callbacks the endpoint is waiting on currently. - ## Class: `QuicEndpointStats` +A `QuicSession` represents the local side of a QUIC connection. + ### `session.close()` * `error` {any} -* Returns: {Promise} + +Immediately destroy the session. All streams will be destroys and the +session will be closed. ### `session.destroyed` @@ -416,6 +353,8 @@ added: REPLACEME * {boolean} +True if `session.destroy()` has been called. Read only. + ### `session.endpoint` * `options` {Object} - * `headers` {Object} + * `body` {ArrayBuffer | ArrayBufferView | Blob} * Returns: {quic.QuicStream} +Open a new bidirectional stream. If the `body` option is not specified, +the outgoing stream will be half-closed. + ### `session.openUnidirectionalStream([options])` * `options` {Object} - * `headers` {Object + * `body` {ArrayBuffer | ArrayBufferView | Blob} * Returns: {quic.QuicStream} +Open a new unidirectional stream. If the `body` option is not specified, +the outgoing stream will be closed. + ### `session.path` -* `datagram` {Uint8Array} +* `datagram` {string|ArrayBufferView} * Returns: {bigint} +Sends an unreliable datagram to the remote peer, returning the datagram ID. +If the datagram payload is specified as an `ArrayBufferView`, then ownership of +that view will be transfered to the underlying stream. + ### `session.state` +Initiate a key update for the session. + ### `session[Symbol.asyncDispose]()` +Calls `session.close()` and returns a promise that fulfills when the +session has closed. + ## Class: `QuicSessionState` - -* {boolean} - ### `sessionState.isHandshakeCompleted` - -* {bigint} - ### `sessionStats.handshakeCompletedAt` - -* {bigint} - ### `sessionStats.bytesReceived` - -* {bigint} - ### `sessionStats.maxBytesInFlights` * `error` {any} -* Returns: {Promise} + +Immediately and abruptly destroys the stream. ### `stream.destroyed` @@ -918,6 +865,8 @@ added: REPLACEME * {boolean} +True if `stream.destroy()` has been called. + ### `stream.direction` - -* {quic.OnHeadersCallback} +The callback to invoke when the stream is blocked. Read/write. ### `stream.onreset` @@ -958,13 +905,7 @@ added: REPLACEME * {quic.OnStreamErrorCallback} -### `stream.ontrailers` - - - -* {quic.OnTrailersCallback} +The callback to invoke when the stream is reset. Read/write. ### `stream.pull(callback)` @@ -974,13 +915,8 @@ added: REPLACEME * `callback` {quic.OnPullCallback} -### `stream.sendHeaders(headers)` - - - -* `headers` {Object} +Read received data from the stream. The callback will be invoked with the +next chunk of received data. ### `stream.session` @@ -990,6 +926,8 @@ added: REPLACEME * {quic.QuicSession} +The session that created this stream. Read only. + ### `stream.state` - -* {boolean} - ### `streamState.wantsReset` - -* {boolean} - ### `streamState.writeEnded` - -#### `applicationOptions.maxHeaderPairs` - - - -* {bigint|number} The maximum number of header pairs that can be received. - -#### `applicationOptions.maxHeaderLength` - - - -* {bigint|number} The maximum number of header bytes that can be received. - -#### `applicationOptions.maxFieldSectionSize` - - - -* {bigint|number} The maximum header field section size. - -#### `applicationOptions.qpackMaxDTableCapacity` - - - -* {bigint|number} The QPack maximum dynamic table capacity. - -#### `applicationOptions.qpackEncoderMaxDTableCapacity` - - - -* {bigint|number} The QPack encoder maximum dynamic table capacity. - -#### `applicationOptions.qpackBlockedStreams` - - - -* {bigint|number} The maximum number of QPack blocked streams. - -#### `applicationOptions.enableConnectProtocol` - - - -* {boolean} - -True to allow use of the `CONNECT` method when using HTTP/3. - -#### `applicationOptions.enableDatagrams` - - - -* {boolean} - -True to allow use of unreliable datagrams. - ### Type: `EndpointOptions` - -* {string|number} - -Specifies the congestion control algorithm that will be used by all sessions -using this endpoint. Must be set to one of: - -* `QuicEndpoint.CC_ALGO_RENO` -* `QuicEndpoint.CC_ALGP_RENO_STR` -* `QuicEndpoint.CC_ALGO_CUBIC` -* `QuicEndpoint.CC_ALGO_CUBIC_STR` -* `QuicEndpoint.CC_ALGO_BBR` -* `QuicEndpoint.CC_ALGO_BBR_STR` - -This is an advanced option that users typically won't have need to specify -unless. - -#### `endpointOptions.disableActiveMigration` - - - -* {boolean} - -When `true`, this option disables the ability for a session to migrate to a different -socket address. - -\*\* THIS OPTION IS AT RISK OF BEING DROPPED \*\* - -#### `endpointOptions.handshakeTimeout` - - - -* {bigint|number} - -Specifies the maximum number of milliseconds a TLS handshake is permitted to take -to complete before timing out. - #### `endpointOptions.ipv6Only` - -* {bigint|number} - -Specifies the maximum UDP packet payload size. - #### `endpointOptions.maxRetries` - -* {bigint|number} - -Specifies the maximum stream flow-control window size. - -#### `endpointOptions.maxWindow` - - - -* {bigint|number} - -Specifies the maxumum session flow-control window size. - -#### `endpointOptions.noUdpPayloadSizeShaping` - - - -* {boolean} - #### `endpointOptions.retryTokenExpiration` -* {bigint|number} +* {boolean} -Specifies the maximum number of unacknowledged packets a session should allow. +When `true`, requires that the endpoint validate peer addresses using retry packets +while establishing a new connection. -#### `endpointOptions.validateAddress` +### Type: `SessionOptions` -* {boolean} +#### `sessionOptions.alpn` -When `true`, requires that the endpoint validate peer addresses while establishing -a connection. + -### Type: `SessionOptions` +* {string} + +The ALPN protocol identifier. + +#### `sessionOptions.ca` -#### `sessionOptions.application` +* {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]} + +The CA certificates to use for sessions. + +#### `sessionOptions.cc` -* {quic.ApplicationOptions} +* {string} + +Specifies the congestion control algorithm that will be used +. Must be set to one of either `'reno'`, `'cubic'`, or `'bbr'`. -The application-level options to use for the session. +This is an advanced option that users typically won't have need to specify. -#### `sessionOptions.minVersion` +#### `sessionOptions.certs` -* {number} +* {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]} -The minimum QUIC version number to allow. This is an advanced option that users -typically won't have need to specify. +The TLS certificates to use for sessions. -#### `sessionOptions.preferredAddressPolicy` +#### `sessionOptions.ciphers` -* {string} One of `'use'`, `'ignore'`, or `'default'`. +* {string} -When the remote peer advertises a preferred address, this option specifies whether -to use it or ignore it. +The list of supported TLS 1.3 cipher algorithms. -#### `sessionOptions.qlog` +#### `sessionOptions.crl` -* {boolean} +* {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]} -True if qlog output should be enabled. +The CRL to use for sessions. -#### `sessionOptions.sessionTicket` +#### `sessionOptions.groups` -* {ArrayBufferView} A session ticket to use for 0RTT session resumption. +* {string} -#### `sessionOptions.tls` +The list of support TLS 1.3 cipher groups. + +#### `sessionOptions.keylog` -* {quic.TlsOptions} +* {boolean} -The TLS options to use for the session. +True to enable TLS keylogging output. -#### `sessionOptions.transportParams` +#### `sessionOptions.keys` -* {quic.TransportParams} +* {KeyObject|CryptoKey|KeyObject\[]|CryptoKey\[]} -The QUIC transport parameters to use for the session. +The TLS crypto keys to use for sessions. -#### `sessionOptions.version` +#### `sessionOptions.maxPayloadSize` -* {number} +* {bigint|number} -The QUIC version number to use. This is an advanced option that users typically -won't have need to specify. +Specifies the maximum UDP packet payload size. -### Type: `TlsOptions` +#### `sessionOptions.maxStreamWindow` -#### `tlsOptions.sni` +* {bigint|number} + +Specifies the maximum stream flow-control window size. + +#### `sessionOptions.maxWindow` -* {string} +* {bigint|number} -The peer server name to target. +Specifies the maxumum session flow-control window size. -#### `tlsOptions.alpn` +#### `sessionOptions.minVersion` -* {string} +* {number} -The ALPN protocol identifier. +The minimum QUIC version number to allow. This is an advanced option that users +typically won't have need to specify. -#### `tlsOptions.ciphers` +#### `sessionOptions.preferredAddressPolicy` -* {string} +* {string} One of `'use'`, `'ignore'`, or `'default'`. -The list of supported TLS 1.3 cipher algorithms. +When the remote peer advertises a preferred address, this option specifies whether +to use it or ignore it. -#### `tlsOptions.groups` +#### `sessionOptions.qlog` -* {string} +* {boolean} -The list of support TLS 1.3 cipher groups. +True if qlog output should be enabled. -#### `tlsOptions.keylog` +#### `sessionOptions.sessionTicket` -* {boolean} +* {ArrayBufferView} A session ticket to use for 0RTT session resumption. -True to enable TLS keylogging output. +#### `sessionOptions.handshakeTimeout` -#### `tlsOptions.verifyClient` + + +* {bigint|number} + +Specifies the maximum number of milliseconds a TLS handshake is permitted to take +to complete before timing out. + +#### `sessionOptions.sni` -* {boolean} +* {string} -True to require verification of TLS client certificate. +The peer server name to target. -#### `tlsOptions.tlsTrace` +#### `sessionOptions.tlsTrace` -* {boolean} +* {quic.TransportParams} -True to require private key verification. +The QUIC transport parameters to use for the session. -#### `tlsOptions.keys` +#### `sessionOptions.unacknowledgedPacketThreshold` -* {KeyObject|CryptoKey|KeyObject\[]|CryptoKey\[]} +* {bigint|number} -The TLS crypto keys to use for sessions. +Specifies the maximum number of unacknowledged packets a session should allow. -#### `tlsOptions.certs` +#### `sessionOptions.verifyClient` -* {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]} +* {boolean} -The TLS certificates to use for sessions. +True to require verification of TLS client certificate. -#### `tlsOptions.ca` +#### `sessionOptions.verifyPrivateKey` -* {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]} +* {boolean} -The CA certificates to use for sessions. +True to require private key verification. -#### `tlsOptions.crl` +#### `sessionOptions.version` -* {ArrayBuffer|ArrayBufferView|ArrayBuffer\[]|ArrayBufferView\[]} +* {number} -The CRL to use for sessions. +The QUIC version number to use. This is an advanced option that users typically +won't have need to specify. ### Type: `TransportParams` @@ -1874,14 +1673,6 @@ added: REPLACEME * {bigint|number} -#### `transportParams.disableActiveMigration` - - - -* {boolean} - ## Callbacks ### Callback: `OnSessionCallback` @@ -1990,24 +1781,6 @@ added: REPLACEME * `this` {quic.QuicStream} * `error` {any} -### Callback: `OnHeadersCallback` - - - -* `this` {quic.QuicStream} -* `headers` {Object} -* `kind` {string} - -### Callback: `OnTrailersCallback` - - - -* `this` {quic.QuicStream} - ### Callback: `OnPullCallback`