Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/56328 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
607 lines
18 KiB
JavaScript
607 lines
18 KiB
JavaScript
'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,
|
|
};
|