Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

2211
lib/internal/quic/quic.js Normal file

File diff suppressed because it is too large Load Diff

606
lib/internal/quic/state.js Normal file
View File

@ -0,0 +1,606 @@
'use strict';
const {
ArrayBuffer,
DataView,
DataViewPrototypeGetBigInt64,
DataViewPrototypeGetBigUint64,
DataViewPrototypeGetUint8,
DataViewPrototypeSetUint8,
JSONStringify,
} = primordials;
const {
codes: {
ERR_ILLEGAL_CONSTRUCTOR,
ERR_INVALID_ARG_TYPE,
},
} = require('internal/errors');
const {
isArrayBuffer,
} = require('util/types');
const { inspect } = require('internal/util/inspect');
const assert = require('internal/assert');
const {
kFinishClose,
kInspect,
kPrivateConstructor,
kWantsHeaders,
kWantsTrailers,
} = require('internal/quic/symbols');
// This file defines the helper objects for accessing state for
// various QUIC objects. Each of these wraps a DataView.
// Some of the state properties are read only, others are mutable.
// An ArrayBuffer is shared with the C++ level to allow for more
// efficient communication of state across the C++/JS boundary.
// When the state object is no longer needed, it is closed to
// prevent further updates to the buffer.
const {
IDX_STATE_SESSION_PATH_VALIDATION,
IDX_STATE_SESSION_VERSION_NEGOTIATION,
IDX_STATE_SESSION_DATAGRAM,
IDX_STATE_SESSION_SESSION_TICKET,
IDX_STATE_SESSION_CLOSING,
IDX_STATE_SESSION_GRACEFUL_CLOSE,
IDX_STATE_SESSION_SILENT_CLOSE,
IDX_STATE_SESSION_STATELESS_RESET,
IDX_STATE_SESSION_HANDSHAKE_COMPLETED,
IDX_STATE_SESSION_HANDSHAKE_CONFIRMED,
IDX_STATE_SESSION_STREAM_OPEN_ALLOWED,
IDX_STATE_SESSION_PRIORITY_SUPPORTED,
IDX_STATE_SESSION_WRAPPED,
IDX_STATE_SESSION_LAST_DATAGRAM_ID,
IDX_STATE_ENDPOINT_BOUND,
IDX_STATE_ENDPOINT_RECEIVING,
IDX_STATE_ENDPOINT_LISTENING,
IDX_STATE_ENDPOINT_CLOSING,
IDX_STATE_ENDPOINT_BUSY,
IDX_STATE_ENDPOINT_PENDING_CALLBACKS,
IDX_STATE_STREAM_ID,
IDX_STATE_STREAM_PENDING,
IDX_STATE_STREAM_FIN_SENT,
IDX_STATE_STREAM_FIN_RECEIVED,
IDX_STATE_STREAM_READ_ENDED,
IDX_STATE_STREAM_WRITE_ENDED,
IDX_STATE_STREAM_PAUSED,
IDX_STATE_STREAM_RESET,
IDX_STATE_STREAM_HAS_OUTBOUND,
IDX_STATE_STREAM_HAS_READER,
IDX_STATE_STREAM_WANTS_BLOCK,
IDX_STATE_STREAM_WANTS_HEADERS,
IDX_STATE_STREAM_WANTS_RESET,
IDX_STATE_STREAM_WANTS_TRAILERS,
} = internalBinding('quic');
assert(IDX_STATE_SESSION_PATH_VALIDATION !== undefined);
assert(IDX_STATE_SESSION_VERSION_NEGOTIATION !== undefined);
assert(IDX_STATE_SESSION_DATAGRAM !== undefined);
assert(IDX_STATE_SESSION_SESSION_TICKET !== undefined);
assert(IDX_STATE_SESSION_CLOSING !== undefined);
assert(IDX_STATE_SESSION_GRACEFUL_CLOSE !== undefined);
assert(IDX_STATE_SESSION_SILENT_CLOSE !== undefined);
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_WRAPPED !== undefined);
assert(IDX_STATE_SESSION_LAST_DATAGRAM_ID !== undefined);
assert(IDX_STATE_ENDPOINT_BOUND !== undefined);
assert(IDX_STATE_ENDPOINT_RECEIVING !== undefined);
assert(IDX_STATE_ENDPOINT_LISTENING !== undefined);
assert(IDX_STATE_ENDPOINT_CLOSING !== undefined);
assert(IDX_STATE_ENDPOINT_BUSY !== undefined);
assert(IDX_STATE_ENDPOINT_PENDING_CALLBACKS !== undefined);
assert(IDX_STATE_STREAM_ID !== undefined);
assert(IDX_STATE_STREAM_PENDING !== undefined);
assert(IDX_STATE_STREAM_FIN_SENT !== undefined);
assert(IDX_STATE_STREAM_FIN_RECEIVED !== undefined);
assert(IDX_STATE_STREAM_READ_ENDED !== undefined);
assert(IDX_STATE_STREAM_WRITE_ENDED !== undefined);
assert(IDX_STATE_STREAM_PAUSED !== undefined);
assert(IDX_STATE_STREAM_RESET !== undefined);
assert(IDX_STATE_STREAM_HAS_OUTBOUND !== undefined);
assert(IDX_STATE_STREAM_HAS_READER !== undefined);
assert(IDX_STATE_STREAM_WANTS_BLOCK !== undefined);
assert(IDX_STATE_STREAM_WANTS_HEADERS !== undefined);
assert(IDX_STATE_STREAM_WANTS_RESET !== undefined);
assert(IDX_STATE_STREAM_WANTS_TRAILERS !== undefined);
class QuicEndpointState {
/** @type {DataView} */
#handle;
/**
* @param {symbol} privateSymbol
* @param {ArrayBuffer} buffer
*/
constructor(privateSymbol, buffer) {
if (privateSymbol !== kPrivateConstructor) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
if (!isArrayBuffer(buffer)) {
throw new ERR_INVALID_ARG_TYPE('buffer', ['ArrayBuffer'], buffer);
}
this.#handle = new DataView(buffer);
}
/** @type {boolean} */
get isBound() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_ENDPOINT_BOUND);
}
/** @type {boolean} */
get isReceiving() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_ENDPOINT_RECEIVING);
}
/** @type {boolean} */
get isListening() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_ENDPOINT_LISTENING);
}
/** @type {boolean} */
get isClosing() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_ENDPOINT_CLOSING);
}
/** @type {boolean} */
get isBusy() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_ENDPOINT_BUSY);
}
/**
* The number of underlying callbacks that are pending. If the session
* is closing, these are the number of callbacks that the session is
* waiting on before it can be closed.
* @type {bigint}
*/
get pendingCallbacks() {
if (this.#handle.byteLength === 0) return undefined;
return DataViewPrototypeGetBigUint64(this.#handle, IDX_STATE_ENDPOINT_PENDING_CALLBACKS);
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
if (this.#handle.byteLength === 0) return {};
return {
__proto__: null,
isBound: this.isBound,
isReceiving: this.isReceiving,
isListening: this.isListening,
isClosing: this.isClosing,
isBusy: this.isBusy,
pendingCallbacks: `${this.pendingCallbacks}`,
};
}
[kInspect](depth, options) {
if (depth < 0)
return this;
if (this.#handle.byteLength === 0) {
return 'QuicEndpointState { <Closed> }';
}
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `QuicEndpointState ${inspect({
isBound: this.isBound,
isReceiving: this.isReceiving,
isListening: this.isListening,
isClosing: this.isClosing,
isBusy: this.isBusy,
pendingCallbacks: this.pendingCallbacks,
}, opts)}`;
}
[kFinishClose]() {
// Snapshot the state into a new DataView since the underlying
// buffer will be destroyed.
if (this.#handle.byteLength === 0) return;
this.#handle = new DataView(new ArrayBuffer(0));
}
}
class QuicSessionState {
/** @type {DataView} */
#handle;
/**
* @param {symbol} privateSymbol
* @param {ArrayBuffer} buffer
*/
constructor(privateSymbol, buffer) {
if (privateSymbol !== kPrivateConstructor) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
if (!isArrayBuffer(buffer)) {
throw new ERR_INVALID_ARG_TYPE('buffer', ['ArrayBuffer'], buffer);
}
this.#handle = new DataView(buffer);
}
/** @type {boolean} */
get hasPathValidationListener() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_PATH_VALIDATION);
}
/** @type {boolean} */
set hasPathValidationListener(val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_SESSION_PATH_VALIDATION, val ? 1 : 0);
}
/** @type {boolean} */
get hasVersionNegotiationListener() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_VERSION_NEGOTIATION);
}
/** @type {boolean} */
set hasVersionNegotiationListener(val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_SESSION_VERSION_NEGOTIATION, val ? 1 : 0);
}
/** @type {boolean} */
get hasDatagramListener() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_DATAGRAM);
}
/** @type {boolean} */
set hasDatagramListener(val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_SESSION_DATAGRAM, val ? 1 : 0);
}
/** @type {boolean} */
get hasSessionTicketListener() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_SESSION_TICKET);
}
/** @type {boolean} */
set hasSessionTicketListener(val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_SESSION_SESSION_TICKET, val ? 1 : 0);
}
/** @type {boolean} */
get isClosing() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_CLOSING);
}
/** @type {boolean} */
get isGracefulClose() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_GRACEFUL_CLOSE);
}
/** @type {boolean} */
get isSilentClose() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_SILENT_CLOSE);
}
/** @type {boolean} */
get isStatelessReset() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_STATELESS_RESET);
}
/** @type {boolean} */
get isHandshakeCompleted() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_HANDSHAKE_COMPLETED);
}
/** @type {boolean} */
get isHandshakeConfirmed() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_HANDSHAKE_CONFIRMED);
}
/** @type {boolean} */
get isStreamOpenAllowed() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_STREAM_OPEN_ALLOWED);
}
/** @type {boolean} */
get isPrioritySupported() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_PRIORITY_SUPPORTED);
}
/** @type {boolean} */
get isWrapped() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_WRAPPED);
}
/** @type {bigint} */
get lastDatagramId() {
if (this.#handle.byteLength === 0) return undefined;
return DataViewPrototypeGetBigUint64(this.#handle, IDX_STATE_SESSION_LAST_DATAGRAM_ID);
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
if (this.#handle.byteLength === 0) return {};
return {
__proto__: null,
hasPathValidationListener: this.hasPathValidationListener,
hasVersionNegotiationListener: this.hasVersionNegotiationListener,
hasDatagramListener: this.hasDatagramListener,
hasSessionTicketListener: this.hasSessionTicketListener,
isClosing: this.isClosing,
isGracefulClose: this.isGracefulClose,
isSilentClose: this.isSilentClose,
isStatelessReset: this.isStatelessReset,
isDestroyed: this.isDestroyed,
isHandshakeCompleted: this.isHandshakeCompleted,
isHandshakeConfirmed: this.isHandshakeConfirmed,
isStreamOpenAllowed: this.isStreamOpenAllowed,
isPrioritySupported: this.isPrioritySupported,
isWrapped: this.isWrapped,
lastDatagramId: `${this.lastDatagramId}`,
};
}
[kInspect](depth, options) {
if (depth < 0)
return this;
if (this.#handle.byteLength === 0) {
return 'QuicSessionState { <Closed> }';
}
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `QuicSessionState ${inspect({
hasPathValidationListener: this.hasPathValidationListener,
hasVersionNegotiationListener: this.hasVersionNegotiationListener,
hasDatagramListener: this.hasDatagramListener,
hasSessionTicketListener: this.hasSessionTicketListener,
isClosing: this.isClosing,
isGracefulClose: this.isGracefulClose,
isSilentClose: this.isSilentClose,
isStatelessReset: this.isStatelessReset,
isDestroyed: this.isDestroyed,
isHandshakeCompleted: this.isHandshakeCompleted,
isHandshakeConfirmed: this.isHandshakeConfirmed,
isStreamOpenAllowed: this.isStreamOpenAllowed,
isPrioritySupported: this.isPrioritySupported,
isWrapped: this.isWrapped,
lastDatagramId: this.lastDatagramId,
}, opts)}`;
}
[kFinishClose]() {
// Snapshot the state into a new DataView since the underlying
// buffer will be destroyed.
if (this.#handle.byteLength === 0) return;
this.#handle = new DataView(new ArrayBuffer(0));
}
}
class QuicStreamState {
/** @type {DataView} */
#handle;
/**
* @param {symbol} privateSymbol
* @param {ArrayBuffer} buffer
*/
constructor(privateSymbol, buffer) {
if (privateSymbol !== kPrivateConstructor) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
if (!isArrayBuffer(buffer)) {
throw new ERR_INVALID_ARG_TYPE('buffer', ['ArrayBuffer'], buffer);
}
this.#handle = new DataView(buffer);
}
/** @type {bigint} */
get id() {
if (this.#handle.byteLength === 0) return undefined;
return DataViewPrototypeGetBigInt64(this.#handle, IDX_STATE_STREAM_ID);
}
/** @type {boolean} */
get pending() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_PENDING);
}
/** @type {boolean} */
get finSent() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_FIN_SENT);
}
/** @type {boolean} */
get finReceived() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_FIN_RECEIVED);
}
/** @type {boolean} */
get readEnded() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_READ_ENDED);
}
/** @type {boolean} */
get writeEnded() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_WRITE_ENDED);
}
/** @type {boolean} */
get paused() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_PAUSED);
}
/** @type {boolean} */
get reset() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_RESET);
}
/** @type {boolean} */
get hasOutbound() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_HAS_OUTBOUND);
}
/** @type {boolean} */
get hasReader() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_HAS_READER);
}
/** @type {boolean} */
get wantsBlock() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_WANTS_BLOCK);
}
/** @type {boolean} */
set wantsBlock(val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_STREAM_WANTS_BLOCK, val ? 1 : 0);
}
/** @type {boolean} */
get [kWantsHeaders]() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_WANTS_HEADERS);
}
/** @type {boolean} */
set [kWantsHeaders](val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_STREAM_WANTS_HEADERS, val ? 1 : 0);
}
/** @type {boolean} */
get wantsReset() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_WANTS_RESET);
}
/** @type {boolean} */
set wantsReset(val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_STREAM_WANTS_RESET, val ? 1 : 0);
}
/** @type {boolean} */
get [kWantsTrailers]() {
if (this.#handle.byteLength === 0) return undefined;
return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_STREAM_WANTS_TRAILERS);
}
/** @type {boolean} */
set [kWantsTrailers](val) {
if (this.#handle.byteLength === 0) return;
DataViewPrototypeSetUint8(this.#handle, IDX_STATE_STREAM_WANTS_TRAILERS, val ? 1 : 0);
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
if (this.#handle.byteLength === 0) return {};
return {
__proto__: null,
id: `${this.id}`,
pending: this.pending,
finSent: this.finSent,
finReceived: this.finReceived,
readEnded: this.readEnded,
writeEnded: this.writeEnded,
paused: this.paused,
reset: this.reset,
hasOutbound: this.hasOutbound,
hasReader: this.hasReader,
wantsBlock: this.wantsBlock,
wantsReset: this.wantsReset,
};
}
[kInspect](depth, options) {
if (depth < 0)
return this;
if (this.#handle.byteLength === 0) {
return 'QuicStreamState { <Closed> }';
}
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `QuicStreamState ${inspect({
id: this.id,
pending: this.pending,
finSent: this.finSent,
finReceived: this.finReceived,
readEnded: this.readEnded,
writeEnded: this.writeEnded,
paused: this.paused,
reset: this.reset,
hasOutbound: this.hasOutbound,
hasReader: this.hasReader,
wantsBlock: this.wantsBlock,
wantsReset: this.wantsReset,
}, opts)}`;
}
[kFinishClose]() {
// Snapshot the state into a new DataView since the underlying
// buffer will be destroyed.
if (this.#handle.byteLength === 0) return;
this.#handle = new DataView(new ArrayBuffer(0));
}
}
module.exports = {
QuicEndpointState,
QuicSessionState,
QuicStreamState,
};

675
lib/internal/quic/stats.js Normal file
View File

@ -0,0 +1,675 @@
'use strict';
const {
BigUint64Array,
JSONStringify,
} = primordials;
const {
isArrayBuffer,
} = require('util/types');
const {
codes: {
ERR_ILLEGAL_CONSTRUCTOR,
ERR_INVALID_ARG_TYPE,
},
} = require('internal/errors');
const { inspect } = require('internal/util/inspect');
const assert = require('internal/assert');
const {
kFinishClose,
kInspect,
kPrivateConstructor,
} = require('internal/quic/symbols');
// This file defines the helper objects for accessing statistics collected
// by various QUIC objects. Each of these wraps a BigUint64Array. Every
// stats object is read-only via the API, and the underlying buffer is
// only updated by the QUIC internals. When the stats object is no longer
// needed, it is closed to prevent further updates to the buffer.
const {
// All of the IDX_STATS_* constants are the index positions of the stats
// fields in the relevant BigUint64Array's that underlie the *Stats objects.
// These are not exposed to end users.
IDX_STATS_ENDPOINT_CREATED_AT,
IDX_STATS_ENDPOINT_DESTROYED_AT,
IDX_STATS_ENDPOINT_BYTES_RECEIVED,
IDX_STATS_ENDPOINT_BYTES_SENT,
IDX_STATS_ENDPOINT_PACKETS_RECEIVED,
IDX_STATS_ENDPOINT_PACKETS_SENT,
IDX_STATS_ENDPOINT_SERVER_SESSIONS,
IDX_STATS_ENDPOINT_CLIENT_SESSIONS,
IDX_STATS_ENDPOINT_SERVER_BUSY_COUNT,
IDX_STATS_ENDPOINT_RETRY_COUNT,
IDX_STATS_ENDPOINT_VERSION_NEGOTIATION_COUNT,
IDX_STATS_ENDPOINT_STATELESS_RESET_COUNT,
IDX_STATS_ENDPOINT_IMMEDIATE_CLOSE_COUNT,
IDX_STATS_SESSION_CREATED_AT,
IDX_STATS_SESSION_CLOSING_AT,
IDX_STATS_SESSION_HANDSHAKE_COMPLETED_AT,
IDX_STATS_SESSION_HANDSHAKE_CONFIRMED_AT,
IDX_STATS_SESSION_BYTES_RECEIVED,
IDX_STATS_SESSION_BYTES_SENT,
IDX_STATS_SESSION_BIDI_IN_STREAM_COUNT,
IDX_STATS_SESSION_BIDI_OUT_STREAM_COUNT,
IDX_STATS_SESSION_UNI_IN_STREAM_COUNT,
IDX_STATS_SESSION_UNI_OUT_STREAM_COUNT,
IDX_STATS_SESSION_MAX_BYTES_IN_FLIGHT,
IDX_STATS_SESSION_BYTES_IN_FLIGHT,
IDX_STATS_SESSION_BLOCK_COUNT,
IDX_STATS_SESSION_CWND,
IDX_STATS_SESSION_LATEST_RTT,
IDX_STATS_SESSION_MIN_RTT,
IDX_STATS_SESSION_RTTVAR,
IDX_STATS_SESSION_SMOOTHED_RTT,
IDX_STATS_SESSION_SSTHRESH,
IDX_STATS_SESSION_DATAGRAMS_RECEIVED,
IDX_STATS_SESSION_DATAGRAMS_SENT,
IDX_STATS_SESSION_DATAGRAMS_ACKNOWLEDGED,
IDX_STATS_SESSION_DATAGRAMS_LOST,
IDX_STATS_STREAM_CREATED_AT,
IDX_STATS_STREAM_OPENED_AT,
IDX_STATS_STREAM_RECEIVED_AT,
IDX_STATS_STREAM_ACKED_AT,
IDX_STATS_STREAM_DESTROYED_AT,
IDX_STATS_STREAM_BYTES_RECEIVED,
IDX_STATS_STREAM_BYTES_SENT,
IDX_STATS_STREAM_MAX_OFFSET,
IDX_STATS_STREAM_MAX_OFFSET_ACK,
IDX_STATS_STREAM_MAX_OFFSET_RECV,
IDX_STATS_STREAM_FINAL_SIZE,
} = internalBinding('quic');
assert(IDX_STATS_ENDPOINT_CREATED_AT !== undefined);
assert(IDX_STATS_ENDPOINT_DESTROYED_AT !== undefined);
assert(IDX_STATS_ENDPOINT_BYTES_RECEIVED !== undefined);
assert(IDX_STATS_ENDPOINT_BYTES_SENT !== undefined);
assert(IDX_STATS_ENDPOINT_PACKETS_RECEIVED !== undefined);
assert(IDX_STATS_ENDPOINT_PACKETS_SENT !== undefined);
assert(IDX_STATS_ENDPOINT_SERVER_SESSIONS !== undefined);
assert(IDX_STATS_ENDPOINT_CLIENT_SESSIONS !== undefined);
assert(IDX_STATS_ENDPOINT_SERVER_BUSY_COUNT !== undefined);
assert(IDX_STATS_ENDPOINT_RETRY_COUNT !== undefined);
assert(IDX_STATS_ENDPOINT_VERSION_NEGOTIATION_COUNT !== undefined);
assert(IDX_STATS_ENDPOINT_STATELESS_RESET_COUNT !== undefined);
assert(IDX_STATS_ENDPOINT_IMMEDIATE_CLOSE_COUNT !== undefined);
assert(IDX_STATS_SESSION_CREATED_AT !== undefined);
assert(IDX_STATS_SESSION_CLOSING_AT !== undefined);
assert(IDX_STATS_SESSION_HANDSHAKE_COMPLETED_AT !== undefined);
assert(IDX_STATS_SESSION_HANDSHAKE_CONFIRMED_AT !== undefined);
assert(IDX_STATS_SESSION_BYTES_RECEIVED !== undefined);
assert(IDX_STATS_SESSION_BYTES_SENT !== undefined);
assert(IDX_STATS_SESSION_BIDI_IN_STREAM_COUNT !== undefined);
assert(IDX_STATS_SESSION_BIDI_OUT_STREAM_COUNT !== undefined);
assert(IDX_STATS_SESSION_UNI_IN_STREAM_COUNT !== undefined);
assert(IDX_STATS_SESSION_UNI_OUT_STREAM_COUNT !== undefined);
assert(IDX_STATS_SESSION_MAX_BYTES_IN_FLIGHT !== undefined);
assert(IDX_STATS_SESSION_BYTES_IN_FLIGHT !== undefined);
assert(IDX_STATS_SESSION_BLOCK_COUNT !== undefined);
assert(IDX_STATS_SESSION_CWND !== undefined);
assert(IDX_STATS_SESSION_LATEST_RTT !== undefined);
assert(IDX_STATS_SESSION_MIN_RTT !== undefined);
assert(IDX_STATS_SESSION_RTTVAR !== undefined);
assert(IDX_STATS_SESSION_SMOOTHED_RTT !== undefined);
assert(IDX_STATS_SESSION_SSTHRESH !== undefined);
assert(IDX_STATS_SESSION_DATAGRAMS_RECEIVED !== undefined);
assert(IDX_STATS_SESSION_DATAGRAMS_SENT !== undefined);
assert(IDX_STATS_SESSION_DATAGRAMS_ACKNOWLEDGED !== undefined);
assert(IDX_STATS_SESSION_DATAGRAMS_LOST !== undefined);
assert(IDX_STATS_STREAM_CREATED_AT !== undefined);
assert(IDX_STATS_STREAM_OPENED_AT !== undefined);
assert(IDX_STATS_STREAM_RECEIVED_AT !== undefined);
assert(IDX_STATS_STREAM_ACKED_AT !== undefined);
assert(IDX_STATS_STREAM_DESTROYED_AT !== undefined);
assert(IDX_STATS_STREAM_BYTES_RECEIVED !== undefined);
assert(IDX_STATS_STREAM_BYTES_SENT !== undefined);
assert(IDX_STATS_STREAM_MAX_OFFSET !== undefined);
assert(IDX_STATS_STREAM_MAX_OFFSET_ACK !== undefined);
assert(IDX_STATS_STREAM_MAX_OFFSET_RECV !== undefined);
assert(IDX_STATS_STREAM_FINAL_SIZE !== undefined);
class QuicEndpointStats {
/** @type {BigUint64Array} */
#handle;
/** @type {boolean} */
#disconnected = false;
/**
* @param {symbol} privateSymbol
* @param {ArrayBuffer} buffer
*/
constructor(privateSymbol, buffer) {
// We use the kPrivateConstructor symbol to restrict the ability to
// create new instances of QuicEndpointStats to internal code.
if (privateSymbol !== kPrivateConstructor) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
if (!isArrayBuffer(buffer)) {
throw new ERR_INVALID_ARG_TYPE('buffer', ['ArrayBuffer'], buffer);
}
this.#handle = new BigUint64Array(buffer);
}
/** @type {bigint} */
get createdAt() {
return this.#handle[IDX_STATS_ENDPOINT_CREATED_AT];
}
/** @type {bigint} */
get destroyedAt() {
return this.#handle[IDX_STATS_ENDPOINT_DESTROYED_AT];
}
/** @type {bigint} */
get bytesReceived() {
return this.#handle[IDX_STATS_ENDPOINT_BYTES_RECEIVED];
}
/** @type {bigint} */
get bytesSent() {
return this.#handle[IDX_STATS_ENDPOINT_BYTES_SENT];
}
/** @type {bigint} */
get packetsReceived() {
return this.#handle[IDX_STATS_ENDPOINT_PACKETS_RECEIVED];
}
/** @type {bigint} */
get packetsSent() {
return this.#handle[IDX_STATS_ENDPOINT_PACKETS_SENT];
}
/** @type {bigint} */
get serverSessions() {
return this.#handle[IDX_STATS_ENDPOINT_SERVER_SESSIONS];
}
/** @type {bigint} */
get clientSessions() {
return this.#handle[IDX_STATS_ENDPOINT_CLIENT_SESSIONS];
}
/** @type {bigint} */
get serverBusyCount() {
return this.#handle[IDX_STATS_ENDPOINT_SERVER_BUSY_COUNT];
}
/** @type {bigint} */
get retryCount() {
return this.#handle[IDX_STATS_ENDPOINT_RETRY_COUNT];
}
/** @type {bigint} */
get versionNegotiationCount() {
return this.#handle[IDX_STATS_ENDPOINT_VERSION_NEGOTIATION_COUNT];
}
/** @type {bigint} */
get statelessResetCount() {
return this.#handle[IDX_STATS_ENDPOINT_STATELESS_RESET_COUNT];
}
/** @type {bigint} */
get immediateCloseCount() {
return this.#handle[IDX_STATS_ENDPOINT_IMMEDIATE_CLOSE_COUNT];
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
return {
__proto__: null,
connected: this.isConnected,
// We need to convert the values to strings because JSON does not
// support BigInts.
createdAt: `${this.createdAt}`,
destroyedAt: `${this.destroyedAt}`,
bytesReceived: `${this.bytesReceived}`,
bytesSent: `${this.bytesSent}`,
packetsReceived: `${this.packetsReceived}`,
packetsSent: `${this.packetsSent}`,
serverSessions: `${this.serverSessions}`,
clientSessions: `${this.clientSessions}`,
serverBusyCount: `${this.serverBusyCount}`,
retryCount: `${this.retryCount}`,
versionNegotiationCount: `${this.versionNegotiationCount}`,
statelessResetCount: `${this.statelessResetCount}`,
immediateCloseCount: `${this.immediateCloseCount}`,
};
}
[kInspect](depth, options) {
if (depth < 0)
return this;
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `QuicEndpointStats ${inspect({
connected: this.isConnected,
createdAt: this.createdAt,
destroyedAt: this.destroyedAt,
bytesReceived: this.bytesReceived,
bytesSent: this.bytesSent,
packetsReceived: this.packetsReceived,
packetsSent: this.packetsSent,
serverSessions: this.serverSessions,
clientSessions: this.clientSessions,
serverBusyCount: this.serverBusyCount,
retryCount: this.retryCount,
versionNegotiationCount: this.versionNegotiationCount,
statelessResetCount: this.statelessResetCount,
immediateCloseCount: this.immediateCloseCount,
}, opts)}`;
}
/**
* True if this QuicEndpointStats object is still connected to the underlying
* Endpoint stats source. If this returns false, then the stats object is
* no longer being updated and should be considered stale.
* @returns {boolean}
*/
get isConnected() {
return !this.#disconnected;
}
[kFinishClose]() {
// Snapshot the stats into a new BigUint64Array since the underlying
// buffer will be destroyed.
this.#handle = new BigUint64Array(this.#handle);
this.#disconnected = true;
}
}
class QuicSessionStats {
/** @type {BigUint64Array} */
#handle;
/** @type {boolean} */
#disconnected = false;
/**
* @param {symbol} privateSynbol
* @param {BigUint64Array} buffer
*/
constructor(privateSynbol, buffer) {
// We use the kPrivateConstructor symbol to restrict the ability to
// create new instances of QuicSessionStats to internal code.
if (privateSynbol !== kPrivateConstructor) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
if (!isArrayBuffer(buffer)) {
throw new ERR_INVALID_ARG_TYPE('buffer', ['ArrayBuffer'], buffer);
}
this.#handle = new BigUint64Array(buffer);
}
/** @type {bigint} */
get createdAt() {
return this.#handle[IDX_STATS_SESSION_CREATED_AT];
}
/** @type {bigint} */
get closingAt() {
return this.#handle[IDX_STATS_SESSION_CLOSING_AT];
}
/** @type {bigint} */
get handshakeCompletedAt() {
return this.#handle[IDX_STATS_SESSION_HANDSHAKE_COMPLETED_AT];
}
/** @type {bigint} */
get handshakeConfirmedAt() {
return this.#handle[IDX_STATS_SESSION_HANDSHAKE_CONFIRMED_AT];
}
/** @type {bigint} */
get bytesReceived() {
return this.#handle[IDX_STATS_SESSION_BYTES_RECEIVED];
}
/** @type {bigint} */
get bytesSent() {
return this.#handle[IDX_STATS_SESSION_BYTES_SENT];
}
/** @type {bigint} */
get bidiInStreamCount() {
return this.#handle[IDX_STATS_SESSION_BIDI_IN_STREAM_COUNT];
}
/** @type {bigint} */
get bidiOutStreamCount() {
return this.#handle[IDX_STATS_SESSION_BIDI_OUT_STREAM_COUNT];
}
/** @type {bigint} */
get uniInStreamCount() {
return this.#handle[IDX_STATS_SESSION_UNI_IN_STREAM_COUNT];
}
/** @type {bigint} */
get uniOutStreamCount() {
return this.#handle[IDX_STATS_SESSION_UNI_OUT_STREAM_COUNT];
}
/** @type {bigint} */
get maxBytesInFlights() {
return this.#handle[IDX_STATS_SESSION_MAX_BYTES_IN_FLIGHT];
}
/** @type {bigint} */
get bytesInFlight() {
return this.#handle[IDX_STATS_SESSION_BYTES_IN_FLIGHT];
}
/** @type {bigint} */
get blockCount() {
return this.#handle[IDX_STATS_SESSION_BLOCK_COUNT];
}
/** @type {bigint} */
get cwnd() {
return this.#handle[IDX_STATS_SESSION_CWND];
}
/** @type {bigint} */
get latestRtt() {
return this.#handle[IDX_STATS_SESSION_LATEST_RTT];
}
/** @type {bigint} */
get minRtt() {
return this.#handle[IDX_STATS_SESSION_MIN_RTT];
}
/** @type {bigint} */
get rttVar() {
return this.#handle[IDX_STATS_SESSION_RTTVAR];
}
/** @type {bigint} */
get smoothedRtt() {
return this.#handle[IDX_STATS_SESSION_SMOOTHED_RTT];
}
/** @type {bigint} */
get ssthresh() {
return this.#handle[IDX_STATS_SESSION_SSTHRESH];
}
/** @type {bigint} */
get datagramsReceived() {
return this.#handle[IDX_STATS_SESSION_DATAGRAMS_RECEIVED];
}
/** @type {bigint} */
get datagramsSent() {
return this.#handle[IDX_STATS_SESSION_DATAGRAMS_SENT];
}
/** @type {bigint} */
get datagramsAcknowledged() {
return this.#handle[IDX_STATS_SESSION_DATAGRAMS_ACKNOWLEDGED];
}
/** @type {bigint} */
get datagramsLost() {
return this.#handle[IDX_STATS_SESSION_DATAGRAMS_LOST];
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
return {
__proto__: null,
connected: this.isConnected,
// We need to convert the values to strings because JSON does not
// support BigInts.
createdAt: `${this.createdAt}`,
closingAt: `${this.closingAt}`,
destroyedAt: `${this.destroyedAt}`,
handshakeCompletedAt: `${this.handshakeCompletedAt}`,
handshakeConfirmedAt: `${this.handshakeConfirmedAt}`,
gracefulClosingAt: `${this.gracefulClosingAt}`,
bytesReceived: `${this.bytesReceived}`,
bytesSent: `${this.bytesSent}`,
bidiInStreamCount: `${this.bidiInStreamCount}`,
bidiOutStreamCount: `${this.bidiOutStreamCount}`,
uniInStreamCount: `${this.uniInStreamCount}`,
uniOutStreamCount: `${this.uniOutStreamCount}`,
maxBytesInFlights: `${this.maxBytesInFlights}`,
bytesInFlight: `${this.bytesInFlight}`,
blockCount: `${this.blockCount}`,
cwnd: `${this.cwnd}`,
latestRtt: `${this.latestRtt}`,
minRtt: `${this.minRtt}`,
rttVar: `${this.rttVar}`,
smoothedRtt: `${this.smoothedRtt}`,
ssthresh: `${this.ssthresh}`,
datagramsReceived: `${this.datagramsReceived}`,
datagramsSent: `${this.datagramsSent}`,
datagramsAcknowledged: `${this.datagramsAcknowledged}`,
datagramsLost: `${this.datagramsLost}`,
};
}
[kInspect](depth, options) {
if (depth < 0)
return this;
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `QuicSessionStats ${inspect({
connected: this.isConnected,
createdAt: this.createdAt,
closingAt: this.closingAt,
destroyedAt: this.destroyedAt,
handshakeCompletedAt: this.handshakeCompletedAt,
handshakeConfirmedAt: this.handshakeConfirmedAt,
gracefulClosingAt: this.gracefulClosingAt,
bytesReceived: this.bytesReceived,
bytesSent: this.bytesSent,
bidiInStreamCount: this.bidiInStreamCount,
bidiOutStreamCount: this.bidiOutStreamCount,
uniInStreamCount: this.uniInStreamCount,
uniOutStreamCount: this.uniOutStreamCount,
maxBytesInFlights: this.maxBytesInFlights,
bytesInFlight: this.bytesInFlight,
blockCount: this.blockCount,
cwnd: this.cwnd,
latestRtt: this.latestRtt,
minRtt: this.minRtt,
rttVar: this.rttVar,
smoothedRtt: this.smoothedRtt,
ssthresh: this.ssthresh,
datagramsReceived: this.datagramsReceived,
datagramsSent: this.datagramsSent,
datagramsAcknowledged: this.datagramsAcknowledged,
datagramsLost: this.datagramsLost,
}, opts)}`;
}
/**
* True if this QuicSessionStats object is still connected to the underlying
* Session stats source. If this returns false, then the stats object is
* no longer being updated and should be considered stale.
* @returns {boolean}
*/
get isConnected() {
return !this.#disconnected;
}
[kFinishClose]() {
// Snapshot the stats into a new BigUint64Array since the underlying
// buffer will be destroyed.
this.#handle = new BigUint64Array(this.#handle);
this.#disconnected = true;
}
}
class QuicStreamStats {
/** @type {BigUint64Array} */
#handle;
/** type {boolean} */
#disconnected = false;
/**
* @param {symbol} privateSymbol
* @param {ArrayBuffer} buffer
*/
constructor(privateSymbol, buffer) {
// We use the kPrivateConstructor symbol to restrict the ability to
// create new instances of QuicStreamStats to internal code.
if (privateSymbol !== kPrivateConstructor) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
if (!isArrayBuffer(buffer)) {
throw new ERR_INVALID_ARG_TYPE('buffer', ['ArrayBuffer'], buffer);
}
this.#handle = new BigUint64Array(buffer);
}
/** @type {bigint} */
get createdAt() {
return this.#handle[IDX_STATS_STREAM_CREATED_AT];
}
/** @type {bigint} */
get openedAt() {
return this.#handle[IDX_STATS_STREAM_OPENED_AT];
}
/** @type {bigint} */
get receivedAt() {
return this.#handle[IDX_STATS_STREAM_RECEIVED_AT];
}
/** @type {bigint} */
get ackedAt() {
return this.#handle[IDX_STATS_STREAM_ACKED_AT];
}
/** @type {bigint} */
get destroyedAt() {
return this.#handle[IDX_STATS_STREAM_DESTROYED_AT];
}
/** @type {bigint} */
get bytesReceived() {
return this.#handle[IDX_STATS_STREAM_BYTES_RECEIVED];
}
/** @type {bigint} */
get bytesSent() {
return this.#handle[IDX_STATS_STREAM_BYTES_SENT];
}
/** @type {bigint} */
get maxOffset() {
return this.#handle[IDX_STATS_STREAM_MAX_OFFSET];
}
/** @type {bigint} */
get maxOffsetAcknowledged() {
return this.#handle[IDX_STATS_STREAM_MAX_OFFSET_ACK];
}
/** @type {bigint} */
get maxOffsetReceived() {
return this.#handle[IDX_STATS_STREAM_MAX_OFFSET_RECV];
}
/** @type {bigint} */
get finalSize() {
return this.#handle[IDX_STATS_STREAM_FINAL_SIZE];
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
return {
__proto__: null,
connected: this.isConnected,
// We need to convert the values to strings because JSON does not
// support BigInts.
createdAt: `${this.createdAt}`,
openedAt: `${this.openedAt}`,
receivedAt: `${this.receivedAt}`,
ackedAt: `${this.ackedAt}`,
destroyedAt: `${this.destroyedAt}`,
bytesReceived: `${this.bytesReceived}`,
bytesSent: `${this.bytesSent}`,
maxOffset: `${this.maxOffset}`,
maxOffsetAcknowledged: `${this.maxOffsetAcknowledged}`,
maxOffsetReceived: `${this.maxOffsetReceived}`,
finalSize: `${this.finalSize}`,
};
}
[kInspect](depth, options) {
if (depth < 0)
return this;
const opts = {
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `StreamStats ${inspect({
connected: this.isConnected,
createdAt: this.createdAt,
openedAt: this.openedAt,
receivedAt: this.receivedAt,
ackedAt: this.ackedAt,
destroyedAt: this.destroyedAt,
bytesReceived: this.bytesReceived,
bytesSent: this.bytesSent,
maxOffset: this.maxOffset,
maxOffsetAcknowledged: this.maxOffsetAcknowledged,
maxOffsetReceived: this.maxOffsetReceived,
finalSize: this.finalSize,
}, opts)}`;
}
/**
* True if this QuicStreamStats object is still connected to the underlying
* Stream stats source. If this returns false, then the stats object is
* no longer being updated and should be considered stale.
* @returns {boolean}
*/
get isConnected() {
return !this.#disconnected;
}
[kFinishClose]() {
// Snapshot the stats into a new BigUint64Array since the underlying
// buffer will be destroyed.
this.#handle = new BigUint64Array(this.#handle);
this.#disconnected = true;
}
}
module.exports = {
QuicEndpointStats,
QuicSessionStats,
QuicStreamStats,
};

View File

@ -0,0 +1,76 @@
'use strict';
const {
Symbol,
} = primordials;
const {
customInspectSymbol: kInspect,
} = require('internal/util');
const {
kHandle: kKeyObjectHandle,
kKeyObject: kKeyObjectInner,
} = require('internal/crypto/util');
// Symbols used to hide various private properties and methods from the
// public API.
const kApplicationProvider = Symbol('kApplicationProvider');
const kBlocked = Symbol('kBlocked');
const kConnect = Symbol('kConnect');
const kDatagram = Symbol('kDatagram');
const kDatagramStatus = Symbol('kDatagramStatus');
const kFinishClose = Symbol('kFinishClose');
const kHandshake = Symbol('kHandshake');
const kHeaders = Symbol('kHeaders');
const kListen = Symbol('kListen');
const kNewSession = Symbol('kNewSession');
const kNewStream = Symbol('kNewStream');
const kOnHeaders = Symbol('kOnHeaders');
const kOnTrailers = Symbol('kOwnTrailers');
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 kSessionTicket = Symbol('kSessionTicket');
const kState = Symbol('kState');
const kTrailers = Symbol('kTrailers');
const kVersionNegotiation = Symbol('kVersionNegotiation');
const kWantsHeaders = Symbol('kWantsHeaders');
const kWantsTrailers = Symbol('kWantsTrailers');
module.exports = {
kApplicationProvider,
kBlocked,
kConnect,
kDatagram,
kDatagramStatus,
kFinishClose,
kHandshake,
kHeaders,
kInspect,
kKeyObjectHandle,
kKeyObjectInner,
kListen,
kNewSession,
kNewStream,
kOnHeaders,
kOnTrailers,
kOwner,
kPathValidation,
kPrivateConstructor,
kRemoveSession,
kRemoveStream,
kReset,
kSendHeaders,
kSessionTicket,
kState,
kTrailers,
kVersionNegotiation,
kWantsHeaders,
kWantsTrailers,
};