From b327d335ff6bc48c3c2aaedccfa9c40522f6b32f Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Wed, 24 Jun 2020 20:16:04 -0500 Subject: [PATCH 01/25] n-api: add version to wasm registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/34045 Reviewed-By: James M Snell Reviewed-By: Gabriel Schulhof Reviewed-By: Tobias Nießen --- src/node_api.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/node_api.h b/src/node_api.h index 7f48d8c8465520..2f1b45572d8130 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -73,11 +73,17 @@ typedef struct { } \ EXTERN_C_END +#define NAPI_MODULE_INITIALIZER_X(base, version) \ + NAPI_MODULE_INITIALIZER_X_HELPER(base, version) +#define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version + #ifdef __wasm32__ +#define NAPI_WASM_INITIALIZER \ + NAPI_MODULE_INITIALIZER_X(napi_register_wasm_v, NAPI_MODULE_VERSION) #define NAPI_MODULE(modname, regfunc) \ EXTERN_C_START \ - NAPI_MODULE_EXPORT napi_value _napi_register(napi_env env, \ - napi_value exports) { \ + NAPI_MODULE_EXPORT napi_value NAPI_WASM_INITIALIZER(napi_env env, \ + napi_value exports) { \ return regfunc(env, exports); \ } \ EXTERN_C_END @@ -88,10 +94,6 @@ typedef struct { #define NAPI_MODULE_INITIALIZER_BASE napi_register_module_v -#define NAPI_MODULE_INITIALIZER_X(base, version) \ - NAPI_MODULE_INITIALIZER_X_HELPER(base, version) -#define NAPI_MODULE_INITIALIZER_X_HELPER(base, version) base##version - #define NAPI_MODULE_INITIALIZER \ NAPI_MODULE_INITIALIZER_X(NAPI_MODULE_INITIALIZER_BASE, \ NAPI_MODULE_VERSION) From 95eecd58b39b7dbedfce509b4a38d0c18e22f7cb Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 25 Jun 2020 21:20:23 -0700 Subject: [PATCH 02/25] doc: improve paragraph in esm.md Edit for clarity, correct tense, and brevity. PR-URL: https://github.com/nodejs/node/pull/34064 Reviewed-By: Guy Bedford Reviewed-By: Luigi Pinca --- doc/api/esm.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/esm.md b/doc/api/esm.md index eca866e452b736..06a9f89ee57579 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -124,12 +124,12 @@ as ES modules and `.cjs` files are always treated as CommonJS. ### Package scope and file extensions A folder containing a `package.json` file, and all subfolders below that folder -down until the next folder containing another `package.json`, is considered a -_package scope_. The `"type"` field defines how `.js` files should be treated -within a particular `package.json` file’s package scope. Every package in a +until the next folder containing another `package.json`, are a +_package scope_. The `"type"` field defines how to treat `.js` files +within the package scope. Every package in a project’s `node_modules` folder contains its own `package.json` file, so each -project’s dependencies have their own package scopes. A `package.json` lacking a -`"type"` field is treated as if it contained `"type": "commonjs"`. +project’s dependencies have their own package scopes. If a `package.json` file +does not have a `"type"` field, the default `"type"` is `"commonjs"`. The package scope applies not only to initial entry points (`node my-app.js`) but also to files referenced by `import` statements and `import()` expressions. From 90d5f35f7abc06b2e4f6c0e05d293183360af3f7 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 25 Jun 2020 21:00:24 -0700 Subject: [PATCH 03/25] doc: make minor improvements to paragraph in child_process.md Use shorter and more direct phrasing. PR-URL: https://github.com/nodejs/node/pull/34063 Reviewed-By: Anna Henningsen Reviewed-By: Trivikram Kamat Reviewed-By: Denys Otrishko --- doc/api/child_process.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 1c1ea3b5547386..0d00a2a2433616 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -194,9 +194,9 @@ metacharacters may be used to trigger arbitrary command execution.** If a `callback` function is provided, it is called with the arguments `(error, stdout, stderr)`. On success, `error` will be `null`. On error, `error` will be an instance of [`Error`][]. The `error.code` property will be -the exit code of the child process while `error.signal` will be set to the -signal that terminated the process. Any exit code other than `0` is considered -to be an error. +the exit code of the process. By convention, any exit code other than `0` +indicates an error. `error.signal` will be the signal that terminated the +process. The `stdout` and `stderr` arguments passed to the callback will contain the stdout and stderr output of the child process. By default, Node.js will decode From da8a1ef21b34af25c31cab5cf2cf023b66abe413 Mon Sep 17 00:00:00 2001 From: Andrey Pechkurov Date: Fri, 26 Jun 2020 16:54:44 +0300 Subject: [PATCH 04/25] zlib: remove redundant variable in zlibBufferOnEnd PR-URL: https://github.com/nodejs/node/pull/34072 Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Richard Lau --- lib/zlib.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/zlib.js b/lib/zlib.js index 8ee2c143e5ae6f..7cc8e2e6275041 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -138,7 +138,6 @@ function zlibBufferOnError(err) { function zlibBufferOnEnd() { let buf; - let err; if (this.nread === 0) { buf = Buffer.alloc(0); } else { @@ -146,9 +145,7 @@ function zlibBufferOnEnd() { buf = (bufs.length === 1 ? bufs[0] : Buffer.concat(bufs, this.nread)); } this.close(); - if (err) - this.cb(err); - else if (this._info) + if (this._info) this.cb(null, { buffer: buf, engine: this }); else this.cb(null, buf); From 9e135accbb045d52eb65de602c81b5090daae1c4 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Wed, 24 Jun 2020 19:45:03 -0500 Subject: [PATCH 05/25] wasi: add reactor support PR-URL: https://github.com/nodejs/node/pull/34046 Reviewed-By: Guy Bedford Reviewed-By: Colin Ihrig --- doc/api/wasi.md | 17 ++ lib/wasi.js | 90 ++++++--- test/wasi/test-wasi-initialize-validation.js | 195 +++++++++++++++++++ test/wasi/test-wasi-start-validation.js | 9 +- 4 files changed, 278 insertions(+), 33 deletions(-) create mode 100644 test/wasi/test-wasi-initialize-validation.js diff --git a/doc/api/wasi.md b/doc/api/wasi.md index 46673af9e7fb34..9fe004ea599cfc 100644 --- a/doc/api/wasi.md +++ b/doc/api/wasi.md @@ -132,6 +132,23 @@ Attempt to begin execution of `instance` as a WASI command by invoking its If `start()` is called more than once, an exception is thrown. +### `wasi.initialize(instance)` + + +* `instance` {WebAssembly.Instance} + +Attempt to initialize `instance` as a WASI reactor by invoking its +`_initialize()` export, if it is present. If `instance` contains a `_start()` +export, then an exception is thrown. + +`initialize()` requires that `instance` exports a [`WebAssembly.Memory`][] named +`memory`. If `instance` does not have a `memory` export an exception is thrown. + +If `initialize()` is called more than once, an exception is thrown. + ### `wasi.wasiImport` + +Type: Documentation-only + +[`socket.bufferSize`][] is just an alias for [`writable.writableLength`][]. + [`--pending-deprecation`]: cli.html#cli_pending_deprecation [`--throw-deprecation`]: cli.html#cli_throw_deprecation [`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size @@ -2824,6 +2837,7 @@ const moduleParents = Object.values(require.cache) [`script.createCachedData()`]: vm.html#vm_script_createcacheddata [`setInterval()`]: timers.html#timers_setinterval_callback_delay_args [`setTimeout()`]: timers.html#timers_settimeout_callback_delay_args +[`socket.bufferSize`]: net.html#net_socket_buffersize [`timeout.ref()`]: timers.html#timers_timeout_ref [`timeout.refresh()`]: timers.html#timers_timeout_refresh [`timeout.unref()`]: timers.html#timers_timeout_unref @@ -2860,6 +2874,7 @@ const moduleParents = Object.values(require.cache) [`util`]: util.html [`worker.exitedAfterDisconnect`]: cluster.html#cluster_worker_exitedafterdisconnect [`worker.terminate()`]: worker_threads.html#worker_threads_worker_terminate +[`writable.writableLength`]: stream.html#stream_writable_writablelength [`zlib.bytesWritten`]: zlib.html#zlib_zlib_byteswritten [Legacy URL API]: url.html#url_legacy_url_api [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf diff --git a/doc/api/net.md b/doc/api/net.md index b9813aae885293..a07c34cee07835 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -529,8 +529,12 @@ socket as reported by the operating system: ### `socket.bufferSize` +> Stability: 0 - Deprecated: Use [`writable.writableLength`][] instead. + * {integer} This property shows the number of characters buffered for writing. The buffer @@ -1268,6 +1272,7 @@ Returns `true` if input is a version 6 IP address, otherwise returns `false`. [`socket.setEncoding()`]: #net_socket_setencoding_encoding [`socket.setTimeout()`]: #net_socket_settimeout_timeout_callback [`socket.setTimeout(timeout)`]: #net_socket_settimeout_timeout_callback +[`writable.writableLength`]: stream.html#stream_writable_writablelength [`writable.destroyed`]: stream.html#stream_writable_destroyed [`writable.destroy()`]: stream.html#stream_writable_destroy_error [`writable.end()`]: stream.html#stream_writable_end_chunk_encoding_callback From eb8fc2bf0f9283eb10751927de8c59e1a985366d Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Sat, 27 Jun 2020 20:26:32 -0700 Subject: [PATCH 14/25] 2020-06-30, Version 14.5.0 (Current) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notable changes: build: * (SEMVER-MINOR) reset embedder string to "-node.0" (Michaël Zasso) https://github.com/nodejs/node/pull/33376 cli: * (SEMVER-MINOR) add alias for report-directory to make it consistent (AshCripps) https://github.com/nodejs/node/pull/33587 crypto: * (SEMVER-MINOR) allow KeyObjects in postMessage (Tobias Nießen) https://github.com/nodejs/node/pull/33360 deps: * (SEMVER-MINOR) V8: cherry-pick 0d6debcc5f08 (Michaël Zasso) https://github.com/nodejs/node/pull/33376 * (SEMVER-MINOR) update V8 to 8.3.110.9 (Michaël Zasso) https://github.com/nodejs/node/pull/33376 dgram: * (SEMVER-MINOR) allow typed arrays in .send() (Sarat Addepalli) https://github.com/nodejs/node/pull/22413 events: * (SEMVER-MINOR) initial implementation of experimental EventTarget (James M Snell) https://github.com/nodejs/node/pull/33556 fs: * (SEMVER-MINOR) implement lutimes (Maël Nison) https://github.com/nodejs/node/pull/33399 http: * (SEMVER-MINOR) expose host and protocol on ClientRequest (wenningplus) https://github.com/nodejs/node/pull/33803 * (SEMVER-MINOR) add maxTotalSockets to agent class (rickyes) https://github.com/nodejs/node/pull/33617 * (SEMVER-MINOR) return this from OutgoingMessage#destroy() (Colin Ihrig) https://github.com/nodejs/node/pull/32789 * (SEMVER-MINOR) return this from ClientRequest#destroy() (Colin Ihrig) https://github.com/nodejs/node/pull/32789 * (SEMVER-MINOR) return this from IncomingMessage#destroy() (Colin Ihrig) https://github.com/nodejs/node/pull/32789 * (SEMVER-MINOR) added scheduling option to http agent (delvedor) https://github.com/nodejs/node/pull/33278 http2: * (SEMVER-MINOR) return this for Http2ServerRequest#setTimeout (Pranshu Srivastava) https://github.com/nodejs/node/pull/33994 * (SEMVER-MINOR) do not modify explicity set date headers (Pranshu Srivastava) https://github.com/nodejs/node/pull/33160 process: * (SEMVER-MINOR) add unhandled-rejection throw and warn-with-error-code (Dan Fabulich) https://github.com/nodejs/node/pull/33475 src: * (SEMVER-MINOR) store key data in separate class (Tobias Nießen) https://github.com/nodejs/node/pull/33360 * (SEMVER-MINOR) add NativeKeyObject base class (Tobias Nießen) https://github.com/nodejs/node/pull/33360 * (SEMVER-MINOR) rename internal key handles to KeyObjectHandle (Tobias Nießen) https://github.com/nodejs/node/pull/33360 * (SEMVER-MINOR) add equality operators for BaseObjectPtr (Anna Henningsen) https://github.com/nodejs/node/pull/33772 * (SEMVER-MINOR) introduce BaseObject base FunctionTemplate (Anna Henningsen) https://github.com/nodejs/node/pull/33772 * (SEMVER-MINOR) add public APIs to manage v8::TracingController (Anna Henningsen) https://github.com/nodejs/node/pull/33850 stream*: * runtime deprecate Transform._transformState (Robert Nagy) https://github.com/nodejs/node/pull/32763 win: * (SEMVER-MINOR) allow skipping the supported platform check (João Reis) https://github.com/nodejs/node/pull/33176 worker: * (SEMVER-MINOR) add public method for marking objects as untransferable (Anna Henningsen) https://github.com/nodejs/node/pull/33979 * (SEMVER-MINOR) emit `'messagerror'` events for failed deserialization (Anna Henningsen) https://github.com/nodejs/node/pull/33772 * (SEMVER-MINOR) allow passing JS wrapper objects via postMessage (Anna Henningsen) https://github.com/nodejs/node/pull/33772 * (SEMVER-MINOR) allow transferring/cloning generic BaseObjects (Anna Henningsen) https://github.com/nodejs/node/pull/33772 worker,fs: * (SEMVER-MINOR) make FileHandle transferable (Anna Henningsen) https://github.com/nodejs/node/pull/33772 zlib: * (SEMVER-MINOR) add `maxOutputLength` option (unknown) https://github.com/nodejs/node/pull/33516 PR-URL: https://github.com/nodejs/node/pull/34093 --- CHANGELOG.md | 3 +- doc/api/cli.md | 2 +- doc/api/crypto.md | 2 +- doc/api/deprecations.md | 6 +- doc/api/dgram.md | 2 +- doc/api/dns.md | 2 +- doc/api/errors.md | 2 +- doc/api/events.md | 64 +++--- doc/api/fs.md | 6 +- doc/api/http.md | 12 +- doc/api/http2.md | 6 +- doc/api/n-api.md | 2 +- doc/api/repl.md | 2 +- doc/api/worker_threads.md | 10 +- doc/api/zlib.md | 4 +- doc/changelogs/CHANGELOG_V14.md | 365 ++++++++++++++++++++++++++++++++ 16 files changed, 428 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e71419016fed..e0bbb28129d87b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,8 @@ release. -14.4.0
+14.5.0
+14.4.0
14.3.0
14.2.0
14.1.0
diff --git a/doc/api/cli.md b/doc/api/cli.md index 8606054148ed40..33c1e94fafb7c4 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1385,7 +1385,7 @@ Overriding this value to an empty string (`''`) will use the built-in REPL. ### `NODE_SKIP_PLATFORM_CHECK=value` If `value` equals `'1'`, the check for a supported platform is skipped during diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 49273db7608dc6..0621ee62d3246d 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1216,7 +1216,7 @@ This can be called many times with new data as it is streamed. Type: Documentation-only (supports [`--pending-deprecation`][]) diff --git a/doc/api/dgram.md b/doc/api/dgram.md index e3c75e026c2d74..be0323a306b27e 100644 --- a/doc/api/dgram.md +++ b/doc/api/dgram.md @@ -399,7 +399,7 @@ if the socket is not connected. A message posted to a [`MessagePort`][] could not be deserialized in the target diff --git a/doc/api/events.md b/doc/api/events.md index d008b95641ea11..39950b5c12f581 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -939,7 +939,7 @@ composed of the emitted event arguments. ## `EventTarget` and `Event` API > Stability: 1 - Experimental @@ -1056,7 +1056,7 @@ The `EventTarget` does not implement any special default handling for ### Class: `Event` The `Event` object is an adaptation of the [`Event` Web API][]. Instances @@ -1064,7 +1064,7 @@ are created internally by Node.js. #### `event.bubbles` * Type: {boolean} Always returns `false`. @@ -1073,7 +1073,7 @@ This is not used in Node.js and is provided purely for completeness. #### `event.cancelBubble()` Alias for `event.stopPropagation()`. This is not used in Node.js and is @@ -1081,14 +1081,14 @@ provided purely for completeness. #### `event.cancelable` * Type: {boolean} True if the event was created with the `cancelable` option. #### `event.composed` * Type: {boolean} Always returns `false`. @@ -1097,7 +1097,7 @@ This is not used in Node.js and is provided purely for completeness. #### `event.composedPath()` Returns an array containing the current `EventTarget` as the only entry or @@ -1106,7 +1106,7 @@ Node.js and is provided purely for completeness. #### `event.currentTarget` * Type: {EventTarget} The `EventTarget` dispatching the event. @@ -1115,7 +1115,7 @@ Alias for `event.target`. #### `event.defaultPrevented` * Type: {boolean} @@ -1125,7 +1125,7 @@ called. #### `event.eventPhase` * Type: {number} Returns `0` while an event is not being dispatched, `2` while @@ -1135,7 +1135,7 @@ This is not used in Node.js and is provided purely for completeness. #### `event.isTrusted` * Type: {boolean} Always returns `false`. @@ -1144,14 +1144,14 @@ This is not used in Node.js and is provided purely for completeness. #### `event.preventDefault()` Sets the `defaultPrevented` property to `true` if `cancelable` is `true`. #### `event.returnValue` * Type: {boolean} True if the event has not been canceled. @@ -1160,7 +1160,7 @@ This is not used in Node.js and is provided purely for completeness. #### `event.srcElement` * Type: {EventTarget} The `EventTarget` dispatching the event. @@ -1169,28 +1169,28 @@ Alias for `event.target`. #### `event.stopImmediatePropagation()` Stops the invocation of event listeners after the current one completes. #### `event.stopPropagation()` This is not used in Node.js and is provided purely for completeness. #### `event.target` * Type: {EventTarget} The `EventTarget` dispatching the event. #### `event.timeStamp` * Type: {number} @@ -1199,7 +1199,7 @@ The millisecond timestamp when the `Event` object was created. #### `event.type` * Type: {string} @@ -1208,12 +1208,12 @@ The event type identifier. ### Class: `EventTarget` #### `eventTarget.addEventListener(type, listener[, options])` * `type` {string} @@ -1255,7 +1255,7 @@ target.removeEventListener('foo', handler, { capture: true }); #### `eventTarget.dispatchEvent(event)` * `event` {Object|Event} @@ -1269,7 +1269,7 @@ were registered. #### `eventTarget.removeEventListener(type, listener)` * `type` {string} @@ -1281,7 +1281,7 @@ Removes the `listener` from the list of handlers for event `type`. ### Class: `NodeEventTarget` * Extends: {EventTarget} @@ -1291,7 +1291,7 @@ that emulates a subset of the `EventEmitter` API. #### `nodeEventTarget.addListener(type, listener[, options])` * `type` {string} @@ -1308,7 +1308,7 @@ equivalent `EventEmitter` API. The only difference between `addListener()` and #### `nodeEventTarget.eventNames()` * Returns: {string[]} @@ -1318,7 +1318,7 @@ of event `type` names for which event listeners are registered. #### `nodeEventTarget.listenerCount(type)` * `type` {string} @@ -1330,7 +1330,7 @@ of event listeners registered for the `type`. #### `nodeEventTarget.off(type, listener)` * `type` {string} @@ -1342,7 +1342,7 @@ Node.js-speciic alias for `eventTarget.removeListener()`. #### `nodeEventTarget.on(type, listener[, options])` * `type` {string} @@ -1356,7 +1356,7 @@ Node.js-specific alias for `eventTarget.addListener()`. #### `nodeEventTarget.once(type, listener[, options])` * `type` {string} @@ -1371,7 +1371,7 @@ with the `once` option set to `true`. #### `nodeEventTarget.removeAllListeners([type])` * `type` {string} @@ -1382,7 +1382,7 @@ listeners. #### `nodeEventTarget.removeListener(type, listener)` * `type` {string} diff --git a/doc/api/fs.md b/doc/api/fs.md index dc6b11f25933d1..6ce2fded3754c8 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -2400,7 +2400,7 @@ Synchronous lchown(2). Returns `undefined`. ## `fs.lutimes(path, atime, mtime, callback)` * `path` {string|Buffer|URL} @@ -2419,7 +2419,7 @@ callback. ## `fs.lutimesSync(path, atime, mtime)` * `path` {string|Buffer|URL} @@ -5071,7 +5071,7 @@ no arguments upon success. ### `fsPromises.lutimes(path, atime, mtime)` * `path` {string|Buffer|URL} diff --git a/doc/api/http.md b/doc/api/http.md index f6447ed35a3ba0..5429bdb297c7e9 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -113,7 +113,7 @@ http.get({ * {number} @@ -660,7 +660,7 @@ is finished. * {string} The request host. ### `request.protocol` * {string} The request protocol. @@ -1917,7 +1917,7 @@ const req = http.request({ @@ -1459,7 +1459,7 @@ server.on('stream', (stream) => { * {string[]} diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index a582332eaeda1c..38167bfd0bf234 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -84,7 +84,7 @@ if (isMainThread) { ## `worker.markAsUntransferable(object)` Mark an object as not transferable. If `object` occurs in the transfer list of @@ -343,7 +343,7 @@ to `postMessage()` and no further arguments. ### Event: `'messageerror'` * `error` {Error} An Error object @@ -366,10 +366,10 @@ are part of the channel. @@ -736,7 +736,7 @@ All messages sent from the worker thread will be emitted before the ### Event: `'messageerror'` * `error` {Error} An Error object diff --git a/doc/api/zlib.md b/doc/api/zlib.md index a64ae57c7bc233..34adda9788dfe4 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -488,7 +488,7 @@ These advanced options are available for controlling decompression: diff --git a/doc/changelogs/CHANGELOG_V14.md b/doc/changelogs/CHANGELOG_V14.md index 2646eae48ca714..cdcb574cb6b060 100644 --- a/doc/changelogs/CHANGELOG_V14.md +++ b/doc/changelogs/CHANGELOG_V14.md @@ -10,6 +10,7 @@ +14.5.0
14.4.0
14.3.0
14.2.0
@@ -35,6 +36,370 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2020-06-30, Version 14.5.0 (Current), @codebytere + +### Notable Changes + +#### V8 engine is updated to version 8.3 + +This version includes performance improvements and now allows WebAssembly +modules to request memories up to 4GB in size. + +For more information, have a look at the [official V8 blog post](https://v8.dev/blog/v8-release-83). + +Contributed by Matheus Marchini and Michaël Zasso - [#33376](https://github.com/nodejs/node/pull/33376). + +#### Initial experimental implementation of EventTarget + +This version introduces an new experimental API `EventTarget`, which provides a DOM interface implemented by objects that can receive events and may have listeners for them. + +It is an adaptation of the Web API EventTarget. + +Example Usage: + +```js +const target = getEventTargetSomehow(); + +target.addEventListener('foo', (event) => { + console.log('foo event happened!'); +}); +``` + +Contributed by James Snell - [#33556](https://github.com/nodejs/node/pull/33556). + +### Semver-Minor Commits + +* [[`4ccaa537d4`](https://github.com/nodejs/node/commit/4ccaa537d4)] - **(SEMVER-MINOR)** **build**: reset embedder string to "-node.0" (Michaël Zasso) [#33376](https://github.com/nodejs/node/pull/33376) +* [[`d194d20828`](https://github.com/nodejs/node/commit/d194d20828)] - **(SEMVER-MINOR)** **cli**: add alias for report-directory to make it consistent (AshCripps) [#33587](https://github.com/nodejs/node/pull/33587) +* [[`70398dbf60`](https://github.com/nodejs/node/commit/70398dbf60)] - **(SEMVER-MINOR)** **crypto**: allow KeyObjects in postMessage (Tobias Nießen) [#33360](https://github.com/nodejs/node/pull/33360) +* [[`9b7ba87aa6`](https://github.com/nodejs/node/commit/9b7ba87aa6)] - **(SEMVER-MINOR)** **deps**: V8: cherry-pick 0d6debcc5f08 (Michaël Zasso) [#33376](https://github.com/nodejs/node/pull/33376) +* [[`ce1a1ae621`](https://github.com/nodejs/node/commit/ce1a1ae621)] - **(SEMVER-MINOR)** **deps**: V8: cherry-pick 74d50c5063b3 (Michaël Zasso) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`aa7267a344`](https://github.com/nodejs/node/commit/aa7267a344)] - **(SEMVER-MINOR)** **deps**: V8: cherry-pick e29c62b74854 (Michaël Zasso) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`1512757a22`](https://github.com/nodejs/node/commit/1512757a22)] - **(SEMVER-MINOR)** **deps**: V8: cherry-pick 3f8dc4b2e5ba (Michaël Zasso) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`3d9cf4bde6`](https://github.com/nodejs/node/commit/3d9cf4bde6)] - **(SEMVER-MINOR)** **deps**: V8: cherry-pick e1eac1b16c96 (Milad Farazmand) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`cdeade308e`](https://github.com/nodejs/node/commit/cdeade308e)] - **(SEMVER-MINOR)** **deps**: fix V8 8.3 on SmartOS (Colin Ihrig) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`883840bc17`](https://github.com/nodejs/node/commit/883840bc17)] - **(SEMVER-MINOR)** **deps**: patch V8 to run on Xcode 8 (Matheus Marchini) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`3831a541fb`](https://github.com/nodejs/node/commit/3831a541fb)] - **(SEMVER-MINOR)** **deps**: V8: silence irrelevant warnings (Michaël Zasso) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`e2fc08f216`](https://github.com/nodejs/node/commit/e2fc08f216)] - **(SEMVER-MINOR)** **deps**: make v8.h compatible with VS2015 (Joao Reis) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`74b623bd51`](https://github.com/nodejs/node/commit/74b623bd51)] - **(SEMVER-MINOR)** **deps**: V8: forward declaration of `Rtl\*FunctionTable` (Refael Ackermann) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`0f5764aec2`](https://github.com/nodejs/node/commit/0f5764aec2)] - **(SEMVER-MINOR)** **deps**: V8: patch register-arm64.h (Refael Ackermann) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`be773fc3cf`](https://github.com/nodejs/node/commit/be773fc3cf)] - **(SEMVER-MINOR)** **deps**: patch V8 to run on older XCode versions (Ujjwal Sharma) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`7aa41c6e6f`](https://github.com/nodejs/node/commit/7aa41c6e6f)] - **(SEMVER-MINOR)** **deps**: V8: un-cherry-pick bd019bd (Refael Ackermann) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`ce901e3906`](https://github.com/nodejs/node/commit/ce901e3906)] - **(SEMVER-MINOR)** **deps**: update V8 dtrace & postmortem metadata (Colin Ihrig) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`1123425dd1`](https://github.com/nodejs/node/commit/1123425dd1)] - **(SEMVER-MINOR)** **deps**: update V8 to 8.3.110.9 (Michaël Zasso) [#33376](https://github.com/nodejs/node/pull/33376) +* [[`1c70b18da8`](https://github.com/nodejs/node/commit/1c70b18da8)] - **(SEMVER-MINOR)** **events**: initial implementation of experimental EventTarget (James M Snell) [#33556](https://github.com/nodejs/node/pull/33556) +* [[`cf97c56dab`](https://github.com/nodejs/node/commit/cf97c56dab)] - **(SEMVER-MINOR)** **fs**: implement lutimes (Maël Nison) [#33399](https://github.com/nodejs/node/pull/33399) +* [[`a24b8df7fb`](https://github.com/nodejs/node/commit/a24b8df7fb)] - **(SEMVER-MINOR)** **http**: expose host and protocol on ClientRequest (wenningplus) [#33803](https://github.com/nodejs/node/pull/33803) +* [[`507a2ef31c`](https://github.com/nodejs/node/commit/507a2ef31c)] - **(SEMVER-MINOR)** **http**: add maxTotalSockets to agent class (rickyes) [#33617](https://github.com/nodejs/node/pull/33617) +* [[`e1e3ae1567`](https://github.com/nodejs/node/commit/e1e3ae1567)] - **(SEMVER-MINOR)** **http**: return this from OutgoingMessage#destroy() (Colin Ihrig) [#32789](https://github.com/nodejs/node/pull/32789) +* [[`d87031def4`](https://github.com/nodejs/node/commit/d87031def4)] - **(SEMVER-MINOR)** **http**: return this from ClientRequest#destroy() (Colin Ihrig) [#32789](https://github.com/nodejs/node/pull/32789) +* [[`c7959557db`](https://github.com/nodejs/node/commit/c7959557db)] - **(SEMVER-MINOR)** **http**: return this from IncomingMessage#destroy() (Colin Ihrig) [#32789](https://github.com/nodejs/node/pull/32789) +* [[`a3a0c0e0fc`](https://github.com/nodejs/node/commit/a3a0c0e0fc)] - **(SEMVER-MINOR)** **http**: added scheduling option to http agent (delvedor) [#33278](https://github.com/nodejs/node/pull/33278) +* [[`e3fd2f5a48`](https://github.com/nodejs/node/commit/e3fd2f5a48)] - **(SEMVER-MINOR)** **http2**: return this for Http2ServerRequest#setTimeout (Pranshu Srivastava) [#33994](https://github.com/nodejs/node/pull/33994) +* [[`7ccb021ffc`](https://github.com/nodejs/node/commit/7ccb021ffc)] - **(SEMVER-MINOR)** **http2**: do not modify explicity set date headers (Pranshu Srivastava) [#33160](https://github.com/nodejs/node/pull/33160) +* [[`f66bb57c13`](https://github.com/nodejs/node/commit/f66bb57c13)] - **(SEMVER-MINOR)** **process**: add unhandled-rejection throw and warn-with-error-code (Dan Fabulich) [#33475](https://github.com/nodejs/node/pull/33475) +* [[`33020256de`](https://github.com/nodejs/node/commit/33020256de)] - **(SEMVER-MINOR)** **src**: store key data in separate class (Tobias Nießen) [#33360](https://github.com/nodejs/node/pull/33360) +* [[`44b9d08344`](https://github.com/nodejs/node/commit/44b9d08344)] - **(SEMVER-MINOR)** **src**: add NativeKeyObject base class (Tobias Nießen) [#33360](https://github.com/nodejs/node/pull/33360) +* [[`13e633873e`](https://github.com/nodejs/node/commit/13e633873e)] - **(SEMVER-MINOR)** **src**: rename internal key handles to KeyObjectHandle (Tobias Nießen) [#33360](https://github.com/nodejs/node/pull/33360) +* [[`a3d0b0e2d7`](https://github.com/nodejs/node/commit/a3d0b0e2d7)] - **(SEMVER-MINOR)** **src**: add equality operators for BaseObjectPtr (Anna Henningsen) [#33772](https://github.com/nodejs/node/pull/33772) +* [[`0720d1ff24`](https://github.com/nodejs/node/commit/0720d1ff24)] - **(SEMVER-MINOR)** **src**: introduce BaseObject base FunctionTemplate (Anna Henningsen) [#33772](https://github.com/nodejs/node/pull/33772) +* [[`5362fef3f5`](https://github.com/nodejs/node/commit/5362fef3f5)] - **(SEMVER-MINOR)** **src**: add public APIs to manage v8::TracingController (Anna Henningsen) [#33850](https://github.com/nodejs/node/pull/33850) +* [[`db2d1ca51b`](https://github.com/nodejs/node/commit/db2d1ca51b)] - **(SEMVER-MINOR)** **stream**: runtime deprecate Transform.\_transformState (Robert Nagy) [#32763](https://github.com/nodejs/node/pull/32763) +* [[`b6da77756e`](https://github.com/nodejs/node/commit/b6da77756e)] - **(SEMVER-MINOR)** **test**: stop testing --interpreted-frames-native-stack for s390x (Michaël Zasso) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`5cad007408`](https://github.com/nodejs/node/commit/5cad007408)] - **(SEMVER-MINOR)** **test**: fix test-zlib-unused-weak on V8 8.2 (Matheus Marchini) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`2c59f9bbe2`](https://github.com/nodejs/node/commit/2c59f9bbe2)] - **(SEMVER-MINOR)** **tools**: update V8 gypfiles for V8 8.3 (Michaël Zasso) [#32831](https://github.com/nodejs/node/pull/32831) +* [[`0ef6e0426f`](https://github.com/nodejs/node/commit/0ef6e0426f)] - **(SEMVER-MINOR)** **win**: allow skipping the supported platform check (João Reis) [#33176](https://github.com/nodejs/node/pull/33176) +* [[`4e42eb5e14`](https://github.com/nodejs/node/commit/4e42eb5e14)] - **(SEMVER-MINOR)** **worker**: add public method for marking objects as untransferable (Anna Henningsen) [#33979](https://github.com/nodejs/node/pull/33979) +* [[`4a37180b09`](https://github.com/nodejs/node/commit/4a37180b09)] - **(SEMVER-MINOR)** **worker**: emit `'messagerror'` events for failed deserialization (Anna Henningsen) [#33772](https://github.com/nodejs/node/pull/33772) +* [[`9692208a91`](https://github.com/nodejs/node/commit/9692208a91)] - **(SEMVER-MINOR)** **worker**: allow passing JS wrapper objects via postMessage (Anna Henningsen) [#33772](https://github.com/nodejs/node/pull/33772) +* [[`eaccf842eb`](https://github.com/nodejs/node/commit/eaccf842eb)] - **(SEMVER-MINOR)** **worker**: allow transferring/cloning generic BaseObjects (Anna Henningsen) [#33772](https://github.com/nodejs/node/pull/33772) +* [[`5b1fd10048`](https://github.com/nodejs/node/commit/5b1fd10048)] - **(SEMVER-MINOR)** **worker,fs**: make FileHandle transferable (Anna Henningsen) [#33772](https://github.com/nodejs/node/pull/33772) +* [[`c1f625fe1f`](https://github.com/nodejs/node/commit/c1f625fe1f)] - **(SEMVER-MINOR)** **zlib**: add `maxOutputLength` option (unknown) [#33516](https://github.com/nodejs/node/pull/33516) + +### Semver-Patch Commits + +* [[`ef05e1526a`](https://github.com/nodejs/node/commit/ef05e1526a)] - **async_hooks**: callback trampoline for MakeCallback (Stephen Belanger) [#33801](https://github.com/nodejs/node/pull/33801) +* [[`0eed22d6ed`](https://github.com/nodejs/node/commit/0eed22d6ed)] - **benchmark**: fix EventTarget benchmark (Brian White) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`bf56decc79`](https://github.com/nodejs/node/commit/bf56decc79)] - **benchmark**: fix async-resource benchmark (Anna Henningsen) [#33642](https://github.com/nodejs/node/pull/33642) +* [[`26269be510`](https://github.com/nodejs/node/commit/26269be510)] - **benchmark**: fixing http\_server\_for\_chunky\_client.js (Adrian Estrada) [#33271](https://github.com/nodejs/node/pull/33271) +* [[`c31d5145d9`](https://github.com/nodejs/node/commit/c31d5145d9)] - **buffer**: remove hoisted variable (Nikolai Vavilov) [#33470](https://github.com/nodejs/node/pull/33470) +* [[`43fd4746e9`](https://github.com/nodejs/node/commit/43fd4746e9)] - **build**: configure byte order for mips targets (Ben Noordhuis) [#33898](https://github.com/nodejs/node/pull/33898) +* [[`ebb2fb81fa`](https://github.com/nodejs/node/commit/ebb2fb81fa)] - **build**: add target specific build\_type variable (Daniel Bevenius) [#33925](https://github.com/nodejs/node/pull/33925) +* [[`e8f7670b77`](https://github.com/nodejs/node/commit/e8f7670b77)] - **build**: add LINT\_CPP\_FILES to checkimports check (Daniel Bevenius) [#33697](https://github.com/nodejs/node/pull/33697) +* [[`1355d35a61`](https://github.com/nodejs/node/commit/1355d35a61)] - **build**: output dots in "Build from tarball" action (Michaël Zasso) [#33696](https://github.com/nodejs/node/pull/33696) +* [[`153f5eda0e`](https://github.com/nodejs/node/commit/153f5eda0e)] - **build**: fix compiling addons with older versions of Node.js (Richard Lau) [#33688](https://github.com/nodejs/node/pull/33688) +* [[`7a4c689912`](https://github.com/nodejs/node/commit/7a4c689912)] - **build**: fix node.gyp config (gengjiawen) [#33685](https://github.com/nodejs/node/pull/33685) +* [[`1f7a65529d`](https://github.com/nodejs/node/commit/1f7a65529d)] - **build**: add --v8-lite-mode flag (Maciej Kacper Jagiełło) [#33541](https://github.com/nodejs/node/pull/33541) +* [[`3ac05b75ca`](https://github.com/nodejs/node/commit/3ac05b75ca)] - **build**: zlib build error on Windows on Arm (Richard Townsend) [#33511](https://github.com/nodejs/node/pull/33511) +* [[`fc032247e0`](https://github.com/nodejs/node/commit/fc032247e0)] - **build**: fix GetCurrentThreadStackLimits error on Windows on Arm (Richard Townsend) [#33511](https://github.com/nodejs/node/pull/33511) +* [[`e393e879cf`](https://github.com/nodejs/node/commit/e393e879cf)] - **build**: fix python-version selection with actions (Richard Lau) [#33589](https://github.com/nodejs/node/pull/33589) +* [[`8ed25eda60`](https://github.com/nodejs/node/commit/8ed25eda60)] - **build**: fix inability to detect correct python command in configure (Eli Schwartz) [#32925](https://github.com/nodejs/node/pull/32925) +* [[`8b887c4462`](https://github.com/nodejs/node/commit/8b887c4462)] - **build**: fix makefile script on windows (Thomas) [#33136](https://github.com/nodejs/node/pull/33136) +* [[`85ce30fe57`](https://github.com/nodejs/node/commit/85ce30fe57)] - **build**: run full test suite in ASAN action (Anna Henningsen) [#33170](https://github.com/nodejs/node/pull/33170) +* [[`71c4d9174e`](https://github.com/nodejs/node/commit/71c4d9174e)] - **build,win**: add support for MSVC cross-compilation (Richard Townsend) [#32867](https://github.com/nodejs/node/pull/32867) +* [[`ac7946eb08`](https://github.com/nodejs/node/commit/ac7946eb08)] - **build,win**: add support for MSVC cross-compilation (Richard Townsend) [#32867](https://github.com/nodejs/node/pull/32867) +* [[`22b5ec19a2`](https://github.com/nodejs/node/commit/22b5ec19a2)] - **cli**: support --experimental-top-level-await in NODE\_OPTIONS (Dan Fabulich) [#33495](https://github.com/nodejs/node/pull/33495) +* [[`0a7f13e26b`](https://github.com/nodejs/node/commit/0a7f13e26b)] - **configure**: account for CLANG\_VENDOR when checking for llvm version (Nathan Blair) [#33860](https://github.com/nodejs/node/pull/33860) +* [[`a6a74ae1d5`](https://github.com/nodejs/node/commit/a6a74ae1d5)] - **console**: name console functions appropriately (Ruben Bridgewater) [#33524](https://github.com/nodejs/node/pull/33524) +* [[`9d24f71d45`](https://github.com/nodejs/node/commit/9d24f71d45)] - **console**: mark special console properties as non-enumerable (Ruben Bridgewater) [#33524](https://github.com/nodejs/node/pull/33524) +* [[`bce99867f7`](https://github.com/nodejs/node/commit/bce99867f7)] - **console**: remove dead code (Ruben Bridgewater) [#33524](https://github.com/nodejs/node/pull/33524) +* [[`134ed0eea3`](https://github.com/nodejs/node/commit/134ed0eea3)] - **crypto**: fix wrong error message (Ben Bucksch) [#33482](https://github.com/nodejs/node/pull/33482) +* [[`5957afc31a`](https://github.com/nodejs/node/commit/5957afc31a)] - **deps**: V8: cherry-pick 767e65f945e7 (Gus Caplan) [#33859](https://github.com/nodejs/node/pull/33859) +* [[`162092ea2a`](https://github.com/nodejs/node/commit/162092ea2a)] - **deps**: V8: cherry-pick eec10a2fd8fa (Stephen Belanger) [#33778](https://github.com/nodejs/node/pull/33778) +* [[`499c7402b1`](https://github.com/nodejs/node/commit/499c7402b1)] - **deps**: V8: cherry-pick 4e1bf2bc92bd (Milad Farazmand) [#33702](https://github.com/nodejs/node/pull/33702) +* [[`0524c7ad5d`](https://github.com/nodejs/node/commit/0524c7ad5d)] - **deps**: V8: cherry-pick b5939c758924 (Milad Farazmand) [#33702](https://github.com/nodejs/node/pull/33702) +* [[`7ad6cfa005`](https://github.com/nodejs/node/commit/7ad6cfa005)] - **deps**: V8: backport 22014de00115 (Joyee Cheung) [#33300](https://github.com/nodejs/node/pull/33300) +* [[`817befde11`](https://github.com/nodejs/node/commit/817befde11)] - **deps**: V8: backport bb9f0c2b2fe9 (Joyee Cheung) [#33300](https://github.com/nodejs/node/pull/33300) +* [[`8f82692999`](https://github.com/nodejs/node/commit/8f82692999)] - **deps**: V8: backport ea0719b8ed08 (Joyee Cheung) [#33300](https://github.com/nodejs/node/pull/33300) +* [[`773d76ea04`](https://github.com/nodejs/node/commit/773d76ea04)] - **deps**: uvwasi: cherry-pick 9e75217 (Colin Ihrig) [#33521](https://github.com/nodejs/node/pull/33521) +* [[`748720e7b6`](https://github.com/nodejs/node/commit/748720e7b6)] - **deps**: V8: cherry-pick 548f6c81d424 (Dominykas Blyžė) [#33484](https://github.com/nodejs/node/pull/33484) +* [[`b0bce9b2a4`](https://github.com/nodejs/node/commit/b0bce9b2a4)] - **deps**: update node-inspect to v2.0.0 (Jan Krems) [#33447](https://github.com/nodejs/node/pull/33447) +* [[`ac459b34e7`](https://github.com/nodejs/node/commit/ac459b34e7)] - **deps**: V8: cherry-pick fa3e37e511ee (Anna Henningsen) [#32885](https://github.com/nodejs/node/pull/32885) +* [[`2bc79f5b50`](https://github.com/nodejs/node/commit/2bc79f5b50)] - **deps**: V8: cherry-pick 2db93c023379 (Anna Henningsen) [#32885](https://github.com/nodejs/node/pull/32885) +* [[`8d47e8bf7b`](https://github.com/nodejs/node/commit/8d47e8bf7b)] - **deps**: update to uvwasi 0.0.9 (Colin Ihrig) [#33445](https://github.com/nodejs/node/pull/33445) +* [[`9d6fd4599d`](https://github.com/nodejs/node/commit/9d6fd4599d)] - **deps**: upgrade to libuv 1.38.0 (Colin Ihrig) [#33446](https://github.com/nodejs/node/pull/33446) +* [[`33a662ad2d`](https://github.com/nodejs/node/commit/33a662ad2d)] - **deps**: update icu to include tzdata2020a (Shelley Vohr) [#33362](https://github.com/nodejs/node/pull/33362) +* [[`f151bde312`](https://github.com/nodejs/node/commit/f151bde312)] - **(SEMVER-MINOR)** **dgram**: allow typed arrays in .send() (Sarat Addepalli) [#22413](https://github.com/nodejs/node/pull/22413) +* [[`d4442b15bf`](https://github.com/nodejs/node/commit/d4442b15bf)] - **dns**: make dns.Resolver timeout configurable (Ben Noordhuis) [#33472](https://github.com/nodejs/node/pull/33472) +* [[`eb55d9e4b1`](https://github.com/nodejs/node/commit/eb55d9e4b1)] - **dns**: use ternary operator simplify statement (Wenning Zhang) [#33234](https://github.com/nodejs/node/pull/33234) +* [[`d61de303c9`](https://github.com/nodejs/node/commit/d61de303c9)] - **doc**: specify maxHeaderCount alias for maxHeaderListPairs (Pranshu Srivastava) [#33519](https://github.com/nodejs/node/pull/33519) +* [[`4323346f5a`](https://github.com/nodejs/node/commit/4323346f5a)] - **doc**: add allowed info strings to style guide (Derek Lewis) [#34024](https://github.com/nodejs/node/pull/34024) +* [[`0dbad26db4`](https://github.com/nodejs/node/commit/0dbad26db4)] - **doc**: fix lexical sorting of bottom-references in http doc (Pranshu Srivastava) [#34007](https://github.com/nodejs/node/pull/34007) +* [[`ec07e61f6a`](https://github.com/nodejs/node/commit/ec07e61f6a)] - **doc**: clarify thread-safe function references (legendecas) [#33871](https://github.com/nodejs/node/pull/33871) +* [[`5a4dcfcf4c`](https://github.com/nodejs/node/commit/5a4dcfcf4c)] - **doc**: use npm team for npm upgrades in collaborator guide (Rich Trott) [#33999](https://github.com/nodejs/node/pull/33999) +* [[`319707add2`](https://github.com/nodejs/node/commit/319707add2)] - **doc**: correct default values in http2 docs (Rich Trott) [#33997](https://github.com/nodejs/node/pull/33997) +* [[`b4d0eebe7c`](https://github.com/nodejs/node/commit/b4d0eebe7c)] - **doc**: use a single space between sentences (Rich Trott) [#33995](https://github.com/nodejs/node/pull/33995) +* [[`24105a7f44`](https://github.com/nodejs/node/commit/24105a7f44)] - **doc**: piping from async generators using pipeline() (WilliamConnatser) [#33992](https://github.com/nodejs/node/pull/33992) +* [[`9590d81349`](https://github.com/nodejs/node/commit/9590d81349)] - **doc**: revise text in dns module documentation introduction (Rich Trott) [#33986](https://github.com/nodejs/node/pull/33986) +* [[`ed26e8e2fb`](https://github.com/nodejs/node/commit/ed26e8e2fb)] - **doc**: update fs.md (Shakil-Shahadat) [#33820](https://github.com/nodejs/node/pull/33820) +* [[`6dc541778e`](https://github.com/nodejs/node/commit/6dc541778e)] - **doc**: warn that tls.connect() doesn't set SNI (Alba Mendez) [#33855](https://github.com/nodejs/node/pull/33855) +* [[`d9c78ac270`](https://github.com/nodejs/node/commit/d9c78ac270)] - **doc**: fix lexical sorting of bottom-references in dns doc (Rich Trott) [#33987](https://github.com/nodejs/node/pull/33987) +* [[`98228b25af`](https://github.com/nodejs/node/commit/98228b25af)] - **doc**: change "GitHub Repo" to "Code repository" (Rich Trott) [#33985](https://github.com/nodejs/node/pull/33985) +* [[`645cd481e9`](https://github.com/nodejs/node/commit/645cd481e9)] - **doc**: use Class: consistently (Rich Trott) [#33978](https://github.com/nodejs/node/pull/33978) +* [[`72e2fd315e`](https://github.com/nodejs/node/commit/72e2fd315e)] - **doc**: update WASM code sample (Pragyan Das) [#33626](https://github.com/nodejs/node/pull/33626) +* [[`894ec7d5c6`](https://github.com/nodejs/node/commit/894ec7d5c6)] - **doc**: standardize on sentence case for headers (Rich Trott) [#33889](https://github.com/nodejs/node/pull/33889) +* [[`61de26a2f3`](https://github.com/nodejs/node/commit/61de26a2f3)] - **doc**: link readable.\_read in stream.md (Pranshu Srivastava) [#33767](https://github.com/nodejs/node/pull/33767) +* [[`76fe2a93a9`](https://github.com/nodejs/node/commit/76fe2a93a9)] - **doc**: specify default encoding in writable.write (Pranshu Srivastava) [#33765](https://github.com/nodejs/node/pull/33765) +* [[`2427d6544b`](https://github.com/nodejs/node/commit/2427d6544b)] - **doc**: move --force-context-aware option in cli.md (Daniel Bevenius) [#33823](https://github.com/nodejs/node/pull/33823) +* [[`fdaf0ca550`](https://github.com/nodejs/node/commit/fdaf0ca550)] - **doc**: add snippet for AsyncResource and EE integration (Andrey Pechkurov) [#33751](https://github.com/nodejs/node/pull/33751) +* [[`8f5ac3865c`](https://github.com/nodejs/node/commit/8f5ac3865c)] - **doc**: use single quotes in --tls-cipher-list (Daniel Bevenius) [#33709](https://github.com/nodejs/node/pull/33709) +* [[`922c13c6bb`](https://github.com/nodejs/node/commit/922c13c6bb)] - **doc**: fix misc. mislabeled code block info strings (Derek Lewis) [#33548](https://github.com/nodejs/node/pull/33548) +* [[`114d77e30b`](https://github.com/nodejs/node/commit/114d77e30b)] - **doc**: standardize constructor doc header layout (Rich Trott) [#33781](https://github.com/nodejs/node/pull/33781) +* [[`b10d20385e`](https://github.com/nodejs/node/commit/b10d20385e)] - **doc**: update V8 inspector example (Colin Ihrig) [#33758](https://github.com/nodejs/node/pull/33758) +* [[`785760448b`](https://github.com/nodejs/node/commit/785760448b)] - **doc**: fix linting in doc-style-guide.md (Pranshu Srivastava) [#33787](https://github.com/nodejs/node/pull/33787) +* [[`2288840a8f`](https://github.com/nodejs/node/commit/2288840a8f)] - **doc**: remove "currently" from repl.md (Rich Trott) [#33756](https://github.com/nodejs/node/pull/33756) +* [[`cc0f827182`](https://github.com/nodejs/node/commit/cc0f827182)] - **doc**: remove "currently" from events.md (Rich Trott) [#33756](https://github.com/nodejs/node/pull/33756) +* [[`4a738e6462`](https://github.com/nodejs/node/commit/4a738e6462)] - **doc**: remove "currently" from vm.md (Rich Trott) [#33756](https://github.com/nodejs/node/pull/33756) +* [[`bb29a8177f`](https://github.com/nodejs/node/commit/bb29a8177f)] - **doc**: remove "currently" from addons.md (Rich Trott) [#33756](https://github.com/nodejs/node/pull/33756) +* [[`f0597d9a6e`](https://github.com/nodejs/node/commit/f0597d9a6e)] - **doc**: remove "currently" from util.md (Rich Trott) [#33756](https://github.com/nodejs/node/pull/33756) +* [[`095efac2ef`](https://github.com/nodejs/node/commit/095efac2ef)] - **doc**: add formatting for version numbers to doc-style-guide.md (Rich Trott) [#33755](https://github.com/nodejs/node/pull/33755) +* [[`843ab3eb94`](https://github.com/nodejs/node/commit/843ab3eb94)] - **doc**: change "pre Node.js v0.10" to "prior to Node.js 0.10" (Rich Trott) [#33754](https://github.com/nodejs/node/pull/33754) +* [[`b565897996`](https://github.com/nodejs/node/commit/b565897996)] - **doc**: remove default parameter value from header (Rich Trott) [#33752](https://github.com/nodejs/node/pull/33752) +* [[`ebf2378731`](https://github.com/nodejs/node/commit/ebf2378731)] - **doc**: fix typo in cli.md for report-dir (AshCripps) [#33725](https://github.com/nodejs/node/pull/33725) +* [[`16b69818ba`](https://github.com/nodejs/node/commit/16b69818ba)] - **doc**: remove shell dollar signs without output (Nick Schonning) [#33692](https://github.com/nodejs/node/pull/33692) +* [[`b3d500f949`](https://github.com/nodejs/node/commit/b3d500f949)] - **doc**: add lint disabling comment for collaborator list (Rich Trott) [#33719](https://github.com/nodejs/node/pull/33719) +* [[`61bb789fa0`](https://github.com/nodejs/node/commit/61bb789fa0)] - **doc**: use consistent Default: in events (Colin Ihrig) [#33678](https://github.com/nodejs/node/pull/33678) +* [[`1e4edd8d75`](https://github.com/nodejs/node/commit/1e4edd8d75)] - **doc**: remove "it is important" (Colin Ihrig) [#33678](https://github.com/nodejs/node/pull/33678) +* [[`cb8b9ec98a`](https://github.com/nodejs/node/commit/cb8b9ec98a)] - **doc**: fix urls to avoid redirection (sapics) [#33614](https://github.com/nodejs/node/pull/33614) +* [[`c184929975`](https://github.com/nodejs/node/commit/c184929975)] - **doc**: improve buffer.md a tiny bit (Tom Nagle) [#33547](https://github.com/nodejs/node/pull/33547) +* [[`6d25b5753a`](https://github.com/nodejs/node/commit/6d25b5753a)] - **doc**: normalize Markdown code block info strings (Derek Lewis) [#33542](https://github.com/nodejs/node/pull/33542) +* [[`e7c3890901`](https://github.com/nodejs/node/commit/e7c3890901)] - **doc**: normalize JavaScript code block info strings (Derek Lewis) [#33531](https://github.com/nodejs/node/pull/33531) +* [[`352adcb437`](https://github.com/nodejs/node/commit/352adcb437)] - **doc**: outline when origin is set to unhandledRejection (Ruben Bridgewater) [#33530](https://github.com/nodejs/node/pull/33530) +* [[`94177dae8e`](https://github.com/nodejs/node/commit/94177dae8e)] - **doc**: add --experimental-top-level-await to man page (Colin Ihrig) [#33529](https://github.com/nodejs/node/pull/33529) +* [[`8e3a0d7773`](https://github.com/nodejs/node/commit/8e3a0d7773)] - **doc**: update ```txt ```fandamental and ```raw code blocks (Zeke Sikelianos) [#33028](https://github.com/nodejs/node/pull/33028) +* [[`4cc391b495`](https://github.com/nodejs/node/commit/4cc391b495)] - **doc**: normalize shell code block info strings (Derek Lewis) [#33486](https://github.com/nodejs/node/pull/33486) +* [[`24ada7acd4`](https://github.com/nodejs/node/commit/24ada7acd4)] - **doc**: normalize C code block info strings (Derek Lewis) [#33507](https://github.com/nodejs/node/pull/33507) +* [[`8c04e61f16`](https://github.com/nodejs/node/commit/8c04e61f16)] - **doc**: normalize Bash code block info strings (Derek Lewis) [#33510](https://github.com/nodejs/node/pull/33510) +* [[`7c87fc1c48`](https://github.com/nodejs/node/commit/7c87fc1c48)] - **doc**: correct tls.rootCertificates to match implementation (Eric Bickle) [#33313](https://github.com/nodejs/node/pull/33313) +* [[`0c2b7c0adf`](https://github.com/nodejs/node/commit/0c2b7c0adf)] - **doc**: fix Buffer.from(object) documentation (Nikolai Vavilov) [#33327](https://github.com/nodejs/node/pull/33327) +* [[`de608c3124`](https://github.com/nodejs/node/commit/de608c3124)] - **doc**: fix typo in pathToFileURL example (Antoine du HAMEL) [#33418](https://github.com/nodejs/node/pull/33418) +* [[`23cf39ab78`](https://github.com/nodejs/node/commit/23cf39ab78)] - **doc**: eliminate dead space in API section's sidebar (John Gardner) [#33469](https://github.com/nodejs/node/pull/33469) +* [[`95e7a80cbf`](https://github.com/nodejs/node/commit/95e7a80cbf)] - **doc**: mention --experimental-top-level-await flag (dfabulich) [#33473](https://github.com/nodejs/node/pull/33473) +* [[`64410f206e`](https://github.com/nodejs/node/commit/64410f206e)] - **doc**: normalize C++ code block info strings (Derek Lewis) [#33483](https://github.com/nodejs/node/pull/33483) +* [[`c8f79d80a4`](https://github.com/nodejs/node/commit/c8f79d80a4)] - **doc**: fixed a grammatical error in path.md (Deep310) [#33489](https://github.com/nodejs/node/pull/33489) +* [[`500bad1103`](https://github.com/nodejs/node/commit/500bad1103)] - **doc**: correct CommonJS self-resolve spec (Guy Bedford) [#33391](https://github.com/nodejs/node/pull/33391) +* [[`4e74f050a7`](https://github.com/nodejs/node/commit/4e74f050a7)] - **doc**: fix readline key binding documentation (Ruben Bridgewater) [#33361](https://github.com/nodejs/node/pull/33361) +* [[`7c553cd4f6`](https://github.com/nodejs/node/commit/7c553cd4f6)] - **doc**: claim ABI version 85 for Electron 11 (Shelley Vohr) [#33375](https://github.com/nodejs/node/pull/33375) +* [[`4cc5e9668f`](https://github.com/nodejs/node/commit/4cc5e9668f)] - **doc**: document module.path (Antoine du Hamel) [#33323](https://github.com/nodejs/node/pull/33323) +* [[`c1fe152132`](https://github.com/nodejs/node/commit/c1fe152132)] - **doc**: add fs.open() multiple constants example (Ethan Arrowood) [#33281](https://github.com/nodejs/node/pull/33281) +* [[`b02cfef510`](https://github.com/nodejs/node/commit/b02cfef510)] - **doc**: fix typos in handle scope descriptions (Tobias Nießen) [#33267](https://github.com/nodejs/node/pull/33267) +* [[`d4e871424f`](https://github.com/nodejs/node/commit/d4e871424f)] - **doc**: update function description for `decipher.setAAD` (Jonathan Buhacoff) [#33095](https://github.com/nodejs/node/pull/33095) +* [[`e2484b24cb`](https://github.com/nodejs/node/commit/e2484b24cb)] - **doc**: add comment about highWaterMark limit (Benjamin Gruenbaum) [#33432](https://github.com/nodejs/node/pull/33432) +* [[`b8c88891a6`](https://github.com/nodejs/node/commit/b8c88891a6)] - **doc**: clarify about the Node.js-only extensions in perf\_hooks (Joyee Cheung) [#33199](https://github.com/nodejs/node/pull/33199) +* [[`d1efdb29b4`](https://github.com/nodejs/node/commit/d1efdb29b4)] - **doc**: document ICU time zone data update process (Andrew Paprocki) [#30364](https://github.com/nodejs/node/pull/30364) +* [[`1d918b67ca`](https://github.com/nodejs/node/commit/1d918b67ca)] - **doc,stream**: split finish and end events into separate entries (Rich Trott) [#33881](https://github.com/nodejs/node/pull/33881) +* [[`af9fb5969d`](https://github.com/nodejs/node/commit/af9fb5969d)] - **doc,tools**: properly syntax highlight API ref docs (Derek Lewis) [#33442](https://github.com/nodejs/node/pull/33442) +* [[`122d2b5c02`](https://github.com/nodejs/node/commit/122d2b5c02)] - **domain**: remove native domain code (Stephen Belanger) [#33801](https://github.com/nodejs/node/pull/33801) +* [[`e060060aa2`](https://github.com/nodejs/node/commit/e060060aa2)] - **errors**: fully inspect errors on exit (Ruben Bridgewater) [#33523](https://github.com/nodejs/node/pull/33523) +* [[`aca07f428e`](https://github.com/nodejs/node/commit/aca07f428e)] - **errors**: skip fatal error highlighting on windows (Thomas) [#33132](https://github.com/nodejs/node/pull/33132) +* [[`50adccadc1`](https://github.com/nodejs/node/commit/50adccadc1)] - **esm**: fix loader hooks doc annotations (Derek Lewis) [#33563](https://github.com/nodejs/node/pull/33563) +* [[`5bef20c2fc`](https://github.com/nodejs/node/commit/5bef20c2fc)] - **esm**: share package.json cache between ESM and CJS loaders (Kirill Shatskiy) [#33229](https://github.com/nodejs/node/pull/33229) +* [[`828d5d22eb`](https://github.com/nodejs/node/commit/828d5d22eb)] - **esm**: doc & validate source values for formats (Bradley Farias) [#32202](https://github.com/nodejs/node/pull/32202) +* [[`2724514f53`](https://github.com/nodejs/node/commit/2724514f53)] - **event**: cancelBubble is a property (Benjamin Gruenbaum) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`c9dec0c0f0`](https://github.com/nodejs/node/commit/c9dec0c0f0)] - **event**: cancelBubble is a property (Benjamin Gruenbaum) [#33613](https://github.com/nodejs/node/pull/33613) +* [[`0c32920a82`](https://github.com/nodejs/node/commit/0c32920a82)] - **events**: fix add-remove-add case in EventTarget (Anna Henningsen) [#34056](https://github.com/nodejs/node/pull/34056) +* [[`c34f4743c4`](https://github.com/nodejs/node/commit/c34f4743c4)] - **events**: improve argument handling, start passive (James M Snell) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`ea1a2d7bc9`](https://github.com/nodejs/node/commit/ea1a2d7bc9)] - **events**: support dispatching event from event (James M Snell) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`5ce153365e`](https://github.com/nodejs/node/commit/5ce153365e)] - **events**: add event-target tests (James M Snell) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`91b6c093b1`](https://github.com/nodejs/node/commit/91b6c093b1)] - **events**: support event handlers (Benjamin Gruenbaum) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`b392fdd4aa`](https://github.com/nodejs/node/commit/b392fdd4aa)] - **events**: expose Event statics (Benjamin Gruenbaum) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`cd3a1429a3`](https://github.com/nodejs/node/commit/cd3a1429a3)] - **events**: Handle a range of this values for dispatchEvent (Zirak) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`aa1cb3f186`](https://github.com/nodejs/node/commit/aa1cb3f186)] - **events**: fix EventTarget support (Benjamin Gruenbaum) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`0f0f4e0c40`](https://github.com/nodejs/node/commit/0f0f4e0c40)] - **events**: fix depth in customInspectSymbol and clean up (Denys Otrishko) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`6ce3293cc4`](https://github.com/nodejs/node/commit/6ce3293cc4)] - **events**: use internal/validators in event\_target.js (Denys Otrishko) [#34015](https://github.com/nodejs/node/pull/34015) +* [[`eb01214ab2`](https://github.com/nodejs/node/commit/eb01214ab2)] - **events**: use property, primordials (Benjamin Gruenbaum) [#33775](https://github.com/nodejs/node/pull/33775) +* [[`667195ef8f`](https://github.com/nodejs/node/commit/667195ef8f)] - **events**: improve listeners() performance (Brian White) [#33863](https://github.com/nodejs/node/pull/33863) +* [[`f1b0291d82`](https://github.com/nodejs/node/commit/f1b0291d82)] - **events**: lazy load perf\_hooks for EventTarget (James M Snell) [#33717](https://github.com/nodejs/node/pull/33717) +* [[`c291ce599c`](https://github.com/nodejs/node/commit/c291ce599c)] - **events**: improve arrayClone performance (Brian White) [#33774](https://github.com/nodejs/node/pull/33774) +* [[`a3ef2b7335`](https://github.com/nodejs/node/commit/a3ef2b7335)] - **events**: support useCapture boolean (Benjamin Gruenbaum) [#33618](https://github.com/nodejs/node/pull/33618) +* [[`2e6eceac5c`](https://github.com/nodejs/node/commit/2e6eceac5c)] - **events**: set target property to null (Benjamin Gruenbaum) [#33615](https://github.com/nodejs/node/pull/33615) +* [[`bc2e821ccc`](https://github.com/nodejs/node/commit/bc2e821ccc)] - **events**: deal with no argument case (Benjamin Gruenbaum) [#33611](https://github.com/nodejs/node/pull/33611) +* [[`e7bce2e03a`](https://github.com/nodejs/node/commit/e7bce2e03a)] - **events**: deal with Symbol() passed to event constructor (Benjamin Gruenbaum) [#33612](https://github.com/nodejs/node/pull/33612) +* [[`27c90efce0`](https://github.com/nodejs/node/commit/27c90efce0)] - **events**: variable originalListener is useless (fuxingZhang) [#33596](https://github.com/nodejs/node/pull/33596) +* [[`2a29ced050`](https://github.com/nodejs/node/commit/2a29ced050)] - **events**: fix event-target enumerable keys (Benjamin Gruenbaum) [#33616](https://github.com/nodejs/node/pull/33616) +* [[`f3d0d3089d`](https://github.com/nodejs/node/commit/f3d0d3089d)] - **events**: add tests, better toString (Benjamin Gruenbaum) [#33622](https://github.com/nodejs/node/pull/33622) +* [[`95cbfcec99`](https://github.com/nodejs/node/commit/95cbfcec99)] - **fs**: fix readdir failure when libuv returns UV\_DIRENT\_UNKNOWN (Kirill Shatskiy) [#33395](https://github.com/nodejs/node/pull/33395) +* [[`b894df860a`](https://github.com/nodejs/node/commit/b894df860a)] - **fs**: fix realpath inode link caching (Denys Otrishko) [#33945](https://github.com/nodejs/node/pull/33945) +* [[`b280c86213`](https://github.com/nodejs/node/commit/b280c86213)] - **fs**: support util.promisify for fs.readv (Lucas Holmquist) [#33590](https://github.com/nodejs/node/pull/33590) +* [[`2c03661860`](https://github.com/nodejs/node/commit/2c03661860)] - **fs**: unify style in preprocessSymlinkDestination (Bartosz Sosnowski) [#33496](https://github.com/nodejs/node/pull/33496) +* [[`b675ea0272`](https://github.com/nodejs/node/commit/b675ea0272)] - **fs**: replace checkPosition with validateInteger (rickyes) [#33277](https://github.com/nodejs/node/pull/33277) +* [[`a90b96f338`](https://github.com/nodejs/node/commit/a90b96f338)] - **fs**: refactor the import of internalUtil (rickyes) [#33296](https://github.com/nodejs/node/pull/33296) +* [[`a0a61b81a5`](https://github.com/nodejs/node/commit/a0a61b81a5)] - **http**: used already defined validator for boolean check (Yash Ladha) [#33731](https://github.com/nodejs/node/pull/33731) +* [[`6dbd63c8ba`](https://github.com/nodejs/node/commit/6dbd63c8ba)] - ***Revert*** "**http**: set IncomingMessage.destroyed" (Robert Nagy) [#33686](https://github.com/nodejs/node/pull/33686) +* [[`feb6e1ffb8`](https://github.com/nodejs/node/commit/feb6e1ffb8)] - **http**: don't throw on `Uint8Array`s for `http.ServerResponse#write` (Pranshu Srivastava) [#33155](https://github.com/nodejs/node/pull/33155) +* [[`bcdf7e94be`](https://github.com/nodejs/node/commit/bcdf7e94be)] - **http**: simplify Agent initialization (himself65) [#33551](https://github.com/nodejs/node/pull/33551) +* [[`c2aad813c0`](https://github.com/nodejs/node/commit/c2aad813c0)] - **http**: tidy up exposure of header validation (Osher) [#33371](https://github.com/nodejs/node/pull/33371) +* [[`0752d2309f`](https://github.com/nodejs/node/commit/0752d2309f)] - **http2**: always call callback on Http2ServerResponse#end (Pranshu Srivastava) [#33911](https://github.com/nodejs/node/pull/33911) +* [[`d8aeafb4bf`](https://github.com/nodejs/node/commit/d8aeafb4bf)] - **http2**: add writable\* properties to compat api (Pranshu Srivastava) [#33506](https://github.com/nodejs/node/pull/33506) +* [[`0b34c4fb75`](https://github.com/nodejs/node/commit/0b34c4fb75)] - **http2**: add type checks for Http2ServerResponse.end (Pranshu Srivastava) [#33146](https://github.com/nodejs/node/pull/33146) +* [[`cc74f3c67c`](https://github.com/nodejs/node/commit/cc74f3c67c)] - **http2**: use `Object.create(null)` for `getHeaders` (Pranshu Srivastava) [#33188](https://github.com/nodejs/node/pull/33188) +* [[`8457033d83`](https://github.com/nodejs/node/commit/8457033d83)] - **http2**: reuse .\_onTimeout() in Http2Session and Http2Stream classes (rickyes) [#33354](https://github.com/nodejs/node/pull/33354) +* [[`c972ce200e`](https://github.com/nodejs/node/commit/c972ce200e)] - **http2**: comment on usage of `Object.create(null)` (Pranshu Srivastava) [#33183](https://github.com/nodejs/node/pull/33183) +* [[`e58f14fee7`](https://github.com/nodejs/node/commit/e58f14fee7)] - **inspector**: drop 'chrome-' from inspector url (Colin Ihrig) [#33758](https://github.com/nodejs/node/pull/33758) +* [[`42df2baa21`](https://github.com/nodejs/node/commit/42df2baa21)] - **inspector**: throw error when activating an already active inspector (Joyee Cheung) [#33015](https://github.com/nodejs/node/pull/33015) +* [[`c9489f2f23`](https://github.com/nodejs/node/commit/c9489f2f23)] - **internal**: rename error-serdes for consistency (Evan Lucas) [#33793](https://github.com/nodejs/node/pull/33793) +* [[`b7690da65e`](https://github.com/nodejs/node/commit/b7690da65e)] - **lib**: improve debuglog() performance (Brian White) [#32260](https://github.com/nodejs/node/pull/32260) +* [[`b6ef6c8476`](https://github.com/nodejs/node/commit/b6ef6c8476)] - **lib**: remove manual exception handling in queueMicrotask (Gus Caplan) [#33859](https://github.com/nodejs/node/pull/33859) +* [[`ec01867623`](https://github.com/nodejs/node/commit/ec01867623)] - **lib**: replace charCodeAt with fixed Unicode (rickyes) [#32758](https://github.com/nodejs/node/pull/32758) +* [[`76123b9ae7`](https://github.com/nodejs/node/commit/76123b9ae7)] - **lib**: add Int16Array primordials (Sebastien Ahkrin) [#31205](https://github.com/nodejs/node/pull/31205) +* [[`59d435ed4d`](https://github.com/nodejs/node/commit/59d435ed4d)] - **lib**: update TODO comments (Ruben Bridgewater) [#33361](https://github.com/nodejs/node/pull/33361) +* [[`e62a8b5007`](https://github.com/nodejs/node/commit/e62a8b5007)] - **lib**: update executionAsyncId/triggerAsyncId comment (Daniel Bevenius) [#33396](https://github.com/nodejs/node/pull/33396) +* [[`4ae4073abf`](https://github.com/nodejs/node/commit/4ae4073abf)] - **lib,src**: remove cpu profiler idle notifier (Ben Noordhuis) [#34010](https://github.com/nodejs/node/pull/34010) +* [[`fc7cad828b`](https://github.com/nodejs/node/commit/fc7cad828b)] - **meta**: introduce codeowners again (James M Snell) [#33895](https://github.com/nodejs/node/pull/33895) +* [[`b162c532d7`](https://github.com/nodejs/node/commit/b162c532d7)] - **meta**: fix a typo in the flaky test template (Colin Ihrig) [#33677](https://github.com/nodejs/node/pull/33677) +* [[`148c1f1344`](https://github.com/nodejs/node/commit/148c1f1344)] - **meta**: wrap flaky test template at 80 characters (Colin Ihrig) [#33677](https://github.com/nodejs/node/pull/33677) +* [[`2aa6469bea`](https://github.com/nodejs/node/commit/2aa6469bea)] - **meta**: add flaky test issue template (Ash Cripps) [#33500](https://github.com/nodejs/node/pull/33500) +* [[`84a5e6cec8`](https://github.com/nodejs/node/commit/84a5e6cec8)] - **module**: fix error message about importing names from cjs (Fábio Santos) [#33882](https://github.com/nodejs/node/pull/33882) +* [[`8c9e3a9dfb`](https://github.com/nodejs/node/commit/8c9e3a9dfb)] - **module**: remove dynamicInstantiate loader hook (Jan Krems) [#33501](https://github.com/nodejs/node/pull/33501) +* [[`53dbb9d232`](https://github.com/nodejs/node/commit/53dbb9d232)] - **n-api**: add version to wasm registration (Gus Caplan) [#34045](https://github.com/nodejs/node/pull/34045) +* [[`e924439d96`](https://github.com/nodejs/node/commit/e924439d96)] - **n-api**: document nextTick timing in callbacks (Mathias Buus) [#33804](https://github.com/nodejs/node/pull/33804) +* [[`524daf89a1`](https://github.com/nodejs/node/commit/524daf89a1)] - **n-api**: ensure scope present for finalization (Michael Dawson) [#33508](https://github.com/nodejs/node/pull/33508) +* [[`e83642f73d`](https://github.com/nodejs/node/commit/e83642f73d)] - **n-api**: remove `napi\_env::CallIntoModuleThrow` (Gabriel Schulhof) [#33570](https://github.com/nodejs/node/pull/33570) +* [[`4c235b07ae`](https://github.com/nodejs/node/commit/4c235b07ae)] - ***Revert*** "**n-api**: detect deadlocks in thread-safe function" (Anna Henningsen) [#33453](https://github.com/nodejs/node/pull/33453) +* [[`022dcebcd8`](https://github.com/nodejs/node/commit/022dcebcd8)] - **napi**: add \_\_wasm32\_\_ guards (Gus Caplan) [#33597](https://github.com/nodejs/node/pull/33597) +* [[`164461edfd`](https://github.com/nodejs/node/commit/164461edfd)] - **net**: refactor check for Windows (rickyes) [#33497](https://github.com/nodejs/node/pull/33497) +* [[`e0b0ddd257`](https://github.com/nodejs/node/commit/e0b0ddd257)] - **querystring**: fix stringify for empty array (sapics) [#33918](https://github.com/nodejs/node/pull/33918) +* [[`e8572e7070`](https://github.com/nodejs/node/commit/e8572e7070)] - **querystring**: improve stringify() performance (Brian White) [#33669](https://github.com/nodejs/node/pull/33669) +* [[`011fe1d443`](https://github.com/nodejs/node/commit/011fe1d443)] - **repl**: add builtinModules (Ruben Bridgewater) [#33295](https://github.com/nodejs/node/pull/33295) +* [[`71d6599191`](https://github.com/nodejs/node/commit/71d6599191)] - **repl**: simplify repl autocompletion (Ruben Bridgewater) [#33450](https://github.com/nodejs/node/pull/33450) +* [[`1330cfc2a9`](https://github.com/nodejs/node/commit/1330cfc2a9)] - **repl**: support optional chaining during autocompletion (Ruben Bridgewater) [#33450](https://github.com/nodejs/node/pull/33450) +* [[`9760c6caff`](https://github.com/nodejs/node/commit/9760c6caff)] - **src**: add errorProperties on process.report (himself65) [#28426](https://github.com/nodejs/node/pull/28426) +* [[`da81930b13`](https://github.com/nodejs/node/commit/da81930b13)] - **src**: tolerate EPERM returned from tcsetattr (patr0nus) [#33944](https://github.com/nodejs/node/pull/33944) +* [[`c1664a9008`](https://github.com/nodejs/node/commit/c1664a9008)] - **src**: clang\_format base\_object (Yash Ladha) [#33680](https://github.com/nodejs/node/pull/33680) +* [[`a789474945`](https://github.com/nodejs/node/commit/a789474945)] - **src**: fix ParseEncoding (sapics) [#33957](https://github.com/nodejs/node/pull/33957) +* [[`74f4aae22f`](https://github.com/nodejs/node/commit/74f4aae22f)] - **src**: remove unnecessary calculation in base64.h (sapics) [#33839](https://github.com/nodejs/node/pull/33839) +* [[`c492a2715e`](https://github.com/nodejs/node/commit/c492a2715e)] - **src**: use ToLocal in node\_os.cc (wenningplus) [#33939](https://github.com/nodejs/node/pull/33939) +* [[`9a52cd9cc0`](https://github.com/nodejs/node/commit/9a52cd9cc0)] - **src**: handle empty Maybe(Local) in node\_util.cc (Anna Henningsen) [#33867](https://github.com/nodejs/node/pull/33867) +* [[`e1bebf13db`](https://github.com/nodejs/node/commit/e1bebf13db)] - **src**: fix FastStringKey equal operator (sapics) [#33748](https://github.com/nodejs/node/pull/33748) +* [[`0dd67d992e`](https://github.com/nodejs/node/commit/0dd67d992e)] - **src**: reduce scope of code cache mutex (Anna Henningsen) [#33980](https://github.com/nodejs/node/pull/33980) +* [[`cd0ae4007f`](https://github.com/nodejs/node/commit/cd0ae4007f)] - **src**: improve indention for upd\_wrap.cc (gengjiawen) [#33976](https://github.com/nodejs/node/pull/33976) +* [[`6014e4e0b8`](https://github.com/nodejs/node/commit/6014e4e0b8)] - **src**: remove unnecessary ToLocalChecked call (Daniel Bevenius) [#33902](https://github.com/nodejs/node/pull/33902) +* [[`4715a41c1c`](https://github.com/nodejs/node/commit/4715a41c1c)] - **src**: simplify alignment-handling code (Anna Henningsen) [#33884](https://github.com/nodejs/node/pull/33884) +* [[`33cff40bb7`](https://github.com/nodejs/node/commit/33cff40bb7)] - **src**: remove ref to tools/generate\_code\_cache.js (Daniel Bevenius) [#33825](https://github.com/nodejs/node/pull/33825) +* [[`dfa0ee13ee`](https://github.com/nodejs/node/commit/dfa0ee13ee)] - **src**: remove unused vector include in string\_bytes (Daniel Bevenius) [#33824](https://github.com/nodejs/node/pull/33824) +* [[`fb2b0a094b`](https://github.com/nodejs/node/commit/fb2b0a094b)] - **src**: avoid unnecessary ToLocalChecked calls (Daniel Bevenius) [#33824](https://github.com/nodejs/node/pull/33824) +* [[`07c21d0d27`](https://github.com/nodejs/node/commit/07c21d0d27)] - **src**: reduce FileHandle size by reordering fields (Anna Henningsen) [#33784](https://github.com/nodejs/node/pull/33784) +* [[`83aaad7ec3`](https://github.com/nodejs/node/commit/83aaad7ec3)] - **src**: do not track BaseObjects via cleanup hooks (Anna Henningsen) [#33809](https://github.com/nodejs/node/pull/33809) +* [[`f8dddd3416`](https://github.com/nodejs/node/commit/f8dddd3416)] - **src**: handle missing TracingController everywhere (Anna Henningsen) [#33815](https://github.com/nodejs/node/pull/33815) +* [[`3b71aa8029`](https://github.com/nodejs/node/commit/3b71aa8029)] - **src**: remove unused `ERR\_TRANSFERRING\_EXTERNALIZED\_SHAREDARRAYBUFFER` (Anna Henningsen) [#33810](https://github.com/nodejs/node/pull/33810) +* [[`1f996b7372`](https://github.com/nodejs/node/commit/1f996b7372)] - **src**: simplify Reindent function in json\_utils.cc (sapics) [#33722](https://github.com/nodejs/node/pull/33722) +* [[`cdcd76810e`](https://github.com/nodejs/node/commit/cdcd76810e)] - **src**: add "missing" bash completion options (Daniel Bevenius) [#33744](https://github.com/nodejs/node/pull/33744) +* [[`cc8d70531d`](https://github.com/nodejs/node/commit/cc8d70531d)] - **src**: use Check() instead of FromJust in environment (Daniel Bevenius) [#33706](https://github.com/nodejs/node/pull/33706) +* [[`858c6b9dfd`](https://github.com/nodejs/node/commit/858c6b9dfd)] - **src**: use ToLocal in SafeGetenv (Daniel Bevenius) [#33695](https://github.com/nodejs/node/pull/33695) +* [[`c2f49319b7`](https://github.com/nodejs/node/commit/c2f49319b7)] - **src**: remove unnecessary ToLocalChecked call (Daniel Bevenius) [#33683](https://github.com/nodejs/node/pull/33683) +* [[`21f1e64737`](https://github.com/nodejs/node/commit/21f1e64737)] - **src**: simplify format in node\_file.cc (himself65) [#33660](https://github.com/nodejs/node/pull/33660) +* [[`c3728c6235`](https://github.com/nodejs/node/commit/c3728c6235)] - **src**: simplify MaybeStackBuffer::capacity() (Ben Noordhuis) [#33602](https://github.com/nodejs/node/pull/33602) +* [[`7725ff392c`](https://github.com/nodejs/node/commit/7725ff392c)] - **src**: remove superfluous inline keywords (James M Snell) [#33291](https://github.com/nodejs/node/pull/33291) +* [[`27e9cb7e85`](https://github.com/nodejs/node/commit/27e9cb7e85)] - **src**: turn AllocatedBuffer into thin wrapper around v8::BackingStore (James M Snell) [#33291](https://github.com/nodejs/node/pull/33291) +* [[`d8f040e33d`](https://github.com/nodejs/node/commit/d8f040e33d)] - **src**: extract AllocatedBuffer from env.h (James M Snell) [#33291](https://github.com/nodejs/node/pull/33291) +* [[`a8824ae0a5`](https://github.com/nodejs/node/commit/a8824ae0a5)] - **src**: avoid OOB read in URL parser (Anna Henningsen) [#33640](https://github.com/nodejs/node/pull/33640) +* [[`6ef2efe33a`](https://github.com/nodejs/node/commit/6ef2efe33a)] - **src**: use MaybeLocal.ToLocal instead of IsEmpty worker (Daniel Bevenius) [#33599](https://github.com/nodejs/node/pull/33599) +* [[`522fbbc8d9`](https://github.com/nodejs/node/commit/522fbbc8d9)] - **src**: don't use semicolon outside function (Shelley Vohr) [#33592](https://github.com/nodejs/node/pull/33592) +* [[`ad970996cf`](https://github.com/nodejs/node/commit/ad970996cf)] - **src**: remove unused using declarations (Daniel Bevenius) [#33268](https://github.com/nodejs/node/pull/33268) +* [[`20d54f6908`](https://github.com/nodejs/node/commit/20d54f6908)] - **src**: use MaybeLocal.ToLocal instead of IsEmpty (Daniel Bevenius) [#33554](https://github.com/nodejs/node/pull/33554) +* [[`5438611984`](https://github.com/nodejs/node/commit/5438611984)] - **src**: use NewFromUtf8Literal in GetLinkedBinding (Daniel Bevenius) [#33552](https://github.com/nodejs/node/pull/33552) +* [[`a5e860cd29`](https://github.com/nodejs/node/commit/a5e860cd29)] - **src**: use const in constant args.Length() (himself65) [#33555](https://github.com/nodejs/node/pull/33555) +* [[`7e351f15cb`](https://github.com/nodejs/node/commit/7e351f15cb)] - **src**: use MaybeLocal::FromMaybe to return exception (Daniel Bevenius) [#33514](https://github.com/nodejs/node/pull/33514) +* [[`3f1c756f89`](https://github.com/nodejs/node/commit/3f1c756f89)] - ***Revert*** "**src**: fix missing extra ca in tls.rootCertificates" (Eric Bickle) [#33313](https://github.com/nodejs/node/pull/33313) +* [[`d1e1dbf188`](https://github.com/nodejs/node/commit/d1e1dbf188)] - **src**: remove BeforeExit callback list (Ben Noordhuis) [#33386](https://github.com/nodejs/node/pull/33386) +* [[`ee45b78b7f`](https://github.com/nodejs/node/commit/ee45b78b7f)] - **src**: use MaybeLocal.ToLocal instead of IsEmpty (Daniel Bevenius) [#33457](https://github.com/nodejs/node/pull/33457) +* [[`9018e92b13`](https://github.com/nodejs/node/commit/9018e92b13)] - **src**: remove unused headers in src/util.h (Juan José Arboleda) [#33070](https://github.com/nodejs/node/pull/33070) +* [[`7d1d00f97a`](https://github.com/nodejs/node/commit/7d1d00f97a)] - **src**: use enum for refed flag on native immediates (Anna Henningsen) [#33444](https://github.com/nodejs/node/pull/33444) +* [[`e8cc269ee0`](https://github.com/nodejs/node/commit/e8cc269ee0)] - **src**: use symbol to store `AsyncWrap` resource (Anna Henningsen) [#31745](https://github.com/nodejs/node/pull/31745) +* [[`ab2454dec5`](https://github.com/nodejs/node/commit/ab2454dec5)] - **src**: prefer make\_unique (Michael Dawson) [#33378](https://github.com/nodejs/node/pull/33378) +* [[`a942f7280a`](https://github.com/nodejs/node/commit/a942f7280a)] - **src**: remove unnecessary else in base\_object-inl.h (Daniel Bevenius) [#33413](https://github.com/nodejs/node/pull/33413) +* [[`f6227c0577`](https://github.com/nodejs/node/commit/f6227c0577)] - **src**: reduce duplication in RegisterHandleCleanups (Daniel Bevenius) [#33421](https://github.com/nodejs/node/pull/33421) +* [[`f24292e106`](https://github.com/nodejs/node/commit/f24292e106)] - **src**: remove unused IsolateSettings variable (Daniel Bevenius) [#33417](https://github.com/nodejs/node/pull/33417) +* [[`308be6ca0c`](https://github.com/nodejs/node/commit/308be6ca0c)] - **src**: remove unused misc variable (Daniel Bevenius) [#33417](https://github.com/nodejs/node/pull/33417) +* [[`7fd0519a91`](https://github.com/nodejs/node/commit/7fd0519a91)] - **src**: add promise\_resolve to SetupHooks comment (Daniel Bevenius) [#33365](https://github.com/nodejs/node/pull/33365) +* [[`26a3cf058d`](https://github.com/nodejs/node/commit/26a3cf058d)] - **src,build**: add --openssl-default-cipher-list (Daniel Bevenius) [#33708](https://github.com/nodejs/node/pull/33708) +* [[`b0fa611e68`](https://github.com/nodejs/node/commit/b0fa611e68)] - **stream**: fix the spellings (antsmartian) [#33635](https://github.com/nodejs/node/pull/33635) +* [[`1db0d51ab2`](https://github.com/nodejs/node/commit/1db0d51ab2)] - **stream**: forward writableObjectMode (Robert Nagy) [#33390](https://github.com/nodejs/node/pull/33390) +* [[`2c568c80f3`](https://github.com/nodejs/node/commit/2c568c80f3)] - **test**: add non-ASCII character embedding test (Anna Henningsen) [#33972](https://github.com/nodejs/node/pull/33972) +* [[`d4a2ae094e`](https://github.com/nodejs/node/commit/d4a2ae094e)] - **test**: add test for Http2ServerResponse#\[writableCorked,cork,uncork\] (Pranshu Srivastava) [#33956](https://github.com/nodejs/node/pull/33956) +* [[`4a61013fb2`](https://github.com/nodejs/node/commit/4a61013fb2)] - **test**: print arguments passed to mustNotCall function (Denys Otrishko) [#33951](https://github.com/nodejs/node/pull/33951) +* [[`1b55d90975`](https://github.com/nodejs/node/commit/1b55d90975)] - **test**: AsyncLocalStorage works with thenables (Gerhard Stoebich) [#34008](https://github.com/nodejs/node/pull/34008) +* [[`195980d667`](https://github.com/nodejs/node/commit/195980d667)] - **test**: account for non-node basename (Shelley Vohr) [#33952](https://github.com/nodejs/node/pull/33952) +* [[`90223f0a88`](https://github.com/nodejs/node/commit/90223f0a88)] - **test**: fix typo in common/index.js (gengjiawen) [#33976](https://github.com/nodejs/node/pull/33976) +* [[`d427d7f905`](https://github.com/nodejs/node/commit/d427d7f905)] - **test**: add common/udppair utility (James M Snell) [#33380](https://github.com/nodejs/node/pull/33380) +* [[`b8fdde400a`](https://github.com/nodejs/node/commit/b8fdde400a)] - ***Revert*** "**test**: stop testing --interpreted-frames-native-stack for s390x" (Michaël Zasso) [#33794](https://github.com/nodejs/node/pull/33794) +* [[`e3a53329c2`](https://github.com/nodejs/node/commit/e3a53329c2)] - **test**: temporarily exclude test on arm (Michael Dawson) [#33814](https://github.com/nodejs/node/pull/33814) +* [[`b6e3616911`](https://github.com/nodejs/node/commit/b6e3616911)] - **test**: fix invalid regular expressions in case test-trace-exit (legendecas) [#33769](https://github.com/nodejs/node/pull/33769) +* [[`c3ac47c03d`](https://github.com/nodejs/node/commit/c3ac47c03d)] - **test**: changed function to arrow function (Sagar Jadhav) [#33711](https://github.com/nodejs/node/pull/33711) +* [[`15eb5a3da4`](https://github.com/nodejs/node/commit/15eb5a3da4)] - **test**: uv\_tty\_init now returns EINVAL on IBM i (Xu Meng) [#33629](https://github.com/nodejs/node/pull/33629) +* [[`da5e970a8c`](https://github.com/nodejs/node/commit/da5e970a8c)] - **test**: make flaky test stricter (Robert Nagy) [#33539](https://github.com/nodejs/node/pull/33539) +* [[`47396a42cf`](https://github.com/nodejs/node/commit/47396a42cf)] - **test**: fix flaky test-trace-atomics-wait (Anna Henningsen) [#33428](https://github.com/nodejs/node/pull/33428) +* [[`eb877a4c49`](https://github.com/nodejs/node/commit/eb877a4c49)] - **test**: mark test-dgram-multicast-ssmv6-multi-process flaky (AshCripps) [#33498](https://github.com/nodejs/node/pull/33498) +* [[`5dca04ee8e`](https://github.com/nodejs/node/commit/5dca04ee8e)] - **tools**: remove superfluous regex in tools/doc/json.js (Rich Trott) [#33998](https://github.com/nodejs/node/pull/33998) +* [[`1791d5727c`](https://github.com/nodejs/node/commit/1791d5727c)] - **tools**: update remark-preset-lint-node@1.15.1 to 1.16.0 (Rich Trott) [#33852](https://github.com/nodejs/node/pull/33852) +* [[`01d8b91942`](https://github.com/nodejs/node/commit/01d8b91942)] - **tools**: prevent js2c from running if nothing changed (Daniel Bevenius) [#33844](https://github.com/nodejs/node/pull/33844) +* [[`e837f00b4f`](https://github.com/nodejs/node/commit/e837f00b4f)] - **tools**: remove unused vector include in mkdcodecache (Daniel Bevenius) [#33828](https://github.com/nodejs/node/pull/33828) +* [[`800dbb6bdd`](https://github.com/nodejs/node/commit/800dbb6bdd)] - **tools**: update ESLint to 7.2.0 (Colin Ihrig) [#33776](https://github.com/nodejs/node/pull/33776) +* [[`a14e38a6c0`](https://github.com/nodejs/node/commit/a14e38a6c0)] - **tools**: remove unused using declarations code\_cache (Daniel Bevenius) [#33697](https://github.com/nodejs/node/pull/33697) +* [[`9fb1eb09d9`](https://github.com/nodejs/node/commit/9fb1eb09d9)] - **tools**: update remark-preset-lint-node from 1.15.0 to 1.15.1 (Rich Trott) [#33727](https://github.com/nodejs/node/pull/33727) +* [[`a331a00eac`](https://github.com/nodejs/node/commit/a331a00eac)] - **tools**: fix check-imports.py to match on word boundaries (Richard Lau) [#33268](https://github.com/nodejs/node/pull/33268) +* [[`9325ed9e1c`](https://github.com/nodejs/node/commit/9325ed9e1c)] - **tools**: update ESLint to 7.1.0 (Colin Ihrig) [#33526](https://github.com/nodejs/node/pull/33526) +* [[`6dab63f36d`](https://github.com/nodejs/node/commit/6dab63f36d)] - **tools**: add docserve target (Antoine du HAMEL) [#33221](https://github.com/nodejs/node/pull/33221) +* [[`2384044c95`](https://github.com/nodejs/node/commit/2384044c95)] - **tools,gyp**: add support for MSVC cross-compilation (Richard Townsend) [#32867](https://github.com/nodejs/node/pull/32867) +* [[`987c927225`](https://github.com/nodejs/node/commit/987c927225)] - **util**: fix width detection for DEL without ICU (Ruben Bridgewater) [#33650](https://github.com/nodejs/node/pull/33650) +* [[`91d0d53b59`](https://github.com/nodejs/node/commit/91d0d53b59)] - **util**: support Combining Diacritical Marks for Symbols (Ruben Bridgewater) [#33650](https://github.com/nodejs/node/pull/33650) +* [[`e3d53f999d`](https://github.com/nodejs/node/commit/e3d53f999d)] - **util**: gracefully handle unknown colors (Ruben Bridgewater) [#33797](https://github.com/nodejs/node/pull/33797) +* [[`a90c9aa858`](https://github.com/nodejs/node/commit/a90c9aa858)] - **util**: fix inspection of class instance prototypes (Ruben Bridgewater) [#33449](https://github.com/nodejs/node/pull/33449) +* [[`2380d90f0a`](https://github.com/nodejs/node/commit/2380d90f0a)] - **util**: mark classes while inspecting them (Ruben Bridgewater) [#32332](https://github.com/nodejs/node/pull/32332) +* [[`879c9322ce`](https://github.com/nodejs/node/commit/879c9322ce)] - **vm**: allow proxy callbacks to throw (Gus Caplan) [#33808](https://github.com/nodejs/node/pull/33808) +* [[`af14c1f776`](https://github.com/nodejs/node/commit/af14c1f776)] - **wasi**: allow WASI stdio to be configured (Colin Ihrig) [#33544](https://github.com/nodejs/node/pull/33544) +* [[`5eecea375f`](https://github.com/nodejs/node/commit/5eecea375f)] - **wasi**: simplify WASI memory management (Colin Ihrig) [#33525](https://github.com/nodejs/node/pull/33525) +* [[`f98e888fdd`](https://github.com/nodejs/node/commit/f98e888fdd)] - **wasi**: refactor and enable poll\_oneoff() test (Colin Ihrig) [#33521](https://github.com/nodejs/node/pull/33521) +* [[`6b20e8442f`](https://github.com/nodejs/node/commit/6b20e8442f)] - **wasi**: relax WebAssembly.Instance type check (Ben Noordhuis) [#33431](https://github.com/nodejs/node/pull/33431) +* [[`d15383253a`](https://github.com/nodejs/node/commit/d15383253a)] - **wasi,worker**: handle termination exception (Ben Noordhuis) [#33386](https://github.com/nodejs/node/pull/33386) +* [[`3f971d89a9`](https://github.com/nodejs/node/commit/3f971d89a9)] - **win,fs**: use namespaced path in absolute symlinks (Bartosz Sosnowski) [#33351](https://github.com/nodejs/node/pull/33351) +* [[`3520a134af`](https://github.com/nodejs/node/commit/3520a134af)] - **win,msi**: add arm64 config for windows msi (Dennis Ameling) [#33689](https://github.com/nodejs/node/pull/33689) +* [[`b79495905f`](https://github.com/nodejs/node/commit/b79495905f)] - **worker**: fix variable referencing in template string (Harshitha KP) [#33467](https://github.com/nodejs/node/pull/33467) +* [[`9c3008005d`](https://github.com/nodejs/node/commit/9c3008005d)] - **worker**: perform initial port.unref() before preload modules (Anna Henningsen) [#33455](https://github.com/nodejs/node/pull/33455) +* [[`64cae13799`](https://github.com/nodejs/node/commit/64cae13799)] - **worker**: use \_writev in internal communication (Anna Henningsen) [#33454](https://github.com/nodejs/node/pull/33454) +* [[`7817b875a7`](https://github.com/nodejs/node/commit/7817b875a7)] - **worker**: fix race condition in node\_messaging.cc (Anna Henningsen) [#33429](https://github.com/nodejs/node/pull/33429) + ## 2020-06-02, Version 14.4.0 (Current), @targos From e20beaf004917d3f6bcdf0954769ab1a2c6fd02f Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 17:26:27 -0700 Subject: [PATCH 15/25] meta: fixup CODEOWNERS so it hopefully works the CODEOWNERS rules for QUIC are not working and it's not entirely clear why. Hoping it's just the way the paths were specified. PR-URL: https://github.com/nodejs/node/pull/34147 Reviewed-By: Anna Henningsen Reviewed-By: Jiawen Geng --- .github/CODEOWNERS | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6fb4f7dc493b6a..e78612d424d09a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -59,12 +59,12 @@ # quic -./deps/ngtcp2/* @nodejs/quic -./deps/nghttp3/* @nodejs/quic -./doc/api/quic.md @nodejs/quic -./lib/internal/quic/* @nodejs/quic -./src/node_bob* @nodejs/quic -./src/quic/* @nodejs/quic +/deps/ngtcp2/ @nodejs/quic +/deps/nghttp3/ @nodejs/quic +/doc/api/quic.md @nodejs/quic +/lib/internal/quic/ @nodejs/quic +/src/node_bob* @nodejs/quic +/src/quic/ @nodejs/quic # modules From 73a51bb9dc677cea405ae6a143d6027e31eb375e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 27 Jun 2020 08:55:53 -0700 Subject: [PATCH 16/25] quic: cleanups in JS API PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- lib/internal/quic/core.js | 19 +++++-------------- src/quic/node_quic.cc | 1 - src/quic/node_quic_socket.cc | 2 +- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index 7e874ff34d58de..c6b3f5a5ea6b40 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -247,19 +247,11 @@ let warnedVerifyHostnameIdentity = false; assert(process.versions.ngtcp2 !== undefined); -// Called by the C++ internals when the socket is closed. -// When this is called, the only thing left to do is destroy -// the QuicSocket instance. -function onSocketClose() { - this[owner_symbol].destroy(); -} - -// Called by the C++ internals when an error occurs on the socket. -// When this is called, the only thing left to do is destroy -// the QuicSocket instance with an error. -// TODO(@jasnell): Should consolidate this with onSocketClose -function onSocketError(err) { - this[owner_symbol].destroy(errnoException(err)); +// Called by the C++ internals when the QuicSocket is closed with +// or without an error. The only thing left to do is destroy the +// QuicSocket instance. +function onSocketClose(err) { + this[owner_symbol].destroy(err != null ? errnoException(err) : undefined); } // Called by the C++ internals when the server busy state of @@ -579,7 +571,6 @@ function onStreamBlocked() { // Register the callbacks with the QUIC internal binding. setCallbacks({ onSocketClose, - onSocketError, onSocketServerBusy, onSessionReady, onSessionCert, diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc index 202767a670a30b..2ddc1dd7fe9fa9 100644 --- a/src/quic/node_quic.cc +++ b/src/quic/node_quic.cc @@ -55,7 +55,6 @@ void QuicSetCallbacks(const FunctionCallbackInfo& args) { } while (0) SETFUNCTION("onSocketClose", socket_close); - SETFUNCTION("onSocketError", socket_error); SETFUNCTION("onSessionReady", session_ready); SETFUNCTION("onSessionCert", session_cert); SETFUNCTION("onSessionClientHello", session_client_hello); diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc index ce7c5820b2d7b1..6c341d529c16f2 100644 --- a/src/quic/node_quic_socket.cc +++ b/src/quic/node_quic_socket.cc @@ -137,7 +137,7 @@ void JSQuicSocketListener::OnError(ssize_t code) { HandleScope scope(env->isolate()); Context::Scope context_scope(env->context()); Local arg = Number::New(env->isolate(), static_cast(code)); - socket()->MakeCallback(env->quic_on_socket_error_function(), 1, &arg); + socket()->MakeCallback(env->quic_on_socket_close_function(), 1, &arg); } void JSQuicSocketListener::OnSessionReady(BaseObjectPtr session) { From d6034186d661ab69f33af81f8ce649375dafadd7 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 09:15:31 -0700 Subject: [PATCH 17/25] quic: cleanups for QuicSocket PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- src/env.h | 1 - src/quic/node_quic_session.cc | 2 +- src/quic/node_quic_socket-inl.h | 8 +-- src/quic/node_quic_socket.cc | 101 ++++++++++++++++++++------------ src/quic/node_quic_socket.h | 51 +++++++++------- 5 files changed, 96 insertions(+), 67 deletions(-) diff --git a/src/env.h b/src/env.h index 2add16c8eddaa7..0a25a0df4b80f0 100644 --- a/src/env.h +++ b/src/env.h @@ -450,7 +450,6 @@ constexpr size_t kFsStatsBufferLength = #if defined(NODE_EXPERIMENTAL_QUIC) && NODE_EXPERIMENTAL_QUIC # define QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ V(quic_on_socket_close_function, v8::Function) \ - V(quic_on_socket_error_function, v8::Function) \ V(quic_on_socket_server_busy_function, v8::Function) \ V(quic_on_session_cert_function, v8::Function) \ V(quic_on_session_client_hello_function, v8::Function) \ diff --git a/src/quic/node_quic_session.cc b/src/quic/node_quic_session.cc index 027b5c9ae6abc4..60e1471c480ce9 100644 --- a/src/quic/node_quic_session.cc +++ b/src/quic/node_quic_session.cc @@ -2323,7 +2323,7 @@ bool QuicSession::set_socket(QuicSocket* socket, bool nat_rebinding) { socket->ReceiveStart(); // Step 4: Update ngtcp2 - auto& local_address = socket->local_address(); + auto local_address = socket->local_address(); if (nat_rebinding) { ngtcp2_addr addr; ngtcp2_addr_init( diff --git a/src/quic/node_quic_socket-inl.h b/src/quic/node_quic_socket-inl.h index 8156fd04ad7bf5..38f3ad927180f0 100644 --- a/src/quic/node_quic_socket-inl.h +++ b/src/quic/node_quic_socket-inl.h @@ -79,8 +79,8 @@ void QuicSocket::AssociateStatelessResetToken( token_map_[token] = session; } -const SocketAddress& QuicSocket::local_address() { - CHECK(preferred_endpoint_); +SocketAddress QuicSocket::local_address() const { + DCHECK(preferred_endpoint_); return preferred_endpoint_->local_address(); } @@ -221,10 +221,6 @@ void QuicSocket::AddEndpoint( endpoint_->ReceiveStart(); } -void QuicSocket::SessionReady(BaseObjectPtr session) { - listener_->OnSessionReady(session); -} - } // namespace quic } // namespace node diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc index 6c341d529c16f2..e3112887d8e4fb 100644 --- a/src/quic/node_quic_socket.cc +++ b/src/quic/node_quic_socket.cc @@ -83,10 +83,10 @@ bool IsShortHeader( } } // namespace -QuicPacket::QuicPacket(const char* diagnostic_label, size_t len) : - data_{0}, - len_(len), - diagnostic_label_(diagnostic_label) { +QuicPacket::QuicPacket(const char* diagnostic_label, size_t len) + : data_{0}, + len_(len), + diagnostic_label_(diagnostic_label) { CHECK_LE(len, MAX_PKTLEN); } @@ -100,8 +100,6 @@ const char* QuicPacket::diagnostic_label() const { diagnostic_label_ : "unspecified"; } -void QuicPacket::MemoryInfo(MemoryTracker* tracker) const {} - QuicSocketListener::~QuicSocketListener() { if (socket_) socket_->RemoveListener(this); @@ -174,10 +172,10 @@ QuicEndpoint::QuicEndpoint( QuicState* quic_state, Local wrap, QuicSocket* listener, - Local udp_wrap) : - BaseObject(quic_state->env(), wrap), - listener_(listener), - quic_state_(quic_state) { + Local udp_wrap) + : BaseObject(quic_state->env(), wrap), + listener_(listener), + quic_state_(quic_state) { MakeWeak(); udp_ = static_cast( udp_wrap->GetAlignedPointerFromInternalField( @@ -187,7 +185,9 @@ QuicEndpoint::QuicEndpoint( strong_ptr_.reset(udp_->GetAsyncWrap()); } -void QuicEndpoint::MemoryInfo(MemoryTracker* tracker) const {} +QuicEndpoint::~QuicEndpoint() { + udp_->set_listener(nullptr); +} uv_buf_t QuicEndpoint::OnAlloc(size_t suggested_size) { return AllocatedBuffer::AllocateManaged(env(), suggested_size).release(); @@ -229,6 +229,14 @@ void QuicEndpoint::OnAfterBind() { listener_->OnBind(this); } +template +void QuicSocketStatsTraits::ToString(const QuicSocket& ptr, Fn&& add_field) { +#define V(_n, name, label) \ + add_field(label, ptr.GetStat(&QuicSocketStats::name)); + SOCKET_STATS(V) +#undef V +} + QuicSocket::QuicSocket( QuicState* quic_state, Local wrap, @@ -240,17 +248,17 @@ QuicSocket::QuicSocket( QlogMode qlog, const uint8_t* session_reset_secret, bool disable_stateless_reset) - : AsyncWrap(quic_state->env(), wrap, AsyncWrap::PROVIDER_QUICSOCKET), - StatsBase(quic_state->env(), wrap), - alloc_info_(MakeAllocator()), - options_(options), - max_connections_(max_connections), - max_connections_per_host_(max_connections_per_host), - max_stateless_resets_per_host_(max_stateless_resets_per_host), - retry_token_expiration_(retry_token_expiration), - qlog_(qlog), - server_alpn_(NGHTTP3_ALPN_H3), - quic_state_(quic_state) { + : AsyncWrap(quic_state->env(), wrap, AsyncWrap::PROVIDER_QUICSOCKET), + StatsBase(quic_state->env(), wrap), + alloc_info_(MakeAllocator()), + options_(options), + max_connections_(max_connections), + max_connections_per_host_(max_connections_per_host), + max_stateless_resets_per_host_(max_stateless_resets_per_host), + retry_token_expiration_(retry_token_expiration), + qlog_(qlog), + server_alpn_(NGHTTP3_ALPN_H3), + quic_state_(quic_state) { MakeWeak(); PushListener(&default_listener_); @@ -279,15 +287,13 @@ QuicSocket::~QuicSocket() { if (listener == listener_) RemoveListener(listener_); - DebugStats(); -} + // In a clean shutdown, all QuicSessions associated with the QuicSocket + // would have been destroyed explicitly. However, if the QuicSocket is + // garbage collected / freed before Destroy having been called, there + // may be sessions remaining. This is not really a good thing. + Debug(this, "Destroying with %d sessions remaining", sessions_.size()); -template -void QuicSocketStatsTraits::ToString(const QuicSocket& ptr, Fn&& add_field) { -#define V(_n, name, label) \ - add_field(label, ptr.GetStat(&QuicSocketStats::name)); - SOCKET_STATS(V) -#undef V + DebugStats(); } void QuicSocket::MemoryInfo(MemoryTracker* tracker) const { @@ -310,7 +316,6 @@ void QuicSocket::Listen( const std::string& alpn, uint32_t options) { CHECK(sc); - CHECK(!server_secure_context_); CHECK(!is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)); Debug(this, "Starting to listen"); server_session_config_.Set(quic_state(), preferred_address); @@ -323,6 +328,7 @@ void QuicSocket::Listen( } void QuicSocket::OnError(QuicEndpoint* endpoint, ssize_t error) { + // TODO(@jasnell): What should we do with the endpoint? Debug(this, "Reading data from UDP socket failed. Error %" PRId64, error); listener_->OnError(error); } @@ -341,7 +347,7 @@ void QuicSocket::OnEndpointDone(QuicEndpoint* endpoint) { } void QuicSocket::OnBind(QuicEndpoint* endpoint) { - const SocketAddress& local_address = endpoint->local_address(); + SocketAddress local_address = endpoint->local_address(); bound_endpoints_[local_address] = BaseObjectWeakPtr(endpoint); Debug(this, "Endpoint %s bound", local_address); @@ -545,6 +551,13 @@ void QuicSocket::OnReceive( IncrementStat(&QuicSocketStats::packets_ignored); return; } + + // The QuicSession was destroyed while it was being set up. There's + // no further processing we can do here. + if (session->is_destroyed()) { + IncrementStat(&QuicSocketStats::packets_ignored); + return; + } } CHECK(session); @@ -683,6 +696,8 @@ bool QuicSocket::SendRetry( } // Shutdown a connection prematurely, before a QuicSession is created. +// This should only be called t the start of a session before the crypto +// keys have been established. void QuicSocket::ImmediateConnectionClose( const QuicCID& scid, const QuicCID& dcid, @@ -819,6 +834,18 @@ BaseObjectPtr QuicSocket::AcceptInitialPacket( listener_->OnSessionReady(session); + // It's possible that the session was destroyed while processing + // the ready callback. If it was, then we need to send an early + // CONNECTION_CLOSE. + if (session->is_destroyed()) { + ImmediateConnectionClose( + QuicCID(hd.scid), + QuicCID(hd.dcid), + local_addr, + remote_addr, + NGTCP2_CONNECTION_REFUSED); + } + return session; } @@ -826,9 +853,9 @@ QuicSocket::SendWrap::SendWrap( QuicState* quic_state, Local req_wrap_obj, size_t total_length) - : ReqWrap(quic_state->env(), req_wrap_obj, PROVIDER_QUICSOCKET), - total_length_(total_length), - quic_state_(quic_state) { + : ReqWrap(quic_state->env(), req_wrap_obj, PROVIDER_QUICSOCKET), + total_length_(total_length), + quic_state_(quic_state) { } std::string QuicSocket::SendWrap::MemoryInfoName() const { @@ -1093,7 +1120,7 @@ void QuicSocketStopListening(const FunctionCallbackInfo& args) { socket->StopListening(); } -void QuicSocketset_server_busy(const FunctionCallbackInfo& args) { +void QuicSocketSetServerBusy(const FunctionCallbackInfo& args) { QuicSocket* socket; ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); CHECK_EQ(args.Length(), 1); @@ -1164,7 +1191,7 @@ void QuicSocket::Initialize( QuicSocketSetDiagnosticPacketLoss); env->SetProtoMethod(socket, "setServerBusy", - QuicSocketset_server_busy); + QuicSocketSetServerBusy); env->SetProtoMethod(socket, "stopListening", QuicSocketStopListening); diff --git a/src/quic/node_quic_socket.h b/src/quic/node_quic_socket.h index 08ccd74dcb52f0..2fcf0fb7f19d31 100644 --- a/src/quic/node_quic_socket.h +++ b/src/quic/node_quic_socket.h @@ -33,6 +33,9 @@ using v8::Value; namespace quic { +class QuicSocket; +class QuicEndpoint; + enum QuicSocketOptions : uint32_t { // When enabled the QuicSocket will validate the address // using a RETRY packet to the peer. @@ -80,9 +83,6 @@ struct QuicSocketStatsTraits { static void ToString(const Base& ptr, Fn&& add_field); }; -class QuicSocket; -class QuicEndpoint; - // This is the generic interface for objects that control QuicSocket // instances. The default `JSQuicSocketListener` emits events to // JavaScript @@ -96,7 +96,7 @@ class QuicSocketListener { virtual void OnEndpointDone(QuicEndpoint* endpoint); virtual void OnDestroy(); - QuicSocket* socket() { return socket_.get(); } + QuicSocket* socket() const { return socket_.get(); } private: BaseObjectWeakPtr socket_; @@ -104,7 +104,7 @@ class QuicSocketListener { friend class QuicSocket; }; -class JSQuicSocketListener : public QuicSocketListener { +class JSQuicSocketListener final : public QuicSocketListener { public: void OnError(ssize_t code) override; void OnSessionReady(BaseObjectPtr session) override; @@ -121,17 +121,21 @@ constexpr size_t MAX_PKTLEN = std::max(NGTCP2_MAX_PKTLEN_IPV4, NGTCP2_MAX_PKTLEN_IPV6); // A serialized QuicPacket to be sent by a QuicSocket instance. +// QuicPackets are intended to be transient. They are created, +// filled with the contents of a serialized packet, and passed +// off immediately to the QuicSocket to be sent. As soon as +// the packet is sent, it is freed. class QuicPacket : public MemoryRetainer { public: // Creates a new QuicPacket. By default the packet will be // stack allocated with a max size of NGTCP2_MAX_PKTLEN_IPV4. // If a larger packet size is specified, it will be heap - // allocated. Generally speaking, a QUIC packet should never - // be larger than the current MTU to avoid IP fragmentation. + // allocated. A QUIC packet should never be larger than the + // current MTU to avoid IP fragmentation. // - // The content of a QuicPacket is provided by ngtcp2. The - // typical use pattern is to create a QuicPacket instance - // and then pass a pointer to it's internal buffer and max + // The content of a QuicPacket is provided by ngtcp2 and is + // opaque for us. The typical use pattern is to create a QuicPacket + // instance and then pass a pointer to it's internal buffer and max // size in to an ngtcp2 function that serializes the data. // ngtcp2 will fill the buffer as much as possible then return // the number of bytes serialized. User code is then responsible @@ -159,17 +163,22 @@ class QuicPacket : public MemoryRetainer { QuicPacket(const char* diagnostic_label, size_t len); QuicPacket(const QuicPacket& other); + uint8_t* data() { return data_; } + size_t length() const { return len_; } + uv_buf_t buf() const { return uv_buf_init( const_cast(reinterpret_cast(&data_)), length()); } + inline void set_length(size_t len); + const char* diagnostic_label() const; - void MemoryInfo(MemoryTracker* tracker) const override; + SET_NO_MEMORY_INFO(); SET_MEMORY_INFO_NAME(QuicPacket); SET_SELF_SIZE(QuicPacket); @@ -198,8 +207,8 @@ class QuicEndpointListener { // A QuicEndpoint wraps a UDPBaseWrap. A single QuicSocket may // have multiple QuicEndpoints, the lifecycles of which are // attached to the QuicSocket. -class QuicEndpoint : public BaseObject, - public UDPListener { +class QuicEndpoint final : public BaseObject, + public UDPListener { public: static void Initialize( Environment* env, @@ -212,9 +221,10 @@ class QuicEndpoint : public BaseObject, QuicSocket* listener, Local udp_wrap); - const SocketAddress& local_address() const { - local_address_ = udp_->GetSockName(); - return local_address_; + ~QuicEndpoint() override; + + SocketAddress local_address() const { + return udp_->GetSockName(); } // Implementation for UDPListener @@ -242,17 +252,16 @@ class QuicEndpoint : public BaseObject, void IncrementPendingCallbacks() { pending_callbacks_++; } void DecrementPendingCallbacks() { pending_callbacks_--; } - bool has_pending_callbacks() { return pending_callbacks_ > 0; } + bool has_pending_callbacks() const { return pending_callbacks_ > 0; } inline void WaitForPendingCallbacks(); QuicState* quic_state() const { return quic_state_.get(); } - void MemoryInfo(MemoryTracker* tracker) const override; + SET_NO_MEMORY_INFO(); SET_MEMORY_INFO_NAME(QuicEndpoint) SET_SELF_SIZE(QuicEndpoint) private: - mutable SocketAddress local_address_; BaseObjectWeakPtr listener_; UDPWrapBase* udp_; BaseObjectPtr strong_ptr_; @@ -298,7 +307,7 @@ class QuicSocket : public AsyncWrap, // Returns the default/preferred local address. Additional // QuicEndpoint instances may be associated with the // QuicSocket bound to other local addresses. - inline const SocketAddress& local_address(); + inline SocketAddress local_address() const; void MaybeClose(); @@ -342,8 +351,6 @@ class QuicSocket : public AsyncWrap, std::unique_ptr packet, BaseObjectPtr session = BaseObjectPtr()); - inline void SessionReady(BaseObjectPtr session); - inline void set_server_busy(bool on); inline void set_diagnostic_packet_loss(double rx = 0.0, double tx = 0.0); From def8e769992332a9373559ecd2058ef4ac8698fa Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 09:35:59 -0700 Subject: [PATCH 18/25] quic: fixup set_final_size Ignore subsequent calls to set_final_size unless the new size is more than the previous, in which case, we have us a bug. PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- src/quic/node_quic_stream-inl.h | 7 ++++++- src/quic/node_quic_stream.cc | 1 - src/quic/node_quic_stream.h | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/quic/node_quic_stream-inl.h b/src/quic/node_quic_stream-inl.h index 3da0f5fb3b57cf..3dd1fc216d9783 100644 --- a/src/quic/node_quic_stream-inl.h +++ b/src/quic/node_quic_stream-inl.h @@ -35,7 +35,12 @@ void QuicStream::set_flag(int32_t flag, bool on) { } void QuicStream::set_final_size(uint64_t final_size) { - CHECK_EQ(GetStat(&QuicStreamStats::final_size), 0); + // Only set the final size once. + if (is_flag_set(QUICSTREAM_FLAG_FIN)) { + CHECK_LE(final_size, GetStat(&QuicStreamStats::final_size)); + return; + } + set_flag(QUICSTREAM_FLAG_FIN); SetStat(&QuicStreamStats::final_size, final_size); } diff --git a/src/quic/node_quic_stream.cc b/src/quic/node_quic_stream.cc index 7a1054db407f59..ce8cc78a1ec8c5 100644 --- a/src/quic/node_quic_stream.cc +++ b/src/quic/node_quic_stream.cc @@ -355,7 +355,6 @@ void QuicStream::ReceiveData( // When fin != 0, we've received that last chunk of data for this // stream, indicating that the stream will no longer be readable. if (flags & NGTCP2_STREAM_DATA_FLAG_FIN) { - set_flag(QUICSTREAM_FLAG_FIN); set_final_size(offset + datalen); EmitRead(UV_EOF); } diff --git a/src/quic/node_quic_stream.h b/src/quic/node_quic_stream.h index 97174dcb7b925d..d8297f300ba85c 100644 --- a/src/quic/node_quic_stream.h +++ b/src/quic/node_quic_stream.h @@ -256,7 +256,11 @@ class QuicStream : public AsyncWrap, // Specifies the kind of headers currently being processed. inline void set_headers_kind(QuicStreamHeadersKind kind); - // Set the final size for the QuicStream + // Set the final size for the QuicStream. This only works + // the first time it is called. Subsequent calls will be + // ignored unless the subsequent size is greater than the + // prior set size, in which case we have a bug and we'll + // assert. inline void set_final_size(uint64_t final_size); // The final size is the maximum amount of data that has been From 3bae2d50733eaa269771203a877cb339f0bad1c8 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 10:12:00 -0700 Subject: [PATCH 19/25] quic: consolidate onSessionClose and onSessionSilentClose Use a single callback function for both PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- lib/internal/quic/core.js | 40 ++++++++++++------------------ src/env.h | 1 - src/quic/node_quic.cc | 1 - src/quic/node_quic_session.cc | 46 +++++++++++------------------------ src/quic/node_quic_session.h | 18 +++++++++----- 5 files changed, 42 insertions(+), 64 deletions(-) diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index c6b3f5a5ea6b40..5074cb279606bd 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -271,23 +271,24 @@ function onSessionReady(handle) { process.nextTick(emit.bind(socket, 'session', session)); } -// During an immediate close, all currently open QuicStreams are -// abruptly closed. If they are still writable or readable, an abort -// event will be emitted, and RESET_STREAM and STOP_SENDING frames -// will be transmitted as necessary. Once streams have been -// shutdown, a CONNECTION_CLOSE frame will be sent and the -// session will enter the closing period, after which it will -// be destroyed either when the idle timeout expires, the -// QuicSession is silently closed, or destroy is called. -function onSessionClose(code, family) { +// Called when the session needs to be closed and destroyed. +// If silent is true, then the session is going to be closed +// immediately without sending any CONNECTION_CLOSE to the +// connected peer. If silent is false, a CONNECTION_CLOSE +// is going to be sent to the peer. +function onSessionClose(code, family, silent, statelessReset) { if (this[owner_symbol]) { - this[owner_symbol][kClose](family, code); - } else { - // When there's no owner_symbol, the session was closed - // before it could be fully set up. Just immediately - // close everything down on the native side. - this.destroy(code, family); + if (silent) { + this[owner_symbol][kDestroy](statelessReset, family, code); + } else { + this[owner_symbol][kClose](family, code); + } + return; } + // When there's no owner_symbol, the session was closed + // before it could be fully set up. Just immediately + // close everything down on the native side. + this.destroy(code, family); } // Called by the C++ internals when a QuicSession has been destroyed. @@ -554,14 +555,6 @@ function onStreamHeaders(id, headers, kind, push_id) { this[owner_symbol][kHeaders](id, headers, kind, push_id); } -// During a silent close, all currently open QuicStreams are abruptly -// closed. If they are still writable or readable, an abort event will be -// emitted, otherwise the stream is just destroyed. No RESET_STREAM or -// STOP_SENDING is transmitted to the peer. -function onSessionSilentClose(statelessReset, code, family) { - this[owner_symbol][kDestroy](statelessReset, family, code); -} - // When a stream is flow control blocked, causes a blocked event // to be emitted. This is a purely informational event. function onStreamBlocked() { @@ -581,7 +574,6 @@ setCallbacks({ onSessionHandshake, onSessionKeylog, onSessionQlog, - onSessionSilentClose, onSessionStatus, onSessionTicket, onSessionVersionNegotiation, diff --git a/src/env.h b/src/env.h index 0a25a0df4b80f0..54e86db2f47e0b 100644 --- a/src/env.h +++ b/src/env.h @@ -462,7 +462,6 @@ constexpr size_t kFsStatsBufferLength = V(quic_on_session_use_preferred_address_function, v8::Function) \ V(quic_on_session_qlog_function, v8::Function) \ V(quic_on_session_ready_function, v8::Function) \ - V(quic_on_session_silent_close_function, v8::Function) \ V(quic_on_session_status_function, v8::Function) \ V(quic_on_session_ticket_function, v8::Function) \ V(quic_on_session_version_negotiation_function, v8::Function) \ diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc index 2ddc1dd7fe9fa9..62e98782931342 100644 --- a/src/quic/node_quic.cc +++ b/src/quic/node_quic.cc @@ -66,7 +66,6 @@ void QuicSetCallbacks(const FunctionCallbackInfo& args) { SETFUNCTION("onSessionUsePreferredAddress", session_use_preferred_address); SETFUNCTION("onSessionPathValidation", session_path_validation); SETFUNCTION("onSessionQlog", session_qlog); - SETFUNCTION("onSessionSilentClose", session_silent_close); SETFUNCTION("onSessionStatus", session_status); SETFUNCTION("onSessionTicket", session_ticket); SETFUNCTION("onSessionVersionNegotiation", session_version_negotiation); diff --git a/src/quic/node_quic_session.cc b/src/quic/node_quic_session.cc index 60e1471c480ce9..d058035a0b7896 100644 --- a/src/quic/node_quic_session.cc +++ b/src/quic/node_quic_session.cc @@ -293,9 +293,9 @@ void QuicSessionListener::OnSessionDestroyed() { previous_listener_->OnSessionDestroyed(); } -void QuicSessionListener::OnSessionClose(QuicError error) { +void QuicSessionListener::OnSessionClose(QuicError error, int flags) { if (previous_listener_ != nullptr) - previous_listener_->OnSessionClose(error); + previous_listener_->OnSessionClose(error, flags); } void QuicSessionListener::OnStreamReady(BaseObjectPtr stream) { @@ -328,13 +328,6 @@ void QuicSessionListener::OnStreamBlocked(int64_t stream_id) { } } -void QuicSessionListener::OnSessionSilentClose( - bool stateless_reset, - QuicError error) { - if (previous_listener_ != nullptr) - previous_listener_->OnSessionSilentClose(stateless_reset, error); -} - void QuicSessionListener::OnUsePreferredAddress( int family, const PreferredAddress& preferred_address) { @@ -525,14 +518,20 @@ void JSQuicSessionListener::OnSessionDestroyed() { env->quic_on_session_destroyed_function(), 0, nullptr); } -void JSQuicSessionListener::OnSessionClose(QuicError error) { +void JSQuicSessionListener::OnSessionClose(QuicError error, int flags) { Environment* env = session()->env(); HandleScope scope(env->isolate()); Context::Scope context_scope(env->context()); Local argv[] = { Number::New(env->isolate(), static_cast(error.code)), - Integer::New(env->isolate(), error.family) + Integer::New(env->isolate(), error.family), + flags & SESSION_CLOSE_FLAG_SILENT + ? v8::True(env->isolate()) + : v8::False(env->isolate()), + flags & SESSION_CLOSE_FLAG_STATELESS_RESET + ? v8::True(env->isolate()) + : v8::False(env->isolate()) }; // Grab a shared pointer to this to prevent the QuicSession @@ -664,26 +663,6 @@ void JSQuicSessionListener::OnSessionTicket(int size, SSL_SESSION* sess) { arraysize(argv), argv); } -void JSQuicSessionListener::OnSessionSilentClose( - bool stateless_reset, - QuicError error) { - Environment* env = session()->env(); - HandleScope scope(env->isolate()); - Context::Scope context_scope(env->context()); - - Local argv[] = { - stateless_reset ? v8::True(env->isolate()) : v8::False(env->isolate()), - Number::New(env->isolate(), static_cast(error.code)), - Integer::New(env->isolate(), error.family) - }; - - // Grab a shared pointer to this to prevent the QuicSession - // from being freed while the MakeCallback is running. - BaseObjectPtr ptr(session()); - session()->MakeCallback( - env->quic_on_session_silent_close_function(), arraysize(argv), argv); -} - void JSQuicSessionListener::OnUsePreferredAddress( int family, const PreferredAddress& preferred_address) { @@ -2377,7 +2356,10 @@ void QuicSession::SilentClose() { err.code, is_stateless_reset() ? "yes" : "no"); - listener()->OnSessionSilentClose(is_stateless_reset(), err); + int flags = QuicSessionListener::SESSION_CLOSE_FLAG_SILENT; + if (is_stateless_reset()) + flags |= QuicSessionListener::SESSION_CLOSE_FLAG_STATELESS_RESET; + listener()->OnSessionClose(err, flags); } // Begin connection close by serializing the CONNECTION_CLOSE packet. // There are two variants: one to serialize an application close, the diff --git a/src/quic/node_quic_session.h b/src/quic/node_quic_session.h index f27aaabdd98ef1..78fb7ee0a30540 100644 --- a/src/quic/node_quic_session.h +++ b/src/quic/node_quic_session.h @@ -260,6 +260,12 @@ struct QuicSessionStatsTraits { class QuicSessionListener { public: + enum SessionCloseFlags { + SESSION_CLOSE_FLAG_NONE, + SESSION_CLOSE_FLAG_SILENT, + SESSION_CLOSE_FLAG_STATELESS_RESET + }; + virtual ~QuicSessionListener(); virtual void OnKeylog(const char* str, size_t size); @@ -280,7 +286,9 @@ class QuicSessionListener { int64_t stream_id, uint64_t app_error_code); virtual void OnSessionDestroyed(); - virtual void OnSessionClose(QuicError error); + virtual void OnSessionClose( + QuicError error, + int flags = SESSION_CLOSE_FLAG_NONE); virtual void OnStreamReady(BaseObjectPtr stream); virtual void OnHandshakeCompleted(); virtual void OnPathValidation( @@ -291,9 +299,6 @@ class QuicSessionListener { int family, const PreferredAddress& preferred_address); virtual void OnSessionTicket(int size, SSL_SESSION* session); - virtual void OnSessionSilentClose( - bool stateless_reset, - QuicError error); virtual void OnStreamBlocked(int64_t stream_id); virtual void OnVersionNegotiation( uint32_t supported_version, @@ -329,7 +334,9 @@ class JSQuicSessionListener : public QuicSessionListener { int64_t stream_id, uint64_t app_error_code) override; void OnSessionDestroyed() override; - void OnSessionClose(QuicError error) override; + void OnSessionClose( + QuicError error, + int flags = SESSION_CLOSE_FLAG_NONE) override; void OnStreamReady(BaseObjectPtr stream) override; void OnHandshakeCompleted() override; void OnPathValidation( @@ -337,7 +344,6 @@ class JSQuicSessionListener : public QuicSessionListener { const sockaddr* local, const sockaddr* remote) override; void OnSessionTicket(int size, SSL_SESSION* session) override; - void OnSessionSilentClose(bool stateless_reset, QuicError error) override; void OnUsePreferredAddress( int family, const PreferredAddress& preferred_address) override; From b1fab88ff077aecf2ae1ce1730d319eb692e4363 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 10:20:15 -0700 Subject: [PATCH 20/25] quic: remove unused callback function PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- lib/internal/quic/core.js | 9 --------- src/env.h | 1 - src/quic/node_quic.cc | 1 - 3 files changed, 11 deletions(-) diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index 5074cb279606bd..f7d801acb067ee 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -474,14 +474,6 @@ function onSessionQlog(str) { } } -// Called when an error occurs in a QuicSession. When this happens, -// the only remedy is to destroy the session. -function onSessionError(error) { - if (this[owner_symbol]) { - this[owner_symbol].destroy(error); - } -} - // Called by the C++ internals when a client QuicSession receives // a version negotiation response from the server. function onSessionVersionNegotiation( @@ -570,7 +562,6 @@ setCallbacks({ onSessionClientHello, onSessionClose, onSessionDestroyed, - onSessionError, onSessionHandshake, onSessionKeylog, onSessionQlog, diff --git a/src/env.h b/src/env.h index 54e86db2f47e0b..f16c79b500171c 100644 --- a/src/env.h +++ b/src/env.h @@ -455,7 +455,6 @@ constexpr size_t kFsStatsBufferLength = V(quic_on_session_client_hello_function, v8::Function) \ V(quic_on_session_close_function, v8::Function) \ V(quic_on_session_destroyed_function, v8::Function) \ - V(quic_on_session_error_function, v8::Function) \ V(quic_on_session_handshake_function, v8::Function) \ V(quic_on_session_keylog_function, v8::Function) \ V(quic_on_session_path_validation_function, v8::Function) \ diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc index 62e98782931342..a879e31d8706c3 100644 --- a/src/quic/node_quic.cc +++ b/src/quic/node_quic.cc @@ -60,7 +60,6 @@ void QuicSetCallbacks(const FunctionCallbackInfo& args) { SETFUNCTION("onSessionClientHello", session_client_hello); SETFUNCTION("onSessionClose", session_close); SETFUNCTION("onSessionDestroyed", session_destroyed); - SETFUNCTION("onSessionError", session_error); SETFUNCTION("onSessionHandshake", session_handshake); SETFUNCTION("onSessionKeylog", session_keylog); SETFUNCTION("onSessionUsePreferredAddress", session_use_preferred_address); From 2afc1abd05cc8f4a21ffb2c892949b7e30de2d00 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 10:41:30 -0700 Subject: [PATCH 21/25] quic: fixup constant exports, export all protocol error codes PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- src/quic/node_quic.cc | 19 ++++++++++++++++++- test/parallel/test-quic-binding.js | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc index a879e31d8706c3..ed147eb92b649f 100644 --- a/src/quic/node_quic.cc +++ b/src/quic/node_quic.cc @@ -189,7 +189,6 @@ void Initialize(Local target, V(NGTCP2_APP_NOERROR) \ V(NGTCP2_PATH_VALIDATION_RESULT_FAILURE) \ V(NGTCP2_PATH_VALIDATION_RESULT_SUCCESS) \ - V(NGTCP2_DEFAULT_MAX_PKTLEN) \ V(NGTCP2_CC_ALGO_CUBIC) \ V(NGTCP2_CC_ALGO_RENO) \ V(QUIC_ERROR_APPLICATION) \ @@ -232,11 +231,29 @@ void Initialize(Local target, QUIC_CONSTANTS(V) #undef V + NODE_DEFINE_CONSTANT(constants, NGTCP2_DEFAULT_MAX_PKTLEN); NODE_DEFINE_CONSTANT(constants, NGTCP2_PROTO_VER); NODE_DEFINE_CONSTANT(constants, NGTCP2_DEFAULT_MAX_ACK_DELAY); NODE_DEFINE_CONSTANT(constants, NGTCP2_MAX_CIDLEN); NODE_DEFINE_CONSTANT(constants, NGTCP2_MIN_CIDLEN); + NODE_DEFINE_CONSTANT(constants, NGTCP2_NO_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_INTERNAL_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_CONNECTION_REFUSED); + NODE_DEFINE_CONSTANT(constants, NGTCP2_FLOW_CONTROL_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_STREAM_LIMIT_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_STREAM_STATE_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_FINAL_SIZE_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_FRAME_ENCODING_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_TRANSPORT_PARAMETER_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_CONNECTION_ID_LIMIT_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_PROTOCOL_VIOLATION); + NODE_DEFINE_CONSTANT(constants, NGTCP2_INVALID_TOKEN); + NODE_DEFINE_CONSTANT(constants, NGTCP2_APPLICATION_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_CRYPTO_BUFFER_EXCEEDED); + NODE_DEFINE_CONSTANT(constants, NGTCP2_KEY_UPDATE_ERROR); + NODE_DEFINE_CONSTANT(constants, NGTCP2_CRYPTO_ERROR); + NODE_DEFINE_CONSTANT(constants, AF_INET); NODE_DEFINE_CONSTANT(constants, AF_INET6); NODE_DEFINE_STRING_CONSTANT(constants, diff --git a/test/parallel/test-quic-binding.js b/test/parallel/test-quic-binding.js index 6f28944664ffc9..2044ed43b48e53 100644 --- a/test/parallel/test-quic-binding.js +++ b/test/parallel/test-quic-binding.js @@ -20,6 +20,23 @@ assert(quic.constants); assert.strictEqual(quic.constants.NGTCP2_PROTO_VER.toString(16), 'ff00001d'); assert.strictEqual(quic.constants.NGHTTP3_ALPN_H3, '\u0005h3-29'); +assert.strictEqual(quic.constants.NGTCP2_NO_ERROR, 0); +assert.strictEqual(quic.constants.NGTCP2_INTERNAL_ERROR, 1); +assert.strictEqual(quic.constants.NGTCP2_CONNECTION_REFUSED, 2); +assert.strictEqual(quic.constants.NGTCP2_FLOW_CONTROL_ERROR, 3); +assert.strictEqual(quic.constants.NGTCP2_STREAM_LIMIT_ERROR, 4); +assert.strictEqual(quic.constants.NGTCP2_STREAM_STATE_ERROR, 5); +assert.strictEqual(quic.constants.NGTCP2_FINAL_SIZE_ERROR, 6); +assert.strictEqual(quic.constants.NGTCP2_FRAME_ENCODING_ERROR, 7); +assert.strictEqual(quic.constants.NGTCP2_TRANSPORT_PARAMETER_ERROR, 8); +assert.strictEqual(quic.constants.NGTCP2_CONNECTION_ID_LIMIT_ERROR, 9); +assert.strictEqual(quic.constants.NGTCP2_PROTOCOL_VIOLATION, 0xa); +assert.strictEqual(quic.constants.NGTCP2_INVALID_TOKEN, 0xb); +assert.strictEqual(quic.constants.NGTCP2_APPLICATION_ERROR, 0xc); +assert.strictEqual(quic.constants.NGTCP2_CRYPTO_BUFFER_EXCEEDED, 0xd); +assert.strictEqual(quic.constants.NGTCP2_KEY_UPDATE_ERROR, 0xe); +assert.strictEqual(quic.constants.NGTCP2_CRYPTO_ERROR, 0x100); + // The following just tests for the presence of things we absolutely need. // They don't test the functionality of those things. From b5fe31ef19a5852fe806675f1c56436c7d36bd8f Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 11:21:03 -0700 Subject: [PATCH 22/25] quic: avoid using private JS fields for now They introduce a significant performance bottleneck at the moment. PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- lib/internal/quic/core.js | 977 +++++++++++++++++++++----------------- 1 file changed, 530 insertions(+), 447 deletions(-) diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index f7d801acb067ee..a052ba6e2f3d03 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -208,13 +208,17 @@ const { const emit = EventEmitter.prototype.emit; +const kAfterLookup = Symbol('kAfterLookup'); +const kAfterPreferredAddressLookup = Symbol('kAfterPreferredAddressLookup'); const kAddSession = Symbol('kAddSession'); const kAddStream = Symbol('kAddStream'); const kClose = Symbol('kClose'); const kCert = Symbol('kCert'); const kClientHello = Symbol('kClientHello'); -const kContinueBind = Symbol('kContinueBind'); const kContinueConnect = Symbol('kContinueConnect'); +const kCompleteListen = Symbol('kCompleteListen'); +const kContinueListen = Symbol('kContinueListen'); +const kContinueBind = Symbol('kContinueBind'); const kDestroy = Symbol('kDestroy'); const kEndpointBound = Symbol('kEndpointBound'); const kEndpointClose = Symbol('kEndpointClose'); @@ -222,19 +226,30 @@ const kGetStreamOptions = Symbol('kGetStreamOptions'); const kHandshake = Symbol('kHandshake'); const kHandshakePost = Symbol('kHandshakePost'); const kHeaders = Symbol('kHeaders'); +const kInternalState = Symbol('kInternalState'); +const kInternalClientState = Symbol('kInternalClientState'); +const kInternalServerState = Symbol('kInternalServerState'); +const kMakeStream = Symbol('kMakeStream'); const kMaybeBind = Symbol('kMaybeBind'); +const kMaybeReady = Symbol('kMaybeReady'); +const kOnFileOpened = Symbol('kOnFileOpened'); +const kOnFileUnpipe = Symbol('kOnFileUnpipe'); +const kOnPipedFileHandleRead = Symbol('kOnPipedFileHandleRead'); const kSocketReady = Symbol('kSocketReady'); const kRemoveSession = Symbol('kRemove'); const kRemoveStream = Symbol('kRemoveStream'); const kServerBusy = Symbol('kServerBusy'); const kSetHandle = Symbol('kSetHandle'); const kSetSocket = Symbol('kSetSocket'); +const kSetSocketAfterBind = Symbol('kSetSocketAfterBind'); +const kStartFilePipe = Symbol('kStartFilePipe'); const kStreamClose = Symbol('kStreamClose'); const kStreamReset = Symbol('kStreamReset'); const kTrackWriteState = Symbol('kTrackWriteState'); const kUDPHandleForTesting = Symbol('kUDPHandleForTesting'); const kUsePreferredAddress = Symbol('kUsePreferredAddress'); const kVersionNegotiation = Symbol('kVersionNegotiation'); +const kWriteGeneric = Symbol('kWriteGeneric'); const kSocketUnbound = 0; const kSocketPending = 1; @@ -614,16 +629,18 @@ function onRemoveListener(event) { // by a QuicSocket. It does not exist independently // of the QuicSocket. class QuicEndpoint { - #state = kSocketUnbound; - #socket = undefined; - #udpSocket = undefined; - #address = undefined; - #ipv6Only = undefined; - #lookup = undefined; - #port = undefined; - #reuseAddr = undefined; - #type = undefined; - #fd = undefined; + [kInternalState] = { + state: kSocketUnbound, + socket: undefined, + udpSocket: undefined, + address: undefined, + ipv6Only: undefined, + lookup: undefined, + port: undefined, + reuseAddr: undefined, + type: undefined, + fd: undefined + }; constructor(socket, options) { const { @@ -635,35 +652,37 @@ class QuicEndpoint { type, preferred, } = validateQuicEndpointOptions(options); - this.#socket = socket; - this.#address = address || (type === AF_INET6 ? '::' : '0.0.0.0'); - this.#ipv6Only = !!ipv6Only; - this.#lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); - this.#port = port; - this.#reuseAddr = !!reuseAddr; - this.#type = type; - this.#udpSocket = dgram.createSocket(type === AF_INET6 ? 'udp6' : 'udp4'); + const state = this[kInternalState]; + state.socket = socket; + state.address = address || (type === AF_INET6 ? '::' : '0.0.0.0'); + state.ipv6Only = !!ipv6Only; + state.lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); + state.port = port; + state.reuseAddr = !!reuseAddr; + state.type = type; + state.udpSocket = dgram.createSocket(type === AF_INET6 ? 'udp6' : 'udp4'); // kUDPHandleForTesting is only used in the Node.js test suite to // artificially test the endpoint. This code path should never be // used in user code. if (typeof options[kUDPHandleForTesting] === 'object') { - this.#udpSocket.bind(options[kUDPHandleForTesting]); - this.#state = kSocketBound; - this.#socket[kEndpointBound](this); + state.udpSocket.bind(options[kUDPHandleForTesting]); + state.state = kSocketBound; + state.socket[kEndpointBound](this); } - const udpHandle = this.#udpSocket[internalDgram.kStateSymbol].handle; + const udpHandle = state.udpSocket[internalDgram.kStateSymbol].handle; const handle = new QuicEndpointHandle(socket[kHandle], udpHandle); handle[owner_symbol] = this; this[kHandle] = handle; - socket[kHandle].addEndpoint(handle, !!preferred); + socket[kHandle].addEndpoint(handle, preferred); } [kInspect]() { + // TODO(@jasnell): Proper custom inspect implementation const obj = { address: this.address, - fd: this.#fd, - type: this.#type === AF_INET6 ? 'udp6' : 'udp4' + fd: this[kInternalState].fd, + type: this[kInternalState].type === AF_INET6 ? 'udp6' : 'udp4' }; return `QuicEndpoint ${util.format(obj)}`; } @@ -673,29 +692,31 @@ class QuicEndpoint { // address. Once resolution is complete, the ip address needs to // be passed on to the [kContinueBind] function or the QuicEndpoint // needs to be destroyed. - static #afterLookup = function(err, ip) { + static [kAfterLookup](err, ip) { if (err) { this.destroy(err); return; } this[kContinueBind](ip); - }; + } // kMaybeBind binds the endpoint on-demand if it is not already // bound. If it is bound, we return immediately, otherwise put // the endpoint into the pending state and initiate the binding // process by calling the lookup to resolve the IP address. [kMaybeBind]() { - if (this.#state !== kSocketUnbound) + const state = this[kInternalState]; + if (state.state !== kSocketUnbound) return; - this.#state = kSocketPending; - this.#lookup(this.#address, QuicEndpoint.#afterLookup.bind(this)); + state.state = kSocketPending; + state.lookup(state.address, QuicEndpoint[kAfterLookup].bind(this)); } // IP address resolution is completed and we're ready to finish // binding to the local port. [kContinueBind](ip) { - const udpHandle = this.#udpSocket[internalDgram.kStateSymbol].handle; + const state = this[kInternalState]; + const udpHandle = state.udpSocket[internalDgram.kStateSymbol].handle; if (udpHandle == null) { // TODO(@jasnell): We may need to throw an error here. Under // what conditions does this happen? @@ -703,22 +724,22 @@ class QuicEndpoint { } const flags = - (this.#reuseAddr ? UV_UDP_REUSEADDR : 0) | - (this.#type === AF_INET6 && this.#ipv6Only ? UV_UDP_IPV6ONLY : 0); + (state.reuseAddr ? UV_UDP_REUSEADDR : 0) | + (state.type === AF_INET6 && state.ipv6Only ? UV_UDP_IPV6ONLY : 0); - const ret = udpHandle.bind(ip, this.#port, flags); + const ret = udpHandle.bind(ip, state.port, flags); if (ret) { - this.destroy(exceptionWithHostPort(ret, 'bind', ip, this.#port || 0)); + this.destroy(exceptionWithHostPort(ret, 'bind', ip, state.port || 0)); return; } // On Windows, the fd will be meaningless, but we always record it. - this.#fd = udpHandle.fd; - this.#state = kSocketBound; + state.fd = udpHandle.fd; + state.state = kSocketBound; // Notify the owning socket that the QuicEndpoint has been successfully // bound to the local UDP port. - this.#socket[kEndpointBound](this); + state.socket[kEndpointBound](this); } [kDestroy](error) { @@ -727,9 +748,10 @@ class QuicEndpoint { this[kHandle] = undefined; handle[owner_symbol] = undefined; handle.ondone = () => { - this.#udpSocket.close((err) => { + const state = this[kInternalState]; + state.udpSocket.close((err) => { if (err) error = err; - this.#socket[kEndpointClose](this, error); + state.socket[kEndpointClose](this, error); }); }; handle.waitForPendingCallbacks(); @@ -740,9 +762,10 @@ class QuicEndpoint { // the local IP address, port, and address type to which it // is bound. Otherwise, returns an empty object. get address() { - if (this.#state !== kSocketDestroyed) { + const state = this[kInternalState]; + if (state.state !== kSocketDestroyed) { try { - return this.#udpSocket.address(); + return state.udpSocket.address(); } catch (err) { if (err.code === 'EBADF') { // If there is an EBADF error, the socket is not bound. @@ -757,97 +780,102 @@ class QuicEndpoint { } get fd() { - return this.#fd; + return this[kInternalState].fd; } // True if the QuicEndpoint has been destroyed and is // no longer usable. get destroyed() { - return this.#state === kSocketDestroyed; + return this[kInternalState].state === kSocketDestroyed; } // True if binding has been initiated and is in progress. get pending() { - return this.#state === kSocketPending; + return this[kInternalState].state === kSocketPending; } - // True if the QuicEndpoint has been bound to the local - // UDP port. + // True if the QuicEndpoint has been bound to the localUDP port. get bound() { - return this.#state === kSocketBound; + return this[kInternalState].state === kSocketBound; } setTTL(ttl) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('setTTL'); - this.#udpSocket.setTTL(ttl); + state.udpSocket.setTTL(ttl); return this; } setMulticastTTL(ttl) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('setMulticastTTL'); - this.#udpSocket.setMulticastTTL(ttl); + state.udpSocket.setMulticastTTL(ttl); return this; } setBroadcast(on = true) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('setBroadcast'); - this.#udpSocket.setBroadcast(on); + state.udpSocket.setBroadcast(on); return this; } setMulticastLoopback(on = true) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('setMulticastLoopback'); - this.#udpSocket.setMulticastLoopback(on); + state.udpSocket.setMulticastLoopback(on); return this; } setMulticastInterface(iface) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('setMulticastInterface'); - this.#udpSocket.setMulticastInterface(iface); + state.udpSocket.setMulticastInterface(iface); return this; } addMembership(address, iface) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('addMembership'); - this.#udpSocket.addMembership(address, iface); + state.udpSocket.addMembership(address, iface); return this; } dropMembership(address, iface) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('dropMembership'); - this.#udpSocket.dropMembership(address, iface); + state.udpSocket.dropMembership(address, iface); return this; } ref() { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('ref'); - this.#udpSocket.ref(); + state.udpSocket.ref(); return this; } unref() { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('unref'); - this.#udpSocket.unref(); + state.udpSocket.unref(); return this; } destroy(error) { - // If the QuicEndpoint is already destroyed, do nothing - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) return; - - // Mark the QuicEndpoint as being destroyed. - this.#state = kSocketDestroyed; - + state.state = kSocketDestroyed; this[kDestroy](error); } } @@ -856,20 +884,22 @@ class QuicEndpoint { // Protocol state. There may be *multiple* QUIC connections (QuicSession) // associated with a single QuicSocket. class QuicSocket extends EventEmitter { - #alpn = undefined; - #autoClose = undefined; - #client = undefined; - #defaultEncoding = undefined; - #endpoints = new Set(); - #highWaterMark = undefined; - #lookup = undefined; - #server = undefined; - #serverBusy = false; - #serverListening = false; - #serverSecureContext = undefined; - #sessions = new Set(); - #state = kSocketUnbound; - #stats = undefined; + [kInternalState] = { + alpn: undefined, + autoClose: undefined, + client: undefined, + defaultEncoding: undefined, + endpoints: new Set(), + highWaterMark: undefined, + lookup: undefined, + server: undefined, + serverBusy: undefined, + serverListening: false, + serverSecureContext: undefined, + sessions: new Set(), + state: kSocketUnbound, + stats: undefined, + }; constructor(options) { const { @@ -921,10 +951,12 @@ class QuicSocket extends EventEmitter { } = validateQuicSocketOptions(options); super({ captureRejections: true }); - this.#autoClose = autoClose; - this.#client = client; - this.#lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); - this.#server = server; + const state = this[kInternalState]; + + state.autoClose = autoClose; + state.client = client; + state.lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); + state.server = server; const socketOptions = (validateAddress ? QUICSOCKET_OPTIONS_VALIDATE_ADDRESS : 0) | @@ -942,7 +974,7 @@ class QuicSocket extends EventEmitter { disableStatelessReset)); this.addEndpoint({ - lookup: this.#lookup, + lookup: state.lookup, // Keep the lookup and ...endpoint in this order // to allow the passed in endpoint options to // override the lookup specifically for that endpoint @@ -955,9 +987,10 @@ class QuicSocket extends EventEmitter { // streams. These are passed on to new client and server // QuicSession instances when they are created. [kGetStreamOptions]() { + const state = this[kInternalState]; return { - highWaterMark: this.#highWaterMark, - defaultEncoding: this.#defaultEncoding, + highWaterMark: state.highWaterMark, + defaultEncoding: state.defaultEncoding, }; } @@ -970,19 +1003,21 @@ class QuicSocket extends EventEmitter { } [kInspect]() { + // TODO(@jasnell): Proper custom inspect implementation + const state = this[kInternalState]; const obj = { endpoints: this.endpoints, - sessions: this.#sessions, + sessions: state.sessions, }; return `QuicSocket ${util.format(obj)}`; } [kAddSession](session) { - this.#sessions.add(session); + this[kInternalState].sessions.add(session); } [kRemoveSession](session) { - this.#sessions.delete(session); + this[kInternalState].sessions.delete(session); this[kMaybeDestroy](); } @@ -990,29 +1025,31 @@ class QuicSocket extends EventEmitter { // Function is a non-op if the socket is already bound or in the process of // being bound, and will call the callback once the socket is ready. [kMaybeBind](callback = () => {}) { - if (this.#state === kSocketBound) + const state = this[kInternalState]; + if (state.state === kSocketBound) return process.nextTick(callback); this.once('ready', callback); - if (this.#state === kSocketPending) + if (state.state === kSocketPending) return; - this.#state = kSocketPending; + state.state = kSocketPending; - for (const endpoint of this.#endpoints) + for (const endpoint of state.endpoints) endpoint[kMaybeBind](); } [kEndpointBound](endpoint) { - if (this.#state === kSocketBound) + const state = this[kInternalState]; + if (state.state === kSocketBound) return; - this.#state = kSocketBound; + state.state = kSocketBound; // Once the QuicSocket has been bound, we notify all currently // existing QuicSessions. QuicSessions created after this // point will automatically be notified that the QuicSocket // is ready. - for (const session of this.#sessions) + for (const session of state.sessions) session[kSocketReady](); // The ready event indicates that the QuicSocket is ready to be @@ -1024,14 +1061,15 @@ class QuicSocket extends EventEmitter { // Called when a QuicEndpoint closes [kEndpointClose](endpoint, error) { - this.#endpoints.delete(endpoint); + const state = this[kInternalState]; + state.endpoints.delete(endpoint); process.nextTick(emit.bind(this, 'endpointClose', endpoint, error)); // If there are no more QuicEndpoints, the QuicSocket is no // longer usable. - if (this.#endpoints.size === 0) { + if (state.endpoints.size === 0) { // Ensure that there are absolutely no additional sessions - for (const session of this.#sessions) + for (const session of state.sessions) session.destroy(error); if (error) process.nextTick(emit.bind(this, 'error', error)); @@ -1044,7 +1082,7 @@ class QuicSocket extends EventEmitter { [kDestroy](error) { // The QuicSocket will be destroyed once all QuicEndpoints // are destroyed. See [kEndpointClose]. - for (const endpoint of this.#endpoints) + for (const endpoint of this[kInternalState].endpoints) endpoint.destroy(error); } @@ -1052,7 +1090,7 @@ class QuicSocket extends EventEmitter { // is called. The QuicSocket will be destroyed if there are no remaining // open sessions. [kMaybeDestroy]() { - if (this.closing && this.#sessions.size === 0) { + if (this.closing && this[kInternalState].sessions.size === 0) { this.destroy(); return true; } @@ -1061,33 +1099,34 @@ class QuicSocket extends EventEmitter { // Called by the C++ internals to notify when server busy status is toggled. [kServerBusy](on) { - this.#serverBusy = on; + this[kInternalState].serverBusy = on; process.nextTick(emit.bind(this, 'busy', on)); } addEndpoint(options = {}) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('listen'); // TODO(@jasnell): Also forbid adding an endpoint if // the QuicSocket is closing. const endpoint = new QuicEndpoint(this, options); - this.#endpoints.add(endpoint); + state.endpoints.add(endpoint); // If the QuicSocket is already bound at this point, // also bind the newly created QuicEndpoint. - if (this.#state !== kSocketUnbound) + if (state.state !== kSocketUnbound) endpoint[kMaybeBind](); return endpoint; } - // Used only from within the #continueListen function. When a preferred + // Used only from within the [kContinueListen] function. When a preferred // address has been provided, the hostname given must be resolved into an // IP address, which must be passed on to #completeListen or the QuicSocket // needs to be destroyed. - static #afterPreferredAddressLookup = function( + static [kAfterPreferredAddressLookup]( transportParams, port, type, @@ -1097,13 +1136,13 @@ class QuicSocket extends EventEmitter { this.destroy(err); return; } - this.#completeListen(transportParams, { address, port, type }); - }; + this[kCompleteListen](transportParams, { address, port, type }); + } // The #completeListen function is called after all of the necessary // DNS lookups have been performed and we're ready to let the C++ // internals begin listening for new QuicServerSession instances. - #completeListen = function(transportParams, preferredAddress) { + [kCompleteListen](transportParams, preferredAddress) { const { address, port, @@ -1129,15 +1168,16 @@ class QuicSocket extends EventEmitter { // When the handle is told to listen, it will begin acting as a QUIC // server and will emit session events whenever a new QuicServerSession // is created. + const state = this[kInternalState]; this[kHandle].listen( - this.#serverSecureContext.context, + state.serverSecureContext.context, address, type, port, - this.#alpn, + state.alpn, options); process.nextTick(emit.bind(this, 'listening')); - }; + } // When the QuicSocket listen() function is called, the first step is to bind // the underlying QuicEndpoint's. Once at least one endpoint has been bound, @@ -1148,7 +1188,7 @@ class QuicSocket extends EventEmitter { // connecting clients to use. If specified, this will be communicate to the // client as part of the tranport parameters exchanged during the TLS // handshake. - #continueListen = function(transportParams, lookup) { + [kContinueListen](transportParams, lookup) { const { preferredAddress } = transportParams; // TODO(@jasnell): Currently, we wait to start resolving the @@ -1170,7 +1210,7 @@ class QuicSocket extends EventEmitter { // preferred address. lookup( address || (typeVal === AF_INET6 ? '::' : '0.0.0.0'), - QuicSocket.#afterPreferredAddressLookup.bind( + QuicSocket[kAfterPreferredAddressLookup].bind( this, transportParams, port, @@ -1178,19 +1218,20 @@ class QuicSocket extends EventEmitter { return; } // If preferred address is not set, we can skip directly to the listen - this.#completeListen(transportParams); - }; + this[kCompleteListen](transportParams); + } // Begin listening for server connections. The callback that may be // passed to this function is registered as a handler for the // on('session') event. Errors may be thrown synchronously by this // function. listen(options, callback) { - if (this.#serverListening) + const state = this[kInternalState]; + if (state.serverListening) throw new ERR_QUICSOCKET_LISTENING(); - if (this.#state === kSocketDestroyed || - this.#state === kSocketClosing) { + if (state.state === kSocketDestroyed || + state.state === kSocketClosing) { throw new ERR_QUICSOCKET_DESTROYED('listen'); } @@ -1203,7 +1244,7 @@ class QuicSocket extends EventEmitter { throw new ERR_INVALID_CALLBACK(callback); options = { - ...this.#server, + ...state.server, ...options, minVersion: 'TLSv1.3', maxVersion: 'TLSv1.3', @@ -1219,15 +1260,15 @@ class QuicSocket extends EventEmitter { // Store the secure context so that it is not garbage collected // while we still need to make use of it. - this.#serverSecureContext = + state.serverSecureContext = createSecureContext( options, initSecureContext); - this.#highWaterMark = highWaterMark; - this.#defaultEncoding = defaultEncoding; - this.#serverListening = true; - this.#alpn = alpn; + state.highWaterMark = highWaterMark; + state.defaultEncoding = defaultEncoding; + state.serverListening = true; + state.alpn = alpn; // If the callback function is provided, it is registered as a // handler for the on('session') event and will be called whenever @@ -1236,10 +1277,10 @@ class QuicSocket extends EventEmitter { this.on('session', callback); // Bind the QuicSocket to the local port if it hasn't been bound already. - this[kMaybeBind](this.#continueListen.bind( + this[kMaybeBind](this[kContinueListen].bind( this, transportParams, - this.#lookup)); + state.lookup)); } // When the QuicSocket connect() function is called, the first step is to bind @@ -1248,7 +1289,7 @@ class QuicSocket extends EventEmitter { // process. // // The immediate next step is to resolve the address into an ip address. - #continueConnect = function(session, lookup, address, type) { + [kContinueConnect](session, lookup, address, type) { // TODO(@jasnell): Currently, we perform the DNS resolution after // the QuicSocket has been bound. We don't have to. We could do // it in parallel while we're waitint to be bound but doing so @@ -1259,14 +1300,13 @@ class QuicSocket extends EventEmitter { lookup( address || (type === AF_INET6 ? '::' : '0.0.0.0'), connectAfterLookup.bind(session, type)); - }; + } // Creates and returns a new QuicClientSession. connect(options, callback) { - if (this.#state === kSocketDestroyed || - this.#state === kSocketClosing) { + const state = this[kInternalState]; + if (state.state === kSocketDestroyed || state.state === kSocketClosing) throw new ERR_QUICSOCKET_DESTROYED('connect'); - } if (typeof options === 'function') { callback = options; @@ -1274,7 +1314,7 @@ class QuicSocket extends EventEmitter { } options = { - ...this.#client, + ...state.client, ...options }; @@ -1290,10 +1330,10 @@ class QuicSocket extends EventEmitter { if (typeof callback === 'function') session.once('ready', callback); - this[kMaybeBind](this.#continueConnect.bind( + this[kMaybeBind](this[kContinueConnect].bind( this, session, - this.#lookup, + state.lookup, address, type)); @@ -1328,7 +1368,8 @@ class QuicSocket extends EventEmitter { // once('close') event (if specified) and the QuicSocket is destroyed // immediately. close(callback) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('close'); // If a callback function is specified, it is registered as a @@ -1344,17 +1385,17 @@ class QuicSocket extends EventEmitter { // If we are already closing, do nothing else and wait // for the close event to be invoked. - if (this.#state === kSocketClosing) + if (state.state === kSocketClosing) return; // If the QuicSocket is otherwise not bound to the local // port, destroy the QuicSocket immediately. - if (this.#state !== kSocketBound) { + if (state.state !== kSocketBound) { this.destroy(); } // Mark the QuicSocket as closing to prevent re-entry - this.#state = kSocketClosing; + state.state = kSocketClosing; // Otherwise, gracefully close each QuicSession, with // [kMaybeDestroy]() being called after each closes. @@ -1367,7 +1408,7 @@ class QuicSocket extends EventEmitter { if (this[kHandle]) { this[kHandle].stopListening(); } - this.#serverListening = false; + state.serverListening = false; // If there are no sessions, calling maybeDestroy // will immediately and synchronously destroy the @@ -1381,7 +1422,7 @@ class QuicSocket extends EventEmitter { // they will call the kMaybeDestroy function. When there // are no remaining session instances, the QuicSocket // will be closed and destroyed. - for (const session of this.#sessions) + for (const session of state.sessions) session.close(maybeDestroy); } @@ -1398,84 +1439,88 @@ class QuicSocket extends EventEmitter { // flushed from the QuicSocket's queue, the QuicSocket C++ instance // will be destroyed and freed from memory. destroy(error) { + const state = this[kInternalState]; // If the QuicSocket is already destroyed, do nothing - if (this.#state === kSocketDestroyed) + if (state.state === kSocketDestroyed) return; // Mark the QuicSocket as being destroyed. - this.#state = kSocketDestroyed; + state.state = kSocketDestroyed; // Immediately close any sessions that may be remaining. // If the udp socket is in a state where it is able to do so, // a final attempt to send CONNECTION_CLOSE frames for each // closed session will be made. - for (const session of this.#sessions) + for (const session of state.sessions) session.destroy(error); this[kDestroy](error); } ref() { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('ref'); - for (const endpoint of this.#endpoints) + for (const endpoint of state.endpoints) endpoint.ref(); return this; } unref() { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('unref'); - for (const endpoint of this.#endpoints) + for (const endpoint of state.endpoints) endpoint.unref(); return this; } get endpoints() { - return Array.from(this.#endpoints); + return Array.from(this[kInternalState].endpoints); } // The sever secure context is the SecureContext specified when calling // listen. It is the context that will be used with all new server // QuicSession instances. get serverSecureContext() { - return this.#serverSecureContext; + return this[kInternalState].serverSecureContext; } // True if at least one associated QuicEndpoint has been successfully // bound to a local UDP port. get bound() { - return this.#state === kSocketBound; + return this[kInternalState].state === kSocketBound; } // True if graceful close has been initiated by calling close() get closing() { - return this.#state === kSocketClosing; + return this[kInternalState].state === kSocketClosing; } // True if the QuicSocket has been destroyed and is no longer usable get destroyed() { - return this.#state === kSocketDestroyed; + return this[kInternalState].state === kSocketDestroyed; } // True if listen() has been called successfully get listening() { - return this.#serverListening; + return this[kInternalState].serverListening; } // True if the QuicSocket is currently waiting on at least one // QuicEndpoint to succesfully bind.g get pending() { - return this.#state === kSocketPending; + return this[kInternalState].state === kSocketPending; } // Marking a server as busy will cause all new // connection attempts to fail with a SERVER_BUSY CONNECTION_CLOSE. setServerBusy(on = true) { - if (this.#state === kSocketDestroyed) + const state = this[kInternalState]; + if (state.state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('setServerBusy'); validateBoolean(on, 'on'); - if (this.#serverBusy !== on) + if (state.serverBusy !== on) this[kHandle].setServerBusy(on); } @@ -1483,7 +1528,7 @@ class QuicSocket extends EventEmitter { // TODO(@jasnell): If the object is destroyed, it should // use a fixed duration rather than calculating from now const now = process.hrtime.bigint(); - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return now - stats[IDX_QUIC_SOCKET_STATS_CREATED_AT]; } @@ -1491,7 +1536,7 @@ class QuicSocket extends EventEmitter { // TODO(@jasnell): If the object is destroyed, it should // use a fixed duration rather than calculating from now const now = process.hrtime.bigint(); - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return now - stats[IDX_QUIC_SOCKET_STATS_BOUND_AT]; } @@ -1499,56 +1544,56 @@ class QuicSocket extends EventEmitter { // TODO(@jasnell): If the object is destroyed, it should // use a fixed duration rather than calculating from now const now = process.hrtime.bigint(); - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return now - stats[IDX_QUIC_SOCKET_STATS_LISTEN_AT]; } get bytesReceived() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED]; } get bytesSent() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_BYTES_SENT]; } get packetsReceived() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED]; } get packetsSent() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_PACKETS_SENT]; } get packetsIgnored() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED]; } get serverBusy() { - return this.#serverBusy; + return this[kInternalState].serverBusy; } get serverSessions() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS]; } get clientSessions() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS]; } get statelessResetCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT]; } get serverBusyCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SOCKET_STATS_SERVER_BUSY_COUNT]; } @@ -1557,7 +1602,7 @@ class QuicSocket extends EventEmitter { // option is a number between 0 and 1 that identifies the possibility of // packet loss in the given direction. setDiagnosticPacketLoss(options) { - if (this.#state === kSocketDestroyed) + if (this[kInternalState].state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('setDiagnosticPacketLoss'); const { rx = 0.0, @@ -1590,36 +1635,38 @@ class QuicSocket extends EventEmitter { // to be switched on/off dynamically through the lifetime of the // socket. toggleStatelessReset() { - if (this.#state === kSocketDestroyed) + if (this[kInternalState].state === kSocketDestroyed) throw new ERR_QUICSOCKET_DESTROYED('toggleStatelessReset'); return this[kHandle].toggleStatelessReset(); } } class QuicSession extends EventEmitter { - #alpn = undefined; - #cipher = undefined; - #cipherVersion = undefined; - #closeCode = NGTCP2_NO_ERROR; - #closeFamily = QUIC_ERROR_APPLICATION; - #closing = false; - #destroyed = false; - #earlyData = false; - #handshakeComplete = false; - #idleTimeout = false; - #maxPacketLength = NGTCP2_DEFAULT_MAX_PKTLEN; - #servername = undefined; - #socket = undefined; - #statelessReset = false; - #stats = undefined; - #pendingStreams = new Set(); - #streams = new Map(); - #verifyErrorReason = undefined; - #verifyErrorCode = undefined; - #handshakeAckHistogram = undefined; - #handshakeContinuationHistogram = undefined; - #highWaterMark = undefined; - #defaultEncoding = undefined; + [kInternalState] = { + alpn: undefined, + cipher: undefined, + cipherVersion: undefined, + closeCode: NGTCP2_NO_ERROR, + closeFamily: QUIC_ERROR_APPLICATION, + closing: false, + destroyed: false, + earlyData: false, + handshakeComplete: false, + idleTimeout: false, + maxPacketLength: NGTCP2_DEFAULT_MAX_PKTLEN, + servername: undefined, + socket: undefined, + statelessReset: false, + stats: undefined, + pendingStreams: new Set(), + streams: new Map(), + verifyErrorReason: undefined, + verifyErrorCode: undefined, + handshakeAckHistogram: undefined, + handshakeContinuationHistogram: undefined, + highWaterMark: undefined, + defaultEncoding: undefined, + }; constructor(socket, options) { const { @@ -1631,20 +1678,22 @@ class QuicSession extends EventEmitter { super({ captureRejections: true }); this.on('newListener', onNewListener); this.on('removeListener', onRemoveListener); - this.#socket = socket; - this.#servername = servername; - this.#alpn = alpn; - this.#highWaterMark = highWaterMark; - this.#defaultEncoding = defaultEncoding; + const state = this[kInternalState]; + state.socket = socket; + state.servername = servername; + state.alpn = alpn; + state.highWaterMark = highWaterMark; + state.defaultEncoding = defaultEncoding; socket[kAddSession](this); } // kGetStreamOptions is called to get the configured options // for peer initiated QuicStream instances. [kGetStreamOptions]() { + const state = this[kInternalState]; return { - highWaterMark: this.#highWaterMark, - defaultEncoding: this.#defaultEncoding, + highWaterMark: state.highWaterMark, + defaultEncoding: state.defaultEncoding, }; } @@ -1655,16 +1704,17 @@ class QuicSession extends EventEmitter { // must first perform DNS resolution on the provided address // before the underlying QuicSession handle can be created. [kSetHandle](handle) { + const state = this[kInternalState]; this[kHandle] = handle; if (handle !== undefined) { handle[owner_symbol] = this; - this.#handshakeAckHistogram = new Histogram(handle.ack); - this.#handshakeContinuationHistogram = new Histogram(handle.rate); + state.handshakeAckHistogram = new Histogram(handle.ack); + state.handshakeContinuationHistogram = new Histogram(handle.rate); } else { - if (this.#handshakeAckHistogram) - this.#handshakeAckHistogram[kDestroyHistogram](); - if (this.#handshakeContinuationHistogram) - this.#handshakeContinuationHistogram[kDestroyHistogram](); + if (state.handshakeAckHistogram) + state.handshakeAckHistogram[kDestroyHistogram](); + if (state.handshakeContinuationHistogram) + state.handshakeContinuationHistogram[kDestroyHistogram](); } } @@ -1689,9 +1739,10 @@ class QuicSession extends EventEmitter { // Causes the QuicSession to be immediately destroyed, but with // additional metadata set. [kDestroy](statelessReset, family, code) { - this.#statelessReset = !!statelessReset; - this.#closeCode = code; - this.#closeFamily = family; + const state = this[kInternalState]; + state.statelessReset = statelessReset; + state.closeCode = code; + state.closeFamily = family; this.destroy(); } @@ -1702,21 +1753,22 @@ class QuicSession extends EventEmitter { // CONNECTION_CLOSE will be generated and sent, switching // the session into the closing period. [kClose](family, code) { + const state = this[kInternalState]; // Do nothing if the QuicSession has already been destroyed. - if (this.#destroyed) + if (state.destroyed) return; // Set the close code and family so we can keep track. - this.#closeCode = code; - this.#closeFamily = family; + state.closeCode = code; + state.closeFamily = family; // Shutdown all pending streams. These are Streams that // have been created but do not yet have a handle assigned. - for (const stream of this.#pendingStreams) + for (const stream of state.pendingStreams) stream[kClose](family, code); // Shutdown all of the remaining streams - for (const stream of this.#streams.values()) + for (const stream of state.streams.values()) stream[kClose](family, code); // By this point, all necessary RESET_STREAM and @@ -1729,7 +1781,7 @@ class QuicSession extends EventEmitter { // Closes the specified stream with the given code. The // QuicStream object will be destroyed. [kStreamClose](id, code) { - const stream = this.#streams.get(id); + const stream = this[kInternalState].streams.get(id); if (stream === undefined) return; @@ -1740,7 +1792,7 @@ class QuicSession extends EventEmitter { // instance. This will only be called if the ALPN selected // is known to support headers. [kHeaders](id, headers, kind, push_id) { - const stream = this.#streams.get(id); + const stream = this[kInternalState].streams.get(id); if (stream === undefined) return; @@ -1748,7 +1800,7 @@ class QuicSession extends EventEmitter { } [kStreamReset](id, code) { - const stream = this.#streams.get(id); + const stream = this[kInternalState].streams.get(id); if (stream === undefined) return; @@ -1756,16 +1808,18 @@ class QuicSession extends EventEmitter { } [kInspect]() { + // TODO(@jasnell): A proper custom inspect implementation + const state = this[kInternalState]; const obj = { - alpn: this.#alpn, + alpn: state.alpn, cipher: this.cipher, closing: this.closing, closeCode: this.closeCode, destroyed: this.destroyed, - earlyData: this.#earlyData, + earlyData: state.earlyData, maxStreams: this.maxStreams, servername: this.servername, - streams: this.#streams.size, + streams: state.streams.size, stats: { handshakeAck: this.handshakeAckHistogram, handshakeContinuation: this.handshakeContinuationHistogram, @@ -1775,7 +1829,7 @@ class QuicSession extends EventEmitter { } [kSetSocket](socket) { - this.#socket = socket; + this[kInternalState].socket = socket; } // Called at the completion of the TLS handshake for the local peer @@ -1788,15 +1842,16 @@ class QuicSession extends EventEmitter { verifyErrorReason, verifyErrorCode, earlyData) { - this.#handshakeComplete = true; - this.#servername = servername; - this.#alpn = alpn; - this.#cipher = cipher; - this.#cipherVersion = cipherVersion; - this.#maxPacketLength = maxPacketLength; - this.#verifyErrorReason = verifyErrorReason; - this.#verifyErrorCode = verifyErrorCode; - this.#earlyData = earlyData; + const state = this[kInternalState]; + state.handshakeComplete = true; + state.servername = servername; + state.alpn = alpn; + state.cipher = cipher; + state.cipherVersion = cipherVersion; + state.maxPacketLength = maxPacketLength; + state.verifyErrorReason = verifyErrorReason; + state.verifyErrorCode = verifyErrorCode; + state.earlyData = earlyData; if (!this[kHandshakePost]()) return; @@ -1812,18 +1867,19 @@ class QuicSession extends EventEmitter { } [kRemoveStream](stream) { - this.#streams.delete(stream.id); + this[kInternalState].streams.delete(stream.id); } [kAddStream](id, stream) { stream.once('close', this[kMaybeDestroy].bind(this)); - this.#streams.set(id, stream); + this[kInternalState].streams.set(id, stream); } // The QuicSession will be destroyed if closing has been // called and there are no remaining streams [kMaybeDestroy]() { - if (this.#closing && this.#streams.size === 0) + const state = this[kInternalState]; + if (state.closing && state.streams.size === 0) this.destroy(); } @@ -1841,7 +1897,8 @@ class QuicSession extends EventEmitter { // opened. Calls to openStream() will fail, and new streams // from the peer will be rejected/ignored. close(callback) { - if (this.#destroyed) + const state = this[kInternalState]; + if (state.destroyed) throw new ERR_QUICSESSION_DESTROYED('close'); if (callback) { @@ -1853,10 +1910,10 @@ class QuicSession extends EventEmitter { // If we're already closing, do nothing else. // Callback will be invoked once the session // has been destroyed - if (this.#closing) + if (state.closing) return; - this.#closing = true; + state.closing = true; this[kHandle].gracefulClose(); // See if we can close immediately. @@ -1877,11 +1934,12 @@ class QuicSession extends EventEmitter { // Once destroyed, and after the 'error' event (if any), // the close event is emitted on next tick. destroy(error) { + const state = this[kInternalState]; // Destroy can only be called once. Multiple calls will be ignored - if (this.#destroyed) + if (state.destroyed) return; - this.#destroyed = true; - this.#closing = false; + state.destroyed = true; + state.closing = false; if (typeof error === 'number' || (error != null && @@ -1891,19 +1949,19 @@ class QuicSession extends EventEmitter { closeCode, closeFamily } = validateCloseCode(error); - this.#closeCode = closeCode; - this.#closeFamily = closeFamily; + state.closeCode = closeCode; + state.closeFamily = closeFamily; error = new ERR_QUIC_ERROR(closeCode, closeFamily); } // Destroy any pending streams immediately. These // are streams that have been created but have not // yet been assigned an internal handle. - for (const stream of this.#pendingStreams) + for (const stream of state.pendingStreams) stream.destroy(error); // Destroy any remaining streams immediately. - for (const stream of this.#streams.values()) + for (const stream of state.streams.values()) stream.destroy(error); this.removeListener('newListener', onNewListener); @@ -1914,20 +1972,20 @@ class QuicSession extends EventEmitter { const handle = this[kHandle]; if (handle !== undefined) { // Copy the stats for use after destruction - this.#stats = new BigInt64Array(handle.stats); - this.#idleTimeout = !!handle.state[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT]; + state.stats = new BigInt64Array(handle.stats); + state.idleTimeout = !!handle.state[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT]; // Calling destroy will cause a CONNECTION_CLOSE to be // sent to the peer and will destroy the QuicSession // handler immediately. - handle.destroy(this.#closeCode, this.#closeFamily); + handle.destroy(state.closeCode, state.closeFamily); } else { process.nextTick(emit.bind(this, 'close')); } // Remove the QuicSession JavaScript object from the // associated QuicSocket. - this.#socket[kRemoveSession](this); - this.#socket = undefined; + state.socket[kRemoveSession](this); + state.socket = undefined; } // For server QuicSession instances, true if earlyData is @@ -1937,7 +1995,7 @@ class QuicSession extends EventEmitter { // TLS handshake is completed (immeditely before the // secure event is emitted) get usingEarlyData() { - return this.#earlyData; + return this[kInternalState].earlyData; } get maxStreams() { @@ -1951,37 +2009,35 @@ class QuicSession extends EventEmitter { } get address() { - return this.#socket ? this.#socket.address : {}; + return this[kInternalState].socket?.address || {}; } get maxDataLeft() { - return this[kHandle] ? - this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] : 0; + return this[kHandle]?.state[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] || 0; } get bytesInFlight() { - return this[kHandle] ? - this[kHandle].state[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] : 0; + return this[kHandle]?.state[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] || 0; } get blockCount() { - return this[kHandle] ? - this[kHandle].state[IDX_QUIC_SESSION_STATS_BLOCK_COUNT] : 0; + return this[kHandle]?.state[IDX_QUIC_SESSION_STATS_BLOCK_COUNT] || 0; } get authenticated() { // Specifically check for null. Undefined means the check has not // been performed yet, another other value other than null means // there was an error - return this.#verifyErrorReason == null; + return this[kInternalState].verifyErrorReason == null; } get authenticationError() { if (this.authenticated) return undefined; + const state = this[kInternalState]; // eslint-disable-next-line no-restricted-syntax - const err = new Error(this.#verifyErrorReason); - const code = 'ERR_QUIC_VERIFY_' + this.#verifyErrorCode; + const err = new Error(state.verifyErrorReason); + const code = 'ERR_QUIC_VERIFY_' + state.verifyErrorCode; err.name = `Error [${code}]`; err.code = code; return err; @@ -1995,26 +2051,30 @@ class QuicSession extends EventEmitter { } get handshakeComplete() { - return this.#handshakeComplete; + return this[kInternalState].handshakeComplete; } get handshakeConfirmed() { - return Boolean(this[kHandle] ? - this[kHandle].state[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] : 0); + return Boolean( + this[kHandle]?.state[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED]); } get idleTimeout() { - return this.#idleTimeout; + return this[kInternalState].idleTimeout; } get alpnProtocol() { - return this.#alpn; + return this[kInternalState].alpn; } get cipher() { - const name = this.#cipher; - const version = this.#cipherVersion; - return this.handshakeComplete ? { name, version } : {}; + if (!this.handshakeComplete) + return {}; + const state = this[kInternalState]; + return { + name: state.cipher, + version: state.cipherVersion, + }; } getCertificate() { @@ -2035,34 +2095,36 @@ class QuicSession extends EventEmitter { } get servername() { - return this.#servername; + return this[kInternalState].servername; } get destroyed() { - return this.#destroyed; + return this[kInternalState].destroyed; } get closing() { - return this.#closing; + return this[kInternalState].closing; } get closeCode() { + const state = this[kInternalState]; return { - code: this.#closeCode, - family: this.#closeFamily + code: state.closeCode, + family: state.closeFamily }; } get socket() { - return this.#socket; + return this[kInternalState].socket; } get statelessReset() { - return this.#statelessReset; + return this[kInternalState].statelessReset; } openStream(options) { - if (this.#destroyed || this.#closing) + const state = this[kInternalState]; + if (state.destroyed || state.closing) throw new ERR_QUICSESSION_DESTROYED('openStream'); const { halfOpen, // Unidirectional or Bidirectional @@ -2082,13 +2144,13 @@ class QuicSession extends EventEmitter { stream.read(); } - this.#pendingStreams.add(stream); + state.pendingStreams.add(stream); // If early data is being used, we can create the internal QuicStream on the // ready event, that is immediately after the internal QuicSession handle // has been created. Otherwise, we have to wait until the secure event // signaling the completion of the TLS handshake. - const makeStream = QuicSession.#makeStream.bind(this, stream, halfOpen); + const makeStream = QuicSession[kMakeStream].bind(this, stream, halfOpen); let deferred = false; if (this.allowEarlyData && !this.ready) { deferred = true; @@ -2104,8 +2166,8 @@ class QuicSession extends EventEmitter { return stream; } - static #makeStream = function(stream, halfOpen) { - this.#pendingStreams.delete(stream); + static [kMakeStream](stream, halfOpen) { + this[kInternalState].pendingStreams.delete(stream); const handle = halfOpen ? _openUnidirectionalStream(this[kHandle]) : @@ -2118,16 +2180,16 @@ class QuicSession extends EventEmitter { stream[kSetHandle](handle); this[kAddStream](stream.id, stream); - }; + } get duration() { const now = process.hrtime.bigint(); - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return now - stats[IDX_QUIC_SESSION_STATS_CREATED_AT]; } get handshakeDuration() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; const end = this.handshakeComplete ? stats[4] : process.hrtime.bigint(); @@ -2135,85 +2197,86 @@ class QuicSession extends EventEmitter { } get bytesReceived() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_BYTES_RECEIVED]; } get bytesSent() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_BYTES_SENT]; } get bidiStreamCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT]; } get uniStreamCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT]; } get maxInFlightBytes() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT]; } get lossRetransmitCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT]; } get ackDelayRetransmitCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT]; } get peerInitiatedStreamCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT]; } get selfInitiatedStreamCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT]; } get keyUpdateCount() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT]; } get minRTT() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_MIN_RTT]; } get latestRTT() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_LATEST_RTT]; } get smoothedRTT() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_SESSION_STATS_SMOOTHED_RTT]; } updateKey() { + const state = this[kInternalState]; // Initiates a key update for the connection. - if (this.#destroyed || this.#closing) + if (state.destroyed || state.closing) throw new ERR_QUICSESSION_DESTROYED('updateKey'); - if (!this.handshakeConfirmed) + if (!state.handshakeConfirmed) throw new ERR_QUICSESSION_UPDATEKEY(); return this[kHandle].updateKey(); } get handshakeAckHistogram() { - return this.#handshakeAckHistogram; + return this[kInternalState].handshakeAckHistogram; } get handshakeContinuationHistogram() { - return this.#handshakeContinuationHistogram; + return this[kInternalState].handshakeContinuationHistogram; } // TODO(addaleax): This is a temporary solution for testing and should be @@ -2224,7 +2287,9 @@ class QuicSession extends EventEmitter { } class QuicServerSession extends QuicSession { - #contexts = []; + [kInternalServerState] = { + contexts: [] + }; constructor(socket, handle, options) { const { @@ -2254,11 +2319,12 @@ class QuicServerSession extends QuicSession { // Called only when an OCSPRequest event handler is registered. // Allows user code the ability to answer the OCSP query. [kCert](servername, callback) { + const state = this[kInternalServerState]; const { serverSecureContext } = this.socket; let { context } = serverSecureContext; - for (var i = 0; i < this.#contexts.length; i++) { - const elem = this.#contexts[i]; + for (var i = 0; i < state.contexts.length; i++) { + const elem = state.contexts[i]; if (elem[0].test(servername)) { context = elem[1]; break; @@ -2289,27 +2355,30 @@ class QuicServerSession extends QuicSession { servername.replace(/([.^$+?\-\\[\]{}])/g, '\\$1') .replace(/\*/g, '[^.]*') + '$'); - this.#contexts.push([re, _createSecureContext(context)]); + this[kInternalServerState].contexts.push( + [re, _createSecureContext(context)]); } } class QuicClientSession extends QuicSession { - #allowEarlyData = false; - #autoStart = true; - #dcid = undefined; - #handshakeStarted = false; - #ipv6Only = undefined; - #minDHSize = undefined; - #port = undefined; - #ready = 0; - #remoteTransportParams = undefined; - #requestOCSP = undefined; - #secureContext = undefined; - #sessionTicket = undefined; - #transportParams = undefined; - #preferredAddressPolicy; - #verifyHostnameIdentity = true; - #qlogEnabled = false; + [kInternalClientState] = { + allowEarlyData: false, + autoStart: true, + dcid: undefined, + handshakeStarted: false, + ipv6Only: undefined, + minDHSize: undefined, + port: undefined, + ready: 0, + remoteTransportParams: undefined, + requestOCSP: undefined, + secureContext: undefined, + sessionTicket: undefined, + transportParams: undefined, + preferredAddressPolicy: undefined, + verifyHostnameIdentity: true, + qlogEnabled: false, + }; constructor(socket, options) { const sc_options = { @@ -2345,27 +2414,28 @@ class QuicClientSession extends QuicSession { } super(socket, { servername, alpn, highWaterMark, defaultEncoding }); - this.#autoStart = autoStart; - this.#handshakeStarted = autoStart; - this.#dcid = dcid; - this.#ipv6Only = ipv6Only; - this.#minDHSize = minDHSize; - this.#port = port || 0; - this.#preferredAddressPolicy = preferredAddressPolicy; - this.#requestOCSP = requestOCSP; - this.#secureContext = + const state = this[kInternalClientState]; + state.autoStart = autoStart; + state.handshakeStarted = autoStart; + state.dcid = dcid; + state.ipv6Only = ipv6Only; + state.minDHSize = minDHSize; + state.port = port || 0; + state.preferredAddressPolicy = preferredAddressPolicy; + state.requestOCSP = requestOCSP; + state.secureContext = createSecureContext( sc_options, initSecureContextClient); - this.#transportParams = validateTransportParams(options); - this.#verifyHostnameIdentity = verifyHostnameIdentity; - this.#qlogEnabled = qlog; + state.transportParams = validateTransportParams(options); + state.verifyHostnameIdentity = verifyHostnameIdentity; + state.qlogEnabled = qlog; // If provided, indicates that the client is attempting to // resume a prior session. Early data would be enabled. - this.#remoteTransportParams = remoteTransportParams; - this.#sessionTicket = sessionTicket; - this.#allowEarlyData = + state.remoteTransportParams = remoteTransportParams; + state.sessionTicket = sessionTicket; + state.allowEarlyData = remoteTransportParams !== undefined && sessionTicket !== undefined; @@ -2375,7 +2445,7 @@ class QuicClientSession extends QuicSession { [kHandshakePost]() { const { type, size } = this.ephemeralKeyInfo; - if (type === 'DH' && size < this.#minDHSize) { + if (type === 'DH' && size < this[kInternalClientState].minDHSize) { this.destroy(new ERR_TLS_DH_PARAM_SIZE(size)); return false; } @@ -2387,12 +2457,13 @@ class QuicClientSession extends QuicSession { } [kContinueConnect](type, ip) { - setTransportParams(this.#transportParams); + const state = this[kInternalClientState]; + setTransportParams(state.transportParams); const options = - (this.#verifyHostnameIdentity ? + (state.verifyHostnameIdentity ? QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY : 0) | - (this.#requestOCSP ? + (state.requestOCSP ? QUICCLIENTSESSION_OPTION_REQUEST_OCSP : 0); const handle = @@ -2400,23 +2471,23 @@ class QuicClientSession extends QuicSession { this.socket[kHandle], type, ip, - this.#port, - this.#secureContext.context, + state.port, + state.secureContext.context, this.servername || ip, - this.#remoteTransportParams, - this.#sessionTicket, - this.#dcid, - this.#preferredAddressPolicy, + state.remoteTransportParams, + state.sessionTicket, + state.dcid, + state.preferredAddressPolicy, this.alpnProtocol, options, - this.#qlogEnabled, - this.#autoStart); + state.qlogEnabled, + state.autoStart); // We no longer need these, unset them so // memory can be garbage collected. - this.#remoteTransportParams = undefined; - this.#sessionTicket = undefined; - this.#dcid = undefined; + state.remoteTransportParams = undefined; + state.sessionTicket = undefined; + state.dcid = undefined; // If handle is a number, creating the session failed. if (typeof handle === 'number') { @@ -2452,40 +2523,41 @@ class QuicClientSession extends QuicSession { if (this.listenerCount('usePreferredAddress') > 0) toggleListeners(handle, 'usePreferredAddress', true); - this.#maybeReady(0x2); + this[kMaybeReady](0x2); } [kSocketReady]() { - this.#maybeReady(0x1); + this[kMaybeReady](0x1); } // The QuicClientSession is ready for use only after // (a) The QuicSocket has been bound and // (b) The internal handle has been created - #maybeReady = function(flag) { - this.#ready |= flag; + [kMaybeReady](flag) { + this[kInternalClientState].ready |= flag; if (this.ready) process.nextTick(emit.bind(this, 'ready')); - }; + } get allowEarlyData() { - return this.#allowEarlyData; + return this[kInternalClientState].allowEarlyData; } get ready() { - return this.#ready === 0x3; + return this[kInternalClientState].ready === 0x3; } get handshakeStarted() { - return this.#handshakeStarted; + return this[kInternalClientState].handshakeStarted; } startHandshake() { + const state = this[kInternalClientState]; if (this.destroyed) throw new ERR_QUICSESSION_DESTROYED('startHandshake'); - if (this.#handshakeStarted) + if (state.handshakeStarted) return; - this.#handshakeStarted = true; + state.handshakeStarted = true; if (!this.ready) { this.once('ready', () => this[kHandle].startHandshake()); } else { @@ -2499,7 +2571,7 @@ class QuicClientSession extends QuicSession { {}; } - #setSocketAfterBind = function(socket, callback) { + [kSetSocketAfterBind](socket, callback) { if (socket.destroyed) { callback(new ERR_QUICSOCKET_DESTROYED('setSocket')); return; @@ -2518,7 +2590,7 @@ class QuicClientSession extends QuicSession { this[kSetSocket](socket); callback(); - }; + } setSocket(socket, callback) { if (!(socket instanceof QuicSocket)) @@ -2527,7 +2599,7 @@ class QuicClientSession extends QuicSession { if (typeof callback !== 'function') throw new ERR_INVALID_CALLBACK(); - socket[kMaybeBind](() => this.#setSocketAfterBind(socket, callback)); + socket[kMaybeBind](() => this[kSetSocketAfterBind](socket, callback)); } } @@ -2542,19 +2614,21 @@ function streamOnPause() { } class QuicStream extends Duplex { - #closed = false; - #aborted = false; - #defaultEncoding = undefined; - #didRead = false; - #id = undefined; - #highWaterMark = undefined; - #push_id = undefined; - #resetCode = undefined; - #session = undefined; - #dataRateHistogram = undefined; - #dataSizeHistogram = undefined; - #dataAckHistogram = undefined; - #stats = undefined; + [kInternalState] = { + closed: false, + aborted: false, + defaultEncoding: undefined, + didRead: false, + id: undefined, + highWaterMark: undefined, + push_id: undefined, + resetCode: undefined, + session: undefined, + dataRateHistogram: undefined, + dataSizeHistogram: undefined, + dataAckHistogram: undefined, + stats: undefined, + }; constructor(options, session, push_id) { const { @@ -2571,16 +2645,18 @@ class QuicStream extends Duplex { autoDestroy: false, captureRejections: true, }); - this.#highWaterMark = highWaterMark; - this.#defaultEncoding = defaultEncoding; - this.#session = session; - this.#push_id = push_id; + const state = this[kInternalState]; + state.highWaterMark = highWaterMark; + state.defaultEncoding = defaultEncoding; + state.session = session; + state.push_id = push_id; this._readableState.readingMore = true; this.on('pause', streamOnPause); // The QuicStream writes are corked until kSetHandle // is set, ensuring that writes are buffered in JavaScript // until we have somewhere to send them. + // TODO(@jasnell): We need a better mechanism for this. this.cork(); } @@ -2592,28 +2668,29 @@ class QuicStream extends Duplex { // written will be buffered until kSetHandle is called. [kSetHandle](handle) { this[kHandle] = handle; + const state = this[kInternalState]; if (handle !== undefined) { handle.onread = onStreamRead; handle[owner_symbol] = this; this[async_id_symbol] = handle.getAsyncId(); - this.#id = handle.id(); - this.#dataRateHistogram = new Histogram(handle.rate); - this.#dataSizeHistogram = new Histogram(handle.size); - this.#dataAckHistogram = new Histogram(handle.ack); + state.id = handle.id(); + state.dataRateHistogram = new Histogram(handle.rate); + state.dataSizeHistogram = new Histogram(handle.size); + state.dataAckHistogram = new Histogram(handle.ack); this.uncork(); this.emit('ready'); } else { - if (this.#dataRateHistogram) - this.#dataRateHistogram[kDestroyHistogram](); - if (this.#dataSizeHistogram) - this.#dataSizeHistogram[kDestroyHistogram](); - if (this.#dataAckHistogram) - this.#dataAckHistogram[kDestroyHistogram](); + if (state.dataRateHistogram) + state.dataRateHistogram[kDestroyHistogram](); + if (state.dataSizeHistogram) + state.dataSizeHistogram[kDestroyHistogram](); + if (state.dataAckHistogram) + state.dataAckHistogram[kDestroyHistogram](); } } [kStreamReset](code) { - this.#resetCode = code | 0; + this[kInternalState].resetCode = code | 0; this.push(null); this.read(); } @@ -2643,6 +2720,7 @@ class QuicStream extends Duplex { } [kClose](family, code) { + const state = this[kInternalState]; // Trigger the abrupt shutdown of the stream. If the stream is // already no-longer readable or writable, this does nothing. If // the stream is readable or writable, then the abort event will @@ -2653,15 +2731,15 @@ class QuicStream extends Duplex { // having been closed to be destroyed. // Do nothing if we've already been destroyed - if (this.destroyed || this.#closed) + if (this.destroyed || state.closed) return; if (this.pending) return this.once('ready', () => this[kClose](family, code)); - this.#closed = true; + state.closed = true; - this.#aborted = this.readable || this.writable; + state.aborted = this.readable || this.writable; // Trigger scheduling of the RESET_STREAM and STOP_SENDING frames // as appropriate. Notify ngtcp2 that the stream is to be shutdown. @@ -2687,10 +2765,11 @@ class QuicStream extends Duplex { } [kInspect]() { + // TODO(@jasnell): Proper custom inspect implementation const direction = this.bidirectional ? 'bidirectional' : 'unidirectional'; const initiated = this.serverInitiated ? 'server' : 'client'; const obj = { - id: this.#id, + id: this[kInternalState].id, direction, initiated, writableState: this._writableState, @@ -2717,15 +2796,15 @@ class QuicStream extends Duplex { get pending() { // The id is set in the kSetHandle function - return this.#id === undefined; + return this[kInternalState].id === undefined; } get aborted() { - return this.#aborted; + return this[kInternalState].aborted; } get serverInitiated() { - return !!(this.#id & 0b01); + return !!(this[kInternalState].id & 0b01); } get clientInitiated() { @@ -2733,14 +2812,14 @@ class QuicStream extends Duplex { } get unidirectional() { - return !!(this.#id & 0b10); + return !!(this[kInternalState].id & 0b10); } get bidirectional() { return !this.unidirectional; } - #writeGeneric = function(writev, data, encoding, cb) { + [kWriteGeneric](writev, data, encoding, cb) { if (this.destroyed) return; // TODO(addaleax): Can this happen? @@ -2750,7 +2829,7 @@ class QuicStream extends Duplex { // ready event is emitted. if (this.pending) { return this.once('ready', () => { - this.#writeGeneric(writev, data, encoding, cb); + this[kWriteGeneric](writev, data, encoding, cb); }); } @@ -2760,14 +2839,14 @@ class QuicStream extends Duplex { writeGeneric(this, data, encoding, cb); this[kTrackWriteState](this, req.bytes); - }; + } _write(data, encoding, cb) { - this.#writeGeneric(false, data, encoding, cb); + this[kWriteGeneric](false, data, encoding, cb); } _writev(data, cb) { - this.#writeGeneric(true, data, '', cb); + this[kWriteGeneric](true, data, '', cb); } // Called when the last chunk of data has been @@ -2806,19 +2885,20 @@ class QuicStream extends Duplex { this.push(null); return; } - if (!this.#didRead) { + const state = this[kInternalState]; + if (!state.didRead) { this._readableState.readingMore = false; - this.#didRead = true; + state.didRead = true; } streamOnResume.call(this); } sendFile(path, options = {}) { - fs.open(path, 'r', QuicStream.#onFileOpened.bind(this, options)); + fs.open(path, 'r', QuicStream[kOnFileOpened].bind(this, options)); } - static #onFileOpened = function(options, err, fd) { + static [kOnFileOpened](options, err, fd) { const onError = options.onError; if (err) { if (onError) { @@ -2836,10 +2916,10 @@ class QuicStream extends Duplex { } this.sendFD(fd, options, true); - }; + } sendFD(fd, { offset = -1, length = -1 } = {}, ownsFd = false) { - if (this.destroyed || this.#closed) + if (this.destroyed || this[kInternalState].closed) return; validateInteger(offset, 'options.offset', /* min */ -1); @@ -2865,17 +2945,17 @@ class QuicStream extends Duplex { this.end(); defaultTriggerAsyncIdScope(this[async_id_symbol], - QuicStream.#startFilePipe, + QuicStream[kStartFilePipe], this, fd, offset, length); } - static #startFilePipe = (stream, fd, offset, length) => { + static [kStartFilePipe](stream, fd, offset, length) { const handle = new FileHandle(fd, offset, length); - handle.onread = QuicStream.#onPipedFileHandleRead; + handle.onread = QuicStream[kOnPipedFileHandleRead]; handle.stream = stream; const pipe = new StreamPipe(handle, stream[kHandle]); - pipe.onunpipe = QuicStream.#onFileUnpipe; + pipe.onunpipe = QuicStream[kOnFileUnpipe]; pipe.start(); // Exact length of the file doesn't matter here, since the @@ -2884,24 +2964,25 @@ class QuicStream extends Duplex { stream[kTrackWriteState](stream, 1); } - static #onFileUnpipe = function() { // Called on the StreamPipe instance. + static [kOnFileUnpipe]() { // Called on the StreamPipe instance. const stream = this.sink[owner_symbol]; if (stream.ownsFd) this.source.close().catch(stream.destroy.bind(stream)); else this.source.releaseFD(); - }; + } - static #onPipedFileHandleRead = function() { + static [kOnPipedFileHandleRead]() { const err = streamBaseState[kReadBytesOrError]; if (err < 0 && err !== UV_EOF) { this.stream.destroy(errnoException(err, 'sendFD')); } - }; + } get resetReceived() { - return (this.#resetCode !== undefined) ? - { code: this.#resetCode | 0 } : + const state = this[kInternalState]; + return (state.resetCode !== undefined) ? + { code: state.resetCode | 0 } : undefined; } @@ -2911,11 +2992,11 @@ class QuicStream extends Duplex { } get id() { - return this.#id; + return this[kInternalState].id; } get push_id() { - return this.#push_id; + return this[kInternalState].push_id; } close(code) { @@ -2923,17 +3004,18 @@ class QuicStream extends Duplex { } get session() { - return this.#session; + return this[kInternalState].session; } _destroy(error, callback) { - this.#session[kRemoveStream](this); + const state = this[kInternalState]; + state.session[kRemoveStream](this); const handle = this[kHandle]; // Do not use handle after this point as the underlying C++ // object has been destroyed. Any attempt to use the object // will segfault and crash the process. if (handle !== undefined) { - this.#stats = new BigInt64Array(handle.stats); + state.stats = new BigInt64Array(handle.stats); handle.destroy(); } // The destroy callback must be invoked in a nextTick @@ -2945,24 +3027,25 @@ class QuicStream extends Duplex { } get dataRateHistogram() { - return this.#dataRateHistogram; + return this[kInternalState].dataRateHistogram; } get dataSizeHistogram() { - return this.#dataSizeHistogram; + return this[kInternalState].dataSizeHistogram; } get dataAckHistogram() { - return this.#dataAckHistogram; + return this[kInternalState].dataAckHistogram; } pushStream(headers = {}, options = {}) { if (this.destroyed) throw new ERR_QUICSTREAM_DESTROYED('push'); + const state = this[kInternalState]; const { - highWaterMark = this.#highWaterMark, - defaultEncoding = this.#defaultEncoding, + highWaterMark = state.highWaterMark, + defaultEncoding = state.defaultEncoding, } = validateQuicStreamOptions(options); validateObject(headers, 'headers'); @@ -3094,37 +3177,37 @@ class QuicStream extends Duplex { get duration() { const now = process.hrtime.bigint(); - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return now - stats[IDX_QUIC_STREAM_STATS_CREATED_AT]; } get bytesReceived() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_STREAM_STATS_BYTES_RECEIVED]; } get bytesSent() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_STREAM_STATS_BYTES_SENT]; } get maxExtendedOffset() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET]; } get finalSize() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_STREAM_STATS_FINAL_SIZE]; } get maxAcknowledgedOffset() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK]; } get maxReceivedOffset() { - const stats = this.#stats || this[kHandle].stats; + const stats = this[kInternalState].stats || this[kHandle].stats; return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV]; } } From 31d6d9d0f7cc8e39bc183d66961ec09033a621c2 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 30 Jun 2020 14:24:52 -0700 Subject: [PATCH 23/25] quic: reduce duplication of code PR-URL: https://github.com/nodejs/node/pull/34137 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier --- lib/internal/quic/core.js | 131 +++++++++++++++----------------------- 1 file changed, 53 insertions(+), 78 deletions(-) diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index a052ba6e2f3d03..446bec3761374b 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -147,6 +147,7 @@ const { IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT, IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT, IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT, + IDX_QUIC_SESSION_STATS_HANDSHAKE_COMPLETED_AT, IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT, IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT, IDX_QUIC_SESSION_STATS_BLOCK_COUNT, @@ -625,6 +626,13 @@ function onRemoveListener(event) { toggleListeners(this[kHandle], event, false); } +function getStats(obj, idx) { + const stats = obj[kHandle]?.stats || obj[kInternalState].stats; + // If stats is undefined at this point, it's just a bug + assert(stats); + return stats[idx]; +} + // QuicEndpoint wraps a UDP socket and is owned // by a QuicSocket. It does not exist independently // of the QuicSocket. @@ -1524,77 +1532,65 @@ class QuicSocket extends EventEmitter { this[kHandle].setServerBusy(on); } + get serverBusy() { + return this[kInternalState].serverBusy; + } + get duration() { // TODO(@jasnell): If the object is destroyed, it should // use a fixed duration rather than calculating from now - const now = process.hrtime.bigint(); - const stats = this[kInternalState].stats || this[kHandle].stats; - return now - stats[IDX_QUIC_SOCKET_STATS_CREATED_AT]; + return process.hrtime.bigint() - + getStats(this, IDX_QUIC_SOCKET_STATS_CREATED_AT); } get boundDuration() { // TODO(@jasnell): If the object is destroyed, it should // use a fixed duration rather than calculating from now - const now = process.hrtime.bigint(); - const stats = this[kInternalState].stats || this[kHandle].stats; - return now - stats[IDX_QUIC_SOCKET_STATS_BOUND_AT]; + return process.hrtime.bigint() - + getStats(this, IDX_QUIC_SOCKET_STATS_BOUND_AT); } get listenDuration() { // TODO(@jasnell): If the object is destroyed, it should // use a fixed duration rather than calculating from now - const now = process.hrtime.bigint(); - const stats = this[kInternalState].stats || this[kHandle].stats; - return now - stats[IDX_QUIC_SOCKET_STATS_LISTEN_AT]; + return process.hrtime.bigint() - + getStats(this, IDX_QUIC_SOCKET_STATS_LISTEN_AT); } get bytesReceived() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED]; + return getStats(this, IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED); } get bytesSent() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_BYTES_SENT]; + return getStats(this, IDX_QUIC_SOCKET_STATS_BYTES_SENT); } get packetsReceived() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED]; + return getStats(this, IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED); } get packetsSent() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_PACKETS_SENT]; + return getStats(this, IDX_QUIC_SOCKET_STATS_PACKETS_SENT); } get packetsIgnored() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED]; - } - - get serverBusy() { - return this[kInternalState].serverBusy; + return getStats(this, IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED); } get serverSessions() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS]; + return getStats(this, IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS); } get clientSessions() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS]; + return getStats(this, IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS); } get statelessResetCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT]; + return getStats(this, IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT); } get serverBusyCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SOCKET_STATS_SERVER_BUSY_COUNT]; + return getStats(this, IDX_QUIC_SOCKET_STATS_SERVER_BUSY_COUNT); } // Diagnostic packet loss is a testing mechanism that allows simulating @@ -2183,82 +2179,68 @@ class QuicSession extends EventEmitter { } get duration() { - const now = process.hrtime.bigint(); - const stats = this[kInternalState].stats || this[kHandle].stats; - return now - stats[IDX_QUIC_SESSION_STATS_CREATED_AT]; + return process.hrtime.bigint() - + getStats(this, IDX_QUIC_SESSION_STATS_CREATED_AT); } get handshakeDuration() { - const stats = this[kInternalState].stats || this[kHandle].stats; const end = this.handshakeComplete ? - stats[4] : process.hrtime.bigint(); - return end - stats[IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT]; + getStats(this, IDX_QUIC_SESSION_STATS_HANDSHAKE_COMPLETED_AT) : + process.hrtime.bigint(); + return end - getStats(this, IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT); } get bytesReceived() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_BYTES_RECEIVED]; + return getStats(this, IDX_QUIC_SESSION_STATS_BYTES_RECEIVED); } get bytesSent() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_BYTES_SENT]; + return getStats(this, IDX_QUIC_SESSION_STATS_BYTES_SENT); } get bidiStreamCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT]; + return getStats(this, IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT); } get uniStreamCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT]; + return getStats(this, IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT); } get maxInFlightBytes() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT]; + return getStats(this, IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT); } get lossRetransmitCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT]; + return getStats(this, IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT); } get ackDelayRetransmitCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT]; + return getStats(this, IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT); } get peerInitiatedStreamCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT]; + return getStats(this, IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT); } get selfInitiatedStreamCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT]; + return getStats(this, IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT); } get keyUpdateCount() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT]; + return getStats(this, IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT); } get minRTT() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_MIN_RTT]; + return getStats(this, IDX_QUIC_SESSION_STATS_MIN_RTT); } get latestRTT() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_LATEST_RTT]; + return getStats(this, IDX_QUIC_SESSION_STATS_LATEST_RTT); } get smoothedRTT() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_SESSION_STATS_SMOOTHED_RTT]; + return getStats(this, IDX_QUIC_SESSION_STATS_SMOOTHED_RTT); } updateKey() { @@ -3176,39 +3158,32 @@ class QuicStream extends Duplex { } get duration() { - const now = process.hrtime.bigint(); - const stats = this[kInternalState].stats || this[kHandle].stats; - return now - stats[IDX_QUIC_STREAM_STATS_CREATED_AT]; + return process.hrtime.bigint() - + getStats(this, IDX_QUIC_STREAM_STATS_CREATED_AT); } get bytesReceived() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_STREAM_STATS_BYTES_RECEIVED]; + return getStats(this, IDX_QUIC_STREAM_STATS_BYTES_RECEIVED); } get bytesSent() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_STREAM_STATS_BYTES_SENT]; + return getStats(this, IDX_QUIC_STREAM_STATS_BYTES_SENT); } get maxExtendedOffset() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET]; + return getStats(this, IDX_QUIC_STREAM_STATS_MAX_OFFSET); } get finalSize() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_STREAM_STATS_FINAL_SIZE]; + return getStats(this, IDX_QUIC_STREAM_STATS_FINAL_SIZE); } get maxAcknowledgedOffset() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK]; + return getStats(this, IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK); } get maxReceivedOffset() { - const stats = this[kInternalState].stats || this[kHandle].stats; - return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV]; + return getStats(this, IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV); } } From 584fc7e965d0425346325a03c3288c6f885ae649 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sat, 27 Jun 2020 21:25:46 -0700 Subject: [PATCH 24/25] doc: clarify ambiguous rdev description Replace "is considered 'special'" with "represents a device". The latter is more clear and is derived from the stat() man page. The former is also derived from the man page, but is ambiguous because: * "considered" introduces doubt. Is it, or isn't it? * "special" is never defined. "represents a device" communicates more meaning. * "special" is in scare-quotes, introducing more doubt as to what exactly it means. Refs: https://man7.org/linux/man-pages/man2/fstat.2.html PR-URL: https://github.com/nodejs/node/pull/34094 Reviewed-By: Anna Henningsen Reviewed-By: Ujjwal Sharma --- doc/api/fs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 6ce2fded3754c8..95c8b9fdbc4168 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -888,7 +888,7 @@ The numeric group identifier of the group that owns the file (POSIX). * {number|bigint} -A numeric device identifier if the file is considered "special". +A numeric device identifier if the file represents a device. ### `stats.size` From 8ff8c684d6f808f69ff13dbaa4f591a699a502b3 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sat, 27 Jun 2020 22:22:12 -0700 Subject: [PATCH 25/25] doc: clarify O_EXCL text in fs.md PR-URL: https://github.com/nodejs/node/pull/34096 Reviewed-By: Anna Henningsen Reviewed-By: Ujjwal Sharma --- doc/api/fs.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index 95c8b9fdbc4168..9969412dbdb48f 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -5841,10 +5841,10 @@ are available from `fs.constants`. On Windows, flags are translated to their equivalent ones where applicable, e.g. `O_WRONLY` to `FILE_GENERIC_WRITE`, or `O_EXCL|O_CREAT` to `CREATE_NEW`, as accepted by `CreateFileW`. -The exclusive flag `'x'` (`O_EXCL` flag in open(2)) ensures that path is newly -created. On POSIX systems, path is considered to exist even if it is a symlink -to a non-existent file. The exclusive flag may or may not work with network -file systems. +The exclusive flag `'x'` (`O_EXCL` flag in open(2)) causes the operation to +return an error if the path already exists. On POSIX, if the path is a symbolic +link, using `O_EXCL` returns an error even if the link is to a path that does +not exist. The exclusive flag may or may not work with network file systems. On Linux, positional writes don't work when the file is opened in append mode. The kernel ignores the position argument and always appends the data to