node/lib/internal/dtls/stats.js
James M Snell f835cc42da src,lib: add dtls interop tests
PR-URL: https://github.com/nodejs/node/pull/63182
Fixes: https://github.com/nodejs/node/issues/61630
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
2026-05-21 14:13:07 -07:00

341 lines
8.8 KiB
JavaScript

'use strict';
// TODO(@jasnell) Temporarily ignoring c8 coverage for this file while tests
// are still being developed.
/* c8 ignore start */
const {
BigUint64Array,
JSONStringify,
} = primordials;
const {
getOptionValue,
} = require('internal/options');
if (!process.features.dtls || !getOptionValue('--experimental-dtls')) {
return;
}
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,
kPrivateConstructor,
} = require('internal/dtls/symbols');
// This file defines the helper objects for accessing statistics collected
// by DTLS endpoints and sessions. Each wraps a BigUint64Array backed by
// a shared ArrayBuffer that is updated by the C++ internals.
const {
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_SESSION_CREATED_AT,
IDX_STATS_SESSION_DESTROYED_AT,
IDX_STATS_SESSION_CLOSING_AT,
IDX_STATS_SESSION_HANDSHAKE_COMPLETED_AT,
IDX_STATS_SESSION_BYTES_RECEIVED,
IDX_STATS_SESSION_BYTES_SENT,
IDX_STATS_SESSION_MESSAGES_RECEIVED,
IDX_STATS_SESSION_MESSAGES_SENT,
IDX_STATS_SESSION_RETRANSMIT_COUNT,
} = internalBinding('dtls');
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_SESSION_CREATED_AT !== undefined);
assert(IDX_STATS_SESSION_DESTROYED_AT !== undefined);
assert(IDX_STATS_SESSION_CLOSING_AT !== undefined);
assert(IDX_STATS_SESSION_HANDSHAKE_COMPLETED_AT !== undefined);
assert(IDX_STATS_SESSION_BYTES_RECEIVED !== undefined);
assert(IDX_STATS_SESSION_BYTES_SENT !== undefined);
assert(IDX_STATS_SESSION_MESSAGES_RECEIVED !== undefined);
assert(IDX_STATS_SESSION_MESSAGES_SENT !== undefined);
assert(IDX_STATS_SESSION_RETRANSMIT_COUNT !== undefined);
class DTLSEndpointStats {
/** @type {BigUint64Array} */
#handle;
/** @type {boolean} */
#disconnected = false;
/**
* @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 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];
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
return {
__proto__: null,
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}`,
};
}
[inspect.custom](depth, options) {
if (depth < 0)
return this;
const opts = {
__proto__: null,
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `DTLSEndpointStats ${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,
}, opts)}`;
}
/**
* True if this stats object is still connected to the underlying
* stats source. If false, the stats are stale.
* @type {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 DTLSSessionStats {
/** @type {BigUint64Array} */
#handle;
/** @type {boolean} */
#disconnected = false;
/**
* @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 BigUint64Array(buffer);
}
/** @type {bigint} */
get createdAt() {
return this.#handle[IDX_STATS_SESSION_CREATED_AT];
}
/** @type {bigint} */
get destroyedAt() {
return this.#handle[IDX_STATS_SESSION_DESTROYED_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 bytesReceived() {
return this.#handle[IDX_STATS_SESSION_BYTES_RECEIVED];
}
/** @type {bigint} */
get bytesSent() {
return this.#handle[IDX_STATS_SESSION_BYTES_SENT];
}
/** @type {bigint} */
get messagesReceived() {
return this.#handle[IDX_STATS_SESSION_MESSAGES_RECEIVED];
}
/** @type {bigint} */
get messagesSent() {
return this.#handle[IDX_STATS_SESSION_MESSAGES_SENT];
}
/** @type {bigint} */
get retransmitCount() {
return this.#handle[IDX_STATS_SESSION_RETRANSMIT_COUNT];
}
toString() {
return JSONStringify(this.toJSON());
}
toJSON() {
return {
__proto__: null,
connected: this.isConnected,
createdAt: `${this.createdAt}`,
destroyedAt: `${this.destroyedAt}`,
closingAt: `${this.closingAt}`,
handshakeCompletedAt: `${this.handshakeCompletedAt}`,
bytesReceived: `${this.bytesReceived}`,
bytesSent: `${this.bytesSent}`,
messagesReceived: `${this.messagesReceived}`,
messagesSent: `${this.messagesSent}`,
retransmitCount: `${this.retransmitCount}`,
};
}
[inspect.custom](depth, options) {
if (depth < 0)
return this;
const opts = {
__proto__: null,
...options,
depth: options.depth == null ? null : options.depth - 1,
};
return `DTLSSessionStats ${inspect({
connected: this.isConnected,
createdAt: this.createdAt,
destroyedAt: this.destroyedAt,
closingAt: this.closingAt,
handshakeCompletedAt: this.handshakeCompletedAt,
bytesReceived: this.bytesReceived,
bytesSent: this.bytesSent,
messagesReceived: this.messagesReceived,
messagesSent: this.messagesSent,
retransmitCount: this.retransmitCount,
}, opts)}`;
}
/**
* True if this stats object is still connected to the underlying
* stats source. If false, the stats are stale.
* @type {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 = {
DTLSEndpointStats,
DTLSSessionStats,
};
/* c8 ignore stop */