Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,404 changes: 764 additions & 640 deletions doc/api/quic.md

Large diffs are not rendered by default.

874 changes: 874 additions & 0 deletions lib/internal/quic/http3.js

Large diffs are not rendered by default.

889 changes: 114 additions & 775 deletions lib/internal/quic/quic.js

Large diffs are not rendered by default.

73 changes: 28 additions & 45 deletions lib/internal/quic/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ const {
IDX_STATE_SESSION_HANDSHAKE_COMPLETED,
IDX_STATE_SESSION_HANDSHAKE_CONFIRMED,
IDX_STATE_SESSION_STREAM_OPEN_ALLOWED,
IDX_STATE_SESSION_PRIORITY_SUPPORTED,
IDX_STATE_SESSION_HEADERS_SUPPORTED,
IDX_STATE_SESSION_WRAPPED,
IDX_STATE_SESSION_APPLICATION_TYPE,
IDX_STATE_SESSION_IS_SERVER,
IDX_STATE_SESSION_HAS_APPLICATION,
IDX_STATE_SESSION_NO_ERROR_CODE,
IDX_STATE_SESSION_INTERNAL_ERROR_CODE,
IDX_STATE_SESSION_MAX_DATAGRAM_SIZE,
Expand Down Expand Up @@ -117,10 +116,9 @@ assert(IDX_STATE_SESSION_STATELESS_RESET !== undefined);
assert(IDX_STATE_SESSION_HANDSHAKE_COMPLETED !== undefined);
assert(IDX_STATE_SESSION_HANDSHAKE_CONFIRMED !== undefined);
assert(IDX_STATE_SESSION_STREAM_OPEN_ALLOWED !== undefined);
assert(IDX_STATE_SESSION_PRIORITY_SUPPORTED !== undefined);
assert(IDX_STATE_SESSION_HEADERS_SUPPORTED !== undefined);
assert(IDX_STATE_SESSION_HAS_APPLICATION !== undefined);
assert(IDX_STATE_SESSION_IS_SERVER !== undefined);
assert(IDX_STATE_SESSION_WRAPPED !== undefined);
assert(IDX_STATE_SESSION_APPLICATION_TYPE !== undefined);
assert(IDX_STATE_SESSION_NO_ERROR_CODE !== undefined);
assert(IDX_STATE_SESSION_INTERNAL_ERROR_CODE !== undefined);
assert(IDX_STATE_SESSION_MAX_DATAGRAM_SIZE !== undefined);
Expand Down Expand Up @@ -351,7 +349,6 @@ class QuicSessionState {
static #LISTENER_SESSION_TICKET = 1 << 3;
static #LISTENER_NEW_TOKEN = 1 << 4;
static #LISTENER_ORIGIN = 1 << 5;
static #LISTENER_APPLICATION = 1 << 6;

#getListenerFlag(flag) {
const handle = this.#handle;
Expand All @@ -370,13 +367,6 @@ class QuicSessionState {
val ? (current | flag) : (current & ~flag), kIsLittleEndian);
}

/** @type {boolean} */
get hasApplicationListener() {
return this.#getListenerFlag(QuicSessionState.#LISTENER_APPLICATION);
}
set hasApplicationListener(val) {
this.#setListenerFlag(QuicSessionState.#LISTENER_APPLICATION, val);
}

/** @type {boolean} */
get hasPathValidationListener() {
Expand Down Expand Up @@ -475,22 +465,15 @@ class QuicSessionState {
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_SESSION_STREAM_OPEN_ALLOWED) !== 0;
}

/** @type {boolean} */
get isPrioritySupported() {
const handle = this.#handle;
if (handle === undefined) return undefined;
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_SESSION_PRIORITY_SUPPORTED) !== 0;
}

/**
* Whether the negotiated application protocol supports headers.
* Returns 0 (unknown), 1 (supported), or 2 (not supported).
* @type {number}
* Whether a protocol application (vs the native raw-stream path) is
* installed on the session.
* @type {boolean}
*/
get headersSupported() {
get hasApplication() {
const handle = this.#handle;
if (handle === undefined) return undefined;
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_SESSION_HEADERS_SUPPORTED);
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_SESSION_HAS_APPLICATION) !== 0;
}

/** @type {boolean} */
Expand All @@ -500,17 +483,21 @@ class QuicSessionState {
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_SESSION_WRAPPED) !== 0;
}

/** @type {number} */
get applicationType() {
/**
* True for server (accepted) sessions, false for client (initiated)
* sessions. Fixed for the session's lifetime.
* @type {boolean}
*/
get isServer() {
const handle = this.#handle;
if (handle === undefined) return undefined;
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_SESSION_APPLICATION_TYPE);
return DataViewPrototypeGetUint8(handle, this.#offset + IDX_STATE_SESSION_IS_SERVER) !== 0;
}

/**
* The negotiated application protocol's "no error" code, populated
* by the C++ layer when the application is selected during ALPN
* negotiation. For raw QUIC this is `0n`; for HTTP/3 this is
* The installed application's "no error" code, populated
* by the C++ layer when the application is installed.
* For raw QUIC this is `0n`; for HTTP/3 this is
* `0x100n` (`H3_NO_ERROR`).
* @type {bigint}
*/
Expand All @@ -522,7 +509,7 @@ class QuicSessionState {
}

/**
* The negotiated application protocol's "internal error" code,
* The installed application's "internal error" code,
* populated by the C++ layer when the application is selected
* during ALPN negotiation. Used as the wire code for `RESET_STREAM`
* frames when a stream is aborted without a more specific code.
Expand Down Expand Up @@ -590,10 +577,9 @@ class QuicSessionState {
isHandshakeCompleted,
isHandshakeConfirmed,
isStreamOpenAllowed,
isPrioritySupported,
headersSupported,
hasApplication,
isWrapped,
applicationType,
isServer,
noErrorCode,
internalErrorCode,
maxDatagramSize,
Expand All @@ -615,10 +601,9 @@ class QuicSessionState {
isHandshakeCompleted,
isHandshakeConfirmed,
isStreamOpenAllowed,
isPrioritySupported,
headersSupported,
hasApplication,
isWrapped,
applicationType,
isServer,
noErrorCode: `${noErrorCode}`,
internalErrorCode: `${internalErrorCode}`,
maxDatagramSize: `${maxDatagramSize}`,
Expand Down Expand Up @@ -656,10 +641,9 @@ class QuicSessionState {
isHandshakeCompleted,
isHandshakeConfirmed,
isStreamOpenAllowed,
isPrioritySupported,
headersSupported,
hasApplication,
isWrapped,
applicationType,
isServer,
noErrorCode,
internalErrorCode,
maxDatagramSize,
Expand All @@ -681,10 +665,9 @@ class QuicSessionState {
isHandshakeCompleted,
isHandshakeConfirmed,
isStreamOpenAllowed,
isPrioritySupported,
headersSupported,
hasApplication,
isWrapped,
applicationType,
isServer,
noErrorCode,
internalErrorCode,
maxDatagramSize,
Expand Down
14 changes: 0 additions & 14 deletions lib/internal/quic/symbols.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,23 @@ const kDatagram = Symbol('kDatagram');
const kDatagramStatus = Symbol('kDatagramStatus');
const kEarlyDataRejected = Symbol('kEarlyDataRejected');
const kFinishClose = Symbol('kFinishClose');
const kGoaway = Symbol('kGoaway');
const kHandshake = Symbol('kHandshake');
const kHandshakeCompleted = Symbol('kHandshakeCompleted');
const kVerifyPeer = Symbol('kVerifyPeer');
const kHeaders = Symbol('kHeaders');
const kKeylog = Symbol('kKeylog');
const kListen = Symbol('kListen');
const kQlog = Symbol('kQlog');
const kNewSession = Symbol('kNewSession');
const kNewStream = Symbol('kNewStream');
const kNewToken = Symbol('kNewToken');
const kStreamCallbacks = Symbol('kStreamCallbacks');
const kOrigin = Symbol('kOrigin');
const kOwner = Symbol('kOwner');
const kPathValidation = Symbol('kPathValidation');
const kPrivateConstructor = Symbol('kPrivateConstructor');
const kRemoveSession = Symbol('kRemoveSession');
const kRemoveStream = Symbol('kRemoveStream');
const kReset = Symbol('kReset');
const kSendHeaders = Symbol('kSendHeaders');
const kSessionApplication = Symbol('kSessionApplication');
const kSessionTicket = Symbol('kSessionTicket');
const kStopSending = Symbol('kStopSending');
const kTrailers = Symbol('kTrailers');
const kVersionNegotiation = Symbol('kVersionNegotiation');

module.exports = {
Expand All @@ -70,32 +63,25 @@ module.exports = {
kDrain,
kEarlyDataRejected,
kFinishClose,
kGoaway,
kHandshake,
kHandshakeCompleted,
kVerifyPeer,
kHeaders,
kInspect,
kKeylog,
kKeyObjectHandle,
kListen,
kNewSession,
kNewStream,
kNewToken,
kStreamCallbacks,
kOrigin,
kOwner,
kQlog,
kPathValidation,
kPrivateConstructor,
kRemoveSession,
kRemoveStream,
kReset,
kSendHeaders,
kSessionApplication,
kSessionTicket,
kStopSending,
kTrailers,
kVersionNegotiation,
};

Expand Down
11 changes: 11 additions & 0 deletions lib/quic.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ const {
DEFAULT_GROUPS,
} = require('internal/quic/quic');

const {
connect: connectHttp3,
listen: listenHttp3,
Http3Session,
Http3Stream,
} = require('internal/quic/http3');

const cc = {
get RENO() { return CC_ALGO_RENO; },
get CUBIC() { return CC_ALGO_CUBIC; },
Expand All @@ -35,7 +42,11 @@ const constants = {
module.exports = {
connect,
listen,
connectHttp3,
listenHttp3,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need separate connect and listen variants for http3?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, no.

Those two APIs create a auto-H3 client/server with sensible H3 defaults that automatically wires up the h3 application. It's roughly the equivalent to the current auto-HTTP3 support. But with this PR you can now use new Http3Session(quicSession) to dynamically create an HTTP/3 session on top of QUIC instead, so a separate server/client API is no longer necessary.

It is mildly more fiddly this way: you have to manually create the H3 session every time, and we wouldn't offer a way to set up any HTTP/3 specific defaults (e.g. ALPN) for you. Given we now intend this only for low-level use cases that's fine though, it gives you more control en route anyway.

There is one practical limitation right now: you can't do server 0RTT with dynamic attach like this (because by the time session event fires on the server to attach, that's been partially processed already). That is fixable I think, it's just a separate step I was going to leave until later. I could investigate that further within this PR instead though, and then if there's a good solution then we could drop these with no practical downside.

listEndpoints,
Http3Session,
Http3Stream,
QuicEndpoint,
QuicError,
QuicSession,
Expand Down
3 changes: 2 additions & 1 deletion src/node_builtins.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ BuiltinLoader::BuiltinCategories BuiltinLoader::GetBuiltinCategories() const {
#endif // !HAVE_OPENSSL
#ifndef OPENSSL_NO_QUIC
"internal/quic/quic", "internal/quic/symbols", "internal/quic/stats",
"internal/quic/state",
"internal/quic/state", "internal/quic/diagnostics",
"internal/quic/http3",
#endif // !OPENSSL_NO_QUIC
#if HAVE_DTLS
"internal/dtls/dtls", "internal/dtls/symbols", "internal/dtls/stats",
Expand Down
Loading
Loading