diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index 75f07c8d7e5871..00907398b09b8d 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -39,6 +39,7 @@ const { validateQuicEndpointOptions, validateCreateSecureContextOptions, validateQuicSocketConnectOptions, + QuicSessionSharedState, } = require('internal/quic/util'); const util = require('util'); const assert = require('internal/assert'); @@ -131,12 +132,6 @@ const { AF_INET, AF_INET6, NGTCP2_DEFAULT_MAX_PKTLEN, - IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI, - IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI, - IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT, - IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED, - IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT, - IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT, IDX_QUIC_SESSION_STATS_CREATED_AT, IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT, IDX_QUIC_SESSION_STATS_BYTES_RECEIVED, @@ -605,11 +600,11 @@ function createSecureContext(options, init_cb) { } function onNewListener(event) { - toggleListeners(this[kHandle], event, true); + toggleListeners(this[kInternalState].state, event, true); } function onRemoveListener(event) { - toggleListeners(this[kHandle], event, false); + toggleListeners(this[kInternalState].state, event, false); } function getStats(obj, idx) { @@ -1651,6 +1646,7 @@ class QuicSession extends EventEmitter { handshakeContinuationHistogram: undefined, highWaterMark: undefined, defaultEncoding: undefined, + state: undefined, }; constructor(socket, options) { @@ -1693,6 +1689,7 @@ class QuicSession extends EventEmitter { this[kHandle] = handle; if (handle !== undefined) { handle[owner_symbol] = this; + state.state = new QuicSessionSharedState(handle.state); state.handshakeAckHistogram = new Histogram(handle.ack); state.handshakeContinuationHistogram = new Histogram(handle.rate); } else { @@ -1849,10 +1846,10 @@ class QuicSession extends EventEmitter { return false; } - // Closing allows any existing QuicStream's to complete - // normally but disallows any new QuicStreams from being - // opened. Calls to openStream() will fail, and new streams - // from the peer will be rejected/ignored. + // Closing allows any existing QuicStream's to gracefully + // complete while disallowing any new QuicStreams from being + // opened (in either direction). Calls to openStream() will + // fail, and new streams from the peer will be rejected/ignored. close(callback) { const state = this[kInternalState]; if (state.destroyed) @@ -1921,8 +1918,7 @@ class QuicSession extends EventEmitter { if (handle !== undefined) { // Copy the stats for use after destruction state.stats = new BigInt64Array(handle.stats); - state.idleTimeout = - Boolean(handle.state[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT]); + state.idleTimeout = this[kInternalState].state.idleTimeout; // Destroy the underlying QuicSession handle handle.destroy(state.closeCode, state.closeFamily); @@ -1950,8 +1946,8 @@ class QuicSession extends EventEmitter { let bidi = 0; let uni = 0; if (this[kHandle]) { - bidi = this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI]; - uni = this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI]; + bidi = this[kInternalState].state.maxStreamsBidi; + uni = this[kInternalState].state.maxStreamsUni; } return { bidi, uni }; } @@ -1961,15 +1957,15 @@ class QuicSession extends EventEmitter { } get maxDataLeft() { - return this[kHandle]?.state[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] || 0; + return this[kHandle] ? this[kInternalState].state.maxDataLeft : 0; } get bytesInFlight() { - return this[kHandle]?.state[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] || 0; + return this[kHandle] ? this[kInternalState].state.bytesInFlight : 0; } get blockCount() { - return this[kHandle]?.state[IDX_QUIC_SESSION_STATS_BLOCK_COUNT] || 0; + return this[kHandle]?.stats[IDX_QUIC_SESSION_STATS_BLOCK_COUNT] || 0; } get authenticated() { @@ -2003,8 +1999,9 @@ class QuicSession extends EventEmitter { } get handshakeConfirmed() { - return Boolean( - this[kHandle]?.state[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED]); + return this[kHandle] ? + this[kInternalState].state.handshakeConfirmed : + false; } get idleTimeout() { @@ -2449,14 +2446,16 @@ class QuicClientSession extends QuicSession { // Listeners may have been added before the handle was created. // Ensure that we toggle those listeners in the handle state. - if (this.listenerCount('keylog') > 0) - toggleListeners(handle, 'keylog', true); + const internalState = this[kInternalState]; + if (this.listenerCount('keylog') > 0) { + toggleListeners(internalState.state, 'keylog', true); + } if (this.listenerCount('pathValidation') > 0) - toggleListeners(handle, 'pathValidation', true); + toggleListeners(internalState.state, 'pathValidation', true); if (this.listenerCount('usePreferredAddress') > 0) - toggleListeners(handle, 'usePreferredAddress', true); + toggleListeners(internalState.state, 'usePreferredAddress', true); this[kMaybeReady](0x2); } diff --git a/lib/internal/quic/util.js b/lib/internal/quic/util.js index efeddee94b6e64..6bbe10959723a9 100644 --- a/lib/internal/quic/util.js +++ b/lib/internal/quic/util.js @@ -15,6 +15,12 @@ const { }, } = require('internal/errors'); +const { + kHandle, +} = require('internal/stream_base_commons'); + +const endianness = require('os').endianness(); + const assert = require('internal/assert'); assert(process.versions.ngtcp2 !== undefined); @@ -52,11 +58,19 @@ const { IDX_QUIC_SESSION_MAX_UDP_PAYLOAD_SIZE, IDX_QUIC_SESSION_CC_ALGO, IDX_QUIC_SESSION_CONFIG_COUNT, - IDX_QUIC_SESSION_STATE_CERT_ENABLED, - IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED, - IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED, - IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED, - IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, + + IDX_QUICSESSION_STATE_KEYLOG_ENABLED, + IDX_QUICSESSION_STATE_CLIENT_HELLO_ENABLED, + IDX_QUICSESSION_STATE_CERT_ENABLED, + IDX_QUICSESSION_STATE_PATH_VALIDATED_ENABLED, + IDX_QUICSESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, + IDX_QUICSESSION_STATE_HANDSHAKE_CONFIRMED, + IDX_QUICSESSION_STATE_IDLE_TIMEOUT, + IDX_QUICSESSION_STATE_MAX_STREAMS_BIDI, + IDX_QUICSESSION_STATE_MAX_STREAMS_UNI, + IDX_QUICSESSION_STATE_MAX_DATA_LEFT, + IDX_QUICSESSION_STATE_BYTES_IN_FLIGHT, + IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, IDX_HTTP3_QPACK_BLOCKED_STREAMS, IDX_HTTP3_MAX_HEADER_LIST_SIZE, @@ -756,29 +770,121 @@ function setTransportParams(config) { // communicate that a handler has been added for the optional events // so that the C++ internals know there is an actual listener. The event // will not be emitted if there is no handler. -function toggleListeners(handle, event, on) { - if (handle === undefined) +function toggleListeners(state, event, on) { + if (state === undefined) return; - const val = on ? 1 : 0; switch (event) { case 'keylog': - handle.state[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] = val; + state.keylogEnabled = on; break; case 'clientHello': - handle.state[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = val; + state.clientHelloEnabled = on; break; case 'pathValidation': - handle.state[IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED] = val; + state.pathValidatedEnabled = on; break; case 'OCSPRequest': - handle.state[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = val; + state.certEnabled = on; break; case 'usePreferredAddress': - handle.state[IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED] = on; + state.usePreferredAddressEnabled = on; break; } } +// A utility class used to handle reading / modifying shared JS/C++ +// state associated with a QuicSession +class QuicSessionSharedState { + constructor(state) { + this[kHandle] = Buffer.from(state); + } + + get keylogEnabled() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSESSION_STATE_KEYLOG_ENABLED)); + } + + set keylogEnabled(on) { + this[kHandle] + .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_KEYLOG_ENABLED); + } + + get clientHelloEnabled() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSESSION_STATE_CLIENT_HELLO_ENABLED)); + } + + set clientHelloEnabled(on) { + this[kHandle] + .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_CLIENT_HELLO_ENABLED); + } + + get certEnabled() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSESSION_STATE_CERT_ENABLED)); + } + + set certEnabled(on) { + this[kHandle] + .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_CERT_ENABLED); + } + + get pathValidatedEnabled() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSESSION_STATE_PATH_VALIDATED_ENABLED)); + } + + set pathValidatedEnabled(on) { + this[kHandle] + .writeUInt8(on ? 1 : 0, IDX_QUICSESSION_STATE_PATH_VALIDATED_ENABLED); + } + + get usePreferredAddressEnabled() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED)); + } + + set usePreferredAddressEnabled(on) { + this[kHandle] + .writeUInt8(on ? 1 : 0, + IDX_QUICSESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED); + } + + get handshakeConfirmed() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSESSION_STATE_HANDSHAKE_CONFIRMED)); + } + + get idleTimeout() { + return Boolean(this[kHandle] + .readUInt8(IDX_QUICSESSION_STATE_IDLE_TIMEOUT)); + } + + get maxStreamsBidi() { + return Number(endianness === 'BE' ? + this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_MAX_STREAMS_BIDI) : + this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_MAX_STREAMS_BIDI)); + } + + get maxStreamsUni() { + return Number(endianness === 'BE' ? + this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_MAX_STREAMS_UNI) : + this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_MAX_STREAMS_UNI)); + } + + get maxDataLeft() { + return Number(endianness === 'BE' ? + this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_MAX_DATA_LEFT) : + this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_MAX_DATA_LEFT)); + } + + get bytesInFlight() { + return Number(endianness === 'BE' ? + this[kHandle].readBigInt64BE(IDX_QUICSESSION_STATE_BYTES_IN_FLIGHT) : + this[kHandle].readBigInt64LE(IDX_QUICSESSION_STATE_BYTES_IN_FLIGHT)); + } +} + module.exports = { getAllowUnauthorized, getSocketType, @@ -796,4 +902,5 @@ module.exports = { validateQuicEndpointOptions, validateCreateSecureContextOptions, validateQuicSocketConnectOptions, + QuicSessionSharedState, }; diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc index ed147eb92b649f..49aba2e5db2870 100644 --- a/src/quic/node_quic.cc +++ b/src/quic/node_quic.cc @@ -173,17 +173,6 @@ void Initialize(Local target, V(IDX_QUIC_SESSION_MAX_ACK_DELAY) \ V(IDX_QUIC_SESSION_CC_ALGO) \ V(IDX_QUIC_SESSION_CONFIG_COUNT) \ - V(IDX_QUIC_SESSION_STATE_CERT_ENABLED) \ - V(IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED) \ - V(IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED) \ - V(IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED) \ - V(IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED) \ - V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI) \ - V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI) \ - V(IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT) \ - V(IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT) \ - V(IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED) \ - V(IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT) \ V(MAX_RETRYTOKEN_EXPIRATION) \ V(MIN_RETRYTOKEN_EXPIRATION) \ V(NGTCP2_APP_NOERROR) \ @@ -212,6 +201,11 @@ void Initialize(Local target, V(ERR_FAILED_TO_CREATE_SESSION) \ V(UV_EBADF) +#define V(id, _, __) \ + NODE_DEFINE_CONSTANT(constants, IDX_QUICSESSION_STATE_##id); + QUICSESSION_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_session-inl.h b/src/quic/node_quic_session-inl.h index 73db45ca56deb1..935c8d024d55c9 100644 --- a/src/quic/node_quic_session-inl.h +++ b/src/quic/node_quic_session-inl.h @@ -105,7 +105,7 @@ ngtcp2_crypto_level QuicCryptoContext::write_crypto_level() const { // to a keylog file that can be consumed by tools like Wireshark to intercept // and decrypt QUIC network traffic. void QuicCryptoContext::Keylog(const char* line) { - if (UNLIKELY(session_->state_[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] == 1)) + if (UNLIKELY(session_->state_->keylog_enabled)) session_->listener()->OnKeylog(line, strlen(line)); } @@ -117,7 +117,7 @@ void QuicCryptoContext::OnClientHelloDone() { [&]() { set_in_client_hello(false); }); // Disable the callback at this point so we don't loop continuously - session_->state_[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = 0; + session_->state_->client_hello_enabled = 0; } // Following a pause in the handshake for OCSP or client hello, we kickstart @@ -274,14 +274,12 @@ void QuicSession::ExtendMaxStreamsRemoteBidi(uint64_t max_streams) { void QuicSession::ExtendMaxStreamsUni(uint64_t max_streams) { Debug(this, "Setting max unidirectional streams to %" PRIu64, max_streams); - state_[IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI] = - static_cast(max_streams); + state_->max_streams_uni = max_streams; } void QuicSession::ExtendMaxStreamsBidi(uint64_t max_streams) { Debug(this, "Setting max bidirectional streams to %" PRIu64, max_streams); - state_[IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI] = - static_cast(max_streams); + state_->max_streams_bidi = max_streams; } // Extends the stream-level flow control by the given number of bytes. @@ -327,7 +325,7 @@ void QuicSession::HandshakeCompleted() { void QuicSession::HandshakeConfirmed() { Debug(this, "Handshake is confirmed"); RecordTimestamp(&QuicSessionStats::handshake_confirmed_at); - state_[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] = 1; + state_->handshake_confirmed = 1; } bool QuicSession::is_handshake_completed() const { @@ -346,7 +344,7 @@ void QuicSession::InitApplication() { // the peer. All existing streams are abandoned and closed. void QuicSession::OnIdleTimeout() { if (!is_destroyed()) { - state_[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT] = 1; + state_->idle_timeout = 1; Debug(this, "Idle timeout"); Close(QuicSessionListener::SESSION_CLOSE_FLAG_SILENT); } diff --git a/src/quic/node_quic_session.cc b/src/quic/node_quic_session.cc index 8263abc3cc1cab..ffc4f25bfcc9d8 100644 --- a/src/quic/node_quic_session.cc +++ b/src/quic/node_quic_session.cc @@ -1,5 +1,6 @@ #include "node_quic_session-inl.h" // NOLINT(build/include) #include "aliased_buffer.h" +#include "aliased_struct-inl.h" #include "allocated_buffer-inl.h" #include "debug_utils-inl.h" #include "env-inl.h" @@ -919,10 +920,8 @@ void QuicCryptoContext::EnableTrace() { // when the peer certificate is received, allowing additional tweaks // and verifications to be performed. int QuicCryptoContext::OnClientHello() { - if (LIKELY(session_->state_[ - IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] == 0)) { + if (LIKELY(session_->state_->client_hello_enabled == 0)) return 0; - } TLSCallbackScope callback_scope(this); @@ -952,7 +951,7 @@ int QuicCryptoContext::OnClientHello() { // function that must be called in order for the TLS handshake to // continue. int QuicCryptoContext::OnOCSP() { - if (LIKELY(session_->state_[IDX_QUIC_SESSION_STATE_CERT_ENABLED] == 0)) { + if (LIKELY(session_->state_->cert_enabled == 0)) { Debug(session(), "No OCSPRequest handler registered"); return 1; } @@ -991,7 +990,7 @@ void QuicCryptoContext::OnOCSPDone( [&]() { set_in_ocsp_request(false); }); // Disable the callback at this point so we don't loop continuously - session_->state_[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = 0; + session_->state_->cert_enabled = 0; if (context) { int err = crypto::UseSNIContext(ssl_, context); @@ -1446,7 +1445,7 @@ QuicSession::QuicSession( idle_(new Timer(socket->env(), [this]() { OnIdleTimeout(); })), retransmit_(new Timer(socket->env(), [this]() { MaybeTimeout(); })), dcid_(dcid), - state_(env()->isolate(), IDX_QUIC_SESSION_STATE_COUNT), + state_(env()->isolate()), quic_state_(socket->quic_state()) { PushListener(&default_listener_); set_connection_id_strategy(RandomConnectionIDStrategy); @@ -1459,13 +1458,10 @@ QuicSession::QuicSession( options)); application_.reset(SelectApplication(this)); - // TODO(@jasnell): For now, the following is a check rather than properly - // handled. Before this code moves out of experimental, this should be - // properly handled. wrap->DefineOwnProperty( env()->context(), env()->state_string(), - state_.GetJSArray(), + state_.GetArrayBuffer(), PropertyAttribute::ReadOnly).Check(); // TODO(@jasnell): memory accounting @@ -1814,7 +1810,7 @@ void QuicSession::PathValidation( // Only emit the callback if there is a handler for the pathValidation // event on the JavaScript QuicSession object. - if (UNLIKELY(state_[IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED] == 1)) { + if (UNLIKELY(state_->path_validated_enabled == 1)) { listener_->OnPathValidation( res, reinterpret_cast(path->local.addr), @@ -2190,14 +2186,12 @@ void QuicSession::IgnorePreferredAddressStrategy( void QuicSession::UsePreferredAddressStrategy( QuicSession* session, const PreferredAddress& preferred_address) { - static constexpr int idx = - IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED; int family = session->socket()->local_address().family(); if (preferred_address.Use(family)) { Debug(session, "Using server preferred address"); // Emit only if the QuicSession has a usePreferredAddress handler // on the JavaScript side. - if (UNLIKELY(session->state_[idx] == 1)) { + if (UNLIKELY(session->state_->use_preferred_address_enabled == 1)) { session->listener()->OnUsePreferredAddress(family, preferred_address); } } else { @@ -2550,7 +2544,6 @@ void QuicSession::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("idle", idle_); tracker->TrackField("retransmit", retransmit_); tracker->TrackField("streams", streams_); - tracker->TrackField("state", state_); tracker->TrackFieldWithSize("current_ngtcp2_memory", current_ngtcp2_memory_); tracker->TrackField("conn_closebuf", conn_closebuf_); tracker->TrackField("application", application_); @@ -2697,14 +2690,12 @@ void QuicSession::UpdateRecoveryStats() { void QuicSession::UpdateDataStats() { if (is_destroyed()) return; - state_[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] = - static_cast(ngtcp2_conn_get_max_data_left(connection())); + state_->max_data_left = ngtcp2_conn_get_max_data_left(connection()); ngtcp2_conn_stat stat; ngtcp2_conn_get_conn_stat(connection(), &stat); - state_[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] = - static_cast(stat.bytes_in_flight); + state_->bytes_in_flight = stat.bytes_in_flight; // The max_bytes_in_flight is a highwater mark that can be used // in performance analysis operations. if (stat.bytes_in_flight > GetStat(&QuicSessionStats::max_bytes_in_flight)) diff --git a/src/quic/node_quic_session.h b/src/quic/node_quic_session.h index c68291aed67858..acf433622b38d3 100644 --- a/src/quic/node_quic_session.h +++ b/src/quic/node_quic_session.h @@ -4,6 +4,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "aliased_buffer.h" +#include "aliased_struct.h" #include "async_wrap.h" #include "env.h" #include "handle_wrap.h" @@ -141,67 +142,32 @@ enum QuicClientSessionOptions : uint32_t { QUICCLIENTSESSION_OPTION_RESUME = 0x4 }; +#define QUICSESSION_SHARED_STATE(V) \ + V(KEYLOG_ENABLED, keylog_enabled, uint8_t) \ + V(CLIENT_HELLO_ENABLED, client_hello_enabled, uint8_t) \ + V(CERT_ENABLED, cert_enabled, uint8_t) \ + V(PATH_VALIDATED_ENABLED, path_validated_enabled, uint8_t) \ + V(USE_PREFERRED_ADDRESS_ENABLED, use_preferred_address_enabled, uint8_t) \ + V(HANDSHAKE_CONFIRMED, handshake_confirmed, uint8_t) \ + V(IDLE_TIMEOUT, idle_timeout, uint8_t) \ + V(MAX_STREAMS_BIDI, max_streams_bidi, uint64_t) \ + V(MAX_STREAMS_UNI, max_streams_uni, uint64_t) \ + V(MAX_DATA_LEFT, max_data_left, uint64_t) \ + V(BYTES_IN_FLIGHT, bytes_in_flight, uint64_t) + +#define V(_, name, type) type name; +struct QuicSessionState { + QUICSESSION_SHARED_STATE(V) +}; +#undef V -// The QuicSessionState enums are used with the QuicSession's -// private state_ array. This is exposed to JavaScript via an -// aliased buffer and is used to communicate various types of -// state efficiently across the native/JS boundary. -enum QuicSessionState : int { - // Communicates whether a 'keylog' event listener has been - // registered on the JavaScript QuicSession object. The - // value will be either 1 or 0. When set to 1, the native - // code will emit TLS keylog entries to the JavaScript - // side triggering the 'keylog' event once for each line. - IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED, - - // Communicates whether a 'clientHello' event listener has - // been registered on the JavaScript QuicServerSession. - // The value will be either 1 or 0. When set to 1, the - // native code will callout to the JavaScript side causing - // the 'clientHello' event to be emitted. This is only - // used on server QuicSession instances. - IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED, - - // Communicates whether a 'cert' event listener has been - // registered on the JavaScript QuicSession. The value will - // be either 1 or 0. When set to 1, the native code will - // callout to the JavaScript side causing the 'cert' event - // to be emitted. - IDX_QUIC_SESSION_STATE_CERT_ENABLED, - - // Communicates whether a 'pathValidation' event listener - // has been registered on the JavaScript QuicSession. The - // value will be either 1 or 0. When set to 1, the native - // code will callout to the JavaScript side causing the - // 'pathValidation' event to be emitted - IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED, - - // Communicates the current max cumulative number of - // bidi and uni streams that may be opened on the session - IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI, - IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI, - - // Communicates the current maxinum number of bytes that - // the local endpoint can send in this connection - // (updated immediately after processing sent/received packets) - IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT, - - // Communicates the current total number of bytes in flight - IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT, - - // Communicates whether a 'usePreferredAddress' event listener - // has been registered. - IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, - - IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED, - - // Communicates whether a session was closed due to idle timeout - IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT, - - // Just the number of session state enums for use when - // creating the AliasedBuffer. - IDX_QUIC_SESSION_STATE_COUNT +#define V(id, name, _) \ + IDX_QUICSESSION_STATE_##id = offsetof(QuicSessionState, name), +enum QuicSessionStateFields { + QUICSESSION_SHARED_STATE(V) + IDX_QUICSESSION_STATE_END }; +#undef V #define SESSION_STATS(V) \ V(CREATED_AT, created_at, "Created At") \ @@ -1163,9 +1129,9 @@ class QuicSession : public AsyncWrap, private: static void RandomConnectionIDStrategy( - QuicSession* session, - ngtcp2_cid* cid, - size_t cidlen); + QuicSession* session, + ngtcp2_cid* cid, + size_t cidlen); // Initialize the QuicSession as a server void InitServer( @@ -1219,8 +1185,8 @@ class QuicSession : public AsyncWrap, inline void HandshakeConfirmed(); void PathValidation( - const ngtcp2_path* path, - ngtcp2_path_validation_result res); + const ngtcp2_path* path, + ngtcp2_path_validation_result res); bool ReceivePacket(ngtcp2_path* path, const uint8_t* data, ssize_t nread); @@ -1484,7 +1450,7 @@ class QuicSession : public AsyncWrap, StreamsMap streams_; - AliasedFloat64Array state_; + AliasedStruct state_; struct RemoteTransportParamsDebug { QuicSession* session;