diff --git a/doc/api/quic.md b/doc/api/quic.md index 1e71654badfffb..6143d3995cfaa8 100644 --- a/doc/api/quic.md +++ b/doc/api/quic.md @@ -1327,17 +1327,17 @@ added: REPLACEME --> Emitted when the server busy state has been toggled using -`quicSocket.serverBusy = true | false`. The callback is invoked with a -single boolean argument indicating `true` if busy status is enabled, -`false` otherwise. This event is strictly informational. +`quicSocket.serverBusy = true | false`. The callback is invoked with no +arguments. Use the `quicsocket.serverBusy` property to determine the +current status. This event is strictly informational. ```js const { createQuicSocket } = require('net'); const socket = createQuicSocket(); -socket.on('busy', (busy) => { - if (busy) +socket.on('busy', () => { + if (socket.serverBusy) console.log('Server is busy'); else console.log('Server is not busy'); diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index a80fed262db1e2..9a1322a06db9d9 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -12,6 +12,7 @@ assertCrypto(); const { Array, BigInt64Array, + Boolean, Error, Map, Number, @@ -38,6 +39,7 @@ const { validateQuicEndpointOptions, validateCreateSecureContextOptions, validateQuicSocketConnectOptions, + QuicSocketSharedState, QuicSessionSharedState, QLogStream, } = require('internal/quic/util'); @@ -269,8 +271,8 @@ function onSocketClose(err) { // Called by the C++ internals when the server busy state of // the QuicSocket has been changed. -function onSocketServerBusy(on) { - this[owner_symbol][kServerBusy](on); +function onSocketServerBusy() { + this[owner_symbol][kServerBusy](); } // Called by the C++ internals when a new server QuicSession has been created. @@ -845,19 +847,17 @@ class QuicEndpoint { class QuicSocket extends EventEmitter { [kInternalState] = { alpn: undefined, - autoClose: undefined, client: undefined, defaultEncoding: undefined, endpoints: new Set(), highWaterMark: undefined, + listenPending: false, lookup: undefined, server: undefined, - serverBusy: false, - serverListening: false, serverSecureContext: undefined, sessions: new Set(), state: kSocketUnbound, - statelessResetEnabled: true, + sharedState: undefined, stats: undefined, }; @@ -865,11 +865,6 @@ class QuicSocket extends EventEmitter { const { endpoint, - // True if the QuicSocket should automatically enter a graceful shutdown - // if it is not listening as a server and the last QuicClientSession - // closes - autoClose, - // Default configuration for QuicClientSessions client, @@ -913,7 +908,6 @@ class QuicSocket extends EventEmitter { const state = this[kInternalState]; - state.autoClose = autoClose; state.client = client; state.lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); state.server = server; @@ -976,6 +970,10 @@ class QuicSocket extends EventEmitter { if (handle !== undefined) { handle[owner_symbol] = this; this[async_id_symbol] = handle.getAsyncId(); + this[kInternalState].sharedState = + new QuicSocketSharedState(handle.state); + } else { + this[kInternalState].sharedState = undefined; } } @@ -1081,16 +1079,13 @@ class QuicSocket extends EventEmitter { } // Called by the C++ internals to notify when server busy status is toggled. - [kServerBusy](on) { - this[kInternalState].serverBusy = on; - // In a nextTick because the event ends up being - // emitted synchronously when quicSocket.serverBusy - // is called. + [kServerBusy]() { + const busy = this.serverBusy; process.nextTick(() => { try { - this.emit('busy', on); + this.emit('busy', busy); } catch (error) { - this[kRejections](error, 'busy', on); + this[kRejections](error, 'busy', busy); } }); } @@ -1161,6 +1156,7 @@ class QuicSocket extends EventEmitter { // server and will emit session events whenever a new QuicServerSession // is created. const state = this[kInternalState]; + state.listenPending = false; this[kHandle].listen( state.serverSecureContext.context, address, @@ -1225,14 +1221,12 @@ class QuicSocket extends EventEmitter { // function. listen(options, callback) { const state = this[kInternalState]; - if (state.serverListening) - throw new ERR_QUICSOCKET_LISTENING(); - if (state.state === kSocketDestroyed || state.state === kSocketClosing) { throw new ERR_QUICSOCKET_DESTROYED('listen'); } - + if (this.listening || state.listenPending) + throw new ERR_QUICSOCKET_LISTENING(); if (typeof options === 'function') { callback = options; options = {}; @@ -1265,8 +1259,8 @@ class QuicSocket extends EventEmitter { state.highWaterMark = highWaterMark; state.defaultEncoding = defaultEncoding; - state.serverListening = true; state.alpn = alpn; + state.listenPending = true; // If the callback function is provided, it is registered as a // handler for the on('session') event and will be called whenever @@ -1403,10 +1397,8 @@ class QuicSocket extends EventEmitter { // listening for new QuicServerSession connections. // New initial connection packets for currently unknown // DCID's will be ignored. - if (this[kHandle]) { - this[kHandle].stopListening(); - } - state.serverListening = false; + if (this[kHandle]) + this[kInternalState].sharedState.serverListening = false; // If there are no sessions, calling maybeDestroy // will immediately and synchronously destroy the @@ -1502,7 +1494,7 @@ class QuicSocket extends EventEmitter { // True if listen() has been called successfully get listening() { - return this[kInternalState].serverListening; + return Boolean(this[kInternalState].sharedState?.serverListening); } // True if the QuicSocket is currently waiting on at least one @@ -1518,12 +1510,27 @@ class QuicSocket extends EventEmitter { if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('serverBusy'); validateBoolean(on, 'on'); - if (state.serverBusy !== on) - this[kHandle].setServerBusy(on); + if (state.sharedState.serverBusy !== on) { + state.sharedState.serverBusy = on; + this[kServerBusy](); + } } get serverBusy() { - return this[kInternalState].serverBusy; + return Boolean(this[kInternalState].sharedState?.serverBusy); + } + + set statelessResetDisabled(on) { + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('statelessResetDisabled'); + validateBoolean(on, 'on'); + if (state.sharedState.statelessResetDisabled !== on) + state.sharedState.statelessResetDisabled = on; + } + + get statelessResetDisabled() { + return Boolean(this[kInternalState].sharedState?.statelessResetDisabled); } get duration() { @@ -1613,21 +1620,6 @@ class QuicSocket extends EventEmitter { } this[kHandle].setDiagnosticPacketLoss(rx, tx); } - - get statelessResetEnabled() { - return this[kInternalState].statelessResetEnabled; - } - - set statelessResetEnabled(on) { - const state = this[kInternalState]; - if (state.state === kSocketDestroyed) - throw new ERR_QUICSOCKET_DESTROYED('serverBusy'); - validateBoolean(on, 'on'); - if (state.statelessResetEnabled !== on) { - this[kHandle].enableStatelessReset(on); - state.statelessResetEnabled = on; - } - } } class QuicSession extends EventEmitter { diff --git a/lib/internal/quic/util.js b/lib/internal/quic/util.js index 143ee4dd8cef80..a7763c9bf3c15a 100644 --- a/lib/internal/quic/util.js +++ b/lib/internal/quic/util.js @@ -82,6 +82,10 @@ const { IDX_QUICSESSION_STATE_MAX_DATA_LEFT, IDX_QUICSESSION_STATE_BYTES_IN_FLIGHT, + IDX_QUICSOCKET_STATE_SERVER_LISTENING, + IDX_QUICSOCKET_STATE_SERVER_BUSY, + IDX_QUICSOCKET_STATE_STATELESS_RESET_DISABLED, + IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, IDX_HTTP3_QPACK_BLOCKED_STREAMS, IDX_HTTP3_MAX_HEADER_LIST_SIZE, @@ -514,7 +518,6 @@ function validateQuicSocketOptions(options = {}) { validateObject(options, 'options'); const { - autoClose = false, client = {}, disableStatelessReset = false, endpoint = { port: 0, type: 'udp4' }, @@ -538,7 +541,6 @@ function validateQuicSocketOptions(options = {}) { validateLookup(lookup); validateBoolean(validateAddress, 'options.validateAddress'); validateBoolean(validateAddressLRU, 'options.validateAddressLRU'); - validateBoolean(autoClose, 'options.autoClose'); validateBoolean(qlog, 'options.qlog'); validateBoolean(disableStatelessReset, 'options.disableStatelessReset'); @@ -576,7 +578,6 @@ function validateQuicSocketOptions(options = {}) { return { endpoint, - autoClose, client, lookup, maxConnections, @@ -803,6 +804,39 @@ function toggleListeners(state, event, on) { } } +class QuicSocketSharedState { + constructor(state) { + this[kHandle] = Buffer.from(state); + } + + get serverListening() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSOCKET_STATE_SERVER_LISTENING)); + } + + set serverListening(on) { + this[kHandle].writeUInt8(on ? 1 : 0, IDX_QUICSOCKET_STATE_SERVER_LISTENING); + } + + get serverBusy() { + return Boolean(this[kHandle].readUInt8(IDX_QUICSOCKET_STATE_SERVER_BUSY)); + } + + set serverBusy(on) { + this[kHandle].writeUInt8(on ? 1 : 0, IDX_QUICSOCKET_STATE_SERVER_BUSY); + } + + get statelessResetDisabled() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSOCKET_STATE_STATELESS_RESET_DISABLED)); + } + + set statelessResetDisabled(on) { + this[kHandle].writeUInt8(on ? 1 : 0, + IDX_QUICSOCKET_STATE_STATELESS_RESET_DISABLED); + } +} + // A utility class used to handle reading / modifying shared JS/C++ // state associated with a QuicSession class QuicSessionSharedState { @@ -938,6 +972,7 @@ module.exports = { validateQuicEndpointOptions, validateCreateSecureContextOptions, validateQuicSocketConnectOptions, + QuicSocketSharedState, QuicSessionSharedState, QLogStream, }; diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc index a651c2f951ffdf..a7baf1aa6df559 100644 --- a/src/quic/node_quic.cc +++ b/src/quic/node_quic.cc @@ -205,6 +205,11 @@ void Initialize(Local target, QUICSESSION_SHARED_STATE(V) #undef V +#define V(id, _, __) \ + NODE_DEFINE_CONSTANT(constants, IDX_QUICSOCKET_STATE_##id); + QUICSOCKET_SHARED_STATE(V) +#undef V + #define V(name, _, __) \ NODE_DEFINE_CONSTANT(constants, IDX_QUIC_SESSION_STATS_##name); SESSION_STATS(V) diff --git a/src/quic/node_quic_socket-inl.h b/src/quic/node_quic_socket-inl.h index 2d269ef87fe248..4d334a19f5b665 100644 --- a/src/quic/node_quic_socket-inl.h +++ b/src/quic/node_quic_socket-inl.h @@ -90,20 +90,6 @@ void QuicSocket::DisassociateStatelessResetToken( token_map_.erase(token); } -// StopListening is called when the QuicSocket is no longer -// accepting new server connections. Typically, this is called -// when the QuicSocket enters a graceful closing state where -// existing sessions are allowed to close naturally but new -// sessions are rejected. -void QuicSocket::StopListening() { - if (is_server_listening()) { - Debug(this, "Stop listening"); - set_server_listening(false); - // It is important to not call ReceiveStop here as there - // is ongoing traffic being exchanged by the peers. - } -} - void QuicSocket::ReceiveStart() { for (const auto& endpoint : endpoints_) CHECK_EQ(endpoint->ReceiveStart(), 0); @@ -157,8 +143,8 @@ size_t QuicSocket::GetCurrentStatelessResetCounter(const SocketAddress& addr) { void QuicSocket::ServerBusy(bool on) { Debug(this, "Turning Server Busy Response %s", on ? "on" : "off"); - set_server_busy(on); - listener_->OnServerBusy(on); + state_->server_busy = on ? 1 : 0; + listener_->OnServerBusy(); } bool QuicSocket::is_diagnostic_packet_loss(double prob) const { @@ -173,11 +159,6 @@ void QuicSocket::set_diagnostic_packet_loss(double rx, double tx) { tx_loss_ = tx; } -bool QuicSocket::EnableStatelessReset(bool on) { - set_stateless_reset_disabled(!on); - return !is_stateless_reset_disabled(); -} - void QuicSocket::set_validated_address(const SocketAddress& addr) { if (has_option_validate_address_lru()) { // Remove the oldest item if we've hit the LRU limit @@ -215,7 +196,7 @@ void QuicSocket::AddEndpoint( if (preferred || endpoints_.empty()) preferred_endpoint_ = endpoint_; endpoints_.emplace_back(endpoint_); - if (is_server_listening()) + if (state_->server_listening) endpoint_->ReceiveStart(); } diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc index 49743ebf9a9d7e..8c6fc253dad6dc 100644 --- a/src/quic/node_quic_socket.cc +++ b/src/quic/node_quic_socket.cc @@ -1,4 +1,5 @@ #include "node_quic_socket-inl.h" // NOLINT(build/include) +#include "aliased_struct-inl.h" #include "allocated_buffer-inl.h" #include "async_wrap-inl.h" #include "debug_utils-inl.h" @@ -38,6 +39,7 @@ using v8::Local; using v8::Number; using v8::Object; using v8::ObjectTemplate; +using v8::PropertyAttribute; using v8::String; using v8::Value; @@ -115,9 +117,9 @@ void QuicSocketListener::OnSessionReady(BaseObjectPtr session) { previous_listener_->OnSessionReady(session); } -void QuicSocketListener::OnServerBusy(bool busy) { +void QuicSocketListener::OnServerBusy() { if (previous_listener_ != nullptr) - previous_listener_->OnServerBusy(busy); + previous_listener_->OnServerBusy(); } void QuicSocketListener::OnEndpointDone(QuicEndpoint* endpoint) { @@ -145,12 +147,12 @@ void JSQuicSocketListener::OnSessionReady(BaseObjectPtr session) { socket()->MakeCallback(env->quic_on_session_ready_function(), 1, &arg); } -void JSQuicSocketListener::OnServerBusy(bool busy) { +void JSQuicSocketListener::OnServerBusy() { Environment* env = socket()->env(); HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - Local arg = Boolean::New(env->isolate(), busy); - socket()->MakeCallback(env->quic_on_socket_server_busy_function(), 1, &arg); + socket()->MakeCallback( + env->quic_on_socket_server_busy_function(), 0, nullptr); } void JSQuicSocketListener::OnEndpointDone(QuicEndpoint* endpoint) { @@ -252,6 +254,7 @@ QuicSocket::QuicSocket( StatsBase(quic_state->env(), wrap), alloc_info_(MakeAllocator()), options_(options), + state_(quic_state->env()->isolate()), max_connections_(max_connections), max_connections_per_host_(max_connections_per_host), max_stateless_resets_per_host_(max_stateless_resets_per_host), @@ -266,8 +269,14 @@ QuicSocket::QuicSocket( EntropySource(token_secret_, kTokenSecretLen); + wrap->DefineOwnProperty( + env()->context(), + env()->state_string(), + state_.GetArrayBuffer(), + PropertyAttribute::ReadOnly).Check(); + if (disable_stateless_reset) - set_stateless_reset_disabled(); + state_->stateless_reset_disabled = 1; // Set the session reset secret to the one provided or random. // Note that a random secret is going to make it exceedingly @@ -316,13 +325,13 @@ void QuicSocket::Listen( const std::string& alpn, uint32_t options) { CHECK(sc); - CHECK(!is_server_listening()); + CHECK_NE(state_->server_listening, 1); Debug(this, "Starting to listen"); server_session_config_.Set(quic_state(), preferred_address); server_secure_context_ = sc; server_alpn_ = alpn; server_options_ = options; - set_server_listening(); + state_->server_listening = 1; RecordTimestamp(&QuicSocketStats::listen_at); ReceiveStart(); } @@ -391,7 +400,7 @@ bool QuicSocket::MaybeStatelessReset( const SocketAddress& local_addr, const SocketAddress& remote_addr, unsigned int flags) { - if (UNLIKELY(is_stateless_reset_disabled() || nread < 16)) + if (UNLIKELY(state_->stateless_reset_disabled || nread < 16)) return false; StatelessResetToken possible_token( data + nread - NGTCP2_STATELESS_RESET_TOKENLEN); @@ -620,7 +629,7 @@ bool QuicSocket::SendStatelessReset( const SocketAddress& local_addr, const SocketAddress& remote_addr, size_t source_len) { - if (UNLIKELY(is_stateless_reset_disabled())) + if (UNLIKELY(state_->stateless_reset_disabled)) return false; constexpr static size_t kRandlen = NGTCP2_MIN_STATELESS_RESET_RANDLEN * 5; constexpr static size_t kMinStatelessResetLen = 41; @@ -737,7 +746,7 @@ BaseObjectPtr QuicSocket::AcceptInitialPacket( QuicCID ocid; // If the QuicSocket is not listening, the paket will be ignored. - if (!is_server_listening()) { + if (!state_->server_listening) { Debug(this, "QuicSocket is not listening"); return {}; } @@ -762,7 +771,7 @@ BaseObjectPtr QuicSocket::AcceptInitialPacket( // Else, check to see if the number of connections total for this QuicSocket // has been exceeded. If the count has been exceeded, shutdown the connection // immediately after the initial keys are installed. - if (UNLIKELY(is_server_busy()) || + if (UNLIKELY(state_->server_busy == 1) || sessions_.size() >= max_connections_ || GetCurrentSocketAddressCounter(remote_addr) >= max_connections_per_host_) { @@ -1115,26 +1124,6 @@ void QuicSocketListen(const FunctionCallbackInfo& args) { options); } -void QuicSocketStopListening(const FunctionCallbackInfo& args) { - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); - socket->StopListening(); -} - -void QuicSocketSetServerBusy(const FunctionCallbackInfo& args) { - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); - CHECK_EQ(args.Length(), 1); - socket->ServerBusy(args[0]->IsTrue()); -} - -void QuicSocketEnableStatelessReset(const FunctionCallbackInfo& args) { - QuicSocket* socket; - ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); - CHECK_EQ(args.Length(), 1); - args.GetReturnValue().Set(socket->EnableStatelessReset(args[0]->IsTrue())); -} - void QuicEndpointWaitForPendingCallbacks( const FunctionCallbackInfo& args) { QuicEndpoint* endpoint; @@ -1191,15 +1180,6 @@ void QuicSocket::Initialize( env->SetProtoMethod(socket, "setDiagnosticPacketLoss", QuicSocketSetDiagnosticPacketLoss); - env->SetProtoMethod(socket, - "setServerBusy", - QuicSocketSetServerBusy); - env->SetProtoMethod(socket, - "stopListening", - QuicSocketStopListening); - env->SetProtoMethod(socket, - "enableStatelessReset", - QuicSocketEnableStatelessReset); socket->Inherit(HandleWrap::GetConstructorTemplate(env)); target->Set(context, class_name, socket->GetFunction(env->context()).ToLocalChecked()).FromJust(); diff --git a/src/quic/node_quic_socket.h b/src/quic/node_quic_socket.h index 1fe9a04da0584b..41092c344c8f16 100644 --- a/src/quic/node_quic_socket.h +++ b/src/quic/node_quic_socket.h @@ -3,6 +3,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include "aliased_struct.h" #include "base_object.h" #include "node.h" #include "node_crypto.h" @@ -47,12 +48,24 @@ enum QuicSocketOptions : uint32_t { }; #undef V -#define QUICSOCKET_FLAGS(V) \ - V(GRACEFUL_CLOSE, graceful_closing) \ - V(WAITING_FOR_CALLBACKS, waiting_for_callbacks) \ - V(SERVER_LISTENING, server_listening) \ - V(SERVER_BUSY, server_busy) \ - V(DISABLE_STATELESS_RESET, stateless_reset_disabled) +#define QUICSOCKET_SHARED_STATE(V) \ + V(SERVER_LISTENING, server_listening, uint8_t) \ + V(SERVER_BUSY, server_busy, uint8_t) \ + V(STATELESS_RESET_DISABLED, stateless_reset_disabled, uint8_t) + +#define V(_, name, type) type name; +struct QuicSocketState { + QUICSOCKET_SHARED_STATE(V) +}; +#undef V + +#define V(id, name, _) \ + IDX_QUICSOCKET_STATE_##id = offsetof(QuicSocketState, name), +enum QuicSocketStateFields { + QUICSOCKET_SHARED_STATE(V) + IDX_QUICSOCKET_STATE_END +}; +#undef V #define SOCKET_STATS(V) \ V(CREATED_AT, created_at, "Created At") \ @@ -98,7 +111,7 @@ class QuicSocketListener { virtual void OnError(ssize_t code); virtual void OnSessionReady(BaseObjectPtr session); - virtual void OnServerBusy(bool busy); + virtual void OnServerBusy(); virtual void OnEndpointDone(QuicEndpoint* endpoint); virtual void OnDestroy(); @@ -114,7 +127,7 @@ class JSQuicSocketListener final : public QuicSocketListener { public: void OnError(ssize_t code) override; void OnSessionReady(BaseObjectPtr session) override; - void OnServerBusy(bool busy) override; + void OnServerBusy() override; void OnEndpointDone(QuicEndpoint* endpoint) override; void OnDestroy() override; }; @@ -357,35 +370,19 @@ class QuicSocket : public AsyncWrap, std::unique_ptr packet, BaseObjectPtr session = BaseObjectPtr()); -#define V(id, name) \ - inline bool is_##name() const { \ - return flags_ & (1 << QUICSOCKET_FLAG_##id); } \ - inline void set_##name(bool on = true) { \ - if (on) \ - flags_ |= (1 << QUICSOCKET_FLAG_##id); \ - else \ - flags_ &= ~(1 << QUICSOCKET_FLAG_##id); \ - } - QUICSOCKET_FLAGS(V) -#undef V - #define V(id, name) \ bool has_option_##name() const { \ return options_ & (1 << QUICSOCKET_OPTIONS_##id); } QUICSOCKET_OPTIONS(V) #undef V + // Allows the server busy status to be enabled from C++. A notification + // will be sent to the JavaScript side informing that the status has + // changed. inline void ServerBusy(bool on); inline void set_diagnostic_packet_loss(double rx = 0.0, double tx = 0.0); - inline void StopListening(); - - // Toggles whether or not stateless reset is enabled or not. - // Returns true if stateless reset is enabled, false if it - // is not. - inline bool EnableStatelessReset(bool on = true); - BaseObjectPtr server_secure_context() const { return server_secure_context_; } @@ -513,13 +510,6 @@ class QuicSocket : public AsyncWrap, // and the current packet should be artificially considered lost. inline bool is_diagnostic_packet_loss(double prob) const; -#define V(id, _) QUICSOCKET_FLAG_##id, - enum QuicSocketFlags : uint32_t { - QUICSOCKET_FLAGS(V) - QUICSOCKET_FLAG_COUNT - }; -#undef V - ngtcp2_mem alloc_info_; std::vector> endpoints_; @@ -530,6 +520,8 @@ class QuicSocket : public AsyncWrap, uint32_t options_ = 0; uint32_t server_options_; + AliasedStruct state_; + size_t max_connections_ = DEFAULT_MAX_CONNECTIONS; size_t max_connections_per_host_ = DEFAULT_MAX_CONNECTIONS_PER_HOST; size_t current_ngtcp2_memory_ = 0; diff --git a/test/parallel/test-quic-errors-quicsocket-create.js b/test/parallel/test-quic-errors-quicsocket-create.js index 57d72e5e6dbd75..64c27a50f2701a 100644 --- a/test/parallel/test-quic-errors-quicsocket-create.js +++ b/test/parallel/test-quic-errors-quicsocket-create.js @@ -82,13 +82,6 @@ const { createQuicSocket } = require('net'); }); }); -// Test invalid QuicSocket autoClose argument option -[1, NaN, 1n, null, {}, []].forEach((autoClose) => { - assert.throws(() => createQuicSocket({ autoClose }), { - code: 'ERR_INVALID_ARG_TYPE' - }); -}); - // Test invalid QuicSocket qlog argument option [1, NaN, 1n, null, {}, []].forEach((qlog) => { assert.throws(() => createQuicSocket({ qlog }), {