mirror of https://github.com/nodejs/node.git
676 lines
20 KiB
JavaScript
676 lines
20 KiB
JavaScript
'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,
|
|
};
|