| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 476926c commit abb881e
17 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1141,12 +1141,26 @@ added: v23.8.0 | |||
| 1141 | 1141 | #### `sessionOptions.alpn` | |
| 1142 | 1142 | ||
| 1143 | 1143 | <!-- YAML | |
| 1144 | - added: v23.8.0 | ||
| 1144 | + added: REPLACEME | ||
| 1145 | 1145 | --> | |
| 1146 | 1146 | ||
| 1147 | - * Type: {string} | ||
| 1147 | + * Type: {string} (client) | {string\[]} (server) | ||
| 1148 | + | ||
| 1149 | + The ALPN (Application-Layer Protocol Negotiation) identifier(s). | ||
| 1150 | + | ||
| 1151 | + For **client** sessions, this is a single string specifying the protocol | ||
| 1152 | + the client wants to use (e.g. `'h3'`). | ||
| 1153 | + | ||
| 1154 | + For **server** sessions, this is an array of protocol names in preference | ||
| 1155 | + order that the server supports (e.g. `['h3', 'h3-29']`). During the TLS | ||
| 1156 | + handshake, the server selects the first protocol from its list that the | ||
| 1157 | + client also supports. | ||
| 1158 | + | ||
| 1159 | + The negotiated ALPN determines which Application implementation is used | ||
| 1160 | + for the session. `'h3'` and `'h3-*'` variants select the HTTP/3 | ||
| 1161 | + application; all other values select the default application. | ||
| 1148 | 1162 | ||
| 1149 | - The ALPN protocol identifier. | ||
| 1163 | + Default: `'h3'` | ||
| 1150 | 1164 | ||
| 1151 | 1165 | #### `sessionOptions.ca` (client only) | |
| 1152 | 1166 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,7 +33,6 @@ let debug = require('internal/util/debuglog').debuglog('quic', (fn) => { | |||
| 33 | 33 | ||
| 34 | 34 | const { | |
| 35 | 35 | Endpoint: Endpoint_, | |
| 36 | - Http3Application: Http3, | ||
| 37 | 36 | setCallbacks, | |
| 38 | 37 | ||
| 39 | 38 | // The constants to be exposed to end users for various options. | |
@@ -115,7 +114,6 @@ const { | |||
| 115 | 114 | const kEmptyObject = { __proto__: null }; | |
| 116 | 115 | ||
| 117 | 116 | const { | |
| 118 | - kApplicationProvider, | ||
| 119 | 117 | kBlocked, | |
| 120 | 118 | kConnect, | |
| 121 | 119 | kDatagram, | |
@@ -1787,7 +1785,6 @@ class QuicEndpoint { | |||
| 1787 | 1785 | * @param {{replace?: boolean}} [options] | |
| 1788 | 1786 | */ | |
| 1789 | 1787 | setSNIContexts(entries, options = kEmptyObject) { | |
| 1790 | - QuicEndpoint.#assertIsQuicEndpoint(this); | ||
| 1791 | 1788 | if (this.#handle === undefined) { | |
| 1792 | 1789 | throw new ERR_INVALID_STATE('Endpoint is destroyed'); | |
| 1793 | 1790 | } | |
@@ -2053,7 +2050,7 @@ function processIdentityOptions(identity, label) { | |||
| 2053 | 2050 | function processTlsOptions(tls, forServer) { | |
| 2054 | 2051 | const { | |
| 2055 | 2052 | servername, | |
| 2056 | - protocol, | ||
| 2053 | + alpn, | ||
| 2057 | 2054 | ciphers = DEFAULT_CIPHERS, | |
| 2058 | 2055 | groups = DEFAULT_GROUPS, | |
| 2059 | 2056 | keylog = false, | |
@@ -2071,9 +2068,6 @@ function processTlsOptions(tls, forServer) { | |||
| 2071 | 2068 | if (servername !== undefined) { | |
| 2072 | 2069 | validateString(servername, 'options.servername'); | |
| 2073 | 2070 | } | |
| 2074 | - if (protocol !== undefined) { | ||
| 2075 | - validateString(protocol, 'options.protocol'); | ||
| 2076 | - } | ||
| 2077 | 2071 | if (ciphers !== undefined) { | |
| 2078 | 2072 | validateString(ciphers, 'options.ciphers'); | |
| 2079 | 2073 | } | |
@@ -2084,11 +2078,42 @@ function processTlsOptions(tls, forServer) { | |||
| 2084 | 2078 | validateBoolean(verifyClient, 'options.verifyClient'); | |
| 2085 | 2079 | validateBoolean(tlsTrace, 'options.tlsTrace'); | |
| 2086 | 2080 | ||
| 2081 | + // Encode the ALPN option to wire format (length-prefixed protocol names). | ||
| 2082 | + // Server: array of protocol names. Client: single protocol name. | ||
| 2083 | + // If not specified, the C++ default (h3) is used. | ||
| 2084 | + let encodedAlpn; | ||
| 2085 | + if (alpn !== undefined) { | ||
| 2086 | + const protocols = forServer ? | ||
| 2087 | + (ArrayIsArray(alpn) ? alpn : [alpn]) : | ||
| 2088 | + [alpn]; | ||
| 2089 | + if (!forServer) { | ||
| 2090 | + validateString(alpn, 'options.alpn'); | ||
| 2091 | + } | ||
| 2092 | + let totalLen = 0; | ||
| 2093 | + for (let i = 0; i < protocols.length; i++) { | ||
| 2094 | + validateString(protocols[i], `options.alpn[${i}]`); | ||
| 2095 | + if (protocols[i].length === 0 || protocols[i].length > 255) { | ||
| 2096 | + throw new ERR_INVALID_ARG_VALUE(`options.alpn[${i}]`, protocols[i], | ||
| 2097 | + 'must be between 1 and 255 characters'); | ||
| 2098 | + } | ||
| 2099 | + totalLen += 1 + protocols[i].length; | ||
| 2100 | + } | ||
| 2101 | + // Build wire format: [len1][name1][len2][name2]... | ||
| 2102 | + const buf = Buffer.allocUnsafe(totalLen); | ||
| 2103 | + let offset = 0; | ||
| 2104 | + for (let i = 0; i < protocols.length; i++) { | ||
| 2105 | + buf[offset++] = protocols[i].length; | ||
| 2106 | + buf.write(protocols[i], offset, 'ascii'); | ||
| 2107 | + offset += protocols[i].length; | ||
| 2108 | + } | ||
| 2109 | + encodedAlpn = buf.toString('latin1'); | ||
| 2110 | + } | ||
| 2111 | + | ||
| 2087 | 2112 | // Shared TLS options (same for all identities on the endpoint). | |
| 2088 | 2113 | const shared = { | |
| 2089 | 2114 | __proto__: null, | |
| 2090 | 2115 | servername, | |
| 2091 | - protocol, | ||
| 2116 | + alpn: encodedAlpn, | ||
| 2092 | 2117 | ciphers, | |
| 2093 | 2118 | groups, | |
| 2094 | 2119 | keylog, | |
@@ -2191,13 +2216,8 @@ function processSessionOptions(options, forServer = false) { | |||
| 2191 | 2216 | maxStreamWindow, | |
| 2192 | 2217 | maxWindow, | |
| 2193 | 2218 | cc, | |
| 2194 | - [kApplicationProvider]: provider, | ||
| 2195 | 2219 | } = options; | |
| 2196 | 2220 | ||
| 2197 | - if (provider !== undefined) { | ||
| 2198 | - validateObject(provider, 'options[kApplicationProvider]'); | ||
| 2199 | - } | ||
| 2200 | - | ||
| 2201 | 2221 | if (cc !== undefined) { | |
| 2202 | 2222 | validateString(cc, 'options.cc'); | |
| 2203 | 2223 | if (cc !== 'reno' || cc !== 'bbr' || cc !== 'cubic') { | |
@@ -2226,7 +2246,6 @@ function processSessionOptions(options, forServer = false) { | |||
| 2226 | 2246 | maxStreamWindow, | |
| 2227 | 2247 | maxWindow, | |
| 2228 | 2248 | sessionTicket, | |
| 2229 | - provider, | ||
| 2230 | 2249 | cc, | |
| 2231 | 2250 | }; | |
| 2232 | 2251 | } | |
@@ -2328,7 +2347,8 @@ module.exports = { | |||
| 2328 | 2347 | QuicEndpoint, | |
| 2329 | 2348 | QuicSession, | |
| 2330 | 2349 | QuicStream, | |
| 2331 | - Http3, | ||
| 2350 | + DEFAULT_CIPHERS, | ||
| 2351 | + DEFAULT_GROUPS, | ||
| 2332 | 2352 | }; | |
| 2333 | 2353 | ||
| 2334 | 2354 | ObjectDefineProperties(module.exports, { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,6 +62,7 @@ const { | |||
| 62 | 62 | IDX_STATE_SESSION_STREAM_OPEN_ALLOWED, | |
| 63 | 63 | IDX_STATE_SESSION_PRIORITY_SUPPORTED, | |
| 64 | 64 | IDX_STATE_SESSION_WRAPPED, | |
| 65 | + IDX_STATE_SESSION_APPLICATION_TYPE, | ||
| 65 | 66 | IDX_STATE_SESSION_LAST_DATAGRAM_ID, | |
| 66 | 67 | ||
| 67 | 68 | IDX_STATE_ENDPOINT_BOUND, | |
@@ -99,6 +100,7 @@ assert(IDX_STATE_SESSION_HANDSHAKE_CONFIRMED !== undefined); | |||
| 99 | 100 | assert(IDX_STATE_SESSION_STREAM_OPEN_ALLOWED !== undefined); | |
| 100 | 101 | assert(IDX_STATE_SESSION_PRIORITY_SUPPORTED !== undefined); | |
| 101 | 102 | assert(IDX_STATE_SESSION_WRAPPED !== undefined); | |
| 103 | + assert(IDX_STATE_SESSION_APPLICATION_TYPE !== undefined); | ||
| 102 | 104 | assert(IDX_STATE_SESSION_LAST_DATAGRAM_ID !== undefined); | |
| 103 | 105 | assert(IDX_STATE_ENDPOINT_BOUND !== undefined); | |
| 104 | 106 | assert(IDX_STATE_ENDPOINT_RECEIVING !== undefined); | |
@@ -347,6 +349,12 @@ class QuicSessionState { | |||
| 347 | 349 | return !!DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_WRAPPED); | |
| 348 | 350 | } | |
| 349 | 351 | ||
| 352 | + /** @type {number} */ | ||
| 353 | + get applicationType() { | ||
| 354 | + if (this.#handle.byteLength === 0) return undefined; | ||
| 355 | + return DataViewPrototypeGetUint8(this.#handle, IDX_STATE_SESSION_APPLICATION_TYPE); | ||
| 356 | + } | ||
| 357 | + | ||
| 350 | 358 | /** @type {bigint} */ | |
| 351 | 359 | get lastDatagramId() { | |
| 352 | 360 | if (this.#handle.byteLength === 0) return undefined; | |
@@ -407,6 +415,7 @@ class QuicSessionState { | |||
| 407 | 415 | isStreamOpenAllowed: this.isStreamOpenAllowed, | |
| 408 | 416 | isPrioritySupported: this.isPrioritySupported, | |
| 409 | 417 | isWrapped: this.isWrapped, | |
| 418 | + applicationType: this.applicationType, | ||
| 410 | 419 | lastDatagramId: this.lastDatagramId, | |
| 411 | 420 | }, opts)}`; | |
| 412 | 421 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,7 +23,6 @@ const { | |||
| 23 | 23 | // Symbols used to hide various private properties and methods from the | |
| 24 | 24 | // public API. | |
| 25 | 25 | ||
| 26 | - const kApplicationProvider = Symbol('kApplicationProvider'); | ||
| 27 | 26 | const kBlocked = Symbol('kBlocked'); | |
| 28 | 27 | const kConnect = Symbol('kConnect'); | |
| 29 | 28 | const kDatagram = Symbol('kDatagram'); | |
@@ -51,7 +50,6 @@ const kWantsHeaders = Symbol('kWantsHeaders'); | |||
| 51 | 50 | const kWantsTrailers = Symbol('kWantsTrailers'); | |
| 52 | 51 | ||
| 53 | 52 | module.exports = { | |
| 54 | - kApplicationProvider, | ||
| 55 | 53 | kBlocked, | |
| 56 | 54 | kConnect, | |
| 57 | 55 | kDatagram, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -464,6 +464,10 @@ class DefaultApplication final : public Session::Application { | |||
| 464 | 464 | // of the namespace. | |
| 465 | 465 | using Application::Application; // NOLINT | |
| 466 | 466 | ||
| 467 | + Session::Application::Type type() const override { | ||
| 468 | + return Session::Application::Type::DEFAULT; | ||
| 469 | + } | ||
| 470 | + | ||
| 467 | 471 | error_code GetNoErrorCode() const override { return 0; } | |
| 468 | 472 | ||
| 469 | 473 | bool ReceiveStreamData(int64_t stream_id, | |
@@ -601,14 +605,9 @@ class DefaultApplication final : public Session::Application { | |||
| 601 | 605 | Stream::Queue stream_queue_; | |
| 602 | 606 | }; | |
| 603 | 607 | ||
| 604 | - std::unique_ptr<Session::Application> Session::SelectApplication( | ||
| 605 | - Session* session, const Config& config) { | ||
| 606 | - if (config.options.application_provider) { | ||
| 607 | - return config.options.application_provider->Create(session); | ||
| 608 | - } | ||
| 609 | - | ||
| 610 | - return std::make_unique<DefaultApplication>(session, | ||
| 611 | - Application_Options::kDefault); | ||
| 608 | + std::unique_ptr<Session::Application> CreateDefaultApplication( | ||
| 609 | + Session* session, const Session::Application_Options& options) { | ||
| 610 | + return std::make_unique<DefaultApplication>(session, options); | ||
| 612 | 611 | } | |
| 613 | 612 | ||
| 614 | 613 | } // namespace quic | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,9 +17,19 @@ class Session::Application : public MemoryRetainer { | |||
| 17 | 17 | public: | |
| 18 | 18 | using Options = Session::Application_Options; | |
| 19 | 19 | ||
| 20 | + // The type of Application, exposed via the session state so JS | ||
| 21 | + // can observe which Application was selected after ALPN negotiation. | ||
| 22 | + enum class Type : uint8_t { | ||
| 23 | + NONE = 0, // Not yet selected (server pre-negotiation) | ||
| 24 | + DEFAULT = 1, // DefaultApplication (non-h3 ALPN) | ||
| 25 | + HTTP3 = 2, // Http3ApplicationImpl (h3 / h3-XX ALPN) | ||
| 26 | + }; | ||
| 27 | + | ||
| 20 | 28 | Application(Session* session, const Options& options); | |
| 21 | 29 | DISALLOW_COPY_AND_MOVE(Application) | |
| 22 | 30 | ||
| 31 | + virtual Type type() const = 0; | ||
| 32 | + | ||
| 23 | 33 | virtual bool Start(); | |
| 24 | 34 | ||
| 25 | 35 | virtual error_code GetNoErrorCode() const = 0; | |
@@ -169,6 +179,10 @@ struct Session::Application::StreamData final { | |||
| 169 | 179 | std::string ToString() const; | |
| 170 | 180 | }; | |
| 171 | 181 | ||
| 182 | + // Create a DefaultApplication for the given session. | ||
| 183 | + std::unique_ptr<Session::Application> CreateDefaultApplication( | ||
| 184 | + Session* session, const Session::Application_Options& options); | ||
| 185 | + | ||
| 172 | 186 | } // namespace node::quic | |
| 173 | 187 | ||
| 174 | 188 | #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,7 +24,6 @@ class Packet; | |||
| 24 | 24 | // The FunctionTemplates the BindingData will store for us. | |
| 25 | 25 | #define QUIC_CONSTRUCTORS(V) \ | |
| 26 | 26 | V(endpoint) \ | |
| 27 | - V(http3application) \ | ||
| 28 | 27 | V(logstream) \ | |
| 29 | 28 | V(session) \ | |
| 30 | 29 | V(stream) \ | |
@@ -59,7 +58,7 @@ class Packet; | |||
| 59 | 58 | V(ack_delay_exponent, "ackDelayExponent") \ | |
| 60 | 59 | V(active_connection_id_limit, "activeConnectionIDLimit") \ | |
| 61 | 60 | V(address_lru_size, "addressLRUSize") \ | |
| 62 | - V(application_provider, "provider") \ | ||
| 61 | + V(application, "application") \ | ||
| 63 | 62 | V(bbr, "bbr") \ | |
| 64 | 63 | V(ca, "ca") \ | |
| 65 | 64 | V(cc_algorithm, "cc") \ | |
@@ -78,7 +77,6 @@ class Packet; | |||
| 78 | 77 | V(groups, "groups") \ | |
| 79 | 78 | V(handshake_timeout, "handshakeTimeout") \ | |
| 80 | 79 | V(http3_alpn, &NGHTTP3_ALPN_H3[1]) \ | |
| 81 | - V(http3application, "Http3Application") \ | ||
| 82 | 80 | V(initial_max_data, "initialMaxData") \ | |
| 83 | 81 | V(initial_max_stream_data_bidi_local, "initialMaxStreamDataBidiLocal") \ | |
| 84 | 82 | V(initial_max_stream_data_bidi_remote, "initialMaxStreamDataBidiRemote") \ | |
@@ -105,7 +103,7 @@ class Packet; | |||
| 105 | 103 | V(max_window, "maxWindow") \ | |
| 106 | 104 | V(min_version, "minVersion") \ | |
| 107 | 105 | V(preferred_address_strategy, "preferredAddressPolicy") \ | |
| 108 | - V(protocol, "protocol") \ | ||
| 106 | + V(alpn, "alpn") \ | ||
| 109 | 107 | V(qlog, "qlog") \ | |
| 110 | 108 | V(qpack_blocked_streams, "qpackBlockedStreams") \ | |
| 111 | 109 | V(qpack_encoder_max_dtable_capacity, "qpackEncoderMaxDTableCapacity") \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -92,7 +92,7 @@ bool is_diagnostic_packet_loss(double probability) { | |||
| 92 | 92 | return (static_cast<double>(c) / 255) < probability; | |
| 93 | 93 | } | |
| 94 | 94 | ||
| 95 | - template <typename Opt, double Opt::*member> | ||
| 95 | + template <typename Opt, double Opt::* member> | ||
| 96 | 96 | bool SetOption(Environment* env, | |
| 97 | 97 | Opt* options, | |
| 98 | 98 | const Local<Object>& object, | |
@@ -113,7 +113,7 @@ bool SetOption(Environment* env, | |||
| 113 | 113 | } | |
| 114 | 114 | #endif // DEBUG | |
| 115 | 115 | ||
| 116 | - template <typename Opt, uint8_t Opt::*member> | ||
| 116 | + template <typename Opt, uint8_t Opt::* member> | ||
| 117 | 117 | bool SetOption(Environment* env, | |
| 118 | 118 | Opt* options, | |
| 119 | 119 | const Local<Object>& object, | |
@@ -140,7 +140,7 @@ bool SetOption(Environment* env, | |||
| 140 | 140 | return true; | |
| 141 | 141 | } | |
| 142 | 142 | ||
| 143 | - template <typename Opt, TokenSecret Opt::*member> | ||
| 143 | + template <typename Opt, TokenSecret Opt::* member> | ||
| 144 | 144 | bool SetOption(Environment* env, | |
| 145 | 145 | Opt* options, | |
| 146 | 146 | const Local<Object>& object, | |
@@ -511,7 +511,6 @@ JS_CONSTRUCTOR_IMPL(Endpoint, endpoint_constructor_template, { | |||
| 511 | 511 | ||
| 512 | 512 | void Endpoint::InitPerIsolate(IsolateData* data, Local<ObjectTemplate> target) { | |
| 513 | 513 | // TODO(@jasnell): Implement the per-isolate state | |
| 514 | - Http3Application::InitPerIsolate(data, target); | ||
| 515 | 514 | } | |
| 516 | 515 | ||
| 517 | 516 | void Endpoint::InitPerContext(Realm* realm, Local<Object> target) { | |
@@ -567,8 +566,6 @@ void Endpoint::InitPerContext(Realm* realm, Local<Object> target) { | |||
| 567 | 566 | NODE_DEFINE_CONSTANT(target, CLOSECONTEXT_SEND_FAILURE); | |
| 568 | 567 | NODE_DEFINE_CONSTANT(target, CLOSECONTEXT_START_FAILURE); | |
| 569 | 568 | ||
| 570 | - Http3Application::InitPerContext(realm, target); | ||
| 571 | - | ||
| 572 | 569 | SetConstructorFunction(realm->context(), | |
| 573 | 570 | target, | |
| 574 | 571 | "Endpoint", | |
| Back | FazBrowse Home | New Git URL |
0 commit comments