| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 95bdf37 commit 7b327ea
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2278,7 +2278,7 @@ not work. | |||
| 2278 | 2278 | For incoming headers: | |
| 2279 | 2279 | * The `:status` header is converted to `number`. | |
| 2280 | 2280 | * Duplicates of `:status`, `:method`, `:authority`, `:scheme`, `:path`, | |
| 2281 | - `age`, `authorization`, `access-control-allow-credentials`, | ||
| 2281 | + `:protocol`, `age`, `authorization`, `access-control-allow-credentials`, | ||
| 2282 | 2282 | `access-control-max-age`, `access-control-request-method`, `content-encoding`, | |
| 2283 | 2283 | `content-language`, `content-length`, `content-location`, `content-md5`, | |
| 2284 | 2284 | `content-range`, `content-type`, `date`, `dnt`, `etag`, `expires`, `from`, | |
@@ -2335,6 +2335,10 @@ properties. | |||
| 2335 | 2335 | * `maxHeaderListSize` {number} Specifies the maximum size (uncompressed octets) | |
| 2336 | 2336 | of header list that will be accepted. The minimum allowed value is 0. The | |
| 2337 | 2337 | maximum allowed value is 2<sup>32</sup>-1. **Default:** `65535`. | |
| 2338 | + * `enableConnectProtocol`{boolean} Specifies `true` if the "Extended Connect | ||
| 2339 | + Protocol" defined by [RFC 8441][] is to be enabled. This setting is only | ||
| 2340 | + meaningful if sent by the server. Once the `enableConnectProtocol` setting | ||
| 2341 | + has been enabled for a given `Http2Session`, it cannot be disabled. | ||
| 2338 | 2342 | ||
| 2339 | 2343 | All additional properties on the settings object are ignored. | |
| 2340 | 2344 | ||
@@ -2501,6 +2505,36 @@ req.on('end', () => { | |||
| 2501 | 2505 | req.end('Jane'); | |
| 2502 | 2506 | ``` | |
| 2503 | 2507 | ||
| 2508 | + ### The Extended CONNECT Protocol | ||
| 2509 | + | ||
| 2510 | + [RFC 8441][] defines an "Extended CONNECT Protocol" extension to HTTP/2 that | ||
| 2511 | + may be used to bootstrap the use of an `Http2Stream` using the `CONNECT` | ||
| 2512 | + method as a tunnel for other communication protocols (such as WebSockets). | ||
| 2513 | + | ||
| 2514 | + The use of the Extended CONNECT Protocol is enabled by HTTP/2 servers by using | ||
| 2515 | + the `enableConnectProtocol` setting: | ||
| 2516 | + | ||
| 2517 | + ```js | ||
| 2518 | + const http2 = require('http2'); | ||
| 2519 | + const settings = { enableConnectProtocol: true }; | ||
| 2520 | + const server = http2.createServer({ settings }); | ||
| 2521 | + ``` | ||
| 2522 | + | ||
| 2523 | + Once the client receives the `SETTINGS` frame from the server indicating that | ||
| 2524 | + the extended CONNECT may be used, it may send `CONNECT` requests that use the | ||
| 2525 | + `':protocol'` HTTP/2 pseudo-header: | ||
| 2526 | + | ||
| 2527 | + ```js | ||
| 2528 | + const http2 = require('http2'); | ||
| 2529 | + const client = http2.connect('http://localhost:8080'); | ||
| 2530 | + client.on('remoteSettings', (settings) => { | ||
| 2531 | + if (settings.enableConnectProtocol) { | ||
| 2532 | + const req = client.request({ ':method': 'CONNECT', ':protocol': 'foo' }); | ||
| 2533 | + // ... | ||
| 2534 | + } | ||
| 2535 | + }); | ||
| 2536 | + ``` | ||
| 2537 | + | ||
| 2504 | 2538 | ## Compatibility API | |
| 2505 | 2539 | ||
| 2506 | 2540 | The Compatibility API has the goal of providing a similar developer experience | |
@@ -3361,6 +3395,7 @@ following additional properties: | |||
| 3361 | 3395 | [Readable Stream]: stream.html#stream_class_stream_readable | |
| 3362 | 3396 | [RFC 7838]: https://tools.ietf.org/html/rfc7838 | |
| 3363 | 3397 | [RFC 8336]: https://tools.ietf.org/html/rfc8336 | |
| 3398 | + [RFC 8441]: https://tools.ietf.org/html/rfc8441 | ||
| 3364 | 3399 | [Using `options.selectPadding()`]: #http2_using_options_selectpadding | |
| 3365 | 3400 | [`'checkContinue'`]: #http2_event_checkcontinue | |
| 3366 | 3401 | [`'request'`]: #http2_event_request | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -191,6 +191,7 @@ const { | |||
| 191 | 191 | HTTP2_HEADER_DATE, | |
| 192 | 192 | HTTP2_HEADER_METHOD, | |
| 193 | 193 | HTTP2_HEADER_PATH, | |
| 194 | + HTTP2_HEADER_PROTOCOL, | ||
| 194 | 195 | HTTP2_HEADER_SCHEME, | |
| 195 | 196 | HTTP2_HEADER_STATUS, | |
| 196 | 197 | HTTP2_HEADER_CONTENT_LENGTH, | |
@@ -1451,7 +1452,7 @@ class ClientHttp2Session extends Http2Session { | |||
| 1451 | 1452 | ||
| 1452 | 1453 | const connect = headers[HTTP2_HEADER_METHOD] === HTTP2_METHOD_CONNECT; | |
| 1453 | 1454 | ||
| 1454 | - if (!connect) { | ||
| 1455 | + if (!connect || headers[HTTP2_HEADER_PROTOCOL] !== undefined) { | ||
| 1455 | 1456 | if (headers[HTTP2_HEADER_AUTHORITY] === undefined) | |
| 1456 | 1457 | headers[HTTP2_HEADER_AUTHORITY] = this[kAuthority]; | |
| 1457 | 1458 | if (headers[HTTP2_HEADER_SCHEME] === undefined) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -20,6 +20,7 @@ const { | |||
| 20 | 20 | HTTP2_HEADER_AUTHORITY, | |
| 21 | 21 | HTTP2_HEADER_SCHEME, | |
| 22 | 22 | HTTP2_HEADER_PATH, | |
| 23 | + HTTP2_HEADER_PROTOCOL, | ||
| 23 | 24 | HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, | |
| 24 | 25 | HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE, | |
| 25 | 26 | HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD, | |
@@ -78,7 +79,8 @@ const kValidPseudoHeaders = new Set([ | |||
| 78 | 79 | HTTP2_HEADER_METHOD, | |
| 79 | 80 | HTTP2_HEADER_AUTHORITY, | |
| 80 | 81 | HTTP2_HEADER_SCHEME, | |
| 81 | - HTTP2_HEADER_PATH | ||
| 82 | + HTTP2_HEADER_PATH, | ||
| 83 | + HTTP2_HEADER_PROTOCOL | ||
| 82 | 84 | ]); | |
| 83 | 85 | ||
| 84 | 86 | // This set contains headers that are permitted to have only a single | |
@@ -89,6 +91,7 @@ const kSingleValueHeaders = new Set([ | |||
| 89 | 91 | HTTP2_HEADER_AUTHORITY, | |
| 90 | 92 | HTTP2_HEADER_SCHEME, | |
| 91 | 93 | HTTP2_HEADER_PATH, | |
| 94 | + HTTP2_HEADER_PROTOCOL, | ||
| 92 | 95 | HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, | |
| 93 | 96 | HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE, | |
| 94 | 97 | HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD, | |
@@ -155,7 +158,8 @@ const IDX_SETTINGS_INITIAL_WINDOW_SIZE = 2; | |||
| 155 | 158 | const IDX_SETTINGS_MAX_FRAME_SIZE = 3; | |
| 156 | 159 | const IDX_SETTINGS_MAX_CONCURRENT_STREAMS = 4; | |
| 157 | 160 | const IDX_SETTINGS_MAX_HEADER_LIST_SIZE = 5; | |
| 158 | - const IDX_SETTINGS_FLAGS = 6; | ||
| 161 | + const IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL = 6; | ||
| 162 | + const IDX_SETTINGS_FLAGS = 7; | ||
| 159 | 163 | ||
| 160 | 164 | const IDX_SESSION_STATE_EFFECTIVE_LOCAL_WINDOW_SIZE = 0; | |
| 161 | 165 | const IDX_SESSION_STATE_EFFECTIVE_RECV_DATA_LENGTH = 1; | |
@@ -277,6 +281,12 @@ function getDefaultSettings() { | |||
| 277 | 281 | settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]; | |
| 278 | 282 | } | |
| 279 | 283 | ||
| 284 | + if ((flags & (1 << IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL)) === | ||
| 285 | + (1 << IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL)) { | ||
| 286 | + holder.enableConnectProtocol = | ||
| 287 | + settingsBuffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL]; | ||
| 288 | + } | ||
| 289 | + | ||
| 280 | 290 | return holder; | |
| 281 | 291 | } | |
| 282 | 292 | ||
@@ -294,7 +304,8 @@ function getSettings(session, remote) { | |||
| 294 | 304 | initialWindowSize: settingsBuffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE], | |
| 295 | 305 | maxFrameSize: settingsBuffer[IDX_SETTINGS_MAX_FRAME_SIZE], | |
| 296 | 306 | maxConcurrentStreams: settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS], | |
| 297 | - maxHeaderListSize: settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE] | ||
| 307 | + maxHeaderListSize: settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE], | ||
| 308 | + enableConnectProtocol: settingsBuffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL] | ||
| 298 | 309 | }; | |
| 299 | 310 | } | |
| 300 | 311 | ||
@@ -329,6 +340,11 @@ function updateSettingsBuffer(settings) { | |||
| 329 | 340 | flags |= (1 << IDX_SETTINGS_ENABLE_PUSH); | |
| 330 | 341 | settingsBuffer[IDX_SETTINGS_ENABLE_PUSH] = Number(settings.enablePush); | |
| 331 | 342 | } | |
| 343 | + if (typeof settings.enableConnectProtocol === 'boolean') { | ||
| 344 | + flags |= (1 << IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL); | ||
| 345 | + settingsBuffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL] = | ||
| 346 | + Number(settings.enableConnectProtocol); | ||
| 347 | + } | ||
| 332 | 348 | ||
| 333 | 349 | settingsBuffer[IDX_SETTINGS_FLAGS] = flags; | |
| 334 | 350 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -219,6 +219,7 @@ void Http2Session::Http2Settings::Init() { | |||
| 219 | 219 | GRABSETTING(INITIAL_WINDOW_SIZE, "initial window size"); | |
| 220 | 220 | GRABSETTING(MAX_HEADER_LIST_SIZE, "max header list size"); | |
| 221 | 221 | GRABSETTING(ENABLE_PUSH, "enable push"); | |
| 222 | + GRABSETTING(ENABLE_CONNECT_PROTOCOL, "enable connect protocol"); | ||
| 222 | 223 | ||
| 223 | 224 | #undef GRABSETTING | |
| 224 | 225 | ||
@@ -287,6 +288,8 @@ void Http2Session::Http2Settings::Update(Environment* env, | |||
| 287 | 288 | fn(**session, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE); | |
| 288 | 289 | buffer[IDX_SETTINGS_ENABLE_PUSH] = | |
| 289 | 290 | fn(**session, NGHTTP2_SETTINGS_ENABLE_PUSH); | |
| 291 | + buffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL] = | ||
| 292 | + fn(**session, NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL); | ||
| 290 | 293 | } | |
| 291 | 294 | ||
| 292 | 295 | // Initializes the shared TypedArray with the default settings values. | |
@@ -3091,6 +3094,7 @@ void Initialize(Local<Object> target, | |||
| 3091 | 3094 | NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE); | |
| 3092 | 3095 | NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_FRAME_SIZE); | |
| 3093 | 3096 | NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE); | |
| 3097 | + NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL); | ||
| 3094 | 3098 | ||
| 3095 | 3099 | NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_NONE); | |
| 3096 | 3100 | NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_ALIGNED); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -160,6 +160,7 @@ struct nghttp2_header : public MemoryRetainer { | |||
| 160 | 160 | V(AUTHORITY, ":authority") \ | |
| 161 | 161 | V(SCHEME, ":scheme") \ | |
| 162 | 162 | V(PATH, ":path") \ | |
| 163 | + V(PROTOCOL, ":protocol") \ | ||
| 163 | 164 | V(ACCEPT_CHARSET, "accept-charset") \ | |
| 164 | 165 | V(ACCEPT_ENCODING, "accept-encoding") \ | |
| 165 | 166 | V(ACCEPT_LANGUAGE, "accept-language") \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,6 +15,7 @@ namespace http2 { | |||
| 15 | 15 | IDX_SETTINGS_MAX_FRAME_SIZE, | |
| 16 | 16 | IDX_SETTINGS_MAX_CONCURRENT_STREAMS, | |
| 17 | 17 | IDX_SETTINGS_MAX_HEADER_LIST_SIZE, | |
| 18 | + IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL, | ||
| 18 | 19 | IDX_SETTINGS_COUNT | |
| 19 | 20 | }; | |
| 20 | 21 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -97,6 +97,7 @@ const expectedHeaderNames = { | |||
| 97 | 97 | HTTP2_HEADER_AUTHORITY: ':authority', | |
| 98 | 98 | HTTP2_HEADER_SCHEME: ':scheme', | |
| 99 | 99 | HTTP2_HEADER_PATH: ':path', | |
| 100 | + HTTP2_HEADER_PROTOCOL: ':protocol', | ||
| 100 | 101 | HTTP2_HEADER_DATE: 'date', | |
| 101 | 102 | HTTP2_HEADER_ACCEPT_CHARSET: 'accept-charset', | |
| 102 | 103 | HTTP2_HEADER_ACCEPT_ENCODING: 'accept-encoding', | |
@@ -217,7 +218,8 @@ const expectedNGConstants = { | |||
| 217 | 218 | NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: 3, | |
| 218 | 219 | NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: 4, | |
| 219 | 220 | NGHTTP2_SETTINGS_MAX_FRAME_SIZE: 5, | |
| 220 | - NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 6 | ||
| 221 | + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 6, | ||
| 222 | + NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: 8 | ||
| 221 | 223 | }; | |
| 222 | 224 | ||
| 223 | 225 | const defaultSettings = { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,30 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const http2 = require('http2'); | ||
| 8 | + | ||
| 9 | + const settings = { enableConnectProtocol: true }; | ||
| 10 | + const server = http2.createServer({ settings }); | ||
| 11 | + server.on('stream', common.mustNotCall()); | ||
| 12 | + server.on('session', common.mustCall((session) => { | ||
| 13 | + // This will force the connection to close because once extended connect | ||
| 14 | + // is on, it cannot be turned off. The server is behaving badly. | ||
| 15 | + session.settings({ enableConnectProtocol: false }); | ||
| 16 | + })); | ||
| 17 | + | ||
| 18 | + server.listen(0, common.mustCall(() => { | ||
| 19 | + const client = http2.connect(`http://localhost:${server.address().port}`); | ||
| 20 | + client.on('remoteSettings', common.mustCall((settings) => { | ||
| 21 | + assert(settings.enableConnectProtocol); | ||
| 22 | + const req = client.request({ | ||
| 23 | + ':method': 'CONNECT', | ||
| 24 | + ':protocol': 'foo' | ||
| 25 | + }); | ||
| 26 | + req.on('error', common.mustCall(() => { | ||
| 27 | + server.close(); | ||
| 28 | + })); | ||
| 29 | + })); | ||
| 30 | + })); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,39 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common'); | ||
| 4 | + if (!common.hasCrypto) | ||
| 5 | + common.skip('missing crypto'); | ||
| 6 | + const assert = require('assert'); | ||
| 7 | + const http2 = require('http2'); | ||
| 8 | + | ||
| 9 | + const settings = { enableConnectProtocol: true }; | ||
| 10 | + const server = http2.createServer({ settings }); | ||
| 11 | + server.on('stream', common.mustCall((stream, headers) => { | ||
| 12 | + assert.strictEqual(headers[':method'], 'CONNECT'); | ||
| 13 | + assert.strictEqual(headers[':scheme'], 'http'); | ||
| 14 | + assert.strictEqual(headers[':protocol'], 'foo'); | ||
| 15 | + assert.strictEqual(headers[':authority'], | ||
| 16 | + `localhost:${server.address().port}`); | ||
| 17 | + assert.strictEqual(headers[':path'], '/'); | ||
| 18 | + stream.respond(); | ||
| 19 | + stream.end('ok'); | ||
| 20 | + })); | ||
| 21 | + | ||
| 22 | + server.listen(0, common.mustCall(() => { | ||
| 23 | + const client = http2.connect(`http://localhost:${server.address().port}`); | ||
| 24 | + client.on('remoteSettings', common.mustCall((settings) => { | ||
| 25 | + assert(settings.enableConnectProtocol); | ||
| 26 | + const req = client.request({ | ||
| 27 | + ':method': 'CONNECT', | ||
| 28 | + ':protocol': 'foo' | ||
| 29 | + }); | ||
| 30 | + req.resume(); | ||
| 31 | + req.on('end', common.mustCall()); | ||
| 32 | + req.on('close', common.mustCall(() => { | ||
| 33 | + assert.strictEqual(req.rstCode, 0); | ||
| 34 | + server.close(); | ||
| 35 | + client.close(); | ||
| 36 | + })); | ||
| 37 | + req.end(); | ||
| 38 | + })); | ||
| 39 | + })); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments