From 18be4761162d2365159862c8e6cd67c18a90161a Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Mon, 16 Oct 2017 22:31:52 -0700 Subject: [PATCH 001/218] benchmark: fix punycode test for --without-intl PR-URL: https://github.com/nodejs/node/pull/16251 Reviewed-By: Anna Henningsen Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- benchmark/misc/punycode.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/benchmark/misc/punycode.js b/benchmark/misc/punycode.js index 630aea3195f098..40bcd70302003c 100644 --- a/benchmark/misc/punycode.js +++ b/benchmark/misc/punycode.js @@ -1,11 +1,14 @@ 'use strict'; const common = require('../common.js'); -const icu = process.binding('icu'); +let icu; +try { + icu = process.binding('icu'); +} catch (err) {} const punycode = require('punycode'); const bench = common.createBenchmark(main, { - method: ['punycode', 'icu'], + method: ['punycode'].concat(icu !== undefined ? ['icu'] : []), n: [1024], val: [ 'افغانستا.icom.museum', @@ -69,8 +72,11 @@ function main(conf) { runPunycode(n, val); break; case 'icu': - runICU(n, val); - break; + if (icu !== undefined) { + runICU(n, val); + break; + } + // fallthrough default: throw new Error('Unexpected method'); } From a653f23dfc2ac7c0717ffcb87bb4a7f5981169df Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 13 Mar 2018 20:42:33 +0100 Subject: [PATCH 002/218] =?UTF-8?q?fs:=20fix=20`createReadStream(=E2=80=A6?= =?UTF-8?q?,=20{end:=20n})`=20for=20non-seekable=20fds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 82bdf8fba2d3f fixed an issue by silently modifying the `start` option for the case when only `end` is passed, in order to perform reads from a specified range in the file. However, that approach does not work for non-seekable files, since a numeric `start` option means that positioned reads will be used to read data from the file. This patch fixes that, and instead ends reading after a specified size by adjusting the read buffer size. This way we avoid re-introducing the bug that 82bdf8fba2d3f fixed, and align behaviour with the native file stream mechanism introduced in https://github.com/nodejs/node/pull/18936 as well. Backport-PR-URL: https://github.com/nodejs/node/pull/19410 PR-URL: https://github.com/nodejs/node/pull/19329 Fixes: https://github.com/nodejs/node/issues/19240 Refs: https://github.com/nodejs/node/pull/18121 Reviewed-By: James M Snell Reviewed-By: Matteo Collina Reviewed-By: Chen Gang --- lib/fs.js | 11 +++++++++-- test/parallel/test-fs-read-stream.js | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index bfd085b4213881..b5565201e2abdd 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1950,8 +1950,7 @@ function ReadStream(path, options) { this.flags = options.flags === undefined ? 'r' : options.flags; this.mode = options.mode === undefined ? 0o666 : options.mode; - this.start = typeof this.fd !== 'number' && options.start === undefined ? - 0 : options.start; + this.start = options.start; this.end = options.end; this.autoClose = options.autoClose === undefined ? true : options.autoClose; this.pos = undefined; @@ -1974,6 +1973,12 @@ function ReadStream(path, options) { this.pos = this.start; } + // Backwards compatibility: Make sure `end` is a number regardless of `start`. + // TODO(addaleax): Make the above typecheck not depend on `start` instead. + // (That is a semver-major change). + if (typeof this.end !== 'number') + this.end = Infinity; + if (typeof this.fd !== 'number') this.open(); @@ -2028,6 +2033,8 @@ ReadStream.prototype._read = function(n) { if (this.pos !== undefined) toRead = Math.min(this.end - this.pos + 1, toRead); + else + toRead = Math.min(this.end - this.bytesRead + 1, toRead); // already read everything we were supposed to read! // treat as EOF. diff --git a/test/parallel/test-fs-read-stream.js b/test/parallel/test-fs-read-stream.js index 6748c68b52596c..11d1c2f63ff507 100644 --- a/test/parallel/test-fs-read-stream.js +++ b/test/parallel/test-fs-read-stream.js @@ -22,6 +22,7 @@ 'use strict'; const common = require('../common'); +const child_process = require('child_process'); const assert = require('assert'); const fs = require('fs'); const fixtures = require('../common/fixtures'); @@ -171,6 +172,31 @@ assert.throws(function() { })); } +if (!common.isWindows) { + // Verify that end works when start is not specified, and we do not try to + // use positioned reads. This makes sure that this keeps working for + // non-seekable file descriptors. + common.refreshTmpDir(); + const filename = `${common.tmpDir}/foo.pipe`; + const mkfifoResult = child_process.spawnSync('mkfifo', [filename]); + if (!mkfifoResult.error) { + child_process.exec(`echo "xyz foobar" > '${filename}'`); + const stream = new fs.createReadStream(filename, { end: 1 }); + stream.data = ''; + + stream.on('data', function(chunk) { + stream.data += chunk; + }); + + stream.on('end', common.mustCall(function() { + assert.strictEqual('xy', stream.data); + fs.unlinkSync(filename); + })); + } else { + common.printSkipMessage('mkfifo not available'); + } +} + { // pause and then resume immediately. const pauseRes = fs.createReadStream(rangeFile); From e0e0ef7bab0b80e8ba76c8ecaf1eb0c8e2a365cc Mon Sep 17 00:00:00 2001 From: buji Date: Mon, 13 Nov 2017 19:44:16 +0800 Subject: [PATCH 003/218] util: escaping object keys in util.inspect() PR-URL: https://github.com/nodejs/node/pull/16986 Fixes: https://github.com/nodejs/node/issues/16979 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Anatoli Papirovski --- lib/util.js | 32 +----------------------------- test/parallel/test-util-inspect.js | 14 +++++++++---- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/lib/util.js b/lib/util.js index 0b1d25ba22a520..4c6db1a479e7b3 100644 --- a/lib/util.js +++ b/lib/util.js @@ -77,9 +77,7 @@ var Debug; /* eslint-disable */ const strEscapeSequencesRegExp = /[\x00-\x1f\x27\x5c]/; -const keyEscapeSequencesRegExp = /[\x00-\x1f\x27]/; const strEscapeSequencesReplacer = /[\x00-\x1f\x27\x5c]/g; -const keyEscapeSequencesReplacer = /[\x00-\x1f\x27]/g; /* eslint-enable */ const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/; const colorRegExp = /\u001b\[\d\d?m/g; @@ -133,34 +131,6 @@ function strEscape(str) { return `'${result}'`; } -// Escape control characters and single quotes. -// Note: for performance reasons this is not combined with strEscape -function keyEscape(str) { - if (str.length < 5000 && !keyEscapeSequencesRegExp.test(str)) - return `'${str}'`; - if (str.length > 100) - return `'${str.replace(keyEscapeSequencesReplacer, escapeFn)}'`; - var result = ''; - var last = 0; - for (var i = 0; i < str.length; i++) { - const point = str.charCodeAt(i); - if (point === 39 || point < 32) { - if (last === i) { - result += meta[point]; - } else { - result += `${str.slice(last, i)}${meta[point]}`; - } - last = i + 1; - } - } - if (last === 0) { - result = str; - } else if (last !== i) { - result += str.slice(last); - } - return `'${result}'`; -} - function tryStringify(arg) { try { return JSON.stringify(arg); @@ -851,7 +821,7 @@ function formatProperty(ctx, value, recurseTimes, key, array) { } else if (keyStrRegExp.test(key)) { name = ctx.stylize(key, 'name'); } else { - name = ctx.stylize(keyEscape(key), 'string'); + name = ctx.stylize(strEscape(key), 'string'); } return `${name}: ${str}`; diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 81c669fa126d8e..d9f8e30616657d 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -558,25 +558,31 @@ assert.doesNotThrow(() => { assert.strictEqual(util.inspect(x).includes('inspect'), true); } -// util.inspect should not display the escaped value of a key. +// util.inspect should display the escaped value of a key. { const w = { '\\': 1, '\\\\': 2, '\\\\\\': 3, '\\\\\\\\': 4, + '\n': 5, + '\r': 6 }; const y = ['a', 'b', 'c']; - y['\\\\\\'] = 'd'; + y['\\\\'] = 'd'; + y['\n'] = 'e'; + y['\r'] = 'f'; assert.strictEqual( util.inspect(w), - '{ \'\\\': 1, \'\\\\\': 2, \'\\\\\\\': 3, \'\\\\\\\\\': 4 }' + '{ \'\\\\\': 1, \'\\\\\\\\\': 2, \'\\\\\\\\\\\\\': 3, ' + + '\'\\\\\\\\\\\\\\\\\': 4, \'\\n\': 5, \'\\r\': 6 }' ); assert.strictEqual( util.inspect(y), - '[ \'a\', \'b\', \'c\', \'\\\\\\\': \'d\' ]' + '[ \'a\', \'b\', \'c\', \'\\\\\\\\\': \'d\', ' + + '\'\\n\': \'e\', \'\\r\': \'f\' ]' ); } From 1d882665432a951d41cba3e75230d6257d38e263 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Wed, 17 Jan 2018 15:44:49 +0100 Subject: [PATCH 004/218] http: free the parser before emitting 'upgrade' Ensure that the parser is freed before emitting the 'connect' or 'upgrade' event. PR-URL: https://github.com/nodejs/node/pull/18209 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Jon Moss Reviewed-By: Fedor Indutny Reviewed-By: Ruben Bridgewater --- lib/_http_client.js | 2 +- .../test-http-parser-freed-before-upgrade.js | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-http-parser-freed-before-upgrade.js diff --git a/lib/_http_client.js b/lib/_http_client.js index 0170b2c9b0e6fb..a836b772cc05ad 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -453,6 +453,7 @@ function socketOnData(d) { socket.removeListener('data', socketOnData); socket.removeListener('end', socketOnEnd); parser.finish(); + freeParser(parser, req, socket); var bodyHead = d.slice(bytesParsed, d.length); @@ -475,7 +476,6 @@ function socketOnData(d) { // Got Upgrade header or CONNECT method, but have no handler. socket.destroy(); } - freeParser(parser, req, socket); } else if (parser.incoming && parser.incoming.complete && // When the status code is 100 (Continue), the server will // send a final response after this client sends a request diff --git a/test/parallel/test-http-parser-freed-before-upgrade.js b/test/parallel/test-http-parser-freed-before-upgrade.js new file mode 100644 index 00000000000000..4ba1de9501681c --- /dev/null +++ b/test/parallel/test-http-parser-freed-before-upgrade.js @@ -0,0 +1,33 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(); + +server.on('upgrade', common.mustCall((request, socket) => { + assert.strictEqual(socket.parser, null); + socket.write([ + 'HTTP/1.1 101 Switching Protocols', + 'Connection: Upgrade', + 'Upgrade: WebSocket', + '\r\n' + ].join('\r\n')); +})); + +server.listen(common.mustCall(() => { + const request = http.get({ + port: server.address().port, + headers: { + Connection: 'Upgrade', + Upgrade: 'WebSocket' + } + }); + + request.on('upgrade', common.mustCall((response, socket) => { + assert.strictEqual(socket.parser, null); + socket.destroy(); + server.close(); + })); +})); From 37e594ed4a63c0c3893b34c6d398001b2a7150e1 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Thu, 18 Jan 2018 18:39:02 +0100 Subject: [PATCH 005/218] stream: remove unreachable code To avoid a function call `BufferList.prototype.concat()` is not called when there is only a buffer in the list. That buffer is instead accessed directly. PR-URL: https://github.com/nodejs/node/pull/18239 Reviewed-By: Matteo Collina --- lib/internal/streams/BufferList.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/internal/streams/BufferList.js b/lib/internal/streams/BufferList.js index 23d5a8a2db0eb7..b2daf82e74190b 100644 --- a/lib/internal/streams/BufferList.js +++ b/lib/internal/streams/BufferList.js @@ -61,8 +61,6 @@ module.exports = class BufferList { concat(n) { if (this.length === 0) return Buffer.alloc(0); - if (this.length === 1) - return this.head.data; const ret = Buffer.allocUnsafe(n >>> 0); var p = this.head; var i = 0; From 6f3fb46541673559a9c21fe95e704a465ee80d82 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Thu, 18 Jan 2018 18:51:09 +0100 Subject: [PATCH 006/218] test: use correct size in test-stream-buffer-list The `n` argument of `BufferList.prototype.concat()` is not the number of `Buffer` instances in the list, but their total length when concatenated. PR-URL: https://github.com/nodejs/node/pull/18239 Reviewed-By: Matteo Collina --- test/parallel/test-stream-buffer-list.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-stream-buffer-list.js b/test/parallel/test-stream-buffer-list.js index ddbff452de4be9..6ea359b458f61b 100644 --- a/test/parallel/test-stream-buffer-list.js +++ b/test/parallel/test-stream-buffer-list.js @@ -14,14 +14,19 @@ assert.strictEqual(emptyList.join(','), ''); assert.deepStrictEqual(emptyList.concat(0), Buffer.alloc(0)); +const buf = Buffer.from('foo'); + // Test buffer list with one element. const list = new BufferList(); -list.push('foo'); +list.push(buf); + +const copy = list.concat(3); -assert.strictEqual(list.concat(1), 'foo'); +assert.notStrictEqual(copy, buf); +assert.deepStrictEqual(copy, buf); assert.strictEqual(list.join(','), 'foo'); const shifted = list.shift(); -assert.strictEqual(shifted, 'foo'); +assert.strictEqual(shifted, buf); assert.deepStrictEqual(list, new BufferList()); From bf523822ba856f1baa456eecee2173b56527397d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=88=9A?= Date: Sat, 20 Jan 2018 08:19:05 +0800 Subject: [PATCH 007/218] stream: simplify `src._readableState` to `state` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/18264 Reviewed-By: Weijia Wang Reviewed-By: Luigi Pinca Reviewed-By: Michaël Zasso Reviewed-By: Matteo Collina --- lib/_stream_readable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index a75f3fa74cba22..e890da252116da 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -645,8 +645,8 @@ Readable.prototype.pipe = function(dest, pipeOpts) { if (((state.pipesCount === 1 && state.pipes === dest) || (state.pipesCount > 1 && state.pipes.indexOf(dest) !== -1)) && !cleanedUp) { - debug('false write response, pause', src._readableState.awaitDrain); - src._readableState.awaitDrain++; + debug('false write response, pause', state.awaitDrain); + state.awaitDrain++; increasedAwaitDrain = true; } src.pause(); From e84e9db6fe2ffe2f8da6716ddae2254797aaff7a Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Fri, 12 Jan 2018 02:07:18 +0200 Subject: [PATCH 008/218] doc: capitalize non-primitive types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport-PR-URL: https://github.com/nodejs/node/pull/18465 PR-URL: https://github.com/nodejs/node/pull/18111 Reviewed-By: Сковорода Никита Андреевич Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- doc/api/assert.md | 2 +- doc/api/cluster.md | 2 +- doc/api/http2.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index 561d59bdc2932f..04d46751793ec3 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -276,7 +276,7 @@ added: v0.1.21 * `expected` {any} * `message` {any} * `operator` {string} **Default:** '!=' -* `stackStartFunction` {function} **Default:** `assert.fail` +* `stackStartFunction` {Function} **Default:** `assert.fail` Throws an `AssertionError`. If `message` is falsy, the error message is set as the values of `actual` and `expected` separated by the provided `operator`. diff --git a/doc/api/cluster.md b/doc/api/cluster.md index d0c801d53ee44e..00db4a9b5c8915 100644 --- a/doc/api/cluster.md +++ b/doc/api/cluster.md @@ -752,7 +752,7 @@ changes: `'ipc'` entry. When this option is provided, it overrides `silent`. * `uid` {number} Sets the user identity of the process. (See setuid(2).) * `gid` {number} Sets the group identity of the process. (See setgid(2).) - * `inspectPort` {number|function} Sets inspector port of worker. + * `inspectPort` {number|Function} Sets inspector port of worker. This can be a number, or a function that takes no arguments and returns a number. By default each worker gets its own port, incremented from the master's `process.debugPort`. diff --git a/doc/api/http2.md b/doc/api/http2.md index a0d5371846f3fa..31afa378f274ea 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1045,7 +1045,7 @@ added: v8.4.0 * `options` {Object} * `endStream` {boolean} Set to `true` to indicate that the response will not include payload data. - * `getTrailers` {function} Callback function invoked to collect trailer + * `getTrailers` {Function} Callback function invoked to collect trailer headers. * Returns: {undefined} From 5209f9e1e20d286a044b5d54c61aeb8b3557a357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Thu, 25 Jan 2018 16:50:05 +0100 Subject: [PATCH 009/218] doc: warn about GCM authenticity PR-URL: https://github.com/nodejs/node/pull/18376 Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- doc/api/crypto.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 5813b44a71a396..90be2d085ae0be 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -371,6 +371,14 @@ received _authentication tag_. If no tag is provided, or if the cipher text has been tampered with, [`decipher.final()`][] will throw, indicating that the cipher text should be discarded due to failed authentication. +Note that this Node.js version does not verify the length of GCM authentication +tags. Such a check *must* be implemented by applications and is crucial to the +authenticity of the encrypted data, otherwise, an attacker can use an +arbitrarily short authentication tag to increase the chances of successfully +passing authentication (up to 0.39%). It is highly recommended to associate one +of the values 16, 15, 14, 13, 12, 8 or 4 bytes with each key, and to only permit +authentication tags of that length, see [NIST SP 800-38D][]. + The `decipher.setAuthTag()` method must be called before [`decipher.final()`][]. @@ -2288,6 +2296,7 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. [HTML5's `keygen` element]: https://www.w3.org/TR/html5/forms.html#the-keygen-element [NIST SP 800-131A]: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf [NIST SP 800-132]: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf +[NIST SP 800-38D]: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [Nonce-Disrespecting Adversaries]: https://github.com/nonce-disrespect/nonce-disrespect [OpenSSL's SPKAC implementation]: https://www.openssl.org/docs/man1.0.2/apps/spkac.html [RFC 2412]: https://www.rfc-editor.org/rfc/rfc2412.txt From 39ea4f12c5e0c9490a885e84ea3281202779bd9a Mon Sep 17 00:00:00 2001 From: Roman Reiss Date: Mon, 29 Jan 2018 21:28:48 +0100 Subject: [PATCH 010/218] doc: fix manpage warnings Fixes: https://github.com/nodejs/node/issues/18434 Reviewed-By: Ben Noordhuis Reviewed-By: Daniel Bevenius Reviewed-By: James M Snell Reviewed-By: Jeremiah Senkpiel Reviewed-By: Colin Ihrig --- doc/node.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/node.1 b/doc/node.1 index 3dfadc699c8749..50b8668007fdae 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -37,9 +37,9 @@ node \- Server-side JavaScript runtime .RI [ script.js \ | .B -e .RI \&" script \&" -.R | +.RI | .B - -.R ] +.RI ] .B [--] .RI [ arguments ] .br From 142ad8d450e3d56e796d397b2c786b0ee00b8cbc Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Wed, 24 Jan 2018 10:50:50 -0800 Subject: [PATCH 011/218] doc: reorder section on updating PR branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It makes more sense to provide instructions on how to update the PR branch before instructions on pushing the commit. PR-URL: https://github.com/nodejs/node/pull/18355 Reviewed-By: Anna Henningsen Reviewed-By: Michaël Zasso Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Jon Moss --- COLLABORATOR_GUIDE.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index 584e598c2ff38c..08b506f45683b8 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -593,20 +593,20 @@ Validate that the commit message is properly formatted using $ git rev-list upstream/master...HEAD | xargs core-validate-commit ``` +Optional: When landing your own commits, force push the amended commit to the +branch you used to open the pull request. If your branch is called `bugfix`, +then the command would be `git push --force-with-lease origin master:bugfix`. +When the pull request is closed, this will cause the pull request to +show the purple merged status rather than the red closed status that is +usually used for pull requests that weren't merged. + Time to push it: ```text $ git push upstream master ``` -* Optional: Force push the amended commit to the branch you used to -open the pull request. If your branch is called `bugfix`, then the -command would be `git push --force-with-lease origin master:bugfix`. -When the pull request is closed, this will cause the pull request to -show the purple merged status rather than the red closed status that is -usually used for pull requests that weren't merged. Only do this when -landing your own contributions. -* Close the pull request with a "Landed in ``" comment. If +Close the pull request with a "Landed in ``" comment. If your pull request shows the purple merged status then you should still add the "Landed in .." comment if you added multiple commits. From 5d03c8219acbb0d0da4206138a9cb0951dc07f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Tue, 30 Jan 2018 23:40:29 +0100 Subject: [PATCH 012/218] url: simplify loop in parser PR-URL: https://github.com/nodejs/node/pull/18468 Reviewed-By: Ruben Bridgewater Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- lib/url.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/url.js b/lib/url.js index 2cc4488a3edfd9..6562be5d8302e6 100644 --- a/lib/url.js +++ b/lib/url.js @@ -880,7 +880,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { // if the path is allowed to go above the root, restore leading ..s if (!mustEndAbs && !removeAllDots) { - for (; up--; up) { + while (up--) { srcPath.unshift('..'); } } From 7feeb1574e04f12bc25083d6abaa0f288bae83d4 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 31 Jan 2018 09:35:31 -0800 Subject: [PATCH 013/218] doc: add Gibson Fahnestock to TSC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Welcome Gibson to the TSC! PR-URL: https://github.com/nodejs/node/pull/18481 Reviewed-By: Colin Ihrig Reviewed-By: Michaël Zasso Reviewed-By: Ali Ijaz Sheikh Reviewed-By: Daniel Bevenius Reviewed-By: Jon Moss Reviewed-By: Sakthipriyan Vairamani Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Evan Lucas Reviewed-By: Сковорода Никита Андреевич Reviewed-By: Richard Lau Reviewed-By: Michael Dawson --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 89f9e3998a2827..aa09f812a16be8 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,8 @@ For more information about the governance of the Node.js project, see **Franziska Hinkelmann** <franziska.hinkelmann@gmail.com> (she/her) * [Fishrock123](https://github.com/Fishrock123) - **Jeremiah Senkpiel** <fishrock123@rocketmail.com> +* [gibfahn](https://github.com/gibfahn) - +**Gibson Fahnestock** <gibfahn@gmail.com> (he/him) * [indutny](https://github.com/indutny) - **Fedor Indutny** <fedor.indutny@gmail.com> * [jasnell](https://github.com/jasnell) - From a1fb2638808473937e71bb9941033f454b12d290 Mon Sep 17 00:00:00 2001 From: Bryan English Date: Thu, 26 Oct 2017 16:20:45 -0700 Subject: [PATCH 014/218] test: fix flaky test-http-dns-error Under some conditions, the error received from getaddrinfo might actually be EAGAIN, meaning the request should be retried. Allowing for 5 retries before erroring out. Also replace one-off function with common.mustNotCall(). PR-URL: https://github.com/nodejs/node/pull/16534 Reviewed-By: Refael Ackermann Reviewed-By: James M Snell --- test/parallel/test-http-dns-error.js | 35 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/test/parallel/test-http-dns-error.js b/test/parallel/test-http-dns-error.js index 723b710647682b..900cf40e6b209b 100644 --- a/test/parallel/test-http-dns-error.js +++ b/test/parallel/test-http-dns-error.js @@ -30,30 +30,41 @@ const http = require('http'); const https = require('https'); const host = '*'.repeat(256); +const MAX_TRIES = 5; -function do_not_call() { - throw new Error('This function should not have been called.'); -} - -function test(mod) { - +function tryGet(mod, tries) { // Bad host name should not throw an uncatchable exception. // Ensure that there is time to attach an error listener. - const req1 = mod.get({ host: host, port: 42 }, do_not_call); - req1.on('error', common.mustCall(function(err) { + const req = mod.get({ host: host, port: 42 }, common.mustNotCall()); + req.on('error', common.mustCall(function(err) { + if (err.code === 'EAGAIN' && tries < MAX_TRIES) { + tryGet(mod, ++tries); + return; + } assert.strictEqual(err.code, 'ENOTFOUND'); })); // http.get() called req1.end() for us +} - const req2 = mod.request({ +function tryRequest(mod, tries) { + const req = mod.request({ method: 'GET', host: host, port: 42 - }, do_not_call); - req2.on('error', common.mustCall(function(err) { + }, common.mustNotCall()); + req.on('error', common.mustCall(function(err) { + if (err.code === 'EAGAIN' && tries < MAX_TRIES) { + tryRequest(mod, ++tries); + return; + } assert.strictEqual(err.code, 'ENOTFOUND'); })); - req2.end(); + req.end(); +} + +function test(mod) { + tryGet(mod, 0); + tryRequest(mod, 0); } if (common.hasCrypto) { From c0f40be23b2b48875166180d1a1507c2f946a849 Mon Sep 17 00:00:00 2001 From: Ivan Filenko Date: Wed, 24 Jan 2018 01:42:40 +0300 Subject: [PATCH 015/218] src: free memory before re-setting URLHost value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/18302 PR-URL: https://github.com/nodejs/node/pull/18357 Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Anatoli Papirovski Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Tobias Nießen --- src/node_url.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/node_url.cc b/src/node_url.cc index d248454f9ca0d2..0f7992264ecf0f 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -92,6 +92,16 @@ class URLHost { Value value_; HostType type_ = HostType::H_FAILED; + inline void Reset() { + using string = std::string; + switch (type_) { + case HostType::H_DOMAIN: value_.domain.~string(); break; + case HostType::H_OPAQUE: value_.opaque.~string(); break; + default: break; + } + type_ = HostType::H_FAILED; + } + // Setting the string members of the union with = is brittle because // it relies on them being initialized to a state that requires no // destruction of old data. @@ -101,23 +111,20 @@ class URLHost { // These helpers are the easiest solution but we might want to consider // just not forcing strings into an union. inline void SetOpaque(std::string&& string) { + Reset(); type_ = HostType::H_OPAQUE; new(&value_.opaque) std::string(std::move(string)); } inline void SetDomain(std::string&& string) { + Reset(); type_ = HostType::H_DOMAIN; new(&value_.domain) std::string(std::move(string)); } }; URLHost::~URLHost() { - using string = std::string; - switch (type_) { - case HostType::H_DOMAIN: value_.domain.~string(); break; - case HostType::H_OPAQUE: value_.opaque.~string(); break; - default: break; - } + Reset(); } #define ARGS(XX) \ From a8b5a96d15b8ef83702c932d6ca9d0be22715ca2 Mon Sep 17 00:00:00 2001 From: Shobhit Chittora Date: Wed, 1 Nov 2017 02:12:56 +0530 Subject: [PATCH 016/218] tools: auto fix custom eslint rule 1. Extends tests 2. Refactors code 3. Adds fixer Refs: #16636 PR-URL: https://github.com/nodejs/node/pull/16652 Refs: https://github.com/nodejs/node/issues/16636 Reviewed-By: Ruben Bridgewater Reviewed-By: Anna Henningsen --- .../test-eslint-prefer-assert-methods.js | 37 +++++++++++++------ tools/eslint-rules/prefer-assert-methods.js | 18 ++++++++- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/test/parallel/test-eslint-prefer-assert-methods.js b/test/parallel/test-eslint-prefer-assert-methods.js index ea1501ed8401eb..f5cf50b1ad32b3 100644 --- a/test/parallel/test-eslint-prefer-assert-methods.js +++ b/test/parallel/test-eslint-prefer-assert-methods.js @@ -9,31 +9,46 @@ const rule = require('../../tools/eslint-rules/prefer-assert-methods'); new RuleTester().run('prefer-assert-methods', rule, { valid: [ - 'assert.strictEqual(foo, bar)', - 'assert(foo === bar && baz)' + 'assert.strictEqual(foo, bar);', + 'assert(foo === bar && baz);', + 'assert.notStrictEqual(foo, bar);', + 'assert(foo !== bar && baz);', + 'assert.equal(foo, bar);', + 'assert(foo == bar && baz);', + 'assert.notEqual(foo, bar);', + 'assert(foo != bar && baz);', + 'assert.ok(foo);', + 'assert.ok(foo != bar);', + 'assert.ok(foo === bar && baz);' ], invalid: [ { - code: 'assert(foo == bar)', - errors: [{ message: "'assert.equal' should be used instead of '=='" }] + code: 'assert(foo == bar);', + errors: [{ + message: "'assert.equal' should be used instead of '=='" + }], + output: 'assert.equal(foo, bar);' }, { - code: 'assert(foo === bar)', + code: 'assert(foo === bar);', errors: [{ message: "'assert.strictEqual' should be used instead of '==='" - }] + }], + output: 'assert.strictEqual(foo, bar);' }, { - code: 'assert(foo != bar)', + code: 'assert(foo != bar);', errors: [{ message: "'assert.notEqual' should be used instead of '!='" - }] + }], + output: 'assert.notEqual(foo, bar);' }, { - code: 'assert(foo !== bar)', + code: 'assert(foo !== bar);', errors: [{ message: "'assert.notStrictEqual' should be used instead of '!=='" - }] - }, + }], + output: 'assert.notStrictEqual(foo, bar);' + } ] }); diff --git a/tools/eslint-rules/prefer-assert-methods.js b/tools/eslint-rules/prefer-assert-methods.js index 0604fd3ed99046..2917d40de40810 100644 --- a/tools/eslint-rules/prefer-assert-methods.js +++ b/tools/eslint-rules/prefer-assert-methods.js @@ -1,3 +1,7 @@ +/** + * @fileoverview Prohibit the use of assert operators ( ===, !==, ==, != ) + */ + 'use strict'; const astSelector = 'ExpressionStatement[expression.type="CallExpression"]' + @@ -21,7 +25,19 @@ module.exports = function(context) { const arg = node.expression.arguments[0]; const assertMethod = preferedAssertMethod[arg.operator]; if (assertMethod) { - context.report(node, parseError(assertMethod, arg.operator)); + context.report({ + node, + message: parseError(assertMethod, arg.operator), + fix: (fixer) => { + const sourceCode = context.getSourceCode(); + const left = sourceCode.getText(arg.left); + const right = sourceCode.getText(arg.right); + return fixer.replaceText( + node, + `assert.${assertMethod}(${left}, ${right});` + ); + } + }); } } }; From ac41aacb059e167d1e9e081e3c35f315bb901d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=88=9A?= Date: Thu, 25 Jan 2018 23:45:17 +0800 Subject: [PATCH 017/218] doc: improve stream documentation PR-URL: https://github.com/nodejs/node/pull/18375 Reviewed-By: Jon Moss Reviewed-By: Ruben Bridgewater --- doc/api/stream.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/stream.md b/doc/api/stream.md index d92d5fde23a38b..678ac359caa034 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -572,8 +572,8 @@ The Readable can switch back to paused mode using one of the following: * If there are no pipe destinations, by calling the [`stream.pause()`][stream-pause] method. -* If there are pipe destinations, by removing any [`'data'`][] event - handlers, and removing all pipe destinations by calling the +* If there are pipe destinations, by removing all pipe destinations. + Multiple pipe destinations may be removed by calling the [`stream.unpipe()`][] method. The important concept to remember is that a Readable will not generate data @@ -1411,7 +1411,7 @@ write succeeded. All calls to `writable.write()` that occur between the time `writable._write()` is called and the `callback` is called will cause the written data to be -buffered. Once the `callback` is invoked, the stream will emit a [`'drain'`][] +buffered. When the `callback` is invoked, the stream might emit a [`'drain'`][] event. If a stream implementation is capable of processing multiple chunks of data at once, the `writable._writev()` method should be implemented. From d630874250394c82d7ac884f98f296b8fc0e13bf Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 29 Jan 2018 03:04:45 +0100 Subject: [PATCH 018/218] test: speed up parallel/test-tls-session-cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test had some unnecessary timeouts that made it run for a much longer time than necessary (about 9 s rather than 0.2 s). PR-URL: https://github.com/nodejs/node/pull/18424 Reviewed-By: Colin Ihrig Reviewed-By: Weijia Wang Reviewed-By: Joyee Cheung Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- test/parallel/test-tls-session-cache.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/parallel/test-tls-session-cache.js b/test/parallel/test-tls-session-cache.js index 2bbf3b642da1b8..7778dd03100857 100644 --- a/test/parallel/test-tls-session-cache.js +++ b/test/parallel/test-tls-session-cache.js @@ -69,11 +69,11 @@ function doTest(testOptions, callback) { server.on('newSession', function(id, data, cb) { ++newSessionCount; // Emulate asynchronous store - setTimeout(function() { + setImmediate(() => { assert.ok(!session); session = { id, data }; cb(); - }, 1000); + }); }); server.on('resumeSession', function(id, callback) { ++resumeCount; @@ -89,9 +89,9 @@ function doTest(testOptions, callback) { } // Just to check that async really works there - setTimeout(function() { + setImmediate(() => { callback(null, data); - }, 100); + }); }); server.listen(0, function() { @@ -132,7 +132,7 @@ function doTest(testOptions, callback) { } assert.strictEqual(code, 0); server.close(common.mustCall(function() { - setTimeout(callback, 100); + setImmediate(callback); })); })); } From 20844d1716f46f4c11225f6805f18a96a886b031 Mon Sep 17 00:00:00 2001 From: Qingyan Li Date: Mon, 22 Jan 2018 16:39:25 +0800 Subject: [PATCH 019/218] lib: remove debugger dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/18426 Refs: https://github.com/nodejs/node/commit/719247ff9575d1a4ab6adc11e6923e9b2335053c Reviewed-By: Michaël Zasso Reviewed-By: Franziska Hinkelmann Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Eugene Ostroukhov Reviewed-By: James M Snell Reviewed-By: Jon Moss Reviewed-By: Anatoli Papirovski Reviewed-By: Ruben Bridgewater --- lib/internal/bootstrap_node.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 9af547a923022b..25d69e97370458 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -58,11 +58,7 @@ NativeModule.require('internal/trace_events_async_hooks').setup(); NativeModule.require('internal/inspector_async_hook').setup(); - // Do not initialize channel in debugger agent, it deletes env variable - // and the main thread won't see it. - if (process.argv[1] !== '--debug-agent') - _process.setupChannel(); - + _process.setupChannel(); _process.setupRawDebug(); const browserGlobals = !process._noBrowserGlobals; From 79a7a17312f9faacb3c581bbee005256172c5ba5 Mon Sep 17 00:00:00 2001 From: Jan Krems Date: Wed, 24 Jan 2018 10:42:54 -0800 Subject: [PATCH 020/218] deps: update node-inspect to 1.11.3 Highlights: * Using a random port works (`node inspect --port=0 script.js`) * `takeHeapSnapshot()` creates valid snapshots again PR-URL: https://github.com/nodejs/node/pull/18354 Reviewed-By: Richard Lau --- deps/node-inspect/CHANGELOG.md | 9 +++++ deps/node-inspect/README.md | 2 +- deps/node-inspect/lib/_inspect.js | 31 ++++++-------- .../lib/internal/inspect_client.js | 10 +++-- .../node-inspect/lib/internal/inspect_repl.js | 20 +++++----- deps/node-inspect/package.json | 4 +- deps/node-inspect/test/cli/break.test.js | 4 +- .../test/cli/heap-profiler.test.js | 34 ++++++++++++++++ deps/node-inspect/test/cli/launch.test.js | 40 +++++++++++++++++++ deps/node-inspect/test/cli/start-cli.js | 4 +- 10 files changed, 118 insertions(+), 40 deletions(-) create mode 100644 deps/node-inspect/test/cli/heap-profiler.test.js diff --git a/deps/node-inspect/CHANGELOG.md b/deps/node-inspect/CHANGELOG.md index 41ed928e781ff6..0db3a7842eb15d 100644 --- a/deps/node-inspect/CHANGELOG.md +++ b/deps/node-inspect/CHANGELOG.md @@ -1,3 +1,12 @@ +### 1.11.3 + +* [`93caa0f`](https://github.com/nodejs/node-inspect/commit/93caa0f5267c7ab452b258d3b03329a0bb5ac7f7) **docs:** Add missing oc in protocol +* [`2d87cbe`](https://github.com/nodejs/node-inspect/commit/2d87cbe76aa968dfc1ac69d9571af1be81abd8e0) **fix:** Make --inspect-port=0 work +* [`ebfd02e`](https://github.com/nodejs/node-inspect/commit/ebfd02ece9b642586023f7791da71defeb13d746) **chore:** Bump tap to 10.7 +* [`c07adb1`](https://github.com/nodejs/node-inspect/commit/c07adb17b164c1cf3da8d38659ea9f5d7ff42e9c) **test:** Use useful break location +* [`94f0bf9`](https://github.com/nodejs/node-inspect/commit/94f0bf97d24c376baf3ecced2088d81715a73464) **fix:** Fix `takeHeapSnapshot()` truncation bug + + ### 1.11.2 * [`42e0cd1`](https://github.com/nodejs/node-inspect/commit/42e0cd111d89ed09faba1c0ec45089b0b44de011) **fix:** look for generic hint text diff --git a/deps/node-inspect/README.md b/deps/node-inspect/README.md index ecd939b3ea26a8..b52cc188a62f5b 100644 --- a/deps/node-inspect/README.md +++ b/deps/node-inspect/README.md @@ -10,7 +10,7 @@ node has two options: 1. `node --debug `: Start `file` with remote debugging enabled. 2. `node debug `: Start an interactive CLI debugger for ``. -But for the Chrome inspector protol, +But for the Chrome inspector protocol, there's only one: `node --inspect `. This project tries to provide the missing second option diff --git a/deps/node-inspect/lib/_inspect.js b/deps/node-inspect/lib/_inspect.js index 26912274cdaec4..d846efbe6a4a52 100644 --- a/deps/node-inspect/lib/_inspect.js +++ b/deps/node-inspect/lib/_inspect.js @@ -42,18 +42,9 @@ const [ InspectClient, createRepl ] = const debuglog = util.debuglog('inspect'); -const DEBUG_PORT_PATTERN = /^--(?:debug|inspect)(?:-port|-brk)?=(\d{1,5})$/; -function getDefaultPort() { - for (const arg of process.execArgv) { - const match = arg.match(DEBUG_PORT_PATTERN); - if (match) { - return +match[1]; - } - } - return 9229; -} - function portIsFree(host, port, timeout = 2000) { + if (port === 0) return Promise.resolve(); // Binding to a random port. + const retryDelay = 150; let didTimeOut = false; @@ -110,9 +101,11 @@ function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) { let output = ''; function waitForListenHint(text) { output += text; - if (/Debugger listening on/.test(output)) { + if (/Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\//.test(output)) { + const host = RegExp.$1; + const port = Number.parseInt(RegExp.$2); child.stderr.removeListener('data', waitForListenHint); - resolve(child); + resolve([child, port, host]); } } @@ -160,10 +153,11 @@ class NodeInspector { options.port, this.childPrint.bind(this)); } else { - this._runScript = () => Promise.resolve(null); + this._runScript = + () => Promise.resolve([null, options.port, options.host]); } - this.client = new InspectClient(options.port, options.host); + this.client = new InspectClient(); this.domainNames = ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime']; this.domainNames.forEach((domain) => { @@ -223,9 +217,8 @@ class NodeInspector { run() { this.killChild(); - const { host, port } = this.options; - return this._runScript().then((child) => { + return this._runScript().then(([child, port, host]) => { this.child = child; let connectionAttempts = 0; @@ -233,7 +226,7 @@ class NodeInspector { ++connectionAttempts; debuglog('connection attempt #%d', connectionAttempts); this.stdout.write('.'); - return this.client.connect() + return this.client.connect(port, host) .then(() => { debuglog('connection established'); this.stdout.write(' ok'); @@ -288,7 +281,7 @@ class NodeInspector { function parseArgv([target, ...args]) { let host = '127.0.0.1'; - let port = getDefaultPort(); + let port = 9229; let isRemote = false; let script = target; let scriptArgs = args; diff --git a/deps/node-inspect/lib/internal/inspect_client.js b/deps/node-inspect/lib/internal/inspect_client.js index c247e2add87706..9b8529de21aae2 100644 --- a/deps/node-inspect/lib/internal/inspect_client.js +++ b/deps/node-inspect/lib/internal/inspect_client.js @@ -164,12 +164,12 @@ function decodeFrameHybi17(data) { } class Client extends EventEmitter { - constructor(port, host) { + constructor() { super(); this.handleChunk = this._handleChunk.bind(this); - this._port = port; - this._host = host; + this._port = undefined; + this._host = undefined; this.reset(); } @@ -284,7 +284,9 @@ class Client extends EventEmitter { }); } - connect() { + connect(port, host) { + this._port = port; + this._host = host; return this._discoverWebsocketPath() .then((urlPath) => this._connectWebsocket(urlPath)); } diff --git a/deps/node-inspect/lib/internal/inspect_repl.js b/deps/node-inspect/lib/internal/inspect_repl.js index 937c1843d3a3ee..38fe4684cf6d71 100644 --- a/deps/node-inspect/lib/internal/inspect_repl.js +++ b/deps/node-inspect/lib/internal/inspect_repl.js @@ -900,10 +900,8 @@ function createRepl(inspector) { return new Promise((resolve, reject) => { const absoluteFile = Path.resolve(filename); const writer = FS.createWriteStream(absoluteFile); - let totalSize; let sizeWritten = 0; function onProgress({ done, total, finished }) { - totalSize = total; if (finished) { print('Heap snaphost prepared.'); } else { @@ -913,13 +911,18 @@ function createRepl(inspector) { function onChunk({ chunk }) { sizeWritten += chunk.length; writer.write(chunk); - print(`Writing snapshot: ${sizeWritten}/${totalSize}`, true); - if (sizeWritten >= totalSize) { - writer.end(); + print(`Writing snapshot: ${sizeWritten}`, true); + } + function onResolve() { + writer.end(() => { teardown(); print(`Wrote snapshot: ${absoluteFile}`); resolve(); - } + }); + } + function onReject(error) { + teardown(); + reject(error); } function teardown() { HeapProfiler.removeListener( @@ -932,10 +935,7 @@ function createRepl(inspector) { print('Heap snapshot: 0/0', true); HeapProfiler.takeHeapSnapshot({ reportProgress: true }) - .then(null, (error) => { - teardown(); - reject(error); - }); + .then(onResolve, onReject); }); }, diff --git a/deps/node-inspect/package.json b/deps/node-inspect/package.json index 070abfa8fe51be..d25376b5d4bb96 100644 --- a/deps/node-inspect/package.json +++ b/deps/node-inspect/package.json @@ -1,6 +1,6 @@ { "name": "node-inspect", - "version": "1.11.2", + "version": "1.11.3", "description": "Node Inspect", "license": "MIT", "main": "lib/_inspect.js", @@ -29,7 +29,7 @@ "devDependencies": { "eslint": "^3.10.2", "nlm": "^3.0.0", - "tap": "^7.1.2" + "tap": "^10.7.0" }, "author": { "name": "Jan Krems", diff --git a/deps/node-inspect/test/cli/break.test.js b/deps/node-inspect/test/cli/break.test.js index 59b12cde388c01..ce8c8d6d7d99bd 100644 --- a/deps/node-inspect/test/cli/break.test.js +++ b/deps/node-inspect/test/cli/break.test.js @@ -134,7 +134,7 @@ test('sb before loading file', (t) => { return cli.waitForInitialBreak() .then(() => cli.waitForPrompt()) - .then(() => cli.command('sb("other.js", 3)')) + .then(() => cli.command('sb("other.js", 2)')) .then(() => { t.match( cli.output, @@ -145,7 +145,7 @@ test('sb before loading file', (t) => { .then(() => { t.match( cli.output, - `break in ${otherScript}:3`, + `break in ${otherScript}:2`, 'found breakpoint in file that was not loaded yet'); }) .then(() => cli.quit()) diff --git a/deps/node-inspect/test/cli/heap-profiler.test.js b/deps/node-inspect/test/cli/heap-profiler.test.js new file mode 100644 index 00000000000000..ebd734e03cb06d --- /dev/null +++ b/deps/node-inspect/test/cli/heap-profiler.test.js @@ -0,0 +1,34 @@ +'use strict'; +const { test } = require('tap'); +const { readFileSync, unlinkSync } = require('fs'); + +const startCLI = require('./start-cli'); +const filename = 'node.heapsnapshot'; + +function cleanup() { + try { + unlinkSync(filename); + } catch (_) { + // Ignore. + } +} + +cleanup(); + +test('Heap profiler take snapshot', (t) => { + const cli = startCLI(['examples/empty.js']); + + function onFatal(error) { + cli.quit(); + throw error; + } + + // Check that the snapshot is valid JSON. + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => cli.command('takeHeapSnapshot()')) + .then(() => JSON.parse(readFileSync(filename, 'utf8'))) + .then(() => cleanup()) + .then(() => cli.quit()) + .then(null, onFatal); +}); diff --git a/deps/node-inspect/test/cli/launch.test.js b/deps/node-inspect/test/cli/launch.test.js index f7efc6eb3f2139..8808d47a08b900 100644 --- a/deps/node-inspect/test/cli/launch.test.js +++ b/deps/node-inspect/test/cli/launch.test.js @@ -26,6 +26,46 @@ test('custom port', (t) => { }); }); +test('random port', (t) => { + const script = Path.join('examples', 'three-lines.js'); + + const cli = startCLI(['--port=0', script]); + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match(cli.output, 'debug>', 'prints a prompt'); + t.match( + cli.output, + /< Debugger listening on /, + 'forwards child output'); + }) + .then(() => cli.quit()) + .then((code) => { + t.equal(code, 0, 'exits with success'); + }); +}); + +test('random port with --inspect-port=0', (t) => { + const script = Path.join('examples', 'three-lines.js'); + + const cli = startCLI([script], ['--inspect-port=0']); + + return cli.waitForInitialBreak() + .then(() => cli.waitForPrompt()) + .then(() => { + t.match(cli.output, 'debug>', 'prints a prompt'); + t.match( + cli.output, + /< Debugger listening on /, + 'forwards child output'); + }) + .then(() => cli.quit()) + .then((code) => { + t.equal(code, 0, 'exits with success'); + }); +}); + test('examples/three-lines.js', (t) => { const script = Path.join('examples', 'three-lines.js'); const cli = startCLI([script]); diff --git a/deps/node-inspect/test/cli/start-cli.js b/deps/node-inspect/test/cli/start-cli.js index ae904308e02270..b086dcd8ba218d 100644 --- a/deps/node-inspect/test/cli/start-cli.js +++ b/deps/node-inspect/test/cli/start-cli.js @@ -16,8 +16,8 @@ const BREAK_MESSAGE = new RegExp('(?:' + [ 'exception', 'other', 'promiseRejection', ].join('|') + ') in', 'i'); -function startCLI(args) { - const child = spawn(process.execPath, [CLI, ...args]); +function startCLI(args, flags = []) { + const child = spawn(process.execPath, [...flags, CLI, ...args]); let isFirstStdoutChunk = true; const outputBuffer = []; From ce3866bdccdda994931e9eb2e28837c486a3b82b Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Tue, 30 Jan 2018 13:49:40 -0800 Subject: [PATCH 021/218] async_hooks: clean up comments With some of the recent work, some of the comments were no longer representative of the code, or were otherwise unclear. This commit fixes some obvious issues I found. Ref: https://github.com/nodejs/node/commit/83e5215a4e8438a43b9f0002b7a43e2fd2dd37a4 Ref: https://github.com/nodejs/node/commit/0784b0440c05464f79b857f7d8698fcc953d3fb3 PR-URL: https://github.com/nodejs/node/pull/18467 Reviewed-By: Andreas Madsen Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- lib/internal/async_hooks.js | 23 ++++++++++++----------- src/async_wrap.cc | 4 ++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 06f6b06f093804..b1e5de2bebc08f 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -11,16 +11,17 @@ const async_wrap = process.binding('async_wrap'); * the various asynchronous states of the application. These are: * kExecutionAsyncId: The async_id assigned to the resource responsible for the * current execution stack. - * kTriggerAsyncId: The trigger_async_id of the resource responsible for - * the current execution stack. + * kTriggerAsyncId: The async_id of the resource that caused (or 'triggered') + * the resource corresponding to the current execution stack. * kAsyncIdCounter: Incremental counter tracking the next assigned async_id. * kDefaultTriggerAsyncId: Written immediately before a resource's constructor - * that sets the value of the init()'s triggerAsyncId. The order of - * retrieving the triggerAsyncId value is passing directly to the - * constructor -> value set in kDefaultTriggerAsyncId -> executionAsyncId of - * the current resource. + * that sets the value of the init()'s triggerAsyncId. The precedence order + * of retrieving the triggerAsyncId value is: + * 1. the value passed directly to the constructor + * 2. value set in kDefaultTriggerAsyncId + * 3. executionAsyncId of the current resource. * - * async_ids_fast_stack is a Float64Array that contains part of the async ID + * async_ids_stack is a Float64Array that contains part of the async ID * stack. Each pushAsyncIds() call adds two doubles to it, and each * popAsyncIds() call removes two doubles from it. * It has a fixed size, so if that is exceeded, calls to the native @@ -28,10 +29,10 @@ const async_wrap = process.binding('async_wrap'); */ const { async_id_symbol, async_hook_fields, async_id_fields } = async_wrap; // Store the pair executionAsyncId and triggerAsyncId in a std::stack on -// Environment::AsyncHooks::ids_stack_ tracks the resource responsible for the -// current execution stack. This is unwound as each resource exits. In the case -// of a fatal exception this stack is emptied after calling each hook's after() -// callback. +// Environment::AsyncHooks::async_ids_stack_ tracks the resource responsible for +// the current execution stack. This is unwound as each resource exits. In the +// case of a fatal exception this stack is emptied after calling each hook's +// after() callback. const { pushAsyncIds: pushAsyncIds_, popAsyncIds: popAsyncIds_ } = async_wrap; // For performance reasons, only track Proimses when a hook is enabled. const { enablePromiseHook, disablePromiseHook } = async_wrap; diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 7e3c0c257ab22d..61fd40a486b3bd 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -554,12 +554,12 @@ void AsyncWrap::Initialize(Local target, // this way to allow JS and C++ to read/write each value as quickly as // possible. The fields are represented as follows: // - // kAsyncUid: Maintains the state of the next unique id to be assigned. + // kAsyncIdCounter: Maintains the state of the next unique id to be assigned. // // kDefaultTriggerAsyncId: Write the id of the resource responsible for a // handle's creation just before calling the new handle's constructor. // After the new handle is constructed kDefaultTriggerAsyncId is set back - // to 0. + // to -1. FORCE_SET_TARGET_FIELD(target, "async_id_fields", env->async_hooks()->async_id_fields().GetJSArray()); From eec9334a2e63db0ea65a3c84242864c7516ed678 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 31 Jan 2018 09:36:51 -0800 Subject: [PATCH 022/218] doc: move Brian White to TSC Emeriti list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/18482 Reviewed-By: Colin Ihrig Reviewed-By: Michaël Zasso Reviewed-By: Daniel Bevenius Reviewed-By: Ali Ijaz Sheikh Reviewed-By: Jon Moss Reviewed-By: Evan Lucas Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Michael Dawson --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa09f812a16be8..47fd568a35be19 100644 --- a/README.md +++ b/README.md @@ -257,8 +257,6 @@ For more information about the governance of the Node.js project, see **Matteo Collina** <matteo.collina@gmail.com> (he/him) * [mhdawson](https://github.com/mhdawson) - **Michael Dawson** <michael_dawson@ca.ibm.com> (he/him) -* [mscdex](https://github.com/mscdex) - -**Brian White** <mscdex@mscdex.net> * [MylesBorins](https://github.com/MylesBorins) - **Myles Borins** <myles.borins@gmail.com> (he/him) * [ofrobots](https://github.com/ofrobots) - @@ -284,6 +282,8 @@ For more information about the governance of the Node.js project, see **Isaac Z. Schlueter** <i@izs.me> * [joshgav](https://github.com/joshgav) - **Josh Gavant** <josh.gavant@outlook.com> +* [mscdex](https://github.com/mscdex) - +**Brian White** <mscdex@mscdex.net> * [nebrius](https://github.com/nebrius) - **Bryan Hughes** <bryan@nebri.us> * [orangemocha](https://github.com/orangemocha) - From 8ac69c457b5121299568a5b733688e88a179dd46 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Mon, 29 Jan 2018 20:05:51 -0700 Subject: [PATCH 023/218] v8: add missing ',' in OpenBSD's 'sources' section. Add stack_trace_posix to 'sources' as well. Fixes https://github.com/nodejs/node/issues/15784 Fixes https://github.com/nodejs/help/issues/992 add stack_trace_posix to OpenBSD's sources PR-URL: https://github.com/nodejs/node/pull/18448 Reviewed-By: Daniel Bevenius Reviewed-By: James M Snell --- deps/v8/src/v8.gyp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/v8/src/v8.gyp b/deps/v8/src/v8.gyp index bf7635ee339e2a..1c56842f81a548 100644 --- a/deps/v8/src/v8.gyp +++ b/deps/v8/src/v8.gyp @@ -2044,9 +2044,10 @@ '-L/usr/local/lib -lexecinfo', ]}, 'sources': [ + 'base/debug/stack_trace_posix.cc', 'base/platform/platform-openbsd.cc', 'base/platform/platform-posix.h', - 'base/platform/platform-posix.cc' + 'base/platform/platform-posix.cc', 'base/platform/platform-posix-time.h', 'base/platform/platform-posix-time.cc', ], From 424703a556ae463ecc632b3a297a0cf04640e0d8 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Tue, 30 Jan 2018 15:14:27 -0700 Subject: [PATCH 024/218] build: add cflags for OpenBSD, remove stray comma. PR-URL: https://github.com/nodejs/node/pull/18448 Reviewed-By: Daniel Bevenius Reviewed-By: James M Snell --- common.gypi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common.gypi b/common.gypi index 99351610eb447b..b89a8afff03a63 100644 --- a/common.gypi +++ b/common.gypi @@ -282,7 +282,7 @@ ], }], [ 'OS in "linux freebsd openbsd solaris aix"', { - 'cflags': [ '-pthread', ], + 'cflags': [ '-pthread' ], 'ldflags': [ '-pthread' ], }], [ 'OS in "linux freebsd openbsd solaris android aix"', { @@ -295,6 +295,7 @@ 'standalone_static_library': 1, }], ['OS=="openbsd"', { + 'cflags': [ '-I/usr/local/include' ], 'ldflags': [ '-Wl,-z,wxneeded' ], }], ], From e5d5137963f505696b9a87cea181c94d219c0b93 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 31 Jan 2018 09:53:10 -0800 Subject: [PATCH 025/218] doc: streamline README intro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shorten text that is duplicated from website and supply link. PR-URL: https://github.com/nodejs/node/pull/18483 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Сковорода Никита Андреевич Reviewed-By: Luigi Pinca Reviewed-By: Joyee Cheung Reviewed-By: Daniel Bevenius Reviewed-By: Ruben Bridgewater --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 47fd568a35be19..478e4414084d85 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,9 @@

-Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js -uses an event-driven, non-blocking I/O model that makes it lightweight and -efficient. The Node.js package ecosystem, [npm][], is the largest ecosystem of -open source libraries in the world. +Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. For +more information on using Node.js, see the +[Node.js Website][]. The Node.js project is supported by the [Node.js Foundation](https://nodejs.org/en/foundation/). Contributions, @@ -593,7 +592,6 @@ Previous releases may also have been signed with one of the following GPG keys: * [Contributing to the project][] * [Working Groups][] -[npm]: https://www.npmjs.com [Code of Conduct]: https://github.com/nodejs/admin/blob/master/CODE_OF_CONDUCT.md [Contributing to the project]: CONTRIBUTING.md [Node.js Help]: https://github.com/nodejs/help From f96b0bf494978f5d5bf6ec9ea0390a37804bf486 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 1 Feb 2018 01:12:05 -0500 Subject: [PATCH 026/218] string_decoder: reset decoder on end This resets the StringDecoder's state after calling `#end`. Further writes to the decoder will act as if it were a brand new instance, allowing simple reuse. PR-URL: https://github.com/nodejs/node/pull/18494 Fixes: https://github.com/nodejs/node/issues/16564 Refs: https://github.com/nodejs/node/pull/16594 Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Sakthipriyan Vairamani Reviewed-By: Ruben Bridgewater Reviewed-By: Anna Henningsen --- lib/string_decoder.js | 15 +++++-- test/parallel/test-string-decoder-end.js | 51 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/lib/string_decoder.js b/lib/string_decoder.js index 5e60bf1ad1c3d7..d22a85b050d8ab 100644 --- a/lib/string_decoder.js +++ b/lib/string_decoder.js @@ -209,8 +209,11 @@ function utf8Text(buf, i) { // character. function utf8End(buf) { const r = (buf && buf.length ? this.write(buf) : ''); - if (this.lastNeed) + if (this.lastNeed) { + this.lastNeed = 0; + this.lastTotal = 0; return r + '\ufffd'; + } return r; } @@ -245,6 +248,8 @@ function utf16End(buf) { const r = (buf && buf.length ? this.write(buf) : ''); if (this.lastNeed) { const end = this.lastTotal - this.lastNeed; + this.lastNeed = 0; + this.lastTotal = 0; return r + this.lastChar.toString('utf16le', 0, end); } return r; @@ -268,8 +273,12 @@ function base64Text(buf, i) { function base64End(buf) { const r = (buf && buf.length ? this.write(buf) : ''); - if (this.lastNeed) - return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed); + if (this.lastNeed) { + const end = 3 - this.lastNeed; + this.lastNeed = 0; + this.lastTotal = 0; + return r + this.lastChar.toString('base64', 0, end); + } return r; } diff --git a/test/parallel/test-string-decoder-end.js b/test/parallel/test-string-decoder-end.js index 0284ee9f6c48c7..2762ef096289db 100644 --- a/test/parallel/test-string-decoder-end.js +++ b/test/parallel/test-string-decoder-end.js @@ -39,6 +39,46 @@ for (let i = 1; i <= 16; i++) { encodings.forEach(testEncoding); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0x82), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0x61), '\uFFFDa'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xAC), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82), Buffer.of(0xE2), '\uFFFD\uFFFD'); +testEnd('utf8', Buffer.of(0xE2, 0x82, 0xAC), Buffer.of(0x61), '€a'); + +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0x61, 0x00), 'a'); +testEnd('utf16le', Buffer.of(0x3D), Buffer.of(0xD8, 0x4D, 0xDC), '\u4DD8'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(), '\uD83D'); +testEnd('utf16le', Buffer.of(0x3D, 0xD8), Buffer.of(0x61, 0x00), '\uD83Da'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8), + Buffer.of(0x4D, 0xDC), + '\uD83D\uDC4D' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D), + Buffer.of(0x61, 0x00), + '\uD83Da' +); +testEnd('utf16le', Buffer.of(0x3D, 0xD8, 0x4D), Buffer.of(0xDC), '\uD83D'); +testEnd( + 'utf16le', + Buffer.of(0x3D, 0xD8, 0x4D, 0xDC), + Buffer.of(0x61, 0x00), + '👍a' +); + +testEnd('base64', Buffer.of(0x61), Buffer.of(), 'YQ=='); +testEnd('base64', Buffer.of(0x61), Buffer.of(0x61), 'YQ==YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(), 'YWE='); +testEnd('base64', Buffer.of(0x61, 0x61), Buffer.of(0x61), 'YWE=YQ=='); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(), 'YWFh'); +testEnd('base64', Buffer.of(0x61, 0x61, 0x61), Buffer.of(0x61), 'YWFhYQ=='); + function testEncoding(encoding) { bufs.forEach((buf) => { testBuf(encoding, buf); @@ -66,3 +106,14 @@ function testBuf(encoding, buf) { assert.strictEqual(res1, res3, 'one byte at a time should match toString'); assert.strictEqual(res2, res3, 'all bytes at once should match toString'); } + +function testEnd(encoding, incomplete, next, expected) { + let res = ''; + const s = new SD(encoding); + res += s.write(incomplete); + res += s.end(); + res += s.write(next); + res += s.end(); + + assert.strictEqual(res, expected); +} From 0cf9d0483fd395d1e4433b0fa4ce6124881ac703 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Thu, 1 Feb 2018 08:10:18 -0500 Subject: [PATCH 027/218] test: add test for tls benchmarks PR-URL: https://github.com/nodejs/node/pull/18489 Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell --- benchmark/tls/throughput.js | 2 +- test/sequential/test-benchmark-tls.js | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 test/sequential/test-benchmark-tls.js diff --git a/benchmark/tls/throughput.js b/benchmark/tls/throughput.js index 51feb85cbaccc1..52907a3343fb1f 100644 --- a/benchmark/tls/throughput.js +++ b/benchmark/tls/throughput.js @@ -45,11 +45,11 @@ function main(conf) { }; server = tls.createServer(options, onConnection); - setTimeout(done, dur * 1000); var conn; server.listen(common.PORT, function() { const opt = { port: common.PORT, rejectUnauthorized: false }; conn = tls.connect(opt, function() { + setTimeout(done, dur * 1000); bench.start(); conn.on('drain', write); write(); diff --git a/test/sequential/test-benchmark-tls.js b/test/sequential/test-benchmark-tls.js new file mode 100644 index 00000000000000..7c87aa3cbcd89e --- /dev/null +++ b/test/sequential/test-benchmark-tls.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); + +if (!common.enoughTestMem) + common.skip('Insufficient memory for TLS benchmark test'); + +// Because the TLS benchmarks use hardcoded ports, this should be in sequential +// rather than parallel to make sure it does not conflict with tests that choose +// random available ports. + +const runBenchmark = require('../common/benchmark'); + +runBenchmark('tls', + [ + 'concurrency=1', + 'dur=0.1', + 'n=1', + 'size=2', + 'type=asc' + ], + { + NODEJS_BENCHMARK_ZERO_ALLOWED: 1, + duration: 0 + }); From e55e08c80e28a0ae6c20a65a95d5a70bb22c4576 Mon Sep 17 00:00:00 2001 From: jvelezpo Date: Thu, 25 Jan 2018 14:47:52 -0500 Subject: [PATCH 028/218] test: verify the shell option works properly on execFile Useful for executing in a shell because it accepts arguments as an array instead of a string as exec does. Depending on the circumstances, that can prove to be useful if the arguments are already prepared. PR-URL: https://github.com/nodejs/node/pull/18384 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Ruben Bridgewater --- test/parallel/test-child-process-execfile.js | 8 ++++++++ test/sequential/test-child-process-execsync.js | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/test/parallel/test-child-process-execfile.js b/test/parallel/test-child-process-execfile.js index 62cc7f534dc86b..a64128d6a3ab6b 100644 --- a/test/parallel/test-child-process-execfile.js +++ b/test/parallel/test-child-process-execfile.js @@ -6,6 +6,7 @@ const uv = process.binding('uv'); const fixtures = require('../common/fixtures'); const fixture = fixtures.path('exit.js'); +const execOpts = { encoding: 'utf8', shell: true }; { execFile( @@ -38,3 +39,10 @@ const fixture = fixtures.path('exit.js'); child.kill(); child.emit('close', code, null); } + +{ + // Verify the shell option works properly + execFile(process.execPath, [fixture, 0], execOpts, common.mustCall((err) => { + assert.ifError(err); + })); +} diff --git a/test/sequential/test-child-process-execsync.js b/test/sequential/test-child-process-execsync.js index 94810e890cd119..f0efff8deb2740 100644 --- a/test/sequential/test-child-process-execsync.js +++ b/test/sequential/test-child-process-execsync.js @@ -29,6 +29,7 @@ const TIMER = 200; const SLEEP = 2000; const start = Date.now(); +const execOpts = { encoding: 'utf8', shell: true }; let err; let caught = false; @@ -124,3 +125,8 @@ assert.strictEqual(ret, `${msg}\n`); return true; }); } + +// Verify the shell option works properly +assert.doesNotThrow(() => { + execFileSync(process.execPath, [], execOpts); +}); From c61754fad93454af4efae2667c424d22ab077222 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Mon, 22 Jan 2018 22:25:34 +0530 Subject: [PATCH 029/218] win, build: fix intl-none option Like #17614, but for the `intl-none` option. Refs: https://github.com/nodejs/node/pull/17614 PR-URL: https://github.com/nodejs/node/pull/18292 Reviewed-By: Richard Lau Reviewed-By: Gireesh Punathil Reviewed-By: James M Snell Reviewed-By: Bartosz Sosnowski --- vcbuild.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcbuild.bat b/vcbuild.bat index a918757332fdb2..99826e600cb5a4 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -105,7 +105,7 @@ if /i "%1"=="build-release" set build_release=1&set sign=1&goto arg-ok if /i "%1"=="upload" set upload=1&goto arg-ok if /i "%1"=="small-icu" set i18n_arg=%1&goto arg-ok if /i "%1"=="full-icu" set i18n_arg=%1&goto arg-ok -if /i "%1"=="intl-none" set i18n_arg=%1&goto arg-ok +if /i "%1"=="intl-none" set i18n_arg=none&goto arg-ok if /i "%1"=="without-intl" set i18n_arg=none&goto arg-ok if /i "%1"=="download-all" set download_arg="--download=all"&goto arg-ok if /i "%1"=="ignore-flaky" set test_args=%test_args% --flaky-tests=dontcare&goto arg-ok From 36ea472393b0b5b43c974df5b93b77c4150bcf89 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Sun, 7 Jan 2018 18:43:39 -0600 Subject: [PATCH 030/218] doc: improve http.request documentation PR-URL: https://github.com/nodejs/node/pull/18289 Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- doc/api/http.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/doc/api/http.md b/doc/api/http.md index dea6c08ef35d6f..cdd6bfd8a4d0fb 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -1880,6 +1880,49 @@ const req = http.request(options, (res) => { }); ``` +In a successful request, the following events will be emitted in the following +order: + +* `socket` +* `response` + * `data` any number of times, on the `res` object + (`data` will not be emitted at all if the response body is empty, for + instance, in most redirects) + * `end` on the `res` object +* `close` + +In the case of a connection error, the following events will be emitted: + +* `socket` +* `error` +* `close` + +If `req.abort()` is called before the connection succeeds, the following events +will be emitted in the following order: + +* `socket` +* (`req.abort()` called here) +* `abort` +* `close` +* `error` with an error with message `Error: socket hang up` and code + `ECONNRESET` + +If `req.abort()` is called after the response is received, the following events +will be emitted in the following order: + +* `socket` +* `response` + * `data` any number of times, on the `res` object +* (`req.abort()` called here) +* `abort` +* `close` + * `aborted` on the `res` object + * `end` on the `res` object + * `close` on the `res` object + +Note that setting the `timeout` option or using the `setTimeout` function will +not abort the request or do anything besides add a `timeout` event. + [`'checkContinue'`]: #http_event_checkcontinue [`'request'`]: #http_event_request [`'response'`]: #http_event_response From 1107a494e42b86f258faff0470651be3d5875857 Mon Sep 17 00:00:00 2001 From: jvelezpo Date: Thu, 18 Jan 2018 11:35:24 -0500 Subject: [PATCH 031/218] doc: shell option for the execFile and execFileSync functions Useful for executing in a shell because it accepts arguments as an array instead of a string as exec does. Depending on the circumstances, that can prove to be useful if the arguments are already prepared. PR-URL: https://github.com/nodejs/node/pull/18237 Fixes: https://github.com/nodejs/node/issues/18199 Reviewed-By: Julian Duque Reviewed-By: James M Snell Reviewed-By: Adrian Estrada Reviewed-By: Ruben Bridgewater --- doc/api/child_process.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 2aeae73c728228..cfdd650b7603b1 100755 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -44,7 +44,7 @@ implemented on top of [`child_process.spawn()`][] or [`child_process.spawnSync() * [`child_process.exec()`][]: spawns a shell and runs a command within that shell, passing the `stdout` and `stderr` to a callback function when complete. * [`child_process.execFile()`][]: similar to [`child_process.exec()`][] except that - it spawns the command directly without first spawning a shell. + it spawns the command directly without first spawning a shell by default. * [`child_process.fork()`][]: spawns a new Node.js process and invokes a specified module with an IPC communication channel established that allows sending messages between parent and child. @@ -78,7 +78,7 @@ when the child process terminates. The importance of the distinction between [`child_process.exec()`][] and [`child_process.execFile()`][] can vary based on platform. On Unix-type operating systems (Unix, Linux, macOS) [`child_process.execFile()`][] can be more efficient -because it does not spawn a shell. On Windows, however, `.bat` and `.cmd` +because it does not spawn a shell by default. On Windows, however, `.bat` and `.cmd` files are not executable on their own without a terminal, and therefore cannot be launched using [`child_process.execFile()`][]. When running on Windows, `.bat` and `.cmd` files can be invoked using [`child_process.spawn()`][] with the `shell` @@ -266,6 +266,10 @@ changes: normally be created on Windows systems. **Default:** `false`. * `windowsVerbatimArguments` {boolean} No quoting or escaping of arguments is done on Windows. Ignored on Unix. **Default:** `false`. + * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses + `'/bin/sh'` on UNIX, and `process.env.ComSpec` on Windows. A different + shell can be specified as a string. See [Shell Requirements][] and + [Default Windows Shell][]. **Default:** `false` (no shell). * `callback` {Function} Called with the output when process terminates. * `error` {Error} * `stdout` {string|Buffer} @@ -273,7 +277,7 @@ changes: * Returns: {ChildProcess} The `child_process.execFile()` function is similar to [`child_process.exec()`][] -except that it does not spawn a shell. Rather, the specified executable `file` +except that it does not spawn a shell by default. Rather, the specified executable `file` is spawned directly as a new process making it slightly more efficient than [`child_process.exec()`][]. @@ -312,6 +316,10 @@ async function getVersion() { getVersion(); ``` +*Note*: If the `shell` option is enabled, do not pass unsanitized user input +to this function. Any input containing shell metacharacters may be used to +trigger arbitrary command execution. + ### child_process.fork(modulePath[, args][, options]) -* Returns: {Worker} A reference to `worker`. +* Returns: {cluster.Worker} A reference to `worker`. In a worker, this function will close all servers, wait for the `'close'` event on those servers, and then disconnect the IPC channel. diff --git a/doc/api/console.md b/doc/api/console.md index 1f7dea5b2b7f15..7dd4571a781a3b 100644 --- a/doc/api/console.md +++ b/doc/api/console.md @@ -78,8 +78,8 @@ const { Console } = console; ``` ### new Console(stdout[, stderr]) -* `stdout` {Writable} -* `stderr` {Writable} +* `stdout` {stream.Writable} +* `stderr` {stream.Writable} Creates a new `Console` with one or two writable stream instances. `stdout` is a writable stream to print log or info output. `stderr` is used for warning or diff --git a/doc/api/fs.md b/doc/api/fs.md index 1eaae690190c70..d4ee09bd0b9afe 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -577,7 +577,7 @@ changes: * `path` {string|Buffer|URL} * `mode` {integer} **Default:** `fs.constants.F_OK` -* Returns: `undefined` +* Returns: {undefined} Synchronously tests a user's permissions for the file or directory specified by `path`. The `mode` argument is an optional integer that specifies the diff --git a/doc/api/http2.md b/doc/api/http2.md index 31afa378f274ea..df729fac50c14b 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -589,7 +589,7 @@ client. added: v8.4.0 --> -* Extends: {Duplex} +* Extends: {stream.Duplex} Each instance of the `Http2Stream` class represents a bidirectional HTTP/2 communications stream over an `Http2Session` instance. Any single `Http2Session` @@ -1308,7 +1308,7 @@ an `Http2Session` object. If no listener is registered for this event, an added: v8.5.0 --> -* `socket` {http2.ServerHttp2Stream} +* `socket` {ServerHttp2Stream} If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here. The stream will already be destroyed when this event is triggered. diff --git a/doc/api/readline.md b/doc/api/readline.md index 7ba1277dce2d9c..42d07da2d0e418 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -323,7 +323,7 @@ Interface's `input` *as if it were provided by the user*. added: v0.7.7 --> -* `stream` {Writable} +* `stream` {stream.Writable} * `dir` {number} * `-1` - to the left from cursor * `1` - to the right from cursor @@ -338,7 +338,7 @@ in a specified direction identified by `dir`. added: v0.7.7 --> -* `stream` {Writable} +* `stream` {stream.Writable} The `readline.clearScreenDown()` method clears the given [TTY][] stream from the current position of the cursor down. @@ -362,9 +362,9 @@ changes: --> * `options` {Object} - * `input` {Readable} The [Readable][] stream to listen to. This option is + * `input` {stream.Readable} The [Readable][] stream to listen to. This option is *required*. - * `output` {Writable} The [Writable][] stream to write readline data to. + * `output` {stream.Writable} The [Writable][] stream to write readline data to. * `completer` {Function} An optional function used for Tab autocompletion. * `terminal` {boolean} `true` if the `input` and `output` streams should be treated like a TTY, and have ANSI/VT100 escape codes written to it. @@ -444,7 +444,7 @@ function completer(linePartial, callback) { added: v0.7.7 --> -* `stream` {Writable} +* `stream` {stream.Writable} * `x` {number} * `y` {number} @@ -456,7 +456,7 @@ given [TTY][] `stream`. added: v0.7.7 --> -* `stream` {Readable} +* `stream` {stream.Readable} * `interface` {readline.Interface} The `readline.emitKeypressEvents()` method causes the given [Readable][] @@ -482,7 +482,7 @@ if (process.stdin.isTTY) added: v0.7.7 --> -* `stream` {Writable} +* `stream` {stream.Writable} * `dx` {number} * `dy` {number} diff --git a/doc/api/repl.md b/doc/api/repl.md index 0ff4ee099cb428..7a54928e187ae2 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -388,9 +388,9 @@ changes: * `options` {Object|string} * `prompt` {string} The input prompt to display. Defaults to `> ` (with a trailing space). - * `input` {Readable} The Readable stream from which REPL input will be read. + * `input` {stream.Readable} The Readable stream from which REPL input will be read. Defaults to `process.stdin`. - * `output` {Writable} The Writable stream to which REPL output will be + * `output` {stream.Writable} The Writable stream to which REPL output will be written. Defaults to `process.stdout`. * `terminal` {boolean} If `true`, specifies that the `output` should be treated as a TTY terminal, and have ANSI/VT100 escape codes written to it. diff --git a/doc/api/stream.md b/doc/api/stream.md index 678ac359caa034..74173abe34f18c 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -394,7 +394,7 @@ changes: --> * `encoding` {string} The new default encoding -* Returns: `this` +* Returns: {this} The `writable.setDefaultEncoding()` method sets the default `encoding` for a [Writable][] stream. @@ -525,7 +525,7 @@ A Writable stream in object mode will always ignore the `encoding` argument. added: v8.0.0 --> -* Returns: `this` +* Returns: {this} Destroy the stream, and emit the passed error. After this call, the writable stream has ended. Implementors should not override this method, @@ -810,7 +810,7 @@ readable.isPaused(); // === false added: v0.9.4 --> -* Returns: `this` +* Returns: {this} The `readable.pause()` method will cause a stream in flowing mode to stop emitting [`'data'`][] events, switching out of flowing mode. Any data that @@ -950,7 +950,7 @@ event has been emitted will return `null`. No runtime error will be raised. added: v0.9.4 --> -* Returns: `this` +* Returns: {this} The `readable.resume()` method causes an explicitly paused Readable stream to resume emitting [`'data'`][] events, switching the stream into flowing mode. @@ -973,7 +973,7 @@ added: v0.9.4 --> * `encoding` {string} The encoding to use. -* Returns: `this` +* Returns: {this} The `readable.setEncoding()` method sets the character encoding for data read from the Readable stream. diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 3bcdd817a2f134..0ab73162dd59e0 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -13,13 +13,13 @@ const jsPrimitives = { 'undefined': 'Undefined' }; const jsGlobalTypes = [ - 'Error', 'Object', 'Function', 'Array', 'TypedArray', 'Uint8Array', - 'Uint16Array', 'Uint32Array', 'Int8Array', 'Int16Array', 'Int32Array', - 'Uint8ClampedArray', 'Float32Array', 'Float64Array', 'Date', 'RegExp', - 'ArrayBuffer', 'DataView', 'Promise', 'EvalError', 'RangeError', - 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError', 'Proxy', 'Map', - 'Set', 'WeakMap', 'WeakSet', 'Generator', 'GeneratorFunction', - 'AsyncFunction', 'SharedArrayBuffer' + 'Array', 'ArrayBuffer', 'AsyncFunction', 'DataView', 'Date', 'Error', + 'EvalError', 'Float32Array', 'Float64Array', 'Function', 'Generator', + 'GeneratorFunction', 'Int16Array', 'Int32Array', 'Int8Array', 'Map', 'Object', + 'Promise', 'Proxy', 'RangeError', 'ReferenceError', 'RegExp', 'Set', + 'SharedArrayBuffer', 'SyntaxError', 'TypeError', 'TypedArray', 'URIError', + 'Uint16Array', 'Uint32Array', 'Uint8Array', 'Uint8ClampedArray', 'WeakMap', + 'WeakSet' ]; const typeMap = { 'Iterable': @@ -27,34 +27,68 @@ const typeMap = { 'Iterator': `${jsDocPrefix}Reference/Iteration_protocols#The_iterator_protocol`, + 'this': `${jsDocPrefix}Reference/Operators/this`, + + 'AsyncHook': 'async_hooks.html#async_hooks_async_hooks_createhook_callbacks', + 'Buffer': 'buffer.html#buffer_class_buffer', 'ChildProcess': 'child_process.html#child_process_class_childprocess', 'cluster.Worker': 'cluster.html#cluster_class_worker', + 'crypto.constants': 'crypto.html#crypto_crypto_constants_1', + 'dgram.Socket': 'dgram.html#dgram_class_dgram_socket', + 'Domain': 'domain.html#domain_class_domain', + 'EventEmitter': 'events.html#events_class_eventemitter', + 'fs.Stats': 'fs.html#fs_class_fs_stats', + 'http.Agent': 'http.html#http_class_http_agent', 'http.ClientRequest': 'http.html#http_class_http_clientrequest', 'http.IncomingMessage': 'http.html#http_class_http_incomingmessage', 'http.Server': 'http.html#http_class_http_server', 'http.ServerResponse': 'http.html#http_class_http_serverresponse', + 'ClientHttp2Stream': 'http2.html#http2_class_clienthttp2stream', + 'HTTP2 Headers Object': 'http2.html#http2_headers_object', + 'HTTP2 Settings Object': 'http2.html#http2_settings_object', + 'http2.Http2ServerRequest': 'http2.html#http2_class_http2_http2serverrequest', + 'http2.Http2ServerResponse': + 'http2.html#http2_class_http2_http2serverresponse', + 'Http2Server': 'http2.html#http2_class_http2server', + 'Http2Session': 'http2.html#http2_class_http2session', + 'Http2Stream': 'http2.html#http2_class_http2stream', + 'ServerHttp2Stream': 'http2.html#http2_class_serverhttp2stream', + 'Handle': 'net.html#net_server_listen_handle_backlog_callback', + 'net.Server': 'net.html#net_class_net_server', 'net.Socket': 'net.html#net_class_net_socket', + 'os.constants.dlopen': 'os.html#os_dlopen_constants', + + 'PerformanceObserver': + 'perf_hooks.html#perf_hooks_class_performanceobserver_callback', + 'PerformanceObserverEntryList': + 'perf_hooks.html#perf_hooks_class_performanceobserverentrylist', + + 'readline.Interface': 'readline.html#readline_class_interface', + 'Stream': 'stream.html#stream_stream', + 'stream.Duplex': 'stream.html#stream_class_stream_duplex', 'stream.Readable': 'stream.html#stream_class_stream_readable', 'stream.Writable': 'stream.html#stream_class_stream_writable', - 'stream.Duplex': 'stream.html#stream_class_stream_duplex', - - 'tls.TLSSocket': 'tls.html#tls_class_tls_tlssocket', + 'Immediate': 'timers.html#timers_class_immediate', + 'Timeout': 'timers.html#timers_class_timeout', 'Timer': 'timers.html#timers_timers', + 'tls.Server': 'tls.html#tls_class_tls_server', + 'tls.TLSSocket': 'tls.html#tls_class_tls_tlssocket', + 'URL': 'url.html#url_the_whatwg_url_api', 'URLSearchParams': 'url.html#url_class_urlsearchparams' }; From d02b72e799764d4e3e6d7eb5765cd89d27e63665 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Mon, 18 Sep 2017 10:30:52 +0200 Subject: [PATCH 046/218] deps: V8: backport 596d55a from upstream Did not remove ActivationsFinder from `src/runtime/runtime-compiler.cc` as in the original commit as the Class is still being used prior to f0acede landing Original Commit Message: Deoptimization and multithreading. When using Lockers and Unlockers it is possible to create a scenario where multiple threads point to the same optimized code object. When that happens, if one of the threads triggers deoptimization, then the stack replacement needs to happen in the stacks of all threads. With this CL, the deoptimizer visits all threads to do so. The CL also adds three tests where V8 used to crash due to this issue. Bug: v8:6563 Change-Id: I74e9af472d4833aa8d13e579df45133791f6a503 Reviewed-on: https://chromium-review.googlesource.com/670783 Reviewed-by: Jaroslav Sevcik Commit-Queue: Juliana Patricia Vicente Franco Cr-Commit-Position: refs/heads/master@{#48060} PR-URL: https://github.com/nodejs/node/pull/19477 Fixes: https://github.com/nodejs/node/issues/19274 Refs: https://github.com/v8/v8/commit/596d55a Refs: https://github.com/v8/v8/commit/f0acede Reviewed-By: Gus Caplan Reviewed-By: Yang Guo Reviewed-By: Franziska Hinkelmann Reviewed-By: Benedikt Meurer --- deps/v8/src/deoptimizer.cc | 77 ++++++--- deps/v8/test/cctest/test-lockers.cc | 238 ++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 20 deletions(-) diff --git a/deps/v8/src/deoptimizer.cc b/deps/v8/src/deoptimizer.cc index 93a21a7b3adf5c..21b36ba49c9149 100644 --- a/deps/v8/src/deoptimizer.cc +++ b/deps/v8/src/deoptimizer.cc @@ -144,6 +144,50 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm, generator.Generate(); } +namespace { +class ActivationsFinder : public ThreadVisitor { + public: + explicit ActivationsFinder(std::set* codes, + Code* topmost_optimized_code, + bool safe_to_deopt_topmost_optimized_code) + : codes_(codes) { +#ifdef DEBUG + topmost_ = topmost_optimized_code; + safe_to_deopt_ = safe_to_deopt_topmost_optimized_code; +#endif + } + + // Find the frames with activations of codes marked for deoptimization, search + // for the trampoline to the deoptimizer call respective to each code, and use + // it to replace the current pc on the stack. + void VisitThread(Isolate* isolate, ThreadLocalTop* top) { + for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) { + if (it.frame()->type() == StackFrame::OPTIMIZED) { + Code* code = it.frame()->LookupCode(); + if (code->kind() == Code::OPTIMIZED_FUNCTION && + code->marked_for_deoptimization()) { + codes_->erase(code); + // Obtain the trampoline to the deoptimizer call. + SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc()); + int trampoline_pc = safepoint.trampoline_pc(); + DCHECK_IMPLIES(code == topmost_, safe_to_deopt_); + // Replace the current pc on the stack with the trampoline. + it.frame()->set_pc(code->instruction_start() + trampoline_pc); + } + } + } + } + + private: + std::set* codes_; + +#ifdef DEBUG + Code* topmost_; + bool safe_to_deopt_; +#endif +}; +} // namespace + void Deoptimizer::VisitAllOptimizedFunctionsForContext( Context* context, OptimizedFunctionVisitor* visitor) { DisallowHeapAllocation no_allocation; @@ -264,9 +308,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) { VisitAllOptimizedFunctionsForContext(context, &unlinker); Isolate* isolate = context->GetHeap()->isolate(); -#ifdef DEBUG Code* topmost_optimized_code = NULL; bool safe_to_deopt_topmost_optimized_code = false; +#ifdef DEBUG // Make sure all activations of optimized code can deopt at their current PC. // The topmost optimized code has special handling because it cannot be // deoptimized due to weak object dependency. @@ -304,6 +348,10 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) { } #endif + // We will use this set to mark those Code objects that are marked for + // deoptimization and have not been found in stack frames. + std::set codes; + // Move marked code from the optimized code list to the deoptimized // code list. // Walk over all optimized code objects in this native context. @@ -335,25 +383,14 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) { element = next; } - // Finds the with activations of codes marked for deoptimization, search for - // the trampoline to the deoptimizer call respective to each code, and use it - // to replace the current pc on the stack. - for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done(); - it.Advance()) { - if (it.frame()->type() == StackFrame::OPTIMIZED) { - Code* code = it.frame()->LookupCode(); - if (code->kind() == Code::OPTIMIZED_FUNCTION && - code->marked_for_deoptimization()) { - // Obtain the trampoline to the deoptimizer call. - SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc()); - int trampoline_pc = safepoint.trampoline_pc(); - DCHECK_IMPLIES(code == topmost_optimized_code, - safe_to_deopt_topmost_optimized_code); - // Replace the current pc on the stack with the trampoline. - it.frame()->set_pc(code->instruction_start() + trampoline_pc); - } - } - } + ActivationsFinder visitor(&codes, topmost_optimized_code, + safe_to_deopt_topmost_optimized_code); + // Iterate over the stack of this thread. + visitor.VisitThread(isolate, isolate->thread_local_top()); + // In addition to iterate over the stack of this thread, we also + // need to consider all the other threads as they may also use + // the code currently beings deoptimized. + isolate->thread_manager()->IterateArchivedThreads(&visitor); } diff --git a/deps/v8/test/cctest/test-lockers.cc b/deps/v8/test/cctest/test-lockers.cc index a310bfd68456c4..36a9f11ee27773 100644 --- a/deps/v8/test/cctest/test-lockers.cc +++ b/deps/v8/test/cctest/test-lockers.cc @@ -55,6 +55,244 @@ using ::v8::Value; using ::v8::V8; +namespace { + +class DeoptimizeCodeThread : public v8::base::Thread { + public: + DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local context, + const char* trigger) + : Thread(Options("DeoptimizeCodeThread")), + isolate_(isolate), + context_(isolate, context), + source_(trigger) {} + + void Run() { + v8::Locker locker(isolate_); + isolate_->Enter(); + v8::HandleScope handle_scope(isolate_); + v8::Local context = + v8::Local::New(isolate_, context_); + v8::Context::Scope context_scope(context); + CHECK_EQ(isolate_, v8::Isolate::GetCurrent()); + // This code triggers deoptimization of some function that will be + // used in a different thread. + CompileRun(source_); + isolate_->Exit(); + } + + private: + v8::Isolate* isolate_; + Persistent context_; + // The code that triggers the deoptimization. + const char* source_; +}; + +void UnlockForDeoptimization(const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + // Gets the pointer to the thread that will trigger the deoptimization of the + // code. + DeoptimizeCodeThread* deoptimizer = + reinterpret_cast(isolate->GetData(0)); + { + // Exits and unlocks the isolate. + isolate->Exit(); + v8::Unlocker unlocker(isolate); + // Starts the deoptimizing thread. + deoptimizer->Start(); + // Waits for deoptimization to finish. + deoptimizer->Join(); + } + // The deoptimizing thread has finished its work, and the isolate + // will now be used by the current thread. + isolate->Enter(); +} + +void UnlockForDeoptimizationIfReady( + const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + bool* ready_to_deoptimize = reinterpret_cast(isolate->GetData(1)); + if (*ready_to_deoptimize) { + // The test should enter here only once, so put the flag back to false. + *ready_to_deoptimize = false; + // Gets the pointer to the thread that will trigger the deoptimization of + // the code. + DeoptimizeCodeThread* deoptimizer = + reinterpret_cast(isolate->GetData(0)); + { + // Exits and unlocks the thread. + isolate->Exit(); + v8::Unlocker unlocker(isolate); + // Starts the thread that deoptimizes the function. + deoptimizer->Start(); + // Waits for the deoptimizing thread to finish. + deoptimizer->Join(); + } + // The deoptimizing thread has finished its work, and the isolate + // will now be used by the current thread. + isolate->Enter(); + } +} +} // namespace + +TEST(LazyDeoptimizationMultithread) { + i::FLAG_allow_natives_syntax = true; + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope scope(isolate); + v8::Local context = v8::Context::New(isolate); + const char* trigger_deopt = "obj = { y: 0, x: 1 };"; + + // We use the isolate to pass arguments to the UnlockForDeoptimization + // function. Namely, we pass a pointer to the deoptimizing thread. + DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt); + isolate->SetData(0, &deoptimize_thread); + v8::Context::Scope context_scope(context); + + // Create the function templace for C++ code that is invoked from + // JavaScript code. + Local fun_templ = + v8::FunctionTemplate::New(isolate, UnlockForDeoptimization); + Local fun = fun_templ->GetFunction(context).ToLocalChecked(); + CHECK(context->Global() + ->Set(context, v8_str("unlock_for_deoptimization"), fun) + .FromJust()); + + // Optimizes a function f, which will be deoptimized in another + // thread. + CompileRun( + "var b = false; var obj = { x: 1 };" + "function f() { g(); return obj.x; }" + "function g() { if (b) { unlock_for_deoptimization(); } }" + "%NeverOptimizeFunction(g);" + "f(); f(); %OptimizeFunctionOnNextCall(f);" + "f();"); + + // Trigger the unlocking. + Local v = CompileRun("b = true; f();"); + + // Once the isolate has been unlocked, the thread will wait for the + // other thread to finish its task. Once this happens, this thread + // continues with its execution, that is, with the execution of the + // function g, which then returns to f. The function f should have + // also been deoptimized. If the replacement did not happen on this + // thread's stack, then the test will fail here. + CHECK(v->IsNumber()); + CHECK_EQ(1, static_cast(v->NumberValue(context).FromJust())); + } + isolate->Dispose(); +} + +TEST(LazyDeoptimizationMultithreadWithNatives) { + i::FLAG_allow_natives_syntax = true; + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope scope(isolate); + v8::Local context = v8::Context::New(isolate); + const char* trigger_deopt = "%DeoptimizeFunction(f);"; + + // We use the isolate to pass arguments to the UnlockForDeoptimization + // function. Namely, we pass a pointer to the deoptimizing thread. + DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt); + isolate->SetData(0, &deoptimize_thread); + bool ready_to_deopt = false; + isolate->SetData(1, &ready_to_deopt); + v8::Context::Scope context_scope(context); + + // Create the function templace for C++ code that is invoked from + // JavaScript code. + Local fun_templ = + v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady); + Local fun = fun_templ->GetFunction(context).ToLocalChecked(); + CHECK(context->Global() + ->Set(context, v8_str("unlock_for_deoptimization"), fun) + .FromJust()); + + // Optimizes a function f, which will be deoptimized in another + // thread. + CompileRun( + "var obj = { x: 1 };" + "function f() { g(); return obj.x;}" + "function g() { " + " unlock_for_deoptimization(); }" + "%NeverOptimizeFunction(g);" + "f(); f(); %OptimizeFunctionOnNextCall(f);"); + + // Trigger the unlocking. + ready_to_deopt = true; + isolate->SetData(1, &ready_to_deopt); + Local v = CompileRun("f();"); + + // Once the isolate has been unlocked, the thread will wait for the + // other thread to finish its task. Once this happens, this thread + // continues with its execution, that is, with the execution of the + // function g, which then returns to f. The function f should have + // also been deoptimized. Otherwise, the test will fail here. + CHECK(v->IsNumber()); + CHECK_EQ(1, static_cast(v->NumberValue(context).FromJust())); + } + isolate->Dispose(); +} + +TEST(EagerDeoptimizationMultithread) { + i::FLAG_allow_natives_syntax = true; + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + v8::Locker locker(isolate); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope scope(isolate); + v8::Local context = v8::Context::New(isolate); + const char* trigger_deopt = "f({y: 0, x: 1});"; + + // We use the isolate to pass arguments to the UnlockForDeoptimization + // function. Namely, we pass a pointer to the deoptimizing thread. + DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt); + isolate->SetData(0, &deoptimize_thread); + bool ready_to_deopt = false; + isolate->SetData(1, &ready_to_deopt); + v8::Context::Scope context_scope(context); + + // Create the function templace for C++ code that is invoked from + // JavaScript code. + Local fun_templ = + v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady); + Local fun = fun_templ->GetFunction(context).ToLocalChecked(); + CHECK(context->Global() + ->Set(context, v8_str("unlock_for_deoptimization"), fun) + .FromJust()); + + // Optimizes a function f, which will be deoptimized by another thread. + CompileRun( + "function f(obj) { unlock_for_deoptimization(); return obj.x; }" + "f({x: 1}); f({x: 1});" + "%OptimizeFunctionOnNextCall(f);" + "f({x: 1});"); + + // Trigger the unlocking. + ready_to_deopt = true; + isolate->SetData(1, &ready_to_deopt); + Local v = CompileRun("f({x: 1});"); + + // Once the isolate has been unlocked, the thread will wait for the + // other thread to finish its task. Once this happens, this thread + // continues with its execution, that is, with the execution of the + // function g, which then returns to f. The function f should have + // also been deoptimized. Otherwise, the test will fail here. + CHECK(v->IsNumber()); + CHECK_EQ(1, static_cast(v->NumberValue(context).FromJust())); + } + isolate->Dispose(); +} + // Migrating an isolate class KangarooThread : public v8::base::Thread { public: From 16bf5fed69905e64d860c6997a00e0248f5b7cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Mon, 18 Dec 2017 13:23:46 +0100 Subject: [PATCH 047/218] crypto: reuse variable instead of reevaluation Backport-PR-URL: https://github.com/nodejs/node/pull/19114 PR-URL: https://github.com/nodejs/node/pull/17735 Reviewed-By: Colin Ihrig Reviewed-By: Daniel Bevenius Reviewed-By: Jon Moss Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- src/node_crypto.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 685e7bb73b5a5c..6b5958b35e7049 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3449,7 +3449,7 @@ void CipherBase::Init(const char* cipher_type, nullptr, reinterpret_cast(key), reinterpret_cast(iv), - kind_ == kCipher); + encrypt); } @@ -3518,7 +3518,7 @@ void CipherBase::InitIv(const char* cipher_type, nullptr, reinterpret_cast(key), reinterpret_cast(iv), - kind_ == kCipher); + encrypt); } From 745600a0b336c5d1591ec15f3e59e2e0c1cc1f5f Mon Sep 17 00:00:00 2001 From: Kyle Farnung Date: Tue, 27 Feb 2018 10:53:45 -0800 Subject: [PATCH 048/218] test: remove orphaned entries from status PR-URL: https://github.com/nodejs/node/pull/18092 Backport-PR-URL: https://github.com/nodejs/node/pull/19043 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Rich Trott Reviewed-By: Gireesh Punathil Reviewed-By: Daniel Bevenius --- test/known_issues/known_issues.status | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/known_issues/known_issues.status b/test/known_issues/known_issues.status index 46c8ed32741c7d..e21913e232c03f 100644 --- a/test/known_issues/known_issues.status +++ b/test/known_issues/known_issues.status @@ -7,8 +7,6 @@ prefix known_issues [true] # This section applies to all platforms [$system==win32] -test-stdout-buffer-flush-on-exit: SKIP -test-cluster-disconnect-handles: SKIP [$system==linux] From 016a28ac08a60498dd1f36dfdc550df8e6f8118c Mon Sep 17 00:00:00 2001 From: Sarat Addepalli Date: Mon, 8 Jan 2018 23:16:27 +0530 Subject: [PATCH 049/218] tools: non-Ascii linter for /lib only Non-ASCII characters in /lib get compiled into the node binary, and may bloat the binary size unnecessarily. A linter rule may help prevent this. PR-URL: https://github.com/nodejs/node/pull/18043 Backport-PR-URL: https://github.com/nodejs/node/pull/19499 Fixes: https://github.com/nodejs/node/issues/11209 Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater Reviewed-By: Teddy Katz --- lib/.eslintrc.yaml | 1 + lib/console.js | 4 +- lib/internal/http2/core.js | 2 +- lib/internal/test/unicode.js | 2 + lib/stream.js | 2 +- lib/timers.js | 2 + lib/zlib.js | 2 +- tools/eslint-rules/non-ascii-character.js | 61 +++++++++++++++++++++++ 8 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 tools/eslint-rules/non-ascii-character.js diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index 437aa575645ad6..0b00638e2a638c 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -6,3 +6,4 @@ rules: buffer-constructor: error no-let-in-for-declaration: error lowercase-name-for-primitive: error + non-ascii-character: error diff --git a/lib/console.js b/lib/console.js index d0f7e61fd5a709..4495074231a2eb 100644 --- a/lib/console.js +++ b/lib/console.js @@ -81,7 +81,7 @@ function createWriteErrorHandler(stream) { // If there was an error, it will be emitted on `stream` as // an `error` event. Adding a `once` listener will keep that error // from becoming an uncaught exception, but since the handler is - // removed after the event, non-console.* writes won’t be affected. + // removed after the event, non-console.* writes won't be affected. // we are only adding noop if there is no one else listening for 'error' if (stream.listenerCount('error') === 0) { stream.on('error', noop); @@ -114,7 +114,7 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) { // even in edge cases such as low stack space. if (e.message === 'Maximum call stack size exceeded') throw e; - // Sorry, there’s no proper way to pass along the error here. + // Sorry, there's no proper way to pass along the error here. } finally { stream.removeListener('error', noop); } diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index d83897c20b7902..cce54ef377fe0c 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -1537,7 +1537,7 @@ function processRespondWithFD(fd, headers, offset = 0, length = -1, return; } // exact length of the file doesn't matter here, since the - // stream is closing anyway — just use 1 to signify that + // stream is closing anyway - just use 1 to signify that // a write does exist trackWriteState(this, 1); } diff --git a/lib/internal/test/unicode.js b/lib/internal/test/unicode.js index 1445276d9ae891..7172a43ec20a8a 100644 --- a/lib/internal/test/unicode.js +++ b/lib/internal/test/unicode.js @@ -3,4 +3,6 @@ // This module exists entirely for regression testing purposes. // See `test/parallel/test-internal-unicode.js`. +/* eslint-disable non-ascii-character */ module.exports = '✓'; +/* eslint-enable non-ascii-character */ diff --git a/lib/stream.js b/lib/stream.js index edc5f231b83411..9a816600a05e5a 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -45,7 +45,7 @@ try { try { Stream._isUint8Array = process.binding('util').isUint8Array; } catch (e) { - // This throws for Node < 4.2.0 because there’s no util binding and + // This throws for Node < 4.2.0 because there's no util binding and // returns undefined for Node < 7.4.0. } } diff --git a/lib/timers.js b/lib/timers.js index 0e6ae45950c5c1..f3c3c6308433eb 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -89,6 +89,7 @@ const TIMEOUT_MAX = 2147483647; // 2^31-1 // TimerWrap C++ handle, which makes the call after the duration to process the // list it is attached to. // +/* eslint-disable non-ascii-character */ // // ╔════ > Object Map // ║ @@ -110,6 +111,7 @@ const TIMEOUT_MAX = 2147483647; // 2^31-1 // ║ // ╚════ > Linked List // +/* eslint-enable non-ascii-character */ // // With this, virtually constant-time insertion (append), removal, and timeout // is possible in the JavaScript layer. Any one list of timers is able to be diff --git a/lib/zlib.js b/lib/zlib.js index 7f41200f86be19..bbe89043248459 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -339,7 +339,7 @@ Zlib.prototype.flush = function flush(kind, callback) { this._scheduledFlushFlag = maxFlush(kind, this._scheduledFlushFlag); // If a callback was passed, always register a new `drain` + flush handler, - // mostly because that’s simpler and flush callbacks piling up is a rare + // mostly because that's simpler and flush callbacks piling up is a rare // thing anyway. if (!alreadyHadFlushScheduled || callback) { const drainHandler = () => this.flush(this._scheduledFlushFlag, callback); diff --git a/tools/eslint-rules/non-ascii-character.js b/tools/eslint-rules/non-ascii-character.js new file mode 100644 index 00000000000000..e67aac7cd91e82 --- /dev/null +++ b/tools/eslint-rules/non-ascii-character.js @@ -0,0 +1,61 @@ +/** + * @fileOverview Any non-ASCII characters in lib/ will increase the size + * of the compiled node binary. This linter rule ensures that + * any such character is reported. + * @author Sarat Addepalli + */ + +'use strict'; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +const nonAsciiRegexPattern = /[^\r\n\x20-\x7e]/; +const suggestions = { + '’': '\'', + '‛': '\'', + '‘': '\'', + '“': '"', + '‟': '"', + '”': '"', + '«': '"', + '»': '"', + '—': '-' +}; + +module.exports = (context) => { + + const reportIfError = (node, sourceCode) => { + + const matches = sourceCode.text.match(nonAsciiRegexPattern); + + if (!matches) return; + + const offendingCharacter = matches[0]; + const offendingCharacterPosition = matches.index; + const suggestion = suggestions[offendingCharacter]; + + let message = `Non-ASCII character '${offendingCharacter}' detected.`; + + message = suggestion ? + `${message} Consider replacing with: ${suggestion}` : + message; + + context.report({ + node, + message, + loc: sourceCode.getLocFromIndex(offendingCharacterPosition), + fix: (fixer) => { + return fixer.replaceText( + node, + suggestion ? `${suggestion}` : '' + ); + } + }); + }; + + return { + Program: (node) => reportIfError(node, context.getSourceCode()) + }; +}; From d537f45aaaced00202efbd45ad6bc9a2c9bf289f Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sun, 21 Jan 2018 01:14:03 +0800 Subject: [PATCH 050/218] build: make lint-md independent of local node PR-URL: https://github.com/nodejs/node/pull/18272 Backport-PR-URL: https://github.com/nodejs/node/pull/19190 Reviewed-By: Gibson Fahnestock Reviewed-By: James M Snell --- Makefile | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 9ea385e46a362d..d6756107d7ce1d 100644 --- a/Makefile +++ b/Makefile @@ -1008,26 +1008,31 @@ lint-md-clean: lint-md-build: @if [ ! -d tools/remark-cli/node_modules ]; then \ echo "Markdown linter: installing remark-cli into tools/"; \ - cd tools/remark-cli && ../../$(NODE) ../../$(NPM) install; fi + cd tools/remark-cli && $(call available-node,$(run-npm-install)) fi @if [ ! -d tools/remark-preset-lint-node/node_modules ]; then \ echo "Markdown linter: installing remark-preset-lint-node into tools/"; \ - cd tools/remark-preset-lint-node && ../../$(NODE) ../../$(NPM) install; fi + cd tools/remark-preset-lint-node && $(call available-node,$(run-npm-install)) fi + ifneq ("","$(wildcard tools/remark-cli/node_modules/)") -LINT_MD_TARGETS = src lib benchmark tools/doc tools/icu -LINT_MD_ROOT_DOCS := $(wildcard *.md) -LINT_MD_FILES := $(shell find $(LINT_MD_TARGETS) -type f \ - -not -path '*node_modules*' -name '*.md') $(LINT_MD_ROOT_DOCS) -LINT_DOC_MD_FILES = $(shell ls doc/**/*.md) -tools/.docmdlintstamp: $(LINT_DOC_MD_FILES) +LINT_MD_DOC_FILES = $(shell ls doc/**/*.md) +run-lint-doc-md = tools/remark-cli/cli.js -q -f $(LINT_MD_DOC_FILES) +# Lint all changed markdown files under doc/ +tools/.docmdlintstamp: $(LINT_MD_DOC_FILES) @echo "Running Markdown linter on docs..." - @$(NODE) tools/remark-cli/cli.js -q -f $(LINT_DOC_MD_FILES) + @$(call available-node,$(run-lint-doc-md)) @touch $@ -tools/.miscmdlintstamp: $(LINT_MD_FILES) +LINT_MD_TARGETS = src lib benchmark tools/doc tools/icu +LINT_MD_ROOT_DOCS := $(wildcard *.md) +LINT_MD_MISC_FILES := $(shell find $(LINT_MD_TARGETS) -type f \ + -not -path '*node_modules*' -name '*.md') $(LINT_MD_ROOT_DOCS) +run-lint-misc-md = tools/remark-cli/cli.js -q -f $(LINT_MD_MISC_FILES) +# Lint other changed markdown files maintained by us +tools/.miscmdlintstamp: $(LINT_MD_MISC_FILES) @echo "Running Markdown linter on misc docs..." - @$(NODE) tools/remark-cli/cli.js -q -f $(LINT_MD_FILES) + @$(call available-node,$(run-lint-misc-md)) @touch $@ tools/.mdlintstamp: tools/.miscmdlintstamp tools/.docmdlintstamp From 333d7dda84447e30d9f27d5b66fd7e30f54d226f Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sun, 21 Jan 2018 01:17:47 +0800 Subject: [PATCH 051/218] build: make lint-js independent of local node PR-URL: https://github.com/nodejs/node/pull/18272 Backport-PR-URL: https://github.com/nodejs/node/pull/19190 Reviewed-By: Gibson Fahnestock Reviewed-By: James M Snell --- Makefile | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index d6756107d7ce1d..d0b481d03ab9f3 100644 --- a/Makefile +++ b/Makefile @@ -1045,37 +1045,29 @@ lint-md: endif LINT_JS_TARGETS = benchmark doc lib test tools -LINT_JS_CMD = tools/eslint/bin/eslint.js --cache \ - --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md \ - $(LINT_JS_TARGETS) + +run-lint-js = tools/eslint/bin/eslint.js --cache \ + --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md $(LINT_JS_TARGETS) +run-lint-js-fix = $(run-lint-js) --fix lint-js-fix: - @if [ -x $(NODE) ]; then \ - $(NODE) $(LINT_JS_CMD) --fix; \ - else \ - node $(LINT_JS_CMD) --fix; \ - fi + @$(call available-node,$(run-lint-js-fix)) lint-js: @echo "Running JS linter..." - @if [ -x $(NODE) ]; then \ - $(NODE) $(LINT_JS_CMD); \ - else \ - node $(LINT_JS_CMD); \ - fi + @$(call available-node,$(run-lint-js)) jslint: lint-js @echo "Please use lint-js instead of jslint" +run-lint-js-ci = tools/lint-js.js $(PARALLEL_ARGS) -f tap -o test-eslint.tap \ + $(LINT_JS_TARGETS) + +.PHONY: lint-js-ci +# On the CI the output is emitted in the TAP format. lint-js-ci: @echo "Running JS linter..." - @if [ -x $(NODE) ]; then \ - $(NODE) tools/lint-js.js $(PARALLEL_ARGS) -f tap -o test-eslint.tap \ - $(LINT_JS_TARGETS); \ - else \ - node tools/lint-js.js $(PARALLEL_ARGS) -f tap -o test-eslint.tap \ - $(LINT_JS_TARGETS); \ - fi + @$(call available-node,$(run-lint-js-ci)) jslint-ci: lint-js-ci @echo "Please use lint-js-ci instead of jslint-ci" From c89781583bb53389f0881a166beb7e2d0e59a194 Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Mon, 2 Apr 2018 17:35:56 +0300 Subject: [PATCH 052/218] doc: fix various nits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replace 2 hyphens (--) by spaced m-dashes (—) as per STYLE_GUIDE.md. * Space infix operators. * Unify quotes in inline code spans (use only single quotes). * Unify `* Returns:` (eliminate deviations). * Dedupe spaces. PR-URL: https://github.com/nodejs/node/pull/19743 Backport-PR-URL: https://github.com/nodejs/node/pull/19753 Reviewed-By: James M Snell Reviewed-By: Trivikram Kamat --- doc/api/addons.md | 2 +- doc/api/async_hooks.md | 2 +- doc/api/buffer.md | 10 +++---- doc/api/child_process.md | 32 ++++++++++----------- doc/api/cli.md | 6 ++-- doc/api/cluster.md | 14 +++++----- doc/api/console.md | 2 +- doc/api/crypto.md | 4 +-- doc/api/debugger.md | 2 +- doc/api/dgram.md | 30 ++++++++++---------- doc/api/dns.md | 22 +++++++-------- doc/api/domain.md | 30 ++++++++++---------- doc/api/errors.md | 16 +++++------ doc/api/esm.md | 14 +++++----- doc/api/fs.md | 36 ++++++++++++------------ doc/api/http.md | 60 ++++++++++++++++++++-------------------- doc/api/http2.md | 28 +++++++++---------- doc/api/https.md | 2 +- doc/api/modules.md | 50 ++++++++++++++++----------------- doc/api/n-api.md | 24 ++++++++-------- doc/api/net.md | 16 +++++------ doc/api/os.md | 4 +-- doc/api/path.md | 6 ++-- doc/api/process.md | 40 +++++++++++++-------------- doc/api/readline.md | 2 +- doc/api/repl.md | 6 ++-- doc/api/stream.md | 8 +++--- doc/api/tls.md | 22 +++++++-------- doc/api/url.md | 4 +-- doc/api/util.md | 10 +++---- doc/api/v8.md | 2 +- doc/api/vm.md | 2 +- doc/api/zlib.md | 20 +++++++------- 33 files changed, 264 insertions(+), 264 deletions(-) diff --git a/doc/api/addons.md b/doc/api/addons.md index c6802530f6dc67..03feb8ec619ed5 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -101,7 +101,7 @@ Addon module name is `addon`. Once the source code has been written, it must be compiled into the binary `addon.node` file. To do so, create a file called `binding.gyp` in the top-level of the project describing the build configuration of the module -using a JSON-like format. This file is used by [node-gyp][] -- a tool written +using a JSON-like format. This file is used by [node-gyp][] — a tool written specifically to compile Node.js Addons. ```json diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 781509900cc4c9..a80e70758a37d3 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -582,7 +582,7 @@ asyncResource.triggerAsyncId(); * `type` {string} The type of async event. * `options` {Object} * `triggerAsyncId` {number} The ID of the execution context that created this - async event. **Default:** `executionAsyncId()` + async event. **Default:** `executionAsyncId()` * `requireManualDestroy` {boolean} Disables automatic `emitDestroy` when the object is garbage collected. This usually does not need to be set (even if `emitDestroy` is called manually), unless the resource's asyncId is retrieved diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 3f827088ce5676..8ab7b472605ba6 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -449,7 +449,7 @@ changes: * `size` {integer} The desired length of the new `Buffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than +Allocates a new `Buffer` of `size` bytes. If the `size` is larger than [`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be thrown. A zero-length `Buffer` will be created if `size` is 0. @@ -535,7 +535,7 @@ const buf = Buffer.alloc(5); console.log(buf); ``` -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than +Allocates a new `Buffer` of `size` bytes. If the `size` is larger than [`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be thrown. A zero-length `Buffer` will be created if `size` is 0. @@ -580,7 +580,7 @@ changes: * `size` {integer} The desired length of the new `Buffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than +Allocates a new `Buffer` of `size` bytes. If the `size` is larger than [`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be thrown. A zero-length `Buffer` will be created if `size` is 0. @@ -626,7 +626,7 @@ added: v5.12.0 * `size` {integer} The desired length of the new `Buffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than +Allocates a new `Buffer` of `size` bytes. If the `size` is larger than [`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be thrown. A zero-length `Buffer` will be created if `size` is 0. @@ -2660,7 +2660,7 @@ deprecated: v6.0.0 * `size` {integer} The desired length of the new `SlowBuffer`. -Allocates a new `Buffer` of `size` bytes. If the `size` is larger than +Allocates a new `Buffer` of `size` bytes. If the `size` is larger than [`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be thrown. A zero-length `Buffer` will be created if `size` is 0. diff --git a/doc/api/child_process.md b/doc/api/child_process.md index cfdd650b7603b1..620719111c48c8 100755 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -38,7 +38,7 @@ the event loop until the spawned process either exits or is terminated. For convenience, the `child_process` module provides a handful of synchronous and asynchronous alternatives to [`child_process.spawn()`][] and -[`child_process.spawnSync()`][]. *Note that each of these alternatives are +[`child_process.spawnSync()`][]. *Note that each of these alternatives are implemented on top of [`child_process.spawn()`][] or [`child_process.spawnSync()`][].* * [`child_process.exec()`][]: spawns a shell and runs a command within that shell, @@ -143,8 +143,8 @@ changes: [Shell Requirements][] and [Default Windows Shell][]. * `timeout` {number} **Default:** `0` * `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or - stderr. **Default:** `200*1024`. If exceeded, the child process is terminated. - See caveat at [`maxBuffer` and Unicode][]. + stderr. **Default:** `200 * 1024`. If exceeded, the child process is + terminated. See caveat at [`maxBuffer` and Unicode][]. * `killSignal` {string|integer} **Default:** `'SIGTERM'` * `uid` {number} Sets the user identity of the process (see setuid(2)). * `gid` {number} Sets the group identity of the process (see setgid(2)). @@ -187,7 +187,7 @@ exec('cat *.js bad_file | wc -l', (error, stdout, stderr) => { ``` If a `callback` function is provided, it is called with the arguments -`(error, stdout, stderr)`. On success, `error` will be `null`. On error, +`(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 @@ -257,8 +257,8 @@ changes: * `encoding` {string} **Default:** `'utf8'` * `timeout` {number} **Default:** `0` * `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or - stderr. **Default:** `200*1024` If exceeded, the child process is terminated. - See caveat at [`maxBuffer` and Unicode][]. + stderr. **Default:** `200 * 1024` If exceeded, the child process is + terminated. See caveat at [`maxBuffer` and Unicode][]. * `killSignal` {string|integer} **Default:** `'SIGTERM'` * `uid` {number} Sets the user identity of the process (see setuid(2)). * `gid` {number} Sets the group identity of the process (see setgid(2)). @@ -536,7 +536,7 @@ disabled*. On non-Windows platforms, if `options.detached` is set to `true`, the child process will be made the leader of a new process group and session. Note that child processes may continue running after the parent exits regardless of -whether they are detached or not. See setsid(2) for more information. +whether they are detached or not. See setsid(2) for more information. By default, the parent will wait for the detached child to exit. To prevent the parent from waiting for a given `subprocess`, use the `subprocess.unref()` @@ -706,8 +706,8 @@ changes: * `killSignal` {string|integer} The signal value to be used when the spawned process will be killed. **Default:** `'SIGTERM'` * `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or - stderr. **Default:** `200*1024` If exceeded, the child process is terminated. - See caveat at [`maxBuffer` and Unicode][]. + stderr. **Default:** `200 * 1024` If exceeded, the child process is + terminated. See caveat at [`maxBuffer` and Unicode][]. * `encoding` {string} The encoding used for all stdio inputs and outputs. **Default:** `'buffer'` * `windowsHide` {boolean} Hide the subprocess console window that would normally be created on Windows systems. **Default:** `false`. @@ -767,8 +767,8 @@ changes: * `killSignal` {string|integer} The signal value to be used when the spawned process will be killed. **Default:** `'SIGTERM'` * `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or - stderr. **Default:** `200*1024` If exceeded, the child process is terminated. - See caveat at [`maxBuffer` and Unicode][]. + stderr. **Default:** `200 * 1024` If exceeded, the child process is + terminated. See caveat at [`maxBuffer` and Unicode][]. * `encoding` {string} The encoding used for all stdio inputs and outputs. **Default:** `'buffer'` * `windowsHide` {boolean} Hide the subprocess console window that would @@ -779,12 +779,12 @@ The `child_process.execSync()` method is generally identical to [`child_process.exec()`][] with the exception that the method will not return until the child process has fully closed. When a timeout has been encountered and `killSignal` is sent, the method won't return until the process has completely -exited. *Note that if the child process intercepts and handles the `SIGTERM` +exited. *Note that if the child process intercepts and handles the `SIGTERM` signal and doesn't exit, the parent process will wait until the child process has exited.* If the process times out or has a non-zero exit code, this method ***will*** -throw. The [`Error`][] object will contain the entire result from +throw. The [`Error`][] object will contain the entire result from [`child_process.spawnSync()`][] *Note*: Never pass unsanitized user input to this function. Any input @@ -825,8 +825,8 @@ changes: * `killSignal` {string|integer} The signal value to be used when the spawned process will be killed. **Default:** `'SIGTERM'` * `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or - stderr. **Default:** `200*1024` If exceeded, the child process is terminated. - See caveat at [`maxBuffer` and Unicode][]. + stderr. **Default:** `200 * 1024` If exceeded, the child process is + terminated. See caveat at [`maxBuffer` and Unicode][]. * `encoding` {string} The encoding used for all stdio inputs and outputs. **Default:** `'buffer'` * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses @@ -1169,7 +1169,7 @@ properties: Defaults to `false`. The optional `callback` is a function that is invoked after the message is -sent but before the child may have received it. The function is called with a +sent but before the child may have received it. The function is called with a single argument: `null` on success, or an [`Error`][] object on failure. If no `callback` function is provided and the message cannot be sent, an diff --git a/doc/api/cli.md b/doc/api/cli.md index 2e918c2876c1a8..3192d846e77933 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -454,7 +454,7 @@ added: v8.0.0 A space-separated list of command line options. `options...` are interpreted as if they had been specified on the command line before the actual command line -(so they can be overridden). Node will exit with an error if an option that is +(so they can be overridden). Node will exit with an error if an option that is not allowed in the environment is used, such as `-p` or a script file. Node options that are allowed are: @@ -516,7 +516,7 @@ added: v3.0.0 Path to the file used to store the persistent REPL history. The default path is `~/.node_repl_history`, which is overridden by this variable. Setting the value -to an empty string (`""` or `" "`) disables persistent REPL history. +to an empty string (`''` or `' '`) disables persistent REPL history. ### `NODE_EXTRA_CA_CERTS=file` @@ -601,7 +601,7 @@ reason any of these APIs takes a long time, other (seemingly unrelated) APIs that run in libuv's threadpool will experience degraded performance. In order to mitigate this issue, one potential solution is to increase the size of libuv's threadpool by setting the `'UV_THREADPOOL_SIZE'` environment variable to a value -greater than `4` (its current default value). For more information, see the +greater than `4` (its current default value). For more information, see the [libuv threadpool documentation][]. [`--openssl-config`]: #cli_openssl_config_file diff --git a/doc/api/cluster.md b/doc/api/cluster.md index 3b56b4888a696e..2c42c9e701adb4 100644 --- a/doc/api/cluster.md +++ b/doc/api/cluster.md @@ -92,8 +92,8 @@ Node.js process and a cluster worker differs: the worker to use the supplied handle, rather than talk to the master process. 3. `server.listen(0)` Normally, this will cause servers to listen on a - random port. However, in a cluster, each worker will receive the - same "random" port each time they do `listen(0)`. In essence, the + random port. However, in a cluster, each worker will receive the + same "random" port each time they do `listen(0)`. In essence, the port is random the first time, but predictable thereafter. To listen on a unique port, generate a port number based on the cluster worker ID. @@ -103,8 +103,8 @@ objects for things like sessions and login. Because workers are all separate processes, they can be killed or re-spawned depending on a program's needs, without affecting other -workers. As long as there are some workers still alive, the server will -continue to accept connections. If no workers are alive, existing connections +workers. As long as there are some workers still alive, the server will +continue to accept connections. If no workers are alive, existing connections will be dropped and new connections will be refused. Node.js does not automatically manage the number of workers, however. It is the application's responsibility to manage the worker pool based on its own needs. @@ -499,7 +499,7 @@ Emitted after the worker IPC channel has disconnected. This can occur when a worker exits gracefully, is killed, or is disconnected manually (such as with worker.disconnect()). -There may be a delay between the `'disconnect'` and `'exit'` events. These events +There may be a delay between the `'disconnect'` and `'exit'` events. These events can be used to detect if the process is stuck in a cleanup or if there are long-living connections. @@ -590,7 +590,7 @@ The `addressType` is one of: * `4` (TCPv4) * `6` (TCPv6) * `-1` (unix domain socket) -* `"udp4"` or `"udp6"` (UDP v4 or v6) +* `'udp4'` or `'udp6'` (UDP v4 or v6) ## Event: 'message' -* Returns {number} the `SO_RCVBUF` socket receive buffer size in bytes. +* Returns: {number} the `SO_RCVBUF` socket receive buffer size in bytes. ### socket.getSendBufferSize() -* Returns {number} the `SO_SNDBUF` socket send buffer size in bytes. +* Returns: {number} the `SO_SNDBUF` socket send buffer size in bytes. ### socket.ref() Sometimes, the domain in use is not the one that ought to be used for a -specific event emitter. Or, the event emitter could have been created +specific event emitter. Or, the event emitter could have been created in the context of one domain, but ought to instead be bound to some other domain. @@ -280,7 +280,7 @@ Returns a new Domain object. The Domain class encapsulates the functionality of routing errors and uncaught exceptions to the active Domain object. -Domain is a child class of [`EventEmitter`][]. To handle the errors that it +Domain is a child class of [`EventEmitter`][]. To handle the errors that it catches, listen to its `'error'` event. ### domain.members @@ -294,13 +294,13 @@ to the domain. * `emitter` {EventEmitter|Timer} emitter or timer to be added to the domain -Explicitly adds an emitter to the domain. If any event handlers called by +Explicitly adds an emitter to the domain. If any event handlers called by the emitter throw an error, or if the emitter emits an `'error'` event, it will be routed to the domain's `'error'` event, just like with implicit binding. This also works with timers that are returned from [`setInterval()`][] and -[`setTimeout()`][]. If their callback function throws, it will be caught by +[`setTimeout()`][]. If their callback function throws, it will be caught by the domain 'error' handler. If the Timer or EventEmitter was already bound to a domain, it is removed @@ -312,7 +312,7 @@ from that one, and bound to this one instead. * Returns: {Function} The bound function The returned function will be a wrapper around the supplied callback -function. When the returned function is called, any errors that are +function. When the returned function is called, any errors that are thrown will be routed to the domain's `'error'` event. #### Example @@ -336,7 +336,7 @@ d.on('error', (er) => { ### domain.dispose() -> Stability: 0 - Deprecated. Please recover from failed IO actions +> Stability: 0 - Deprecated. Please recover from failed IO actions > explicitly via error event handlers set on the domain. Once `dispose` has been called, the domain will no longer be used by callbacks @@ -382,7 +382,7 @@ without exiting the domain. * `callback` {Function} The callback function * Returns: {Function} The intercepted function -This method is almost identical to [`domain.bind(callback)`][]. However, in +This method is almost identical to [`domain.bind(callback)`][]. However, in addition to catching thrown errors, it will also intercept [`Error`][] objects sent as the first argument to the function. @@ -419,7 +419,7 @@ d.on('error', (er) => { * `emitter` {EventEmitter|Timer} emitter or timer to be removed from the domain -The opposite of [`domain.add(emitter)`][]. Removes domain handling from the +The opposite of [`domain.add(emitter)`][]. Removes domain handling from the specified emitter. ### domain.run(fn[, ...args]) diff --git a/doc/api/errors.md b/doc/api/errors.md index 2088fea0de6552..f2de6717a57ee2 100755 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -107,7 +107,7 @@ pass or fail). For *all* `EventEmitter` objects, if an `'error'` event handler is not provided, the error will be thrown, causing the Node.js process to report an -unhandled exception and crash unless either: The [`domain`][domains] module is +unhandled exception and crash unless either: The [`domain`][domains] module is used appropriately or a handler has been registered for the [`process.on('uncaughtException')`][] event. @@ -133,7 +133,7 @@ exactly how errors raised by those methods are propagated. Most asynchronous methods exposed by the Node.js core API follow an idiomatic -pattern referred to as an _error-first callback_ (sometimes referred to as +pattern referred to as an _error-first callback_ (sometimes referred to as a _Node.js style callback_). With this pattern, a callback function is passed to the method as an argument. When the operation either completes or an error is raised, the callback function is called with @@ -156,7 +156,7 @@ fs.readFile('/some/file/that/does-exist', errorFirstCallback); ``` The JavaScript `try / catch` mechanism **cannot** be used to intercept errors -generated by asynchronous APIs. A common mistake for beginners is to try to +generated by asynchronous APIs. A common mistake for beginners is to try to use `throw` inside an error-first callback: ```js @@ -209,7 +209,7 @@ provided text message. If an object is passed as `message`, the text message is generated by calling `message.toString()`. The `error.stack` property will represent the point in the code at which `new Error()` was called. Stack traces are dependent on [V8's stack trace API][]. Stack traces extend only to either -(a) the beginning of *synchronous code execution*, or (b) the number of frames +(a) the beginning of *synchronous code execution*, or (b) the number of frames given by the property `Error.stackTraceLimit`, whichever is smaller. ### Error.captureStackTrace(targetObject[, constructorOpt]) @@ -526,7 +526,7 @@ found [here][online]. - `EACCES` (Permission denied): An attempt was made to access a file in a way forbidden by its file access permissions. -- `EADDRINUSE` (Address already in use): An attempt to bind a server +- `EADDRINUSE` (Address already in use): An attempt to bind a server ([`net`][], [`http`][], or [`https`][]) to a local address failed due to another server on the local system already occupying that address. @@ -554,14 +554,14 @@ found [here][online]. `ulimit -n 2048` in the same shell that will run the Node.js process. - `ENOENT` (No such file or directory): Commonly raised by [`fs`][] operations - to indicate that a component of the specified pathname does not exist -- no + to indicate that a component of the specified pathname does not exist — no entity (file or directory) could be found by the given path. - `ENOTDIR` (Not a directory): A component of the given pathname existed, but was not a directory as expected. Commonly raised by [`fs.readdir`][]. - `ENOTEMPTY` (Directory not empty): A directory with entries was the target - of an operation that requires an empty directory -- usually [`fs.unlink`][]. + of an operation that requires an empty directory — usually [`fs.unlink`][]. - `EPERM` (Operation not permitted): An attempt was made to perform an operation that requires elevated privileges. @@ -573,7 +573,7 @@ found [here][online]. - `ETIMEDOUT` (Operation timed out): A connect or send request failed because the connected party did not properly respond after a period of time. Usually - encountered by [`http`][] or [`net`][] -- often a sign that a `socket.end()` + encountered by [`http`][] or [`net`][] — often a sign that a `socket.end()` was not properly called. diff --git a/doc/api/esm.md b/doc/api/esm.md index b90927c0d57cec..926555fc771484 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -132,12 +132,12 @@ module. This can be one of the following: | `format` | Description | | --- | --- | -| `"esm"` | Load a standard JavaScript module | -| `"commonjs"` | Load a node-style CommonJS module | -| `"builtin"` | Load a node builtin CommonJS module | -| `"json"` | Load a JSON file | -| `"addon"` | Load a [C++ Addon][addons] | -| `"dynamic"` | Use a [dynamic instantiate hook][] | +| `'esm'` | Load a standard JavaScript module | +| `'commonjs'` | Load a node-style CommonJS module | +| `'builtin'` | Load a node builtin CommonJS module | +| `'json'` | Load a JSON file | +| `'addon'` | Load a [C++ Addon][addons] | +| `'dynamic'` | Use a [dynamic instantiate hook][] | For example, a dummy loader to load JavaScript restricted to browser resolution rules with only JS file extension and Node builtin modules support could @@ -191,7 +191,7 @@ would load the module `x.js` as an ES module with relative resolution support To create a custom dynamic module that doesn't correspond to one of the existing `format` interpretations, the `dynamicInstantiate` hook can be used. -This hook is called only for modules that return `format: "dynamic"` from +This hook is called only for modules that return `format: 'dynamic'` from the `resolve` hook. ```js diff --git a/doc/api/fs.md b/doc/api/fs.md index d4ee09bd0b9afe..85add1571691ba 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -6,7 +6,7 @@ -File I/O is provided by simple wrappers around standard POSIX functions. To +File I/O is provided by simple wrappers around standard POSIX functions. To use this module do `require('fs')`. All the methods have asynchronous and synchronous forms. @@ -68,7 +68,7 @@ fs.rename('/tmp/hello', '/tmp/world', (err) => { In busy processes, the programmer is _strongly encouraged_ to use the asynchronous versions of these calls. The synchronous versions will block -the entire process until they complete--halting all connections. +the entire process until they complete — halting all connections. The relative path to a filename can be used. Remember, however, that this path will be relative to `process.cwd()`. @@ -369,16 +369,16 @@ value, will not be reflected in the corresponding alternate representation. The times in the stat object have the following semantics: -* `atime` "Access Time" - Time when file data last accessed. Changed +* `atime` "Access Time" - Time when file data last accessed. Changed by the mknod(2), utimes(2), and read(2) system calls. * `mtime` "Modified Time" - Time when file data last modified. Changed by the mknod(2), utimes(2), and write(2) system calls. * `ctime` "Change Time" - Time when file status was last changed - (inode data modification). Changed by the chmod(2), chown(2), + (inode data modification). Changed by the chmod(2), chown(2), link(2), mknod(2), rename(2), unlink(2), utimes(2), read(2), and write(2) system calls. -* `birthtime` "Birth Time" - Time of file creation. Set once when the - file is created. On filesystems where birthtime is not available, +* `birthtime` "Birth Time" - Time of file creation. Set once when the + file is created. On filesystems where birthtime is not available, this field may instead hold either the `ctime` or `1970-01-01T00:00Z` (ie, unix epoch timestamp `0`). Note that this value may be greater than `atime` or `mtime` in this case. On Darwin @@ -387,7 +387,7 @@ The times in the stat object have the following semantics: utimes(2) system call. Prior to Node v0.12, the `ctime` held the `birthtime` on Windows -systems. Note that as of v0.12, `ctime` is not "creation time", and +systems. Note that as of v0.12, `ctime` is not "creation time", and on Unix systems, it never was. ## Class: fs.WriteStream @@ -862,7 +862,7 @@ changes: * `callback` {Function} * `err` {Error} -Asynchronous close(2). No arguments other than a possible exception are given +Asynchronous close(2). No arguments other than a possible exception are given to the completion callback. ## fs.closeSync(fd) @@ -1009,7 +1009,7 @@ const defaults = { ``` `options` can include `start` and `end` values to read a range of bytes from -the file instead of the entire file. Both `start` and `end` are inclusive and +the file instead of the entire file. Both `start` and `end` are inclusive and start counting at 0. If `fd` is specified and `start` is omitted or `undefined`, `fs.createReadStream()` reads sequentially from the current file position. The `encoding` can be any one of those accepted by [`Buffer`][]. @@ -1079,7 +1079,7 @@ const defaults = { ``` `options` may also include a `start` option to allow writing data at -some position past the beginning of the file. Modifying a file rather +some position past the beginning of the file. Modifying a file rather than replacing it may require a `flags` mode of `r+` rather than the default mode `w`. The `encoding` can be any one of those accepted by [`Buffer`][]. @@ -1115,7 +1115,7 @@ deprecated: v1.0.0 * `exists` {boolean} Test whether or not the given path exists by checking with the file system. -Then call the `callback` argument with either true or false. Example: +Then call the `callback` argument with either true or false. Example: ```js fs.exists('/etc/passwd', (exists) => { @@ -1809,7 +1809,7 @@ to a non-existent file. The exclusive flag may or may not work with network file systems. `flags` can also be a number as documented by open(2); commonly used constants -are available from `fs.constants`. On Windows, flags are translated to +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. @@ -1923,7 +1923,7 @@ changes: * `err` {Error} * `files` {string[]|Buffer[]} -Asynchronous readdir(3). Reads the contents of a directory. +Asynchronous readdir(3). Reads the contents of a directory. The callback gets two arguments `(err, files)` where `files` is an array of the names of the files in the directory excluding `'.'` and `'..'`. @@ -2468,7 +2468,7 @@ Calling `fs.unwatchFile()` with a filename that is not being watched is a no-op, not an error. *Note*: [`fs.watch()`][] is more efficient than `fs.watchFile()` and -`fs.unwatchFile()`. `fs.watch()` should be used instead of `fs.watchFile()` +`fs.unwatchFile()`. `fs.watch()` should be used instead of `fs.watchFile()` and `fs.unwatchFile()` when possible. ## fs.utimes(path, atime, mtime, callback) @@ -2559,12 +2559,12 @@ changes: * `filename` {string|Buffer} Watch for changes on `filename`, where `filename` is either a file or a -directory. The returned object is a [`fs.FSWatcher`][]. +directory. The returned object is a [`fs.FSWatcher`][]. The second argument is optional. If `options` is provided as a string, it specifies the `encoding`. Otherwise `options` should be passed as an object. -The listener callback gets two arguments `(eventType, filename)`. `eventType` is either +The listener callback gets two arguments `(eventType, filename)`. `eventType` is either `'rename'` or `'change'`, and `filename` is the name of the file which triggered the event. @@ -2626,7 +2626,7 @@ content, and one for truncation). Providing `filename` argument in the callback is only supported on Linux, -macOS, Windows, and AIX. Even on supported platforms, `filename` is not always +macOS, Windows, and AIX. Even on supported platforms, `filename` is not always guaranteed to be provided. Therefore, don't assume that `filename` argument is always provided in the callback, and have some fallback logic if it is null. @@ -2775,7 +2775,7 @@ changes: * `written` {integer} * `string` {string} -Write `string` to the file specified by `fd`. If `string` is not a string, then +Write `string` to the file specified by `fd`. If `string` is not a string, then the value will be coerced to one. `position` refers to the offset from the beginning of the file where this data diff --git a/doc/api/http.md b/doc/api/http.md index cdd6bfd8a4d0fb..49083e2193953c 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -9,7 +9,7 @@ To use the HTTP server and client one must `require('http')`. The HTTP interfaces in Node.js are designed to support many features of the protocol which have been traditionally difficult to use. In particular, large, possibly chunk-encoded, messages. The interface is -careful to never buffer entire requests or responses--the +careful to never buffer entire requests or responses — the user is able to stream data. HTTP message headers are represented by an object like this: @@ -33,7 +33,7 @@ parse the actual headers or the body. See [`message.headers`][] for details on how duplicate headers are handled. The raw headers as they were received are retained in the `rawHeaders` -property, which is an array of `[key, value, key2, value2, ...]`. For +property, which is an array of `[key, value, key2, value2, ...]`. For example, the previous message header object might have a `rawHeaders` list like the following: @@ -122,9 +122,9 @@ added: v0.3.4 for TCP Keep-Alive packets. Ignored when the `keepAlive` option is `false` or `undefined`. Defaults to `1000`. * `maxSockets` {number} Maximum number of sockets to allow per - host. Defaults to `Infinity`. + host. Defaults to `Infinity`. * `maxFreeSockets` {number} Maximum number of sockets to leave open - in a free state. Only relevant if `keepAlive` is set to `true`. + in a free state. Only relevant if `keepAlive` is set to `true`. Defaults to `256`. The default [`http.globalAgent`][] that is used by [`http.request()`][] has all @@ -203,9 +203,9 @@ added: v0.11.4 Destroy any sockets that are currently in use by the agent. -It is usually not necessary to do this. However, if using an +It is usually not necessary to do this. However, if using an agent with `keepAlive` enabled, then it is best to explicitly shut down -the agent when it will no longer be used. Otherwise, +the agent when it will no longer be used. Otherwise, sockets may hang open for quite a long time before the server terminates them. @@ -217,7 +217,7 @@ added: v0.11.4 * {Object} An object which contains arrays of sockets currently awaiting use by -the agent when `keepAlive` is enabled. Do not modify. +the agent when `keepAlive` is enabled. Do not modify. ### agent.getName(options) -This object is created internally and returned from [`http.request()`][]. It -represents an _in-progress_ request whose header has already been queued. The +This object is created internally and returned from [`http.request()`][]. It +represents an _in-progress_ request whose header has already been queued. The header is still mutable using the [`setHeader(name, value)`][], - [`getHeader(name)`][], [`removeHeader(name)`][] API. The actual header will + [`getHeader(name)`][], [`removeHeader(name)`][] API. The actual header will be sent along with the first data chunk or when calling [`request.end()`][]. To get the response, add a listener for [`'response'`][] to the request object. [`'response'`][] will be emitted from the request object when the response -headers have been received. The [`'response'`][] event is executed with one +headers have been received. The [`'response'`][] event is executed with one argument which is an instance of [`http.IncomingMessage`][]. During the [`'response'`][] event, one can add listeners to the response object; particularly to listen for the `'data'` event. If no [`'response'`][] handler is added, then the response will be -entirely discarded. However, if a [`'response'`][] event handler is added, +entirely discarded. However, if a [`'response'`][] event handler is added, then the data from the response object **must** be consumed, either by calling `response.read()` whenever there is a `'readable'` event, or by adding a `'data'` handler, or by calling the `.resume()` method. -Until the data is consumed, the `'end'` event will not fire. Also, until +Until the data is consumed, the `'end'` event will not fire. Also, until the data is read it will consume memory that can eventually lead to a 'process out of memory' error. @@ -541,7 +541,7 @@ For efficiency reasons, Node.js normally buffers the request headers until then tries to pack the request headers and data into a single TCP packet. That's usually desired (it saves a TCP round-trip), but not when the first -data is not sent until possibly much later. `request.flushHeaders()` bypasses +data is not sent until possibly much later. `request.flushHeaders()` bypasses the optimization and kickstarts the request. ### request.getHeader(name) @@ -669,9 +669,9 @@ added: v0.1.29 * `encoding` {string} * `callback` {Function} -Sends a chunk of the body. By calling this method +Sends a chunk of the body. By calling this method many times, a request body can be sent to a -server--in that case it is suggested to use the +server — in that case it is suggested to use the `['Transfer-Encoding', 'chunked']` header line when creating the request. @@ -853,7 +853,7 @@ added: v0.1.90 * `callback` {Function} -Stops the server from accepting new connections. See [`net.Server.close()`][]. +Stops the server from accepting new connections. See [`net.Server.close()`][]. ### server.listen() @@ -896,7 +896,7 @@ If there is a `'timeout'` event listener on the Server object, then it will be called with the timed-out socket as an argument. By default, the Server's timeout value is 2 minutes, and sockets are -destroyed automatically if they time out. However, if a callback is assigned +destroyed automatically if they time out. However, if a callback is assigned to the Server's `'timeout'` event, timeouts must be handled explicitly. Returns `server`. @@ -941,7 +941,7 @@ value only affects new connections to the server, not any existing connections. added: v0.1.17 --> -This object is created internally by an HTTP server--not by the user. It is +This object is created internally by an HTTP server — not by the user. It is passed as the second parameter to the [`'request'`][] event. The response implements, but does not inherit from, the [Writable Stream][] @@ -1161,8 +1161,8 @@ added: v0.4.0 * `name` {string} * `value` {string | string[]} -Sets a single header value for implicit headers. If this header already exists -in the to-be-sent headers, its value will be replaced. Use an array of strings +Sets a single header value for implicit headers. If this header already exists +in the to-be-sent headers, its value will be replaced. Use an array of strings here to send multiple headers with the same name. Example: @@ -1202,12 +1202,12 @@ added: v0.9.12 * `msecs` {number} * `callback` {Function} -Sets the Socket's timeout value to `msecs`. If a callback is +Sets the Socket's timeout value to `msecs`. If a callback is provided, then it is added as a listener on the `'timeout'` event on the response object. If no `'timeout'` listener is added to the request, the response, or -the server, then sockets are destroyed when they time out. If a handler is +the server, then sockets are destroyed when they time out. If a handler is assigned to the request, the response, or the server's `'timeout'` events, timed out sockets must be handled explicitly. @@ -1487,8 +1487,8 @@ added: v0.11.6 The raw request/response headers list exactly as they were received. -Note that the keys and values are in the same list. It is *not* a -list of tuples. So, the even-numbered offsets are key values, and the +Note that the keys and values are in the same list. It is *not* a +list of tuples. So, the even-numbered offsets are key values, and the odd-numbered offsets are the associated values. Header names are not lowercased, and duplicates are not merged. @@ -1515,7 +1515,7 @@ added: v0.11.6 * {Array} The raw request/response trailer keys and values exactly as they were -received. Only populated at the `'end'` event. +received. Only populated at the `'end'` event. ### message.setTimeout(msecs, callback) * `settings` {[Settings Object][]} -* Returns {undefined} +* Returns: {undefined} Updates the current local settings for this `Http2Session` and sends a new `SETTINGS` frame to the connected HTTP/2 peer. @@ -1282,7 +1282,7 @@ added: v8.4.0 * Extends: {net.Server} In `Http2Server`, there is no `'clientError'` event as there is in -HTTP1. However, there are `'socketError'`, `'sessionError'`, and +HTTP1. However, there are `'socketError'`, `'sessionError'`, and `'streamError'`, for error happened on the socket, session, or stream respectively. @@ -1609,7 +1609,7 @@ changes: * ...: Any [`tls.createServer()`][] options can be provided. For servers, the identity options (`pfx` or `key`/`cert`) are usually required. * `onRequestHandler` {Function} See [Compatibility API][] -* Returns {Http2SecureServer} +* Returns: {Http2SecureServer} Returns a `tls.Server` instance that creates and manages `Http2Session` instances. @@ -1691,7 +1691,7 @@ changes: [`Duplex`][] stream that is to be used as the connection for this session. * ...: Any [`net.connect()`][] or [`tls.connect()`][] options can be provided. * `listener` {Function} -* Returns {Http2Session} +* Returns: {Http2Session} Returns a HTTP/2 client `Http2Session` instance. @@ -2191,8 +2191,8 @@ added: v8.4.0 The raw request/response headers list exactly as they were received. -Note that the keys and values are in the same list. It is *not* a -list of tuples. So, the even-numbered offsets are key values, and the +Note that the keys and values are in the same list. It is *not* a +list of tuples. So, the even-numbered offsets are key values, and the odd-numbered offsets are the associated values. Header names are not lowercased, and duplicates are not merged. @@ -2219,7 +2219,7 @@ added: v8.4.0 * {Array} The raw request/response trailer keys and values exactly as they were -received. Only populated at the `'end'` event. +received. Only populated at the `'end'` event. #### request.setTimeout(msecs, callback) -This object is created internally by an HTTP server--not by the user. It is +This object is created internally by an HTTP server — not by the user. It is passed as the second parameter to the [`'request'`][] event. The response implements, but does not inherit from, the [Writable Stream][] @@ -2560,8 +2560,8 @@ added: v8.4.0 * `name` {string} * `value` {string|string[]} -Sets a single header value for implicit headers. If this header already exists -in the to-be-sent headers, its value will be replaced. Use an array of strings +Sets a single header value for implicit headers. If this header already exists +in the to-be-sent headers, its value will be replaced. Use an array of strings here to send multiple headers with the same name. Example: @@ -2601,7 +2601,7 @@ added: v8.4.0 * `msecs` {number} * `callback` {Function} -Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is +Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is provided, then it is added as a listener on the `'timeout'` event on the response object. @@ -2760,7 +2760,7 @@ response.writeHead(200, { ``` Note that Content-Length is given in bytes not characters. The -`Buffer.byteLength()` API may be used to determine the number of bytes in a +`Buffer.byteLength()` API may be used to determine the number of bytes in a given encoding. On outbound messages, Node.js does not check if Content-Length and the length of the body being transmitted are equal or not. However, when receiving messages, Node.js will automatically reject messages when the diff --git a/doc/api/https.md b/doc/api/https.md index 4740986170b39a..daf10ac4a2bb94 100644 --- a/doc/api/https.md +++ b/doc/api/https.md @@ -12,7 +12,7 @@ separate module. added: v0.4.5 --> -An Agent object for HTTPS similar to [`http.Agent`][]. See [`https.request()`][] +An Agent object for HTTPS similar to [`http.Agent`][]. See [`https.request()`][] for more information. ## Class: https.Server diff --git a/doc/api/modules.md b/doc/api/modules.md index 07c469c11408ad..6bac6a8b23144b 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -123,12 +123,12 @@ the version that is symlinked into Furthermore, to make the module lookup process even more optimal, rather than putting packages directly in `/usr/lib/node`, we could put them in -`/usr/lib/node_modules//`. Then Node.js will not bother +`/usr/lib/node_modules//`. Then Node.js will not bother looking for missing dependencies in `/usr/node_modules` or `/node_modules`. In order to make modules available to the Node.js REPL, it might be useful to also add the `/usr/lib/node_modules` folder to the `$NODE_PATH` environment -variable. Since the module lookups using `node_modules` folders are all +variable. Since the module lookups using `node_modules` folders are all relative, and based on the real path of the files making the calls to `require()`, the packages themselves can be anywhere. @@ -196,12 +196,12 @@ NODE_MODULES_PATHS(START) -Modules are cached after the first time they are loaded. This means +Modules are cached after the first time they are loaded. This means (among other things) that every call to `require('foo')` will get exactly the same object returned, if it would resolve to the same file. Multiple calls to `require('foo')` may not cause the module code to be -executed multiple times. This is an important feature. With it, +executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles. @@ -212,7 +212,7 @@ that function. -Modules are cached based on their resolved filename. Since modules may +Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from `node_modules` folders), it is not a *guarantee* that `require('foo')` will always return the exact same object, if it @@ -228,14 +228,14 @@ irrespective of whether or not `./foo` and `./FOO` are the same file. -Node.js has several modules compiled into the binary. These modules are +Node.js has several modules compiled into the binary. These modules are described in greater detail elsewhere in this documentation. The core modules are defined within Node.js's source and are located in the `lib/` folder. Core modules are always preferentially loaded if their identifier is -passed to `require()`. For instance, `require('http')` will always +passed to `require()`. For instance, `require('http')` will always return the built in HTTP module, even if there is a file by that name. ## Cycles @@ -275,13 +275,13 @@ console.log('b done'); console.log('main starting'); const a = require('./a.js'); const b = require('./b.js'); -console.log('in main, a.done=%j, b.done=%j', a.done, b.done); +console.log('in main, a.done = %j, b.done = %j', a.done, b.done); ``` -When `main.js` loads `a.js`, then `a.js` in turn loads `b.js`. At that -point, `b.js` tries to load `a.js`. In order to prevent an infinite +When `main.js` loads `a.js`, then `a.js` in turn loads `b.js`. At that +point, `b.js` tries to load `a.js`. In order to prevent an infinite loop, an **unfinished copy** of the `a.js` exports object is returned to the -`b.js` module. `b.js` then finishes loading, and its `exports` object is +`b.js` module. `b.js` then finishes loading, and its `exports` object is provided to the `a.js` module. By the time `main.js` has loaded both modules, they're both finished. @@ -296,7 +296,7 @@ in b, a.done = false b done in a, b.done = true a done -in main, a.done=true, b.done=true +in main, a.done = true, b.done = true ``` Careful planning is required to allow cyclic module dependencies to work @@ -314,7 +314,7 @@ required filename with the added extensions: `.js`, `.json`, and finally parsed as JSON text files. `.node` files are interpreted as compiled addon modules loaded with `dlopen`. -A required module prefixed with `'/'` is an absolute path to the file. For +A required module prefixed with `'/'` is an absolute path to the file. For example, `require('/home/marco/foo.js')` will load the file at `/home/marco/foo.js`. @@ -338,7 +338,7 @@ There are three ways in which a folder may be passed to `require()` as an argument. The first is to create a `package.json` file in the root of the folder, -which specifies a `main` module. An example package.json file might +which specifies a `main` module. An example package.json file might look like this: ```json @@ -352,7 +352,7 @@ If this was in a folder at `./some-library`, then This is the extent of Node.js's awareness of package.json files. -*Note*: If the file specified by the `"main"` entry of `package.json` is +*Note*: If the file specified by the `'main'` entry of `package.json` is missing and can not be resolved, Node.js will report the entire module as missing with the default error: @@ -362,7 +362,7 @@ Error: Cannot find module 'some-library' If there is no package.json file present in the directory, then Node.js will attempt to load an `index.js` or `index.node` file out of that -directory. For example, if there was no package.json file in the above +directory. For example, if there was no package.json file in the above example, then `require('./some-library')` would attempt to load: * `./some-library/index.js` @@ -415,7 +415,7 @@ varying paths before the current [module resolution][] algorithm was frozen. `NODE_PATH` is still supported, but is less necessary now that the Node.js ecosystem has settled on a convention for locating dependent modules. Sometimes deployments that rely on `NODE_PATH` show surprising behavior -when people are unaware that `NODE_PATH` must be set. Sometimes a +when people are unaware that `NODE_PATH` must be set. Sometimes a module's dependencies change, causing a different version (or even a different module) to be loaded as the `NODE_PATH` is searched. @@ -583,14 +583,14 @@ Process files with the extension `.sjs` as `.js`: require.extensions['.sjs'] = require.extensions['.js']; ``` -**Deprecated** In the past, this list has been used to load +**Deprecated** In the past, this list has been used to load non-JavaScript modules into Node.js by compiling them on-demand. However, in practice, there are much better ways to do this, such as loading modules via some other Node.js program, or compiling them to JavaScript ahead of time. Since the module system is locked, this feature will probably never go -away. However, it may have subtle bugs and complexities that are best +away. However, it may have subtle bugs and complexities that are best left untouched. Note that the number of file system operations that the module system @@ -643,7 +643,7 @@ added: v0.1.16 * {Object} In each module, the `module` free variable is a reference to the object -representing the current module. For convenience, `module.exports` is +representing the current module. For convenience, `module.exports` is also accessible via the `exports` module-global. `module` is not actually a global but rather local to each module. @@ -694,7 +694,7 @@ a.on('ready', () => { Note that assignment to `module.exports` must be done immediately. It cannot be -done in any callbacks. This does not work: +done in any callbacks. This does not work: x.js: @@ -774,7 +774,7 @@ added: v0.1.16 * {string} -The identifier for the module. Typically this is the fully resolved +The identifier for the module. Typically this is the fully resolved filename. ### module.loaded @@ -817,7 +817,7 @@ The `module.require` method provides a way to load a module as if `require()` was called from the original module. *Note*: In order to do this, it is necessary to get a reference to the -`module` object. Since `require()` returns the `module.exports`, and the +`module` object. Since `require()` returns the `module.exports`, and the `module` is typically *only* available within a specific module's code, it must be explicitly exported in order to be used. @@ -830,7 +830,7 @@ added: v0.3.7 * {Object} Provides general utility methods when interacting with instances of -`Module` -- the `module` variable often seen in file modules. Accessed +`Module` — the `module` variable often seen in file modules. Accessed via `require('module')`. ### module.builtinModules @@ -840,7 +840,7 @@ added: v8.10.0 * {string[]} -A list of the names of all modules provided by Node.js. Can be used to verify +A list of the names of all modules provided by Node.js. Can be used to verify if a module is maintained by a third-party module or not. [`__dirname`]: #modules_dirname diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 689536c227aafd..f9c24fe4a0e2b1 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -14,7 +14,7 @@ compiled for one version to run on later versions of Node.js without recompilation. Addons are built/packaged with the same approach/tools -outlined in the section titled [C++ Addons](addons.html). +outlined in the section titled [C++ Addons](addons.html). The only difference is the set of APIs that are used by the native code. Instead of using the V8 or [Native Abstractions for Node.js][] APIs, the functions available in the N-API are used. @@ -308,7 +308,7 @@ where the native code can catch the exception, take the appropriate action, and then continue. This is only recommended in specific cases where it is known that the exception can be safely handled. In these cases [`napi_get_and_clear_last_exception`][] can be used to get and -clear the exception. On success, result will contain the handle to +clear the exception. On success, result will contain the handle to the last JavaScript Object thrown. If it is determined, after retrieving the exception, the exception cannot be handled after all it can be re-thrown it with [`napi_throw`][] where error is the @@ -316,7 +316,7 @@ JavaScript Error object to be thrown. The following utility functions are also available in case native code needs to throw an exception or determine if a `napi_value` is an instance -of a JavaScript `Error` object: [`napi_throw_error`][], +of a JavaScript `Error` object: [`napi_throw_error`][], [`napi_throw_type_error`][], [`napi_throw_range_error`][] and [`napi_is_error`][]. @@ -327,7 +327,7 @@ where result is the napi_value that refers to the newly created JavaScript Error object. The Node.js project is adding error codes to all of the errors -generated internally. The goal is for applications to use these +generated internally. The goal is for applications to use these error codes for all error checking. The associated error messages will remain, but will only be meant to be used for logging and display with the expectation that the message can change without @@ -335,7 +335,7 @@ SemVer applying. In order to support this model with N-API, both in internal functionality and for module specific functionality (as its good practice), the `throw_` and `create_` functions take an optional code parameter which is the string for the code -to be added to the error object. If the optional parameter is NULL +to be added to the error object. If the optional parameter is NULL then no code will be associated with the error. If a code is provided, the name associated with the error is also updated to be: @@ -344,7 +344,7 @@ originalName [code] ``` where originalName is the original name associated with the error -and code is the code that was provided. For example if the code +and code is the code that was provided. For example if the code is 'ERR_ERROR_1' and a TypeError is being created the name will be: ```text @@ -2398,7 +2398,7 @@ They can be one or more of the following bitflags: - `napi_default` - Used to indicate that no explicit attributes are set on the given property. By default, a property is read only, not enumerable and not configurable. -- `napi_writable` - Used to indicate that a given property is writable. +- `napi_writable` - Used to indicate that a given property is writable. - `napi_enumerable` - Used to indicate that a given property is enumerable. - `napi_configurable` - Used to indicate that a given property is configurable, as defined in @@ -2430,7 +2430,7 @@ typedef struct { encoded as UTF8. One of `utf8name` or `name` must be provided for the property. - `name`: Optional napi_value that points to a JavaScript string or symbol -to be used as the key for the property. One of `utf8name` or `name` must +to be used as the key for the property. One of `utf8name` or `name` must be provided for the property. - `value`: The value that's retrieved by a get access of the property if the property is a data property. If this is passed in, set `getter`, `setter`, @@ -2883,7 +2883,7 @@ napi_value Init(napi_env env, napi_value exports) { napi_status status; napi_value fn; - status = napi_create_function(env, nullptr, 0, SayHello, nullptr, &fn); + status = napi_create_function(env, nullptr, 0, SayHello, nullptr, &fn); if (status != napi_ok) return nullptr; status = napi_set_named_property(env, exports, "sayHello", fn); @@ -3254,7 +3254,7 @@ napi_status napi_queue_async_work(napi_env env, napi_async_work work); ``` -[`napi_cancel_async_work`][] can be used if the work needs +[`napi_cancel_async_work`][] can be used if the work needs to be cancelled before the work has started execution. After calling [`napi_cancel_async_work`][], the `complete` callback @@ -3356,7 +3356,7 @@ napi_status napi_cancel_async_work(napi_env env, Returns `napi_ok` if the API succeeded. This API cancels queued work if it has not yet -been started. If it has already started executing, it cannot be +been started. If it has already started executing, it cannot be cancelled and `napi_generic_failure` will be returned. If successful, the `complete` callback will be invoked with a status value of `napi_cancelled`. The work should not be deleted before the `complete` @@ -3494,7 +3494,7 @@ napi_status napi_get_version(napi_env env, Returns `napi_ok` if the API succeeded. This API returns the highest N-API version supported by the -Node.js runtime. N-API is planned to be additive such that +Node.js runtime. N-API is planned to be additive such that newer releases of Node.js may support additional API functions. In order to allow an addon to use a newer function when running with versions of Node.js that support it, while providing diff --git a/doc/api/net.md b/doc/api/net.md index 31e0ca7a596fa7..f281d480304862 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -185,8 +185,8 @@ Possible signatures: * [`server.listen([port][, host][, backlog][, callback])`][`server.listen(port, host)`] for TCP servers -This function is asynchronous. When the server starts listening, the -[`'listening'`][] event will be emitted. The last parameter `callback` +This function is asynchronous. When the server starts listening, the +[`'listening'`][] event will be emitted. The last parameter `callback` will be added as a listener for the [`'listening'`][] event. All `listen()` methods can take a `backlog` parameter to specify the maximum @@ -420,8 +420,8 @@ added: v0.1.90 * {Buffer} -Emitted when data is received. The argument `data` will be a `Buffer` or -`String`. Encoding of data is set by `socket.setEncoding()`. +Emitted when data is received. The argument `data` will be a `Buffer` or +`String`. Encoding of data is set by `socket.setEncoding()`. (See the [Readable Stream][] section for more information.) Note that the **data will be lost** if there is no listener when a `Socket` @@ -459,7 +459,7 @@ added: v0.1.90 * {Error} -Emitted when an error occurs. The `'close'` event will be called directly +Emitted when an error occurs. The `'close'` event will be called directly following this event. ### Event: 'lookup' @@ -474,9 +474,9 @@ changes: Emitted after resolving the hostname but before connecting. Not applicable to UNIX sockets. -* `err` {Error|null} The error object. See [`dns.lookup()`][]. +* `err` {Error|null} The error object. See [`dns.lookup()`][]. * `address` {string} The IP address. -* `family` {string|null} The address type. See [`dns.lookup()`][]. +* `family` {string|null} The address type. See [`dns.lookup()`][]. * `host` {string} The hostname. ### Event: 'timeout' @@ -822,7 +822,7 @@ added: v0.1.90 --> Sends data on the socket. The second parameter specifies the encoding in the -case of a string--it defaults to UTF8 encoding. +case of a string — it defaults to UTF8 encoding. Returns `true` if the entire data was flushed successfully to the kernel buffer. Returns `false` if all or part of the data was queued in user memory. diff --git a/doc/api/os.md b/doc/api/os.md index 306a11a3b8dd82..f46292b81386ec 100644 --- a/doc/api/os.md +++ b/doc/api/os.md @@ -225,7 +225,7 @@ The `os.loadavg()` method returns an array containing the 1, 5, and 15 minute load averages. The load average is a measure of system activity, calculated by the operating -system and expressed as a fractional number. As a rule of thumb, the load +system and expressed as a fractional number. As a rule of thumb, the load average should ideally be less than the number of logical CPUs in the system. The load average is a UNIX-specific concept with no real equivalent on @@ -404,7 +404,7 @@ added: v6.0.0 * Returns: {Object} The `os.userInfo()` method returns information about the currently effective -user -- on POSIX platforms, this is typically a subset of the password file. The +user — on POSIX platforms, this is typically a subset of the password file. The returned object includes the `username`, `uid`, `gid`, `shell`, and `homedir`. On Windows, the `uid` and `gid` fields are `-1`, and `shell` is `null`. diff --git a/doc/api/path.md b/doc/api/path.md index f2015db47048d7..ab6039206fbd91 100644 --- a/doc/api/path.md +++ b/doc/api/path.md @@ -163,7 +163,7 @@ changes: The `path.extname()` method returns the extension of the `path`, from the last occurrence of the `.` (period) character to end of string in the last portion of -the `path`. If there is no `.` in the last portion of the `path`, or if the +the `path`. If there is no `.` in the last portion of the `path`, or if the first character of the basename of `path` (see `path.basename()`) is `.`, then an empty string is returned. @@ -396,7 +396,7 @@ path.parse('/home/user/dir/file.txt'); │ root │ │ name │ ext │ " / home/user/dir / file .txt " └──────┴──────────────┴──────┴─────┘ -(all spaces in the "" line should be ignored -- they are purely for formatting) +(all spaces in the "" line should be ignored — they are purely for formatting) ``` On Windows: @@ -418,7 +418,7 @@ path.parse('C:\\path\\dir\\file.txt'); │ root │ │ name │ ext │ " C:\ path\dir \ file .txt " └──────┴──────────────┴──────┴─────┘ -(all spaces in the "" line should be ignored -- they are purely for formatting) +(all spaces in the "" line should be ignored — they are purely for formatting) ``` A [`TypeError`][] is thrown if `path` is not a string. diff --git a/doc/api/process.md b/doc/api/process.md index 0fc006067137de..574fc933671d14 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -191,7 +191,7 @@ process will exit with a non-zero exit code and the stack trace will be printed. This is to avoid infinite recursion. Attempting to resume normally after an uncaught exception can be similar to -pulling out of the power cord when upgrading a computer -- nine out of ten +pulling out of the power cord when upgrading a computer — nine out of ten times nothing happens - but the 10th time, the system becomes corrupted. The correct use of `'uncaughtException'` is to perform synchronous cleanup @@ -359,7 +359,7 @@ For example: process.stdin.resume(); process.on('SIGINT', () => { - console.log('Received SIGINT. Press Control-D to exit.'); + console.log('Received SIGINT. Press Control-D to exit.'); }); ``` @@ -436,7 +436,7 @@ added: v0.1.27 The `process.argv` property returns an array containing the command line arguments passed when the Node.js process was launched. The first element will be [`process.execPath`]. See `process.argv0` if access to the original value of -`argv[0]` is needed. The second element will be the path to the JavaScript +`argv[0]` is needed. The second element will be the path to the JavaScript file being executed. The remaining elements will be any additional command line arguments. @@ -920,7 +920,7 @@ added: v0.1.13 The `process.exit()` method instructs Node.js to terminate the process synchronously with an exit status of `code`. If `code` is omitted, exit uses either the 'success' code `0` or the value of `process.exitCode` if it has been -set. Node.js will not terminate until all the [`'exit'`] event listeners are +set. Node.js will not terminate until all the [`'exit'`] event listeners are called. To exit with a 'failure' code: @@ -1163,7 +1163,7 @@ Windows platforms will throw an error if the `pid` is used to kill a process group. *Note*: Even though the name of this function is `process.kill()`, it is -really just a signal sender, like the `kill` system call. The signal sent may +really just a signal sender, like the `kill` system call. The signal sent may do something other than kill the target process. For example: @@ -1261,7 +1261,7 @@ Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called. This is *not* a simple alias to [`setTimeout(fn, 0)`][]. It is much more -efficient. It runs before any additional I/O events (including +efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop. ```js @@ -1296,7 +1296,7 @@ thing.getReadyForStuff(); ``` It is very important for APIs to be either 100% synchronous or 100% -asynchronous. Consider this example: +asynchronous. Consider this example: ```js // WARNING! DO NOT USE! BAD UNSAFE HAZARD! @@ -1338,7 +1338,7 @@ function definitelyAsync(arg, cb) { ``` *Note*: The next tick queue is completely drained on each pass of the -event loop **before** additional I/O is processed. As a result, +event loop **before** additional I/O is processed. As a result, recursively setting nextTick callbacks will block any I/O from happening, just like a `while(true);` loop. @@ -1438,7 +1438,7 @@ tarball. builds of Node.js and will be missing on all other platforms._ * `lts` {string} a string label identifying the [LTS][] label for this release. This property only exists for LTS releases and is `undefined` for all other - release types, including _Current_ releases. Currently the valid values are: + release types, including _Current_ releases. Currently the valid values are: - `'Argon'` for the 4.x LTS line beginning with 4.2.0. - `'Boron'` for the 6.x LTS line beginning with 6.9.0. - `'Carbon'` for the 8.x LTS line beginning with 8.9.1. @@ -1518,7 +1518,7 @@ added: v2.0.0 The `process.seteuid()` method sets the effective user identity of the process. (See seteuid(2).) The `id` can be passed as either a numeric ID or a username -string. If a username is specified, the method blocks while resolving the +string. If a username is specified, the method blocks while resolving the associated numeric ID. ```js @@ -1544,7 +1544,7 @@ added: v0.1.31 * `id` {string|number} The group name or ID The `process.setgid()` method sets the group identity of the process. (See -setgid(2).) The `id` can be passed as either a numeric ID or a group name +setgid(2).) The `id` can be passed as either a numeric ID or a group name string. If a group name is specified, this method blocks while resolving the associated numeric ID. @@ -1585,7 +1585,7 @@ added: v0.1.28 --> The `process.setuid(id)` method sets the user identity of the process. (See -setuid(2).) The `id` can be passed as either a numeric ID or a username string. +setuid(2).) The `id` can be passed as either a numeric ID or a username string. If a username is specified, the method blocks while resolving the associated numeric ID. @@ -1747,7 +1747,7 @@ different maximum length restrictions on the title. Usually such restrictions are quite limited. For instance, on Linux and macOS, `process.title` is limited to the size of the binary name plus the length of the command line arguments because setting the `process.title` overwrites the `argv` memory of the -process. Node.js v0.8 allowed for longer process title strings by also +process. Node.js v0.8 allowed for longer process title strings by also overwriting the `environ` memory but that was potentially insecure and confusing in some (rather obscure) cases. @@ -1854,7 +1854,7 @@ Will generate an object similar to: ## Exit Codes Node.js will normally exit with a `0` status code when no more async -operations are pending. The following status codes are used in other +operations are pending. The following status codes are used in other cases: * `1` **Uncaught Fatal Exception** - There was an uncaught exception, @@ -1862,12 +1862,12 @@ cases: handler. * `2` - Unused (reserved by Bash for builtin misuse) * `3` **Internal JavaScript Parse Error** - The JavaScript source code - internal in Node.js's bootstrapping process caused a parse error. This + internal in Node.js's bootstrapping process caused a parse error. This is extremely rare, and generally can only happen during development of Node.js itself. * `4` **Internal JavaScript Evaluation Failure** - The JavaScript source code internal in Node.js's bootstrapping process failed to - return a function value when evaluated. This is extremely rare, and + return a function value when evaluated. This is extremely rare, and generally can only happen during development of Node.js itself. * `5` **Fatal Error** - There was a fatal unrecoverable error in V8. Typically a message will be printed to stderr with the prefix `FATAL @@ -1877,22 +1877,22 @@ cases: function was somehow set to a non-function, and could not be called. * `7` **Internal Exception Handler Run-Time Failure** - There was an uncaught exception, and the internal fatal exception handler - function itself threw an error while attempting to handle it. This + function itself threw an error while attempting to handle it. This can happen, for example, if a [`'uncaughtException'`][] or `domain.on('error')` handler throws an error. -* `8` - Unused. In previous versions of Node.js, exit code 8 sometimes +* `8` - Unused. In previous versions of Node.js, exit code 8 sometimes indicated an uncaught exception. * `9` - **Invalid Argument** - Either an unknown option was specified, or an option requiring a value was provided without a value. * `10` **Internal JavaScript Run-Time Failure** - The JavaScript source code internal in Node.js's bootstrapping process threw an error - when the bootstrapping function was called. This is extremely rare, + when the bootstrapping function was called. This is extremely rare, and generally can only happen during development of Node.js itself. * `12` **Invalid Debug Argument** - The `--inspect` and/or `--inspect-brk` options were set, but the port number chosen was invalid or unavailable. * `>128` **Signal Exits** - If Node.js receives a fatal signal such as `SIGKILL` or `SIGHUP`, then its exit code will be `128` plus the - value of the signal code. This is a standard POSIX practice, since + value of the signal code. This is a standard POSIX practice, since exit codes are defined to be 7-bit integers, and signal exits set the high-order bit, and then contain the value of the signal code. diff --git a/doc/api/readline.md b/doc/api/readline.md index 42d07da2d0e418..78571d7e993ef8 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -295,7 +295,7 @@ added: v0.1.98 * `shift` {boolean} `true` to indicate the `` key. * `name` {string} The name of the a key. -The `rl.write()` method will write either `data` or a key sequence identified +The `rl.write()` method will write either `data` or a key sequence identified by `key` to the `output`. The `key` argument is supported only if `output` is a [TTY][] text terminal. diff --git a/doc/api/repl.md b/doc/api/repl.md index 7a54928e187ae2..1b6013cf4ca4ed 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -96,7 +96,7 @@ are declared at the global scope. The default evaluator provides access to any variables that exist in the global scope. It is possible to expose a variable to the REPL explicitly by assigning -it to the `context` object associated with each `REPLServer`. For example: +it to the `context` object associated with each `REPLServer`. For example: ```js const repl = require('repl'); @@ -398,7 +398,7 @@ changes: stream upon instantiation. * `eval` {Function} The function to be used when evaluating each given line of input. Defaults to an async wrapper for the JavaScript `eval()` - function. An `eval` function can error with `repl.Recoverable` to indicate + function. An `eval` function can error with `repl.Recoverable` to indicate the input was incomplete and prompt for additional lines. * `useColors` {boolean} If `true`, specifies that the default `writer` function should include ANSI color styling to REPL output. If a custom @@ -467,7 +467,7 @@ environment variables: - `NODE_REPL_HISTORY` - When a valid path is given, persistent REPL history will be saved to the specified file rather than `.node_repl_history` in the - user's home directory. Setting this value to `""` will disable persistent + user's home directory. Setting this value to `''` will disable persistent REPL history. Whitespace will be trimmed from the value. - `NODE_REPL_HISTORY_SIZE` - Defaults to `1000`. Controls how many lines of history will be persisted if history is available. Must be a positive number. diff --git a/doc/api/stream.md b/doc/api/stream.md index 74173abe34f18c..aed4a1fd90e1d9 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -901,7 +901,7 @@ added: v0.9.4 --> * `size` {number} Optional argument to specify how much data to read. -* Return {string|Buffer|null} +* Returns: {string|Buffer|null} The `readable.read()` method pulls some data out of the internal buffer and returns it. If no data available to be read, `null` is returned. By default, @@ -1482,7 +1482,7 @@ It is recommended that errors occurring during the processing of the the callback and passing the error as the first argument. This will cause an `'error'` event to be emitted by the Writable. Throwing an Error from within `writable._write()` can result in unexpected and inconsistent behavior depending -on how the stream is being used. Using the callback ensures consistent and +on how the stream is being used. Using the callback ensures consistent and predictable handling of errors. ```js @@ -1684,7 +1684,7 @@ changes: read queue. For streams not operating in object mode, `chunk` must be a string, `Buffer` or `Uint8Array`. For object mode streams, `chunk` may be any JavaScript value. -* `encoding` {string} Encoding of string chunks. Must be a valid +* `encoding` {string} Encoding of string chunks. Must be a valid Buffer encoding, such as `'utf8'` or `'ascii'` * Returns: {boolean} `true` if additional chunks of data may continued to be pushed; `false` otherwise. @@ -2117,7 +2117,7 @@ The `transform._transform()` method is prefixed with an underscore because it is internal to the class that defines it, and should never be called directly by user programs. -`transform._transform()` is never called in parallel; streams implement a +`transform._transform()` is never called in parallel; streams implement a queue mechanism, and to receive the next chunk, `callback` must be called, either synchronously or asynchronously. diff --git a/doc/api/tls.md b/doc/api/tls.md index 8c82250b2182b3..e60e3fb8364b82 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -374,7 +374,7 @@ added: v0.6.0 --> Returns the bound address, the address family name, and port of the -server as reported by the operating system. See [`net.Server.address()`][] for +server as reported by the operating system. See [`net.Server.address()`][] for more information. ### server.close([callback]) @@ -462,7 +462,7 @@ changes: * `options` {Object} * `isServer`: The SSL/TLS protocol is asymmetrical, TLSSockets must know if they are to behave as a server or a client. If `true` the TLS socket will be - instantiated as a server. Defaults to `false`. + instantiated as a server. Defaults to `false`. * `server` {net.Server} An optional [`net.Server`][] instance. * `requestCert`: Whether to authenticate the remote peer by requesting a certificate. Clients always request a server certificate. Servers @@ -620,7 +620,7 @@ For example: { ... another certificate, possibly with a .issuerCertificate ... }, raw: < RAW DER buffer >, valid_from: 'Nov 11 09:52:22 2009 GMT', - valid_to: 'Nov 6 09:52:22 2029 GMT', + valid_to: 'Nov 6 09:52:22 2029 GMT', fingerprint: '2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF', serialNumber: 'B9B0D332A1AA5635' } ``` @@ -822,7 +822,7 @@ changes: rather than creating a new socket. Typically, this is an instance of [`net.Socket`][], but any `Duplex` stream is allowed. If this option is specified, `path`, `host` and `port` are ignored, - except for certificate validation. Usually, a socket is already connected + except for certificate validation. Usually, a socket is already connected when passed to `tls.connect()`, but it can be connected later. Note that connection/disconnection/destruction of `socket` is the user's responsibility, calling `tls.connect()` will not cause `net.connect()` to be @@ -983,7 +983,7 @@ changes: decrypted with `object.passphrase` if provided, or `options.passphrase` if it is not. * `key` {string|string[]|Buffer|Buffer[]|Object[]} Optional private keys in PEM format. PEM allows the option of private keys being encrypted. Encrypted - keys will be decrypted with `options.passphrase`. Multiple keys using + keys will be decrypted with `options.passphrase`. Multiple keys using different algorithms can be provided either as an array of unencrypted key strings or buffers, or an array of objects in the form `{pem: [, passphrase: ]}`. The object form can only occur in @@ -996,7 +996,7 @@ changes: consist of the PEM formatted certificate for a provided private `key`, followed by the PEM formatted intermediate certificates (if any), in order, and not including the root CA (the root CA must be pre-known to the peer, - see `ca`). When providing multiple cert chains, they do not have to be in + see `ca`). When providing multiple cert chains, they do not have to be in the same order as their private keys in `key`. If the intermediate certificates are not provided, the peer will not be able to validate the certificate, and the handshake will fail. @@ -1006,7 +1006,7 @@ changes: using this option. The value can be a string or Buffer, or an Array of strings and/or Buffers. Any string or Buffer can contain multiple PEM CAs concatenated together. The peer's certificate must be chainable to a CA - trusted by the server for the connection to be authenticated. When using + trusted by the server for the connection to be authenticated. When using certificates that are not chainable to a well-known CA, the certificate's CA must be explicitly specified as a trusted or the connection will fail to authenticate. @@ -1018,7 +1018,7 @@ changes: * `crl` {string|string[]|Buffer|Buffer[]} Optional PEM formatted CRLs (Certificate Revocation Lists). * `ciphers` {string} Optional cipher suite specification, replacing the - default. For more information, see [modifying the default cipher suite][]. + default. For more information, see [modifying the default cipher suite][]. * `honorCipherOrder` {boolean} Attempt to use the server's cipher suite preferences instead of the client's. When `true`, causes `SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see @@ -1037,8 +1037,8 @@ changes: for stronger security. If omitted or invalid, the parameters are silently discarded and DHE ciphers will not be available. * `secureProtocol` {string} Optional SSL method to use, default is - `"SSLv23_method"`. The possible values are listed as [SSL_METHODS][], use - the function names as strings. For example, `"SSLv3_method"` to force SSL + `'SSLv23_method'`. The possible values are listed as [SSL_METHODS][], use + the function names as strings. For example, `'SSLv3_method'` to force SSL version 3. * `secureOptions` {number} Optionally affect the OpenSSL protocol behavior, which is not usually necessary. This should be used carefully if at all! @@ -1124,7 +1124,7 @@ changes: servers, the identity options (`pfx` or `key`/`cert`) are usually required. * `secureConnectionListener` {Function} -Creates a new [tls.Server][]. The `secureConnectionListener`, if provided, is +Creates a new [tls.Server][]. The `secureConnectionListener`, if provided, is automatically set as a listener for the [`'secureConnection'`][] event. *Note*: The `ticketKeys` options is automatically shared between `cluster` diff --git a/doc/api/url.md b/doc/api/url.md index 00211095627a46..2da532462543b0 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -51,7 +51,7 @@ properties of a WHATWG `URL` object. ├─────────────┴─────────────────────┴─────────────────────┴──────────┴────────────────┴───────┤ │ href │ └─────────────────────────────────────────────────────────────────────────────────────────────┘ -(all spaces in the "" line should be ignored -- they are purely for formatting) +(all spaces in the "" line should be ignored — they are purely for formatting) ``` Parsing the URL string using the WHATWG API: @@ -556,7 +556,7 @@ Instantiate a new `URLSearchParams` object with an iterable map in a way that is similar to [`Map`][]'s constructor. `iterable` can be an Array or any iterable object. That means `iterable` can be another `URLSearchParams`, in which case the constructor will simply create a clone of the provided -`URLSearchParams`. Elements of `iterable` are key-value pairs, and can +`URLSearchParams`. Elements of `iterable` are key-value pairs, and can themselves be any iterable object. Duplicate keys are allowed. diff --git a/doc/api/util.md b/doc/api/util.md index 52e9e41639e0ca..cbbc5e92732894 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -82,9 +82,9 @@ added: v0.11.3 The `util.debuglog()` method is used to create a function that conditionally writes debug messages to `stderr` based on the existence of the `NODE_DEBUG` -environment variable. If the `section` name appears within the value of that +environment variable. If the `section` name appears within the value of that environment variable, then the returned function operates similar to -[`console.error()`][]. If not, then the returned function is a no-op. +[`console.error()`][]. If not, then the returned function is a no-op. For example: @@ -102,7 +102,7 @@ it will output something like: FOO 3245: hello from foo [123] ``` -where `3245` is the process id. If it is not run with that +where `3245` is the process id. If it is not run with that environment variable set, then it will not print anything. Multiple comma-separated `section` names may be specified in the `NODE_DEBUG` @@ -172,7 +172,7 @@ corresponding argument. Supported placeholders are: * `%d` - Number (integer or floating point value). * `%i` - Integer. * `%f` - Floating point value. -* `%j` - JSON. Replaced with the string `'[Circular]'` if the argument +* `%j` - JSON. Replaced with the string `'[Circular]'` if the argument contains circular references. * `%o` - Object. A string representation of an object with generic JavaScript object formatting. @@ -233,7 +233,7 @@ that the two styles are [semantically incompatible][]. * `constructor` {Function} * `superConstructor` {Function} -Inherit the prototype methods from one [constructor][] into another. The +Inherit the prototype methods from one [constructor][] into another. The prototype of `constructor` will be set to a new object created from `superConstructor`. diff --git a/doc/api/v8.md b/doc/api/v8.md index 3684a61583cb37..63a4ded9fe96ea 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -144,7 +144,7 @@ after the VM has started may result in unpredictable behavior, including crashes and data loss; or it may simply do nothing. The V8 options available for a version of Node.js may be determined by running -`node --v8-options`. An unofficial, community-maintained list of options +`node --v8-options`. An unofficial, community-maintained list of options and their effects is available [here][]. Usage: diff --git a/doc/api/vm.md b/doc/api/vm.md index 41101a5e5cf76f..842b0c8ac20cdd 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -116,7 +116,7 @@ changes: will be thrown. * `breakOnSigint`: if `true`, the execution will be terminated when `SIGINT` (Ctrl+C) is received. Existing handlers for the - event that have been attached via `process.on("SIGINT")` will be disabled + event that have been attached via `process.on('SIGINT')` will be disabled during script execution, but will continue to work after that. If execution is terminated, an [`Error`][] will be thrown. diff --git a/doc/api/zlib.md b/doc/api/zlib.md index 7c55b94b26706b..00d3a8c821d2eb 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -64,8 +64,8 @@ header is used to identify the compression encodings actually applied to a message. *Note*: the examples given below are drastically simplified to show -the basic concept. Using `zlib` encoding can be expensive, and the results -ought to be cached. See [Memory Usage Tuning][] for more information +the basic concept. Using `zlib` encoding can be expensive, and the results +ought to be cached. See [Memory Usage Tuning][] for more information on the speed/memory/compression tradeoffs involved in `zlib` usage. ```js @@ -165,7 +165,7 @@ The memory requirements for deflate are (in bytes): (1 << (windowBits + 2)) + (1 << (memLevel + 9)) ``` -That is: 128K for windowBits=15 + 128K for memLevel = 8 +That is: 128K for windowBits = 15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, to reduce the default memory requirements from 256K to 128K, the @@ -178,20 +178,20 @@ const options = { windowBits: 14, memLevel: 7 }; This will, however, generally degrade compression. The memory requirements for inflate are (in bytes) `1 << windowBits`. -That is, 32K for windowBits=15 (default value) plus a few kilobytes +That is, 32K for windowBits = 15 (default value) plus a few kilobytes for small objects. This is in addition to a single internal output slab buffer of size `chunkSize`, which defaults to 16K. The speed of `zlib` compression is affected most dramatically by the -`level` setting. A higher level will result in better compression, but -will take longer to complete. A lower level will result in less +`level` setting. A higher level will result in better compression, but +will take longer to complete. A lower level will result in less compression, but will be much faster. In general, greater memory usage options will mean that Node.js has to make fewer calls to `zlib` because it will be able to process more data on -each `write` operation. So, this is another factor that affects the +each `write` operation. So, this is another factor that affects the speed, at the cost of memory usage. ## Flushing @@ -233,9 +233,9 @@ added: v0.5.8 All of the constants defined in `zlib.h` are also defined on `require('zlib').constants`. In the normal course of operations, it will not be -necessary to use these constants. They are documented so that their presence is +necessary to use these constants. They are documented so that their presence is not surprising. This section is taken almost directly from the -[zlib documentation][]. See for more +[zlib documentation][]. See for more details. *Note*: Previously, the constants were available directly from @@ -296,7 +296,7 @@ changes: -Each class takes an `options` object. All options are optional. +Each class takes an `options` object. All options are optional. Note that some options are only relevant when compressing, and are ignored by the decompression classes. From d2a884edf9de1013284a991302392c1114cfcc95 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 21 Dec 2017 16:27:39 +0100 Subject: [PATCH 053/218] http: fix parsing of binary upgrade response body Fix a bug where a connection upgrade response with a Transfer-Encoding header and a body whose first byte is > 127 causes said byte to be dropped on the floor when passing the remainder of the message to the 'upgrade' event listeners. Fixes: https://github.com/nodejs/node/issues/17789 PR-URL: https://github.com/nodejs/node/pull/17806 Fixes: https://github.com/nodejs/node/issues/17789 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- lib/_http_client.js | 24 ++++++++----------- lib/_http_common.js | 15 +++--------- lib/_http_server.js | 2 +- test/parallel/test-http-upgrade-binary.js | 28 +++++++++++++++++++++++ 4 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 test/parallel/test-http-upgrade-binary.js diff --git a/lib/_http_client.js b/lib/_http_client.js index a836b772cc05ad..c58be1fc3acdb2 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -493,7 +493,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) { var socket = this.socket; var req = socket._httpMessage; - // propagate "domain" setting... if (req.domain && !res.domain) { debug('setting "res.domain"'); @@ -506,29 +505,22 @@ function parserOnIncomingClient(res, shouldKeepAlive) { // We already have a response object, this means the server // sent a double response. socket.destroy(); - return; + return 0; // No special treatment. } req.res = res; // Responses to CONNECT request is handled as Upgrade. - if (req.method === 'CONNECT') { + const method = req.method; + if (method === 'CONNECT') { res.upgrade = true; - return 2; // skip body, and the rest + return 2; // Skip body and treat as Upgrade. } - // Responses to HEAD requests are crazy. - // HEAD responses aren't allowed to have an entity-body - // but *can* have a content-length which actually corresponds - // to the content-length of the entity-body had the request - // been a GET. - var isHeadResponse = req.method === 'HEAD'; - debug('AGENT isHeadResponse', isHeadResponse); - if (res.statusCode === 100) { // restart the parser, as this is a continue message. req.res = null; // Clear res so that we don't hit double-responses. req.emit('continue'); - return true; + return 1; // Skip body but don't treat as Upgrade. } if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) { @@ -538,7 +530,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) { req.shouldKeepAlive = false; } - DTRACE_HTTP_CLIENT_RESPONSE(socket, req); LTTNG_HTTP_CLIENT_RESPONSE(socket, req); COUNTER_HTTP_CLIENT_RESPONSE(); @@ -556,7 +547,10 @@ function parserOnIncomingClient(res, shouldKeepAlive) { if (!handled) res._dump(); - return isHeadResponse; + if (method === 'HEAD') + return 1; // Skip body but don't treat as Upgrade. + + return 0; // No special treatment. } // client diff --git a/lib/_http_common.js b/lib/_http_common.js index ad0dec520d1210..381ffeb807a84e 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -106,19 +106,10 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, parser.incoming.upgrade = upgrade; - var skipBody = 0; // response to HEAD or CONNECT + if (upgrade) + return 2; // Skip body and treat as Upgrade. - if (!upgrade) { - // For upgraded connections and CONNECT method request, we'll emit this - // after parser.execute so that we can capture the first part of the new - // protocol. - skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive); - } - - if (typeof skipBody !== 'number') - return skipBody ? 1 : 0; - else - return skipBody; + return parser.onIncoming(parser.incoming, shouldKeepAlive); } // XXX This is a mess. diff --git a/lib/_http_server.js b/lib/_http_server.js index 32c39e6160e5a9..be591c437ca083 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -618,7 +618,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { } else { server.emit('request', req, res); } - return false; // Not a HEAD response. (Not even a response!) + return 0; // No special treatment. } function resetSocketTimeout(server, socket, state) { diff --git a/test/parallel/test-http-upgrade-binary.js b/test/parallel/test-http-upgrade-binary.js new file mode 100644 index 00000000000000..002ac9c564ad1e --- /dev/null +++ b/test/parallel/test-http-upgrade-binary.js @@ -0,0 +1,28 @@ +'use strict'; +const { mustCall } = require('../common'); +const assert = require('assert'); +const http = require('http'); +const net = require('net'); + +// https://github.com/nodejs/node/issues/17789 - a connection upgrade response +// that has a Transfer-Encoding header and a body whose first byte is > 127 +// triggers a bug where said byte is skipped over. +net.createServer(mustCall(function(conn) { + conn.write('HTTP/1.1 101 Switching Protocols\r\n' + + 'Connection: upgrade\r\n' + + 'Transfer-Encoding: chunked\r\n' + + 'Upgrade: websocket\r\n' + + '\r\n' + + '\u0080', 'latin1'); + this.close(); +})).listen(0, mustCall(function() { + http.get({ + host: this.address().host, + port: this.address().port, + headers: { 'Connection': 'upgrade', 'Upgrade': 'websocket' }, + }).on('upgrade', mustCall((res, conn, head) => { + assert.strictEqual(head.length, 1); + assert.strictEqual(head[0], 128); + conn.destroy(); + })); +})); From 7813a0de0a91f1f0210c5fed5654835c10014ab7 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 23 Oct 2017 02:04:07 +0800 Subject: [PATCH 054/218] test: introduce test/common/internet.addresses This commit introduces test/common/internet.address, which includes a set of addresses for doing internet tests. These addresses can be overriden using NODE_TEST_* environment variables. PR-URL: https://github.com/nodejs/node/pull/16390 Backport-PR-URL: https://github.com/nodejs/node/pull/19706 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: Gibson Fahnestock Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- test/common/README.md | 35 ++++++++++++++++++++++++++ test/common/dns.js | 4 ++- test/common/internet.js | 54 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 test/common/internet.js diff --git a/test/common/README.md b/test/common/README.md index c6a3de0c35a1d0..bb96f88be8308b 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -10,6 +10,7 @@ This directory contains modules used to test the Node.js implementation. * [DNS module](#dns-module) * [Duplex pair helper](#duplex-pair-helper) * [Fixtures module](#fixtures-module) +* [Internet module](#internet-module) * [tmpdir module](#tmpdir-module) * [WPT module](#wpt-module) @@ -483,6 +484,40 @@ Returns the result of Returns the result of `fs.readFileSync(path.join(fixtures.fixturesDir, 'keys', arg), 'enc')`. +## Internet Module + +The `common/internet` module provides utilities for working with +internet-related tests. + +### internet.addresses + +* [<Object>] + * `INET_HOST` [<String>] A generic host that has registered common + DNS records, supports both IPv4 and IPv6, and provides basic HTTP/HTTPS + services + * `INET4_HOST` [<String>] A host that provides IPv4 services + * `INET6_HOST` [<String>] A host that provides IPv6 services + * `INET4_IP` [<String>] An accessible IPv4 IP, defaults to the + Google Public DNS IPv4 address + * `INET6_IP` [<String>] An accessible IPv6 IP, defaults to the + Google Public DNS IPv6 address + * `INVALID_HOST` [<String>] An invalid host that cannot be resolved + * `MX_HOST` [<String>] A host with MX records registered + * `SRV_HOST` [<String>] A host with SRV records registered + * `PTR_HOST` [<String>] A host with PTR records registered + * `NAPTR_HOST` [<String>] A host with NAPTR records registered + * `SOA_HOST` [<String>] A host with SOA records registered + * `CNAME_HOST` [<String>] A host with CNAME records registered + * `NS_HOST` [<String>] A host with NS records registered + * `TXT_HOST` [<String>] A host with TXT records registered + * `DNS4_SERVER` [<String>] An accessible IPv4 DNS server + * `DNS6_SERVER` [<String>] An accessible IPv6 DNS server + +A set of addresses for internet-related tests. All properties are configurable +via `NODE_TEST_*` environment variables. For example, to configure +`internet.addresses.INET_HOST`, set the environment +vairable `NODE_TEST_INET_HOST` to a specified host. + ## tmpdir Module The `tmpdir` module supports the use of a temporary directory for testing. diff --git a/test/common/dns.js b/test/common/dns.js index 6ab3fcbc91278b..432d1b764dde29 100644 --- a/test/common/dns.js +++ b/test/common/dns.js @@ -287,4 +287,6 @@ function writeDNSPacket(parsed) { })); } -module.exports = { types, classes, writeDNSPacket, parseDNSPacket }; +module.exports = { + types, classes, writeDNSPacket, parseDNSPacket +}; diff --git a/test/common/internet.js b/test/common/internet.js new file mode 100644 index 00000000000000..48b532ca8e6606 --- /dev/null +++ b/test/common/internet.js @@ -0,0 +1,54 @@ +/* eslint-disable required-modules */ +'use strict'; + +// Utilities for internet-related tests + +const addresses = { + // A generic host that has registered common DNS records, + // supports both IPv4 and IPv6, and provides basic HTTP/HTTPS services + INET_HOST: 'nodejs.org', + // A host that provides IPv4 services + INET4_HOST: 'nodejs.org', + // A host that provides IPv6 services + INET6_HOST: 'nodejs.org', + // An accessible IPv4 IP, + // defaults to the Google Public DNS IPv4 address + INET4_IP: '8.8.8.8', + // An accessible IPv6 IP, + // defaults to the Google Public DNS IPv6 address + INET6_IP: '2001:4860:4860::8888', + // An invalid host that cannot be resolved + // See https://tools.ietf.org/html/rfc2606#section-2 + INVALID_HOST: 'something.invalid', + // A host with MX records registered + MX_HOST: 'nodejs.org', + // A host with SRV records registered + SRV_HOST: '_jabber._tcp.google.com', + // A host with PTR records registered + PTR_HOST: '8.8.8.8.in-addr.arpa', + // A host with NAPTR records registered + NAPTR_HOST: 'sip2sip.info', + // A host with SOA records registered + SOA_HOST: 'nodejs.org', + // A host with CNAME records registered + CNAME_HOST: 'blog.nodejs.org', + // A host with NS records registered + NS_HOST: 'nodejs.org', + // A host with TXT records registered + TXT_HOST: 'nodejs.org', + // An accessible IPv4 DNS server + DNS4_SERVER: '8.8.8.8', + // An accessible IPv4 DNS server + DNS6_SERVER: '2001:4860:4860::8888' +}; + +for (const key of Object.keys(addresses)) { + const envName = `NODE_TEST_${key}`; + if (process.env[envName]) { + addresses[key] = process.env[envName]; + } +} + +module.exports = { + addresses +}; From daeb6de8ec1647faa4c0576ac77a29e0299e611e Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 23 Oct 2017 02:07:54 +0800 Subject: [PATCH 055/218] test: use internet.addresses in internet tests PR-URL: https://github.com/nodejs/node/pull/16390 Backport-PR-URL: https://github.com/nodejs/node/pull/19706 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: Gibson Fahnestock Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- test/internet/test-dns-cares-domains.js | 7 +- test/internet/test-dns-ipv4.js | 77 +++++++++--------- test/internet/test-dns-ipv6.js | 76 ++++++++++-------- ...t-dns-setserver-in-callback-of-resolve4.js | 11 ++- test/internet/test-dns.js | 80 +++++++++++-------- .../internet/test-http-https-default-ports.js | 5 +- 6 files changed, 142 insertions(+), 114 deletions(-) diff --git a/test/internet/test-dns-cares-domains.js b/test/internet/test-dns-cares-domains.js index 62c1847ea29adf..6609758a7daf17 100644 --- a/test/internet/test-dns-cares-domains.js +++ b/test/internet/test-dns-cares-domains.js @@ -1,5 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); +const { addresses } = require('../common/internet'); const assert = require('assert'); const dns = require('dns'); const domain = require('domain'); @@ -20,8 +21,8 @@ const methods = [ methods.forEach(function(method) { const d = domain.create(); d.run(function() { - dns[method]('google.com', function() { + dns[method](addresses.INET_HOST, common.mustCall(() => { assert.strictEqual(process.domain, d, `${method} retains domain`); - }); + })); }); }); diff --git a/test/internet/test-dns-ipv4.js b/test/internet/test-dns-ipv4.js index d3d5ba22009675..4c6e0ae6865e3f 100644 --- a/test/internet/test-dns-ipv4.js +++ b/test/internet/test-dns-ipv4.js @@ -1,5 +1,6 @@ 'use strict'; const common = require('../common'); +const { addresses } = require('../common/internet'); const assert = require('assert'); const dns = require('dns'); const net = require('net'); @@ -38,68 +39,72 @@ function checkWrap(req) { } TEST(function test_resolve4(done) { - const req = dns.resolve4('www.google.com', - common.mustCall((err, ips) => { - assert.ifError(err); + const req = dns.resolve4( + addresses.INET4_HOST, + common.mustCall((err, ips) => { + assert.ifError(err); - assert.ok(ips.length > 0); + assert.ok(ips.length > 0); - for (let i = 0; i < ips.length; i++) { - assert.ok(isIPv4(ips[i])); - } + for (let i = 0; i < ips.length; i++) { + assert.ok(isIPv4(ips[i])); + } - done(); - })); + done(); + })); checkWrap(req); }); TEST(function test_reverse_ipv4(done) { - const req = dns.reverse('8.8.8.8', - common.mustCall((err, domains) => { - assert.ifError(err); + const req = dns.reverse( + addresses.INET4_IP, + common.mustCall((err, domains) => { + assert.ifError(err); - assert.ok(domains.length > 0); + assert.ok(domains.length > 0); - for (let i = 0; i < domains.length; i++) { - assert.ok(domains[i]); - assert.ok(typeof domains[i] === 'string'); - } + for (let i = 0; i < domains.length; i++) { + assert.ok(domains[i]); + assert.ok(typeof domains[i] === 'string'); + } - done(); - })); + done(); + })); checkWrap(req); }); TEST(function test_lookup_ipv4_explicit(done) { - const req = dns.lookup('www.google.com', 4, - common.mustCall((err, ip, family) => { - assert.ifError(err); - assert.ok(net.isIPv4(ip)); - assert.strictEqual(family, 4); + const req = dns.lookup( + addresses.INET4_HOST, 4, + common.mustCall((err, ip, family) => { + assert.ifError(err); + assert.ok(net.isIPv4(ip)); + assert.strictEqual(family, 4); - done(); - })); + done(); + })); checkWrap(req); }); TEST(function test_lookup_ipv4_implicit(done) { - const req = dns.lookup('www.google.com', - common.mustCall((err, ip, family) => { - assert.ifError(err); - assert.ok(net.isIPv4(ip)); - assert.strictEqual(family, 4); + const req = dns.lookup( + addresses.INET4_HOST, + common.mustCall((err, ip, family) => { + assert.ifError(err); + assert.ok(net.isIPv4(ip)); + assert.strictEqual(family, 4); - done(); - })); + done(); + })); checkWrap(req); }); TEST(function test_lookup_ipv4_explicit_object(done) { - const req = dns.lookup('www.google.com', { + const req = dns.lookup(addresses.INET4_HOST, { family: 4 }, common.mustCall((err, ip, family) => { assert.ifError(err); @@ -113,7 +118,7 @@ TEST(function test_lookup_ipv4_explicit_object(done) { }); TEST(function test_lookup_ipv4_hint_addrconfig(done) { - const req = dns.lookup('www.google.com', { + const req = dns.lookup(addresses.INET4_HOST, { hints: dns.ADDRCONFIG }, common.mustCall((err, ip, family) => { assert.ifError(err); @@ -154,7 +159,7 @@ TEST(function test_lookup_localhost_ipv4(done) { TEST(function test_lookup_all_ipv4(done) { const req = dns.lookup( - 'www.google.com', + addresses.INET4_HOST, { all: true, family: 4 }, common.mustCall((err, ips) => { assert.ifError(err); diff --git a/test/internet/test-dns-ipv6.js b/test/internet/test-dns-ipv6.js index a91b108456c027..8b1a8936802729 100644 --- a/test/internet/test-dns-ipv6.js +++ b/test/internet/test-dns-ipv6.js @@ -1,5 +1,6 @@ 'use strict'; const common = require('../common'); +const { addresses } = require('../common/internet'); if (!common.hasIPv6) common.skip('this test, no IPv6 support'); @@ -38,53 +39,57 @@ function checkWrap(req) { } TEST(function test_resolve6(done) { - const req = dns.resolve6('ipv6.google.com', - common.mustCall((err, ips) => { - assert.ifError(err); + const req = dns.resolve6( + addresses.INET6_HOST, + common.mustCall((err, ips) => { + assert.ifError(err); - assert.ok(ips.length > 0); + assert.ok(ips.length > 0); - for (let i = 0; i < ips.length; i++) - assert.ok(isIPv6(ips[i])); + for (let i = 0; i < ips.length; i++) + assert.ok(isIPv6(ips[i])); - done(); - })); + done(); + })); checkWrap(req); }); TEST(function test_reverse_ipv6(done) { - const req = dns.reverse('2001:4860:4860::8888', - common.mustCall((err, domains) => { - assert.ifError(err); + const req = dns.reverse( + addresses.INET6_IP, + common.mustCall((err, domains) => { + assert.ifError(err); - assert.ok(domains.length > 0); + assert.ok(domains.length > 0); - for (let i = 0; i < domains.length; i++) - assert.ok(typeof domains[i] === 'string'); + for (let i = 0; i < domains.length; i++) + assert.ok(typeof domains[i] === 'string'); - done(); - })); + done(); + })); checkWrap(req); }); TEST(function test_lookup_ipv6_explicit(done) { - const req = dns.lookup('ipv6.google.com', 6, - common.mustCall((err, ip, family) => { - assert.ifError(err); - assert.ok(isIPv6(ip)); - assert.strictEqual(family, 6); + const req = dns.lookup( + addresses.INET6_HOST, + 6, + common.mustCall((err, ip, family) => { + assert.ifError(err); + assert.ok(isIPv6(ip)); + assert.strictEqual(family, 6); - done(); - })); + done(); + })); checkWrap(req); }); /* This ends up just being too problematic to test TEST(function test_lookup_ipv6_implicit(done) { - var req = dns.lookup('ipv6.google.com', function(err, ip, family) { + var req = dns.lookup(addresses.INET6_HOST, function(err, ip, family) { assert.ifError(err); assert.ok(net.isIPv6(ip)); assert.strictEqual(family, 6); @@ -97,7 +102,7 @@ TEST(function test_lookup_ipv6_implicit(done) { */ TEST(function test_lookup_ipv6_explicit_object(done) { - const req = dns.lookup('ipv6.google.com', { + const req = dns.lookup(addresses.INET6_HOST, { family: 6 }, common.mustCall((err, ip, family) => { assert.ifError(err); @@ -111,7 +116,7 @@ TEST(function test_lookup_ipv6_explicit_object(done) { }); TEST(function test_lookup_ipv6_hint(done) { - const req = dns.lookup('www.google.com', { + const req = dns.lookup(addresses.INET6_HOST, { family: 6, hints: dns.V4MAPPED }, common.mustCall((err, ip, family) => { @@ -120,7 +125,7 @@ TEST(function test_lookup_ipv6_hint(done) { if (common.isFreeBSD) { assert(err instanceof Error); assert.strictEqual(err.code, 'EAI_BADFLAGS'); - assert.strictEqual(err.hostname, 'www.google.com'); + assert.strictEqual(err.hostname, addresses.INET_HOST); assert.ok(/getaddrinfo EAI_BADFLAGS/.test(err.message)); done(); return; @@ -139,21 +144,22 @@ TEST(function test_lookup_ipv6_hint(done) { }); TEST(function test_lookup_ip_ipv6(done) { - const req = dns.lookup('::1', - common.mustCall((err, ip, family) => { - assert.ifError(err); - assert.ok(isIPv6(ip)); - assert.strictEqual(family, 6); + const req = dns.lookup( + '::1', + common.mustCall((err, ip, family) => { + assert.ifError(err); + assert.ok(isIPv6(ip)); + assert.strictEqual(family, 6); - done(); - })); + done(); + })); checkWrap(req); }); TEST(function test_lookup_all_ipv6(done) { const req = dns.lookup( - 'www.google.com', + addresses.INET6_HOST, { all: true, family: 6 }, common.mustCall((err, ips) => { assert.ifError(err); diff --git a/test/internet/test-dns-setserver-in-callback-of-resolve4.js b/test/internet/test-dns-setserver-in-callback-of-resolve4.js index 222ac4dcc8ee31..58b3327efe9475 100644 --- a/test/internet/test-dns-setserver-in-callback-of-resolve4.js +++ b/test/internet/test-dns-setserver-in-callback-of-resolve4.js @@ -5,11 +5,14 @@ // a crash or not. If it doesn't crash, the test succeeded. const common = require('../common'); +const { addresses } = require('../common/internet'); const dns = require('dns'); -dns.resolve4('google.com', common.mustCall(function(/* err, nameServers */) { - dns.setServers([ '8.8.8.8' ]); -})); +dns.resolve4( + addresses.INET4_HOST, + common.mustCall(function(/* err, nameServers */) { + dns.setServers([ addresses.DNS4_SERVER ]); + })); // Test https://github.com/nodejs/node/issues/14734 -dns.resolve4('google.com', common.mustCall()); +dns.resolve4(addresses.INET4_HOST, common.mustCall()); diff --git a/test/internet/test-dns.js b/test/internet/test-dns.js index e2214433c51436..054de88dd45f90 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -21,6 +21,7 @@ 'use strict'; const common = require('../common'); +const { addresses } = require('../common/internet'); const assert = require('assert'); const dns = require('dns'); const net = require('net'); @@ -74,7 +75,9 @@ TEST(function test_reverse_bogus(done) { }); TEST(function test_resolve4_ttl(done) { - const req = dns.resolve4('google.com', { ttl: true }, function(err, result) { + const req = dns.resolve4(addresses.INET4_HOST, { + ttl: true + }, function(err, result) { assert.ifError(err); assert.ok(result.length > 0); @@ -95,7 +98,9 @@ TEST(function test_resolve4_ttl(done) { }); TEST(function test_resolve6_ttl(done) { - const req = dns.resolve6('google.com', { ttl: true }, function(err, result) { + const req = dns.resolve6(addresses.INET6_HOST, { + ttl: true + }, function(err, result) { assert.ifError(err); assert.ok(result.length > 0); @@ -116,7 +121,7 @@ TEST(function test_resolve6_ttl(done) { }); TEST(function test_resolveMx(done) { - const req = dns.resolveMx('gmail.com', function(err, result) { + const req = dns.resolveMx(addresses.MX_HOST, function(err, result) { assert.ifError(err); assert.ok(result.length > 0); @@ -138,7 +143,7 @@ TEST(function test_resolveMx(done) { }); TEST(function test_resolveMx_failure(done) { - const req = dns.resolveMx('something.invalid', function(err, result) { + const req = dns.resolveMx(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -151,7 +156,7 @@ TEST(function test_resolveMx_failure(done) { }); TEST(function test_resolveNs(done) { - const req = dns.resolveNs('rackspace.com', function(err, names) { + const req = dns.resolveNs(addresses.NS_HOST, function(err, names) { assert.ifError(err); assert.ok(names.length > 0); @@ -168,7 +173,7 @@ TEST(function test_resolveNs(done) { }); TEST(function test_resolveNs_failure(done) { - const req = dns.resolveNs('something.invalid', function(err, result) { + const req = dns.resolveNs(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -181,7 +186,7 @@ TEST(function test_resolveNs_failure(done) { }); TEST(function test_resolveSrv(done) { - const req = dns.resolveSrv('_jabber._tcp.google.com', function(err, result) { + const req = dns.resolveSrv(addresses.SRV_HOST, function(err, result) { assert.ifError(err); assert.ok(result.length > 0); @@ -205,7 +210,7 @@ TEST(function test_resolveSrv(done) { }); TEST(function test_resolveSrv_failure(done) { - const req = dns.resolveSrv('something.invalid', function(err, result) { + const req = dns.resolveSrv(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -218,7 +223,7 @@ TEST(function test_resolveSrv_failure(done) { }); TEST(function test_resolvePtr(done) { - const req = dns.resolvePtr('8.8.8.8.in-addr.arpa', function(err, result) { + const req = dns.resolvePtr(addresses.PTR_HOST, function(err, result) { assert.ifError(err); assert.ok(result.length > 0); @@ -235,7 +240,7 @@ TEST(function test_resolvePtr(done) { }); TEST(function test_resolvePtr_failure(done) { - const req = dns.resolvePtr('something.invalid', function(err, result) { + const req = dns.resolvePtr(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -248,7 +253,7 @@ TEST(function test_resolvePtr_failure(done) { }); TEST(function test_resolveNaptr(done) { - const req = dns.resolveNaptr('sip2sip.info', function(err, result) { + const req = dns.resolveNaptr(addresses.NAPTR_HOST, function(err, result) { assert.ifError(err); assert.ok(result.length > 0); @@ -272,7 +277,7 @@ TEST(function test_resolveNaptr(done) { }); TEST(function test_resolveNaptr_failure(done) { - const req = dns.resolveNaptr('something.invalid', function(err, result) { + const req = dns.resolveNaptr(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -285,7 +290,7 @@ TEST(function test_resolveNaptr_failure(done) { }); TEST(function test_resolveSoa(done) { - const req = dns.resolveSoa('nodejs.org', function(err, result) { + const req = dns.resolveSoa(addresses.SOA_HOST, function(err, result) { assert.ifError(err); assert.ok(result); assert.strictEqual(typeof result, 'object'); @@ -318,7 +323,7 @@ TEST(function test_resolveSoa(done) { }); TEST(function test_resolveSoa_failure(done) { - const req = dns.resolveSoa('something.invalid', function(err, result) { + const req = dns.resolveSoa(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -331,7 +336,7 @@ TEST(function test_resolveSoa_failure(done) { }); TEST(function test_resolveCname(done) { - const req = dns.resolveCname('www.microsoft.com', function(err, names) { + const req = dns.resolveCname(addresses.CNAME_HOST, function(err, names) { assert.ifError(err); assert.ok(names.length > 0); @@ -348,7 +353,7 @@ TEST(function test_resolveCname(done) { }); TEST(function test_resolveCname_failure(done) { - const req = dns.resolveCname('something.invalid', function(err, result) { + const req = dns.resolveCname(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -362,7 +367,7 @@ TEST(function test_resolveCname_failure(done) { TEST(function test_resolveTxt(done) { - const req = dns.resolveTxt('google.com', function(err, records) { + const req = dns.resolveTxt(addresses.TXT_HOST, function(err, records) { assert.ifError(err); assert.strictEqual(records.length, 1); assert.ok(util.isArray(records[0])); @@ -374,7 +379,7 @@ TEST(function test_resolveTxt(done) { }); TEST(function test_resolveTxt_failure(done) { - const req = dns.resolveTxt('something.invalid', function(err, result) { + const req = dns.resolveTxt(addresses.INVALID_HOST, function(err, result) { assert.ok(err instanceof Error); assert.strictEqual(err.errno, 'ENOTFOUND'); @@ -388,12 +393,12 @@ TEST(function test_resolveTxt_failure(done) { TEST(function test_lookup_failure(done) { - const req = dns.lookup('does.not.exist', 4, function(err, ip, family) { + const req = dns.lookup(addresses.INVALID_HOST, 4, (err, ip, family) => { assert.ok(err instanceof Error); assert.strictEqual(err.errno, dns.NOTFOUND); assert.strictEqual(err.errno, 'ENOTFOUND'); assert.ok(!/ENOENT/.test(err.message)); - assert.ok(/does\.not\.exist/.test(err.message)); + assert.ok(err.message.includes(addresses.INVALID_HOST)); done(); }); @@ -458,7 +463,9 @@ TEST(function test_lookup_null_all(done) { TEST(function test_lookup_all_mixed(done) { - const req = dns.lookup('www.google.com', { all: true }, function(err, ips) { + const req = dns.lookup(addresses.INET_HOST, { + all: true + }, function(err, ips) { assert.ifError(err); assert.ok(Array.isArray(ips)); assert.ok(ips.length > 0); @@ -508,11 +515,11 @@ TEST(function test_reverse_failure(done) { TEST(function test_lookup_failure(done) { - const req = dns.lookup('nosuchhostimsure', function(err) { + const req = dns.lookup(addresses.INVALID_HOST, (err) => { assert(err instanceof Error); assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code... - assert.strictEqual(err.hostname, 'nosuchhostimsure'); - assert.ok(/nosuchhostimsure/.test(err.message)); + assert.strictEqual(err.hostname, addresses.INVALID_HOST); + assert.ok(err.message.includes(addresses.INVALID_HOST)); done(); }); @@ -522,7 +529,7 @@ TEST(function test_lookup_failure(done) { TEST(function test_resolve_failure(done) { - const req = dns.resolve4('nosuchhostimsure', function(err) { + const req = dns.resolve4(addresses.INVALID_HOST, (err) => { assert(err instanceof Error); switch (err.code) { @@ -534,8 +541,8 @@ TEST(function test_resolve_failure(done) { break; } - assert.strictEqual(err.hostname, 'nosuchhostimsure'); - assert.ok(/nosuchhostimsure/.test(err.message)); + assert.strictEqual(err.hostname, addresses.INVALID_HOST); + assert.ok(err.message.includes(addresses.INVALID_HOST)); done(); }); @@ -546,15 +553,16 @@ TEST(function test_resolve_failure(done) { let getaddrinfoCallbackCalled = false; -console.log('looking up nodejs.org...'); +console.log(`looking up ${addresses.INET4_HOST}..`); const cares = process.binding('cares_wrap'); const req = new cares.GetAddrInfoReqWrap(); -cares.getaddrinfo(req, 'nodejs.org', 4, /* hints */ 0, /* verbatim */ true); +cares.getaddrinfo(req, addresses.INET4_HOST, 4, + /* hints */ 0, /* verbatim */ true); req.oncomplete = function(err, domains) { assert.strictEqual(err, 0); - console.log('nodejs.org = ', domains); + console.log(`${addresses.INET4_HOST} = ${domains}`); assert.ok(Array.isArray(domains)); assert.ok(domains.length >= 1); assert.strictEqual(typeof domains[0], 'string'); @@ -569,10 +577,14 @@ process.on('exit', function() { }); -assert.doesNotThrow(() => dns.lookup('nodejs.org', 6, common.mustCall())); +assert.doesNotThrow(() => + dns.lookup(addresses.INET6_HOST, 6, common.mustCall())); -assert.doesNotThrow(() => dns.lookup('nodejs.org', {}, common.mustCall())); +assert.doesNotThrow(() => + dns.lookup(addresses.INET_HOST, {}, common.mustCall())); -assert.doesNotThrow(() => dns.lookupService('0.0.0.0', '0', common.mustCall())); +assert.doesNotThrow(() => + dns.lookupService('0.0.0.0', '0', common.mustCall())); -assert.doesNotThrow(() => dns.lookupService('0.0.0.0', 0, common.mustCall())); +assert.doesNotThrow(() => + dns.lookupService('0.0.0.0', 0, common.mustCall())); diff --git a/test/internet/test-http-https-default-ports.js b/test/internet/test-http-https-default-ports.js index 567b045dc6eacf..5f1b9eddb4f416 100644 --- a/test/internet/test-http-https-default-ports.js +++ b/test/internet/test-http-https-default-ports.js @@ -21,6 +21,7 @@ 'use strict'; const common = require('../common'); +const { addresses } = require('../common/internet'); if (!common.hasCrypto) common.skip('missing crypto'); @@ -29,10 +30,10 @@ const https = require('https'); const http = require('http'); -https.get('https://www.google.com/', common.mustCall(function(res) { +https.get(`https://${addresses.INET_HOST}/`, common.mustCall(function(res) { res.resume(); })); -http.get('http://www.google.com/', common.mustCall(function(res) { +http.get(`http://${addresses.INET_HOST}/`, common.mustCall(function(res) { res.resume(); })); From bff725853542318f01b2ebb33a3feadf5eb53c3b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 3 Feb 2018 18:27:21 +0800 Subject: [PATCH 056/218] test: do not check TXT content in test-dns-any google.com added another TXT record which broke this test. This removes the check on the content of the TXT record since that depends on an external state subject to change. PR-URL: https://github.com/nodejs/node/pull/18547 Backport-PR-URL: https://github.com/nodejs/node/pull/19706 Reviewed-By: Khaidi Chu Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: Jon Moss Reviewed-By: Anatoli Papirovski Reviewed-By: James M Snell --- test/internet/test-dns-any.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/internet/test-dns-any.js b/test/internet/test-dns-any.js index dd80e48bf44e91..a83040801f38f4 100644 --- a/test/internet/test-dns-any.js +++ b/test/internet/test-dns-any.js @@ -59,9 +59,6 @@ const checkers = { checkTXT(r) { assert.ok(Array.isArray(r.entries)); assert.ok(r.entries.length > 0); - r.entries.forEach((txt) => { - assert(txt.startsWith('v=spf1')); - }); assert.strictEqual(r.type, 'TXT'); }, checkSOA(r) { From 34af49401b1779379f3e6acc33c89a4138bab2f4 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 25 Nov 2017 23:58:05 +0900 Subject: [PATCH 057/218] test: add common.dns.errorLookupMock PR-URL: https://github.com/nodejs/node/pull/17296 Backport-PR-URL: https://github.com/nodejs/node/pull/19706 Refs: https://github.com/nodejs/help/issues/687 Reviewed-By: Refael Ackermann Reviewed-By: Rich Trott Reviewed-By: James M Snell --- test/common/README.md | 21 ++++++++++++++++++++- test/common/dns.js | 26 +++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index bb96f88be8308b..030b3e0366b24a 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -404,7 +404,26 @@ called before the callback is invoked. ## DNS Module -The `DNS` module provides a naïve DNS parser/serializer. +The `DNS` module provides utilities related to the `dns` built-in module. + +### errorLookupMock(code, syscall) + +* `code` [<String>] Defaults to `dns.mockedErrorCode`. +* `syscall` [<String>] Defaults to `dns.mockedSysCall`. +* return [<Function>] + + +A mock for the `lookup` option of `net.connect()` that would result in an error +with the `code` and the `syscall` specified. Returns a function that has the +same signature as `dns.lookup()`. + +### mockedErrorCode + +The default `code` of errors generated by `errorLookupMock`. + +### mockedSysCall + +The default `syscall` of errors generated by `errorLookupMock`. ### readDomainFromPacket(buffer, offset) diff --git a/test/common/dns.js b/test/common/dns.js index 432d1b764dde29..69c67ac541cf98 100644 --- a/test/common/dns.js +++ b/test/common/dns.js @@ -1,8 +1,6 @@ /* eslint-disable required-modules */ 'use strict'; -// Naïve DNS parser/serializer. - const assert = require('assert'); const os = require('os'); @@ -22,6 +20,8 @@ const classes = { IN: 1 }; +// Naïve DNS parser/serializer. + function readDomainFromPacket(buffer, offset) { assert.ok(offset < buffer.length); const length = buffer[offset]; @@ -287,6 +287,26 @@ function writeDNSPacket(parsed) { })); } +const mockedErrorCode = 'ENOTFOUND'; +const mockedSysCall = 'getaddrinfo'; + +function errorLookupMock(code = mockedErrorCode, syscall = mockedSysCall) { + return function lookupWithError(host, dnsopts, cb) { + const err = new Error(`${syscall} ${code} ${host}`); + err.code = code; + err.errno = code; + err.syscall = syscall; + err.hostname = host; + cb(err); + }; +} + module.exports = { - types, classes, writeDNSPacket, parseDNSPacket + types, + classes, + writeDNSPacket, + parseDNSPacket, + errorLookupMock, + mockedErrorCode, + mockedSysCall }; From da162278dea69e1372562305618834b6722c6e84 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 25 Nov 2017 23:58:24 +0900 Subject: [PATCH 058/218] test: mock the lookup function in parallel tests These tests should not make any DNS calls. The lookup would fail when the DNS requests are hijacked and time out instead of erroring out. PR-URL: https://github.com/nodejs/node/pull/17296 Backport-PR-URL: https://github.com/nodejs/node/pull/19706 Refs: https://github.com/nodejs/help/issues/687 Reviewed-By: Refael Ackermann Reviewed-By: Rich Trott Reviewed-By: James M Snell --- ...net-better-error-messages-port-hostname.js | 27 +++++++++++------ .../test-net-connect-immediate-finish.js | 29 ++++++++++++------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/test/parallel/test-net-better-error-messages-port-hostname.js b/test/parallel/test-net-better-error-messages-port-hostname.js index 818ea4bfff41f6..1a8aa770b44a22 100644 --- a/test/parallel/test-net-better-error-messages-port-hostname.js +++ b/test/parallel/test-net-better-error-messages-port-hostname.js @@ -1,21 +1,30 @@ 'use strict'; + +// This tests that the error thrown from net.createConnection +// comes with host and port properties. +// See https://github.com/nodejs/node-v0.x-archive/issues/7005 + const common = require('../common'); const net = require('net'); const assert = require('assert'); +const { addresses } = require('../common/internet'); +const { + errorLookupMock, + mockedErrorCode +} = require('../common/dns'); + // Using port 0 as hostname used is already invalid. -const c = net.createConnection(0, 'this.hostname.is.invalid'); +const c = net.createConnection({ + port: 0, + host: addresses.INVALID_HOST, + lookup: common.mustCall(errorLookupMock()) +}); c.on('connect', common.mustNotCall()); c.on('error', common.mustCall(function(e) { - // If Name Service Switch is available on the operating system then it - // might be configured differently (/etc/nsswitch.conf). - // If the system is configured with no dns the error code will be EAI_AGAIN, - // but if there are more services after the dns entry, for example some - // linux distributions ship a myhostname service by default which would - // still produce the ENOTFOUND error. - assert.ok(e.code === 'ENOTFOUND' || e.code === 'EAI_AGAIN'); + assert.strictEqual(e.code, mockedErrorCode); assert.strictEqual(e.port, 0); - assert.strictEqual(e.hostname, 'this.hostname.is.invalid'); + assert.strictEqual(e.hostname, addresses.INVALID_HOST); })); diff --git a/test/parallel/test-net-connect-immediate-finish.js b/test/parallel/test-net-connect-immediate-finish.js index e2e5e1c6715b9a..9adf8c31128b00 100644 --- a/test/parallel/test-net-connect-immediate-finish.js +++ b/test/parallel/test-net-connect-immediate-finish.js @@ -20,28 +20,35 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; + +// This tests that if the socket is still in the 'connecting' state +// when the user calls socket.end() ('finish'), the socket would emit +// 'connect' and defer the handling until the 'connect' event is handled. + const common = require('../common'); const assert = require('assert'); const net = require('net'); +const { addresses } = require('../common/internet'); +const { + errorLookupMock, + mockedErrorCode, + mockedSysCall +} = require('../common/dns'); + const client = net.connect({ - host: 'this.hostname.is.invalid', - port: common.PORT + host: addresses.INVALID_HOST, + port: common.PORT, + lookup: common.mustCall(errorLookupMock()) }); client.once('error', common.mustCall((err) => { assert(err); assert.strictEqual(err.code, err.errno); - // If Name Service Switch is available on the operating system then it - // might be configured differently (/etc/nsswitch.conf). - // If the system is configured with no dns the error code will be EAI_AGAIN, - // but if there are more services after the dns entry, for example some - // linux distributions ship a myhostname service by default which would - // still produce the ENOTFOUND error. - assert.ok(err.code === 'ENOTFOUND' || err.code === 'EAI_AGAIN'); + assert.strictEqual(err.code, mockedErrorCode); assert.strictEqual(err.host, err.hostname); - assert.strictEqual(err.host, 'this.hostname.is.invalid'); - assert.strictEqual(err.syscall, 'getaddrinfo'); + assert.strictEqual(err.host, addresses.INVALID_HOST); + assert.strictEqual(err.syscall, mockedSysCall); })); client.end(); From 53b702fdbabb0ac0625d3d287237927b55ee48c1 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 22 Nov 2017 14:39:38 -0800 Subject: [PATCH 059/218] test: remove common.PORT from parallel tests `common.PORT` should not be used in parallel tests because another test may experience a collision with `common.PORT` when using port 0 to get an open port. This has been observed to result in test failures in CI. PR-URL: https://github.com/nodejs/node/pull/17410 Reviewed-By: Jon Moss Reviewed-By: Colin Ihrig Reviewed-By: Lance Ball Reviewed-By: Luigi Pinca Reviewed-By: Yuta Hiroto Reviewed-By: Michael Dawson --- test/async-hooks/test-graph.tcp.js | 2 +- test/async-hooks/test-graph.tls-write.js | 4 ++-- test/async-hooks/test-tcpwrap.js | 2 +- test/async-hooks/test-writewrap.js | 4 ++-- test/parallel/test-net-connect-immediate-finish.js | 4 ++-- test/{parallel => sequential}/test-tls-connect.js | 0 test/{parallel => sequential}/test-tls-lookup.js | 0 7 files changed, 8 insertions(+), 8 deletions(-) rename test/{parallel => sequential}/test-tls-connect.js (100%) rename test/{parallel => sequential}/test-tls-lookup.js (100%) diff --git a/test/async-hooks/test-graph.tcp.js b/test/async-hooks/test-graph.tcp.js index c2458ef1def769..c2253486ee651e 100644 --- a/test/async-hooks/test-graph.tcp.js +++ b/test/async-hooks/test-graph.tcp.js @@ -15,7 +15,7 @@ const server = net .createServer(common.mustCall(onconnection)) .on('listening', common.mustCall(onlistening)); -server.listen(common.PORT); +server.listen(0); net.connect({ port: server.address().port, host: '::1' }, common.mustCall(onconnected)); diff --git a/test/async-hooks/test-graph.tls-write.js b/test/async-hooks/test-graph.tls-write.js index 0c725d153d731b..26fe1ce41e955c 100644 --- a/test/async-hooks/test-graph.tls-write.js +++ b/test/async-hooks/test-graph.tls-write.js @@ -25,14 +25,14 @@ const server = tls }) .on('listening', common.mustCall(onlistening)) .on('secureConnection', common.mustCall(onsecureConnection)) - .listen(common.PORT); + .listen(0); function onlistening() { // // Creating client and connecting it to server // tls - .connect(common.PORT, { rejectUnauthorized: false }) + .connect(server.address().port, { rejectUnauthorized: false }) .on('secureConnect', common.mustCall(onsecureConnect)); } diff --git a/test/async-hooks/test-tcpwrap.js b/test/async-hooks/test-tcpwrap.js index e7d879caf70551..f3100aba0f0aaa 100644 --- a/test/async-hooks/test-tcpwrap.js +++ b/test/async-hooks/test-tcpwrap.js @@ -24,7 +24,7 @@ const server = net // Calling server.listen creates a TCPWRAP synchronously { - server.listen(common.PORT); + server.listen(0); const tcpsservers = hooks.activitiesOfTypes('TCPSERVERWRAP'); const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP'); assert.strictEqual(tcpsservers.length, 1); diff --git a/test/async-hooks/test-writewrap.js b/test/async-hooks/test-writewrap.js index 65f7b6175fb63a..d349f635665ddd 100644 --- a/test/async-hooks/test-writewrap.js +++ b/test/async-hooks/test-writewrap.js @@ -23,7 +23,7 @@ const server = tls }) .on('listening', common.mustCall(onlistening)) .on('secureConnection', common.mustCall(onsecureConnection)) - .listen(common.PORT); + .listen(0); assert.strictEqual(hooks.activitiesOfTypes('WRITEWRAP').length, 0); @@ -33,7 +33,7 @@ function onlistening() { // Creating client and connecting it to server // tls - .connect(common.PORT, { rejectUnauthorized: false }) + .connect(server.address().port, { rejectUnauthorized: false }) .on('secureConnect', common.mustCall(onsecureConnect)); assert.strictEqual(hooks.activitiesOfTypes('WRITEWRAP').length, 0); diff --git a/test/parallel/test-net-connect-immediate-finish.js b/test/parallel/test-net-connect-immediate-finish.js index 9adf8c31128b00..27d988ab5af45f 100644 --- a/test/parallel/test-net-connect-immediate-finish.js +++ b/test/parallel/test-net-connect-immediate-finish.js @@ -38,9 +38,9 @@ const { const client = net.connect({ host: addresses.INVALID_HOST, - port: common.PORT, + port: 80, // port number doesn't matter because host name is invalid lookup: common.mustCall(errorLookupMock()) -}); +}, common.mustNotCall()); client.once('error', common.mustCall((err) => { assert(err); diff --git a/test/parallel/test-tls-connect.js b/test/sequential/test-tls-connect.js similarity index 100% rename from test/parallel/test-tls-connect.js rename to test/sequential/test-tls-connect.js diff --git a/test/parallel/test-tls-lookup.js b/test/sequential/test-tls-lookup.js similarity index 100% rename from test/parallel/test-tls-lookup.js rename to test/sequential/test-tls-lookup.js From c97237bc104066ae1f3a8e5465dbc74a7fb4b297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Thu, 5 Apr 2018 10:43:42 +0200 Subject: [PATCH 060/218] deps: cherry-pick a4bddba from upstream V8 Original commit message: [Runtime] Use platform specific value for JSReceiver::HashMask This allows us to remove the loop while calculating the hash value and just use the HashMask as the mask for ComputeIntegerHash. This previously overflowed on 32-bit systems failing the Smi::IsValid check. Bug: v8:6404 Change-Id: I84610a7592fa9d7ce4fa5cef7903bd50b8e8a4df Reviewed-on: https://chromium-review.googlesource.com/702675 Reviewed-by: Adam Klein Commit-Queue: Sathya Gunasekaran Cr-Commit-Position: refs/heads/master@{#48319} PR-URL: https://github.com/nodejs/node/pull/19824 Refs: https://github.com/v8/v8/commit/a4bddba0b0dac116d987eea28479dba14663cda0 Fixes: https://github.com/nodejs/node/issues/19769 Reviewed-By: Yang Guo Reviewed-By: Gibson Fahnestock --- deps/v8/include/v8-version.h | 2 +- deps/v8/src/objects.cc | 9 ++------- deps/v8/src/objects.h | 5 +++++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 25ce5d071e71c3..ce000d06c11659 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 6 #define V8_MINOR_VERSION 2 #define V8_BUILD_NUMBER 414 -#define V8_PATCH_LEVEL 50 +#define V8_PATCH_LEVEL 51 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 28c1cd681ffd46..d9d00e058f4ee0 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -6368,13 +6368,8 @@ Smi* JSObject::GetOrCreateIdentityHash(Isolate* isolate) { return Smi::cast(hash_obj); } - int masked_hash; - // TODO(gsathya): Remove the loop and pass kHashMask directly to - // GenerateIdentityHash. - do { - int hash = isolate->GenerateIdentityHash(Smi::kMaxValue); - masked_hash = hash & JSReceiver::kHashMask; - } while (masked_hash == PropertyArray::kNoHashSentinel); + int masked_hash = isolate->GenerateIdentityHash(JSReceiver::kHashMask); + DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); SetIdentityHash(masked_hash); return Smi::FromInt(masked_hash); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index f9987c2837c466..5456bfc47f6027 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1954,8 +1954,13 @@ class PropertyArray : public HeapObject { typedef BodyDescriptor BodyDescriptorWeak; static const int kLengthMask = 0x3ff; +#if V8_TARGET_ARCH_64_BIT static const int kHashMask = 0x7ffffc00; STATIC_ASSERT(kLengthMask + kHashMask == 0x7fffffff); +#else + static const int kHashMask = 0x3ffffc00; + STATIC_ASSERT(kLengthMask + kHashMask == 0x3fffffff); +#endif static const int kMaxLength = kLengthMask; STATIC_ASSERT(kMaxLength > kMaxNumberOfDescriptors); From 09f5e252bfbeb2425a9692c25665dca97c1a61f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Thu, 5 Apr 2018 10:43:58 +0200 Subject: [PATCH 061/218] deps: cherry-pick 7abdadc from upstream V8 Original commit message: Sprinkle some DisallowHeapAllocation Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: I7d34ccddeea08f5935e360e8c36791365f27f89e Reviewed-on: https://chromium-review.googlesource.com/647706 Reviewed-by: Michael Lippautz Commit-Queue: Camillo Bruni Cr-Commit-Position: refs/heads/master@{#47804} PR-URL: https://github.com/nodejs/node/pull/19824 Refs: https://github.com/v8/v8/commit/7abdadca0e3e5a780647b8ade0c586c2e7583f9c Fixes: https://github.com/nodejs/node/issues/19769 Reviewed-By: Yang Guo Reviewed-By: Gibson Fahnestock --- deps/v8/include/v8-version.h | 2 +- deps/v8/src/api.cc | 1 + deps/v8/src/objects.cc | 11 ++++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index ce000d06c11659..f2a716cfdfe970 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 6 #define V8_MINOR_VERSION 2 #define V8_BUILD_NUMBER 414 -#define V8_PATCH_LEVEL 51 +#define V8_PATCH_LEVEL 52 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 078796f5dbb4f2..76b095eb422712 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -5190,6 +5190,7 @@ Local v8::Object::CreationContext() { int v8::Object::GetIdentityHash() { + i::DisallowHeapAllocation no_gc; auto isolate = Utils::OpenHandle(this)->GetIsolate(); i::HandleScope scope(isolate); auto self = Utils::OpenHandle(this); diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index d9d00e058f4ee0..b923864cb5f60e 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -2301,6 +2301,7 @@ namespace { // objects. This avoids a double lookup in the cases where we know we will // add the hash to the JSObject if it does not already exist. Object* GetSimpleHash(Object* object) { + DisallowHeapAllocation no_gc; // The object is either a Smi, a HeapNumber, a name, an odd-ball, a real JS // object, or a Harmony proxy. if (object->IsSmi()) { @@ -2333,10 +2334,10 @@ Object* GetSimpleHash(Object* object) { } // namespace Object* Object::GetHash() { + DisallowHeapAllocation no_gc; Object* hash = GetSimpleHash(this); if (hash->IsSmi()) return hash; - DisallowHeapAllocation no_gc; DCHECK(IsJSReceiver()); JSReceiver* receiver = JSReceiver::cast(this); Isolate* isolate = receiver->GetIsolate(); @@ -2345,10 +2346,12 @@ Object* Object::GetHash() { // static Smi* Object::GetOrCreateHash(Isolate* isolate, Object* key) { + DisallowHeapAllocation no_gc; return key->GetOrCreateHash(isolate); } Smi* Object::GetOrCreateHash(Isolate* isolate) { + DisallowHeapAllocation no_gc; Object* hash = GetSimpleHash(this); if (hash->IsSmi()) return Smi::cast(hash); @@ -6286,6 +6289,7 @@ Object* SetHashAndUpdateProperties(HeapObject* properties, int masked_hash) { } int GetIdentityHashHelper(Isolate* isolate, JSReceiver* object) { + DisallowHeapAllocation no_gc; Object* properties = object->raw_properties_or_hash(); if (properties->IsSmi()) { return Smi::ToInt(properties); @@ -6312,6 +6316,7 @@ int GetIdentityHashHelper(Isolate* isolate, JSReceiver* object) { } // namespace void JSReceiver::SetIdentityHash(int masked_hash) { + DisallowHeapAllocation no_gc; DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); @@ -6322,6 +6327,7 @@ void JSReceiver::SetIdentityHash(int masked_hash) { } void JSReceiver::SetProperties(HeapObject* properties) { + DisallowHeapAllocation no_gc; Isolate* isolate = properties->GetIsolate(); int hash = GetIdentityHashHelper(isolate, this); Object* new_properties = properties; @@ -6337,6 +6343,7 @@ void JSReceiver::SetProperties(HeapObject* properties) { template Smi* GetOrCreateIdentityHashHelper(Isolate* isolate, ProxyType* proxy) { + DisallowHeapAllocation no_gc; Object* maybe_hash = proxy->hash(); if (maybe_hash->IsSmi()) return Smi::cast(maybe_hash); @@ -6346,6 +6353,7 @@ Smi* GetOrCreateIdentityHashHelper(Isolate* isolate, ProxyType* proxy) { } Object* JSObject::GetIdentityHash(Isolate* isolate) { + DisallowHeapAllocation no_gc; if (IsJSGlobalProxy()) { return JSGlobalProxy::cast(this)->hash(); } @@ -6359,6 +6367,7 @@ Object* JSObject::GetIdentityHash(Isolate* isolate) { } Smi* JSObject::GetOrCreateIdentityHash(Isolate* isolate) { + DisallowHeapAllocation no_gc; if (IsJSGlobalProxy()) { return GetOrCreateIdentityHashHelper(isolate, JSGlobalProxy::cast(this)); } From f12db24947c88b734e93abd0846d1fbc104a7190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Thu, 5 Apr 2018 10:44:08 +0200 Subject: [PATCH 062/218] deps: cherry-pick a803fad from upstream V8 Original commit message: Make sure the identity hash is uniform (at least in the lower bits). In the current implementation of hash code for objects (identity hash), we do not bother to shift the hash when we retrieve it from the hash-length bitfield in a property array. (Even worse, we store shifted value even if we do not have property array or inside dictionaries.) That means that the hash-code for objects is always divisible by 1024. Since our hash table uses a simple masking with (2^logsize - 1) to obtain the bucket, we get terrible hash collisions - essentially, our hash table degenerates to a linked list for fewer than 1024 elements. This CL always shifts the hash code so that the value in the lowest 21 bits is uniformly distributed. This results in big improvements on medium to large hash tables. A program storing 1M elements into a WeakMap gets roughly 17x faster. A program retrieving 1M elements from a Map improves even more dramatically (>100x). const a = []; for (let i = 0; i < 1e6; i++) a[i] = {}; const m = new Map(); console.time("Map.set"); for (let i = 0; i < 1e6; i++) { m.set(a[i], i); } console.timeEnd("Map.set"); console.time("Map.get"); let s = 0; for (let i = 0; i < 1e6; i++) { s += m.get(a[i]); } console.timeEnd("Map.get"); const w = new WeakMap(); console.time("WeakMap.set"); for (let i = 0; i < 1e6; i++) { w.set(a[i], i); } console.timeEnd("WeakMap.set"); Before the fix: Map.set: 157.575000 Map.get: 28333.182000 WeakMap.set: 6923.826000 After the fix: Map.set: 178.382000 Map.get: 185.930000 WeakMap.set: 409.529000 Note that Map does not suffer from the hash collision on insertion because it uses chaining (insertion into linked list is fast regardless of size!), and we cleverly avoid lookup in the hash table on update if the key does not have identity hash yet. This is in contrast to the WeakMap, which uses open-addressing, and deals with collisions on insertion. Bug: v8:6916 Change-Id: Ic5497bd4501e3b767b3f4acb7efb4784cbb3a2e4 Reviewed-on: https://chromium-review.googlesource.com/713616 Reviewed-by: Benedikt Meurer Commit-Queue: Benedikt Meurer Cr-Commit-Position: refs/heads/master@{#48480} PR-URL: https://github.com/nodejs/node/pull/19824 Refs: https://github.com/v8/v8/commit/a803fad068211ab6c445f485130dc52cbeadfd52 Fixes: https://github.com/nodejs/node/issues/19769 Reviewed-By: Yang Guo Reviewed-By: Gibson Fahnestock --- deps/v8/include/v8-version.h | 2 +- deps/v8/src/code-stub-assembler.cc | 7 +++-- .../js-native-context-specialization.cc | 8 ++++-- deps/v8/src/ic/accessor-assembler.cc | 18 +++++++----- deps/v8/src/objects-inl.h | 14 ++++------ deps/v8/src/objects.cc | 28 +++++++++---------- deps/v8/src/objects.h | 17 ++++------- deps/v8/src/objects/dictionary.h | 10 ++++--- deps/v8/test/cctest/test-weakmaps.cc | 2 +- 9 files changed, 54 insertions(+), 52 deletions(-) diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index f2a716cfdfe970..6d7eca8ab572ab 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 6 #define V8_MINOR_VERSION 2 #define V8_BUILD_NUMBER 414 -#define V8_PATCH_LEVEL 52 +#define V8_PATCH_LEVEL 53 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/code-stub-assembler.cc b/deps/v8/src/code-stub-assembler.cc index 35261955db2576..915f507b12689c 100644 --- a/deps/v8/src/code-stub-assembler.cc +++ b/deps/v8/src/code-stub-assembler.cc @@ -1187,8 +1187,8 @@ TNode CodeStubAssembler::LoadHashForJSObject( { Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField( properties_or_hash, PropertyArray::kLengthAndHashOffset); - var_hash.Bind(Word32And(length_and_hash_int32, - Int32Constant(PropertyArray::kHashMask))); + var_hash.Bind( + DecodeWord32(length_and_hash_int32)); Goto(&done); } @@ -2508,7 +2508,8 @@ void CodeStubAssembler::InitializePropertyArrayLength(Node* property_array, CSA_ASSERT( this, IntPtrOrSmiLessThanOrEqual( - length, IntPtrOrSmiConstant(PropertyArray::kMaxLength, mode), mode)); + length, IntPtrOrSmiConstant(PropertyArray::LengthField::kMax, mode), + mode)); StoreObjectFieldNoWriteBarrier( property_array, PropertyArray::kLengthAndHashOffset, ParameterToTagged(length, mode), MachineRepresentation::kTaggedSigned); diff --git a/deps/v8/src/compiler/js-native-context-specialization.cc b/deps/v8/src/compiler/js-native-context-specialization.cc index dbe3fc9608216b..57d23589ff4f53 100644 --- a/deps/v8/src/compiler/js-native-context-specialization.cc +++ b/deps/v8/src/compiler/js-native-context-specialization.cc @@ -2255,14 +2255,18 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore( jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel)); hash = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), hash, control); + hash = + graph()->NewNode(simplified()->NumberShiftLeft(), hash, + jsgraph()->Constant(PropertyArray::HashField::kShift)); } else { hash = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()), properties, effect, control); effect = graph()->NewNode( common()->BeginRegion(RegionObservability::kNotObservable), effect); - hash = graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, - jsgraph()->Constant(JSReceiver::kHashMask)); + hash = + graph()->NewNode(simplified()->NumberBitwiseAnd(), hash, + jsgraph()->Constant(PropertyArray::HashField::kMask)); } Node* new_length_and_hash = graph()->NewNode( diff --git a/deps/v8/src/ic/accessor-assembler.cc b/deps/v8/src/ic/accessor-assembler.cc index 1dee1303366e74..8985bad15e9f64 100644 --- a/deps/v8/src/ic/accessor-assembler.cc +++ b/deps/v8/src/ic/accessor-assembler.cc @@ -1091,7 +1091,7 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, // TODO(gsathya): Clean up the type conversions by creating smarter // helpers that do the correct op based on the mode. VARIABLE(var_properties, MachineRepresentation::kTaggedPointer); - VARIABLE(var_hash, MachineRepresentation::kWord32); + VARIABLE(var_encoded_hash, MachineRepresentation::kWord32); VARIABLE(var_length, ParameterRepresentation(mode)); Node* properties = LoadObjectField(object, JSObject::kPropertiesOrHashOffset); @@ -1102,7 +1102,10 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, BIND(&if_smi_hash); { - var_hash.Bind(SmiToWord32(properties)); + Node* hash = SmiToWord32(properties); + Node* encoded_hash = + Word32Shl(hash, Int32Constant(PropertyArray::HashField::kShift)); + var_encoded_hash.Bind(encoded_hash); var_length.Bind(IntPtrOrSmiConstant(0, mode)); var_properties.Bind(EmptyFixedArrayConstant()); Goto(&extend_store); @@ -1112,10 +1115,11 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, { Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField( var_properties.value(), PropertyArray::kLengthAndHashOffset); - var_hash.Bind(Word32And(length_and_hash_int32, - Int32Constant(PropertyArray::kHashMask))); - Node* length_intptr = ChangeInt32ToIntPtr(Word32And( - length_and_hash_int32, Int32Constant(PropertyArray::kLengthMask))); + var_encoded_hash.Bind(Word32And( + length_and_hash_int32, Int32Constant(PropertyArray::HashField::kMask))); + Node* length_intptr = ChangeInt32ToIntPtr( + Word32And(length_and_hash_int32, + Int32Constant(PropertyArray::LengthField::kMask))); Node* length = WordToParameter(length_intptr, mode); var_length.Bind(length); Goto(&extend_store); @@ -1161,7 +1165,7 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object, Node* new_capacity_int32 = TruncateWordToWord32(ParameterToWord(new_capacity, mode)); Node* new_length_and_hash_int32 = - Word32Or(var_hash.value(), new_capacity_int32); + Word32Or(var_encoded_hash.value(), new_capacity_int32); StoreObjectField(new_properties, PropertyArray::kLengthAndHashOffset, SmiFromWord32(new_length_and_hash_int32)); StoreObjectField(object, JSObject::kPropertiesOrHashOffset, new_properties); diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 82b7eb05a6e7b4..91fd08a2dd58c8 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -2679,33 +2679,31 @@ SYNCHRONIZED_SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset) int PropertyArray::length() const { Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - return value & kLengthMask; + return LengthField::decode(value); } void PropertyArray::initialize_length(int len) { SLOW_DCHECK(len >= 0); - SLOW_DCHECK(len < kMaxLength); + SLOW_DCHECK(len < LengthField::kMax); WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(len)); } int PropertyArray::synchronized_length() const { Object* value_obj = ACQUIRE_READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - return value & kLengthMask; + return LengthField::decode(value); } int PropertyArray::Hash() const { Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - int hash = value & kHashMask; - return hash; + return HashField::decode(value); } -void PropertyArray::SetHash(int masked_hash) { - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); +void PropertyArray::SetHash(int hash) { Object* value_obj = READ_FIELD(this, kLengthAndHashOffset); int value = Smi::ToInt(value_obj); - value = (value & kLengthMask) | masked_hash; + value = HashField::update(value, hash); WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(value)); } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index b923864cb5f60e..b2b23c1f68f216 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -6269,22 +6269,22 @@ Handle JSObject::NormalizeElements( namespace { -Object* SetHashAndUpdateProperties(HeapObject* properties, int masked_hash) { - DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); +Object* SetHashAndUpdateProperties(HeapObject* properties, int hash) { + DCHECK_NE(PropertyArray::kNoHashSentinel, hash); + DCHECK(PropertyArray::HashField::is_valid(hash)); if (properties == properties->GetHeap()->empty_fixed_array() || properties == properties->GetHeap()->empty_property_dictionary()) { - return Smi::FromInt(masked_hash); + return Smi::FromInt(hash); } if (properties->IsPropertyArray()) { - PropertyArray::cast(properties)->SetHash(masked_hash); + PropertyArray::cast(properties)->SetHash(hash); return properties; } DCHECK(properties->IsDictionary()); - NameDictionary::cast(properties)->SetHash(masked_hash); + NameDictionary::cast(properties)->SetHash(hash); return properties; } @@ -6315,14 +6315,14 @@ int GetIdentityHashHelper(Isolate* isolate, JSReceiver* object) { } } // namespace -void JSReceiver::SetIdentityHash(int masked_hash) { +void JSReceiver::SetIdentityHash(int hash) { DisallowHeapAllocation no_gc; - DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); + DCHECK_NE(PropertyArray::kNoHashSentinel, hash); + DCHECK(PropertyArray::HashField::is_valid(hash)); HeapObject* existing_properties = HeapObject::cast(raw_properties_or_hash()); Object* new_properties = - SetHashAndUpdateProperties(existing_properties, masked_hash); + SetHashAndUpdateProperties(existing_properties, hash); set_raw_properties_or_hash(new_properties); } @@ -6377,11 +6377,11 @@ Smi* JSObject::GetOrCreateIdentityHash(Isolate* isolate) { return Smi::cast(hash_obj); } - int masked_hash = isolate->GenerateIdentityHash(JSReceiver::kHashMask); - DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash); + int hash = isolate->GenerateIdentityHash(PropertyArray::HashField::kMax); + DCHECK_NE(PropertyArray::kNoHashSentinel, hash); - SetIdentityHash(masked_hash); - return Smi::FromInt(masked_hash); + SetIdentityHash(hash); + return Smi::FromInt(hash); } Object* JSProxy::GetIdentityHash() { return hash(); } diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 5456bfc47f6027..00a8d0da02220e 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1953,17 +1953,10 @@ class PropertyArray : public HeapObject { // No weak fields. typedef BodyDescriptor BodyDescriptorWeak; - static const int kLengthMask = 0x3ff; -#if V8_TARGET_ARCH_64_BIT - static const int kHashMask = 0x7ffffc00; - STATIC_ASSERT(kLengthMask + kHashMask == 0x7fffffff); -#else - static const int kHashMask = 0x3ffffc00; - STATIC_ASSERT(kLengthMask + kHashMask == 0x3fffffff); -#endif - - static const int kMaxLength = kLengthMask; - STATIC_ASSERT(kMaxLength > kMaxNumberOfDescriptors); + static const int kLengthFieldSize = 10; + class LengthField : public BitField {}; + class HashField : public BitField {}; static const int kNoHashSentinel = 0; @@ -2190,7 +2183,7 @@ class JSReceiver: public HeapObject { MUST_USE_RESULT static MaybeHandle GetOwnEntries( Handle object, PropertyFilter filter); - static const int kHashMask = PropertyArray::kHashMask; + static const int kHashMask = PropertyArray::HashField::kMask; // Layout description. static const int kPropertiesOrHashOffset = HeapObject::kHeaderSize; diff --git a/deps/v8/src/objects/dictionary.h b/deps/v8/src/objects/dictionary.h index a989c8fc8a2e53..09fdb9eb0764a2 100644 --- a/deps/v8/src/objects/dictionary.h +++ b/deps/v8/src/objects/dictionary.h @@ -138,14 +138,16 @@ class BaseNameDictionary : public Dictionary { return Smi::ToInt(this->get(kNextEnumerationIndexIndex)); } - void SetHash(int masked_hash) { - DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash); - this->set(kObjectHashIndex, Smi::FromInt(masked_hash)); + void SetHash(int hash) { + DCHECK(PropertyArray::HashField::is_valid(hash)); + this->set(kObjectHashIndex, Smi::FromInt(hash)); } int Hash() const { Object* hash_obj = this->get(kObjectHashIndex); - return Smi::ToInt(hash_obj); + int hash = Smi::ToInt(hash_obj); + DCHECK(PropertyArray::HashField::is_valid(hash)); + return hash; } // Creates a new dictionary. diff --git a/deps/v8/test/cctest/test-weakmaps.cc b/deps/v8/test/cctest/test-weakmaps.cc index 13a0c538d9b82f..f645234144c092 100644 --- a/deps/v8/test/cctest/test-weakmaps.cc +++ b/deps/v8/test/cctest/test-weakmaps.cc @@ -191,7 +191,7 @@ TEST(Regress2060a) { Handle object = factory->NewJSObject(function, TENURED); CHECK(!heap->InNewSpace(*object)); CHECK(!first_page->Contains(object->address())); - int32_t hash = object->GetOrCreateHash(isolate)->value(); + int32_t hash = key->GetOrCreateHash(isolate)->value(); JSWeakCollection::Set(weakmap, key, object, hash); } } From 2019b023a7184f07826e2a63f12859d0684ea3ec Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 29 Oct 2017 01:36:18 +0200 Subject: [PATCH 063/218] lib: refactor ES module loader for readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/16579 Backport-PR-URL: https://github.com/nodejs/node/pull/18085 Reviewed-By: Michaël Zasso Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- doc/api/errors.md | 22 ++++++ lib/internal/errors.js | 3 + lib/internal/loader/Loader.js | 45 ++++++++++-- lib/internal/loader/ModuleJob.js | 111 +++++++++++++++--------------- lib/internal/loader/ModuleWrap.js | 52 ++++++++------ 5 files changed, 151 insertions(+), 82 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index f2de6717a57ee2..e895accada72f2 100755 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -981,6 +981,28 @@ strict compliance with the API specification (which in some cases may accept `func(undefined)` and `func()` are treated identically, and the [`ERR_INVALID_ARG_TYPE`][] error code may be used instead. + +### ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK + +> Stability: 1 - Experimental + +Used when an [ES6 module][] loader hook specifies `format: 'dynamic` but does +not provide a `dynamicInstantiate` hook. + + +### ERR_MISSING_MODULE + +> Stability: 1 - Experimental + +Used when an [ES6 module][] cannot be resolved. + + +### ERR_MODULE_RESOLUTION_LEGACY + +> Stability: 1 - Experimental + +Used when a failure occurs resolving imports in an [ES6 module][]. + ### ERR_NAPI_CONS_FUNCTION diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 9a1bc0ee38d519..ea5526c795343c 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -257,6 +257,9 @@ E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected'); E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe'); E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks'); E('ERR_MISSING_ARGS', missingArgs); +E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', + 'The ES Module loader may not return a format of \'dynamic\' when no ' + + 'dynamicInstantiate function was provided'); E('ERR_MISSING_MODULE', 'Cannot find module %s'); E('ERR_MODULE_RESOLUTION_LEGACY', '%s not found by import in %s.' + ' Legacy behavior in require() would have found it at %s'); diff --git a/lib/internal/loader/Loader.js b/lib/internal/loader/Loader.js index f2c7fa0cfffc47..49c8699771e819 100644 --- a/lib/internal/loader/Loader.js +++ b/lib/internal/loader/Loader.js @@ -10,7 +10,8 @@ const ModuleRequest = require('internal/loader/ModuleRequest'); const errors = require('internal/errors'); const debug = require('util').debuglog('esm'); -function getBase() { +// Returns a file URL for the current working directory. +function getURLStringForCwd() { try { return getURLFromFilePath(`${process.cwd()}/`).href; } catch (e) { @@ -23,22 +24,44 @@ function getBase() { } } +/* A Loader instance is used as the main entry point for loading ES modules. + * Currently, this is a singleton -- there is only one used for loading + * the main module and everything in its dependency graph. */ class Loader { - constructor(base = getBase()) { - this.moduleMap = new ModuleMap(); + constructor(base = getURLStringForCwd()) { if (typeof base !== 'string') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'base', 'string'); } + + this.moduleMap = new ModuleMap(); this.base = base; - this.resolver = ModuleRequest.resolve.bind(null); + // The resolver has the signature + // (specifier : string, parentURL : string, defaultResolve) + // -> Promise<{ url : string, + // format: anything in Loader.validFormats }> + // where defaultResolve is ModuleRequest.resolve (having the same + // signature itself). + // If `.format` on the returned value is 'dynamic', .dynamicInstantiate + // will be used as described below. + this.resolver = ModuleRequest.resolve; + // This hook is only called when resolve(...).format is 'dynamic' and has + // the signature + // (url : string) -> Promise<{ exports: { ... }, execute: function }> + // Where `exports` is an object whose property names define the exported + // names of the generated module. `execute` is a function that receives + // an object with the same keys as `exports`, whose values are get/set + // functions for the actual exported values. this.dynamicInstantiate = undefined; } hook({ resolve = ModuleRequest.resolve, dynamicInstantiate }) { + // Use .bind() to avoid giving access to the Loader instance when it is + // called as this.resolver(...); this.resolver = resolve.bind(null); this.dynamicInstantiate = dynamicInstantiate; } + // Typechecking wrapper around .resolver(). async resolve(specifier, parentURL = this.base) { if (typeof parentURL !== 'string') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', @@ -48,10 +71,11 @@ class Loader { const { url, format } = await this.resolver(specifier, parentURL, ModuleRequest.resolve); - if (typeof format !== 'string') { + if (!Loader.validFormats.includes(format)) { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'format', - ['esm', 'cjs', 'builtin', 'addon', 'json']); + Loader.validFormats); } + if (typeof url !== 'string') { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'string'); } @@ -72,14 +96,20 @@ class Loader { return { url, format }; } + // May create a new ModuleJob instance if one did not already exist. async getModuleJob(specifier, parentURL = this.base) { const { url, format } = await this.resolve(specifier, parentURL); let job = this.moduleMap.get(url); if (job === undefined) { let loaderInstance; if (format === 'dynamic') { + const { dynamicInstantiate } = this; + if (typeof dynamicInstantiate !== 'function') { + throw new errors.Error('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK'); + } + loaderInstance = async (url) => { - const { exports, execute } = await this.dynamicInstantiate(url); + const { exports, execute } = await dynamicInstantiate(url); return createDynamicModule(exports, url, (reflect) => { debug(`Loading custom loader ${url}`); execute(reflect.exports); @@ -100,5 +130,6 @@ class Loader { return module.namespace(); } } +Loader.validFormats = ['esm', 'cjs', 'builtin', 'addon', 'json', 'dynamic']; Object.setPrototypeOf(Loader.prototype, null); module.exports = Loader; diff --git a/lib/internal/loader/ModuleJob.js b/lib/internal/loader/ModuleJob.js index 04d6111b87f1f6..77c89f6230e5de 100644 --- a/lib/internal/loader/ModuleJob.js +++ b/lib/internal/loader/ModuleJob.js @@ -1,27 +1,36 @@ 'use strict'; +const { ModuleWrap } = + require('internal/process').internalBinding('module_wrap'); const { SafeSet, SafePromise } = require('internal/safe_globals'); +const assert = require('assert'); const resolvedPromise = SafePromise.resolve(); +const enableDebug = (process.env.NODE_DEBUG || '').match(/\besm\b/) || + process.features.debug; + +/* A ModuleJob tracks the loading of a single Module, and the ModuleJobs of + * its dependencies, over time. */ class ModuleJob { - /** - * @param {module: ModuleWrap?, compiled: Promise} moduleProvider - */ + // `loader` is the Loader instance used for loading dependencies. + // `moduleProvider` is a function constructor(loader, url, moduleProvider) { this.loader = loader; this.error = null; this.hadError = false; - // linked == promise for dependency jobs, with module populated, - // module wrapper linked - this.moduleProvider = moduleProvider; - this.modulePromise = this.moduleProvider(url); + // This is a Promise<{ module, reflect }>, whose fields will be copied + // onto `this` by `link()` below once it has been resolved. + this.modulePromise = moduleProvider(url); this.module = undefined; this.reflect = undefined; - const linked = async () => { + + // Wait for the ModuleWrap instance being linked with all dependencies. + const link = async () => { const dependencyJobs = []; ({ module: this.module, reflect: this.reflect } = await this.modulePromise); + assert(this.module instanceof ModuleWrap); this.module.link(async (dependencySpecifier) => { const dependencyJobPromise = this.loader.getModuleJob(dependencySpecifier, url); @@ -29,63 +38,57 @@ class ModuleJob { const dependencyJob = await dependencyJobPromise; return (await dependencyJob.modulePromise).module; }); + if (enableDebug) { + // Make sure all dependencies are entered into the list synchronously. + Object.freeze(dependencyJobs); + } return SafePromise.all(dependencyJobs); }; - this.linked = linked(); + // Promise for the list of all dependencyJobs. + this.linked = link(); // instantiated == deep dependency jobs wrappers instantiated, // module wrapper instantiated this.instantiated = undefined; } - instantiate() { + async instantiate() { if (this.instantiated) { return this.instantiated; } - return this.instantiated = new Promise(async (resolve, reject) => { - const jobsInGraph = new SafeSet(); - let jobsReadyToInstantiate = 0; - // (this must be sync for counter to work) - const queueJob = (moduleJob) => { - if (jobsInGraph.has(moduleJob)) { - return; - } - jobsInGraph.add(moduleJob); - moduleJob.linked.then((dependencyJobs) => { - for (const dependencyJob of dependencyJobs) { - queueJob(dependencyJob); - } - checkComplete(); - }, (e) => { - if (!this.hadError) { - this.error = e; - this.hadError = true; - } - checkComplete(); - }); - }; - const checkComplete = () => { - if (++jobsReadyToInstantiate === jobsInGraph.size) { - // I believe we only throw once the whole tree is finished loading? - // or should the error bail early, leaving entire tree to still load? - if (this.hadError) { - reject(this.error); - } else { - try { - this.module.instantiate(); - for (const dependencyJob of jobsInGraph) { - dependencyJob.instantiated = resolvedPromise; - } - resolve(this.module); - } catch (e) { - e.stack; - reject(e); - } - } - } - }; - queueJob(this); - }); + return this.instantiated = this._instantiate(); + } + + // This method instantiates the module associated with this job and its + // entire dependency graph, i.e. creates all the module namespaces and the + // exported/imported variables. + async _instantiate() { + const jobsInGraph = new SafeSet(); + + const addJobsToDependencyGraph = async (moduleJob) => { + if (jobsInGraph.has(moduleJob)) { + return; + } + jobsInGraph.add(moduleJob); + const dependencyJobs = await moduleJob.linked; + return Promise.all(dependencyJobs.map(addJobsToDependencyGraph)); + }; + try { + await addJobsToDependencyGraph(this); + } catch (e) { + if (!this.hadError) { + this.error = e; + this.hadError = true; + } + throw e; + } + this.module.instantiate(); + for (const dependencyJob of jobsInGraph) { + // Calling `this.module.instantiate()` instantiates not only the + // ModuleWrap in this module, but all modules in the graph. + dependencyJob.instantiated = resolvedPromise; + } + return this.module; } async run() { diff --git a/lib/internal/loader/ModuleWrap.js b/lib/internal/loader/ModuleWrap.js index c97b4888ea22ce..0ee05ca81ffbb9 100644 --- a/lib/internal/loader/ModuleWrap.js +++ b/lib/internal/loader/ModuleWrap.js @@ -11,39 +11,49 @@ const createDynamicModule = (exports, url = '', evaluate) => { `creating ESM facade for ${url} with exports: ${ArrayJoin(exports, ', ')}` ); const names = ArrayMap(exports, (name) => `${name}`); - // sanitized ESM for reflection purposes - const src = `export let executor; - ${ArrayJoin(ArrayMap(names, (name) => `export let $${name}`), ';\n')} - ;(() => [ - fn => executor = fn, - { exports: { ${ - ArrayJoin(ArrayMap(names, (name) => `${name}: { - get: () => $${name}, - set: v => $${name} = v - }`), ',\n') -} } } - ]); - `; + // Create two modules: One whose exports are get- and set-able ('reflective'), + // and one which re-exports all of these but additionally may + // run an executor function once everything is set up. + const src = ` + export let executor; + ${ArrayJoin(ArrayMap(names, (name) => `export let $${name};`), '\n')} + /* This function is implicitly returned as the module's completion value */ + (() => ({ + setExecutor: fn => executor = fn, + reflect: { + exports: { ${ + ArrayJoin(ArrayMap(names, (name) => ` + ${name}: { + get: () => $${name}, + set: v => $${name} = v + }`), ', \n')} + } + } + }));`; const reflectiveModule = new ModuleWrap(src, `cjs-facade:${url}`); reflectiveModule.instantiate(); - const [setExecutor, reflect] = reflectiveModule.evaluate()(); + const { setExecutor, reflect } = reflectiveModule.evaluate()(); // public exposed ESM - const reexports = `import { executor, + const reexports = ` + import { + executor, ${ArrayMap(names, (name) => `$${name}`)} } from ""; export { ${ArrayJoin(ArrayMap(names, (name) => `$${name} as ${name}`), ', ')} } - // add await to this later if top level await comes along - typeof executor === "function" ? executor() : void 0;`; + if (typeof executor === "function") { + // add await to this later if top level await comes along + executor() + }`; if (typeof evaluate === 'function') { setExecutor(() => evaluate(reflect)); } - const runner = new ModuleWrap(reexports, `${url}`); - runner.link(async () => reflectiveModule); - runner.instantiate(); + const module = new ModuleWrap(reexports, `${url}`); + module.link(async () => reflectiveModule); + module.instantiate(); return { - module: runner, + module, reflect }; }; From 9ac91b14de5df91b180ae98b1e682d9b2654bf9a Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 7 Mar 2018 19:15:25 +0100 Subject: [PATCH 064/218] src: fix util abort This makes sure util.isRegExp and util.isDate will not abort in case more than one argument is passed to the function. PR-URL: https://github.com/nodejs/node/pull/19224 Reviewed-By: Gibson Fahnestock --- src/node_util.cc | 2 -- test/parallel/test-util.js | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/node_util.cc b/src/node_util.cc index a40401b71926b3..1e51fbfff4b6ae 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -36,7 +36,6 @@ using v8::Value; #define V(_, ucname) \ static void ucname(const FunctionCallbackInfo& args) { \ - CHECK_EQ(1, args.Length()); \ args.GetReturnValue().Set(args[0]->ucname()); \ } @@ -44,7 +43,6 @@ using v8::Value; #undef V static void IsAnyArrayBuffer(const FunctionCallbackInfo& args) { - CHECK_EQ(1, args.Length()); args.GetReturnValue().Set( args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); } diff --git a/test/parallel/test-util.js b/test/parallel/test-util.js index 3b2729c107b4b1..a4aec080a9a0ad 100644 --- a/test/parallel/test-util.js +++ b/test/parallel/test-util.js @@ -43,9 +43,10 @@ assert.strictEqual(false, util.isArray(Object.create(Array.prototype))); // isRegExp assert.strictEqual(true, util.isRegExp(/regexp/)); -assert.strictEqual(true, util.isRegExp(RegExp())); +assert.strictEqual(true, util.isRegExp(RegExp(), 'foo')); assert.strictEqual(true, util.isRegExp(new RegExp())); assert.strictEqual(true, util.isRegExp(context('RegExp')())); +assert.strictEqual(false, util.isRegExp()); assert.strictEqual(false, util.isRegExp({})); assert.strictEqual(false, util.isRegExp([])); assert.strictEqual(false, util.isRegExp(new Date())); @@ -53,7 +54,7 @@ assert.strictEqual(false, util.isRegExp(Object.create(RegExp.prototype))); // isDate assert.strictEqual(true, util.isDate(new Date())); -assert.strictEqual(true, util.isDate(new Date(0))); +assert.strictEqual(true, util.isDate(new Date(0), 'foo')); assert.strictEqual(true, util.isDate(new (context('Date'))())); assert.strictEqual(false, util.isDate(Date())); assert.strictEqual(false, util.isDate({})); From 0071560eb4d2de8d38c3c5ed1b4334c950e011db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Thu, 8 Feb 2018 19:43:05 +0100 Subject: [PATCH 065/218] doc: fix description of createDecipheriv PR-URL: https://github.com/nodejs/node/pull/18651 Refs: https://github.com/nodejs/node/pull/12223 Reviewed-By: Ruben Bridgewater Reviewed-By: Luigi Pinca Reviewed-By: Tiancheng "Timothy" Gu --- doc/api/crypto.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 182a2197567494..afd133f1939ad5 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1289,8 +1289,8 @@ recent OpenSSL releases, `openssl list-cipher-algorithms` will display the available cipher algorithms. The `key` is the raw key used by the `algorithm` and `iv` is an -[initialization vector][]. Both arguments must be `'utf8'` encoded strings or -[buffers][`Buffer`]. +[initialization vector][]. Both arguments must be `'utf8'` encoded strings, +[Buffers][`Buffer`], `TypedArray`, or `DataView`s. ### crypto.createDiffieHellman(prime[, primeEncoding][, generator][, generatorEncoding]) - `algorithm` {string} - `key` {string | Buffer | TypedArray | DataView} - `iv` {string | Buffer | TypedArray | DataView} From ab8edc9d4844aaf66dcbad409415ffcc59c0aebc Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sun, 4 Feb 2018 16:49:00 -0500 Subject: [PATCH 067/218] test: fix flaky timers-block-eventloop test Due to extensive reliance on timings and the fs module, this test is currently inherently flaky. Refactor it to simply use setImmediate and only one busy loop. PR-URL: https://github.com/nodejs/node/pull/18567 Reviewed-By: Matteo Collina Reviewed-By: Ruben Bridgewater --- .../sequential/test-timers-block-eventloop.js | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/test/sequential/test-timers-block-eventloop.js b/test/sequential/test-timers-block-eventloop.js index f6426e454e0882..811216fcb29e7d 100644 --- a/test/sequential/test-timers-block-eventloop.js +++ b/test/sequential/test-timers-block-eventloop.js @@ -1,24 +1,18 @@ 'use strict'; const common = require('../common'); -const fs = require('fs'); -const platformTimeout = common.platformTimeout; +const assert = require('assert'); +let called = false; const t1 = setInterval(() => { - common.busyLoop(platformTimeout(12)); -}, platformTimeout(10)); - -const t2 = setInterval(() => { - common.busyLoop(platformTimeout(15)); -}, platformTimeout(10)); - -const t3 = - setTimeout(common.mustNotCall('eventloop blocked!'), platformTimeout(200)); - -setTimeout(function() { - fs.stat('/dev/nonexistent', () => { + assert(!called); + called = true; + setImmediate(common.mustCall(() => { clearInterval(t1); clearInterval(t2); - clearTimeout(t3); - }); -}, platformTimeout(50)); + })); +}, 10); + +const t2 = setInterval(() => { + common.busyLoop(20); +}, 10); From 86e3c89ea4e281e1aea2a6adec9a598c7c4939b0 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 5 Feb 2018 19:49:50 +0100 Subject: [PATCH 068/218] benchmark: improve compare output The current output uses JSON.stringify to escape the config values. This switches to util.inspect to have a better readable output. PR-URL: https://github.com/nodejs/node/pull/18597 Reviewed-By: James M Snell Reviewed-By: Anatoli Papirovski Reviewed-By: Andreas Madsen Reviewed-By: Luigi Pinca --- benchmark/compare.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmark/compare.js b/benchmark/compare.js index 6b51a70eb9a41b..e7866b60e36418 100644 --- a/benchmark/compare.js +++ b/benchmark/compare.js @@ -1,6 +1,7 @@ 'use strict'; -const fork = require('child_process').fork; +const { fork } = require('child_process'); +const { inspect } = require('util'); const path = require('path'); const CLI = require('./_cli.js'); const BenchmarkProgress = require('./_benchmark_progress.js'); @@ -76,7 +77,7 @@ if (showProgress) { // Construct configuration string, " A=a, B=b, ..." let conf = ''; for (const key of Object.keys(data.conf)) { - conf += ` ${key}=${JSON.stringify(data.conf[key])}`; + conf += ` ${key}=${inspect(data.conf[key])}`; } conf = conf.slice(1); // Escape quotes (") for correct csv formatting From 9bce14172aae3c4e0d59121d552d08fb261ed657 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 1 Feb 2018 22:39:51 +0800 Subject: [PATCH 069/218] build: do not suppress output in make doc-only This helps to show the cause of errors in the CI. PR-URL: https://github.com/nodejs/node/pull/18507 Reviewed-By: James M Snell Reviewed-By: Gibson Fahnestock Reviewed-By: Daniel Bevenius Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d0b481d03ab9f3..03a66fb4bbf00d 100644 --- a/Makefile +++ b/Makefile @@ -558,7 +558,7 @@ doc-only: $(apidoc_dirs) $(apiassets) if [ ! -d doc/api/assets ]; then \ $(MAKE) tools/doc/node_modules/js-yaml/package.json; \ fi; - @$(MAKE) -s $(apidocs_html) $(apidocs_json) + @$(MAKE) $(apidocs_html) $(apidocs_json) doc: $(NODE_EXE) doc-only From cbd698a5217353f0c68e4aca628936d8a8db181b Mon Sep 17 00:00:00 2001 From: cjihrig Date: Thu, 1 Feb 2018 10:33:27 -0500 Subject: [PATCH 070/218] test: refactor test-http-abort-before-end This test was added over six years ago, and the behavior seems to have changed since then. When the test was originally written, common.mustCall() did not exist. The only assertion in this test was not actually executing. Instead of an 'error' event being emitted as expected, an 'abort' event was emitted. PR-URL: https://github.com/nodejs/node/pull/18508 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- test/parallel/test-http-abort-before-end.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/test/parallel/test-http-abort-before-end.js b/test/parallel/test-http-abort-before-end.js index 37d1291b074127..5577f256ca2ec9 100644 --- a/test/parallel/test-http-abort-before-end.js +++ b/test/parallel/test-http-abort-before-end.js @@ -22,25 +22,22 @@ 'use strict'; const common = require('../common'); const http = require('http'); -const assert = require('assert'); const server = http.createServer(common.mustNotCall()); -server.listen(0, function() { +server.listen(0, common.mustCall(() => { const req = http.request({ method: 'GET', host: '127.0.0.1', - port: this.address().port + port: server.address().port }); - req.on('error', function(ex) { - // https://github.com/joyent/node/issues/1399#issuecomment-2597359 - // abort() should emit an Error, not the net.Socket object - assert(ex instanceof Error); - }); + req.on('abort', common.mustCall(() => { + server.close(); + })); + + req.on('error', common.mustNotCall()); req.abort(); req.end(); - - server.close(); -}); +})); From 1faae90b74b02bec5e449ed5494bd7abda03f44b Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sun, 4 Feb 2018 13:43:21 -0500 Subject: [PATCH 071/218] readline: use Date.now() and move test to parallel The readline module wants a truthy time while using Timer.now() doesn't necessarily guarantee that early on in the process' life. It also doesn't actually resolve the timing issues experienced in an earlier issue. Instead, this PR fixes the related tests and moves them back to parallel. Refs: https://github.com/nodejs/node/issues/14674 PR-URL: https://github.com/nodejs/node/pull/18563 Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Ruben Bridgewater --- lib/readline.js | 10 +- test/parallel/test-readline-interface.js | 77 +++++++++++++++ test/sequential/test-readline-interface.js | 108 --------------------- 3 files changed, 81 insertions(+), 114 deletions(-) delete mode 100644 test/sequential/test-readline-interface.js diff --git a/lib/readline.js b/lib/readline.js index 3df7af1df3a99d..d749e2c8f23f0f 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -47,8 +47,6 @@ const { kClearScreenDown } = CSI; -const { now } = process.binding('timer_wrap').Timer; - const kHistorySize = 30; const kMincrlfDelay = 100; // \r\n, \n, or \r followed by something other than \n @@ -400,7 +398,7 @@ Interface.prototype._normalWrite = function(b) { } var string = this._decoder.write(b); if (this._sawReturnAt && - now() - this._sawReturnAt <= this.crlfDelay) { + Date.now() - this._sawReturnAt <= this.crlfDelay) { string = string.replace(/^\n/, ''); this._sawReturnAt = 0; } @@ -413,7 +411,7 @@ Interface.prototype._normalWrite = function(b) { this._line_buffer = null; } if (newPartContainsEnding) { - this._sawReturnAt = string.endsWith('\r') ? now() : 0; + this._sawReturnAt = string.endsWith('\r') ? Date.now() : 0; // got one or more newlines; process into "line" events var lines = string.split(lineEnding); @@ -908,14 +906,14 @@ Interface.prototype._ttyWrite = function(s, key) { switch (key.name) { case 'return': // carriage return, i.e. \r - this._sawReturnAt = now(); + this._sawReturnAt = Date.now(); this._line(); break; case 'enter': // When key interval > crlfDelay if (this._sawReturnAt === 0 || - now() - this._sawReturnAt > this.crlfDelay) { + Date.now() - this._sawReturnAt > this.crlfDelay) { this._line(); } this._sawReturnAt = 0; diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index f3fdd5044f4862..e58b7917bcb563 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -873,3 +873,80 @@ function isWarned(emitter) { assert.strictEqual(rl._prompt, '$ '); } }); + +// For the purposes of the following tests, we do not care about the exact +// value of crlfDelay, only that the behaviour conforms to what's expected. +// Setting it to Infinity allows the test to succeed even under extreme +// CPU stress. +const crlfDelay = Infinity; + +[ true, false ].forEach(function(terminal) { + // sending multiple newlines at once that does not end with a new line + // and a `end` event(last line is) + + // \r\n should emit one line event, not two + { + const fi = new FakeInput(); + const rli = new readline.Interface( + { + input: fi, + output: fi, + terminal: terminal, + crlfDelay + } + ); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + let callCount = 0; + rli.on('line', function(line) { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + fi.emit('data', expectedLines.join('\r\n')); + assert.strictEqual(callCount, expectedLines.length - 1); + rli.close(); + } + + // \r\n should emit one line event when split across multiple writes. + { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + terminal: terminal, + crlfDelay + }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + let callCount = 0; + rli.on('line', function(line) { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + expectedLines.forEach(function(line) { + fi.emit('data', `${line}\r`); + fi.emit('data', '\n'); + }); + assert.strictEqual(callCount, expectedLines.length); + rli.close(); + } + + // Emit one line event when the delay between \r and \n is + // over the default crlfDelay but within the setting value. + { + const fi = new FakeInput(); + const delay = 125; + const rli = new readline.Interface({ + input: fi, + output: fi, + terminal: terminal, + crlfDelay + }); + let callCount = 0; + rli.on('line', () => callCount++); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + }), delay); + } +}); diff --git a/test/sequential/test-readline-interface.js b/test/sequential/test-readline-interface.js deleted file mode 100644 index 488dd8338da761..00000000000000 --- a/test/sequential/test-readline-interface.js +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Flags: --expose_internals -'use strict'; -const common = require('../common'); - -// These test cases are in `sequential` rather than the analogous test file in -// `parallel` because they become unreliable under load. The unreliability under -// load was determined empirically when the test cases were in `parallel` by -// running: -// tools/test.py -j 96 --repeat 192 test/parallel/test-readline-interface.js - -const assert = require('assert'); -const readline = require('readline'); -const EventEmitter = require('events').EventEmitter; -const inherits = require('util').inherits; - -function FakeInput() { - EventEmitter.call(this); -} -inherits(FakeInput, EventEmitter); -FakeInput.prototype.resume = () => {}; -FakeInput.prototype.pause = () => {}; -FakeInput.prototype.write = () => {}; -FakeInput.prototype.end = () => {}; - -[ true, false ].forEach(function(terminal) { - // sending multiple newlines at once that does not end with a new line - // and a `end` event(last line is) - - // \r\n should emit one line event, not two - { - const fi = new FakeInput(); - const rli = new readline.Interface( - { input: fi, output: fi, terminal: terminal } - ); - const expectedLines = ['foo', 'bar', 'baz', 'bat']; - let callCount = 0; - rli.on('line', function(line) { - assert.strictEqual(line, expectedLines[callCount]); - callCount++; - }); - fi.emit('data', expectedLines.join('\r\n')); - assert.strictEqual(callCount, expectedLines.length - 1); - rli.close(); - } - - // \r\n should emit one line event when split across multiple writes. - { - const fi = new FakeInput(); - const rli = new readline.Interface( - { input: fi, output: fi, terminal: terminal } - ); - const expectedLines = ['foo', 'bar', 'baz', 'bat']; - let callCount = 0; - rli.on('line', function(line) { - assert.strictEqual(line, expectedLines[callCount]); - callCount++; - }); - expectedLines.forEach(function(line) { - fi.emit('data', `${line}\r`); - fi.emit('data', '\n'); - }); - assert.strictEqual(callCount, expectedLines.length); - rli.close(); - } - - // Emit one line event when the delay between \r and \n is - // over the default crlfDelay but within the setting value. - { - const fi = new FakeInput(); - const delay = 125; - const crlfDelay = common.platformTimeout(1000); - const rli = new readline.Interface({ - input: fi, - output: fi, - terminal: terminal, - crlfDelay - }); - let callCount = 0; - rli.on('line', () => callCount++); - fi.emit('data', '\r'); - setTimeout(common.mustCall(() => { - fi.emit('data', '\n'); - assert.strictEqual(callCount, 1); - rli.close(); - }), delay); - } -}); From 50bdf0ed78925665d6c1709f5f3e492739127917 Mon Sep 17 00:00:00 2001 From: "Tim O. Peters" Date: Thu, 4 Jan 2018 11:17:01 +0100 Subject: [PATCH 072/218] doc: be more explicit in the sypnosis Assuming less knowledge on the part of the reader, making it easier to get start using Node.js. PR-URL: https://github.com/nodejs/node/pull/17977 Fixes: https://github.com/nodejs/node/issues/17970, Reviewed-By: Vse Mozhet Byt Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Jeremiah Senkpiel --- doc/api/synopsis.md | 76 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/doc/api/synopsis.md b/doc/api/synopsis.md index ada2437387b7e2..508dde1ff483f8 100644 --- a/doc/api/synopsis.md +++ b/doc/api/synopsis.md @@ -9,9 +9,58 @@ Please see the [Command Line Options][] document for information about different options and ways to run scripts with Node.js. ## Example - An example of a [web server][] written with Node.js which responds with -`'Hello World'`: +`'Hello World!'`: + +Commands displayed in this document are shown starting with `$` or `>` +to replicate how they would appear in a user's terminal. +Do not include the `$` and `>` character they are there to +indicate the start of each command. + +There are many tutorials and examples that follow this +convention: `$` or `>` for commands run as a regular user, and `#` +for commands that should be executed as an administrator. + +Lines that don’t start with `$` or `>` character are typically showing +the output of the previous command. + +Firstly, make sure to have downloaded and installed Node.js. +See [this guide][] for further install information. + +Now, create an empty project folder called `projects`, navigate into it: +Project folder can be named base on user's current project title but +this example will use `projects` as the project folder. + +Linux and Mac: + +```console +$ mkdir ~/projects +$ cd ~/projects +``` + +Windows CMD: + +```console +> mkdir %USERPROFILE%\projects +> cd %USERPROFILE%\projects +``` + +Windows PowerShell: + +```console +> mkdir $env:USERPROFILE\projects +> cd $env:USERPROFILE\projects +``` + +Next, create a new source file in the `projects` + folder and call it `hello-world.js`. + +In Node.js it is considered good style to use +hyphens (`-`) or underscores (`_`) to separate + multiple words in filenames. + +Open `hello-world.js` in any preferred text editor and +paste in the following content. ```js const http = require('http'); @@ -22,7 +71,7 @@ const port = 3000; const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); - res.end('Hello World\n'); + res.end('Hello World!\n'); }); server.listen(port, hostname, () => { @@ -30,15 +79,26 @@ server.listen(port, hostname, () => { }); ``` -To run the server, put the code into a file called `example.js` and execute -it with Node.js: +Save the file, go back to the terminal window enter the following command: -```txt -$ node example.js -Server running at http://127.0.0.1:3000/ +```console +$ node hello-world.js ``` +An output like this should appear in the terminal to indicate Node.js +server is running: + + ```console + Server running at http://127.0.0.1:3000/ + ```` + +Now, open any preferred web browser and visit `http://127.0.0.1:3000`. + +If the browser displays the string `Hello, world!`, that indicates +the server is working. + Many of the examples in the documentation can be run similarly. [Command Line Options]: cli.html#cli_command_line_options [web server]: http.html +[this guide]: https://nodejs.org/en/download/package-manager/ From d4bccccf2305978d318de6d8950960e664bbdbe1 Mon Sep 17 00:00:00 2001 From: Shobhit Chittora Date: Wed, 1 Nov 2017 01:31:02 +0530 Subject: [PATCH 073/218] tools: add fixer for prefer-assert-iferror.js PR-URL: https://github.com/nodejs/node/pull/16648 Refs: https://github.com/nodejs/node/issues/16636 Reviewed-By: Anatoli Papirovski Reviewed-By: Gibson Fahnestock Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- .../test-eslint-prefer-assert-iferror.js | 14 +++++++---- tools/eslint-rules/prefer-assert-iferror.js | 23 +++++++++++++++++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/test/parallel/test-eslint-prefer-assert-iferror.js b/test/parallel/test-eslint-prefer-assert-iferror.js index 790207bc30cd43..6ecf92adbef51a 100644 --- a/test/parallel/test-eslint-prefer-assert-iferror.js +++ b/test/parallel/test-eslint-prefer-assert-iferror.js @@ -16,12 +16,18 @@ new RuleTester().run('prefer-assert-iferror', rule, { ], invalid: [ { - code: 'if (err) throw err;', - errors: [{ message: 'Use assert.ifError(err) instead.' }] + code: 'require("assert");\n' + + 'if (err) throw err;', + errors: [{ message: 'Use assert.ifError(err) instead.' }], + output: 'require("assert");\n' + + 'assert.ifError(err);' }, { - code: 'if (error) { throw error; }', - errors: [{ message: 'Use assert.ifError(error) instead.' }] + code: 'require("assert");\n' + + 'if (error) { throw error; }', + errors: [{ message: 'Use assert.ifError(error) instead.' }], + output: 'require("assert");\n' + + 'assert.ifError(error);' } ] }); diff --git a/tools/eslint-rules/prefer-assert-iferror.js b/tools/eslint-rules/prefer-assert-iferror.js index e15287417693e0..399ee7403a6c88 100644 --- a/tools/eslint-rules/prefer-assert-iferror.js +++ b/tools/eslint-rules/prefer-assert-iferror.js @@ -5,9 +5,12 @@ 'use strict'; +const utils = require('./rules-utils.js'); + module.exports = { create(context) { const sourceCode = context.getSourceCode(); + var assertImported = false; function hasSameTokens(nodeA, nodeB) { const aTokens = sourceCode.getTokens(nodeA); @@ -20,8 +23,15 @@ module.exports = { }); } + function checkAssertNode(node) { + if (utils.isRequired(node, ['assert'])) { + assertImported = true; + } + } + return { - IfStatement(node) { + 'CallExpression': (node) => checkAssertNode(node), + 'IfStatement': (node) => { const firstStatement = node.consequent.type === 'BlockStatement' ? node.consequent.body[0] : node.consequent; @@ -30,10 +40,19 @@ module.exports = { firstStatement.type === 'ThrowStatement' && hasSameTokens(node.test, firstStatement.argument) ) { + const argument = sourceCode.getText(node.test); context.report({ node: firstStatement, message: 'Use assert.ifError({{argument}}) instead.', - data: { argument: sourceCode.getText(node.test) } + data: { argument }, + fix: (fixer) => { + if (assertImported) { + return fixer.replaceText( + node, + `assert.ifError(${argument});` + ); + } + } }); } } From 1ea1970c3758eb53baae036138094adbb26e57f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=88=9A?= Date: Fri, 2 Feb 2018 18:40:01 +0800 Subject: [PATCH 074/218] doc: modify the return value of request.write() PR-URL: https://github.com/nodejs/node/pull/18526 Reviewed-By: Ruben Bridgewater Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/http.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/api/http.md b/doc/api/http.md index 49083e2193953c..7331d8bc5d969d 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -681,7 +681,9 @@ Defaults to `'utf8'`. The `callback` argument is optional and will be called when this chunk of data is flushed. -Returns `request`. +Returns `true` if the entire data was flushed successfully to the kernel +buffer. Returns `false` if all or part of the data was queued in user memory. +`'drain'` will be emitted when the buffer is free again. ## Class: http.Server * `urlString` {string} The URL string to parse. From b48ca0a140dc727c0c9d3d2ffb204a201745d2d5 Mon Sep 17 00:00:00 2001 From: BufoViridis <30531954+BufoViridis@users.noreply.github.com> Date: Tue, 30 Jan 2018 19:43:50 +0200 Subject: [PATCH 085/218] src: fix crypto.pbkdf2 callback error argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Callbacks should always return `null` instead of `undefined` if no error occurred. PR-URL: https://github.com/nodejs/node/pull/18458 Reviewed-By: Ben Noordhuis Reviewed-By: Tobias Nießen Reviewed-By: Fedor Indutny Reviewed-By: James M Snell --- src/node_crypto.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 6b5958b35e7049..580d6a4f4d55bc 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5421,7 +5421,7 @@ void PBKDF2Request::Work(uv_work_t* work_req) { void PBKDF2Request::After(Local (*argv)[2]) { if (success_) { - (*argv)[0] = Undefined(env()->isolate()); + (*argv)[0] = Null(env()->isolate()); (*argv)[1] = Buffer::New(env(), key_, keylen_).ToLocalChecked(); key_ = nullptr; keylen_ = 0; From 88d3028e22ff3cc2db522364aa6a693401cef06d Mon Sep 17 00:00:00 2001 From: Yang Guo Date: Fri, 9 Feb 2018 14:23:42 +0100 Subject: [PATCH 086/218] build: no longer have v8-debug.h as dependency. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ref: https://github.com/nodejs/node/issues/18643 PR-URL: https://github.com/nodejs/node/pull/18677 Refs: https://github.com/nodejs/node/issues/18643 Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- node.gyp | 1 - 1 file changed, 1 deletion(-) diff --git a/node.gyp b/node.gyp index 04b090cbd37ec4..ee4157567f5a31 100644 --- a/node.gyp +++ b/node.gyp @@ -364,7 +364,6 @@ 'src/util-inl.h', 'deps/http_parser/http_parser.h', 'deps/v8/include/v8.h', - 'deps/v8/include/v8-debug.h', # javascript files to make for an even more pleasant IDE experience '<@(library_files)', # node.gyp is added to the project by default. From b8f2acd2e863719c483e36dc5ea60affbe0156ff Mon Sep 17 00:00:00 2001 From: Matheus Marchini Date: Mon, 12 Feb 2018 12:43:11 -0500 Subject: [PATCH 087/218] doc: add mmarchini to collaborators PR-URL: https://github.com/nodejs/node/pull/18740 Reviewed-By: Joyee Cheung Reviewed-By: Matteo Collina Reviewed-By: Ruben Bridgewater Reviewed-By: Richard Lau --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f0451582584532..5ba9d5491d666b 100644 --- a/README.md +++ b/README.md @@ -426,6 +426,8 @@ For more information about the governance of the Node.js project, see **Mikeal Rogers** <mikeal.rogers@gmail.com> * [misterdjules](https://github.com/misterdjules) - **Julien Gilli** <jgilli@nodejs.org> +* [mmarchini](https://github.com/mmarchini) - +**Matheus Marchini** <matheus@sthima.com> * [mscdex](https://github.com/mscdex) - **Brian White** <mscdex@mscdex.net> * [MylesBorins](https://github.com/MylesBorins) - From f4ddaaec0ed0b5a7eb8a9b648ffc12db9df5e144 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Tue, 13 Feb 2018 02:09:44 -0500 Subject: [PATCH 088/218] doc: move Fedor to TSC Emeritus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a conversation Fedor requested that this PR be made. They plan to continue working on core as a Collaborator. It is this committers belief that if Fedor would like to join the TSC again in the future there is no reason that could not be made possible. Thanks for all the hard work! PR-URL: https://github.com/nodejs/node/pull/18752 Reviewed-By: Gireesh Punathil Reviewed-By: Fedor Indutny Reviewed-By: Yuta Hiroto Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Anatoli Papirovski Reviewed-By: Ali Ijaz Sheikh Reviewed-By: Matteo Collina Reviewed-By: Сковорода Никита Андреевич Reviewed-By: Rich Trott Reviewed-By: Jon Moss Reviewed-By: Tobias Nießen Reviewed-By: Joyee Cheung Reviewed-By: Sakthipriyan Vairamani Reviewed-By: Michael Dawson Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Evan Lucas --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ba9d5491d666b..e537407c8685bd 100644 --- a/README.md +++ b/README.md @@ -246,8 +246,6 @@ For more information about the governance of the Node.js project, see **Jeremiah Senkpiel** <fishrock123@rocketmail.com> * [gibfahn](https://github.com/gibfahn) - **Gibson Fahnestock** <gibfahn@gmail.com> (he/him) -* [indutny](https://github.com/indutny) - -**Fedor Indutny** <fedor.indutny@gmail.com> * [jasnell](https://github.com/jasnell) - **James M Snell** <jasnell@gmail.com> (he/him) * [joyeecheung](https://github.com/joyeecheung) - @@ -277,6 +275,8 @@ For more information about the governance of the Node.js project, see **Ben Noordhuis** <info@bnoordhuis.nl> * [chrisdickinson](https://github.com/chrisdickinson) - **Chris Dickinson** <christopher.s.dickinson@gmail.com> +* [indutny](https://github.com/indutny) - +**Fedor Indutny** <fedor.indutny@gmail.com> * [isaacs](https://github.com/isaacs) - **Isaac Z. Schlueter** <i@izs.me> * [joshgav](https://github.com/joshgav) - From 9129bc4fde61df5df0447262e756eb8e1168dbcc Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sat, 3 Feb 2018 09:52:29 -0700 Subject: [PATCH 089/218] lib: set process.execPath on OpenBSD PR-URL: https://github.com/nodejs/node/pull/18543 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- lib/internal/bootstrap_node.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index cc2b5ff3daa3ff..6b7ac044f638af 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -71,6 +71,13 @@ // URL::ToObject() method is used. NativeModule.require('internal/url'); + // On OpenBSD process.execPath will be relative unless we + // get the full path before process.execPath is used. + if (process.platform === 'openbsd') { + const { realpathSync } = NativeModule.require('fs'); + process.execPath = realpathSync.native(process.execPath); + } + Object.defineProperty(process, 'argv0', { enumerable: true, configurable: false, From 9236332cc3488e6477602d46c5842d585f8de3c1 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sat, 3 Feb 2018 09:53:57 -0700 Subject: [PATCH 090/218] test: update a few tests to work on OpenBSD PR-URL: https://github.com/nodejs/node/pull/18543 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- test/common/index.js | 1 + test/parallel/test-child-process-exec-timeout.js | 11 +++++++++-- test/parallel/test-fs-utimes.js | 4 ++-- test/parallel/test-http-dns-error.js | 8 ++++++-- test/parallel/test-net-dns-error.js | 8 ++++++-- test/parallel/test-setproctitle.js | 2 +- test/sequential/test-module-loading.js | 12 +++++++++--- 7 files changed, 34 insertions(+), 12 deletions(-) diff --git a/test/common/index.js b/test/common/index.js index 85857f753e3454..80ba48d25a1710 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -45,6 +45,7 @@ exports.isLinuxPPCBE = (process.platform === 'linux') && (os.endianness() === 'BE'); exports.isSunOS = process.platform === 'sunos'; exports.isFreeBSD = process.platform === 'freebsd'; +exports.isOpenBSD = process.platform === 'openbsd'; exports.isLinux = process.platform === 'linux'; exports.isOSX = process.platform === 'darwin'; diff --git a/test/parallel/test-child-process-exec-timeout.js b/test/parallel/test-child-process-exec-timeout.js index ed25d9bff825a9..e08aff908522f2 100644 --- a/test/parallel/test-child-process-exec-timeout.js +++ b/test/parallel/test-child-process-exec-timeout.js @@ -16,15 +16,22 @@ const cmd = `"${process.execPath}" "${__filename}" child`; // Test the case where a timeout is set, and it expires. cp.exec(cmd, { timeout: 1 }, common.mustCall((err, stdout, stderr) => { + let sigterm = 'SIGTERM'; assert.strictEqual(err.killed, true); - assert.strictEqual(err.code, null); + // TODO OpenBSD returns a null signal and 143 for code + if (common.isOpenBSD) { + assert.strictEqual(err.code, 143); + sigterm = null; + } else { + assert.strictEqual(err.code, null); + } // At least starting with Darwin Kernel Version 16.4.0, sending a SIGTERM to a // process that is still starting up kills it with SIGKILL instead of SIGTERM. // See: https://github.com/libuv/libuv/issues/1226 if (common.isOSX) assert.ok(err.signal === 'SIGTERM' || err.signal === 'SIGKILL'); else - assert.strictEqual(err.signal, 'SIGTERM'); + assert.strictEqual(err.signal, sigterm); assert.strictEqual(err.cmd, cmd); assert.strictEqual(stdout.trim(), ''); assert.strictEqual(stderr.trim(), ''); diff --git a/test/parallel/test-fs-utimes.js b/test/parallel/test-fs-utimes.js index 103b4640961937..3dc0bb59def6a2 100644 --- a/test/parallel/test-fs-utimes.js +++ b/test/parallel/test-fs-utimes.js @@ -172,8 +172,8 @@ process.on('exit', function() { const path = `${tmpdir.path}/test-utimes-precision`; fs.writeFileSync(path, ''); -// test Y2K38 for all platforms [except 'arm', and 'SunOS'] -if (!process.arch.includes('arm') && !common.isSunOS) { +// test Y2K38 for all platforms [except 'arm', 'OpenBSD' and 'SunOS'] +if (!process.arch.includes('arm') && !common.isOpenBSD && !common.isSunOS) { // because 2 ** 31 doesn't look right // eslint-disable-next-line space-infix-ops const Y2K38_mtime = 2**31; diff --git a/test/parallel/test-http-dns-error.js b/test/parallel/test-http-dns-error.js index 900cf40e6b209b..06a15c89fb46b3 100644 --- a/test/parallel/test-http-dns-error.js +++ b/test/parallel/test-http-dns-error.js @@ -32,6 +32,10 @@ const https = require('https'); const host = '*'.repeat(256); const MAX_TRIES = 5; +let errCode = 'ENOTFOUND'; +if (common.isOpenBSD) + errCode = 'EAI_FAIL'; + function tryGet(mod, tries) { // Bad host name should not throw an uncatchable exception. // Ensure that there is time to attach an error listener. @@ -41,7 +45,7 @@ function tryGet(mod, tries) { tryGet(mod, ++tries); return; } - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); })); // http.get() called req1.end() for us } @@ -57,7 +61,7 @@ function tryRequest(mod, tries) { tryRequest(mod, ++tries); return; } - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); })); req.end(); } diff --git a/test/parallel/test-net-dns-error.js b/test/parallel/test-net-dns-error.js index beebcd8cb9cf44..a5ae415592fed4 100644 --- a/test/parallel/test-net-dns-error.js +++ b/test/parallel/test-net-dns-error.js @@ -27,17 +27,21 @@ const net = require('net'); const host = '*'.repeat(256); +let errCode = 'ENOTFOUND'; +if (common.isOpenBSD) + errCode = 'EAI_FAIL'; + function do_not_call() { throw new Error('This function should not have been called.'); } const socket = net.connect(42, host, do_not_call); socket.on('error', common.mustCall(function(err) { - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); })); socket.on('lookup', function(err, ip, type) { assert(err instanceof Error); - assert.strictEqual(err.code, 'ENOTFOUND'); + assert.strictEqual(err.code, errCode); assert.strictEqual(ip, undefined); assert.strictEqual(type, undefined); }); diff --git a/test/parallel/test-setproctitle.js b/test/parallel/test-setproctitle.js index 4bb88c4ba06922..1ab6bff6a30848 100644 --- a/test/parallel/test-setproctitle.js +++ b/test/parallel/test-setproctitle.js @@ -34,7 +34,7 @@ exec(cmd, common.mustCall((error, stdout, stderr) => { assert.strictEqual(stderr, ''); // freebsd always add ' (procname)' to the process title - if (common.isFreeBSD) + if (common.isFreeBSD || common.isOpenBSD) title += ` (${path.basename(process.execPath)})`; // omitting trailing whitespace and \n diff --git a/test/sequential/test-module-loading.js b/test/sequential/test-module-loading.js index 8b097142698e6f..3d0de954c58e8a 100644 --- a/test/sequential/test-module-loading.js +++ b/test/sequential/test-module-loading.js @@ -20,7 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const path = require('path'); const fs = require('fs'); @@ -197,7 +197,10 @@ try { require(`${loadOrder}file3`); } catch (e) { // Not a real .node module, but we know we require'd the right thing. - assert.ok(/file3\.node/.test(e.message.replace(backslash, '/'))); + if (common.isOpenBSD) // OpenBSD errors with non-ELF object error + assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); + else + assert.ok(/file3\.node/.test(e.message.replace(backslash, '/'))); } assert.strictEqual(require(`${loadOrder}file4`).file4, 'file4.reg', msg); assert.strictEqual(require(`${loadOrder}file5`).file5, 'file5.reg2', msg); @@ -205,7 +208,10 @@ try { try { require(`${loadOrder}file7`); } catch (e) { - assert.ok(/file7\/index\.node/.test(e.message.replace(backslash, '/'))); + if (common.isOpenBSD) + assert.ok(/File not an ELF object/.test(e.message.replace(backslash, '/'))); + else + assert.ok(/file7\/index\.node/.test(e.message.replace(backslash, '/'))); } assert.strictEqual(require(`${loadOrder}file8`).file8, 'file8/index.reg', msg); From ab005592beaac499b20bb1a056d9d69b40ba0320 Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Mon, 22 Jan 2018 12:19:05 -0800 Subject: [PATCH 091/218] deps: V8: backport 76c3ac5 from upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a bug in the CPU profiler where some ticks were attributed to the wrong file. Original commit message: [cpu-profiler] Fix script name when recording inlining info Use the script name from the shared function info to create an inline entry. Otherwise functions are attributed to the wrong file in the CpuProfileNode. See https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs/issues/89 Bug: v8:7203, v8:7241 Change-Id: I8ea31943741770e6611275a9c93375922b934547 Reviewed-on: https://chromium-review.googlesource.com/848093 Reviewed-by: Jaroslav Sevcik Commit-Queue: Franziska Hinkelmann Cr-Commit-Position: refs/heads/master@{#50339} Refs: https://github.com/v8/v8/commit/76c3ac58b00e08e3e2fecc3f7169bd64d91c8f75 PR-URL: https://github.com/nodejs/node/pull/18298 Reviewed-By: Michaël Zasso Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig --- deps/v8/include/v8-version.h | 2 +- deps/v8/src/profiler/profiler-listener.cc | 17 +++-- deps/v8/test/cctest/test-cpu-profiler.cc | 79 +++++++++++++++++++++++ 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 6d7eca8ab572ab..84c6717e6b486d 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 6 #define V8_MINOR_VERSION 2 #define V8_BUILD_NUMBER 414 -#define V8_PATCH_LEVEL 53 +#define V8_PATCH_LEVEL 54 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/profiler/profiler-listener.cc b/deps/v8/src/profiler/profiler-listener.cc index 540d93002459fb..b90f5a4894fc76 100644 --- a/deps/v8/src/profiler/profiler-listener.cc +++ b/deps/v8/src/profiler/profiler-listener.cc @@ -226,11 +226,18 @@ void ProfilerListener::RecordInliningInfo(CodeEntry* entry, SharedFunctionInfo* shared_info = SharedFunctionInfo::cast( deopt_input_data->LiteralArray()->get(shared_info_id)); if (!depth++) continue; // Skip the current function itself. - CodeEntry* inline_entry = new CodeEntry( - entry->tag(), GetFunctionName(shared_info->DebugName()), - CodeEntry::kEmptyNamePrefix, entry->resource_name(), - CpuProfileNode::kNoLineNumberInfo, - CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start()); + const char* resource_name = + (shared_info->script()->IsScript() && + Script::cast(shared_info->script())->name()->IsName()) + ? GetName(Name::cast(Script::cast(shared_info->script())->name())) + : CodeEntry::kEmptyResourceName; + + CodeEntry* inline_entry = + new CodeEntry(entry->tag(), GetFunctionName(shared_info->DebugName()), + CodeEntry::kEmptyNamePrefix, resource_name, + CpuProfileNode::kNoLineNumberInfo, + CpuProfileNode::kNoColumnNumberInfo, nullptr, + code->instruction_start()); inline_entry->FillFunctionInfo(shared_info); inline_stack.push_back(inline_entry); } diff --git a/deps/v8/test/cctest/test-cpu-profiler.cc b/deps/v8/test/cctest/test-cpu-profiler.cc index f22a42a977d5be..b441d04fdd31db 100644 --- a/deps/v8/test/cctest/test-cpu-profiler.cc +++ b/deps/v8/test/cctest/test-cpu-profiler.cc @@ -1745,6 +1745,85 @@ TEST(FunctionDetails) { script_a->GetUnboundScript()->GetId(), 5, 14); } +TEST(FunctionDetailsInlining) { + if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return; + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope(CcTest::isolate()); + v8::Local env = CcTest::NewContext(PROFILER_EXTENSION); + v8::Context::Scope context_scope(env); + ProfilerHelper helper(env); + + // alpha is in a_script, beta in b_script. beta is + // inlined in alpha, but it should be attributed to b_script. + + v8::Local script_b = CompileWithOrigin( + "function beta(k) {\n" + " let sum = 2;\n" + " for(let i = 0; i < k; i ++) {\n" + " sum += i;\n" + " sum = sum + 'a';\n" + " }\n" + " return sum;\n" + "}\n" + "\n", + "script_b"); + + v8::Local script_a = CompileWithOrigin( + "function alpha(p) {\n" + " let res = beta(p);\n" + " res = res + res;\n" + " return res;\n" + "}\n" + "let p = 2;\n" + "\n" + "\n" + "// Warm up before profiling or the inlining doesn't happen.\n" + "p = alpha(p);\n" + "p = alpha(p);\n" + "%OptimizeFunctionOnNextCall(alpha);\n" + "p = alpha(p);\n" + "\n" + "\n" + "startProfiling();\n" + "for(let i = 0; i < 10000; i++) {\n" + " p = alpha(p);\n" + "}\n" + "stopProfiling();\n" + "\n" + "\n", + "script_a"); + + script_b->Run(env).ToLocalChecked(); + script_a->Run(env).ToLocalChecked(); + + const v8::CpuProfile* profile = i::ProfilerExtension::last_profile; + const v8::CpuProfileNode* current = profile->GetTopDownRoot(); + reinterpret_cast(const_cast(current)) + ->Print(0); + // The tree should look like this: + // 0 (root) 0 #1 + // 5 (program) 0 #6 + // 2 14 #2 script_a:1 + // ;;; deopted at script_id: 14 position: 299 with reason 'Insufficient + // type feedback for call'. + // 1 alpha 14 #4 script_a:1 + // 9 beta 13 #5 script_b:0 + // 0 startProfiling 0 #3 + + const v8::CpuProfileNode* root = profile->GetTopDownRoot(); + const v8::CpuProfileNode* script = GetChild(env, root, ""); + CheckFunctionDetails(env->GetIsolate(), script, "", "script_a", + script_a->GetUnboundScript()->GetId(), 1, 1); + const v8::CpuProfileNode* alpha = FindChild(env, script, "alpha"); + // Return early if profiling didn't sample alpha. + if (!alpha) return; + CheckFunctionDetails(env->GetIsolate(), alpha, "alpha", "script_a", + script_a->GetUnboundScript()->GetId(), 1, 15); + const v8::CpuProfileNode* beta = FindChild(env, alpha, "beta"); + if (!beta) return; + CheckFunctionDetails(env->GetIsolate(), beta, "beta", "script_b", + script_b->GetUnboundScript()->GetId(), 0, 0); +} TEST(DontStopOnFinishedProfileDelete) { v8::HandleScope scope(CcTest::isolate()); From bf4d0743bef23ff38133e4f3375b1eac36cb92a5 Mon Sep 17 00:00:00 2001 From: Santiago Gimeno Date: Sat, 10 Feb 2018 14:52:18 +0100 Subject: [PATCH 092/218] cluster: fix inspector port assignment Make sure that inspector ports in cluster are inside the valid range: `[1024, 65535]`. Fixes flaky `test-inspector-port-zero-cluster`. PR-URL: https://github.com/nodejs/node/pull/18696 Fixes: https://github.com/nodejs/node/issues/18303 Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- lib/internal/cluster/master.js | 3 +++ test/sequential/test-inspector-port-cluster.js | 10 ++++++++++ test/sequential/test-inspector-port-zero-cluster.js | 10 ++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/internal/cluster/master.js b/lib/internal/cluster/master.js index 27a591a6f8d6d5..4836ede540a9cf 100644 --- a/lib/internal/cluster/master.js +++ b/lib/internal/cluster/master.js @@ -14,6 +14,7 @@ const intercom = new EventEmitter(); const SCHED_NONE = 1; const SCHED_RR = 2; const { isLegalPort } = require('internal/net'); +const [ minPort, maxPort ] = [ 1024, 65535 ]; module.exports = cluster; @@ -119,6 +120,8 @@ function createWorkerProcess(id, env) { } } else { inspectPort = process.debugPort + debugPortOffset; + if (inspectPort > maxPort) + inspectPort = inspectPort - maxPort + minPort - 1; debugPortOffset++; } diff --git a/test/sequential/test-inspector-port-cluster.js b/test/sequential/test-inspector-port-cluster.js index 84ec408ebfb178..87469aa7ff77c5 100644 --- a/test/sequential/test-inspector-port-cluster.js +++ b/test/sequential/test-inspector-port-cluster.js @@ -24,6 +24,16 @@ function testRunnerMain() { workers: [{ expectedPort: 9230 }] }); + spawnMaster({ + execArgv: ['--inspect=65534'], + workers: [ + { expectedPort: 65535 }, + { expectedPort: 1024 }, + { expectedPort: 1025 }, + { expectedPort: 1026 } + ] + }); + let port = debuggerPort + offset++ * 5; spawnMaster({ diff --git a/test/sequential/test-inspector-port-zero-cluster.js b/test/sequential/test-inspector-port-zero-cluster.js index f64e05f314c0c6..e522056571d1c2 100644 --- a/test/sequential/test-inspector-port-zero-cluster.js +++ b/test/sequential/test-inspector-port-zero-cluster.js @@ -30,16 +30,14 @@ function serialFork() { if (cluster.isMaster) { Promise.all([serialFork(), serialFork(), serialFork()]) .then(common.mustCall((ports) => { - ports.push(process.debugPort); - ports.sort(); + ports.splice(0, 0, process.debugPort); // 4 = [master, worker1, worker2, worker3].length() assert.strictEqual(ports.length, 4); assert(ports.every((port) => port > 0)); assert(ports.every((port) => port < 65536)); - // Ports should be consecutive. - assert.strictEqual(ports[0] + 1, ports[1]); - assert.strictEqual(ports[1] + 1, ports[2]); - assert.strictEqual(ports[2] + 1, ports[3]); + assert.strictEqual(ports[0] === 65535 ? 1024 : ports[0] + 1, ports[1]); + assert.strictEqual(ports[1] === 65535 ? 1024 : ports[1] + 1, ports[2]); + assert.strictEqual(ports[2] === 65535 ? 1024 : ports[2] + 1, ports[3]); })) .catch( (err) => { From 29c395d9756b56d111f13c976112f54c4f4ed1e5 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Mon, 19 Feb 2018 14:43:02 +0100 Subject: [PATCH 093/218] http: allow _httpMessage to be GC'ed Set `socket._httpMessage` to `null` before emitting the `'connect'` or `'upgrade'` event. PR-URL: https://github.com/nodejs/node/pull/18865 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Matteo Collina --- lib/_http_agent.js | 1 + test/parallel/test-http-connect.js | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/_http_agent.js b/lib/_http_agent.js index 5f1e56caeab981..7586a48680bb6a 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -277,6 +277,7 @@ function installListeners(agent, s, options) { s.removeListener('close', onClose); s.removeListener('free', onFree); s.removeListener('agentRemove', onRemove); + s._httpMessage = null; } s.on('agentRemove', onRemove); } diff --git a/test/parallel/test-http-connect.js b/test/parallel/test-http-connect.js index b019c61573e3fa..0ec682d1b7a03d 100644 --- a/test/parallel/test-http-connect.js +++ b/test/parallel/test-http-connect.js @@ -49,6 +49,10 @@ server.listen(0, common.mustCall(() => { path: 'google.com:443' }, common.mustNotCall()); + req.on('socket', common.mustCall((socket) => { + assert.strictEqual(socket._httpMessage, req); + })); + req.on('close', common.mustCall()); req.on('connect', common.mustCall((res, socket, firstBodyChunk) => { @@ -60,6 +64,7 @@ server.listen(0, common.mustCall(() => { // Make sure this socket has detached. assert(!socket.ondata); assert(!socket.onend); + assert.strictEqual(socket._httpMessage, null); assert.strictEqual(socket.listeners('connect').length, 0); assert.strictEqual(socket.listeners('data').length, 0); From 42b4f3ce0b3411ecdc5c362d199b25a7ffb3f609 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 21 Feb 2018 15:49:47 +0100 Subject: [PATCH 094/218] src: fix abort when taking a heap snapshot Remove an erroneous CHECK that asserted the persistent object's internal field pointer still pointed to a valid object. If ClearWrap() has been called, the field pointer equals nullptr and that is expected behavior. PR-URL: https://github.com/nodejs/node/pull/18898 Fixes: https://github.com/nodejs/node/issues/18256 Reviewed-By: Colin Ihrig Reviewed-By: Joyee Cheung Reviewed-By: Ruben Bridgewater Reviewed-By: Anna Henningsen Reviewed-By: Matheus Marchini --- src/async_wrap.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 61fd40a486b3bd..b1212e97704ef0 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -129,7 +129,7 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) { CHECK_GT(object->InternalFieldCount(), 0); AsyncWrap* wrap = Unwrap(object); - CHECK_NE(nullptr, wrap); + if (wrap == nullptr) return nullptr; // ClearWrap() already called. return new RetainedAsyncInfo(class_id, wrap); } From 6bfdba125fce60b6e60f1eae9b35983fd9d08654 Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Mon, 19 Feb 2018 15:09:48 +0100 Subject: [PATCH 095/218] http: remove default 'drain' listener on upgrade Ensure that the default `'drain'` listener is removed before the `'connect'` or `'upgrade'` event is emitted. PR-URL: https://github.com/nodejs/node/pull/18866 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- lib/_http_client.js | 3 ++- test/parallel/test-http-connect.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/_http_client.js b/lib/_http_client.js index c58be1fc3acdb2..dfab2fce131122 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -37,7 +37,7 @@ const { OutgoingMessage } = require('_http_outgoing'); const Agent = require('_http_agent'); const { Buffer } = require('buffer'); const { urlToOptions, searchParamsSymbol } = require('internal/url'); -const { outHeadersKey } = require('internal/http'); +const { outHeadersKey, ondrain } = require('internal/http'); const { nextTick } = require('internal/process/next_tick'); // The actual list of disallowed characters in regexp form is more like: @@ -452,6 +452,7 @@ function socketOnData(d) { socket.removeListener('data', socketOnData); socket.removeListener('end', socketOnEnd); + socket.removeListener('drain', ondrain); parser.finish(); freeParser(parser, req, socket); diff --git a/test/parallel/test-http-connect.js b/test/parallel/test-http-connect.js index 0ec682d1b7a03d..9499cd95275f8c 100644 --- a/test/parallel/test-http-connect.js +++ b/test/parallel/test-http-connect.js @@ -67,6 +67,7 @@ server.listen(0, common.mustCall(() => { assert.strictEqual(socket._httpMessage, null); assert.strictEqual(socket.listeners('connect').length, 0); assert.strictEqual(socket.listeners('data').length, 0); + assert.strictEqual(socket.listeners('drain').length, 0); // the stream.Duplex onend listener // allow 0 here, so that i can run the same test on streams1 impl From b2213798a3d91ec06ca162b09be53f6d44d9de47 Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Fri, 13 Apr 2018 11:58:00 +0300 Subject: [PATCH 096/218] doc: fix MDN links to avoid redirections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit developer.mozilla.org/en/... -> developer.mozilla.org/en-US/... PR-URL: https://github.com/nodejs/node/pull/18631 Backport-PR-URL: https://github.com/nodejs/node/pull/20005 Reviewed-By: Ruben Bridgewater Reviewed-By: Tobias Nießen --- doc/api/assert.md | 4 ++-- doc/api/fs.md | 2 +- doc/api/intl.md | 14 +++++++------- doc/api/url.md | 2 +- doc/api/util.md | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index 04d46751793ec3..a859408a8be99e 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -631,10 +631,10 @@ For more information, see [MDN's guide on equality comparisons and sameness][mdn-equality-guide]. [`Error.captureStackTrace`]: errors.html#errors_error_capturestacktrace_targetobject_constructoropt -[`Map`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map +[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map [`Object.is()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is [`RegExp`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions -[`Set`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set +[`Set`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set [`TypeError`]: errors.html#errors_class_typeerror [`assert.deepEqual()`]: #assert_assert_deepequal_actual_expected_message [`assert.deepStrictEqual()`]: #assert_assert_deepstrictequal_actual_expected_message diff --git a/doc/api/fs.md b/doc/api/fs.md index 85add1571691ba..93744100057c57 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3177,7 +3177,7 @@ The following constants are meant for use with the [`fs.Stats`][] object's [Caveats]: #fs_caveats [Common System Errors]: errors.html#errors_common_system_errors [FS Constants]: #fs_fs_constants_1 -[MDN-Date]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date +[MDN-Date]: https://developer.mozilla.org/en-US/JavaScript/Reference/Global_Objects/Date [MDN-Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type [MSDN-Rel-Path]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#fully_qualified_vs._relative_paths [Readable Stream]: stream.html#stream_class_stream_readable diff --git a/doc/api/intl.md b/doc/api/intl.md index 224c00a62fe888..83a9947dc21238 100644 --- a/doc/api/intl.md +++ b/doc/api/intl.md @@ -190,17 +190,17 @@ to be helpful: ["ICU Data"]: http://userguide.icu-project.org/icudata [`--icu-data-dir`]: cli.html#cli_icu_data_dir_file -[`Date.prototype.toLocaleString()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString -[`Intl`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Intl +[`Date.prototype.toLocaleString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString +[`Intl`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl [`Intl.DateTimeFormat`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat [`NODE_ICU_DATA`]: cli.html#cli_node_icu_data_file -[`Number.prototype.toLocaleString()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString +[`Number.prototype.toLocaleString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString [`require('buffer').transcode()`]: buffer.html#buffer_buffer_transcode_source_fromenc_toenc [`require('util').TextDecoder`]: util.html#util_class_util_textdecoder -[`String.prototype.localeCompare()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare -[`String.prototype.normalize()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/normalize -[`String.prototype.toLowerCase()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase -[`String.prototype.toUpperCase()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase +[`String.prototype.localeCompare()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare +[`String.prototype.normalize()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize +[`String.prototype.toLowerCase()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase +[`String.prototype.toUpperCase()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase [BUILDING.md]: https://github.com/nodejs/node/blob/master/BUILDING.md [BUILDING.md#full-icu]: https://github.com/nodejs/node/blob/master/BUILDING.md#build-with-full-icu-support-all-locales-supported-by-icu [ECMA-262]: https://tc39.github.io/ecma262/ diff --git a/doc/api/url.md b/doc/api/url.md index 3f152099dcbb92..40cc002f048e81 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -1146,7 +1146,7 @@ console.log(myURL.origin); ``` [`Error`]: errors.html#errors_class_error -[`JSON.stringify()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify +[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify [`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map [`TypeError`]: errors.html#errors_class_typeerror [`URLSearchParams`]: #url_class_urlsearchparams diff --git a/doc/api/util.md b/doc/api/util.md index cbbc5e92732894..7932b815e4a5e9 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -1191,7 +1191,7 @@ Deprecated predecessor of `console.log`. [`Array.isArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray [`Buffer.isBuffer()`]: buffer.html#buffer_class_method_buffer_isbuffer_obj [`Error`]: errors.html#errors_class_error -[`Object.assign()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign +[`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign [`console.error()`]: console.html#console_console_error_data_args [`console.log()`]: console.html#console_console_log_data_args [`util.inspect()`]: #util_util_inspect_object_options @@ -1201,5 +1201,5 @@ Deprecated predecessor of `console.log`. [Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors [Internationalization]: intl.html [WHATWG Encoding Standard]: https://encoding.spec.whatwg.org/ -[constructor]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor +[constructor]: https://developer.mozilla.org/en-US/JavaScript/Reference/Global_Objects/Object/constructor [semantically incompatible]: https://github.com/nodejs/node/issues/4179 From 3225601ffcfec6149671d01ff75dd0a17c4ef816 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 4 Feb 2018 22:48:02 +0100 Subject: [PATCH 097/218] net: remove Socket.prototoype.read Unused since 34b535f4caba366789d949211e4369b1e1d01152. PR-URL: https://github.com/nodejs/node/pull/18568 Reviewed-By: Anatoli Papirovski Reviewed-By: Yuta Hiroto Reviewed-By: Daniel Bevenius Reviewed-By: Luigi Pinca Reviewed-By: Khaidi Chu Reviewed-By: Evan Lucas Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- lib/net.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/net.js b/lib/net.js index dc71693a92c9eb..0f7586f9f748ac 100644 --- a/lib/net.js +++ b/lib/net.js @@ -370,16 +370,6 @@ function writeAfterFIN(chunk, encoding, cb) { } } -Socket.prototype.read = function(n) { - if (n === 0) - return stream.Readable.prototype.read.call(this, n); - - this.read = stream.Readable.prototype.read; - this._consuming = true; - return this.read(n); -}; - - // FIXME(joyeecheung): this method is neither documented nor tested Socket.prototype.listen = function() { debug('socket.listen'); From 077e1870ae8fa7230e5887501fef5fe2baae23e7 Mon Sep 17 00:00:00 2001 From: babygoat Date: Thu, 23 Nov 2017 19:44:20 +0800 Subject: [PATCH 098/218] test: add unhandled rejection guard Add an unhandled rejection function in addons-napi/test_promise/test.js. Also, add a rejection handler to catch the unhandled rejection after introducing the guard and test the reason code. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/17275 Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater --- test/addons-napi/test_promise/test.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/addons-napi/test_promise/test.js b/test/addons-napi/test_promise/test.js index b43ecd87363e44..6dc51b3fa558a2 100644 --- a/test/addons-napi/test_promise/test.js +++ b/test/addons-napi/test_promise/test.js @@ -7,6 +7,8 @@ const common = require('../../common'); const assert = require('assert'); const test_promise = require(`./build/${common.buildType}/test_promise`); +common.crashOnUnhandledRejection(); + // A resolution { const expected_result = 42; @@ -44,7 +46,14 @@ const test_promise = require(`./build/${common.buildType}/test_promise`); } assert.strictEqual(test_promise.isPromise(test_promise.createPromise()), true); -assert.strictEqual(test_promise.isPromise(Promise.reject(-1)), true); + +const rejectPromise = Promise.reject(-1); +const expected_reason = -1; +assert.strictEqual(test_promise.isPromise(rejectPromise), true); +rejectPromise.catch((reason) => { + assert.strictEqual(reason, expected_reason); +}); + assert.strictEqual(test_promise.isPromise(2.4), false); assert.strictEqual(test_promise.isPromise('I promise!'), false); assert.strictEqual(test_promise.isPromise(undefined), false); From 2e100c82be12d94b80c1574fc4e5bed7dfab6ffe Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 12 Dec 2017 21:20:47 -0800 Subject: [PATCH 099/218] test: remove literals that obscure assert messages Remove string literals as messages to `assert.strictEqual()`. They can be misleading here (where perhaps the reason an assertino failed isn't that the deleter wasn't called but rather was called too many times. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/17642 Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Jon Moss Reviewed-By: James M Snell Reviewed-By: Anatoli Papirovski Reviewed-By: Michael Dawson --- test/addons-napi/test_buffer/test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/addons-napi/test_buffer/test.js b/test/addons-napi/test_buffer/test.js index 713966775df18b..740b0474a79c60 100644 --- a/test/addons-napi/test_buffer/test.js +++ b/test/addons-napi/test_buffer/test.js @@ -9,14 +9,13 @@ assert.strictEqual(binding.newBuffer().toString(), binding.theText); assert.strictEqual(binding.newExternalBuffer().toString(), binding.theText); console.log('gc1'); global.gc(); -assert.strictEqual(binding.getDeleterCallCount(), 1, 'deleter was not called'); +assert.strictEqual(binding.getDeleterCallCount(), 1); assert.strictEqual(binding.copyBuffer().toString(), binding.theText); let buffer = binding.staticBuffer(); -assert.strictEqual(binding.bufferHasInstance(buffer), true, - 'buffer type checking fails'); +assert.strictEqual(binding.bufferHasInstance(buffer), true); assert.strictEqual(binding.bufferInfo(buffer), true); buffer = null; global.gc(); console.log('gc2'); -assert.strictEqual(binding.getDeleterCallCount(), 2, 'deleter was not called'); +assert.strictEqual(binding.getDeleterCallCount(), 2); From a744535f99efebd407b63f1681e8331261f30634 Mon Sep 17 00:00:00 2001 From: alnyan Date: Sun, 17 Dec 2017 12:22:46 +0200 Subject: [PATCH 100/218] n-api: fix memory leak in napi_async_destroy() Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/17714 Reviewed-By: Alexey Orlenko Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Timothy Gu Reviewed-By: James M Snell --- src/node_api.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node_api.cc b/src/node_api.cc index f16ceefd5eb5b9..ac0b0959b599d6 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -2770,6 +2770,8 @@ napi_status napi_async_destroy(napi_env env, reinterpret_cast(async_context); node::EmitAsyncDestroy(isolate, *node_async_context); + delete node_async_context; + return napi_clear_last_error(env); } From ad8c079af794d36d9b18e11060ee8ddebcc3d1cd Mon Sep 17 00:00:00 2001 From: Nicholas Drane Date: Thu, 21 Dec 2017 16:48:41 -0600 Subject: [PATCH 101/218] test: remove ambiguous error messages from test_error assert.strictEqual accepts 3 arguments, the last of which allows for user-specified error message to be thrown when the assertion fails. Unfortunately, this error message is less helpful than the default when it is vague. This commit removes vague, user-specified error messages, instead relying on clearer, default error messages. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/17812 Reviewed-By: Colin Ihrig Reviewed-By: Jon Moss Reviewed-By: Rich Trott Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- test/addons-napi/test_error/test.js | 46 ++++++++--------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/test/addons-napi/test_error/test.js b/test/addons-napi/test_error/test.js index bc2b7a8db75d3e..d5c92cb4c3cd2e 100644 --- a/test/addons-napi/test_error/test.js +++ b/test/addons-napi/test_error/test.js @@ -83,56 +83,34 @@ common.expectsError( let error = test_error.createError(); assert.ok(error instanceof Error, 'expected error to be an instance of Error'); -assert.strictEqual(error.message, 'error', 'expected message to be "error"'); +assert.strictEqual(error.message, 'error'); error = test_error.createRangeError(); assert.ok(error instanceof RangeError, 'expected error to be an instance of RangeError'); -assert.strictEqual(error.message, - 'range error', - 'expected message to be "range error"'); +assert.strictEqual(error.message, 'range error'); error = test_error.createTypeError(); assert.ok(error instanceof TypeError, 'expected error to be an instance of TypeError'); -assert.strictEqual(error.message, - 'type error', - 'expected message to be "type error"'); +assert.strictEqual(error.message, 'type error'); error = test_error.createErrorCode(); assert.ok(error instanceof Error, 'expected error to be an instance of Error'); -assert.strictEqual(error.code, - 'ERR_TEST_CODE', - 'expected code to be "ERR_TEST_CODE"'); -assert.strictEqual(error.message, - 'Error [error]', - 'expected message to be "Error [error]"'); -assert.strictEqual(error.name, - 'Error [ERR_TEST_CODE]', - 'expected name to be "Error [ERR_TEST_CODE]"'); +assert.strictEqual(error.code, 'ERR_TEST_CODE'); +assert.strictEqual(error.message, 'Error [error]'); +assert.strictEqual(error.name, 'Error [ERR_TEST_CODE]'); error = test_error.createRangeErrorCode(); assert.ok(error instanceof RangeError, 'expected error to be an instance of RangeError'); -assert.strictEqual(error.message, - 'RangeError [range error]', - 'expected message to be "RangeError [range error]"'); -assert.strictEqual(error.code, - 'ERR_TEST_CODE', - 'expected code to be "ERR_TEST_CODE"'); -assert.strictEqual(error.name, - 'RangeError [ERR_TEST_CODE]', - 'expected name to be "RangeError[ERR_TEST_CODE]"'); +assert.strictEqual(error.message, 'RangeError [range error]'); +assert.strictEqual(error.code, 'ERR_TEST_CODE'); +assert.strictEqual(error.name, 'RangeError [ERR_TEST_CODE]'); error = test_error.createTypeErrorCode(); assert.ok(error instanceof TypeError, 'expected error to be an instance of TypeError'); -assert.strictEqual(error.message, - 'TypeError [type error]', - 'expected message to be "TypeError [type error]"'); -assert.strictEqual(error.code, - 'ERR_TEST_CODE', - 'expected code to be "ERR_TEST_CODE"'); -assert.strictEqual(error.name, - 'TypeError [ERR_TEST_CODE]', - 'expected name to be "TypeError[ERR_TEST_CODE]"'); +assert.strictEqual(error.message, 'TypeError [type error]'); +assert.strictEqual(error.code, 'ERR_TEST_CODE'); +assert.strictEqual(error.name, 'TypeError [ERR_TEST_CODE]'); From 79ecc2c586a372e997490a4afd32b80f0b766072 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Fri, 5 Jan 2018 12:19:20 -0500 Subject: [PATCH 102/218] doc: updates examples to use NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Examples in the N-API doc used a mix of nullptr and NULL. We should be consistent and because N-API is a 'C' API I believe using NULL is better. This will avoid any potential confusion as to whether N-API can be used with plain C. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18008 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Tobias Nießen Reviewed-By: Franziska Hinkelmann Reviewed-By: Gireesh Punathil --- doc/api/n-api.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index f9c24fe4a0e2b1..52eb6dbe983cf6 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -905,9 +905,9 @@ napi_value Init(napi_env env, napi_value exports) { napi_status status; napi_property_descriptor desc = {"hello", Method, 0, 0, 0, napi_default, 0}; - if (status != napi_ok) return nullptr; + if (status != napi_ok) return NULL; status = napi_define_properties(env, exports, 1, &desc); - if (status != napi_ok) return nullptr; + if (status != napi_ok) return NULL; return exports; } ``` @@ -919,7 +919,7 @@ napi_value Init(napi_env env, napi_value exports) { napi_value method; napi_status status; status = napi_create_function(env, "exports", Method, NULL, &method)); - if (status != napi_ok) return nullptr; + if (status != napi_ok) return NULL; return method; } ``` @@ -932,21 +932,21 @@ For example, to define a class so that new instances can be created napi_value Init(napi_env env, napi_value exports) { napi_status status; napi_property_descriptor properties[] = { - { "value", nullptr, GetValue, SetValue, 0, napi_default, 0 }, + { "value", NULL, GetValue, SetValue, 0, napi_default, 0 }, DECLARE_NAPI_METHOD("plusOne", PlusOne), DECLARE_NAPI_METHOD("multiply", Multiply), }; napi_value cons; status = - napi_define_class(env, "MyObject", New, nullptr, 3, properties, &cons); - if (status != napi_ok) return nullptr; + napi_define_class(env, "MyObject", New, NULL, 3, properties, &cons); + if (status != napi_ok) return NULL; status = napi_create_reference(env, cons, 1, &constructor); - if (status != napi_ok) return nullptr; + if (status != napi_ok) return NULL; status = napi_set_named_property(env, exports, "MyObject", cons); - if (status != napi_ok) return nullptr; + if (status != napi_ok) return NULL; return exports; } @@ -2364,8 +2364,8 @@ if (status != napi_ok) return status; // Set the properties napi_property_descriptor descriptors[] = { - { "foo", nullptr, 0, 0, 0, fooValue, napi_default, 0 }, - { "bar", nullptr, 0, 0, 0, barValue, napi_default, 0 } + { "foo", NULL, 0, 0, 0, fooValue, napi_default, 0 }, + { "bar", NULL, 0, 0, 0, barValue, napi_default, 0 } } status = napi_define_properties(env, obj, @@ -2876,18 +2876,18 @@ object. A sample module might look as follows: ```C napi_value SayHello(napi_env env, napi_callback_info info) { printf("Hello\n"); - return nullptr; + return NULL; } napi_value Init(napi_env env, napi_value exports) { napi_status status; napi_value fn; - status = napi_create_function(env, nullptr, 0, SayHello, nullptr, &fn); - if (status != napi_ok) return nullptr; + status = napi_create_function(env, NULL, 0, SayHello, nullptr, &fn); + if (status != napi_ok) return NULL; status = napi_set_named_property(env, exports, "sayHello", fn); - if (status != napi_ok) return nullptr; + if (status != napi_ok) return NULL; return exports; } @@ -2952,7 +2952,7 @@ napi_status napi_get_new_target(napi_env env, Returns `napi_ok` if the API succeeded. This API returns the `new.target` of the constructor call. If the current -callback is not a constructor call, the result is `nullptr`. +callback is not a constructor call, the result is `NULL`. ### *napi_new_instance* @@ -3389,7 +3389,7 @@ napi_status napi_async_init(napi_env env, Returns `napi_ok` if the API succeeded. -### *napi_async_destroy** +### napi_async_destroy @@ -3403,7 +3403,7 @@ napi_status napi_async_destroy(napi_env env, Returns `napi_ok` if the API succeeded. -### *napi_make_callback* +### napi_make_callback @@ -1038,7 +1038,7 @@ JavaScript arrays are described in [Section 22.1](https://tc39.github.io/ecma262/#sec-array-objects) of the ECMAScript Language Specification. -#### *napi_create_array_with_length* +#### napi_create_array_with_length @@ -1067,7 +1067,7 @@ JavaScript arrays are described in [Section 22.1](https://tc39.github.io/ecma262/#sec-array-objects) of the ECMAScript Language Specification. -#### *napi_create_arraybuffer* +#### napi_create_arraybuffer @@ -1099,7 +1099,7 @@ JavaScript ArrayBuffer objects are described in [Section 24.1](https://tc39.github.io/ecma262/#sec-arraybuffer-objects) of the ECMAScript Language Specification. -#### *napi_create_buffer* +#### napi_create_buffer @@ -1120,7 +1120,7 @@ Returns `napi_ok` if the API succeeded. This API allocates a `node::Buffer` object. While this is still a fully-supported data structure, in most cases using a TypedArray will suffice. -#### *napi_create_buffer_copy* +#### napi_create_buffer_copy @@ -1145,7 +1145,7 @@ This API allocates a `node::Buffer` object and initializes it with data copied from the passed-in buffer. While this is still a fully-supported data structure, in most cases using a TypedArray will suffice. -#### *napi_create_external* +#### napi_create_external @@ -1212,7 +1212,7 @@ JavaScript ArrayBuffers are described in [Section 24.1](https://tc39.github.io/ecma262/#sec-arraybuffer-objects) of the ECMAScript Language Specification. -#### *napi_create_external_buffer* +#### napi_create_external_buffer @@ -1243,7 +1243,7 @@ structure, in most cases using a TypedArray will suffice. *Note*: For Node.js >=4 `Buffers` are Uint8Arrays. -#### *napi_create_function* +#### napi_create_function @@ -1276,7 +1276,7 @@ JavaScript Functions are described in [Section 19.2](https://tc39.github.io/ecma262/#sec-function-objects) of the ECMAScript Language Specification. -#### *napi_create_object* +#### napi_create_object @@ -1296,7 +1296,7 @@ The JavaScript Object type is described in [Section 6.1.7](https://tc39.github.io/ecma262/#sec-object-type) of the ECMAScript Language Specification. -#### *napi_create_symbol* +#### napi_create_symbol @@ -1319,7 +1319,7 @@ The JavaScript Symbol type is described in [Section 19.4](https://tc39.github.io/ecma262/#sec-symbol-objects) of the ECMAScript Language Specification. -#### *napi_create_typedarray* +#### napi_create_typedarray @@ -1355,7 +1355,7 @@ JavaScript TypedArray Objects are described in of the ECMAScript Language Specification. -#### *napi_create_dataview* +#### napi_create_dataview @@ -1389,7 +1389,7 @@ JavaScript DataView Objects are described in [Section 24.3][] of the ECMAScript Language Specification. ### Functions to convert from C types to N-API -#### *napi_create_int32* +#### napi_create_int32 @@ -1410,7 +1410,7 @@ The JavaScript Number type is described in [Section 6.1.6](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type) of the ECMAScript Language Specification. -#### *napi_create_uint32* +#### napi_create_uint32 @@ -1431,7 +1431,7 @@ The JavaScript Number type is described in [Section 6.1.6](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type) of the ECMAScript Language Specification. -#### *napi_create_int64* +#### napi_create_int64 @@ -1458,7 +1458,7 @@ outside the range of [`Number.MAX_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.max_safe_integer) (2^53 - 1) will lose precision. -#### *napi_create_double* +#### napi_create_double @@ -1479,7 +1479,7 @@ The JavaScript Number type is described in [Section 6.1.6](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type) of the ECMAScript Language Specification. -#### *napi_create_string_latin1* +#### napi_create_string_latin1 @@ -1504,7 +1504,7 @@ The JavaScript String type is described in [Section 6.1.4](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-string-type) of the ECMAScript Language Specification. -#### *napi_create_string_utf16* +#### napi_create_string_utf16 @@ -1529,7 +1529,7 @@ The JavaScript String type is described in [Section 6.1.4](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-string-type) of the ECMAScript Language Specification. -#### *napi_create_string_utf8* +#### napi_create_string_utf8 @@ -1555,7 +1555,7 @@ The JavaScript String type is described in of the ECMAScript Language Specification. ### Functions to convert from N-API to C types -#### *napi_get_array_length* +#### napi_get_array_length @@ -1578,7 +1578,7 @@ Array length is described in [Section 22.1.4.1](https://tc39.github.io/ecma262/#sec-properties-of-array-instances-length) of the ECMAScript Language Specification. -#### *napi_get_arraybuffer_info* +#### napi_get_arraybuffer_info @@ -1606,7 +1606,7 @@ which can be used to guarantee control over the lifetime of the ArrayBuffer. It's also safe to use the returned data buffer within the same callback as long as there are no calls to other APIs that might trigger a GC. -#### *napi_get_buffer_info* +#### napi_get_buffer_info @@ -1630,7 +1630,7 @@ and it's length. *Warning*: Use caution while using this API since the underlying data buffer's lifetime is not guaranteed if it's managed by the VM. -#### *napi_get_prototype* +#### napi_get_prototype @@ -1648,7 +1648,7 @@ not the same as the function's `prototype` property). Returns `napi_ok` if the API succeeded. -#### *napi_get_typedarray_info* +#### napi_get_typedarray_info @@ -1680,7 +1680,7 @@ is managed by the VM -#### *napi_get_dataview_info* +#### napi_get_dataview_info @@ -1708,7 +1708,7 @@ Returns `napi_ok` if the API succeeded. This API returns various properties of a DataView. -#### *napi_get_value_bool* +#### napi_get_value_bool @@ -1727,7 +1727,7 @@ passed in it returns `napi_boolean_expected`. This API returns the C boolean primitive equivalent of the given JavaScript Boolean. -#### *napi_get_value_double* +#### napi_get_value_double @@ -1749,7 +1749,7 @@ This API returns the C double primitive equivalent of the given JavaScript Number. -#### *napi_get_value_external* +#### napi_get_value_external @@ -1769,7 +1769,7 @@ passed in it returns `napi_invalid_arg`. This API retrieves the external data pointer that was previously passed to `napi_create_external()`. -#### *napi_get_value_int32* +#### napi_get_value_int32 @@ -1792,7 +1792,7 @@ of the given JavaScript Number. If the number exceeds the range of the bottom 32 bits. This can result in a large positive number becoming a negative number if the value is > 2^31 -1. -#### *napi_get_value_int64* +#### napi_get_value_int64 @@ -1812,7 +1812,7 @@ is passed in it returns `napi_number_expected`. This API returns the C int64 primitive equivalent of the given JavaScript Number -#### *napi_get_value_string_latin1* +#### napi_get_value_string_latin1 @@ -1839,7 +1839,7 @@ is passed in it returns `napi_string_expected`. This API returns the ISO-8859-1-encoded string corresponding the value passed in. -#### *napi_get_value_string_utf8* +#### napi_get_value_string_utf8 @@ -1865,7 +1865,7 @@ is passed in it returns `napi_string_expected`. This API returns the UTF8-encoded string corresponding the value passed in. -#### *napi_get_value_string_utf16* +#### napi_get_value_string_utf16 @@ -1891,7 +1891,7 @@ is passed in it returns `napi_string_expected`. This API returns the UTF16-encoded string corresponding the value passed in. -#### *napi_get_value_uint32* +#### napi_get_value_uint32 @@ -1913,7 +1913,7 @@ This API returns the C primitive equivalent of the given `napi_value` as a `uint32_t`. ### Functions to get global instances -#### *napi_get_boolean* +#### napi_get_boolean @@ -1931,7 +1931,7 @@ Returns `napi_ok` if the API succeeded. This API is used to return the JavaScript singleton object that is used to represent the given boolean value -#### *napi_get_global* +#### napi_get_global @@ -1946,7 +1946,7 @@ Returns `napi_ok` if the API succeeded. This API returns the global Object. -#### *napi_get_null* +#### napi_get_null @@ -1961,7 +1961,7 @@ Returns `napi_ok` if the API succeeded. This API returns the null Object. -#### *napi_get_undefined* +#### napi_get_undefined @@ -1989,7 +1989,7 @@ These APIs support doing one of the following: 2. Check the type of a JavaScript value 3. Check for equality between two JavaScript values -### *napi_coerce_to_bool* +### napi_coerce_to_bool @@ -2010,7 +2010,7 @@ This API implements the abstract operation ToBoolean as defined in of the ECMAScript Language Specification. This API can be re-entrant if getters are defined on the passed-in Object. -### *napi_coerce_to_number* +### napi_coerce_to_number @@ -2031,7 +2031,7 @@ This API implements the abstract operation ToNumber as defined in of the ECMAScript Language Specification. This API can be re-entrant if getters are defined on the passed-in Object. -### *napi_coerce_to_object* +### napi_coerce_to_object @@ -2052,7 +2052,7 @@ This API implements the abstract operation ToObject as defined in of the ECMAScript Language Specification. This API can be re-entrant if getters are defined on the passed-in Object. -### *napi_coerce_to_string* +### napi_coerce_to_string @@ -2073,7 +2073,7 @@ This API implements the abstract operation ToString as defined in of the ECMAScript Language Specification. This API can be re-entrant if getters are defined on the passed-in Object. -### *napi_typeof* +### napi_typeof @@ -2094,7 +2094,7 @@ the object as defined in [Section 12.5.5][] of the ECMAScript Language Specification. However, it has support for detecting an External value. If `value` has a type that is invalid, an error is returned. -### *napi_instanceof* +### napi_instanceof @@ -2119,7 +2119,7 @@ defined in [Section 12.10.4](https://tc39.github.io/ecma262/#sec-instanceofoperator) of the ECMAScript Language Specification. -### *napi_is_array* +### napi_is_array @@ -2137,7 +2137,7 @@ This API represents invoking the `IsArray` operation on the object as defined in [Section 7.2.2](https://tc39.github.io/ecma262/#sec-isarray) of the ECMAScript Language Specification. -### *napi_is_arraybuffer* +### napi_is_arraybuffer @@ -2153,7 +2153,7 @@ Returns `napi_ok` if the API succeeded. This API checks if the Object passsed in is an array buffer. -### *napi_is_buffer* +### napi_is_buffer @@ -2170,7 +2170,7 @@ Returns `napi_ok` if the API succeeded. This API checks if the Object passsed in is a buffer. -### *napi_is_error* +### napi_is_error @@ -2186,7 +2186,7 @@ Returns `napi_ok` if the API succeeded. This API checks if the Object passsed in is an Error. -### *napi_is_typedarray* +### napi_is_typedarray @@ -2204,7 +2204,7 @@ This API checks if the Object passsed in is a typed array. -### *napi_is_dataview* +### napi_is_dataview @@ -2221,7 +2221,7 @@ Returns `napi_ok` if the API succeeded. This API checks if the Object passed in is a DataView. -### *napi_strict_equals* +### napi_strict_equals @@ -2375,7 +2375,7 @@ if (status != napi_ok) return status; ``` ### Structures -#### *napi_property_attributes* +#### napi_property_attributes ```C typedef enum { napi_default = 0, @@ -2409,7 +2409,7 @@ a static property on a class as opposed to an instance property, which is the default. This is used only by [`napi_define_class`][]. It is ignored by `napi_define_properties`. -#### *napi_property_descriptor* +#### napi_property_descriptor ```C typedef struct { // One of utf8name or name should be NULL. @@ -2455,7 +2455,7 @@ this function is invoked. See [`napi_property_attributes`](#n_api_napi_property_attributes). ### Functions -#### *napi_get_property_names* +#### napi_get_property_names @@ -2476,7 +2476,7 @@ Returns `napi_ok` if the API succeeded. This API returns the array of propertys for the Object passed in -#### *napi_set_property* +#### napi_set_property @@ -2496,7 +2496,7 @@ Returns `napi_ok` if the API succeeded. This API set a property on the Object passed in. -#### *napi_get_property* +#### napi_get_property @@ -2517,7 +2517,7 @@ Returns `napi_ok` if the API succeeded. This API gets the requested property from the Object passed in. -#### *napi_has_property* +#### napi_has_property @@ -2538,7 +2538,7 @@ Returns `napi_ok` if the API succeeded. This API checks if the Object passed in has the named property. -#### *napi_delete_property* +#### napi_delete_property @@ -2560,7 +2560,7 @@ Returns `napi_ok` if the API succeeded. This API attempts to delete the `key` own property from `object`. -#### *napi_has_own_property* +#### napi_has_own_property @@ -2583,7 +2583,7 @@ be a string or a Symbol, or an error will be thrown. N-API will not perform any conversion between data types. -#### *napi_set_named_property* +#### napi_set_named_property @@ -2604,7 +2604,7 @@ Returns `napi_ok` if the API succeeded. This method is equivalent to calling [`napi_set_property`][] with a `napi_value` created from the string passed in as `utf8Name` -#### *napi_get_named_property* +#### napi_get_named_property @@ -2625,7 +2625,7 @@ Returns `napi_ok` if the API succeeded. This method is equivalent to calling [`napi_get_property`][] with a `napi_value` created from the string passed in as `utf8Name` -#### *napi_has_named_property* +#### napi_has_named_property @@ -2646,7 +2646,7 @@ Returns `napi_ok` if the API succeeded. This method is equivalent to calling [`napi_has_property`][] with a `napi_value` created from the string passed in as `utf8Name` -#### *napi_set_element* +#### napi_set_element @@ -2666,7 +2666,7 @@ Returns `napi_ok` if the API succeeded. This API sets and element on the Object passed in. -#### *napi_get_element* +#### napi_get_element @@ -2686,7 +2686,7 @@ Returns `napi_ok` if the API succeeded. This API gets the element at the requested index. -#### *napi_has_element* +#### napi_has_element @@ -2707,7 +2707,7 @@ Returns `napi_ok` if the API succeeded. This API returns if the Object passed in has an element at the requested index. -#### *napi_delete_element* +#### napi_delete_element @@ -2728,7 +2728,7 @@ Returns `napi_ok` if the API succeeded. This API attempts to delete the specified `index` from `object`. -#### *napi_define_properties* +#### napi_define_properties @@ -2771,7 +2771,7 @@ like a regular JavaScript function call, or as a constructor function. -### *napi_call_function* +### napi_call_function @@ -2837,7 +2837,7 @@ status = napi_get_value_int32(env, return_val, &result); if (status != napi_ok) return; ``` -### *napi_create_function* +### napi_create_function @@ -2905,7 +2905,7 @@ myaddon.sayHello(); `NAPI_MODULE` in the earlier snippet but the name of the target in `binding.gyp` responsible for creating the `.node` file. -### *napi_get_cb_info* +### napi_get_cb_info @@ -2935,7 +2935,7 @@ Returns `napi_ok` if the API succeeded. This method is used within a callback function to retrieve details about the call like the arguments and the `this` pointer from a given callback info. -### *napi_get_new_target* +### napi_get_new_target @@ -2954,7 +2954,7 @@ Returns `napi_ok` if the API succeeded. This API returns the `new.target` of the constructor call. If the current callback is not a constructor call, the result is `NULL`. -### *napi_new_instance* +### napi_new_instance @@ -3049,7 +3049,7 @@ if (is_instance) { The reference must be freed once it is no longer needed. -### *napi_define_class* +### napi_define_class @@ -3105,7 +3105,7 @@ case, to prevent the function value from being garbage-collected, create a persistent reference to it using [`napi_create_reference`][] and ensure the reference count is kept >= 1. -### *napi_wrap* +### napi_wrap @@ -3167,7 +3167,7 @@ native instance associated with it by virtue of a previous call to another native instance with the given object, call `napi_remove_wrap()` on it first. -### *napi_unwrap* +### napi_unwrap @@ -3192,7 +3192,7 @@ method or accessor, then the `this` argument to the callback is the wrapper object; the wrapped C++ instance that is the target of the call can be obtained then by calling `napi_unwrap()` on the wrapper object. -### *napi_remove_wrap* +### napi_remove_wrap From ca10fda0640cebbb8e886d05e772a1c210a0dbf2 Mon Sep 17 00:00:00 2001 From: Jinho Bang Date: Mon, 8 Jan 2018 23:37:27 +0900 Subject: [PATCH 107/218] n-api: throw RangeError napi_create_typedarray() According to the ECMA spec, we should throw a RangeError in the following cases: - `(length * elementSize) + offset` > the size of the array passed in - `offset % elementSize` != `0` In the current implementation, this check was omitted. So, the following code will cause a crash. ``` napi_create_typedarray(env, napi_uint16_array, 2 /* length */, buffer, 1 /* byte_offset */, &output_array); ``` This change fixes the problem and write some related tests. Refs: https://tc39.github.io/ecma262/#sec-typedarray-buffer-byteoffset-length Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18037 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Michael Dawson --- doc/api/errors.md | 12 +++++ lib/internal/errors.js | 3 ++ src/node_api.cc | 51 +++++++++++++++---- test/addons-napi/test_typedarray/test.js | 18 +++++++ .../test_typedarray/test_typedarray.c | 28 ++++++++-- 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 069a6c2c9dcd0f..cd25222051bb5a 100755 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1019,6 +1019,18 @@ While using `N-API`, `Constructor.prototype` was not an object. While calling `napi_create_dataview()`, a given `offset` was outside the bounds of the dataview or `offset + length` was larger than a length of given `buffer`. + +### ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT + +While calling `napi_create_typedarray()`, the provided `offset` was not a +multiple of the element size. + + +### ERR_NAPI_INVALID_TYPEDARRAY_LENGTH + +While calling `napi_create_typedarray()`, `(length * size_of_element) + +byte_offset` was larger than the length of given `buffer`. + ### ERR_NO_ICU diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 01f447cb7d525f..7d5dfcd8478b10 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -268,6 +268,9 @@ E('ERR_NAPI_CONS_PROTOTYPE_OBJECT', 'Constructor.prototype must be an object'); E('ERR_NAPI_INVALID_DATAVIEW_ARGS', 'byte_offset + byte_length should be less than or eqaul to the size in ' + 'bytes of the array passed in'); +E('ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT', 'start offset of %s should be a ' + + 'multiple of %s'); +E('ERR_NAPI_INVALID_TYPEDARRAY_LENGTH', 'Invalid typed array length'); E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support'); E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU'); E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s'); diff --git a/src/node_api.cc b/src/node_api.cc index 2101fcba020d95..27ab6707de7a6f 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -142,6 +142,30 @@ struct napi_env__ { (!try_catch.HasCaught() ? napi_ok \ : napi_set_last_error((env), napi_pending_exception)) +#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ + do { \ + if (!(condition)) { \ + napi_throw_range_error((env), (error), (message)); \ + return napi_set_last_error((env), napi_generic_failure); \ + } \ + } while (0) + +#define CREATE_TYPED_ARRAY( \ + env, type, size_of_element, buffer, byte_offset, length, out) \ + do { \ + if ((size_of_element) > 1) { \ + THROW_RANGE_ERROR_IF_FALSE( \ + (env), (byte_offset) % (size_of_element) == 0, \ + "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", \ + "start offset of "#type" should be a multiple of "#size_of_element); \ + } \ + THROW_RANGE_ERROR_IF_FALSE((env), (length) * (size_of_element) + \ + (byte_offset) <= buffer->ByteLength(), \ + "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", \ + "Invalid typed array length"); \ + (out) = v8::type::New((buffer), (byte_offset), (length)); \ + } while (0) + namespace { namespace v8impl { @@ -3063,31 +3087,40 @@ napi_status napi_create_typedarray(napi_env env, switch (type) { case napi_int8_array: - typedArray = v8::Int8Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Int8Array, 1, buffer, byte_offset, length, typedArray); break; case napi_uint8_array: - typedArray = v8::Uint8Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Uint8Array, 1, buffer, byte_offset, length, typedArray); break; case napi_uint8_clamped_array: - typedArray = v8::Uint8ClampedArray::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Uint8ClampedArray, 1, buffer, byte_offset, length, typedArray); break; case napi_int16_array: - typedArray = v8::Int16Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Int16Array, 2, buffer, byte_offset, length, typedArray); break; case napi_uint16_array: - typedArray = v8::Uint16Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Uint16Array, 2, buffer, byte_offset, length, typedArray); break; case napi_int32_array: - typedArray = v8::Int32Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Int32Array, 4, buffer, byte_offset, length, typedArray); break; case napi_uint32_array: - typedArray = v8::Uint32Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Uint32Array, 4, buffer, byte_offset, length, typedArray); break; case napi_float32_array: - typedArray = v8::Float32Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Float32Array, 4, buffer, byte_offset, length, typedArray); break; case napi_float64_array: - typedArray = v8::Float64Array::New(buffer, byte_offset, length); + CREATE_TYPED_ARRAY( + env, Float64Array, 8, buffer, byte_offset, length, typedArray); break; default: return napi_set_last_error(env, napi_invalid_arg); diff --git a/test/addons-napi/test_typedarray/test.js b/test/addons-napi/test_typedarray/test.js index 27ef054fe4635e..4a4e79ebe7bcdb 100644 --- a/test/addons-napi/test_typedarray/test.js +++ b/test/addons-napi/test_typedarray/test.js @@ -55,3 +55,21 @@ arrayTypes.forEach((currentType) => { assert.notStrictEqual(theArray, template); assert.strictEqual(theArray.buffer, buffer); }); + +arrayTypes.forEach((currentType) => { + const template = Reflect.construct(currentType, buffer); + assert.throws(() => { + test_typedarray.CreateTypedArray(template, buffer, 0, 136); + }, /Invalid typed array length/); +}); + +const nonByteArrayTypes = [ Int16Array, Uint16Array, Int32Array, Uint32Array, + Float32Array, Float64Array ]; +nonByteArrayTypes.forEach((currentType) => { + const template = Reflect.construct(currentType, buffer); + assert.throws(() => { + test_typedarray.CreateTypedArray(template, buffer, + currentType.BYTES_PER_ELEMENT + 1, 1); + console.log(`start of offset ${currentType}`); + }, /start offset of/); +}); diff --git a/test/addons-napi/test_typedarray/test_typedarray.c b/test/addons-napi/test_typedarray/test_typedarray.c index 0325faedd09f8e..82c7a5bf361d30 100644 --- a/test/addons-napi/test_typedarray/test_typedarray.c +++ b/test/addons-napi/test_typedarray/test_typedarray.c @@ -97,11 +97,11 @@ napi_value External(napi_env env, napi_callback_info info) { } napi_value CreateTypedArray(napi_env env, napi_callback_info info) { - size_t argc = 2; - napi_value args[2]; + size_t argc = 4; + napi_value args[4]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); - NAPI_ASSERT(env, argc == 2, "Wrong number of arguments"); + NAPI_ASSERT(env, argc == 2 || argc == 4, "Wrong number of arguments"); napi_value input_array = args[0]; napi_valuetype valuetype0; @@ -136,6 +136,28 @@ napi_value CreateTypedArray(napi_env env, napi_callback_info info) { NAPI_CALL(env, napi_get_typedarray_info( env, input_array, &type, &length, NULL, &in_array_buffer, &byte_offset)); + if (argc == 4) { + napi_valuetype valuetype2; + NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2)); + + NAPI_ASSERT(env, valuetype2 == napi_number, + "Wrong type of arguments. Expects a number as third argument."); + + uint32_t uint32_length; + NAPI_CALL(env, napi_get_value_uint32(env, args[2], &uint32_length)); + length = uint32_length; + + napi_valuetype valuetype3; + NAPI_CALL(env, napi_typeof(env, args[3], &valuetype3)); + + NAPI_ASSERT(env, valuetype3 == napi_number, + "Wrong type of arguments. Expects a number as third argument."); + + uint32_t uint32_byte_offset; + NAPI_CALL(env, napi_get_value_uint32(env, args[3], &uint32_byte_offset)); + byte_offset = uint32_byte_offset; + } + napi_value output_array; NAPI_CALL(env, napi_create_typedarray( env, type, length, input_buffer, byte_offset, &output_array)); From 8cfa87832d151344a7048ba9fd646136a07a0cb2 Mon Sep 17 00:00:00 2001 From: furstenheim Date: Sun, 14 Jan 2018 19:01:51 +0100 Subject: [PATCH 108/218] test: fixed typos in napi test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18148 Reviewed-By: James M Snell Reviewed-By: Jon Moss Reviewed-By: Colin Ihrig Reviewed-By: Michael Dawson Reviewed-By: Anna Henningsen Reviewed-By: Tobias Nießen Reviewed-By: Luigi Pinca --- test/addons-napi/test_symbol/test_symbol.c | 2 +- test/addons-napi/test_typedarray/test_typedarray.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/addons-napi/test_symbol/test_symbol.c b/test/addons-napi/test_symbol/test_symbol.c index 6acc4c55db0b2b..c91b6ae54f4932 100644 --- a/test/addons-napi/test_symbol/test_symbol.c +++ b/test/addons-napi/test_symbol/test_symbol.c @@ -12,7 +12,7 @@ napi_value Test(napi_env env, napi_callback_info info) { NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); NAPI_ASSERT(env, valuetype == napi_symbol, - "Wrong type of argments. Expects a symbol."); + "Wrong type of arguments. Expects a symbol."); char buffer[128]; size_t buffer_size = 128; diff --git a/test/addons-napi/test_typedarray/test_typedarray.c b/test/addons-napi/test_typedarray/test_typedarray.c index 82c7a5bf361d30..2758a6f53298fe 100644 --- a/test/addons-napi/test_typedarray/test_typedarray.c +++ b/test/addons-napi/test_typedarray/test_typedarray.c @@ -13,20 +13,20 @@ napi_value Multiply(napi_env env, napi_callback_info info) { NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); NAPI_ASSERT(env, valuetype0 == napi_object, - "Wrong type of argments. Expects a typed array as first argument."); + "Wrong type of arguments. Expects a typed array as first argument."); napi_value input_array = args[0]; bool is_typedarray; NAPI_CALL(env, napi_is_typedarray(env, input_array, &is_typedarray)); NAPI_ASSERT(env, is_typedarray, - "Wrong type of argments. Expects a typed array as first argument."); + "Wrong type of arguments. Expects a typed array as first argument."); napi_valuetype valuetype1; NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); NAPI_ASSERT(env, valuetype1 == napi_number, - "Wrong type of argments. Expects a number as second argument."); + "Wrong type of arguments. Expects a number as second argument."); double multiplier; NAPI_CALL(env, napi_get_value_double(env, args[1], &multiplier)); @@ -108,26 +108,26 @@ napi_value CreateTypedArray(napi_env env, napi_callback_info info) { NAPI_CALL(env, napi_typeof(env, input_array, &valuetype0)); NAPI_ASSERT(env, valuetype0 == napi_object, - "Wrong type of argments. Expects a typed array as first argument."); + "Wrong type of arguments. Expects a typed array as first argument."); bool is_typedarray; NAPI_CALL(env, napi_is_typedarray(env, input_array, &is_typedarray)); NAPI_ASSERT(env, is_typedarray, - "Wrong type of argments. Expects a typed array as first argument."); + "Wrong type of arguments. Expects a typed array as first argument."); napi_valuetype valuetype1; napi_value input_buffer = args[1]; NAPI_CALL(env, napi_typeof(env, input_buffer, &valuetype1)); NAPI_ASSERT(env, valuetype1 == napi_object, - "Wrong type of argments. Expects an array buffer as second argument."); + "Wrong type of arguments. Expects an array buffer as second argument."); bool is_arraybuffer; NAPI_CALL(env, napi_is_arraybuffer(env, input_buffer, &is_arraybuffer)); NAPI_ASSERT(env, is_arraybuffer, - "Wrong type of argments. Expects an array buffer as second argument."); + "Wrong type of arguments. Expects an array buffer as second argument."); napi_typedarray_type type; napi_value in_array_buffer; From 84e0a0372717185715949401ad625cc54c6d7b75 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 16 Jan 2018 14:05:26 -0500 Subject: [PATCH 109/218] doc: remove uannecessary Require This was the only instance were we said a parameter was required. It is assumed parameters are required unless the doc says they are option. Remove `Required` to make consistent with the rest of the doc Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18184 Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Rich Trott Reviewed-By: Gireesh Punathil Reviewed-By: Daniel Bevenius --- doc/api/n-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index d884853ec41e52..9b780803e27688 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3283,7 +3283,7 @@ napi_status napi_create_async_work(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] async_resource`: An optional object associated with the async work that will be passed to possible async_hooks [`init` hooks][]. -- `[in] async_resource_name`: An identifier for the kind of resource that is +- `[in] async_resource_name`: Identifier for the kind of resource that is being provided for diagnostic information exposed by the `async_hooks` API. - `[in] execute`: The native function which should be called to excute the logic asynchronously. @@ -3382,7 +3382,7 @@ napi_status napi_async_init(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] async_resource`: An optional object associated with the async work that will be passed to possible `async_hooks` [`init` hooks][]. -- `[in] async_resource_name`: Required identifier for the kind of resource +- `[in] async_resource_name`: Identifier for the kind of resource that is being provided for diagnostic information exposed by the `async_hooks` API. - `[out] result`: The initialized async context. From e15f57745dad7ec3faedc09613901ff9ece8b6d0 Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Sat, 13 Jan 2018 16:51:28 -0500 Subject: [PATCH 110/218] timers: allow Immediates to be unrefed Refactor Immediates handling to allow for them to be unrefed, similar to setTimeout, but without extra handles. Document the new `immediate.ref()` and `immediate.unref()` methods. Add SetImmediateUnref on the C++ side. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18139 Reviewed-By: Anna Henningsen Reviewed-By: Franziska Hinkelmann Reviewed-By: James M Snell --- test/addons-napi/test_uv_loop/test_uv_loop.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/addons-napi/test_uv_loop/test_uv_loop.cc b/test/addons-napi/test_uv_loop/test_uv_loop.cc index 44819f72bb6b9d..048e25af9ddfb3 100644 --- a/test/addons-napi/test_uv_loop/test_uv_loop.cc +++ b/test/addons-napi/test_uv_loop/test_uv_loop.cc @@ -24,6 +24,15 @@ void* SetImmediate(napi_env env, T&& cb) { assert(cb() != nullptr); }); + // Idle handle is needed only to stop the event loop from blocking in poll. + uv_idle_t* idle = new uv_idle_t; + uv_idle_init(loop, idle); + uv_idle_start(idle, [](uv_idle_t* idle) { + uv_close(reinterpret_cast(idle), [](uv_handle_t* handle) { + delete reinterpret_cast(handle); + }); + }); + return nullptr; } From 5b1b74c5a59ba6e238e8eed314697e468107613e Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 23 Jan 2018 22:18:38 -0800 Subject: [PATCH 111/218] test: refactor addons-napi/test_exception/test.js * provide block scoping to prevent unintended side effects * remove confusing and unnecessary assertion message * use consisitent `actual, expected` argument order for assertions Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18340 Reviewed-By: Colin Ihrig Reviewed-By: Jon Moss Reviewed-By: James M Snell --- test/addons-napi/test_exception/test.js | 86 ++++++++++++------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/test/addons-napi/test_exception/test.js b/test/addons-napi/test_exception/test.js index 8bd2f50b12b15f..83961411df7574 100644 --- a/test/addons-napi/test_exception/test.js +++ b/test/addons-napi/test_exception/test.js @@ -4,49 +4,47 @@ const common = require('../../common'); const test_exception = require(`./build/${common.buildType}/test_exception`); const assert = require('assert'); const theError = new Error('Some error'); -function throwTheError() { - throw theError; + +{ + const throwTheError = () => { throw theError; }; + + // Test that the native side successfully captures the exception + let returnedError = test_exception.returnException(throwTheError); + assert.strictEqual(theError, returnedError); + + // Test that the native side passes the exception through + assert.throws( + () => { test_exception.allowException(throwTheError); }, + (err) => err === theError + ); + + // Test that the exception thrown above was marked as pending + // before it was handled on the JS side + assert.strictEqual(test_exception.wasPending(), true, + 'VM was marked as having an exception pending' + + ' when it was allowed through'); + + // Test that the native side does not capture a non-existing exception + returnedError = test_exception.returnException(common.mustCall()); + assert.strictEqual(returnedError, undefined, + 'Returned error should be undefined when no exception is' + + ` thrown, but ${returnedError} was passed`); } -let caughtError; - -// Test that the native side successfully captures the exception -let returnedError = test_exception.returnException(throwTheError); -assert.strictEqual(theError, returnedError); - -// Test that the native side passes the exception through -assert.throws( - () => { - test_exception.allowException(throwTheError); - }, - function(err) { - return err === theError; - }, - 'Thrown exception was allowed to pass through unhindered' -); - -// Test that the exception thrown above was marked as pending -// before it was handled on the JS side -assert.strictEqual(test_exception.wasPending(), true, - 'VM was marked as having an exception pending' + - ' when it was allowed through'); - -// Test that the native side does not capture a non-existing exception -returnedError = test_exception.returnException(common.mustCall()); -assert.strictEqual(undefined, returnedError, - 'Returned error should be undefined when no exception is' + - ` thrown, but ${returnedError} was passed`); - -// Test that no exception appears that was not thrown by us -try { - test_exception.allowException(common.mustCall()); -} catch (anError) { - caughtError = anError; + +{ + // Test that no exception appears that was not thrown by us + let caughtError; + try { + test_exception.allowException(common.mustCall()); + } catch (anError) { + caughtError = anError; + } + assert.strictEqual(caughtError, undefined, + 'No exception originated on the native side, but' + + ` ${caughtError} was passed`); + + // Test that the exception state remains clear when no exception is thrown + assert.strictEqual(test_exception.wasPending(), false, + 'VM was not marked as having an exception pending' + + ' when none was allowed through'); } -assert.strictEqual(undefined, caughtError, - 'No exception originated on the native side, but' + - ` ${caughtError} was passed`); - -// Test that the exception state remains clear when no exception is thrown -assert.strictEqual(test_exception.wasPending(), false, - 'VM was not marked as having an exception pending' + - ' when none was allowed through'); From af655f586ce3ce23088b20745fa962df8acbdbc7 Mon Sep 17 00:00:00 2001 From: Aaron Kau Date: Sat, 27 Jan 2018 14:20:13 -0500 Subject: [PATCH 112/218] n-api: change assert ok check to notStrictEqual. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18414 Reviewed-By: Gireesh Punathil Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- test/addons-napi/test_general/test.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/addons-napi/test_general/test.js b/test/addons-napi/test_general/test.js index ee6618c8121289..c89d718ca18575 100644 --- a/test/addons-napi/test_general/test.js +++ b/test/addons-napi/test_general/test.js @@ -28,9 +28,9 @@ assert.strictEqual(test_general.testGetPrototype(baseObject), Object.getPrototypeOf(baseObject)); assert.strictEqual(test_general.testGetPrototype(extendedObject), Object.getPrototypeOf(extendedObject)); -assert.ok(test_general.testGetPrototype(baseObject) !== - test_general.testGetPrototype(extendedObject), - 'Prototypes for base and extended should be different'); +// Prototypes for base and extended should be different. +assert.notStrictEqual(test_general.testGetPrototype(baseObject), + test_general.testGetPrototype(extendedObject)); // test version management functions // expected version is currently 1 @@ -70,17 +70,15 @@ assert.strictEqual(test_general.derefItemWasCalled(), true, // Assert that wrapping twice fails. const x = {}; test_general.wrap(x); -assert.throws(function() { - test_general.wrap(x); -}, Error); +assert.throws(() => test_general.wrap(x), Error); // Ensure that wrapping, removing the wrap, and then wrapping again works. const y = {}; test_general.wrap(y); test_general.removeWrap(y); -assert.doesNotThrow(function() { - test_general.wrap(y); -}, Error, 'Wrapping twice succeeds if a remove_wrap() separates the instances'); +assert.doesNotThrow(() => test_general.wrap(y), Error, + 'Wrapping twice succeeds if a remove_wrap()' + + ' separates the instances'); // Ensure that removing a wrap and garbage collecting does not fire the // finalize callback. From bebcdfe382df24dae62c20c1a9e7699e7c83936d Mon Sep 17 00:00:00 2001 From: Ben Wilcox Date: Sat, 27 Jan 2018 14:39:55 -0500 Subject: [PATCH 113/218] test: show pending exception error in napi tests Shows the result of the wasPending in the error message if the assertion fails. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18413 Reviewed-By: James M Snell Reviewed-By: Rich Trott Reviewed-By: Ruben Bridgewater --- test/addons-napi/test_exception/test.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/addons-napi/test_exception/test.js b/test/addons-napi/test_exception/test.js index 83961411df7574..787b7d78b1c72b 100644 --- a/test/addons-napi/test_exception/test.js +++ b/test/addons-napi/test_exception/test.js @@ -20,9 +20,10 @@ const theError = new Error('Some error'); // Test that the exception thrown above was marked as pending // before it was handled on the JS side - assert.strictEqual(test_exception.wasPending(), true, - 'VM was marked as having an exception pending' + - ' when it was allowed through'); + const exception_pending = test_exception.wasPending(); + assert.strictEqual(exception_pending, true, + 'Exception not pending as expected,' + + ` .wasPending() returned ${exception_pending}`); // Test that the native side does not capture a non-existing exception returnedError = test_exception.returnException(common.mustCall()); @@ -44,7 +45,8 @@ const theError = new Error('Some error'); ` ${caughtError} was passed`); // Test that the exception state remains clear when no exception is thrown - assert.strictEqual(test_exception.wasPending(), false, - 'VM was not marked as having an exception pending' + - ' when none was allowed through'); + const exception_pending = test_exception.wasPending(); + assert.strictEqual(exception_pending, false, + 'Exception state did not remain clear as expected,' + + ` .wasPending() returned ${exception_pending}`); } From d89f5937eb801cca499ba951f4bf469b0a057311 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Mon, 22 Jan 2018 23:13:02 -0500 Subject: [PATCH 114/218] n-api: implement wrapping using private properties Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18311 Reviewed-By: Anna Henningsen Reviewed-By: Michael Dawson Fixes: https://github.com/nodejs/node/issues/14367 --- src/env.h | 2 + src/node_api.cc | 160 +++++++++++++++--------------------------------- 2 files changed, 52 insertions(+), 110 deletions(-) diff --git a/src/env.h b/src/env.h index 6113e6d2de26ea..0dc180bf7489e1 100644 --- a/src/env.h +++ b/src/env.h @@ -93,6 +93,8 @@ class ModuleWrap; V(processed_private_symbol, "node:processed") \ V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") \ V(domain_private_symbol, "node:domain") \ + V(napi_env, "node:napi:env") \ + V(napi_wrapper, "node:napi:wrapper") \ // Strings are per-isolate primitives but Environment proxies them // for the sake of convenience. Strings should be ASCII-only. diff --git a/src/node_api.cc b/src/node_api.cc index 27ab6707de7a6f..78841d14882eeb 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -17,6 +17,7 @@ #include #include "node_api.h" #include "node_internals.h" +#include "env.h" static napi_status napi_set_last_error(napi_env env, napi_status error_code, @@ -46,6 +47,9 @@ struct napi_env__ { uv_loop_t* loop = nullptr; }; +#define NAPI_PRIVATE_KEY(context, suffix) \ + (node::Environment::GetCurrent((context))->napi_ ## suffix()) + #define ENV_OBJECT_TEMPLATE(env, prefix, destination, field_count) \ do { \ if ((env)->prefix ## _template.IsEmpty()) { \ @@ -373,6 +377,10 @@ class Reference : private Finalizer { } public: + void* Data() { + return _finalize_data; + } + static Reference* New(napi_env env, v8::Local value, uint32_t initial_refcount, @@ -732,45 +740,6 @@ v8::Local CreateAccessorCallbackData(napi_env env, return cbdata; } -int kWrapperFields = 3; - -// Pointer used to identify items wrapped by N-API. Used by FindWrapper and -// napi_wrap(). -const char napi_wrap_name[] = "N-API Wrapper"; - -// Search the object's prototype chain for the wrapper object. Usually the -// wrapper would be the first in the chain, but it is OK for other objects to -// be inserted in the prototype chain. -static -bool FindWrapper(v8::Local obj, - v8::Local* result = nullptr, - v8::Local* parent = nullptr) { - v8::Local wrapper = obj; - - do { - v8::Local proto = wrapper->GetPrototype(); - if (proto.IsEmpty() || !proto->IsObject()) { - return false; - } - if (parent != nullptr) { - *parent = wrapper; - } - wrapper = proto.As(); - if (wrapper->InternalFieldCount() == kWrapperFields) { - v8::Local external = wrapper->GetInternalField(1); - if (external->IsExternal() && - external.As()->Value() == v8impl::napi_wrap_name) { - break; - } - } - } while (true); - - if (result != nullptr) { - *result = wrapper; - } - return true; -} - static void DeleteEnv(napi_env env, void* data, void* hint) { delete env; } @@ -787,11 +756,8 @@ napi_env GetEnv(v8::Local context) { // because we need to stop hard if either of them is empty. // // Re https://github.com/nodejs/node/pull/14217#discussion_r128775149 - auto key = v8::Private::ForApi(isolate, - v8::String::NewFromOneByte(isolate, - reinterpret_cast("N-API Environment"), - v8::NewStringType::kInternalized).ToLocalChecked()); - auto value = global->GetPrivate(context, key).ToLocalChecked(); + auto value = global->GetPrivate(context, NAPI_PRIVATE_KEY(context, env)) + .ToLocalChecked(); if (value->IsExternal()) { result = static_cast(value.As()->Value()); @@ -801,7 +767,8 @@ napi_env GetEnv(v8::Local context) { // We must also stop hard if the result of assigning the env to the global // is either nothing or false. - CHECK(global->SetPrivate(context, key, external).FromJust()); + CHECK(global->SetPrivate(context, NAPI_PRIVATE_KEY(context, env), external) + .FromJust()); // Create a self-destructing reference to external that will get rid of the // napi_env when external goes out of scope. @@ -811,28 +778,46 @@ napi_env GetEnv(v8::Local context) { return result; } +enum UnwrapAction { + KeepWrap, + RemoveWrap +}; + static napi_status Unwrap(napi_env env, napi_value js_object, void** result, - v8::Local* wrapper, - v8::Local* parent = nullptr) { + UnwrapAction action) { + NAPI_PREAMBLE(env); CHECK_ARG(env, js_object); - CHECK_ARG(env, result); + if (action == KeepWrap) { + CHECK_ARG(env, result); + } + + v8::Isolate* isolate = env->isolate; + v8::Local context = isolate->GetCurrentContext(); v8::Local value = v8impl::V8LocalValueFromJsValue(js_object); RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); v8::Local obj = value.As(); - RETURN_STATUS_IF_FALSE( - env, v8impl::FindWrapper(obj, wrapper, parent), napi_invalid_arg); + auto val = obj->GetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) + .ToLocalChecked(); + RETURN_STATUS_IF_FALSE(env, val->IsExternal(), napi_invalid_arg); + Reference* reference = + static_cast(val.As()->Value()); - v8::Local unwrappedValue = (*wrapper)->GetInternalField(0); - RETURN_STATUS_IF_FALSE(env, unwrappedValue->IsExternal(), napi_invalid_arg); + if (result) { + *result = reference->Data(); + } - *result = unwrappedValue.As()->Value(); + if (action == RemoveWrap) { + CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) + .FromJust()); + Reference::Delete(reference); + } - return napi_ok; + return GET_RETURN_STATUS(env); } static @@ -2391,26 +2376,9 @@ napi_status napi_wrap(napi_env env, v8::Local obj = value.As(); // If we've already wrapped this object, we error out. - RETURN_STATUS_IF_FALSE(env, !v8impl::FindWrapper(obj), napi_invalid_arg); - - // Create a wrapper object with an internal field to hold the wrapped pointer - // and a second internal field to identify the owner as N-API. - v8::Local wrapper_template; - ENV_OBJECT_TEMPLATE(env, wrap, wrapper_template, v8impl::kWrapperFields); - - auto maybe_object = wrapper_template->NewInstance(context); - CHECK_MAYBE_EMPTY(env, maybe_object, napi_generic_failure); - v8::Local wrapper = maybe_object.ToLocalChecked(); - - // Store the pointer as an external in the wrapper. - wrapper->SetInternalField(0, v8::External::New(isolate, native_object)); - wrapper->SetInternalField(1, v8::External::New(isolate, - reinterpret_cast(const_cast(v8impl::napi_wrap_name)))); - - // Insert the wrapper into the object's prototype chain. - v8::Local proto = obj->GetPrototype(); - CHECK(wrapper->SetPrototype(context, proto).FromJust()); - CHECK(obj->SetPrototype(context, wrapper).FromJust()); + RETURN_STATUS_IF_FALSE(env, + !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).FromJust(), + napi_invalid_arg); v8impl::Reference* reference = nullptr; if (result != nullptr) { @@ -2422,52 +2390,24 @@ napi_status napi_wrap(napi_env env, reference = v8impl::Reference::New( env, obj, 0, false, finalize_cb, native_object, finalize_hint); *result = reinterpret_cast(reference); - } else if (finalize_cb != nullptr) { - // Create a self-deleting reference just for the finalize callback. - reference = v8impl::Reference::New( - env, obj, 0, true, finalize_cb, native_object, finalize_hint); + } else { + // Create a self-deleting reference. + reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, + native_object, finalize_cb == nullptr ? nullptr : finalize_hint); } - if (reference != nullptr) { - wrapper->SetInternalField(2, v8::External::New(isolate, reference)); - } + CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), + v8::External::New(isolate, reference)).FromJust()); return GET_RETURN_STATUS(env); } napi_status napi_unwrap(napi_env env, napi_value obj, void** result) { - // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw - // JS exceptions. - CHECK_ENV(env); - v8::Local wrapper; - return napi_set_last_error(env, v8impl::Unwrap(env, obj, result, &wrapper)); + return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap); } napi_status napi_remove_wrap(napi_env env, napi_value obj, void** result) { - NAPI_PREAMBLE(env); - v8::Local wrapper; - v8::Local parent; - napi_status status = v8impl::Unwrap(env, obj, result, &wrapper, &parent); - if (status != napi_ok) { - return napi_set_last_error(env, status); - } - - v8::Local external = wrapper->GetInternalField(2); - if (external->IsExternal()) { - v8impl::Reference::Delete( - static_cast(external.As()->Value())); - } - - if (!parent.IsEmpty()) { - v8::Maybe maybe = parent->SetPrototype( - env->isolate->GetCurrentContext(), wrapper->GetPrototype()); - CHECK_MAYBE_NOTHING(env, maybe, napi_generic_failure); - if (!maybe.FromMaybe(false)) { - return napi_set_last_error(env, napi_generic_failure); - } - } - - return GET_RETURN_STATUS(env); + return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap); } napi_status napi_create_external(napi_env env, From 969a52099033b7b79cbe7d5b1df53202a66ff40d Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 2 Feb 2018 17:27:01 +0100 Subject: [PATCH 115/218] n-api: wrap control flow macro in do/while Make CHECK_ENV() safe to use in the following context: if (condition) CHECK_ENV(env); else something_else(); Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18532 Reviewed-By: Michael Dawson Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Ali Ijaz Sheikh Reviewed-By: Ruben Bridgewater Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Daniel Bevenius --- src/node_api.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/node_api.cc b/src/node_api.cc index 78841d14882eeb..611f6c8085e384 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -70,10 +70,12 @@ struct napi_env__ { } \ } while (0) -#define CHECK_ENV(env) \ - if ((env) == nullptr) { \ - return napi_invalid_arg; \ - } +#define CHECK_ENV(env) \ + do { \ + if ((env) == nullptr) { \ + return napi_invalid_arg; \ + } \ + } while (0) #define CHECK_ARG(env, arg) \ RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) From 7ed1dfef2864f785ecc7e72addd7c0ae6108fa89 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Fri, 2 Feb 2018 10:33:01 -0500 Subject: [PATCH 116/218] doc: remove usage of you in n-api doc We avoid using 'you' in the documentation based on our guidelines. Remove usage in the n-api doc. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18528 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Gireesh Punathil Reviewed-By: Daniel Bevenius --- doc/api/n-api.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 9b780803e27688..d10b2f1aa63452 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3161,11 +3161,8 @@ required in order to enable correct proper of the reference. Afterward, additional manipulation of the wrapper's prototype chain may cause `napi_unwrap()` to fail. -*Note*: Calling `napi_wrap()` a second time on an object that already has a -native instance associated with it by virtue of a previous call to -`napi_wrap()` will cause an error to be returned. If you wish to associate -another native instance with the given object, call `napi_remove_wrap()` on it -first. +Calling napi_wrap() a second time on an object will return an error. To associate +another native instance with the object, use napi_remove_wrap() first. ### napi_unwrap +```C +NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, + napi_value resource_object, + napi_async_context context, + napi_callback_scope* result) +``` +- `[in] env`: The environment that the API is invoked under. +- `[in] resource_object`: An optional object associated with the async work + that will be passed to possible async_hooks [`init` hooks][]. +- `[in] context`: Context for the async operation that is +invoking the callback. This should be a value previously obtained +from [`napi_async_init`][]. +- `[out] result`: The newly created scope. + +There are cases(for example resolving promises) where it is +necessary to have the equivalent of the scope associated with a callback +in place when making certain N-API calls. If there is no other script on +the stack the [`napi_open_callback_scope`][] and +[`napi_close_callback_scope`][] functions can be used to open/close +the required scope. + +### *napi_close_callback_scope* + +```C +NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, + napi_callback_scope scope) +``` +- `[in] env`: The environment that the API is invoked under. +- `[in] scope`: The scope to be closed. + ## Version Management ### napi_get_node_version @@ -3725,6 +3761,7 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, [`napi_async_init`]: #n_api_napi_async_init [`napi_cancel_async_work`]: #n_api_napi_cancel_async_work [`napi_close_escapable_handle_scope`]: #n_api_napi_close_escapable_handle_scope +[`napi_close_callback_scope`]: #n_api_napi_close_callback_scope [`napi_close_handle_scope`]: #n_api_napi_close_handle_scope [`napi_create_async_work`]: #n_api_napi_create_async_work [`napi_create_error`]: #n_api_napi_create_error @@ -3750,6 +3787,7 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, [`napi_get_last_error_info`]: #n_api_napi_get_last_error_info [`napi_get_and_clear_last_exception`]: #n_api_napi_get_and_clear_last_exception [`napi_make_callback`]: #n_api_napi_make_callback +[`napi_open_callback_scope`]: #n_api_napi_open_callback_scope [`napi_open_escapable_handle_scope`]: #n_api_napi_open_escapable_handle_scope [`napi_open_handle_scope`]: #n_api_napi_open_handle_scope [`napi_property_descriptor`]: #n_api_napi_property_descriptor diff --git a/src/node_api.cc b/src/node_api.cc index 611f6c8085e384..2c5f3066f728b1 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -44,6 +44,7 @@ struct napi_env__ { v8::Persistent accessor_data_template; napi_extended_error_info last_error; int open_handle_scopes = 0; + int open_callback_scopes = 0; uv_loop_t* loop = nullptr; }; @@ -253,6 +254,18 @@ V8EscapableHandleScopeFromJsEscapableHandleScope( return reinterpret_cast(s); } +static +napi_callback_scope JsCallbackScopeFromV8CallbackScope( + node::CallbackScope* s) { + return reinterpret_cast(s); +} + +static +node::CallbackScope* V8CallbackScopeFromJsCallbackScope( + napi_callback_scope s) { + return reinterpret_cast(s); +} + //=== Conversion between V8 Handles and napi_value ======================== // This asserts v8::Local<> will always be implemented with a single @@ -544,6 +557,7 @@ class CallbackWrapperBase : public CallbackWrapper { napi_clear_last_error(env); int open_handle_scopes = env->open_handle_scopes; + int open_callback_scopes = env->open_callback_scopes; napi_value result = cb(env, cbinfo_wrapper); @@ -552,6 +566,7 @@ class CallbackWrapperBase : public CallbackWrapper { } CHECK_EQ(env->open_handle_scopes, open_handle_scopes); + CHECK_EQ(env->open_callback_scopes, open_callback_scopes); if (!env->last_exception.IsEmpty()) { isolate->ThrowException( @@ -911,7 +926,8 @@ const char* error_messages[] = {nullptr, "An exception is pending", "The async work item was cancelled", "napi_escape_handle already called on scope", - "Invalid handle scope usage"}; + "Invalid handle scope usage", + "Invalid callback scope usage"}; static inline napi_status napi_clear_last_error(napi_env env) { env->last_error.error_code = napi_ok; @@ -942,9 +958,9 @@ napi_status napi_get_last_error_info(napi_env env, // We don't have a napi_status_last as this would result in an ABI // change each time a message was added. static_assert( - node::arraysize(error_messages) == napi_handle_scope_mismatch + 1, + node::arraysize(error_messages) == napi_callback_scope_mismatch + 1, "Count of error messages must match count of error values"); - CHECK_LE(env->last_error.error_code, napi_handle_scope_mismatch); + CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); // Wait until someone requests the last error information to fetch the error // message string @@ -2633,6 +2649,46 @@ napi_status napi_escape_handle(napi_env env, return napi_set_last_error(env, napi_escape_called_twice); } +napi_status napi_open_callback_scope(napi_env env, + napi_value resource_object, + napi_async_context async_context_handle, + napi_callback_scope* result) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local context = env->isolate->GetCurrentContext(); + + node::async_context* node_async_context = + reinterpret_cast(async_context_handle); + + v8::Local resource; + CHECK_TO_OBJECT(env, context, resource, resource_object); + + *result = v8impl::JsCallbackScopeFromV8CallbackScope( + new node::CallbackScope(env->isolate, + resource, + *node_async_context)); + + env->open_callback_scopes++; + return napi_clear_last_error(env); +} + +napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) { + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, scope); + if (env->open_callback_scopes == 0) { + return napi_callback_scope_mismatch; + } + + env->open_callback_scopes--; + delete v8impl::V8CallbackScopeFromJsCallbackScope(scope); + return napi_clear_last_error(env); +} + napi_status napi_new_instance(napi_env env, napi_value constructor, size_t argc, diff --git a/src/node_api.h b/src/node_api.h index ee0ad3518e13aa..5940c2b4a57ac2 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -424,6 +424,14 @@ NAPI_EXTERN napi_status napi_escape_handle(napi_env env, napi_value escapee, napi_value* result); +NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, + napi_value resource_object, + napi_async_context context, + napi_callback_scope* result); + +NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, + napi_callback_scope scope); + // Methods to support error handling NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); NAPI_EXTERN napi_status napi_throw_error(napi_env env, diff --git a/src/node_api_types.h b/src/node_api_types.h index 230c1f4ae3446f..76f38802e83e2e 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -15,6 +15,7 @@ typedef struct napi_value__ *napi_value; typedef struct napi_ref__ *napi_ref; typedef struct napi_handle_scope__ *napi_handle_scope; typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope; +typedef struct napi_callback_scope__ *napi_callback_scope; typedef struct napi_callback_info__ *napi_callback_info; typedef struct napi_async_context__ *napi_async_context; typedef struct napi_async_work__ *napi_async_work; @@ -70,7 +71,8 @@ typedef enum { napi_pending_exception, napi_cancelled, napi_escape_called_twice, - napi_handle_scope_mismatch + napi_handle_scope_mismatch, + napi_callback_scope_mismatch } napi_status; typedef napi_value (*napi_callback)(napi_env env, diff --git a/test/addons-napi/test_callback_scope/binding.cc b/test/addons-napi/test_callback_scope/binding.cc new file mode 100644 index 00000000000000..e6631b6ac7bb52 --- /dev/null +++ b/test/addons-napi/test_callback_scope/binding.cc @@ -0,0 +1,138 @@ +#include "node_api.h" +#include "uv.h" +#include "../common.h" + +namespace { + +// the test needs to fake out the async structure, so we need to use +// the raw structure here and then cast as done behind the scenes +// in napi calls. +struct async_context { + double async_id; + double trigger_async_id; +}; + + +napi_value RunInCallbackScope(napi_env env, napi_callback_info info) { + size_t argc; + napi_value args[4]; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr)); + NAPI_ASSERT(env, argc == 4 , "Wrong number of arguments"); + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + napi_valuetype valuetype; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_number, + "Wrong type of arguments. Expects a number as second argument."); + + NAPI_CALL(env, napi_typeof(env, args[2], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_number, + "Wrong type of arguments. Expects a number as third argument."); + + NAPI_CALL(env, napi_typeof(env, args[3], &valuetype)); + NAPI_ASSERT(env, valuetype == napi_function, + "Wrong type of arguments. Expects a function as third argument."); + + struct async_context context; + NAPI_CALL(env, napi_get_value_double(env, args[1], &context.async_id)); + NAPI_CALL(env, + napi_get_value_double(env, args[2], &context.trigger_async_id)); + + napi_callback_scope scope = nullptr; + NAPI_CALL( + env, + napi_open_callback_scope(env, + args[0], + reinterpret_cast(&context), + &scope)); + + // if the function has an exception pending after the call that is ok + // so we don't use NAPI_CALL as we must close the callback scope regardless + napi_value result = nullptr; + napi_status function_call_result = + napi_call_function(env, args[0], args[3], 0, nullptr, &result); + if (function_call_result != napi_ok) { + GET_AND_THROW_LAST_ERROR((env)); + } + + NAPI_CALL(env, napi_close_callback_scope(env, scope)); + + return result; +} + +static napi_env shared_env = nullptr; +static napi_deferred deferred = nullptr; + +static void Callback(uv_work_t* req, int ignored) { + napi_env env = shared_env; + + napi_handle_scope handle_scope = nullptr; + NAPI_CALL_RETURN_VOID(env, napi_open_handle_scope(env, &handle_scope)); + + napi_value resource_name; + NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8( + env, "test", NAPI_AUTO_LENGTH, &resource_name)); + napi_async_context context; + NAPI_CALL_RETURN_VOID(env, + napi_async_init(env, nullptr, resource_name, &context)); + + napi_value resource_object; + NAPI_CALL_RETURN_VOID(env, napi_create_object(env, &resource_object)); + + napi_value undefined_value; + NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined_value)); + + napi_callback_scope scope = nullptr; + NAPI_CALL_RETURN_VOID(env, napi_open_callback_scope(env, + resource_object, + context, + &scope)); + + NAPI_CALL_RETURN_VOID(env, + napi_resolve_deferred(env, deferred, undefined_value)); + + NAPI_CALL_RETURN_VOID(env, napi_close_callback_scope(env, scope)); + + NAPI_CALL_RETURN_VOID(env, napi_close_handle_scope(env, handle_scope)); + delete req; +} + +napi_value TestResolveAsync(napi_env env, napi_callback_info info) { + napi_value promise = nullptr; + if (deferred == nullptr) { + shared_env = env; + NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); + + uv_loop_t* loop = nullptr; + NAPI_CALL(env, napi_get_uv_event_loop(env, &loop)); + + uv_work_t* req = new uv_work_t(); + uv_queue_work(loop, + req, + [](uv_work_t*) {}, + Callback); + } + return promise; +} + +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("runInCallbackScope", RunInCallbackScope), + DECLARE_NAPI_PROPERTY("testResolveAsync", TestResolveAsync) + }; + + NAPI_CALL(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + + return exports; +} + +} // anonymous namespace + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_callback_scope/binding.gyp b/test/addons-napi/test_callback_scope/binding.gyp new file mode 100644 index 00000000000000..7ede63d94a0d77 --- /dev/null +++ b/test/addons-napi/test_callback_scope/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], + 'sources': [ 'binding.cc' ] + } + ] +} diff --git a/test/addons-napi/test_callback_scope/test-async-hooks.js b/test/addons-napi/test_callback_scope/test-async-hooks.js new file mode 100644 index 00000000000000..1a11bf60398f9b --- /dev/null +++ b/test/addons-napi/test_callback_scope/test-async-hooks.js @@ -0,0 +1,29 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); +const async_hooks = require('async_hooks'); + +// The async_hook that we enable would register the process.emitWarning() +// call from loading the N-API addon as asynchronous activity because +// it contains a process.nextTick() call. Monkey patch it to be a no-op +// before we load the addon in order to avoid this. +process.emitWarning = () => {}; + +const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); + +let insideHook = false; +async_hooks.createHook({ + before: common.mustCall((id) => { + assert.strictEqual(id, 1000); + insideHook = true; + }), + after: common.mustCall((id) => { + assert.strictEqual(id, 1000); + insideHook = false; + }) +}).enable(); + +runInCallbackScope({}, 1000, 1000, () => { + assert(insideHook); +}); diff --git a/test/addons-napi/test_callback_scope/test-resolve-async.js b/test/addons-napi/test_callback_scope/test-resolve-async.js new file mode 100644 index 00000000000000..e9f4b9044c0154 --- /dev/null +++ b/test/addons-napi/test_callback_scope/test-resolve-async.js @@ -0,0 +1,13 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); +const { testResolveAsync } = require(`./build/${common.buildType}/binding`); + +let called = false; +testResolveAsync().then(common.mustCall(() => { + called = true; +})); + +setTimeout(common.mustCall(() => { assert(called); }), + common.platformTimeout(20)); diff --git a/test/addons-napi/test_callback_scope/test.js b/test/addons-napi/test_callback_scope/test.js new file mode 100644 index 00000000000000..2f2efe5f47b98a --- /dev/null +++ b/test/addons-napi/test_callback_scope/test.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); +const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); + +assert.strictEqual(runInCallbackScope({}, 0, 0, () => 42), 42); + +{ + process.once('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); + })); + + runInCallbackScope({}, 0, 0, () => { + throw new Error('foo'); + }); +} From 9244e1d2347f2a00e5ab3a95a6b13551f0f39dad Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Fri, 2 Feb 2018 20:34:19 -0500 Subject: [PATCH 119/218] n-api: remove extra reference from test Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18542 Reviewed-By: Michael Dawson Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Colin Ihrig --- test/addons-napi/test_constructor/test_constructor.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/addons-napi/test_constructor/test_constructor.c b/test/addons-napi/test_constructor/test_constructor.c index 70f53ec5a92f55..4ee8323dd6ed40 100644 --- a/test/addons-napi/test_constructor/test_constructor.c +++ b/test/addons-napi/test_constructor/test_constructor.c @@ -3,7 +3,6 @@ static double value_ = 1; static double static_value_ = 10; -napi_ref constructor_; napi_value GetValue(napi_env env, napi_callback_info info) { size_t argc = 0; @@ -80,8 +79,6 @@ napi_value Init(napi_env env, napi_value exports) { NAPI_CALL(env, napi_define_class(env, "MyObject", NAPI_AUTO_LENGTH, New, NULL, sizeof(properties)/sizeof(*properties), properties, &cons)); - NAPI_CALL(env, napi_create_reference(env, cons, 1, &constructor_)); - return cons; } From 4d4360747440ca8edb62db61289c35e6c5bb0b8b Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Tue, 6 Feb 2018 02:00:12 +0200 Subject: [PATCH 120/218] doc: fix typo in n-api.md Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18590 Reviewed-By: Jon Moss Reviewed-By: Weijia Wang Reviewed-By: Colin Ihrig Reviewed-By: Rich Trott Reviewed-By: Daniel Bevenius Reviewed-By: Yuta Hiroto --- doc/api/n-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 8ed758b0dfe460..4b2bb5d44b69e9 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3464,7 +3464,7 @@ invoking the callback. This should be a value previously obtained from [`napi_async_init`][]. - `[out] result`: The newly created scope. -There are cases(for example resolving promises) where it is +There are cases (for example resolving promises) where it is necessary to have the equivalent of the scope associated with a callback in place when making certain N-API calls. If there is no other script on the stack the [`napi_open_callback_scope`][] and From 5c0983e5a2393d8c4af8f88dd2e03fc19b25a476 Mon Sep 17 00:00:00 2001 From: Bhavani Shankar Date: Thu, 1 Feb 2018 15:32:41 +0530 Subject: [PATCH 121/218] test: improve error message output Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18498 Reviewed-By: Ruben Bridgewater Reviewed-By: Anatoli Papirovski Reviewed-By: Luigi Pinca Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil Reviewed-By: Michael Dawson --- test/addons-napi/test_general/test.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/test/addons-napi/test_general/test.js b/test/addons-napi/test_general/test.js index c89d718ca18575..bcaa13d894bedc 100644 --- a/test/addons-napi/test_general/test.js +++ b/test/addons-napi/test_general/test.js @@ -63,22 +63,25 @@ let w = {}; test_general.wrap(w); w = null; global.gc(); -assert.strictEqual(test_general.derefItemWasCalled(), true, +const derefItemWasCalled = test_general.derefItemWasCalled(); +assert.strictEqual(derefItemWasCalled, true, 'deref_item() was called upon garbage collecting a ' + - 'wrapped object'); + 'wrapped object. test_general.derefItemWasCalled() ' + + `returned ${derefItemWasCalled}`); + // Assert that wrapping twice fails. const x = {}; test_general.wrap(x); -assert.throws(() => test_general.wrap(x), Error); +common.expectsError(() => test_general.wrap(x), + { type: Error, message: 'Invalid argument' }); // Ensure that wrapping, removing the wrap, and then wrapping again works. const y = {}; test_general.wrap(y); test_general.removeWrap(y); -assert.doesNotThrow(() => test_general.wrap(y), Error, - 'Wrapping twice succeeds if a remove_wrap()' + - ' separates the instances'); +// Wrapping twice succeeds if a remove_wrap() separates the instances +assert.doesNotThrow(() => test_general.wrap(y)); // Ensure that removing a wrap and garbage collecting does not fire the // finalize callback. @@ -87,8 +90,11 @@ test_general.testFinalizeWrap(z); test_general.removeWrap(z); z = null; global.gc(); -assert.strictEqual(test_general.finalizeWasCalled(), false, - 'finalize callback was not called upon garbage collection'); +const finalizeWasCalled = test_general.finalizeWasCalled(); +assert.strictEqual(finalizeWasCalled, false, + 'finalize callback was not called upon garbage collection.' + + ' test_general.finalizeWasCalled() ' + + `returned ${finalizeWasCalled}`); // test napi_adjust_external_memory const adjustedValue = test_general.testAdjustExternalMemory(); From 2838f9b150da5889f8cc890fc8db33d654a4d00d Mon Sep 17 00:00:00 2001 From: Jack Horton Date: Mon, 5 Feb 2018 11:00:33 -0800 Subject: [PATCH 122/218] test: convert new tests to use error types Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18581 Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- test/addons-napi/test_typedarray/test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/addons-napi/test_typedarray/test.js b/test/addons-napi/test_typedarray/test.js index 4a4e79ebe7bcdb..4c139d92200fe3 100644 --- a/test/addons-napi/test_typedarray/test.js +++ b/test/addons-napi/test_typedarray/test.js @@ -60,7 +60,7 @@ arrayTypes.forEach((currentType) => { const template = Reflect.construct(currentType, buffer); assert.throws(() => { test_typedarray.CreateTypedArray(template, buffer, 0, 136); - }, /Invalid typed array length/); + }, RangeError); }); const nonByteArrayTypes = [ Int16Array, Uint16Array, Int32Array, Uint32Array, @@ -71,5 +71,5 @@ nonByteArrayTypes.forEach((currentType) => { test_typedarray.CreateTypedArray(template, buffer, currentType.BYTES_PER_ELEMENT + 1, 1); console.log(`start of offset ${currentType}`); - }, /start offset of/); + }, RangeError); }); From 58688d97dc7807181b10f261f6fcc37345f71325 Mon Sep 17 00:00:00 2001 From: Aonghus O Nia Date: Thu, 8 Feb 2018 17:01:23 -0500 Subject: [PATCH 123/218] doc: fix exporting a function example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missing the length argument in napi_create_function. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18661 Reviewed-By: Michael Dawson Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- doc/api/n-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 4b2bb5d44b69e9..4e9ba918ee5b0d 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -918,7 +918,7 @@ For example, to set a function to be returned by the `require()` for the addon: napi_value Init(napi_env env, napi_value exports) { napi_value method; napi_status status; - status = napi_create_function(env, "exports", Method, NULL, &method); + status = napi_create_function(env, "exports", NAPI_AUTO_LENGTH, Method, NULL, &method); if (status != napi_ok) return NULL; return method; } From 87d0fd821253898a34ce9873f6ce8236a2d4c4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 10 Feb 2018 16:32:01 +0100 Subject: [PATCH 124/218] doc: mark NAPI_AUTO_LENGTH as code Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18697 Reviewed-By: Ruben Bridgewater Reviewed-By: Colin Ihrig Reviewed-By: Jeremiah Senkpiel Reviewed-By: Luigi Pinca --- doc/api/n-api.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 4e9ba918ee5b0d..aad4cadd948ad2 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -554,10 +554,10 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location, - `[in] location`: Optional location at which the error occurred. - `[in] location_len`: The length of the location in bytes, or -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[in] message`: The message associated with the error. - `[in] message_len`: The length of the message in bytes, or -NAPI_AUTO_LENGTH if it is +`NAPI_AUTO_LENGTH` if it is null-terminated. The function call does not return, the process will be terminated. @@ -1260,7 +1260,7 @@ napi_status napi_create_function(napi_env env, - `[in] utf8name`: A string representing the name of the function encoded as UTF8. - `[in] length`: The length of the utf8name in bytes, or -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[in] cb`: A function pointer to the native function to be invoked when the created function is invoked from JavaScript. - `[in] data`: Optional arbitrary context data to be passed into the native @@ -1493,7 +1493,7 @@ napi_status napi_create_string_latin1(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] str`: Character buffer representing a ISO-8859-1-encoded string. - `[in] length`: The length of the string in bytes, or -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. Returns `napi_ok` if the API succeeded. @@ -1518,7 +1518,7 @@ napi_status napi_create_string_utf16(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] str`: Character buffer representing a UTF16-LE-encoded string. - `[in] length`: The length of the string in two-byte code units, or -NAPI_AUTO_LENGTH if it is null-terminated. +`NAPI_AUTO_LENGTH` if it is null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. Returns `napi_ok` if the API succeeded. @@ -1542,7 +1542,7 @@ napi_status napi_create_string_utf8(napi_env env, - `[in] env`: The environment that the API is invoked under. - `[in] str`: Character buffer representing a UTF8-encoded string. -- `[in] length`: The length of the string in bytes, or NAPI_AUTO_LENGTH +- `[in] length`: The length of the string in bytes, or `NAPI_AUTO_LENGTH` if it is null-terminated. - `[out] result`: A `napi_value` representing a JavaScript String. @@ -3068,7 +3068,7 @@ napi_status napi_define_class(napi_env env, - `[in] utf8name`: Name of the JavaScript constructor function; this is not required to be the same as the C++ class name, though it is recommended for clarity. - - `[in] length`: The length of the utf8name in bytes, or NAPI_AUTO_LENGTH + - `[in] length`: The length of the utf8name in bytes, or `NAPI_AUTO_LENGTH` if it is null-terminated. - `[in] constructor`: Callback function that handles constructing instances of the class. (This should be a static method on the class, not an actual From 6129ff4b9976450bdd84a43541573d66a6fcc5ec Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sun, 11 Feb 2018 17:07:39 -0500 Subject: [PATCH 125/218] test: remove unnecessary timer The timer in NAPI's test_callback_scope/test-resolve-async.js can be removed. If the test fails, it will timeout on its own. The extra timer increases the chances of the test being flaky. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/18719 Fixes: https://github.com/nodejs/node/issues/18702 Reviewed-By: Ruben Bridgewater Reviewed-By: Santiago Gimeno Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- .../test_callback_scope/test-resolve-async.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/addons-napi/test_callback_scope/test-resolve-async.js b/test/addons-napi/test_callback_scope/test-resolve-async.js index e9f4b9044c0154..77f25c9dde533f 100644 --- a/test/addons-napi/test_callback_scope/test-resolve-async.js +++ b/test/addons-napi/test_callback_scope/test-resolve-async.js @@ -1,13 +1,6 @@ 'use strict'; const common = require('../../common'); -const assert = require('assert'); const { testResolveAsync } = require(`./build/${common.buildType}/binding`); -let called = false; -testResolveAsync().then(common.mustCall(() => { - called = true; -})); - -setTimeout(common.mustCall(() => { assert(called); }), - common.platformTimeout(20)); +testResolveAsync().then(common.mustCall()); From cb3f90a1a937638c3095ac509e1126244275671f Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Tue, 27 Feb 2018 13:02:24 -0500 Subject: [PATCH 126/218] n-api: fix object test Passing a pointer to a static integer is sufficient for the test. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19039 Reviewed-By: Colin Ihrig Reviewed-By: Michael Dawson Reviewed-By: Hitesh Kanwathirtha --- test/addons-napi/test_object/test_object.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/addons-napi/test_object/test_object.c b/test/addons-napi/test_object/test_object.c index 49a90dd3f99f45..ccf1573114a6f1 100644 --- a/test/addons-napi/test_object/test_object.c +++ b/test/addons-napi/test_object/test_object.c @@ -1,7 +1,6 @@ #include #include "../common.h" #include -#include static int test_value = 3; @@ -199,9 +198,7 @@ napi_value Wrap(napi_env env, napi_callback_info info) { napi_value arg; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &arg, NULL, NULL)); - int32_t* data = malloc(sizeof(int32_t)); - *data = test_value; - NAPI_CALL(env, napi_wrap(env, arg, data, NULL, NULL, NULL)); + NAPI_CALL(env, napi_wrap(env, arg, &test_value, NULL, NULL, NULL)); return NULL; } From 1abb16883816b3e23da0ec9c8429a79f490e2c9c Mon Sep 17 00:00:00 2001 From: Eric Bickle Date: Thu, 1 Mar 2018 09:27:16 -0800 Subject: [PATCH 127/218] doc: fix n-api asynchronous threading docs Documentation for N-API Custom Asynchronous Operations incorrectly stated that async execution happens on the main event loop. Added details to napi_create_async_work about which threads are used to invoke the execute and complete callbacks. Changed 'async' to 'asynchronous' in the documentation for Custom Asynchronous Operations. Changed "executes in parallel" to "can execute in parallel" for the documentation of napi_create_async_work execute parameter. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19073 Fixes: https://github.com/nodejs/node/issues/19071 Reviewed-By: Michael Dawson Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- doc/api/n-api.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index aad4cadd948ad2..9055e44da7b562 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3283,9 +3283,11 @@ napi_status napi_create_async_work(napi_env env, - `[in] async_resource_name`: Identifier for the kind of resource that is being provided for diagnostic information exposed by the `async_hooks` API. - `[in] execute`: The native function which should be called to excute -the logic asynchronously. +the logic asynchronously. The given function is called from a worker pool +thread and can execute in parallel with the main event loop thread. - `[in] complete`: The native function which will be called when the -asynchronous logic is comple or is cancelled. +asynchronous logic is completed or is cancelled. The given function is called +from the main event loop thread. - `[in] data`: User-provided data context. This will be passed back into the execute and complete functions. - `[out] result`: `napi_async_work*` which is the handle to the newly created @@ -3361,9 +3363,9 @@ callback invocation, even if it has been successfully cancelled. ## Custom Asynchronous Operations The simple asynchronous work APIs above may not be appropriate for every -scenario, because with those the async execution still happens on the main -event loop. When using any other async mechanism, the following APIs are -necessary to ensure an async operation is properly tracked by the runtime. +scenario. When using any other asynchronous mechanism, the following APIs +are necessary to ensure an asynchronous operation is properly tracked by +the runtime. ### napi_async_init +```C +napi_status napi_fatal_exception(napi_env env, napi_value err); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] err`: The error you want to pass to `uncaughtException`. + +Trigger an `uncaughtException` in JavaScript. Useful if an async +callback throws an exception with no way to recover. + ### Fatal Errors In the event of an unrecoverable error in a native module, a fatal error can be diff --git a/src/node_api.cc b/src/node_api.cc index 9645b5ab30720d..efc08f8c4e570e 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -173,6 +173,7 @@ struct napi_env__ { (out) = v8::type::New((buffer), (byte_offset), (length)); \ } while (0) + namespace { namespace v8impl { @@ -295,6 +296,13 @@ v8::Local V8LocalValueFromJsValue(napi_value v) { return local; } +static inline void trigger_fatal_exception( + napi_env env, v8::Local local_err) { + v8::Local local_msg = + v8::Exception::CreateMessage(env->isolate, local_err); + node::FatalException(env->isolate, local_err, local_msg); +} + static inline napi_status V8NameFromPropertyDescriptor(napi_env env, const napi_property_descriptor* p, v8::Local* result) { @@ -971,6 +979,16 @@ napi_status napi_get_last_error_info(napi_env env, return napi_ok; } +napi_status napi_fatal_exception(napi_env env, napi_value err) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, err); + + v8::Local local_err = v8impl::V8LocalValueFromJsValue(err); + v8impl::trigger_fatal_exception(env, local_err); + + return napi_clear_last_error(env); +} + NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, @@ -3375,10 +3393,9 @@ class Work : public node::AsyncResource { // report it as a fatal exception. (There is no JavaScript on the // callstack that can possibly handle it.) if (!env->last_exception.IsEmpty()) { - v8::TryCatch try_catch(env->isolate); - env->isolate->ThrowException( - v8::Local::New(env->isolate, env->last_exception)); - node::FatalException(env->isolate, try_catch); + v8::Local local_err = v8::Local::New( + env->isolate, env->last_exception); + v8impl::trigger_fatal_exception(env, local_err); } } } diff --git a/src/node_api.h b/src/node_api.h index 5940c2b4a57ac2..627e56118011e6 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -112,6 +112,8 @@ NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env, const napi_extended_error_info** result); +NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err); + NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, size_t location_len, const char* message, diff --git a/src/node_internals.h b/src/node_internals.h index 8f3fb4fb9aa27d..7f73f4b8deaa9a 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -218,6 +218,11 @@ void GetSockOrPeerName(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(err); } +void FatalException(v8::Isolate* isolate, + v8::Local error, + v8::Local message); + + void SignalExit(int signo); #ifdef __POSIX__ void RegisterSignalHandler(int signal, diff --git a/test/addons-napi/test_async/test-uncaught.js b/test/addons-napi/test_async/test-uncaught.js new file mode 100644 index 00000000000000..fdcb3203f54410 --- /dev/null +++ b/test/addons-napi/test_async/test-uncaught.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const test_async = require(`./build/${common.buildType}/test_async`); + +process.on('uncaughtException', common.mustCall(function(err) { + try { + throw new Error('should not fail'); + } catch (err) { + assert.strictEqual(err.message, 'should not fail'); + } + assert.strictEqual(err.message, 'uncaught'); +})); + +// Successful async execution and completion callback. +test_async.Test(5, {}, common.mustCall(function() { + throw new Error('uncaught'); +})); diff --git a/test/addons-napi/test_fatal_exception/binding.gyp b/test/addons-napi/test_fatal_exception/binding.gyp new file mode 100644 index 00000000000000..f4dc0a71ea2817 --- /dev/null +++ b/test/addons-napi/test_fatal_exception/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_fatal_exception", + "sources": [ "test_fatal_exception.c" ] + } + ] +} diff --git a/test/addons-napi/test_fatal_exception/test.js b/test/addons-napi/test_fatal_exception/test.js new file mode 100644 index 00000000000000..f02b9bce1e8169 --- /dev/null +++ b/test/addons-napi/test_fatal_exception/test.js @@ -0,0 +1,11 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const test_fatal = require(`./build/${common.buildType}/test_fatal_exception`); + +process.on('uncaughtException', common.mustCall(function(err) { + assert.strictEqual(err.message, 'fatal error'); +})); + +const err = new Error('fatal error'); +test_fatal.Test(err); diff --git a/test/addons-napi/test_fatal_exception/test_fatal_exception.c b/test/addons-napi/test_fatal_exception/test_fatal_exception.c new file mode 100644 index 00000000000000..fd81c56d856db8 --- /dev/null +++ b/test/addons-napi/test_fatal_exception/test_fatal_exception.c @@ -0,0 +1,26 @@ +#include +#include "../common.h" + +napi_value Test(napi_env env, napi_callback_info info) { + napi_value err; + size_t argc = 1; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &err, NULL, NULL)); + + NAPI_CALL(env, napi_fatal_exception(env, err)); + + return NULL; +} + +napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("Test", Test), + }; + + NAPI_CALL(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); + + return exports; +} + +NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) From 24b8bb670829e3c337b5c5a7c6d49198ad99c7a2 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Mon, 19 Mar 2018 11:59:41 -0400 Subject: [PATCH 137/218] n-api: re-write test_make_callback This re-writes the test in C by dropping std::vector in favour of a C99 variable length array, and by dropping the anonymous namespace in favour of static function declarations. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19448 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Michael Dawson --- .../{binding.cc => binding.c} | 21 +++++++++---------- .../test_make_callback/binding.gyp | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) rename test/addons-napi/test_make_callback/{binding.cc => binding.c} (80%) diff --git a/test/addons-napi/test_make_callback/binding.cc b/test/addons-napi/test_make_callback/binding.c similarity index 80% rename from test/addons-napi/test_make_callback/binding.cc rename to test/addons-napi/test_make_callback/binding.c index 952dfcc1cb5bec..23750f56b838fc 100644 --- a/test/addons-napi/test_make_callback/binding.cc +++ b/test/addons-napi/test_make_callback/binding.c @@ -1,13 +1,13 @@ #include #include "../common.h" -#include -namespace { +#define MAX_ARGUMENTS 10 +static napi_value MakeCallback(napi_env env, napi_callback_info info) { - const int kMaxArgs = 10; - size_t argc = kMaxArgs; - napi_value args[kMaxArgs]; + size_t argc = MAX_ARGUMENTS; + size_t n; + napi_value args[MAX_ARGUMENTS]; // NOLINTNEXTLINE (readability/null_usage) NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); @@ -16,9 +16,9 @@ napi_value MakeCallback(napi_env env, napi_callback_info info) { napi_value recv = args[0]; napi_value func = args[1]; - std::vector argv; - for (size_t n = 2; n < argc; n += 1) { - argv.push_back(args[n]); + napi_value argv[MAX_ARGUMENTS - 2]; + for (n = 2; n < argc; n += 1) { + argv[n - 2] = args[n]; } napi_valuetype func_type; @@ -35,7 +35,7 @@ napi_value MakeCallback(napi_env env, napi_callback_info info) { napi_value result; if (func_type == napi_function) { NAPI_CALL(env, napi_make_callback( - env, context, recv, func, argv.size(), argv.data(), &result)); + env, context, recv, func, argc - 2, argv, &result)); } else { NAPI_ASSERT(env, false, "Unexpected argument type"); } @@ -45,6 +45,7 @@ napi_value MakeCallback(napi_env env, napi_callback_info info) { return result; } +static napi_value Init(napi_env env, napi_value exports) { napi_value fn; NAPI_CALL(env, napi_create_function( @@ -54,6 +55,4 @@ napi_value Init(napi_env env, napi_value exports) { return exports; } -} // namespace - NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) diff --git a/test/addons-napi/test_make_callback/binding.gyp b/test/addons-napi/test_make_callback/binding.gyp index 7ede63d94a0d77..23daf507916ff6 100644 --- a/test/addons-napi/test_make_callback/binding.gyp +++ b/test/addons-napi/test_make_callback/binding.gyp @@ -3,7 +3,7 @@ { 'target_name': 'binding', 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], - 'sources': [ 'binding.cc' ] + 'sources': [ 'binding.c' ] } ] } From f0ba2c6cebfc0620087ac5318640c73938c36b63 Mon Sep 17 00:00:00 2001 From: jiangq Date: Fri, 23 Mar 2018 22:28:39 +0800 Subject: [PATCH 138/218] doc: Add a missing comma Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19555 Reviewed-By: Colin Ihrig Reviewed-By: Trivikram Kamat --- doc/api/n-api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index ed748abe5febd6..4bcbbe1ae14912 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -610,7 +610,7 @@ that has a loop which iterates through the elements in a large array: ```C for (int i = 0; i < 1000000; i++) { napi_value result; - napi_status status = napi_get_element(e object, i, &result); + napi_status status = napi_get_element(e, object, i, &result); if (status != napi_ok) { break; } @@ -647,7 +647,7 @@ for (int i = 0; i < 1000000; i++) { break; } napi_value result; - status = napi_get_element(e object, i, &result); + status = napi_get_element(e, object, i, &result); if (status != napi_ok) { break; } From 367113f5d7e0e085afb1d7f8dbf8f5ecb6cf4858 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 20 Mar 2018 17:14:24 -0400 Subject: [PATCH 139/218] n-api: bump version of n-api supported Bump the version due to additions to the api. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19497 Reviewed-By: Colin Ihrig Reviewed-By: Trivikram Kamat Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater --- src/node_version.h | 2 +- test/addons-napi/test_general/test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node_version.h b/src/node_version.h index 611cc2a978663f..4d5113766807f1 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -108,6 +108,6 @@ #define NODE_MODULE_VERSION 57 // the NAPI_VERSION provided by this version of the runtime -#define NAPI_VERSION 2 +#define NAPI_VERSION 3 #endif // SRC_NODE_VERSION_H_ diff --git a/test/addons-napi/test_general/test.js b/test/addons-napi/test_general/test.js index bcaa13d894bedc..4faf508d5db145 100644 --- a/test/addons-napi/test_general/test.js +++ b/test/addons-napi/test_general/test.js @@ -33,8 +33,8 @@ assert.notStrictEqual(test_general.testGetPrototype(baseObject), test_general.testGetPrototype(extendedObject)); // test version management functions -// expected version is currently 1 -assert.strictEqual(test_general.testGetVersion(), 2); +// expected version is currently 3 +assert.strictEqual(test_general.testGetVersion(), 3); const [ major, minor, patch, release ] = test_general.testGetNodeVersion(); assert.strictEqual(process.version.split('-')[0], From 92f699e021c19a75a4f5d6b318b5a3fc131a5ac7 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Thu, 22 Mar 2018 17:01:37 -0400 Subject: [PATCH 140/218] n-api: ensure in-module exceptions are propagated Whenever we call into an addon, whether it is for a callback, for module init, or for async work-related reasons, we should make sure that * the last error is cleared, * the scopes before the call are the same as after, and * if an exception was thrown and captured inside the module, then it is re-thrown after the call. Therefore we should call into the module in a unified fashion. This change introduces the macro NAPI_CALL_INTO_MODULE() which should be used whenever invoking a callback provided by the module. Fixes: https://github.com/nodejs/node/issues/19437 Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19537 Reviewed-By: James M Snell Reviewed-By: Michael Dawson --- src/node_api.cc | 78 ++++++++++--------- test/addons-napi/test_exception/test.js | 30 ++++++- .../test_exception/test_exception.c | 42 ++++++++-- 3 files changed, 107 insertions(+), 43 deletions(-) diff --git a/src/node_api.cc b/src/node_api.cc index efc08f8c4e570e..f190b09e168052 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -173,6 +173,23 @@ struct napi_env__ { (out) = v8::type::New((buffer), (byte_offset), (length)); \ } while (0) +#define NAPI_CALL_INTO_MODULE(env, call, handle_exception) \ + do { \ + int open_handle_scopes = (env)->open_handle_scopes; \ + int open_callback_scopes = (env)->open_callback_scopes; \ + napi_clear_last_error((env)); \ + call; \ + CHECK_EQ((env)->open_handle_scopes, open_handle_scopes); \ + CHECK_EQ((env)->open_callback_scopes, open_callback_scopes); \ + if (!(env)->last_exception.IsEmpty()) { \ + handle_exception( \ + v8::Local::New((env)->isolate, (env)->last_exception)); \ + (env)->last_exception.Reset(); \ + } \ + } while (0) + +#define NAPI_CALL_INTO_MODULE_THROW(env, call) \ + NAPI_CALL_INTO_MODULE((env), call, (env)->isolate->ThrowException) namespace { namespace v8impl { @@ -352,10 +369,11 @@ class Finalizer { static void FinalizeBufferCallback(char* data, void* hint) { Finalizer* finalizer = static_cast(hint); if (finalizer->_finalize_callback != nullptr) { - finalizer->_finalize_callback( - finalizer->_env, - data, - finalizer->_finalize_hint); + NAPI_CALL_INTO_MODULE_THROW(finalizer->_env, + finalizer->_finalize_callback( + finalizer->_env, + data, + finalizer->_finalize_hint)); } Delete(finalizer); @@ -467,10 +485,11 @@ class Reference : private Finalizer { bool delete_self = reference->_delete_self; if (reference->_finalize_callback != nullptr) { - reference->_finalize_callback( - reference->_env, - reference->_finalize_data, - reference->_finalize_hint); + NAPI_CALL_INTO_MODULE_THROW(reference->_env, + reference->_finalize_callback( + reference->_env, + reference->_finalize_data, + reference->_finalize_hint)); } if (delete_self) { @@ -555,32 +574,17 @@ class CallbackWrapperBase : public CallbackWrapper { napi_callback cb = reinterpret_cast( v8::Local::Cast( _cbdata->GetInternalField(kInternalFieldIndex))->Value()); - v8::Isolate* isolate = _cbinfo.GetIsolate(); napi_env env = static_cast( v8::Local::Cast( _cbdata->GetInternalField(kEnvIndex))->Value()); - // Make sure any errors encountered last time we were in N-API are gone. - napi_clear_last_error(env); - - int open_handle_scopes = env->open_handle_scopes; - int open_callback_scopes = env->open_callback_scopes; - - napi_value result = cb(env, cbinfo_wrapper); + napi_value result; + NAPI_CALL_INTO_MODULE_THROW(env, result = cb(env, cbinfo_wrapper)); if (result != nullptr) { this->SetReturnValue(result); } - - CHECK_EQ(env->open_handle_scopes, open_handle_scopes); - CHECK_EQ(env->open_callback_scopes, open_callback_scopes); - - if (!env->last_exception.IsEmpty()) { - isolate->ThrowException( - v8::Local::New(isolate, env->last_exception)); - env->last_exception.Reset(); - } } const Info& _cbinfo; @@ -888,8 +892,10 @@ void napi_module_register_cb(v8::Local exports, // one is found. napi_env env = v8impl::GetEnv(context); - napi_value _exports = - mod->nm_register_func(env, v8impl::JsValueFromV8LocalValue(exports)); + napi_value _exports; + NAPI_CALL_INTO_MODULE_THROW(env, + _exports = mod->nm_register_func(env, + v8impl::JsValueFromV8LocalValue(exports))); // If register function returned a non-null exports object different from // the exports object we passed it, set that as the "exports" property of @@ -3384,19 +3390,17 @@ class Work : public node::AsyncResource { v8::HandleScope scope(env->isolate); CallbackScope callback_scope(work); - work->_complete(env, ConvertUVErrorCode(status), work->_data); + NAPI_CALL_INTO_MODULE(env, + work->_complete(env, ConvertUVErrorCode(status), work->_data), + [env] (v8::Local local_err) { + // If there was an unhandled exception in the complete callback, + // report it as a fatal exception. (There is no JavaScript on the + // callstack that can possibly handle it.) + v8impl::trigger_fatal_exception(env, local_err); + }); // Note: Don't access `work` after this point because it was // likely deleted by the complete callback. - - // If there was an unhandled exception in the complete callback, - // report it as a fatal exception. (There is no JavaScript on the - // callstack that can possibly handle it.) - if (!env->last_exception.IsEmpty()) { - v8::Local local_err = v8::Local::New( - env->isolate, env->last_exception); - v8impl::trigger_fatal_exception(env, local_err); - } } } diff --git a/test/addons-napi/test_exception/test.js b/test/addons-napi/test_exception/test.js index 787b7d78b1c72b..b9311add6c92d7 100644 --- a/test/addons-napi/test_exception/test.js +++ b/test/addons-napi/test_exception/test.js @@ -1,10 +1,26 @@ 'use strict'; +// Flags: --expose-gc const common = require('../../common'); -const test_exception = require(`./build/${common.buildType}/test_exception`); const assert = require('assert'); const theError = new Error('Some error'); +// The test module throws an error during Init, but in order for its exports to +// not be lost, it attaches them to the error's "bindings" property. This way, +// we can make sure that exceptions thrown during the module initialization +// phase are propagated through require() into JavaScript. +// https://github.com/nodejs/node/issues/19437 +const test_exception = (function() { + let resultingException; + try { + require(`./build/${common.buildType}/test_exception`); + } catch (anException) { + resultingException = anException; + } + assert.strictEqual(resultingException.message, 'Error during Init'); + return resultingException.binding; +})(); + { const throwTheError = () => { throw theError; }; @@ -50,3 +66,15 @@ const theError = new Error('Some error'); 'Exception state did not remain clear as expected,' + ` .wasPending() returned ${exception_pending}`); } + +// Make sure that exceptions that occur during finalization are propagated. +function testFinalize(binding) { + let x = test_exception[binding](); + x = null; + assert.throws(() => { global.gc(); }, /Error during Finalize/); + + // To assuage the linter's concerns. + (function() {})(x); +} +testFinalize('createExternal'); +testFinalize('createExternalBuffer'); diff --git a/test/addons-napi/test_exception/test_exception.c b/test/addons-napi/test_exception/test_exception.c index 8b664210e902a5..61116d0603bfae 100644 --- a/test/addons-napi/test_exception/test_exception.c +++ b/test/addons-napi/test_exception/test_exception.c @@ -3,7 +3,7 @@ static bool exceptionWasPending = false; -napi_value returnException(napi_env env, napi_callback_info info) { +static napi_value returnException(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); @@ -22,7 +22,7 @@ napi_value returnException(napi_env env, napi_callback_info info) { return NULL; } -napi_value allowException(napi_env env, napi_callback_info info) { +static napi_value allowException(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); @@ -38,23 +38,55 @@ napi_value allowException(napi_env env, napi_callback_info info) { return NULL; } -napi_value wasPending(napi_env env, napi_callback_info info) { +static napi_value wasPending(napi_env env, napi_callback_info info) { napi_value result; NAPI_CALL(env, napi_get_boolean(env, exceptionWasPending, &result)); return result; } -napi_value Init(napi_env env, napi_value exports) { +static void finalizer(napi_env env, void *data, void *hint) { + NAPI_CALL_RETURN_VOID(env, + napi_throw_error(env, NULL, "Error during Finalize")); +} + +static napi_value createExternal(napi_env env, napi_callback_info info) { + napi_value external; + + NAPI_CALL(env, + napi_create_external(env, NULL, finalizer, NULL, &external)); + + return external; +} + +static char buffer_data[12]; + +static napi_value createExternalBuffer(napi_env env, napi_callback_info info) { + napi_value buffer; + NAPI_CALL(env, napi_create_external_buffer(env, sizeof(buffer_data), + buffer_data, finalizer, NULL, &buffer)); + return buffer; +} + +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("returnException", returnException), DECLARE_NAPI_PROPERTY("allowException", allowException), DECLARE_NAPI_PROPERTY("wasPending", wasPending), + DECLARE_NAPI_PROPERTY("createExternal", createExternal), + DECLARE_NAPI_PROPERTY("createExternalBuffer", createExternalBuffer), }; - NAPI_CALL(env, napi_define_properties( env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); + napi_value error, code, message; + NAPI_CALL(env, napi_create_string_utf8(env, "Error during Init", + NAPI_AUTO_LENGTH, &message)); + NAPI_CALL(env, napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &code)); + NAPI_CALL(env, napi_create_error(env, code, message, &error)); + NAPI_CALL(env, napi_set_named_property(env, error, "binding", exports)); + NAPI_CALL(env, napi_throw(env, error)); + return exports; } From 8b3ef4660a6d9824ae965027a99458bd8f7cd333 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Sat, 31 Mar 2018 21:37:17 -0400 Subject: [PATCH 141/218] n-api: back up env before finalize Heed the comment to not use fields of a Reference after calling its finalize callback, because such a call may destroy the Reference. Fixes: https://github.com/nodejs/node/issues/19673 Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19718 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- src/node_api.cc | 3 ++- test/addons-napi/8_passing_wrapped/binding.cc | 15 ++++++++++++--- test/addons-napi/8_passing_wrapped/myobject.cc | 12 +++++++++++- test/addons-napi/8_passing_wrapped/test.js | 9 ++++++++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/node_api.cc b/src/node_api.cc index f190b09e168052..c4fadacb5bd224 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -483,9 +483,10 @@ class Reference : private Finalizer { // Check before calling the finalize callback, because the callback might // delete it. bool delete_self = reference->_delete_self; + napi_env env = reference->_env; if (reference->_finalize_callback != nullptr) { - NAPI_CALL_INTO_MODULE_THROW(reference->_env, + NAPI_CALL_INTO_MODULE_THROW(env, reference->_finalize_callback( reference->_env, reference->_finalize_data, diff --git a/test/addons-napi/8_passing_wrapped/binding.cc b/test/addons-napi/8_passing_wrapped/binding.cc index c284c85f9b4936..48e94f10ec4838 100644 --- a/test/addons-napi/8_passing_wrapped/binding.cc +++ b/test/addons-napi/8_passing_wrapped/binding.cc @@ -1,7 +1,9 @@ #include "myobject.h" #include "../common.h" -napi_value CreateObject(napi_env env, napi_callback_info info) { +extern size_t finalize_count; + +static napi_value CreateObject(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); @@ -12,7 +14,7 @@ napi_value CreateObject(napi_env env, napi_callback_info info) { return instance; } -napi_value Add(napi_env env, napi_callback_info info) { +static napi_value Add(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); @@ -29,12 +31,19 @@ napi_value Add(napi_env env, napi_callback_info info) { return sum; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value FinalizeCount(napi_env env, napi_callback_info info) { + napi_value return_value; + NAPI_CALL(env, napi_create_uint32(env, finalize_count, &return_value)); + return return_value; +} + +static napi_value Init(napi_env env, napi_value exports) { MyObject::Init(env); napi_property_descriptor desc[] = { DECLARE_NAPI_PROPERTY("createObject", CreateObject), DECLARE_NAPI_PROPERTY("add", Add), + DECLARE_NAPI_PROPERTY("finalizeCount", FinalizeCount), }; NAPI_CALL(env, diff --git a/test/addons-napi/8_passing_wrapped/myobject.cc b/test/addons-napi/8_passing_wrapped/myobject.cc index 19cc7dd2a29493..0c9ca90f52f8f3 100644 --- a/test/addons-napi/8_passing_wrapped/myobject.cc +++ b/test/addons-napi/8_passing_wrapped/myobject.cc @@ -1,9 +1,14 @@ #include "myobject.h" #include "../common.h" +size_t finalize_count = 0; + MyObject::MyObject() : env_(nullptr), wrapper_(nullptr) {} -MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); } +MyObject::~MyObject() { + finalize_count++; + napi_delete_reference(env_, wrapper_); +} void MyObject::Destructor( napi_env env, void* nativeObject, void* /*finalize_hint*/) { @@ -45,6 +50,11 @@ napi_value MyObject::New(napi_env env, napi_callback_info info) { } obj->env_ = env; + + // It is important that the below call to napi_wrap() be such that we request + // a reference to the wrapped object via the out-parameter, because this + // ensures that we test the code path that deals with a reference that is + // destroyed from its own finalizer. NAPI_CALL(env, napi_wrap(env, _this, obj, diff --git a/test/addons-napi/8_passing_wrapped/test.js b/test/addons-napi/8_passing_wrapped/test.js index 3d24fa5d9fdaa7..7793133f7750ba 100644 --- a/test/addons-napi/8_passing_wrapped/test.js +++ b/test/addons-napi/8_passing_wrapped/test.js @@ -1,9 +1,16 @@ 'use strict'; +// Flags: --expose-gc + const common = require('../../common'); const assert = require('assert'); const addon = require(`./build/${common.buildType}/binding`); -const obj1 = addon.createObject(10); +let obj1 = addon.createObject(10); const obj2 = addon.createObject(20); const result = addon.add(obj1, obj2); assert.strictEqual(result, 30); + +// Make sure the native destructor gets called. +obj1 = null; +global.gc(); +assert.strictEqual(addon.finalizeCount(), 1); From 07a677061435c19b0ef66cd76fe262831c4c1eb8 Mon Sep 17 00:00:00 2001 From: Kyle Farnung Date: Thu, 15 Mar 2018 17:22:30 -0700 Subject: [PATCH 142/218] n-api: add more `int64_t` tests * Updated tests for `Number` and `int32_t` * Added new tests for `int64_t` * Updated N-API `int64_t` behavior to return zero for all non-finite numbers * Clarified the documentation for these calls. Backport-PR-URL: https://github.com/nodejs/node/pull/19265 PR-URL: https://github.com/nodejs/node/pull/19402 Refs: https://github.com/nodejs/node-chakracore/pull/500 Reviewed-By: Michael Dawson Reviewed-By: James M Snell --- doc/api/n-api.md | 27 ++++-- src/node_api.cc | 11 ++- test/addons-napi/test_number/test.js | 135 ++++++++++++++++++++------- 3 files changed, 125 insertions(+), 48 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 4bcbbe1ae14912..7ac2ab39448273 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -1808,13 +1808,17 @@ napi_status napi_get_value_int32(napi_env env, - `[out] result`: C int32 primitive equivalent of the given JavaScript Number. Returns `napi_ok` if the API succeeded. If a non-number `napi_value` -is passed in `napi_number_expected . +is passed in `napi_number_expected`. This API returns the C int32 primitive equivalent -of the given JavaScript Number. If the number exceeds the range of the -32 bit integer, then the result is truncated to the equivalent of the -bottom 32 bits. This can result in a large positive number becoming -a negative number if the value is > 2^31 -1. +of the given JavaScript Number. + +If the number exceeds the range of the 32 bit integer, then the result is +truncated to the equivalent of the bottom 32 bits. This can result in a large +positive number becoming a negative number if the value is > 2^31 -1. + +Non-finite number values (NaN, positive infinity, or negative infinity) set the +result to zero. #### napi_get_value_int64 -The `'close'` event is emitted once the `Http2Session` has been terminated. +The `'close'` event is emitted once the `Http2Session` has been destroyed. #### Event: 'connect' - -The `'socketError'` event is emitted when an `'error'` is emitted on the -`Socket` instance bound to the `Http2Session`. If this event is not handled, -the `'error'` event will be re-emitted on the `Socket`. - -For `ServerHttp2Session` instances, a `'socketError'` event listener is always -registered that will, by default, forward the event on to the owning -`Http2Server` instance if no additional handlers are registered. - #### Event: 'timeout' + +* `callback` {Function} + +Gracefully closes the `Http2Session`, allowing any existing streams to +complete on their own and preventing new `Http2Stream` instances from being +created. Once closed, `http2session.destroy()` *might* be called if there +are no open `Http2Stream` instances. + +If specified, the `callback` function is registered as a handler for the +`'close'` event. + +#### http2session.closed + + +* Value: {boolean} + +Will be `true` if this `Http2Session` instance has been closed, otherwise +`false`. + +#### http2session.destroy([error,][code]) +* `error` {Error} An `Error` object if the `Http2Session` is being destroyed + due to an error. +* `code` {number} The HTTP/2 error code to send in the final `GOAWAY` frame. + If unspecified, and `error` is not undefined, the default is `INTERNAL_ERROR`, + otherwise defaults to `NO_ERROR`. * Returns: {undefined} Immediately terminates the `Http2Session` and the associated `net.Socket` or `tls.TLSSocket`. +Once destroyed, the `Http2Session` will emit the `'close'` event. If `error` +is not undefined, an `'error'` event will be emitted immediately after the +`'close'` event. + +If there are any remaining open `Http2Streams` associatd with the +`Http2Session`, those will also be destroyed. + #### http2session.destroyed + +* `code` {number} An HTTP/2 error code +* `lastStreamID` {number} The numeric ID of the last processed `Http2Stream` +* `opaqueData` {Buffer|TypedArray|DataView} A `TypedArray` or `DataView` + instance containing additional data to be carried within the GOAWAY frame. + +Transmits a `GOAWAY` frame to the connected peer *without* shutting down the +`Http2Session`. + #### http2session.localSettings * `options` {Object} - * `graceful` {boolean} `true` to attempt a polite shutdown of the - `Http2Session`. * `errorCode` {number} The HTTP/2 [error code][] to return. Note that this is *not* the same thing as an HTTP Response Status Code. **Default:** `0x00` (No Error). * `lastStreamID` {number} The Stream ID of the last successfully processed - `Http2Stream` on this `Http2Session`. + `Http2Stream` on this `Http2Session`. If unspecified, will default to the + ID of the most recently received stream. * `opaqueData` {Buffer|Uint8Array} A `Buffer` or `Uint8Array` instance containing arbitrary additional data to send to the peer upon disconnection. This is used, typically, to provide additional data for debugging failures, @@ -487,19 +523,16 @@ Attempts to shutdown this `Http2Session` using HTTP/2 defined procedures. If specified, the given `callback` function will be invoked once the shutdown process has completed. -Note that calling `http2session.shutdown()` does *not* destroy the session or -tear down the `Socket` connection. It merely prompts both sessions to begin -preparing to cease activity. - -During a "graceful" shutdown, the session will first send a `GOAWAY` frame to -the connected peer identifying the last processed stream as 232-1. +If the `Http2Session` instance is a server-side session and the `errorCode` +option is `0x00` (No Error), a "graceful" shutdown will be initiated. During a +"graceful" shutdown, the session will first send a `GOAWAY` frame to +the connected peer identifying the last processed stream as 231-1. Then, on the next tick of the event loop, a second `GOAWAY` frame identifying the most recently processed stream identifier is sent. This process allows the remote peer to begin preparing for the connection to be terminated. ```js -session.shutdown({ - graceful: true, +session.close({ opaqueData: Buffer.from('add some debugging data here') }, () => session.destroy()); ``` @@ -639,7 +672,7 @@ is not yet ready for use. All [`Http2Stream`][] instances are destroyed either when: * An `RST_STREAM` frame for the stream is received by the connected peer. -* The `http2stream.rstStream()` methods is called. +* The `http2stream.close()` method is called. * The `http2stream.destroy()` or `http2session.destroy()` methods are called. When an `Http2Stream` instance is destroyed, an attempt will be made to send an @@ -732,6 +765,29 @@ added: v8.4.0 Set to `true` if the `Http2Stream` instance was aborted abnormally. When set, the `'aborted'` event will have been emitted. +#### http2stream.close(code[, callback]) + + +* code {number} Unsigned 32-bit integer identifying the error code. **Default:** + `http2.constant.NGHTTP2_NO_ERROR` (`0x00`) +* `callback` {Function} An optional function registered to listen for the + `'close'` event. +* Returns: {undefined} + +Closes the `Http2Stream` instance by sending an `RST_STREAM` frame to the +connected HTTP/2 peer. + +#### http2stream.closed + + +* Value: {boolean} + +Set to `true` if the `Http2Stream` instance has been closed. + #### http2stream.destroyed + +* Value: {boolean} + +Set to `true` if the `Http2Stream` instance has not yet been assigned a +numeric stream identifier. + #### http2stream.priority(options) - -* code {number} Unsigned 32-bit integer identifying the error code. **Default:** - `http2.constant.NGHTTP2_NO_ERROR` (`0x00`) -* Returns: {undefined} - -Sends an `RST_STREAM` frame to the connected HTTP/2 peer, causing this -`Http2Stream` to be closed on both sides using [error code][] `code`. - -#### http2stream.rstWithNoError() - - -* Returns: {undefined} - -Shortcut for `http2stream.rstStream()` using error code `0x00` (No Error). - -#### http2stream.rstWithProtocolError() - - -* Returns: {undefined} - -Shortcut for `http2stream.rstStream()` using error code `0x01` (Protocol Error). - -#### http2stream.rstWithCancel() - - -* Returns: {undefined} - -Shortcut for `http2stream.rstStream()` using error code `0x08` (Cancel). - -#### http2stream.rstWithRefuse() - - -* Returns: {undefined} - -Shortcut for `http2stream.rstStream()` using error code `0x07` (Refused Stream). - -#### http2stream.rstWithInternalError() - - -* Returns: {undefined} - -Shortcut for `http2stream.rstStream()` using error code `0x02` (Internal Error). - #### http2stream.session -#### Event: 'socketError' +* `request` {http2.Http2ServerRequest} +* `response` {http2.Http2ServerResponse} + +If a [`'request'`][] listener is registered or [`http2.createServer()`][] is +supplied a callback function, the `'checkContinue'` event is emitted each time +a request with an HTTP `Expect: 100-continue` is received. If this event is +not listened for, the server will automatically respond with a status +`100 Continue` as appropriate. + +Handling this event involves calling [`response.writeContinue()`][] if the client +should continue to send the request body, or generating an appropriate HTTP +response (e.g. 400 Bad Request) if the client should not continue to send the +request body. + +Note that when this event is emitted and handled, the [`'request'`][] event will +not be emitted. + +#### Event: 'request' -The `'socketError'` event is emitted when a `'socketError'` event is emitted by -an `Http2Session` associated with the server. +* `request` {http2.Http2ServerRequest} +* `response` {http2.Http2ServerResponse} + +Emitted each time there is a request. Note that there may be multiple requests +per session. See the [Compatibility API][]. + +#### Event: 'session' + + +The `'session'` event is emitted when a new `Http2Session` is created by the +`Http2Server`. #### Event: 'sessionError' The `'sessionError'` event is emitted when an `'error'` event is emitted by -an `Http2Session` object. If no listener is registered for this event, an -`'error'` event is emitted. +an `Http2Session` object associated with the `Http2Server`. #### Event: 'streamError' -* `socket` {ServerHttp2Stream} - If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here. The stream will already be destroyed when this event is triggered. @@ -1344,17 +1383,6 @@ server.on('stream', (stream, headers, flags) => { }); ``` -#### Event: 'request' - - -* `request` {http2.Http2ServerRequest} -* `response` {http2.Http2ServerResponse} - -Emitted each time there is a request. Note that there may be multiple requests -per session. See the [Compatibility API][]. - #### Event: 'timeout' - -* `request` {http2.Http2ServerRequest} -* `response` {http2.Http2ServerResponse} - -If a [`'request'`][] listener is registered or [`http2.createServer()`][] is -supplied a callback function, the `'checkContinue'` event is emitted each time -a request with an HTTP `Expect: 100-continue` is received. If this event is -not listened for, the server will automatically respond with a status -`100 Continue` as appropriate. - -Handling this event involves calling [`response.writeContinue()`][] if the client -should continue to send the request body, or generating an appropriate HTTP -response (e.g. 400 Bad Request) if the client should not continue to send the -request body. - -Note that when this event is emitted and handled, the [`'request'`][] event will -not be emitted. - ### Class: Http2SecureServer The `'sessionError'` event is emitted when an `'error'` event is emitted by -an `Http2Session` object. If no listener is registered for this event, an -`'error'` event is emitted on the `Http2Session` instance instead. - -#### Event: 'socketError' - - -The `'socketError'` event is emitted when a `'socketError'` event is emitted by -an `Http2Session` associated with the server. +an `Http2Session` object associated with the `Http2SecureServer`. #### Event: 'unknownProtocol' -* Value: {[Settings Object][]} +Calls [`ref()`][`net.Socket.prototype.ref`] on this `Http2Session` +instance's underlying [`net.Socket`]. -A prototype-less object describing the current remote settings of this -`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer. - -#### http2session.request(headers[, options]) +#### http2session.remoteSettings -* `headers` {[Headers Object][]} -* `options` {Object} - * `endStream` {boolean} `true` if the `Http2Stream` *writable* side should - be closed initially, such as when sending a `GET` request that should not - expect a payload body. - * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream, - the created stream is made the sole direct dependency of the parent, with - all other existing dependents made a dependent of the newly created stream. - **Default:** `false` - * `parent` {number} Specifies the numeric identifier of a stream the newly - created stream is dependent on. - * `weight` {number} Specifies the relative dependency of a stream in relation - to other streams with the same `parent`. The value is a number between `1` - and `256` (inclusive). - * `getTrailers` {Function} Callback function invoked to collect trailer - headers. - -* Returns: {ClientHttp2Stream} - -For HTTP/2 Client `Http2Session` instances only, the `http2session.request()` -creates and returns an `Http2Stream` instance that can be used to send an -HTTP/2 request to the connected server. - -This method is only available if `http2session.type` is equal to -`http2.constants.NGHTTP2_SESSION_CLIENT`. - -```js -const http2 = require('http2'); -const clientSession = http2.connect('https://localhost:1234'); -const { - HTTP2_HEADER_PATH, - HTTP2_HEADER_STATUS -} = http2.constants; - -const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' }); -req.on('response', (headers) => { - console.log(headers[HTTP2_HEADER_STATUS]); - req.on('data', (chunk) => { /** .. **/ }); - req.on('end', () => { /** .. **/ }); -}); -``` - -When set, the `options.getTrailers()` function is called immediately after -queuing the last chunk of payload data to be sent. The callback is passed a -single object (with a `null` prototype) that the listener may used to specify -the trailing header fields to send to the peer. - -*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2 -"pseudo-header" fields (e.g. `':method'`, `':path'`, etc). An `'error'` event -will be emitted if the `getTrailers` callback attempts to set such header -fields. - -The the `:method` and `:path` pseudoheaders are not specified within `headers`, -they respectively default to: +* Value: {[Settings Object][]} -* `:method` = `'GET'` -* `:path` = `/` +A prototype-less object describing the current remote settings of this +`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer. #### http2session.setTimeout(msecs, callback) + +Calls [`unref()`][`net.Socket.prototype.unref`] on this `Http2Session` +instance's underlying [`net.Socket`]. + +### Class: ClientHttp2Session + + +#### clienthttp2session.request(headers[, options]) + + +* `headers` {[Headers Object][]} +* `options` {Object} + * `endStream` {boolean} `true` if the `Http2Stream` *writable* side should + be closed initially, such as when sending a `GET` request that should not + expect a payload body. + * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream, + the created stream is made the sole direct dependency of the parent, with + all other existing dependents made a dependent of the newly created stream. + **Default:** `false` + * `parent` {number} Specifies the numeric identifier of a stream the newly + created stream is dependent on. + * `weight` {number} Specifies the relative dependency of a stream in relation + to other streams with the same `parent`. The value is a number between `1` + and `256` (inclusive). + * `getTrailers` {Function} Callback function invoked to collect trailer + headers. + +* Returns: {ClientHttp2Stream} + +For HTTP/2 Client `Http2Session` instances only, the `http2session.request()` +creates and returns an `Http2Stream` instance that can be used to send an +HTTP/2 request to the connected server. + +This method is only available if `http2session.type` is equal to +`http2.constants.NGHTTP2_SESSION_CLIENT`. + +```js +const http2 = require('http2'); +const clientSession = http2.connect('https://localhost:1234'); +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS +} = http2.constants; + +const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' }); +req.on('response', (headers) => { + console.log(headers[HTTP2_HEADER_STATUS]); + req.on('data', (chunk) => { /** .. **/ }); + req.on('end', () => { /** .. **/ }); +}); +``` + +When set, the `options.getTrailers()` function is called immediately after +queuing the last chunk of payload data to be sent. The callback is passed a +single object (with a `null` prototype) that the listener may used to specify +the trailing header fields to send to the peer. + +*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2 +"pseudo-header" fields (e.g. `':method'`, `':path'`, etc). An `'error'` event +will be emitted if the `getTrailers` callback attempts to set such header +fields. + +The `:method` and `:path` pseudoheaders are not specified within `headers`, +they respectively default to: + +* `:method` = `'GET'` +* `:path` = `/` + ### Class: Http2Stream + +#### serverhttp2session.altsvc(alt, originOrStream) + + +* `alt` {string} A description of the alternative service configuration as + defined by [RFC 7838][]. +* `originOrStream` {number|string|URL|Object} Either a URL string specifying + the origin (or an Object with an `origin` property) or the numeric identifier + of an active `Http2Stream` as given by the `http2stream.id` property. + +Submits an `ALTSVC` frame (as defined by [RFC 7838][]) to the connected client. + +```js +const http2 = require('http2'); + +const server = http2.createServer(); +server.on('session', (session) => { + // Set altsvc for origin https://example.org:80 + session.altsvc('h2=":8000"', 'https://example.org:80'); +}); + +server.on('stream', (stream) => { + // Set altsvc for a specific stream + stream.session.altsvc('h2=":8000"', stream.id); +}); +``` + +Sending an `ALTSVC` frame with a specific stream ID indicates that the alternate +service is associated with the origin of the given `Http2Stream`. + +The `alt` and origin string *must* contain only ASCII bytes and are +strictly interpreted as a sequence of ASCII bytes. The special value `'clear'` +may be passed to clear any previously set alternative service for a given +domain. + +When a string is passed for the `originOrStream` argument, it will be parsed as +a URL and the origin will be derived. For insetance, the origin for the +HTTP URL `'https://example.org/foo/bar'` is the ASCII string +`'https://example.org'`. An error will be thrown if either the given string +cannot be parsed as a URL or if a valid origin cannot be derived. + +A `URL` object, or any object with an `origin` property, may be passed as +`originOrStream`, in which case the value of the `origin` property will be +used. The value of the `origin` property *must* be a properly serialized +ASCII origin. + +#### Specifying alternative services + +The format of the `alt` parameter is strictly defined by [RFC 7838][] as an +ASCII string containing a comma-delimited list of "alternative" protocols +associated with a specific host and port. + +For example, the value `'h2="example.org:81"'` indicates that the HTTP/2 +protocol is available on the host `'example.org'` on TCP/IP port 81. The +host and port *must* be contained within the quote (`"`) characters. + +Multiple alternatives may be specified, for instance: `'h2="example.org:81", +h2=":82"'` + +The protocol identifier (`'h2'` in the examples) may be any valid +[ALPN Protocol ID][]. + +The syntax of these values is not validated by the Node.js implementation and +are passed through as provided by the user or received from the peer. + ### Class: ClientHttp2Session +#### Event: 'altsvc' + + +The `'altsvc'` event is emitted whenever an `ALTSVC` frame is received by +the client. The event is emitted with the `ALTSVC` value, origin, and stream +ID, if any. If no `origin` is provided in the `ALTSVC` frame, `origin` will +be an empty string. + +```js +const http2 = require('http2'); +const client = http2.connect('https://example.org'); + +client.on('altsvc', (alt, origin, stream) => { + console.log(alt); + console.log(origin); + console.log(stream); +}); +``` + #### clienthttp2session.request(headers[, options]) + +* Value: {string|undefined} + +Value will be `undefined` if the `Http2Session` is not yet connected to a +socket, `h2c` if the `Http2Session` is not connected to a `TLSSocket`, or +will return the value of the connected `TLSSocket`'s own `alpnProtocol` +property. + #### http2session.close([callback]) + +* Value: {boolean|undefined} + +Value is `undefined` if the `Http2Session` session socket has not yet been +connected, `true` if the `Http2Session` is connected with a `TLSSocket`, +and `false` if the `Http2Session` is connected to any other kind of socket +or stream. + #### http2session.goaway([code, [lastStreamID, [opaqueData]]]) + +* Value: {string[]|undefined} + +If the `Http2Session` is connected to a `TLSSocket`, the `originSet` property +will return an Array of origins for which the `Http2Session` may be +considered authoritative. + #### http2session.pendingSettingsAck + + + +```js +const http2 = require('../common/http2'); +``` + +### Class: Frame + +The `http2.Frame` is a base class that creates a `Buffer` containing a +serialized HTTP/2 frame header. + + + + + +```js +// length is a 24-bit unsigned integer +// type is an 8-bit unsigned integer identifying the frame type +// flags is an 8-bit unsigned integer containing the flag bits +// id is the 32-bit stream identifier, if any. +const frame = new http2.Frame(length, type, flags, id); + +// Write the frame data to a socket +socket.write(frame.data); +``` + +The serialized `Buffer` may be retrieved using the `frame.data` property. + +### Class: DataFrame extends Frame + +The `http2.DataFrame` is a subclass of `http2.Frame` that serializes a `DATA` +frame. + + + + + +```js +// id is the 32-bit stream identifier +// payload is a Buffer containing the DATA payload +// padlen is an 8-bit integer giving the number of padding bytes to include +// final is a boolean indicating whether the End-of-stream flag should be set, +// defaults to false. +const data = new http2.DataFrame(id, payload, padlen, final); + +socket.write(frame.data); +``` + +### Class: HeadersFrame + +The `http2.HeadersFrame` is a subclass of `http2.Frame` that serializes a +`HEADERS` frame. + + + + + +```js +// id is the 32-bit stream identifier +// payload is a Buffer containing the HEADERS payload (see either +// http2.kFakeRequestHeaders or http2.kFakeResponseHeaders). +// padlen is an 8-bit integer giving the number of padding bytes to include +// final is a boolean indicating whether the End-of-stream flag should be set, +// defaults to false. +const data = new http2.HeadersFrame(id, http2.kFakeRequestHeaders, + padlen, final); + +socket.write(frame.data); +``` + +### Class: SettingsFrame + +The `http2.SettingsFrame` is a subclass of `http2.Frame` that serializes an +empty `SETTINGS` frame. + + + + + +```js +// ack is a boolean indicating whether or not to set the ACK flag. +const frame = new http2.SettingsFrame(ack); + +socket.write(frame.data); +``` + +### http2.kFakeRequestHeaders + +Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 +request headers to be used as the payload of a `http2.HeadersFrame`. + + + + + +```js +const frame = new http2.HeadersFrame(1, http2.kFakeRequestHeaders, 0, true); + +socket.write(frame.data); +``` + +### http2.kFakeResponseHeaders + +Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 +response headers to be used as the payload a `http2.HeadersFrame`. + + + + + +```js +const frame = new http2.HeadersFrame(1, http2.kFakeResponseHeaders, 0, true); + +socket.write(frame.data); +``` + +### http2.kClientMagic + +Set to a `Buffer` containing the preamble bytes an HTTP/2 client must send +upon initial establishment of a connection. + + + + + +```js +socket.write(http2.kClientMagic); +``` + + [<Array>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array [<ArrayBufferView[]>]: https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView [<Boolean>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type From 3766e04f3158c5628fdaa8c54d5e5ea6705766c2 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 2 Jan 2018 13:00:12 -0500 Subject: [PATCH 170/218] src: silence http2 -Wunused-result warnings Backport-PR-URL: https://github.com/nodejs/node/pull/18050 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17954 Reviewed-By: Anatoli Papirovski Reviewed-By: James M Snell Reviewed-By: Anna Henningsen --- src/node_http2.cc | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/node_http2.cc b/src/node_http2.cc index 4a144b9e56cc8d..0c458ca33a3778 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -560,13 +560,13 @@ inline void Http2Stream::EmitStatistics() { FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstByte"), Number::New(env->isolate(), (entry->first_byte() - entry->startTimeNano()) / 1e6), - attr); + attr).FromJust(); obj->DefineOwnProperty( context, FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstHeader"), Number::New(env->isolate(), (entry->first_header() - entry->startTimeNano()) / 1e6), - attr); + attr).FromJust(); entry->Notify(obj); } delete entry; @@ -592,25 +592,29 @@ inline void Http2Session::EmitStatistics() { String::NewFromUtf8(env->isolate(), entry->typeName(), v8::NewStringType::kInternalized) - .ToLocalChecked(), attr); + .ToLocalChecked(), attr).FromJust(); if (entry->ping_rtt() != 0) { obj->DefineOwnProperty( context, FIXED_ONE_BYTE_STRING(env->isolate(), "pingRTT"), - Number::New(env->isolate(), entry->ping_rtt() / 1e6), attr); + Number::New(env->isolate(), entry->ping_rtt() / 1e6), + attr).FromJust(); } obj->DefineOwnProperty( context, FIXED_ONE_BYTE_STRING(env->isolate(), "framesReceived"), - Integer::NewFromUnsigned(env->isolate(), entry->frame_count()), attr); + Integer::NewFromUnsigned(env->isolate(), entry->frame_count()), + attr).FromJust(); obj->DefineOwnProperty( context, FIXED_ONE_BYTE_STRING(env->isolate(), "streamCount"), - Integer::New(env->isolate(), entry->stream_count()), attr); + Integer::New(env->isolate(), entry->stream_count()), + attr).FromJust(); obj->DefineOwnProperty( context, FIXED_ONE_BYTE_STRING(env->isolate(), "streamAverageDuration"), - Number::New(env->isolate(), entry->stream_average_duration()), attr); + Number::New(env->isolate(), entry->stream_average_duration()), + attr).FromJust(); entry->Notify(obj); } delete entry; From 9c85ada4e394800671814c2d59dbbec655575dbb Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 3 Jan 2018 11:15:57 -0800 Subject: [PATCH 171/218] http2: implement maxSessionMemory The maxSessionMemory is a cap for the amount of memory an Http2Session is permitted to consume. If exceeded, new `Http2Stream` sessions will be rejected with an `ENHANCE_YOUR_CALM` error and existing `Http2Stream` instances that are still receiving headers will be terminated with an `ENHANCE_YOUR_CALM` error. Backport-PR-URL: https://github.com/nodejs/node/pull/18050 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17967 Reviewed-By: Anna Henningsen Reviewed-By: Matteo Collina --- doc/api/http2.md | 27 +++++++++ lib/internal/http2/util.js | 8 ++- src/node_http2.cc | 55 +++++++++++++++---- src/node_http2.h | 45 ++++++++++++++- src/node_http2_state.h | 1 + .../test-http2-util-update-options-buffer.js | 7 ++- .../test-http2-max-session-memory.js | 44 +++++++++++++++ 7 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 test/sequential/test-http2-max-session-memory.js diff --git a/doc/api/http2.md b/doc/api/http2.md index fac3c35210a7d2..17c1ae018f9e4b 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1652,6 +1652,15 @@ changes: * `options` {Object} * `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size for deflating header fields. **Default:** `4Kib` + * `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session` + is permitted to use. The value is expressed in terms of number of megabytes, + e.g. `1` equal 1 megabyte. The minimum value allowed is `1`. **Default:** + `10`. This is a credit based limit, existing `Http2Stream`s may cause this + limit to be exceeded, but new `Http2Stream` instances will be rejected + while this limit is exceeded. The current number of `Http2Stream` sessions, + the current memory use of the header compression tables, current data + queued to be sent, and unacknowledged PING and SETTINGS frames are all + counted towards the current limit. * `maxHeaderListPairs` {number} Sets the maximum number of header entries. **Default:** `128`. The minimum value is `4`. * `maxOutstandingPings` {number} Sets the maximum number of outstanding, @@ -1730,6 +1739,15 @@ changes: `false`. See the [`'unknownProtocol'`][] event. See [ALPN negotiation][]. * `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size for deflating header fields. **Default:** `4Kib` + * `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session` + is permitted to use. The value is expressed in terms of number of megabytes, + e.g. `1` equal 1 megabyte. The minimum value allowed is `1`. **Default:** + `10`. This is a credit based limit, existing `Http2Stream`s may cause this + limit to be exceeded, but new `Http2Stream` instances will be rejected + while this limit is exceeded. The current number of `Http2Stream` sessions, + the current memory use of the header compression tables, current data + queued to be sent, and unacknowledged PING and SETTINGS frames are all + counted towards the current limit. * `maxHeaderListPairs` {number} Sets the maximum number of header entries. **Default:** `128`. The minimum value is `4`. * `maxOutstandingPings` {number} Sets the maximum number of outstanding, @@ -1813,6 +1831,15 @@ changes: * `options` {Object} * `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size for deflating header fields. **Default:** `4Kib` + * `maxSessionMemory`{number} Sets the maximum memory that the `Http2Session` + is permitted to use. The value is expressed in terms of number of megabytes, + e.g. `1` equal 1 megabyte. The minimum value allowed is `1`. **Default:** + `10`. This is a credit based limit, existing `Http2Stream`s may cause this + limit to be exceeded, but new `Http2Stream` instances will be rejected + while this limit is exceeded. The current number of `Http2Stream` sessions, + the current memory use of the header compression tables, current data + queued to be sent, and unacknowledged PING and SETTINGS frames are all + counted towards the current limit. * `maxHeaderListPairs` {number} Sets the maximum number of header entries. **Default:** `128`. The minimum value is `1`. * `maxOutstandingPings` {number} Sets the maximum number of outstanding, diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 51a2094d43af56..ef48b83d783af0 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -175,7 +175,8 @@ const IDX_OPTIONS_PADDING_STRATEGY = 4; const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5; const IDX_OPTIONS_MAX_OUTSTANDING_PINGS = 6; const IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS = 7; -const IDX_OPTIONS_FLAGS = 8; +const IDX_OPTIONS_MAX_SESSION_MEMORY = 8; +const IDX_OPTIONS_FLAGS = 9; function updateOptionsBuffer(options) { var flags = 0; @@ -219,6 +220,11 @@ function updateOptionsBuffer(options) { optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS] = Math.max(1, options.maxOutstandingSettings); } + if (typeof options.maxSessionMemory === 'number') { + flags |= (1 << IDX_OPTIONS_MAX_SESSION_MEMORY); + optionsBuffer[IDX_OPTIONS_MAX_SESSION_MEMORY] = + Math.max(1, options.maxSessionMemory); + } optionsBuffer[IDX_OPTIONS_FLAGS] = flags; } diff --git a/src/node_http2.cc b/src/node_http2.cc index 0c458ca33a3778..2ead727b116b35 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -174,6 +174,18 @@ Http2Options::Http2Options(Environment* env) { if (flags & (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS)) { SetMaxOutstandingSettings(buffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS]); } + + // The HTTP2 specification places no limits on the amount of memory + // that a session can consume. In order to prevent abuse, we place a + // cap on the amount of memory a session can consume at any given time. + // this is a credit based system. Existing streams may cause the limit + // to be temporarily exceeded but once over the limit, new streams cannot + // created. + // Important: The maxSessionMemory option in javascript is expressed in + // terms of MB increments (i.e. the value 1 == 1 MB) + if (flags & (1 << IDX_OPTIONS_MAX_SESSION_MEMORY)) { + SetMaxSessionMemory(buffer[IDX_OPTIONS_MAX_SESSION_MEMORY] * 1e6); + } } void Http2Session::Http2Settings::Init() { @@ -482,11 +494,13 @@ Http2Session::Http2Session(Environment* env, // Capture the configuration options for this session Http2Options opts(env); - int32_t maxHeaderPairs = opts.GetMaxHeaderPairs(); + max_session_memory_ = opts.GetMaxSessionMemory(); + + uint32_t maxHeaderPairs = opts.GetMaxHeaderPairs(); max_header_pairs_ = type == NGHTTP2_SESSION_SERVER - ? std::max(maxHeaderPairs, 4) // minimum # of request headers - : std::max(maxHeaderPairs, 1); // minimum # of response headers + ? std::max(maxHeaderPairs, 4U) // minimum # of request headers + : std::max(maxHeaderPairs, 1U); // minimum # of response headers max_outstanding_pings_ = opts.GetMaxOutstandingPings(); max_outstanding_settings_ = opts.GetMaxOutstandingSettings(); @@ -673,18 +687,21 @@ inline bool Http2Session::CanAddStream() { size_t maxSize = std::min(streams_.max_size(), static_cast(maxConcurrentStreams)); // We can add a new stream so long as we are less than the current - // maximum on concurrent streams - return streams_.size() < maxSize; + // maximum on concurrent streams and there's enough available memory + return streams_.size() < maxSize && + IsAvailableSessionMemory(sizeof(Http2Stream)); } inline void Http2Session::AddStream(Http2Stream* stream) { CHECK_GE(++statistics_.stream_count, 0); streams_[stream->id()] = stream; + IncrementCurrentSessionMemory(stream->self_size()); } -inline void Http2Session::RemoveStream(int32_t id) { - streams_.erase(id); +inline void Http2Session::RemoveStream(Http2Stream* stream) { + streams_.erase(stream->id()); + DecrementCurrentSessionMemory(stream->self_size()); } // Used as one of the Padding Strategy functions. Will attempt to ensure @@ -1678,7 +1695,7 @@ Http2Stream::Http2Stream( Http2Stream::~Http2Stream() { if (session_ != nullptr) { - session_->RemoveStream(id_); + session_->RemoveStream(this); session_ = nullptr; } @@ -2008,7 +2025,7 @@ inline int Http2Stream::DoWrite(WriteWrap* req_wrap, i == nbufs - 1 ? req_wrap : nullptr, bufs[i] }); - available_outbound_length_ += bufs[i].len; + IncrementAvailableOutboundLength(bufs[i].len); } CHECK_NE(nghttp2_session_resume_data(**session_, id_), NGHTTP2_ERR_NOMEM); return 0; @@ -2030,7 +2047,10 @@ inline bool Http2Stream::AddHeader(nghttp2_rcbuf* name, if (this->statistics_.first_header == 0) this->statistics_.first_header = uv_hrtime(); size_t length = GetBufferLength(name) + GetBufferLength(value) + 32; - if (current_headers_.size() == max_header_pairs_ || + // A header can only be added if we have not exceeded the maximum number + // of headers and the session has memory available for it. + if (!session_->IsAvailableSessionMemory(length) || + current_headers_.size() == max_header_pairs_ || current_headers_length_ + length > max_header_length_) { return false; } @@ -2174,7 +2194,7 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle, // Just return the length, let Http2Session::OnSendData take care of // actually taking the buffers out of the queue. *flags |= NGHTTP2_DATA_FLAG_NO_COPY; - stream->available_outbound_length_ -= amount; + stream->DecrementAvailableOutboundLength(amount); } } @@ -2197,6 +2217,15 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle, return amount; } +inline void Http2Stream::IncrementAvailableOutboundLength(size_t amount) { + available_outbound_length_ += amount; + session_->IncrementCurrentSessionMemory(amount); +} + +inline void Http2Stream::DecrementAvailableOutboundLength(size_t amount) { + available_outbound_length_ -= amount; + session_->DecrementCurrentSessionMemory(amount); +} // Implementation of the JavaScript API @@ -2690,6 +2719,7 @@ Http2Session::Http2Ping* Http2Session::PopPing() { if (!outstanding_pings_.empty()) { ping = outstanding_pings_.front(); outstanding_pings_.pop(); + DecrementCurrentSessionMemory(ping->self_size()); } return ping; } @@ -2698,6 +2728,7 @@ bool Http2Session::AddPing(Http2Session::Http2Ping* ping) { if (outstanding_pings_.size() == max_outstanding_pings_) return false; outstanding_pings_.push(ping); + IncrementCurrentSessionMemory(ping->self_size()); return true; } @@ -2706,6 +2737,7 @@ Http2Session::Http2Settings* Http2Session::PopSettings() { if (!outstanding_settings_.empty()) { settings = outstanding_settings_.front(); outstanding_settings_.pop(); + DecrementCurrentSessionMemory(settings->self_size()); } return settings; } @@ -2714,6 +2746,7 @@ bool Http2Session::AddSettings(Http2Session::Http2Settings* settings) { if (outstanding_settings_.size() == max_outstanding_settings_) return false; outstanding_settings_.push(settings); + IncrementCurrentSessionMemory(settings->self_size()); return true; } diff --git a/src/node_http2.h b/src/node_http2.h index ae35314c1ad380..5b5f7e5f52e084 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -82,6 +82,9 @@ void inline debug_vfprintf(const char* format, ...) { // Also strictly limit the number of outstanding SETTINGS frames a user sends #define DEFAULT_MAX_SETTINGS 10 +// Default maximum total memory cap for Http2Session. +#define DEFAULT_MAX_SESSION_MEMORY 1e7; + // These are the standard HTTP/2 defaults as specified by the RFC #define DEFAULT_SETTINGS_HEADER_TABLE_SIZE 4096 #define DEFAULT_SETTINGS_ENABLE_PUSH 1 @@ -500,8 +503,17 @@ class Http2Options { return max_outstanding_settings_; } + void SetMaxSessionMemory(uint64_t max) { + max_session_memory_ = max; + } + + uint64_t GetMaxSessionMemory() { + return max_session_memory_; + } + private: nghttp2_option* options_; + uint64_t max_session_memory_ = DEFAULT_MAX_SESSION_MEMORY; uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE; size_t max_outstanding_pings_ = DEFAULT_MAX_PINGS; @@ -628,6 +640,9 @@ class Http2Stream : public AsyncWrap, // Returns the stream identifier for this stream inline int32_t id() const { return id_; } + inline void IncrementAvailableOutboundLength(size_t amount); + inline void DecrementAvailableOutboundLength(size_t amount); + inline bool AddHeader(nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags); @@ -848,7 +863,7 @@ class Http2Session : public AsyncWrap { inline void AddStream(Http2Stream* stream); // Removes a stream instance from this session - inline void RemoveStream(int32_t id); + inline void RemoveStream(Http2Stream* stream); // Write data to the session inline ssize_t Write(const uv_buf_t* bufs, size_t nbufs); @@ -906,6 +921,30 @@ class Http2Session : public AsyncWrap { Http2Settings* PopSettings(); bool AddSettings(Http2Settings* settings); + void IncrementCurrentSessionMemory(uint64_t amount) { + current_session_memory_ += amount; + } + + void DecrementCurrentSessionMemory(uint64_t amount) { + current_session_memory_ -= amount; + } + + // Returns the current session memory including the current size of both + // the inflate and deflate hpack headers, the current outbound storage + // queue, and pending writes. + uint64_t GetCurrentSessionMemory() { + uint64_t total = current_session_memory_ + sizeof(Http2Session); + total += nghttp2_session_get_hd_deflate_dynamic_table_size(session_); + total += nghttp2_session_get_hd_inflate_dynamic_table_size(session_); + total += outgoing_storage_.size(); + return total; + } + + // Return true if current_session_memory + amount is less than the max + bool IsAvailableSessionMemory(uint64_t amount) { + return GetCurrentSessionMemory() + amount <= max_session_memory_; + } + struct Statistics { uint64_t start_time; uint64_t end_time; @@ -1035,6 +1074,10 @@ class Http2Session : public AsyncWrap { // The maximum number of header pairs permitted for streams on this session uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS; + // The maximum amount of memory allocated for this session + uint64_t max_session_memory_ = DEFAULT_MAX_SESSION_MEMORY; + uint64_t current_session_memory_ = 0; + // The collection of active Http2Streams associated with this session std::unordered_map streams_; diff --git a/src/node_http2_state.h b/src/node_http2_state.h index ef8696ce60d8f8..af0740c994e765 100644 --- a/src/node_http2_state.h +++ b/src/node_http2_state.h @@ -50,6 +50,7 @@ namespace http2 { IDX_OPTIONS_MAX_HEADER_LIST_PAIRS, IDX_OPTIONS_MAX_OUTSTANDING_PINGS, IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS, + IDX_OPTIONS_MAX_SESSION_MEMORY, IDX_OPTIONS_FLAGS }; diff --git a/test/parallel/test-http2-util-update-options-buffer.js b/test/parallel/test-http2-util-update-options-buffer.js index 5768ce0204dc8c..6ab8bcff02866e 100644 --- a/test/parallel/test-http2-util-update-options-buffer.js +++ b/test/parallel/test-http2-util-update-options-buffer.js @@ -20,7 +20,8 @@ const IDX_OPTIONS_PADDING_STRATEGY = 4; const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5; const IDX_OPTIONS_MAX_OUTSTANDING_PINGS = 6; const IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS = 7; -const IDX_OPTIONS_FLAGS = 8; +const IDX_OPTIONS_MAX_SESSION_MEMORY = 8; +const IDX_OPTIONS_FLAGS = 9; { updateOptionsBuffer({ @@ -31,7 +32,8 @@ const IDX_OPTIONS_FLAGS = 8; paddingStrategy: 5, maxHeaderListPairs: 6, maxOutstandingPings: 7, - maxOutstandingSettings: 8 + maxOutstandingSettings: 8, + maxSessionMemory: 9 }); strictEqual(optionsBuffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE], 1); @@ -42,6 +44,7 @@ const IDX_OPTIONS_FLAGS = 8; strictEqual(optionsBuffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS], 6); strictEqual(optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS], 7); strictEqual(optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS], 8); + strictEqual(optionsBuffer[IDX_OPTIONS_MAX_SESSION_MEMORY], 9); const flags = optionsBuffer[IDX_OPTIONS_FLAGS]; diff --git a/test/sequential/test-http2-max-session-memory.js b/test/sequential/test-http2-max-session-memory.js new file mode 100644 index 00000000000000..e16000d1261ab0 --- /dev/null +++ b/test/sequential/test-http2-max-session-memory.js @@ -0,0 +1,44 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); + +// Test that maxSessionMemory Caps work + +const largeBuffer = Buffer.alloc(1e6); + +const server = http2.createServer({ maxSessionMemory: 1 }); + +server.on('stream', common.mustCall((stream) => { + stream.respond(); + stream.end(largeBuffer); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + { + const req = client.request(); + + req.on('response', () => { + // This one should be rejected because the server is over budget + // on the current memory allocation + const req = client.request(); + req.on('error', common.expectsError({ + code: 'ERR_HTTP2_STREAM_ERROR', + type: Error, + message: 'Stream closed with error code 11' + })); + req.on('close', common.mustCall(() => { + server.close(); + client.destroy(); + })); + }); + + req.resume(); + req.on('close', common.mustCall()); + } +})); From a85518ed224b3b51bccab4ab5ba1f250cccaf383 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 3 Jan 2018 12:02:46 -0800 Subject: [PATCH 172/218] http2: verify that a dependency cycle may exist Backport-PR-URL: https://github.com/nodejs/node/pull/18050 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17968 Reviewed-By: Anna Henningsen --- test/parallel/test-http2-priority-cycle-.js | 69 +++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/parallel/test-http2-priority-cycle-.js diff --git a/test/parallel/test-http2-priority-cycle-.js b/test/parallel/test-http2-priority-cycle-.js new file mode 100644 index 00000000000000..af0d66d8343cbf --- /dev/null +++ b/test/parallel/test-http2-priority-cycle-.js @@ -0,0 +1,69 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const Countdown = require('../common/countdown'); + +const server = http2.createServer(); +const largeBuffer = Buffer.alloc(1e4); + +// Verify that a dependency cycle may exist, but that it doesn't crash anything + +server.on('stream', common.mustCall((stream) => { + stream.respond(); + setImmediate(() => { + stream.end(largeBuffer); + }); +}, 3)); +server.on('session', common.mustCall((session) => { + session.on('priority', (id, parent, weight, exclusive) => { + assert.strictEqual(weight, 16); + assert.strictEqual(exclusive, false); + switch (id) { + case 1: + assert.strictEqual(parent, 5); + break; + case 3: + assert.strictEqual(parent, 1); + break; + case 5: + assert.strictEqual(parent, 3); + break; + default: + assert.fail('should not happen'); + } + }); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const countdown = new Countdown(3, () => { + client.close(); + server.close(); + }); + + { + const req = client.request(); + req.priority({ parent: 5 }); + req.resume(); + req.on('close', () => countdown.dec()); + } + + { + const req = client.request(); + req.priority({ parent: 1 }); + req.resume(); + req.on('close', () => countdown.dec()); + } + + { + const req = client.request(); + req.priority({ parent: 3 }); + req.resume(); + req.on('close', () => countdown.dec()); + } +})); From 591f78bb0a9b9f379611e575e538f95a215f615f Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 3 Jan 2018 16:39:46 -0800 Subject: [PATCH 173/218] doc: grammar fixes in http2.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport-PR-URL: https://github.com/nodejs/node/pull/18050 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17972 Reviewed-By: Weijia Wang Reviewed-By: Jon Moss Reviewed-By: Evan Lucas Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Tobias Nießen --- doc/api/http2.md | 56 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 17c1ae018f9e4b..b0b692c492fe64 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -166,7 +166,7 @@ When invoked, the handler function will receive three arguments: If the `'frameError'` event is associated with a stream, the stream will be closed and destroyed immediately following the `'frameError'` event. If the -event is not associated with a stream, the `Http2Session` will be shutdown +event is not associated with a stream, the `Http2Session` will be shut down immediately following the `'frameError'` event. #### Event: 'goaway' @@ -183,7 +183,7 @@ the handler function will receive three arguments: * `opaqueData` {Buffer} If additional opaque data was included in the GOAWAY frame, a `Buffer` instance will be passed containing that data. -*Note*: The `Http2Session` instance will be shutdown automatically when the +*Note*: The `Http2Session` instance will be shut down automatically when the `'goaway'` event is emitted. #### Event: 'localSettings' @@ -499,7 +499,7 @@ added: v8.4.0 has been completed. * Returns: {undefined} -Attempts to shutdown this `Http2Session` using HTTP/2 defined procedures. +Attempts to shut down this `Http2Session` using HTTP/2 defined procedures. If specified, the given `callback` function will be invoked once the shutdown process has completed. @@ -647,7 +647,7 @@ may be passed to clear any previously set alternative service for a given domain. When a string is passed for the `originOrStream` argument, it will be parsed as -a URL and the origin will be derived. For insetance, the origin for the +a URL and the origin will be derived. For instance, the origin for the HTTP URL `'https://example.org/foo/bar'` is the ASCII string `'https://example.org'`. An error will be thrown if either the given string cannot be parsed as a URL or if a valid origin cannot be derived. @@ -751,15 +751,15 @@ req.on('response', (headers) => { When set, the `options.getTrailers()` function is called immediately after queuing the last chunk of payload data to be sent. The callback is passed a -single object (with a `null` prototype) that the listener may used to specify +single object (with a `null` prototype) that the listener may use to specify the trailing header fields to send to the peer. *Note*: The HTTP/1 specification forbids trailers from containing HTTP/2 -"pseudo-header" fields (e.g. `':method'`, `':path'`, etc). An `'error'` event +pseudo-header fields (e.g. `':method'`, `':path'`, etc). An `'error'` event will be emitted if the `getTrailers` callback attempts to set such header fields. -The `:method` and `:path` pseudoheaders are not specified within `headers`, +The `:method` and `:path` pseudo-headers are not specified within `headers`, they respectively default to: * `:method` = `'GET'` @@ -786,7 +786,7 @@ On the client, `Http2Stream` instances are created and returned when either the `'push'` event. *Note*: The `Http2Stream` class is a base for the [`ServerHttp2Stream`][] and -[`ClientHttp2Stream`][] classes, each of which are used specifically by either +[`ClientHttp2Stream`][] classes, each of which is used specifically by either the Server or Client side, respectively. All `Http2Stream` instances are [`Duplex`][] streams. The `Writable` side of the @@ -810,7 +810,7 @@ On the client side, instances of [`ClientHttp2Stream`][] are created when the `http2session.request()` may not be immediately ready for use if the parent `Http2Session` has not yet been fully established. In such cases, operations called on the `Http2Stream` will be buffered until the `'ready'` event is -emitted. User code should rarely, if ever, have need to handle the `'ready'` +emitted. User code should rarely, if ever, need to handle the `'ready'` event directly. The ready status of an `Http2Stream` can be determined by checking the value of `http2stream.id`. If the value is `undefined`, the stream is not yet ready for use. @@ -1067,7 +1067,7 @@ added: v8.4.0 --> The `'headers'` event is emitted when an additional block of headers is received -for a stream, such as when a block of `1xx` informational headers are received. +for a stream, such as when a block of `1xx` informational headers is received. The listener callback is passed the [Headers Object][] and flags associated with the headers. @@ -1217,7 +1217,7 @@ server.on('stream', (stream) => { When set, the `options.getTrailers()` function is called immediately after queuing the last chunk of payload data to be sent. The callback is passed a -single object (with a `null` prototype) that the listener may used to specify +single object (with a `null` prototype) that the listener may use to specify the trailing header fields to send to the peer. ```js @@ -1234,7 +1234,7 @@ server.on('stream', (stream) => { ``` *Note*: The HTTP/1 specification forbids trailers from containing HTTP/2 -"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event +pseudo-header fields (e.g. `':status'`, `':path'`, etc). An `'error'` event will be emitted if the `getTrailers` callback attempts to set such header fields. @@ -1291,7 +1291,7 @@ requests. When set, the `options.getTrailers()` function is called immediately after queuing the last chunk of payload data to be sent. The callback is passed a -single object (with a `null` prototype) that the listener may used to specify +single object (with a `null` prototype) that the listener may use to specify the trailing header fields to send to the peer. ```js @@ -1318,7 +1318,7 @@ server.on('close', () => fs.closeSync(fd)); ``` *Note*: The HTTP/1 specification forbids trailers from containing HTTP/2 -"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event +pseudo-header fields (e.g. `':status'`, `':path'`, etc). An `'error'` event will be emitted if the `getTrailers` callback attempts to set such header fields. @@ -1350,7 +1350,7 @@ of the given file: If an error occurs while attempting to read the file data, the `Http2Stream` will be closed using an `RST_STREAM` frame using the standard `INTERNAL_ERROR` -code. If the `onError` callback is defined it will be called, otherwise +code. If the `onError` callback is defined, then it will be called. Otherwise the stream will be destroyed. Example using a file path: @@ -1410,7 +1410,7 @@ default behavior is to destroy the stream. When set, the `options.getTrailers()` function is called immediately after queuing the last chunk of payload data to be sent. The callback is passed a -single object (with a `null` prototype) that the listener may used to specify +single object (with a `null` prototype) that the listener may use to specify the trailing header fields to send to the peer. ```js @@ -1427,7 +1427,7 @@ server.on('stream', (stream) => { ``` *Note*: The HTTP/1 specification forbids trailers from containing HTTP/2 -"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event +pseudo-header fields (e.g. `':status'`, `':path'`, etc). An `'error'` event will be emitted if the `getTrailers` callback attempts to set such header fields. @@ -1497,7 +1497,7 @@ an `Http2Session` object associated with the `Http2Server`. added: v8.5.0 --> -If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here. +If a `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here. The stream will already be destroyed when this event is triggered. #### Event: 'stream' @@ -1683,7 +1683,7 @@ changes: * `http2.constants.PADDING_STRATEGY_ALIGNED` - Will *attempt* to apply enough padding to ensure that the total frame length, including the 9-byte header, is a multiple of 8. For each frame, however, there is a - maxmimum allowed number of padding bytes that is determined by current + maximum allowed number of padding bytes that is determined by current flow control state and settings. If this maximum is less than the calculated amount needed to ensure alignment, the maximum will be used and the total frame length will *not* necessarily be aligned at 8 bytes. @@ -1770,7 +1770,7 @@ changes: * `http2.constants.PADDING_STRATEGY_ALIGNED` - Will *attempt* to apply enough padding to ensure that the total frame length, including the 9-byte header, is a multiple of 8. For each frame, however, there is a - maxmimum allowed number of padding bytes that is determined by current + maximum allowed number of padding bytes that is determined by current flow control state and settings. If this maximum is less than the calculated amount needed to ensure alignment, the maximum will be used and the total frame length will *not* necessarily be aligned at 8 bytes. @@ -1866,7 +1866,7 @@ changes: * `http2.constants.PADDING_STRATEGY_ALIGNED` - Will *attempt* to apply enough padding to ensure that the total frame length, including the 9-byte header, is a multiple of 8. For each frame, however, there is a - maxmimum allowed number of padding bytes that is determined by current + maximum allowed number of padding bytes that is determined by current flow control state and settings. If this maximum is less than the calculated amount needed to ensure alignment, the maximum will be used and the total frame length will *not* necessarily be aligned at 8 bytes. @@ -2208,8 +2208,8 @@ req.end('Jane'); The Compatibility API has the goal of providing a similar developer experience of HTTP/1 when using HTTP/2, making it possible to develop applications -that supports both [HTTP/1][] and HTTP/2. This API targets only the -**public API** of the [HTTP/1][], however many modules uses internal +that support both [HTTP/1][] and HTTP/2. This API targets only the +**public API** of the [HTTP/1][]. However many modules use internal methods or state, and those _are not supported_ as it is a completely different implementation. @@ -2237,7 +2237,7 @@ the status message for HTTP codes is ignored. ### ALPN negotiation -ALPN negotiation allows to support both [HTTPS][] and HTTP/2 over +ALPN negotiation allows supporting both [HTTPS][] and HTTP/2 over the same socket. The `req` and `res` objects can be either HTTP/1 or HTTP/2, and an application **must** restrict itself to the public API of [HTTP/1][], and detect if it is possible to use the more advanced @@ -2279,7 +2279,7 @@ added: v8.4.0 A `Http2ServerRequest` object is created by [`http2.Server`][] or [`http2.SecureServer`][] and passed as the first argument to the -[`'request'`][] event. It may be used to access a request status, headers and +[`'request'`][] event. It may be used to access a request status, headers, and data. It implements the [Readable Stream][] interface, as well as the @@ -2340,7 +2340,7 @@ console.log(request.headers); See [Headers Object][]. -*Note*: In HTTP/2, the request path, host name, protocol, and method are +*Note*: In HTTP/2, the request path, hostname, protocol, and method are represented as special headers prefixed with the `:` character (e.g. `':path'`). These special headers will be included in the `request.headers` object. Care must be taken not to inadvertently modify these special headers or errors may @@ -2373,7 +2373,7 @@ added: v8.4.0 * {string} -The request method as a string. Read only. Example: +The request method as a string. Read-only. Example: `'GET'`, `'DELETE'`. #### request.rawHeaders @@ -3031,7 +3031,7 @@ If `name` is equal to `Http2Session`, the `PerformanceEntry` will contain the following additional properties: * `pingRTT` {number} The number of milliseconds elapsed since the transmission - of a `PING` frame and the reception of its acknowledgement. Only present if + of a `PING` frame and the reception of its acknowledgment. Only present if a `PING` frame has been sent on the `Http2Session`. * `streamCount` {number} The number of `Http2Stream` instances processed by the `Http2Session`. From 46d1b331e0b5f92d7df808fd3e44fc95b11de1bd Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 3 Jan 2018 13:34:45 -0800 Subject: [PATCH 174/218] http2: verify flood error and unsolicited frames * verify protections against ping and settings flooding * Strictly handle and verify handling of unsolicited ping and settings frame acks. Backport-PR-URL: https://github.com/nodejs/node/pull/18050 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17969 Reviewed-By: Anna Henningsen Reviewed-By: Anatoli Papirovski Reviewed-By: Matteo Collina --- src/node_http2.cc | 40 ++++++++++++- test/common/http2.js | 10 ++++ .../test-http2-ping-unsolicited-ack.js | 43 ++++++++++++++ .../test-http2-settings-unsolicited-ack.js | 50 +++++++++++++++++ test/sequential/sequential.status | 2 + test/sequential/test-http2-ping-flood.js | 56 +++++++++++++++++++ test/sequential/test-http2-settings-flood.js | 53 ++++++++++++++++++ 7 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-http2-ping-unsolicited-ack.js create mode 100644 test/parallel/test-http2-settings-unsolicited-ack.js create mode 100644 test/sequential/test-http2-ping-flood.js create mode 100644 test/sequential/test-http2-settings-flood.js diff --git a/src/node_http2.cc b/src/node_http2.cc index 2ead727b116b35..844421fa35c1c3 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1306,8 +1306,24 @@ inline void Http2Session::HandlePingFrame(const nghttp2_frame* frame) { bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK; if (ack) { Http2Ping* ping = PopPing(); - if (ping != nullptr) + if (ping != nullptr) { ping->Done(true, frame->ping.opaque_data); + } else { + // PING Ack is unsolicited. Treat as a connection error. The HTTP/2 + // spec does not require this, but there is no legitimate reason to + // receive an unsolicited PING ack on a connection. Either the peer + // is buggy or malicious, and we're not going to tolerate such + // nonsense. + Isolate* isolate = env()->isolate(); + HandleScope scope(isolate); + Local context = env()->context(); + Context::Scope context_scope(context); + + Local argv[1] = { + Integer::New(isolate, NGHTTP2_ERR_PROTO), + }; + MakeCallback(env()->error_string(), arraysize(argv), argv); + } } } @@ -1318,8 +1334,28 @@ inline void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { // If this is an acknowledgement, we should have an Http2Settings // object for it. Http2Settings* settings = PopSettings(); - if (settings != nullptr) + if (settings != nullptr) { settings->Done(true); + } else { + // SETTINGS Ack is unsolicited. Treat as a connection error. The HTTP/2 + // spec does not require this, but there is no legitimate reason to + // receive an unsolicited SETTINGS ack on a connection. Either the peer + // is buggy or malicious, and we're not going to tolerate such + // nonsense. + // Note that nghttp2 currently prevents this from happening for SETTINGS + // frames, so this block is purely defensive just in case that behavior + // changes. Specifically, unlike unsolicited PING acks, unsolicited + // SETTINGS acks should *never* make it this far. + Isolate* isolate = env()->isolate(); + HandleScope scope(isolate); + Local context = env()->context(); + Context::Scope context_scope(context); + + Local argv[1] = { + Integer::New(isolate, NGHTTP2_ERR_PROTO), + }; + MakeCallback(env()->error_string(), arraysize(argv), argv); + } } else { // Otherwise, notify the session about a new settings MakeCallback(env()->onsettings_string(), 0, nullptr); diff --git a/test/common/http2.js b/test/common/http2.js index 44f5f9191e6e33..1d4c269fffd5b5 100644 --- a/test/common/http2.js +++ b/test/common/http2.js @@ -118,11 +118,21 @@ class HeadersFrame extends Frame { } } +class PingFrame extends Frame { + constructor(ack = false) { + const buffers = [Buffer.alloc(8)]; + super(8, 6, ack ? 1 : 0, 0); + buffers.unshift(this[kFrameData]); + this[kFrameData] = Buffer.concat(buffers); + } +} + module.exports = { Frame, DataFrame, HeadersFrame, SettingsFrame, + PingFrame, kFakeRequestHeaders, kFakeResponseHeaders, kClientMagic diff --git a/test/parallel/test-http2-ping-unsolicited-ack.js b/test/parallel/test-http2-ping-unsolicited-ack.js new file mode 100644 index 00000000000000..5a3a261cb098b1 --- /dev/null +++ b/test/parallel/test-http2-ping-unsolicited-ack.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); +const net = require('net'); +const http2util = require('../common/http2'); + +// Test that ping flooding causes the session to be torn down + +const kSettings = new http2util.SettingsFrame(); +const kPingAck = new http2util.PingFrame(true); + +const server = http2.createServer(); + +server.on('stream', common.mustNotCall()); +server.on('session', common.mustCall((session) => { + session.on('error', common.expectsError({ + code: 'ERR_HTTP2_ERROR', + message: 'Protocol error' + })); + session.on('close', common.mustCall(() => server.close())); +})); + +server.listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + + client.on('connect', common.mustCall(() => { + client.write(http2util.kClientMagic, () => { + client.write(kSettings.data); + // Send an unsolicited ping ack + client.write(kPingAck.data); + }); + })); + + // An error event may or may not be emitted, depending on operating system + // and timing. We do not really care if one is emitted here or not, as the + // error on the server side is what we are testing for. Do not make this + // a common.mustCall() and there's no need to check the error details. + client.on('error', () => {}); +})); diff --git a/test/parallel/test-http2-settings-unsolicited-ack.js b/test/parallel/test-http2-settings-unsolicited-ack.js new file mode 100644 index 00000000000000..fa63e9ee3f6425 --- /dev/null +++ b/test/parallel/test-http2-settings-unsolicited-ack.js @@ -0,0 +1,50 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); +const net = require('net'); +const http2util = require('../common/http2'); +const Countdown = require('../common/countdown'); + +// Test that an unsolicited settings ack is ignored. + +const kSettings = new http2util.SettingsFrame(); +const kSettingsAck = new http2util.SettingsFrame(true); + +const server = http2.createServer(); +let client; + +const countdown = new Countdown(3, () => { + client.destroy(); + server.close(); +}); + +server.on('stream', common.mustNotCall()); +server.on('session', common.mustCall((session) => { + session.on('remoteSettings', common.mustCall(() => countdown.dec())); +})); + +server.listen(0, common.mustCall(() => { + client = net.connect(server.address().port); + + // Ensures that the clients settings frames are not sent until the + // servers are received, so that the first ack is actually expected. + client.once('data', (chunk) => { + // The very first chunk of data we get from the server should + // be a settings frame. + assert.deepStrictEqual(chunk.slice(0, 9), kSettings.data); + // The first ack is expected. + client.write(kSettingsAck.data, () => countdown.dec()); + // The second one is not and will be ignored. + client.write(kSettingsAck.data, () => countdown.dec()); + }); + + client.on('connect', common.mustCall(() => { + client.write(http2util.kClientMagic); + client.write(kSettings.data); + })); +})); diff --git a/test/sequential/sequential.status b/test/sequential/sequential.status index 656eb80c4db4ee..5e39392e658a79 100644 --- a/test/sequential/sequential.status +++ b/test/sequential/sequential.status @@ -12,6 +12,8 @@ test-inspector-bindings : PASS, FLAKY test-inspector-debug-end : PASS, FLAKY test-inspector-async-hook-setup-at-signal: PASS, FLAKY test-inspector-stop-profile-after-done: PASS, FLAKY +test-http2-ping-flood : PASS, FLAKY +test-http2-settings-flood : PASS, FLAKY [$system==linux] diff --git a/test/sequential/test-http2-ping-flood.js b/test/sequential/test-http2-ping-flood.js new file mode 100644 index 00000000000000..5b47d51be9c5a8 --- /dev/null +++ b/test/sequential/test-http2-ping-flood.js @@ -0,0 +1,56 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); +const net = require('net'); +const http2util = require('../common/http2'); + +// Test that ping flooding causes the session to be torn down + +const kSettings = new http2util.SettingsFrame(); +const kPing = new http2util.PingFrame(); + +const server = http2.createServer(); + +server.on('stream', common.mustNotCall()); +server.on('session', common.mustCall((session) => { + session.on('error', common.expectsError({ + code: 'ERR_HTTP2_ERROR', + message: + 'Flooding was detected in this HTTP/2 session, and it must be closed' + })); + session.on('close', common.mustCall(() => { + server.close(); + })); +})); + +server.listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + + // nghttp2 uses a limit of 10000 items in it's outbound queue. + // If this number is exceeded, a flooding error is raised. Set + // this lim higher to account for the ones that nghttp2 is + // successfully able to respond to. + // TODO(jasnell): Unfortunately, this test is inherently flaky because + // it is entirely dependent on how quickly the server is able to handle + // the inbound frames and whether those just happen to overflow nghttp2's + // outbound queue. The threshold at which the flood error occurs can vary + // from one system to another, and from one test run to another. + client.on('connect', common.mustCall(() => { + client.write(http2util.kClientMagic, () => { + client.write(kSettings.data, () => { + for (let n = 0; n < 35000; n++) + client.write(kPing.data); + }); + }); + })); + + // An error event may or may not be emitted, depending on operating system + // and timing. We do not really care if one is emitted here or not, as the + // error on the server side is what we are testing for. Do not make this + // a common.mustCall() and there's no need to check the error details. + client.on('error', () => {}); +})); diff --git a/test/sequential/test-http2-settings-flood.js b/test/sequential/test-http2-settings-flood.js new file mode 100644 index 00000000000000..bad4cec9a8d509 --- /dev/null +++ b/test/sequential/test-http2-settings-flood.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const http2 = require('http2'); +const net = require('net'); +const http2util = require('../common/http2'); + +// Test that settings flooding causes the session to be torn down + +const kSettings = new http2util.SettingsFrame(); + +const server = http2.createServer(); + +server.on('stream', common.mustNotCall()); +server.on('session', common.mustCall((session) => { + session.on('error', common.expectsError({ + code: 'ERR_HTTP2_ERROR', + message: + 'Flooding was detected in this HTTP/2 session, and it must be closed' + })); + session.on('close', common.mustCall(() => { + server.close(); + })); +})); + +server.listen(0, common.mustCall(() => { + const client = net.connect(server.address().port); + + // nghttp2 uses a limit of 10000 items in it's outbound queue. + // If this number is exceeded, a flooding error is raised. Set + // this lim higher to account for the ones that nghttp2 is + // successfully able to respond to. + // TODO(jasnell): Unfortunately, this test is inherently flaky because + // it is entirely dependent on how quickly the server is able to handle + // the inbound frames and whether those just happen to overflow nghttp2's + // outbound queue. The threshold at which the flood error occurs can vary + // from one system to another, and from one test run to another. + client.on('connect', common.mustCall(() => { + client.write(http2util.kClientMagic, () => { + for (let n = 0; n < 35000; n++) + client.write(kSettings.data); + }); + })); + + // An error event may or may not be emitted, depending on operating system + // and timing. We do not really care if one is emitted here or not, as the + // error on the server side is what we are testing for. Do not make this + // a common.mustCall() and there's no need to check the error details. + client.on('error', () => {}); +})); From 0205b3f0c1b9982d11a421251c5a7e61b1b77479 Mon Sep 17 00:00:00 2001 From: sreepurnajasti Date: Fri, 29 Dec 2017 10:25:31 +0530 Subject: [PATCH 175/218] doc: correct spelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport-PR-URL: https://github.com/nodejs/node/pull/18050 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17911 Reviewed-By: James M Snell Reviewed-By: Rich Trott Reviewed-By: Daniel Bevenius Reviewed-By: Luigi Pinca Reviewed-By: Gibson Fahnestock Reviewed-By: Tobias Nießen Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Gireesh Punathil Reviewed-By: Weijia Wang --- doc/api/errors.md | 15 ++++++++++++--- doc/api/http2.md | 16 ++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index 72def9a644bdd7..1c5836e553df1a 100755 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -770,7 +770,7 @@ An operation was performed on a stream that had already been destroyed. ### ERR_HTTP2_MAX_PENDING_SETTINGS_ACK Whenever an HTTP/2 `SETTINGS` frame is sent to a connected peer, the peer is -required to send an acknowledgement that it has received and applied the new +required to send an acknowledgment that it has received and applied the new `SETTINGS`. By default, a maximum number of unacknowledged `SETTINGS` frames may be sent at any given time. This error code is used when that limit has been reached. @@ -796,7 +796,7 @@ forbidden. ### ERR_HTTP2_PING_CANCEL -An HTTP/2 ping was cancelled. +An HTTP/2 ping was canceled. ### ERR_HTTP2_PING_LENGTH @@ -1028,7 +1028,16 @@ Used when an [ES6 module][] cannot be resolved. > Stability: 1 - Experimental -Used when a failure occurs resolving imports in an [ES6 module][]. +Used when a failure occurred resolving imports in an [ES6 module][]. + + +### ERR_MULTIPLE_CALLBACK + +A callback was called more than once. + +*Note*: A callback is almost always meant to only be called once as the query +can either be fulfilled or rejected but not both at the same time. The latter +would be possible by calling a callback more than once. ### ERR_NAPI_CONS_FUNCTION diff --git a/doc/api/http2.md b/doc/api/http2.md index b0b692c492fe64..8b3e33723b4c7d 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -191,7 +191,7 @@ the handler function will receive three arguments: added: v8.4.0 --> -The `'localSettings'` event is emitted when an acknowledgement SETTINGS frame +The `'localSettings'` event is emitted when an acknowledgment SETTINGS frame has been received. When invoked, the handler function will receive a copy of the local settings. @@ -339,7 +339,7 @@ Once destroyed, the `Http2Session` will emit the `'close'` event. If `error` is not undefined, an `'error'` event will be emitted immediately after the `'close'` event. -If there are any remaining open `Http2Streams` associatd with the +If there are any remaining open `Http2Streams` associated with the `Http2Session`, those will also be destroyed. #### http2session.destroyed @@ -406,7 +406,7 @@ added: v8.4.0 * Value: {boolean} Indicates whether or not the `Http2Session` is currently waiting for an -acknowledgement for a sent SETTINGS frame. Will be `true` after calling the +acknowledgment for a sent SETTINGS frame. Will be `true` after calling the `http2session.settings()` method. Will be `false` once all sent SETTINGS frames have been acknowledged. @@ -428,12 +428,12 @@ The maximum number of outstanding (unacknowledged) pings is determined by the If provided, the `payload` must be a `Buffer`, `TypedArray`, or `DataView` containing 8 bytes of data that will be transmitted with the `PING` and -returned with the ping acknowledgement. +returned with the ping acknowledgment. The callback will be invoked with three arguments: an error argument that will be `null` if the `PING` was successfully acknowledged, a `duration` argument that reports the number of milliseconds elapsed since the ping was sent and the -acknowledgement was received, and a `Buffer` containing the 8-byte `PING` +acknowledgment was received, and a `Buffer` containing the 8-byte `PING` payload. ```js @@ -581,8 +581,8 @@ while the session is waiting for the remote peer to acknowledge the new settings. *Note*: The new settings will not become effective until the SETTINGS -acknowledgement is received and the `'localSettings'` event is emitted. It -is possible to send multiple SETTINGS frames while acknowledgement is still +acknowledgment is received and the `'localSettings'` event is emitted. It +is possible to send multiple SETTINGS frames while acknowledgment is still pending. #### http2session.type @@ -885,7 +885,7 @@ added: v8.4.0 --> The `'timeout'` event is emitted after no activity is received for this -`'Http2Stream'` within the number of millseconds set using +`'Http2Stream'` within the number of milliseconds set using `http2stream.setTimeout()`. #### Event: 'trailers' From c924adf33d5f4c7c0ca840500b777855b148dbe6 Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Thu, 4 Jan 2018 02:18:31 +0200 Subject: [PATCH 176/218] doc: fix code nits in common/README 1. Sync comments and code 2. Fix typos Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17971 Reviewed-By: Rich Trott Reviewed-By: Khaidi Chu Reviewed-By: Jon Moss Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- test/common/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index e4bf2887ca3a71..e7ab9dec0bada0 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -610,7 +610,7 @@ frame. // padlen is an 8-bit integer giving the number of padding bytes to include // final is a boolean indicating whether the End-of-stream flag should be set, // defaults to false. -const data = new http2.DataFrame(id, payload, padlen, final); +const frame = new http2.DataFrame(id, payload, padlen, final); socket.write(frame.data); ``` @@ -631,8 +631,7 @@ The `http2.HeadersFrame` is a subclass of `http2.Frame` that serializes a // padlen is an 8-bit integer giving the number of padding bytes to include // final is a boolean indicating whether the End-of-stream flag should be set, // defaults to false. -const data = new http2.HeadersFrame(id, http2.kFakeRequestHeaders, - padlen, final); +const frame = new http2.HeadersFrame(id, payload, padlen, final); socket.write(frame.data); ``` From e9de5a976b82528d8c1ebb816cd1295017d429fb Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Thu, 4 Jan 2018 02:37:39 +0200 Subject: [PATCH 177/218] doc: re-alphabetise sections in common/README.md Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17971 Reviewed-By: Rich Trott Reviewed-By: Khaidi Chu Reviewed-By: Jon Moss Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- test/common/README.md | 101 ++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 57 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index e7ab9dec0bada0..17ded3526bdf7f 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -10,10 +10,10 @@ This directory contains modules used to test the Node.js implementation. * [DNS module](#dns-module) * [Duplex pair helper](#duplex-pair-helper) * [Fixtures module](#fixtures-module) +* [HTTP2 module](#http2-module) * [Internet module](#internet-module) * [tmpdir module](#tmpdir-module) * [WPT module](#wpt-module) -* [HTTP2 module](#http2-module) ## Benchmark Module @@ -504,62 +504,6 @@ Returns the result of Returns the result of `fs.readFileSync(path.join(fixtures.fixturesDir, 'keys', arg), 'enc')`. -## Internet Module - -The `common/internet` module provides utilities for working with -internet-related tests. - -### internet.addresses - -* [<Object>] - * `INET_HOST` [<String>] A generic host that has registered common - DNS records, supports both IPv4 and IPv6, and provides basic HTTP/HTTPS - services - * `INET4_HOST` [<String>] A host that provides IPv4 services - * `INET6_HOST` [<String>] A host that provides IPv6 services - * `INET4_IP` [<String>] An accessible IPv4 IP, defaults to the - Google Public DNS IPv4 address - * `INET6_IP` [<String>] An accessible IPv6 IP, defaults to the - Google Public DNS IPv6 address - * `INVALID_HOST` [<String>] An invalid host that cannot be resolved - * `MX_HOST` [<String>] A host with MX records registered - * `SRV_HOST` [<String>] A host with SRV records registered - * `PTR_HOST` [<String>] A host with PTR records registered - * `NAPTR_HOST` [<String>] A host with NAPTR records registered - * `SOA_HOST` [<String>] A host with SOA records registered - * `CNAME_HOST` [<String>] A host with CNAME records registered - * `NS_HOST` [<String>] A host with NS records registered - * `TXT_HOST` [<String>] A host with TXT records registered - * `DNS4_SERVER` [<String>] An accessible IPv4 DNS server - * `DNS6_SERVER` [<String>] An accessible IPv6 DNS server - -A set of addresses for internet-related tests. All properties are configurable -via `NODE_TEST_*` environment variables. For example, to configure -`internet.addresses.INET_HOST`, set the environment -vairable `NODE_TEST_INET_HOST` to a specified host. - -## tmpdir Module - -The `tmpdir` module supports the use of a temporary directory for testing. - -### path -* [<String>] - -The realpath of the testing temporary directory. - -### refresh() - -Deletes and recreates the testing temporary directory. - -## WPT Module - -The wpt.js module is a port of parts of -[W3C testharness.js](https://github.com/w3c/testharness.js) for testing the -Node.js -[WHATWG URL API](https://nodejs.org/api/url.html#url_the_whatwg_url_api) -implementation with tests from -[W3C Web Platform Tests](https://github.com/w3c/web-platform-tests). - ## HTTP/2 Module The http2.js module provides a handful of utilities for creating mock HTTP/2 @@ -695,6 +639,49 @@ upon initial establishment of a connection. socket.write(http2.kClientMagic); ``` +## Internet Module + +The `common/internet` module provides utilities for working with +internet-related tests. + +### internet.addresses + +* [<Object>] + * `INET_HOST` [<String>] A generic host that has registered common + DNS records, supports both IPv4 and IPv6, and provides basic HTTP/HTTPS + services + * `INET4_HOST` [<String>] A host that provides IPv4 services + * `INET6_HOST` [<String>] A host that provides IPv6 services + * `INET4_IP` [<String>] An accessible IPv4 IP, defaults to the + Google Public DNS IPv4 address + * `INET6_IP` [<String>] An accessible IPv6 IP, defaults to the + Google Public DNS IPv6 address + * `INVALID_HOST` [<String>] An invalid host that cannot be resolved + * `MX_HOST` [<String>] A host with MX records registered + * `SRV_HOST` [<String>] A host with SRV records registered + * `PTR_HOST` [<String>] A host with PTR records registered + * `NAPTR_HOST` [<String>] A host with NAPTR records registered + * `SOA_HOST` [<String>] A host with SOA records registered + * `CNAME_HOST` [<String>] A host with CNAME records registered + * `NS_HOST` [<String>] A host with NS records registered + * `TXT_HOST` [<String>] A host with TXT records registered + * `DNS4_SERVER` [<String>] An accessible IPv4 DNS server + * `DNS6_SERVER` [<String>] An accessible IPv6 DNS server + +A set of addresses for internet-related tests. All properties are configurable +via `NODE_TEST_*` environment variables. For example, to configure +`internet.addresses.INET_HOST`, set the environment +vairable `NODE_TEST_INET_HOST` to a specified host. + +## WPT Module + +The wpt.js module is a port of parts of +[W3C testharness.js](https://github.com/w3c/testharness.js) for testing the +Node.js +[WHATWG URL API](https://nodejs.org/api/url.html#url_the_whatwg_url_api) +implementation with tests from +[W3C Web Platform Tests](https://github.com/w3c/web-platform-tests). + [<Array>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array [<ArrayBufferView[]>]: https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView From 940457394a58dbe89d25c68fc347bba3b7ed9ea4 Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Thu, 4 Jan 2018 02:08:49 +0200 Subject: [PATCH 178/218] doc: compact eslint directives in common/README Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/17971 Reviewed-By: Rich Trott Reviewed-By: Khaidi Chu Reviewed-By: Jon Moss Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater --- test/common/README.md | 40 ++++++++-------------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index 17ded3526bdf7f..fad4467775ac32 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -509,10 +509,7 @@ Returns the result of The http2.js module provides a handful of utilities for creating mock HTTP/2 frames for testing of HTTP/2 endpoints - - - - + ```js const http2 = require('../common/http2'); ``` @@ -522,10 +519,7 @@ const http2 = require('../common/http2'); The `http2.Frame` is a base class that creates a `Buffer` containing a serialized HTTP/2 frame header. - - - - + ```js // length is a 24-bit unsigned integer // type is an 8-bit unsigned integer identifying the frame type @@ -544,10 +538,7 @@ The serialized `Buffer` may be retrieved using the `frame.data` property. The `http2.DataFrame` is a subclass of `http2.Frame` that serializes a `DATA` frame. - - - - + ```js // id is the 32-bit stream identifier // payload is a Buffer containing the DATA payload @@ -564,10 +555,7 @@ socket.write(frame.data); The `http2.HeadersFrame` is a subclass of `http2.Frame` that serializes a `HEADERS` frame. - - - - + ```js // id is the 32-bit stream identifier // payload is a Buffer containing the HEADERS payload (see either @@ -585,10 +573,7 @@ socket.write(frame.data); The `http2.SettingsFrame` is a subclass of `http2.Frame` that serializes an empty `SETTINGS` frame. - - - - + ```js // ack is a boolean indicating whether or not to set the ACK flag. const frame = new http2.SettingsFrame(ack); @@ -601,10 +586,7 @@ socket.write(frame.data); Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 request headers to be used as the payload of a `http2.HeadersFrame`. - - - - + ```js const frame = new http2.HeadersFrame(1, http2.kFakeRequestHeaders, 0, true); @@ -616,10 +598,7 @@ socket.write(frame.data); Set to a `Buffer` instance that contains a minimal set of serialized HTTP/2 response headers to be used as the payload a `http2.HeadersFrame`. - - - - + ```js const frame = new http2.HeadersFrame(1, http2.kFakeResponseHeaders, 0, true); @@ -631,10 +610,7 @@ socket.write(frame.data); Set to a `Buffer` containing the preamble bytes an HTTP/2 client must send upon initial establishment of a connection. - - - - + ```js socket.write(http2.kClientMagic); ``` From 3cd205431bf49f70ec564b1e437d060f37aade0a Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sat, 6 Jan 2018 11:50:58 -0800 Subject: [PATCH 179/218] http2: use aliased buffer for perf stats, add stats Add an aliased buffer for session and stream statistics, add a few more metrics Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18020 Reviewed-By: Anna Henningsen Reviewed-By: Tiancheng "Timothy" Gu --- doc/api/http2.md | 20 ++++- lib/perf_hooks.js | 68 ++++++++++++++ src/node_http2.cc | 120 ++++++++++++++----------- src/node_http2.h | 50 +++++++++-- src/node_http2_state.h | 37 ++++++++ test/parallel/test-http2-perf_hooks.js | 11 ++- 6 files changed, 240 insertions(+), 66 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 8b3e33723b4c7d..2706f4cff3f5e2 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -3022,23 +3022,35 @@ The `name` property of the `PerformanceEntry` will be equal to either If `name` is equal to `Http2Stream`, the `PerformanceEntry` will contain the following additional properties: +* `bytesRead` {number} The number of DATA frame bytes received for this + `Http2Stream`. +* `bytesWritten` {number} The number of DATA frame bytes sent for this + `Http2Stream`. +* `id` {number} The identifier of the associated `Http2Stream` * `timeToFirstByte` {number} The number of milliseconds elapsed between the `PerformanceEntry` `startTime` and the reception of the first `DATA` frame. +* `timeToFirstByteSent` {number} The number of milliseconds elapsed between + the `PerformanceEntry` `startTime` and sending of the first `DATA` frame. * `timeToFirstHeader` {number} The number of milliseconds elapsed between the `PerformanceEntry` `startTime` and the reception of the first header. If `name` is equal to `Http2Session`, the `PerformanceEntry` will contain the following additional properties: +* `bytesRead` {number} The number of bytes received for this `Http2Session`. +* `bytesWritten` {number} The number of bytes sent for this `Http2Session`. +* `framesReceived` {number} The number of HTTP/2 frames received by the + `Http2Session`. +* `framesSent` {number} The number of HTTP/2 frames sent by the `Http2Session`. +* `maxConcurrentStreams` {number} The maximum number of streams concurrently + open during the lifetime of the `Http2Session`. * `pingRTT` {number} The number of milliseconds elapsed since the transmission of a `PING` frame and the reception of its acknowledgment. Only present if a `PING` frame has been sent on the `Http2Session`. -* `streamCount` {number} The number of `Http2Stream` instances processed by - the `Http2Session`. * `streamAverageDuration` {number} The average duration (in milliseconds) for all `Http2Stream` instances. -* `framesReceived` {number} The number of HTTP/2 frames received by the - `Http2Session`. +* `streamCount` {number} The number of `Http2Stream` instances processed by + the `Http2Session`. * `type` {string} Either `'server'` or `'client'` to identify the type of `Http2Session`. diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js index 7e1d085b525b44..4a05f7ccba7bc8 100644 --- a/lib/perf_hooks.js +++ b/lib/perf_hooks.js @@ -66,6 +66,70 @@ const observerableTypes = [ 'http2' ]; +const IDX_STREAM_STATS_ID = 0; +const IDX_STREAM_STATS_TIMETOFIRSTBYTE = 1; +const IDX_STREAM_STATS_TIMETOFIRSTHEADER = 2; +const IDX_STREAM_STATS_TIMETOFIRSTBYTESENT = 3; +const IDX_STREAM_STATS_SENTBYTES = 4; +const IDX_STREAM_STATS_RECEIVEDBYTES = 5; + +const IDX_SESSION_STATS_TYPE = 0; +const IDX_SESSION_STATS_PINGRTT = 1; +const IDX_SESSION_STATS_FRAMESRECEIVED = 2; +const IDX_SESSION_STATS_FRAMESSENT = 3; +const IDX_SESSION_STATS_STREAMCOUNT = 4; +const IDX_SESSION_STATS_STREAMAVERAGEDURATION = 5; +const IDX_SESSION_STATS_DATA_SENT = 6; +const IDX_SESSION_STATS_DATA_RECEIVED = 7; +const IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS = 8; + +let sessionStats; +let streamStats; + +function collectHttp2Stats(entry) { + switch (entry.name) { + case 'Http2Stream': + if (streamStats === undefined) + streamStats = process.binding('http2').streamStats; + entry.id = + streamStats[IDX_STREAM_STATS_ID] >>> 0; + entry.timeToFirstByte = + streamStats[IDX_STREAM_STATS_TIMETOFIRSTBYTE]; + entry.timeToFirstHeader = + streamStats[IDX_STREAM_STATS_TIMETOFIRSTHEADER]; + entry.timeToFirstByteSent = + streamStats[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT]; + entry.bytesWritten = + streamStats[IDX_STREAM_STATS_SENTBYTES]; + entry.bytesRead = + streamStats[IDX_STREAM_STATS_RECEIVEDBYTES]; + break; + case 'Http2Session': + if (sessionStats === undefined) + sessionStats = process.binding('http2').sessionStats; + entry.type = + sessionStats[IDX_SESSION_STATS_TYPE] >>> 0 === 0 ? 'server' : 'client'; + entry.pingRTT = + sessionStats[IDX_SESSION_STATS_PINGRTT]; + entry.framesReceived = + sessionStats[IDX_SESSION_STATS_FRAMESRECEIVED]; + entry.framesSent = + sessionStats[IDX_SESSION_STATS_FRAMESSENT]; + entry.streamCount = + sessionStats[IDX_SESSION_STATS_STREAMCOUNT]; + entry.streamAverageDuration = + sessionStats[IDX_SESSION_STATS_STREAMAVERAGEDURATION]; + entry.bytesWritten = + sessionStats[IDX_SESSION_STATS_DATA_SENT]; + entry.bytesRead = + sessionStats[IDX_SESSION_STATS_DATA_RECEIVED]; + entry.maxConcurrentStreams = + sessionStats[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS]; + break; + } +} + + let errors; function lazyErrors() { if (errors === undefined) @@ -467,6 +531,10 @@ function doNotify() { // Set up the callback used to receive PerformanceObserver notifications function observersCallback(entry) { const type = mapTypes(entry.entryType); + + if (type === NODE_PERFORMANCE_ENTRY_TYPE_HTTP2) + collectHttp2Stats(entry); + performance[kInsertEntry](entry); const list = getObserversList(type); diff --git a/src/node_http2.cc b/src/node_http2.cc index 844421fa35c1c3..2bcf7ccfa0554e 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -471,6 +471,8 @@ Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) { callbacks, OnSendData); nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( callbacks, OnInvalidFrame); + nghttp2_session_callbacks_set_on_frame_send_callback( + callbacks, OnFrameSent); if (kHasGetPaddingCallback) { nghttp2_session_callbacks_set_select_padding_callback( @@ -560,28 +562,35 @@ inline void Http2Stream::EmitStatistics() { if (!HasHttp2Observer(env())) return; Http2StreamPerformanceEntry* entry = - new Http2StreamPerformanceEntry(env(), statistics_); + new Http2StreamPerformanceEntry(env(), id_, statistics_); env()->SetImmediate([](Environment* env, void* data) { - Local context = env->context(); Http2StreamPerformanceEntry* entry = static_cast(data); if (HasHttp2Observer(env)) { - Local obj = entry->ToObject(); - v8::PropertyAttribute attr = - static_cast(v8::ReadOnly | v8::DontDelete); - obj->DefineOwnProperty( - context, - FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstByte"), - Number::New(env->isolate(), - (entry->first_byte() - entry->startTimeNano()) / 1e6), - attr).FromJust(); - obj->DefineOwnProperty( - context, - FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstHeader"), - Number::New(env->isolate(), - (entry->first_header() - entry->startTimeNano()) / 1e6), - attr).FromJust(); - entry->Notify(obj); + AliasedBuffer& buffer = + env->http2_state()->stream_stats_buffer; + buffer[IDX_STREAM_STATS_ID] = entry->id(); + if (entry->first_byte() != 0) { + buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] = + (entry->first_byte() - entry->startTimeNano()) / 1e6; + } else { + buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] = 0; + } + if (entry->first_header() != 0) { + buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] = + (entry->first_header() - entry->startTimeNano()) / 1e6; + } else { + buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] = 0; + } + if (entry->first_byte_sent() != 0) { + buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] = + (entry->first_byte_sent() - entry->startTimeNano()) / 1e6; + } else { + buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] = 0; + } + buffer[IDX_STREAM_STATS_SENTBYTES] = entry->sent_bytes(); + buffer[IDX_STREAM_STATS_RECEIVEDBYTES] = entry->received_bytes(); + entry->Notify(entry->ToObject()); } delete entry; }, static_cast(entry)); @@ -591,45 +600,25 @@ inline void Http2Session::EmitStatistics() { if (!HasHttp2Observer(env())) return; Http2SessionPerformanceEntry* entry = - new Http2SessionPerformanceEntry(env(), statistics_, TypeName()); + new Http2SessionPerformanceEntry(env(), statistics_, session_type_); env()->SetImmediate([](Environment* env, void* data) { - Local context = env->context(); Http2SessionPerformanceEntry* entry = static_cast(data); if (HasHttp2Observer(env)) { - Local obj = entry->ToObject(); - v8::PropertyAttribute attr = - static_cast(v8::ReadOnly | v8::DontDelete); - obj->DefineOwnProperty( - context, - FIXED_ONE_BYTE_STRING(env->isolate(), "type"), - String::NewFromUtf8(env->isolate(), - entry->typeName(), - v8::NewStringType::kInternalized) - .ToLocalChecked(), attr).FromJust(); - if (entry->ping_rtt() != 0) { - obj->DefineOwnProperty( - context, - FIXED_ONE_BYTE_STRING(env->isolate(), "pingRTT"), - Number::New(env->isolate(), entry->ping_rtt() / 1e6), - attr).FromJust(); - } - obj->DefineOwnProperty( - context, - FIXED_ONE_BYTE_STRING(env->isolate(), "framesReceived"), - Integer::NewFromUnsigned(env->isolate(), entry->frame_count()), - attr).FromJust(); - obj->DefineOwnProperty( - context, - FIXED_ONE_BYTE_STRING(env->isolate(), "streamCount"), - Integer::New(env->isolate(), entry->stream_count()), - attr).FromJust(); - obj->DefineOwnProperty( - context, - FIXED_ONE_BYTE_STRING(env->isolate(), "streamAverageDuration"), - Number::New(env->isolate(), entry->stream_average_duration()), - attr).FromJust(); - entry->Notify(obj); + AliasedBuffer& buffer = + env->http2_state()->session_stats_buffer; + buffer[IDX_SESSION_STATS_TYPE] = entry->type(); + buffer[IDX_SESSION_STATS_PINGRTT] = entry->ping_rtt() / 1e6; + buffer[IDX_SESSION_STATS_FRAMESRECEIVED] = entry->frame_count(); + buffer[IDX_SESSION_STATS_FRAMESSENT] = entry->frame_sent(); + buffer[IDX_SESSION_STATS_STREAMCOUNT] = entry->stream_count(); + buffer[IDX_SESSION_STATS_STREAMAVERAGEDURATION] = + entry->stream_average_duration(); + buffer[IDX_SESSION_STATS_DATA_SENT] = entry->data_sent(); + buffer[IDX_SESSION_STATS_DATA_RECEIVED] = entry->data_received(); + buffer[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS] = + entry->max_concurrent_streams(); + entry->Notify(entry->ToObject()); } delete entry; }, static_cast(entry)); @@ -695,6 +684,9 @@ inline bool Http2Session::CanAddStream() { inline void Http2Session::AddStream(Http2Stream* stream) { CHECK_GE(++statistics_.stream_count, 0); streams_[stream->id()] = stream; + size_t size = streams_.size(); + if (size > statistics_.max_concurrent_streams) + statistics_.max_concurrent_streams = size; IncrementCurrentSessionMemory(stream->self_size()); } @@ -963,6 +955,14 @@ inline int Http2Session::OnFrameNotSent(nghttp2_session* handle, return 0; } +inline int Http2Session::OnFrameSent(nghttp2_session* handle, + const nghttp2_frame* frame, + void* user_data) { + Http2Session* session = static_cast(user_data); + session->statistics_.frame_sent += 1; + return 0; +} + // Called by nghttp2 when a stream closes. inline int Http2Session::OnStreamClose(nghttp2_session* handle, int32_t id, @@ -1040,6 +1040,7 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle, // If the stream has been destroyed, ignore this chunk if (stream->IsDestroyed()) return 0; + stream->statistics_.received_bytes += len; stream->AddChunk(data, len); } return 0; @@ -1494,6 +1495,7 @@ void Http2Session::SendPendingData() { size_t offset = 0; size_t i = 0; for (const nghttp2_stream_write& write : outgoing_buffers_) { + statistics_.data_sent += write.buf.len; if (write.buf.base == nullptr) { bufs[i++] = uv_buf_init( reinterpret_cast(outgoing_storage_.data() + offset), @@ -1643,6 +1645,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread, if (bufs->len > 0) { // Only pass data on if nread > 0 uv_buf_t buf[] { uv_buf_init((*bufs).base, nread) }; + session->statistics_.data_received += nread; ssize_t ret = session->Write(buf, 1); // Note: if ssize_t is not defined (e.g. on Win32), nghttp2 will typedef @@ -2142,6 +2145,8 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle, void* user_data) { Http2Session* session = static_cast(user_data); Http2Stream* stream = session->FindStream(id); + if (stream->statistics_.first_byte_sent == 0) + stream->statistics_.first_byte_sent = uv_hrtime(); DEBUG_HTTP2SESSION2(session, "reading outbound file data for stream %d", id); CHECK_EQ(id, stream->id()); @@ -2192,6 +2197,7 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle, return NGHTTP2_ERR_CALLBACK_FAILURE; } + stream->statistics_.sent_bytes += numchars; return numchars; } @@ -2217,6 +2223,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle, Http2Session* session = static_cast(user_data); DEBUG_HTTP2SESSION2(session, "reading outbound data for stream %d", id); Http2Stream* stream = GetStream(session, id, source); + if (stream->statistics_.first_byte_sent == 0) + stream->statistics_.first_byte_sent = uv_hrtime(); CHECK_EQ(id, stream->id()); size_t amount = 0; // amount of data being sent in this data frame. @@ -2250,6 +2258,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle, if (session->IsDestroyed()) return NGHTTP2_ERR_CALLBACK_FAILURE; } + + stream->statistics_.sent_bytes += amount; return amount; } @@ -2863,6 +2873,10 @@ void Initialize(Local target, "settingsBuffer", state->settings_buffer.GetJSArray()); SET_STATE_TYPEDARRAY( "optionsBuffer", state->options_buffer.GetJSArray()); + SET_STATE_TYPEDARRAY( + "streamStats", state->stream_stats_buffer.GetJSArray()); + SET_STATE_TYPEDARRAY( + "sessionStats", state->session_stats_buffer.GetJSArray()); #undef SET_STATE_TYPEDARRAY env->set_http2_state(std::move(state)); diff --git a/src/node_http2.h b/src/node_http2.h index 5b5f7e5f52e084..a7938a5f800854 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -715,8 +715,11 @@ class Http2Stream : public AsyncWrap, struct Statistics { uint64_t start_time; uint64_t end_time; - uint64_t first_header; // Time first header was received - uint64_t first_byte; // Time first data frame byte was received + uint64_t first_header; // Time first header was received + uint64_t first_byte; // Time first DATA frame byte was received + uint64_t first_byte_sent; // Time first DATA frame byte was sent + uint64_t sent_bytes; + uint64_t received_bytes; }; Statistics statistics_ = {}; @@ -949,8 +952,12 @@ class Http2Session : public AsyncWrap { uint64_t start_time; uint64_t end_time; uint64_t ping_rtt; + uint64_t data_sent; + uint64_t data_received; uint32_t frame_count; + uint32_t frame_sent; int32_t stream_count; + size_t max_concurrent_streams; double stream_average_duration; }; @@ -995,6 +1002,10 @@ class Http2Session : public AsyncWrap { const nghttp2_frame* frame, int error_code, void* user_data); + static inline int OnFrameSent( + nghttp2_session* session, + const nghttp2_frame* frame, + void* user_data); static inline int OnStreamClose( nghttp2_session* session, int32_t id, @@ -1115,21 +1126,29 @@ class Http2SessionPerformanceEntry : public PerformanceEntry { Http2SessionPerformanceEntry( Environment* env, const Http2Session::Statistics& stats, - const char* kind) : + nghttp2_session_type type) : PerformanceEntry(env, "Http2Session", "http2", stats.start_time, stats.end_time), ping_rtt_(stats.ping_rtt), + data_sent_(stats.data_sent), + data_received_(stats.data_received), frame_count_(stats.frame_count), + frame_sent_(stats.frame_sent), stream_count_(stats.stream_count), + max_concurrent_streams_(stats.max_concurrent_streams), stream_average_duration_(stats.stream_average_duration), - kind_(kind) { } + session_type_(type) { } uint64_t ping_rtt() const { return ping_rtt_; } + uint64_t data_sent() const { return data_sent_; } + uint64_t data_received() const { return data_received_; } uint32_t frame_count() const { return frame_count_; } + uint32_t frame_sent() const { return frame_sent_; } int32_t stream_count() const { return stream_count_; } + size_t max_concurrent_streams() const { return max_concurrent_streams_; } double stream_average_duration() const { return stream_average_duration_; } - const char* typeName() const { return kind_; } + nghttp2_session_type type() const { return session_type_; } void Notify(Local obj) { PerformanceEntry::Notify(env(), kind(), obj); @@ -1137,33 +1156,50 @@ class Http2SessionPerformanceEntry : public PerformanceEntry { private: uint64_t ping_rtt_; + uint64_t data_sent_; + uint64_t data_received_; uint32_t frame_count_; + uint32_t frame_sent_; int32_t stream_count_; + size_t max_concurrent_streams_; double stream_average_duration_; - const char* kind_; + nghttp2_session_type session_type_; }; class Http2StreamPerformanceEntry : public PerformanceEntry { public: Http2StreamPerformanceEntry( Environment* env, + int32_t id, const Http2Stream::Statistics& stats) : PerformanceEntry(env, "Http2Stream", "http2", stats.start_time, stats.end_time), + id_(id), first_header_(stats.first_header), - first_byte_(stats.first_byte) { } + first_byte_(stats.first_byte), + first_byte_sent_(stats.first_byte_sent), + sent_bytes_(stats.sent_bytes), + received_bytes_(stats.received_bytes) { } + int32_t id() const { return id_; } uint64_t first_header() const { return first_header_; } uint64_t first_byte() const { return first_byte_; } + uint64_t first_byte_sent() const { return first_byte_sent_; } + uint64_t sent_bytes() const { return sent_bytes_; } + uint64_t received_bytes() const { return received_bytes_; } void Notify(Local obj) { PerformanceEntry::Notify(env(), kind(), obj); } private: + int32_t id_; uint64_t first_header_; uint64_t first_byte_; + uint64_t first_byte_sent_; + uint64_t sent_bytes_; + uint64_t received_bytes_; }; class Http2Session::Http2Ping : public AsyncWrap { diff --git a/src/node_http2_state.h b/src/node_http2_state.h index af0740c994e765..ed88f068a04b16 100644 --- a/src/node_http2_state.h +++ b/src/node_http2_state.h @@ -61,6 +61,29 @@ namespace http2 { PADDING_BUF_FIELD_COUNT }; + enum Http2StreamStatisticsIndex { + IDX_STREAM_STATS_ID, + IDX_STREAM_STATS_TIMETOFIRSTBYTE, + IDX_STREAM_STATS_TIMETOFIRSTHEADER, + IDX_STREAM_STATS_TIMETOFIRSTBYTESENT, + IDX_STREAM_STATS_SENTBYTES, + IDX_STREAM_STATS_RECEIVEDBYTES, + IDX_STREAM_STATS_COUNT + }; + + enum Http2SessionStatisticsIndex { + IDX_SESSION_STATS_TYPE, + IDX_SESSION_STATS_PINGRTT, + IDX_SESSION_STATS_FRAMESRECEIVED, + IDX_SESSION_STATS_FRAMESSENT, + IDX_SESSION_STATS_STREAMCOUNT, + IDX_SESSION_STATS_STREAMAVERAGEDURATION, + IDX_SESSION_STATS_DATA_SENT, + IDX_SESSION_STATS_DATA_RECEIVED, + IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS, + IDX_SESSION_STATS_COUNT + }; + class http2_state { public: explicit http2_state(v8::Isolate* isolate) : @@ -77,6 +100,16 @@ class http2_state { offsetof(http2_state_internal, stream_state_buffer), IDX_STREAM_STATE_COUNT, root_buffer), + stream_stats_buffer( + isolate, + offsetof(http2_state_internal, stream_stats_buffer), + IDX_STREAM_STATS_COUNT, + root_buffer), + session_stats_buffer( + isolate, + offsetof(http2_state_internal, session_stats_buffer), + IDX_SESSION_STATS_COUNT, + root_buffer), padding_buffer( isolate, offsetof(http2_state_internal, padding_buffer), @@ -97,6 +130,8 @@ class http2_state { AliasedBuffer root_buffer; AliasedBuffer session_state_buffer; AliasedBuffer stream_state_buffer; + AliasedBuffer stream_stats_buffer; + AliasedBuffer session_stats_buffer; AliasedBuffer padding_buffer; AliasedBuffer options_buffer; AliasedBuffer settings_buffer; @@ -106,6 +141,8 @@ class http2_state { // doubles first so that they are always sizeof(double)-aligned double session_state_buffer[IDX_SESSION_STATE_COUNT]; double stream_state_buffer[IDX_STREAM_STATE_COUNT]; + double stream_stats_buffer[IDX_STREAM_STATS_COUNT]; + double session_stats_buffer[IDX_SESSION_STATS_COUNT]; uint32_t padding_buffer[PADDING_BUF_FIELD_COUNT]; uint32_t options_buffer[IDX_OPTIONS_FLAGS + 1]; uint32_t settings_buffer[IDX_SETTINGS_COUNT + 1]; diff --git a/test/parallel/test-http2-perf_hooks.js b/test/parallel/test-http2-perf_hooks.js index f2ef29cec25e06..07d9c55ed7e0d2 100644 --- a/test/parallel/test-http2-perf_hooks.js +++ b/test/parallel/test-http2-perf_hooks.js @@ -8,7 +8,7 @@ const h2 = require('http2'); const { PerformanceObserver } = require('perf_hooks'); -const obs = new PerformanceObserver((items) => { +const obs = new PerformanceObserver(common.mustCall((items) => { const entry = items.getEntries()[0]; assert.strictEqual(entry.entryType, 'http2'); assert.strictEqual(typeof entry.startTime, 'number'); @@ -19,6 +19,10 @@ const obs = new PerformanceObserver((items) => { assert.strictEqual(typeof entry.streamAverageDuration, 'number'); assert.strictEqual(typeof entry.streamCount, 'number'); assert.strictEqual(typeof entry.framesReceived, 'number'); + assert.strictEqual(typeof entry.framesSent, 'number'); + assert.strictEqual(typeof entry.bytesWritten, 'number'); + assert.strictEqual(typeof entry.bytesRead, 'number'); + assert.strictEqual(typeof entry.maxConcurrentStreams, 'number'); switch (entry.type) { case 'server': assert.strictEqual(entry.streamCount, 1); @@ -34,12 +38,15 @@ const obs = new PerformanceObserver((items) => { break; case 'Http2Stream': assert.strictEqual(typeof entry.timeToFirstByte, 'number'); + assert.strictEqual(typeof entry.timeToFirstByteSent, 'number'); assert.strictEqual(typeof entry.timeToFirstHeader, 'number'); + assert.strictEqual(typeof entry.bytesWritten, 'number'); + assert.strictEqual(typeof entry.bytesRead, 'number'); break; default: assert.fail('invalid entry name'); } -}); +}, 4)); obs.observe({ entryTypes: ['http2'] }); const body = From d9d0d0e98ebcf68252aeebdadeda801dafb509b1 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 10 Jan 2018 09:48:21 -0800 Subject: [PATCH 180/218] doc: update pushStream docs to use err first Refs: https://github.com/nodejs/node/pull/17406#issuecomment-356661798 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18088 Reviewed-By: Anna Henningsen Reviewed-By: Matteo Collina Reviewed-By: Luigi Pinca Reviewed-By: Daniel Bevenius --- doc/api/http2.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 2706f4cff3f5e2..e1ac74400b49f7 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1174,14 +1174,16 @@ added: v8.4.0 * Returns: {undefined} Initiates a push stream. The callback is invoked with the new `Http2Stream` -instance created for the push stream. +instance created for the push stream passed as the second argument, or an +`Error` passed as the first argument. ```js const http2 = require('http2'); const server = http2.createServer(); server.on('stream', (stream) => { stream.respond({ ':status': 200 }); - stream.pushStream({ ':path': '/' }, (pushStream) => { + stream.pushStream({ ':path': '/' }, (err, pushStream) => { + if (err) throw err; pushStream.respond({ ':status': 200 }); pushStream.end('some pushed data'); }); From 88babd5a233ad10f353106cf28e0b1c8a4014add Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 11 Jan 2018 10:05:15 -0800 Subject: [PATCH 181/218] doc: fix s/rstStream/close in example Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18088 Reviewed-By: Anna Henningsen Reviewed-By: Matteo Collina Reviewed-By: Luigi Pinca Reviewed-By: Daniel Bevenius --- doc/api/http2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index e1ac74400b49f7..e92baddf3cc695 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1015,7 +1015,7 @@ const { NGHTTP2_CANCEL } = http2.constants; const req = client.request({ ':path': '/' }); // Cancel the stream if there's no activity after 5 seconds -req.setTimeout(5000, () => req.rstStream(NGHTTP2_CANCEL)); +req.setTimeout(5000, () => req.close(NGHTTP2_CANCEL)); ``` #### http2stream.state From 6bdfb1f8f01ed7832039c6c10668dd1209d32cc5 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 8 Jan 2018 13:32:30 -0800 Subject: [PATCH 182/218] perf_hooks,http2: add performance.clear() Add missing clear() method to `perf_hooks.performance` to remove the entries from the master timeline to prevent that from being a memory leak. Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18046 Reviewed-By: Matteo Collina --- doc/api/perf_hooks.md | 8 ++++++++ lib/perf_hooks.js | 4 ++++ test/parallel/test-http2-perf_hooks.js | 10 +++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 9b4ffb7ccb63c6..2f2910af93db34 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -29,6 +29,14 @@ added: v8.5.0 The `Performance` provides access to performance metric data. A single instance of this class is provided via the `performance` property. +### performance.clearEntries(name) + + +Remove all performance entry objects with `entryType` equal to `name` from the +Performance Timeline. + ### performance.clearFunctions([name]) + +* Value: {[Headers Object][]} + +An object containing the outbound headers sent for this `Http2Stream`. + +#### http2stream.sentInfoHeaders + + +* Value: {[Headers Object][]\[\]} + +An array of objects containing the outbound informational (additional) headers +sent for this `Http2Stream`. + +#### http2stream.sentTrailers + + +* Value: {[Headers Object][]} + +An object containing the outbound trailers sent for this this `HttpStream`. + #### http2stream.session * code {number} Unsigned 32-bit integer identifying the error code. **Default:** - `http2.constant.NGHTTP2_NO_ERROR` (`0x00`) + `http2.constants.NGHTTP2_NO_ERROR` (`0x00`) * `callback` {Function} An optional function registered to listen for the `'close'` event. * Returns: {undefined} From 4e807d648e9793e7924c1e2706be55c9f7b610dd Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 8 Jan 2018 01:52:17 +0100 Subject: [PATCH 187/218] src: introduce internal buffer slice constructor Add a C++ variant of `Buffer.from(arrayBuffer, offset, length)`. Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18030 Reviewed-By: James M Snell --- src/node_buffer.cc | 48 ++++++++++++++++++-------------------------- src/node_internals.h | 13 ++++++++++++ 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 66ae9feb697a32..0c68e04e5a7b18 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -303,15 +303,14 @@ MaybeLocal New(Environment* env, size_t length) { data, length, ArrayBufferCreationMode::kInternalized); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - return scope.Escape(ui); + MaybeLocal ui = Buffer::New(env, ab, 0, length); - // Object failed to be created. Clean up resources. - free(data); - return Local(); + if (ui.IsEmpty()) { + // Object failed to be created. Clean up resources. + free(data); + } + + return scope.Escape(ui.FromMaybe(Local())); } @@ -349,15 +348,14 @@ MaybeLocal Copy(Environment* env, const char* data, size_t length) { new_data, length, ArrayBufferCreationMode::kInternalized); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - return scope.Escape(ui); + MaybeLocal ui = Buffer::New(env, ab, 0, length); - // Object failed to be created. Clean up resources. - free(new_data); - return Local(); + if (ui.IsEmpty()) { + // Object failed to be created. Clean up resources. + free(new_data); + } + + return scope.Escape(ui.FromMaybe(Local())); } @@ -392,15 +390,14 @@ MaybeLocal New(Environment* env, // correct. if (data == nullptr) ab->Neuter(); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); + MaybeLocal ui = Buffer::New(env, ab, 0, length); - if (!mb.FromMaybe(false)) + if (ui.IsEmpty()) { return Local(); + } CallbackInfo::New(env->isolate(), ab, callback, data, hint); - return scope.Escape(ui); + return scope.Escape(ui.ToLocalChecked()); } @@ -415,8 +412,6 @@ MaybeLocal New(Isolate* isolate, char* data, size_t length) { MaybeLocal New(Environment* env, char* data, size_t length) { - EscapableHandleScope scope(env->isolate()); - if (length > 0) { CHECK_NE(data, nullptr); CHECK(length <= kMaxLength); @@ -427,12 +422,7 @@ MaybeLocal New(Environment* env, char* data, size_t length) { data, length, ArrayBufferCreationMode::kInternalized); - Local ui = Uint8Array::New(ab, 0, length); - Maybe mb = - ui->SetPrototype(env->context(), env->buffer_prototype_object()); - if (mb.FromMaybe(false)) - return scope.Escape(ui); - return Local(); + return Buffer::New(env, ab, 0, length).FromMaybe(Local()); } namespace { diff --git a/src/node_internals.h b/src/node_internals.h index 7f73f4b8deaa9a..b2cef8c9abcad5 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -330,6 +330,19 @@ v8::MaybeLocal New(Environment* env, // Mixing operator new and free() is undefined behavior so don't do that. v8::MaybeLocal New(Environment* env, char* data, size_t length); +inline +v8::MaybeLocal New(Environment* env, + v8::Local ab, + size_t byte_offset, + size_t length) { + v8::Local ui = v8::Uint8Array::New(ab, byte_offset, length); + v8::Maybe mb = + ui->SetPrototype(env->context(), env->buffer_prototype_object()); + if (mb.IsNothing()) + return v8::MaybeLocal(); + return ui; +} + // Construct a Buffer from a MaybeStackBuffer (and also its subclasses like // Utf8Value and TwoByteValue). // If |buf| is invalidated, an empty MaybeLocal is returned, and nothing is From 8b0a1b32de3e85c476edd3029a318703777c8519 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 8 Jan 2018 03:50:51 +0100 Subject: [PATCH 188/218] http2: refactor read mechanism Refactor the read mechanism to completely avoid copying. Instead of copying individual `DATA` frame contents into buffers, create `ArrayBuffer` instances for all socket reads and emit slices of those `ArrayBuffer`s to JS. Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18030 Reviewed-By: James M Snell --- lib/internal/http2/core.js | 30 ++- src/node_http2.cc | 184 ++++++++---------- src/node_http2.h | 18 +- test/common/README.md | 11 ++ test/common/index.js | 6 + test/parallel/test-http2-backpressure.js | 49 +++++ ...t-http2-misbehaving-flow-control-paused.js | 3 + 7 files changed, 174 insertions(+), 127 deletions(-) create mode 100644 test/parallel/test-http2-backpressure.js diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 3e68ed4a59f750..bc1067e9963ba1 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -277,7 +277,7 @@ function submitRstStream(code) { // point, close them. If there is an open fd for file send, close that also. // At this point the underlying node::http2:Http2Stream handle is no // longer usable so destroy it also. -function onStreamClose(code, hasData) { +function onStreamClose(code) { const stream = this[kOwner]; if (stream.destroyed) return; @@ -285,8 +285,7 @@ function onStreamClose(code, hasData) { const state = stream[kState]; debug(`Http2Stream ${stream[kID]} [Http2Session ` + - `${sessionName(stream[kSession][kType])}]: closed with code ${code}` + - ` [has data? ${hasData}]`); + `${sessionName(stream[kSession][kType])}]: closed with code ${code}`); if (!stream.closed) { // Unenroll from timeouts @@ -304,13 +303,14 @@ function onStreamClose(code, hasData) { if (state.fd !== undefined) tryClose(state.fd); - stream[kMaybeDestroy](null, code, hasData); + stream.push(null); + stream[kMaybeDestroy](null, code); } // Receives a chunk of data for a given stream and forwards it on // to the Http2Stream Duplex for processing. -function onStreamRead(nread, buf, handle) { - const stream = handle[kOwner]; +function onStreamRead(nread, buf) { + const stream = this[kOwner]; if (nread >= 0 && !stream.destroyed) { debug(`Http2Stream ${stream[kID]} [Http2Session ` + `${sessionName(stream[kSession][kType])}]: receiving data chunk ` + @@ -318,7 +318,7 @@ function onStreamRead(nread, buf, handle) { stream[kUpdateTimer](); if (!stream.push(buf)) { if (!stream.destroyed) // we have to check a second time - handle.readStop(); + this.readStop(); } return; } @@ -1427,13 +1427,8 @@ function streamOnResume() { } function streamOnPause() { - // if (!this.destroyed && !this.pending) - // this[kHandle].readStop(); -} - -function handleFlushData(self) { if (!this.destroyed && !this.pending) - this[kHandle].flushData(); + this[kHandle].readStop(); } // If the writable side of the Http2Stream is still open, emit the @@ -1679,11 +1674,10 @@ class Http2Stream extends Duplex { this.push(null); return; } - const flushfn = handleFlushData.bind(this); if (!this.pending) { - flushfn(); + streamOnResume.call(this); } else { - this.once('ready', flushfn); + this.once('ready', streamOnResume); } } @@ -1822,10 +1816,10 @@ class Http2Stream extends Duplex { // The Http2Stream can be destroyed if it has closed and if the readable // side has received the final chunk. - [kMaybeDestroy](error, code = NGHTTP2_NO_ERROR, hasData = true) { + [kMaybeDestroy](error, code = NGHTTP2_NO_ERROR) { if (error == null) { if (code === NGHTTP2_NO_ERROR && - ((!this._readableState.ended && hasData) || + (!this._readableState.ended || !this._writableState.ended || this._writableState.pendingcb > 0 || !this.closed)) { diff --git a/src/node_http2.cc b/src/node_http2.cc index e4864c92dee29c..bd7eeee8655e52 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -9,6 +9,7 @@ namespace node { +using v8::ArrayBuffer; using v8::Boolean; using v8::Context; using v8::Float64Array; @@ -979,7 +980,6 @@ inline int Http2Session::OnStreamClose(nghttp2_session* handle, // Intentionally ignore the callback if the stream does not exist or has // already been destroyed if (stream != nullptr && !stream->IsDestroyed()) { - stream->AddChunk(nullptr, 0); stream->Close(code); // It is possible for the stream close to occur before the stream is // ever passed on to the javascript side. If that happens, skip straight @@ -990,9 +990,8 @@ inline int Http2Session::OnStreamClose(nghttp2_session* handle, stream->object()->Get(context, env->onstreamclose_string()) .ToLocalChecked(); if (fn->IsFunction()) { - Local argv[2] = { - Integer::NewFromUnsigned(isolate, code), - Boolean::New(isolate, stream->HasDataChunks(true)) + Local argv[] = { + Integer::NewFromUnsigned(isolate, code) }; stream->MakeCallback(fn.As(), arraysize(argv), argv); } else { @@ -1029,6 +1028,8 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle, Http2Session* session = static_cast(user_data); DEBUG_HTTP2SESSION2(session, "buffering data chunk for stream %d, size: " "%d, flags: %d", id, len, flags); + Environment* env = session->env(); + HandleScope scope(env->isolate()); // We should never actually get a 0-length chunk so this check is // only a precaution at this point. if (len > 0) { @@ -1040,8 +1041,25 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle, // If the stream has been destroyed, ignore this chunk if (stream->IsDestroyed()) return 0; + stream->statistics_.received_bytes += len; - stream->AddChunk(data, len); + + // There is a single large array buffer for the entire data read from the + // network; create a slice of that array buffer and emit it as the + // received data buffer. + CHECK(!session->stream_buf_ab_.IsEmpty()); + size_t offset = reinterpret_cast(data) - session->stream_buf_; + // Verify that the data offset is inside the current read buffer. + CHECK_LE(offset, session->stream_buf_size_); + + Local buf = + Buffer::New(env, session->stream_buf_ab_, offset, len).ToLocalChecked(); + + stream->EmitData(len, buf, Local()); + if (!stream->IsReading()) + stream->inbound_consumed_data_while_paused_ += len; + else + nghttp2_session_consume_stream(handle, id, len); } return 0; } @@ -1227,9 +1245,8 @@ inline void Http2Session::HandlePriorityFrame(const nghttp2_frame* frame) { // Called by OnFrameReceived when a complete DATA frame has been received. -// If we know that this is the last DATA frame (because the END_STREAM flag -// is set), then we'll terminate the readable side of the StreamBase. If -// the StreamBase is flowing, we'll push the chunks of data out to JS land. +// If we know that this was the last DATA frame (because the END_STREAM flag +// is set), then we'll terminate the readable side of the StreamBase. inline void Http2Session::HandleDataFrame(const nghttp2_frame* frame) { int32_t id = GetFrameID(frame); DEBUG_HTTP2SESSION2(this, "handling data frame for stream %d", id); @@ -1240,11 +1257,8 @@ inline void Http2Session::HandleDataFrame(const nghttp2_frame* frame) { return; if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - stream->AddChunk(nullptr, 0); + stream->EmitData(UV_EOF, Local(), Local()); } - - if (stream->IsReading()) - stream->FlushDataChunks(); } @@ -1619,45 +1633,67 @@ void Http2Session::OnStreamAllocImpl(size_t suggested_size, uv_buf_t* buf, void* ctx) { Http2Session* session = static_cast(ctx); - buf->base = session->stream_alloc(); - buf->len = kAllocBufferSize; + CHECK_EQ(session->stream_buf_, nullptr); + CHECK_EQ(session->stream_buf_size_, 0); + buf->base = session->stream_buf_ = Malloc(suggested_size); + buf->len = session->stream_buf_size_ = suggested_size; + session->IncrementCurrentSessionMemory(suggested_size); } // Callback used to receive inbound data from the i/o stream void Http2Session::OnStreamReadImpl(ssize_t nread, - const uv_buf_t* bufs, + const uv_buf_t* buf, uv_handle_type pending, void* ctx) { Http2Session* session = static_cast(ctx); Http2Scope h2scope(session); CHECK_NE(session->stream_, nullptr); DEBUG_HTTP2SESSION2(session, "receiving %d bytes", nread); - if (nread < 0) { - uv_buf_t tmp_buf; - tmp_buf.base = nullptr; - tmp_buf.len = 0; - session->prev_read_cb_.fn(nread, - &tmp_buf, - pending, - session->prev_read_cb_.ctx); - return; - } - if (bufs->len > 0) { + if (nread <= 0) { + free(session->stream_buf_); + if (nread < 0) { + uv_buf_t tmp_buf = uv_buf_init(nullptr, 0); + session->prev_read_cb_.fn(nread, + &tmp_buf, + pending, + session->prev_read_cb_.ctx); + } + } else { // Only pass data on if nread > 0 - uv_buf_t buf[] { uv_buf_init((*bufs).base, nread) }; + + // Verify that currently: There is memory allocated into which + // the data has been read, and that memory buffer is at least as large + // as the amount of data we have read, but we have not yet made an + // ArrayBuffer out of it. + CHECK_NE(session->stream_buf_, nullptr); + CHECK_EQ(session->stream_buf_, buf->base); + CHECK_EQ(session->stream_buf_size_, buf->len); + CHECK_GE(session->stream_buf_size_, static_cast(nread)); + CHECK(session->stream_buf_ab_.IsEmpty()); + + Environment* env = session->env(); + Isolate* isolate = env->isolate(); + HandleScope scope(isolate); + Local context = env->context(); + Context::Scope context_scope(context); + + // Create an array buffer for the read data. DATA frames will be emitted + // as slices of this array buffer to avoid having to copy memory. + session->stream_buf_ab_ = + ArrayBuffer::New(isolate, + session->stream_buf_, + session->stream_buf_size_, + v8::ArrayBufferCreationMode::kInternalized); + + uv_buf_t buf_ = uv_buf_init(buf->base, nread); session->statistics_.data_received += nread; - ssize_t ret = session->Write(buf, 1); + ssize_t ret = session->Write(&buf_, 1); // Note: if ssize_t is not defined (e.g. on Win32), nghttp2 will typedef // ssize_t to int. Cast here so that the < 0 check actually works on // Windows. if (static_cast(ret) < 0) { DEBUG_HTTP2SESSION2(session, "fatal error receiving data: %d", ret); - Environment* env = session->env(); - Isolate* isolate = env->isolate(); - HandleScope scope(isolate); - Local context = env->context(); - Context::Scope context_scope(context); Local argv[1] = { Integer::New(isolate, ret), @@ -1668,6 +1704,13 @@ void Http2Session::OnStreamReadImpl(ssize_t nread, nghttp2_session_want_read(**session)); } } + + // Since we are finished handling this write, reset the stream buffer. + // The memory has either been free()d or was handed over to V8. + session->DecrementCurrentSessionMemory(session->stream_buf_size_); + session->stream_buf_ = nullptr; + session->stream_buf_size_ = 0; + session->stream_buf_ab_ = Local(); } void Http2Session::OnStreamDestructImpl(void* ctx) { @@ -1782,30 +1825,6 @@ void Http2Stream::OnTrailers(const SubmitTrailers& submit_trailers) { } } -inline bool Http2Stream::HasDataChunks(bool ignore_eos) { - return data_chunks_.size() > (ignore_eos ? 1 : 0); -} - -// Appends a chunk of received DATA frame data to this Http2Streams internal -// queue. Note that we must memcpy each chunk because of the way that nghttp2 -// handles it's internal memory`. -inline void Http2Stream::AddChunk(const uint8_t* data, size_t len) { - CHECK(!this->IsDestroyed()); - if (this->statistics_.first_byte == 0) - this->statistics_.first_byte = uv_hrtime(); - if (flags_ & NGHTTP2_STREAM_FLAG_EOS) - return; - char* buf = nullptr; - if (len > 0 && data != nullptr) { - buf = Malloc(len); - memcpy(buf, data, len); - } else if (data == nullptr) { - flags_ |= NGHTTP2_STREAM_FLAG_EOS; - } - data_chunks_.emplace(uv_buf_init(buf, len)); -} - - inline void Http2Stream::Close(int32_t code) { CHECK(!this->IsDestroyed()); flags_ |= NGHTTP2_STREAM_FLAG_CLOSED; @@ -1842,13 +1861,6 @@ inline void Http2Stream::Destroy() { DEBUG_HTTP2STREAM(this, "destroying stream"); - // Free any remaining incoming data chunks. - while (!data_chunks_.empty()) { - uv_buf_t buf = data_chunks_.front(); - free(buf.base); - data_chunks_.pop(); - } - // Wait until the start of the next loop to delete because there // may still be some pending operations queued for this stream. env()->SetImmediate([](Environment* env, void* data) { @@ -1874,39 +1886,6 @@ inline void Http2Stream::Destroy() { } -// Uses the StreamBase API to push a single chunk of queued inbound DATA -// to JS land. -void Http2Stream::OnDataChunk(uv_buf_t* chunk) { - CHECK(!this->IsDestroyed()); - Isolate* isolate = env()->isolate(); - HandleScope scope(isolate); - ssize_t len = -1; - Local buf; - if (chunk != nullptr) { - len = chunk->len; - buf = Buffer::New(isolate, chunk->base, len).ToLocalChecked(); - } - EmitData(len, buf, this->object()); -} - - -inline void Http2Stream::FlushDataChunks() { - CHECK(!this->IsDestroyed()); - Http2Scope h2scope(this); - if (!data_chunks_.empty()) { - uv_buf_t buf = data_chunks_.front(); - data_chunks_.pop(); - if (buf.len > 0) { - CHECK_EQ(nghttp2_session_consume_stream(session_->session(), - id_, buf.len), 0); - OnDataChunk(&buf); - } else { - OnDataChunk(nullptr); - } - } -} - - // Initiates a response on the Http2Stream using data provided via the // StreamBase Streams API. inline int Http2Stream::SubmitResponse(nghttp2_nv* nva, @@ -2013,13 +1992,20 @@ inline Http2Stream* Http2Stream::SubmitPushPromise(nghttp2_nv* nva, // Switch the StreamBase into flowing mode to begin pushing chunks of data // out to JS land. inline int Http2Stream::ReadStart() { + Http2Scope h2scope(this); CHECK(!this->IsDestroyed()); flags_ |= NGHTTP2_STREAM_FLAG_READ_START; flags_ &= ~NGHTTP2_STREAM_FLAG_READ_PAUSED; - // Flush any queued data chunks immediately out to the JS layer - FlushDataChunks(); DEBUG_HTTP2STREAM(this, "reading starting"); + + // Tell nghttp2 about our consumption of the data that was handed + // off to JS land. + nghttp2_session_consume_stream(session_->session(), + id_, + inbound_consumed_data_while_paused_); + inbound_consumed_data_while_paused_ = 0; + return 0; } diff --git a/src/node_http2.h b/src/node_http2.h index 710ee91d91535f..5acd45dc51ee18 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -549,12 +549,6 @@ class Http2Stream : public AsyncWrap, inline void EmitStatistics(); - inline bool HasDataChunks(bool ignore_eos = false); - - inline void AddChunk(const uint8_t* data, size_t len); - - inline void FlushDataChunks(); - // Process a Data Chunk void OnDataChunk(uv_buf_t* chunk); @@ -740,8 +734,11 @@ class Http2Stream : public AsyncWrap, uint32_t current_headers_length_ = 0; // total number of octets std::vector current_headers_; - // Inbound Data... This is the data received via DATA frames for this stream. - std::queue data_chunks_; + // This keeps track of the amount of data read from the socket while the + // socket was in paused mode. When `ReadStart()` is called (and not before + // then), we tell nghttp2 that we consumed that data to get proper + // backpressure handling. + size_t inbound_consumed_data_while_paused_ = 0; // Outbound Data... This is the data written by the JS layer that is // waiting to be written out to the socket. @@ -1085,8 +1082,9 @@ class Http2Session : public AsyncWrap { // use this to allow timeout tracking during long-lasting writes uint32_t chunks_sent_since_last_write_ = 0; - uv_prepare_t* prep_ = nullptr; - char stream_buf_[kAllocBufferSize]; + char* stream_buf_ = nullptr; + size_t stream_buf_size_ = 0; + v8::Local stream_buf_ab_; size_t max_outstanding_pings_ = DEFAULT_MAX_PINGS; std::queue outstanding_pings_; diff --git a/test/common/README.md b/test/common/README.md index 01ed65d95bdac1..236b7e5515f042 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -262,6 +262,17 @@ fail. If `fn` is not provided, an empty function will be used. +### mustCallAsync([fn][, exact]) +* `fn` [<Function>] +* `exact` [<Number>] default = 1 +* return [<Function>] + +The same as `mustCall()`, except that it is also checked that the Promise +returned by the function is fulfilled for each invocation of the function. + +The return value of the wrapped function is the return value of the original +function, if necessary wrapped as a promise. + ### mustCallAtLeast([fn][, minimum]) * `fn` [<Function>] default = () => {} * `minimum` [<Number>] default = 1 diff --git a/test/common/index.js b/test/common/index.js index 80ba48d25a1710..cb82cd6a93ee3a 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -442,6 +442,12 @@ exports.mustCallAtLeast = function(fn, minimum) { return _mustCallInner(fn, minimum, 'minimum'); }; +exports.mustCallAsync = function(fn, exact) { + return exports.mustCall((...args) => { + return Promise.resolve(fn(...args)).then(exports.mustCall((val) => val)); + }, exact); +}; + function _mustCallInner(fn, criteria = 1, field) { if (process._exiting) throw new Error('Cannot use common.mustCall*() in process exit handler'); diff --git a/test/parallel/test-http2-backpressure.js b/test/parallel/test-http2-backpressure.js new file mode 100644 index 00000000000000..9b69dddbfd2e26 --- /dev/null +++ b/test/parallel/test-http2-backpressure.js @@ -0,0 +1,49 @@ +'use strict'; + +// Verifies that a full HTTP2 pipeline handles backpressure. + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const makeDuplexPair = require('../common/duplexpair'); + +common.crashOnUnhandledRejection(); + +{ + let req; + const server = http2.createServer(); + server.on('stream', common.mustCallAsync(async (stream, headers) => { + stream.respond({ + 'content-type': 'text/html', + ':status': 200 + }); + req._readableState.highWaterMark = 20; + stream._writableState.highWaterMark = 20; + assert.strictEqual(stream.write('A'.repeat(5)), true); + assert.strictEqual(stream.write('A'.repeat(40)), false); + assert.strictEqual(await event(req, 'data'), 'A'.repeat(5)); + assert.strictEqual(await event(req, 'data'), 'A'.repeat(40)); + await event(stream, 'drain'); + assert.strictEqual(stream.write('A'.repeat(5)), true); + assert.strictEqual(stream.write('A'.repeat(40)), false); + })); + + const { clientSide, serverSide } = makeDuplexPair(); + server.emit('connection', serverSide); + + const client = http2.connect('http://localhost:80', { + createConnection: common.mustCall(() => clientSide) + }); + + req = client.request({ ':path': '/' }); + req.setEncoding('utf8'); + req.end(); +} + +function event(ee, eventName) { + return new Promise((resolve) => { + ee.once(eventName, common.mustCall(resolve)); + }); +} diff --git a/test/parallel/test-http2-misbehaving-flow-control-paused.js b/test/parallel/test-http2-misbehaving-flow-control-paused.js index 0b7299d5ac80a8..d69e0fd802979a 100644 --- a/test/parallel/test-http2-misbehaving-flow-control-paused.js +++ b/test/parallel/test-http2-misbehaving-flow-control-paused.js @@ -56,6 +56,9 @@ let client; const server = h2.createServer({ settings: { initialWindowSize: 36 } }); server.on('stream', (stream) => { + // Set the high water mark to zero, since otherwise we still accept + // reads from the source stream (if we can consume them). + stream._readableState.highWaterMark = 0; stream.pause(); stream.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', From ac64b4f6a7b87c771c41b32c514e4b094e2ee553 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 16 Jan 2018 09:34:20 -0800 Subject: [PATCH 189/218] http2: add checks for server close callback Verify that server close callbacks are being called Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18182 Refs: https://github.com/nodejs/node/issues/18176 Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater --- test/parallel/test-http2-create-client-secure-session.js | 3 ++- test/parallel/test-http2-create-client-session.js | 4 +++- test/parallel/test-http2-createwritereq.js | 2 +- test/parallel/test-http2-misbehaving-flow-control.js | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/test/parallel/test-http2-create-client-secure-session.js b/test/parallel/test-http2-create-client-secure-session.js index 6120a58602065d..b0111e15b69c15 100644 --- a/test/parallel/test-http2-create-client-secure-session.js +++ b/test/parallel/test-http2-create-client-secure-session.js @@ -38,6 +38,7 @@ function onStream(stream, headers) { function verifySecureSession(key, cert, ca, opts) { const server = h2.createSecureServer({ cert, key }); server.on('stream', common.mustCall(onStream)); + server.on('close', common.mustCall()); server.listen(0, common.mustCall(() => { opts = opts || { }; opts.secureContext = tls.createSecureContext({ ca }); @@ -72,7 +73,7 @@ function verifySecureSession(key, cert, ca, opts) { assert.strictEqual(jsonData.servername, opts.servername || 'localhost'); assert.strictEqual(jsonData.alpnProtocol, 'h2'); - server.close(); + server.close(common.mustCall()); client[kSocket].destroy(); })); })); diff --git a/test/parallel/test-http2-create-client-session.js b/test/parallel/test-http2-create-client-session.js index b5be6bc8581452..963db2faa173b7 100644 --- a/test/parallel/test-http2-create-client-session.js +++ b/test/parallel/test-http2-create-client-session.js @@ -29,6 +29,8 @@ function onStream(stream, headers, flags) { stream.end(body.slice(20)); } +server.on('close', common.mustCall()); + server.listen(0); server.on('listening', common.mustCall(() => { @@ -46,7 +48,7 @@ server.on('listening', common.mustCall(() => { const countdown = new Countdown(count, () => { client.close(); - server.close(); + server.close(common.mustCall()); }); for (let n = 0; n < count; n++) { diff --git a/test/parallel/test-http2-createwritereq.js b/test/parallel/test-http2-createwritereq.js index 1d2b31676284d0..1575424d1609b4 100644 --- a/test/parallel/test-http2-createwritereq.js +++ b/test/parallel/test-http2-createwritereq.js @@ -60,7 +60,7 @@ server.listen(0, common.mustCall(function() { testsFinished++; if (testsFinished === testsToRun) { - server.close(); + server.close(common.mustCall()); } })); diff --git a/test/parallel/test-http2-misbehaving-flow-control.js b/test/parallel/test-http2-misbehaving-flow-control.js index 8a0b411b8de65c..161a88ea1fb407 100644 --- a/test/parallel/test-http2-misbehaving-flow-control.js +++ b/test/parallel/test-http2-misbehaving-flow-control.js @@ -72,7 +72,7 @@ server.on('stream', (stream) => { message: 'Stream closed with error code 3' })); stream.on('close', common.mustCall(() => { - server.close(); + server.close(common.mustCall()); client.destroy(); })); stream.resume(); @@ -80,6 +80,8 @@ server.on('stream', (stream) => { stream.end('ok'); }); +server.on('close', common.mustCall()); + server.listen(0, () => { client = net.connect(server.address().port, () => { client.write(preamble); From 15023c7d28d642607fca2608a6e762edcb408067 Mon Sep 17 00:00:00 2001 From: Peter Dalgaard-Jensen Date: Fri, 19 Jan 2018 21:11:09 +0100 Subject: [PATCH 190/218] doc: fix documentation of http2Stream.pushstream() Improve documentation of callback signature of http2Stream.pushStream() function to align with the changes made in https://github.com/nodejs/node/pull/17406. Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18258 Fixes: https://github.com/nodejs/node/issues/18198 Refs: https://github.com/nodejs/node/pull/17406 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/http2.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index cbdb8c2d9f258d..f7cae68ce4439b 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1199,6 +1199,10 @@ added: v8.4.0 created stream is dependent on. * `callback` {Function} Callback that is called once the push stream has been initiated. + * `err` {Error} + * `pushStream` {[`ServerHttp2Stream`][]} The returned pushStream object. + * `headers` {[Headers Object][]} Headers object the pushStream was initiated + with. * Returns: {undefined} Initiates a push stream. The callback is invoked with the new `Http2Stream` @@ -1210,7 +1214,7 @@ const http2 = require('http2'); const server = http2.createServer(); server.on('stream', (stream) => { stream.respond({ ':status': 200 }); - stream.pushStream({ ':path': '/' }, (err, pushStream) => { + stream.pushStream({ ':path': '/' }, (err, pushStream, headers) => { if (err) throw err; pushStream.respond({ ':status': 200 }); pushStream.end('some pushed data'); From 9a4a8c127e608c813ea74fca6fd34bb7a03ad535 Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Sat, 27 Jan 2018 04:35:29 +0200 Subject: [PATCH 191/218] doc: unify type linkification Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18407 Reviewed-By: Joyee Cheung Reviewed-By: Luigi Pinca Reviewed-By: Jon Moss Reviewed-By: Colin Ihrig --- doc/api/http2.md | 70 ++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index f7cae68ce4439b..7c874b5b4870f1 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -229,8 +229,8 @@ added: v8.4.0 The `'stream'` event is emitted when a new `Http2Stream` is created. When invoked, the handler function will receive a reference to the `Http2Stream` -object, a [Headers Object][], and numeric flags associated with the creation -of the stream. +object, a [HTTP2 Headers Object][], and numeric flags associated with the +creation of the stream. ```js const http2 = require('http2'); @@ -382,7 +382,7 @@ Transmits a `GOAWAY` frame to the connected peer *without* shutting down the added: v8.4.0 --> -* Value: {[Settings Object][]} +* Value: {HTTP2 Settings Object} A prototype-less object describing the current local settings of this `Http2Session`. The local settings are local to *this* `Http2Session` instance. @@ -461,7 +461,7 @@ instance's underlying [`net.Socket`]. added: v8.4.0 --> -* Value: {[Settings Object][]} +* Value: {HTTP2 Settings Object} A prototype-less object describing the current remote settings of this `Http2Session`. The remote settings are set by the *connected* HTTP/2 peer. @@ -570,8 +570,8 @@ An object describing the current status of this `Http2Session`. added: v8.4.0 --> -* `settings` {[Settings Object][]} -* Returns: {undefined} +* `settings` {HTTP2 Settings Object} +* Returns {undefined} Updates the current local settings for this `Http2Session` and sends a new `SETTINGS` frame to the connected HTTP/2 peer. @@ -707,7 +707,7 @@ client.on('altsvc', (alt, origin, stream) => { added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `endStream` {boolean} `true` if the `Http2Stream` *writable* side should be closed initially, such as when sending a `GET` request that should not @@ -895,7 +895,7 @@ added: v8.4.0 The `'trailers'` event is emitted when a block of headers associated with trailing header fields is received. The listener callback is passed the -[Headers Object][] and flags associated with the headers. +[HTTP2 Headers Object][] and flags associated with the headers. ```js stream.on('trailers', (headers, flags) => { @@ -994,7 +994,7 @@ calling `http2stream.close()`, or `http2stream.destroy()`. Will be added: REPLACEME --> -* Value: {[Headers Object][]} +* Value: {HTTP2 Headers Object} An object containing the outbound headers sent for this `Http2Stream`. @@ -1003,7 +1003,7 @@ An object containing the outbound headers sent for this `Http2Stream`. added: REPLACEME --> -* Value: {[Headers Object][]\[\]} +* Value: {HTTP2 Headers Object[]} An array of objects containing the outbound informational (additional) headers sent for this `Http2Stream`. @@ -1013,7 +1013,7 @@ sent for this `Http2Stream`. added: REPLACEME --> -* Value: {[Headers Object][]} +* Value: {HTTP2 Headers Object} An object containing the outbound trailers sent for this this `HttpStream`. @@ -1096,8 +1096,8 @@ added: v8.4.0 The `'headers'` event is emitted when an additional block of headers is received for a stream, such as when a block of `1xx` informational headers is received. -The listener callback is passed the [Headers Object][] and flags associated with -the headers. +The listener callback is passed the [HTTP2 Headers Object][] and flags +associated with the headers. ```js stream.on('headers', (headers, flags) => { @@ -1111,8 +1111,8 @@ added: v8.4.0 --> The `'push'` event is emitted when response headers for a Server Push stream -are received. The listener callback is passed the [Headers Object][] and flags -associated with the headers. +are received. The listener callback is passed the [HTTP2 Headers Object][] and +flags associated with the headers. ```js stream.on('push', (headers, flags) => { @@ -1128,7 +1128,7 @@ added: v8.4.0 The `'response'` event is emitted when a response `HEADERS` frame has been received for this stream from the connected HTTP/2 server. The listener is invoked with two arguments: an Object containing the received -[Headers Object][], and flags associated with the headers. +[HTTP2 Headers Object][], and flags associated with the headers. For example: @@ -1158,7 +1158,7 @@ provide additional methods such as `http2stream.pushStream()` and added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * Returns: {undefined} Sends an additional informational `HEADERS` frame to the connected HTTP/2 peer. @@ -1189,7 +1189,7 @@ accepts push streams, `false` otherwise. Settings are the same for every added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream, the created stream is made the sole direct dependency of the parent, with @@ -1200,9 +1200,9 @@ added: v8.4.0 * `callback` {Function} Callback that is called once the push stream has been initiated. * `err` {Error} - * `pushStream` {[`ServerHttp2Stream`][]} The returned pushStream object. - * `headers` {[Headers Object][]} Headers object the pushStream was initiated - with. + * `pushStream` {ServerHttp2Stream} The returned pushStream object. + * `headers` {HTTP2 Headers Object} Headers object the pushStream was + initiated with. * Returns: {undefined} Initiates a push stream. The callback is invoked with the new `Http2Stream` @@ -1232,7 +1232,7 @@ a `weight` value to `http2stream.priority` with the `silent` option set to added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `endStream` {boolean} Set to `true` to indicate that the response will not include payload data. @@ -1278,7 +1278,7 @@ added: v8.4.0 --> * `fd` {number} A readable file descriptor. -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `statCheck` {Function} * `getTrailers` {Function} Callback function invoked to collect trailer @@ -1362,7 +1362,7 @@ added: v8.4.0 --> * `path` {string|Buffer|URL} -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `statCheck` {Function} * `onError` {Function} Callback function invoked in the case of an @@ -1728,7 +1728,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {[Settings Object][]} The initial settings to send to the + * `settings` {HTTP2 Settings Object} The initial settings to send to the remote peer upon connection. * `onRequestHandler` {Function} See [Compatibility API][] * Returns: {Http2Server} @@ -1815,7 +1815,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {[Settings Object][]} The initial settings to send to the + * `settings` {HTTP2 Settings Object} The initial settings to send to the remote peer upon connection. * ...: Any [`tls.createServer()`][] options can be provided. For servers, the identity options (`pfx` or `key`/`cert`) are usually required. @@ -1911,7 +1911,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {[Settings Object][]} The initial settings to send to the + * `settings` {HTTP2 Settings Object} The initial settings to send to the remote peer upon connection. * `createConnection` {Function} An optional callback that receives the `URL` instance passed to `connect` and the `options` object, and returns any @@ -1964,7 +1964,7 @@ a given number of milliseconds set using `http2server.setTimeout()`. added: v8.4.0 --> -* Returns: {[Settings Object][]} +* Returns: {HTTP2 Settings Object} Returns an object containing the default settings for an `Http2Session` instance. This method returns a new object instance every time it is called @@ -1975,7 +1975,7 @@ so instances returned may be safely modified for use. added: v8.4.0 --> -* `settings` {[Settings Object][]} +* `settings` {HTTP2 Settings Object} * Returns: {Buffer} Returns a `Buffer` instance containing serialized representation of the given @@ -1997,10 +1997,10 @@ added: v8.4.0 --> * `buf` {Buffer|Uint8Array} The packed settings. -* Returns: {[Settings Object][]} +* Returns: {HTTP2 Settings Object} -Returns a [Settings Object][] containing the deserialized settings from the -given `Buffer` as generated by `http2.getPackedSettings()`. +Returns a [HTTP2 Settings Object][] containing the deserialized settings from +the given `Buffer` as generated by `http2.getPackedSettings()`. ### Headers Object @@ -2372,7 +2372,7 @@ Example: console.log(request.headers); ``` -See [Headers Object][]. +See [HTTP2 Headers Object][]. *Note*: In HTTP/2, the request path, hostname, protocol, and method are represented as special headers prefixed with the `:` character (e.g. `':path'`). @@ -3094,13 +3094,13 @@ following additional properties: [Compatibility API]: #http2_compatibility_api [HTTP/1]: http.html [HTTP/2]: https://tools.ietf.org/html/rfc7540 +[HTTP2 Headers Object]: #http2_headers_object +[HTTP2 Settings Object]: #http2_settings_object [HTTPS]: https.html -[Headers Object]: #http2_headers_object [Http2Session and Sockets]: #http2_http2session_and_sockets [Performance Observer]: perf_hooks.html [Readable Stream]: stream.html#stream_class_stream_readable [RFC 7838]: https://tools.ietf.org/html/rfc7838 -[Settings Object]: #http2_settings_object [Using options.selectPadding]: #http2_using_options_selectpadding [Writable Stream]: stream.html#stream_writable_streams [`'checkContinue'`]: #http2_event_checkcontinue From 2da965c5b8d71c23b11ec4193a97a00be1d7333d Mon Sep 17 00:00:00 2001 From: Kelvin Jin Date: Mon, 29 Jan 2018 10:58:29 -0800 Subject: [PATCH 192/218] doc: remove removed apis from http2 docs Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18439 Reviewed-By: James M Snell Reviewed-By: Anatoli Papirovski --- doc/api/http2.md | 144 +++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 85 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index 7c874b5b4870f1..8190eab7faa6d8 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -19,8 +19,8 @@ compatibility with the existing [HTTP/1][] module API. However, the [Compatibility API][] is. The `http2` Core API is much more symmetric between client and server than the -`http` API. For instance, most events, like `error` and `socketError`, can be -emitted either by client-side code or server-side code. +`http` API. For instance, most events, like `error`, `connect` and `stream`, can +be emitted either by client-side code or server-side code. ### Server-side example @@ -36,7 +36,6 @@ const server = http2.createSecureServer({ cert: fs.readFileSync('localhost-cert.pem') }); server.on('error', (err) => console.error(err)); -server.on('socketError', (err) => console.error(err)); server.on('stream', (stream, headers) => { // stream is a Duplex @@ -68,7 +67,6 @@ const client = http2.connect('https://localhost:8443', { ca: fs.readFileSync('localhost-cert.pem') }); client.on('error', (err) => console.error(err)); -client.on('socketError', (err) => console.error(err)); const req = client.request({ ':path': '/' }); @@ -479,44 +477,6 @@ Used to set a callback function that is called when there is no activity on the `Http2Session` after `msecs` milliseconds. The given `callback` is registered as a listener on the `'timeout'` event. -#### http2session.close(options[, callback]) - - -* `options` {Object} - * `errorCode` {number} The HTTP/2 [error code][] to return. Note that this is - *not* the same thing as an HTTP Response Status Code. **Default:** `0x00` - (No Error). - * `lastStreamID` {number} The Stream ID of the last successfully processed - `Http2Stream` on this `Http2Session`. If unspecified, will default to the - ID of the most recently received stream. - * `opaqueData` {Buffer|Uint8Array} A `Buffer` or `Uint8Array` instance - containing arbitrary additional data to send to the peer upon disconnection. - This is used, typically, to provide additional data for debugging failures, - if necessary. -* `callback` {Function} A callback that is invoked after the session shutdown - has been completed. -* Returns: {undefined} - -Attempts to shut down this `Http2Session` using HTTP/2 defined procedures. -If specified, the given `callback` function will be invoked once the shutdown -process has completed. - -If the `Http2Session` instance is a server-side session and the `errorCode` -option is `0x00` (No Error), a "graceful" shutdown will be initiated. During a -"graceful" shutdown, the session will first send a `GOAWAY` frame to -the connected peer identifying the last processed stream as 231-1. -Then, on the next tick of the event loop, a second `GOAWAY` frame identifying -the most recently processed stream identifier is sent. This process allows the -remote peer to begin preparing for the connection to be terminated. - -```js -session.close({ - opaqueData: Buffer.from('add some debugging data here') -}, () => session.destroy()); -``` - #### http2session.socket +* `alt`: {string} +* `origin`: {string} +* `streamId`: {number} + The `'altsvc'` event is emitted whenever an `ALTSVC` frame is received by the client. The event is emitted with the `ALTSVC` value, origin, and stream -ID, if any. If no `origin` is provided in the `ALTSVC` frame, `origin` will +ID. If no `origin` is provided in the `ALTSVC` frame, `origin` will be an empty string. ```js const http2 = require('http2'); const client = http2.connect('https://example.org'); -client.on('altsvc', (alt, origin, stream) => { +client.on('altsvc', (alt, origin, streamId) => { console.log(alt); console.log(origin); - console.log(stream); + console.log(streamId); }); ``` @@ -1472,10 +1436,9 @@ added: v8.4.0 * Extends: {net.Server} -In `Http2Server`, there is no `'clientError'` event as there is in -HTTP1. However, there are `'socketError'`, `'sessionError'`, and -`'streamError'`, for errors emitted on the socket, `Http2Session`, or -`Http2Stream`. +In `Http2Server`, there are no `'clientError'` events as there are in +HTTP1. However, there are `'sessionError'`, and `'streamError'` events for +errors emitted on the socket, or from `Http2Session` or `Http2Stream` instances. #### Event: 'checkContinue' + +* `request` {http2.Http2ServerRequest} +* `response` {http2.Http2ServerResponse} + +If a [`'request'`][] listener is registered or [`http2.createSecureServer()`][] +is supplied a callback function, the `'checkContinue'` event is emitted each +time a request with an HTTP `Expect: 100-continue` is received. If this event +is not listened for, the server will automatically respond with a status +`100 Continue` as appropriate. + +Handling this event involves calling [`response.writeContinue()`][] if the client +should continue to send the request body, or generating an appropriate HTTP +response (e.g. 400 Bad Request) if the client should not continue to send the +request body. + +Note that when this event is emitted and handled, the [`'request'`][] event will +not be emitted. + +#### Event: 'request' -The `'sessionError'` event is emitted when an `'error'` event is emitted by -an `Http2Session` object associated with the `Http2SecureServer`. +* `request` {http2.Http2ServerRequest} +* `response` {http2.Http2ServerResponse} -#### Event: 'unknownProtocol' +Emitted each time there is a request. Note that there may be multiple requests +per session. See the [Compatibility API][]. + +#### Event: 'session' -The `'unknownProtocol'` event is emitted when a connecting client fails to -negotiate an allowed protocol (i.e. HTTP/2 or HTTP/1.1). The event handler -receives the socket for handling. If no listener is registered for this event, -the connection is terminated. See the [Compatibility API][]. +The `'session'` event is emitted when a new `Http2Session` is created by the +`Http2SecureServer`. + +#### Event: 'sessionError' + + +The `'sessionError'` event is emitted when an `'error'` event is emitted by +an `Http2Session` object associated with the `Http2SecureServer`. #### Event: 'stream' -* `request` {http2.Http2ServerRequest} -* `response` {http2.Http2ServerResponse} - -Emitted each time there is a request. Note that there may be multiple requests -per session. See the [Compatibility API][]. +The `'timeout'` event is emitted when there is no activity on the Server for +a given number of milliseconds set using `http2secureServer.setTimeout()`. -#### Event: 'timeout' +#### Event: 'unknownProtocol' -#### Event: 'checkContinue' - - -* `request` {http2.Http2ServerRequest} -* `response` {http2.Http2ServerResponse} - -If a [`'request'`][] listener is registered or [`http2.createSecureServer()`][] -is supplied a callback function, the `'checkContinue'` event is emitted each -time a request with an HTTP `Expect: 100-continue` is received. If this event -is not listened for, the server will automatically respond with a status -`100 Continue` as appropriate. - -Handling this event involves calling [`response.writeContinue()`][] if the client -should continue to send the request body, or generating an appropriate HTTP -response (e.g. 400 Bad Request) if the client should not continue to send the -request body. - -Note that when this event is emitted and handled, the [`'request'`][] event will -not be emitted. +The `'unknownProtocol'` event is emitted when a connecting client fails to +negotiate an allowed protocol (i.e. HTTP/2 or HTTP/1.1). The event handler +receives the socket for handling. If no listener is registered for this event, +the connection is terminated. See the [Compatibility API][]. ### http2.createServer(options[, onRequestHandler]) @@ -230,7 +230,7 @@ added: v8.4.0 The `'stream'` event is emitted when a new `Http2Stream` is created. When invoked, the handler function will receive a reference to the `Http2Stream` -object, a [HTTP2 Headers Object][], and numeric flags associated with the +object, a [HTTP/2 Headers Object][], and numeric flags associated with the creation of the stream. ```js @@ -383,7 +383,7 @@ Transmits a `GOAWAY` frame to the connected peer *without* shutting down the added: v8.4.0 --> -* Value: {HTTP2 Settings Object} +* Value: {HTTP/2 Settings Object} A prototype-less object describing the current local settings of this `Http2Session`. The local settings are local to *this* `Http2Session` instance. @@ -462,7 +462,7 @@ instance's underlying [`net.Socket`]. added: v8.4.0 --> -* Value: {HTTP2 Settings Object} +* Value: {HTTP/2 Settings Object} A prototype-less object describing the current remote settings of this `Http2Session`. The remote settings are set by the *connected* HTTP/2 peer. @@ -533,8 +533,7 @@ An object describing the current status of this `Http2Session`. added: v8.4.0 --> -* `settings` {HTTP2 Settings Object} -* Returns {undefined} +* `settings` {HTTP/2 Settings Object} Updates the current local settings for this `Http2Session` and sends a new `SETTINGS` frame to the connected HTTP/2 peer. @@ -674,7 +673,7 @@ client.on('altsvc', (alt, origin, streamId) => { added: v8.4.0 --> -* `headers` {HTTP2 Headers Object} +* `headers` {HTTP/2 Headers Object} * `options` {Object} * `endStream` {boolean} `true` if the `Http2Stream` *writable* side should be closed initially, such as when sending a `GET` request that should not @@ -862,7 +861,7 @@ added: v8.4.0 The `'trailers'` event is emitted when a block of headers associated with trailing header fields is received. The listener callback is passed the -[HTTP2 Headers Object][] and flags associated with the headers. +[HTTP/2 Headers Object][] and flags associated with the headers. ```js stream.on('trailers', (headers, flags) => { @@ -961,7 +960,7 @@ calling `http2stream.close()`, or `http2stream.destroy()`. Will be added: REPLACEME --> -* Value: {HTTP2 Headers Object} +* Value: {HTTP/2 Headers Object} An object containing the outbound headers sent for this `Http2Stream`. @@ -970,7 +969,7 @@ An object containing the outbound headers sent for this `Http2Stream`. added: REPLACEME --> -* Value: {HTTP2 Headers Object[]} +* Value: {HTTP/2 Headers Object[]} An array of objects containing the outbound informational (additional) headers sent for this `Http2Stream`. @@ -980,7 +979,7 @@ sent for this `Http2Stream`. added: REPLACEME --> -* Value: {HTTP2 Headers Object} +* Value: {HTTP/2 Headers Object} An object containing the outbound trailers sent for this this `HttpStream`. @@ -1063,7 +1062,7 @@ added: v8.4.0 The `'headers'` event is emitted when an additional block of headers is received for a stream, such as when a block of `1xx` informational headers is received. -The listener callback is passed the [HTTP2 Headers Object][] and flags +The listener callback is passed the [HTTP/2 Headers Object][] and flags associated with the headers. ```js @@ -1078,7 +1077,7 @@ added: v8.4.0 --> The `'push'` event is emitted when response headers for a Server Push stream -are received. The listener callback is passed the [HTTP2 Headers Object][] and +are received. The listener callback is passed the [HTTP/2 Headers Object][] and flags associated with the headers. ```js @@ -1095,7 +1094,7 @@ added: v8.4.0 The `'response'` event is emitted when a response `HEADERS` frame has been received for this stream from the connected HTTP/2 server. The listener is invoked with two arguments: an Object containing the received -[HTTP2 Headers Object][], and flags associated with the headers. +[HTTP/2 Headers Object][], and flags associated with the headers. For example: @@ -1125,8 +1124,7 @@ provide additional methods such as `http2stream.pushStream()` and added: v8.4.0 --> -* `headers` {HTTP2 Headers Object} -* Returns: {undefined} +* `headers` {HTTP/2 Headers Object} Sends an additional informational `HEADERS` frame to the connected HTTP/2 peer. @@ -1156,7 +1154,7 @@ accepts push streams, `false` otherwise. Settings are the same for every added: v8.4.0 --> -* `headers` {HTTP2 Headers Object} +* `headers` {HTTP/2 Headers Object} * `options` {Object} * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream, the created stream is made the sole direct dependency of the parent, with @@ -1168,7 +1166,7 @@ added: v8.4.0 initiated. * `err` {Error} * `pushStream` {ServerHttp2Stream} The returned pushStream object. - * `headers` {HTTP2 Headers Object} Headers object the pushStream was + * `headers` {HTTP/2 Headers Object} Headers object the pushStream was initiated with. * Returns: {undefined} @@ -1199,7 +1197,7 @@ a `weight` value to `http2stream.priority` with the `silent` option set to added: v8.4.0 --> -* `headers` {HTTP2 Headers Object} +* `headers` {HTTP/2 Headers Object} * `options` {Object} * `endStream` {boolean} Set to `true` to indicate that the response will not include payload data. @@ -1245,7 +1243,7 @@ added: v8.4.0 --> * `fd` {number} A readable file descriptor. -* `headers` {HTTP2 Headers Object} +* `headers` {HTTP/2 Headers Object} * `options` {Object} * `statCheck` {Function} * `getTrailers` {Function} Callback function invoked to collect trailer @@ -1336,7 +1334,7 @@ added: v8.4.0 --> * `path` {string|Buffer|URL} -* `headers` {HTTP2 Headers Object} +* `headers` {HTTP/2 Headers Object} * `options` {Object} * `statCheck` {Function} * `onError` {Function} Callback function invoked in the case of an @@ -1712,7 +1710,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {HTTP2 Settings Object} The initial settings to send to the + * `settings` {HTTP/2 Settings Object} The initial settings to send to the remote peer upon connection. * `onRequestHandler` {Function} See [Compatibility API][] * Returns: {Http2Server} @@ -1807,7 +1805,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {HTTP2 Settings Object} The initial settings to send to the + * `settings` {HTTP/2 Settings Object} The initial settings to send to the remote peer upon connection. * ...: Any [`tls.createServer()`][] options can be provided. For servers, the identity options (`pfx` or `key`/`cert`) are usually required. @@ -1903,7 +1901,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {HTTP2 Settings Object} The initial settings to send to the + * `settings` {HTTP/2 Settings Object} The initial settings to send to the remote peer upon connection. * `createConnection` {Function} An optional callback that receives the `URL` instance passed to `connect` and the `options` object, and returns any @@ -1956,7 +1954,7 @@ a given number of milliseconds set using `http2server.setTimeout()`. added: v8.4.0 --> -* Returns: {HTTP2 Settings Object} +* Returns: {HTTP/2 Settings Object} Returns an object containing the default settings for an `Http2Session` instance. This method returns a new object instance every time it is called @@ -1967,7 +1965,7 @@ so instances returned may be safely modified for use. added: v8.4.0 --> -* `settings` {HTTP2 Settings Object} +* `settings` {HTTP/2 Settings Object} * Returns: {Buffer} Returns a `Buffer` instance containing serialized representation of the given @@ -1989,9 +1987,9 @@ added: v8.4.0 --> * `buf` {Buffer|Uint8Array} The packed settings. -* Returns: {HTTP2 Settings Object} +* Returns: {HTTP/2 Settings Object} -Returns a [HTTP2 Settings Object][] containing the deserialized settings from +Returns a [HTTP/2 Settings Object][] containing the deserialized settings from the given `Buffer` as generated by `http2.getPackedSettings()`. ### Headers Object @@ -2256,7 +2254,7 @@ In order to create a mixed [HTTPS][] and HTTP/2 server, refer to the [ALPN negotiation][] section. Upgrading from non-tls HTTP/1 servers is not supported. -The HTTP2 compatibility API is composed of [`Http2ServerRequest`]() and +The HTTP/2 compatibility API is composed of [`Http2ServerRequest`]() and [`Http2ServerResponse`](). They aim at API compatibility with HTTP/1, but they do not hide the differences between the protocols. As an example, the status message for HTTP codes is ignored. @@ -2364,7 +2362,7 @@ Example: console.log(request.headers); ``` -See [HTTP2 Headers Object][]. +See [HTTP/2 Headers Object][]. *Note*: In HTTP/2, the request path, hostname, protocol, and method are represented as special headers prefixed with the `:` character (e.g. `':path'`). @@ -3087,8 +3085,8 @@ following additional properties: [HTTP/1]: http.html [HTTP/2]: https://tools.ietf.org/html/rfc7540 [HTTP/2 Unencrypted]: https://http2.github.io/faq/#does-http2-require-encryption -[HTTP2 Headers Object]: #http2_headers_object -[HTTP2 Settings Object]: #http2_settings_object +[HTTP/2 Headers Object]: #http2_headers_object +[HTTP/2 Settings Object]: #http2_settings_object [HTTPS]: https.html [Http2Session and Sockets]: #http2_http2session_and_sockets [Performance Observer]: perf_hooks.html diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 0ab73162dd59e0..7999d55d740719 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -54,8 +54,8 @@ const typeMap = { 'http.ServerResponse': 'http.html#http_class_http_serverresponse', 'ClientHttp2Stream': 'http2.html#http2_class_clienthttp2stream', - 'HTTP2 Headers Object': 'http2.html#http2_headers_object', - 'HTTP2 Settings Object': 'http2.html#http2_settings_object', + 'HTTP/2 Headers Object': 'http2.html#http2_headers_object', + 'HTTP/2 Settings Object': 'http2.html#http2_settings_object', 'http2.Http2ServerRequest': 'http2.html#http2_class_http2_http2serverrequest', 'http2.Http2ServerResponse': 'http2.html#http2_class_http2_http2serverresponse', From 83d8ad351ca8ed8db9018a186ae0d5db37780b4d Mon Sep 17 00:00:00 2001 From: Trivikram <16024985+trivikr@users.noreply.github.com> Date: Sun, 18 Feb 2018 19:30:51 -0800 Subject: [PATCH 208/218] test: http2 stream.respond() error checks Backport-PR-URL: https://github.com/nodejs/node/pull/19579 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18861 Reviewed-By: James M Snell --- test/parallel/test-http2-respond-errors.js | 144 ++++++++---------- .../test-http2-respond-nghttperrors.js | 99 ++++++++++++ 2 files changed, 165 insertions(+), 78 deletions(-) create mode 100644 test/parallel/test-http2-respond-nghttperrors.js diff --git a/test/parallel/test-http2-respond-errors.js b/test/parallel/test-http2-respond-errors.js index 629fec4fa684d2..5854c4fb8d02e4 100644 --- a/test/parallel/test-http2-respond-errors.js +++ b/test/parallel/test-http2-respond-errors.js @@ -5,93 +5,81 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const http2 = require('http2'); -const { - constants, - Http2Stream, - nghttp2ErrorString -} = process.binding('http2'); +const { Http2Stream } = process.binding('http2'); + +const types = { + boolean: true, + function: () => {}, + number: 1, + object: {}, + array: [], + null: null, + symbol: Symbol('test') +}; -// tests error handling within respond -// - every other NGHTTP2 error from binding (should emit stream error) +const server = http2.createServer(); -const specificTestKeys = []; +Http2Stream.prototype.respond = () => 1; +server.on('stream', common.mustCall((stream) => { -const specificTests = []; + // Check for all possible TypeError triggers on options.getTrailers + Object.entries(types).forEach(([type, value]) => { + if (type === 'function') { + return; + } -const genericTests = Object.getOwnPropertyNames(constants) - .filter((key) => ( - key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0 - )) - .map((key) => ({ - ngError: constants[key], - error: { - code: 'ERR_HTTP2_ERROR', + common.expectsError( + () => stream.respond({ + 'content-type': 'text/plain' + }, { + ['getTrailers']: value + }), + { + type: TypeError, + code: 'ERR_INVALID_OPT_VALUE', + message: `The value "${String(value)}" is invalid ` + + 'for option "getTrailers"' + } + ); + }); + + // Send headers + stream.respond({ + 'content-type': 'text/plain' + }, { + ['getTrailers']: () => common.mustCall() + }); + + // Should throw if headers already sent + common.expectsError( + () => stream.respond(), + { type: Error, - message: nghttp2ErrorString(constants[key]) - }, - type: 'stream' - })); - - -const tests = specificTests.concat(genericTests); - -let currentError; - -// mock submitResponse because we only care about testing error handling -Http2Stream.prototype.respond = () => currentError.ngError; - -const server = http2.createServer(); -server.on('stream', common.mustCall((stream, headers) => { - const errorMustCall = common.expectsError(currentError.error); - const errorMustNotCall = common.mustNotCall( - `${currentError.error.code} should emit on ${currentError.type}` + code: 'ERR_HTTP2_HEADERS_SENT', + message: 'Response has already been initiated.' + } ); - if (currentError.type === 'stream') { - stream.session.on('error', errorMustNotCall); - stream.on('error', errorMustCall); - stream.on('error', common.mustCall(() => { - stream.destroy(); - })); - } else { - stream.session.once('error', errorMustCall); - stream.on('error', errorMustNotCall); - } - - stream.respond(); -}, tests.length)); - -server.listen(0, common.mustCall(() => runTest(tests.shift()))); - -function runTest(test) { - const port = server.address().port; - const url = `http://localhost:${port}`; - const headers = { - ':path': '/', - ':method': 'POST', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - - const client = http2.connect(url); - const req = client.request(headers); - req.on('error', common.expectsError({ - code: 'ERR_HTTP2_STREAM_ERROR', - type: Error, - message: 'Stream closed with error code 2' - })); + // Should throw if stream already destroyed + stream.destroy(); + common.expectsError( + () => stream.respond(), + { + type: Error, + code: 'ERR_HTTP2_INVALID_STREAM', + message: 'The stream has been destroyed' + } + ); +})); - currentError = test; - req.resume(); - req.end(); +server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + const req = client.request(); req.on('end', common.mustCall(() => { client.close(); - - if (!tests.length) { - server.close(); - } else { - runTest(tests.shift()); - } + server.close(); })); -} + req.resume(); + req.end(); +})); diff --git a/test/parallel/test-http2-respond-nghttperrors.js b/test/parallel/test-http2-respond-nghttperrors.js new file mode 100644 index 00000000000000..5ec953c5442360 --- /dev/null +++ b/test/parallel/test-http2-respond-nghttperrors.js @@ -0,0 +1,99 @@ +'use strict'; +// Flags: --expose-internals + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const http2 = require('http2'); +const { + constants, + Http2Stream, + nghttp2ErrorString +} = process.binding('http2'); +const { NghttpError } = require('internal/http2/util'); + +// tests error handling within respond +// - every other NGHTTP2 error from binding (should emit stream error) + +const specificTestKeys = []; + +const specificTests = []; + +const genericTests = Object.getOwnPropertyNames(constants) + .filter((key) => ( + key.indexOf('NGHTTP2_ERR') === 0 && specificTestKeys.indexOf(key) < 0 + )) + .map((key) => ({ + ngError: constants[key], + error: { + code: 'ERR_HTTP2_ERROR', + type: NghttpError, + name: 'Error [ERR_HTTP2_ERROR]', + message: nghttp2ErrorString(constants[key]) + }, + type: 'stream' + })); + + +const tests = specificTests.concat(genericTests); + +let currentError; + +// mock submitResponse because we only care about testing error handling +Http2Stream.prototype.respond = () => currentError.ngError; + +const server = http2.createServer(); +server.on('stream', common.mustCall((stream, headers) => { + const errorMustCall = common.expectsError(currentError.error); + const errorMustNotCall = common.mustNotCall( + `${currentError.error.code} should emit on ${currentError.type}` + ); + + if (currentError.type === 'stream') { + stream.session.on('error', errorMustNotCall); + stream.on('error', errorMustCall); + stream.on('error', common.mustCall(() => { + stream.destroy(); + })); + } else { + stream.session.once('error', errorMustCall); + stream.on('error', errorMustNotCall); + } + + stream.respond(); +}, tests.length)); + +server.listen(0, common.mustCall(() => runTest(tests.shift()))); + +function runTest(test) { + const port = server.address().port; + const url = `http://localhost:${port}`; + const headers = { + ':path': '/', + ':method': 'POST', + ':scheme': 'http', + ':authority': `localhost:${port}` + }; + + const client = http2.connect(url); + const req = client.request(headers); + req.on('error', common.expectsError({ + code: 'ERR_HTTP2_STREAM_ERROR', + type: Error, + message: 'Stream closed with error code 2' + })); + + currentError = test; + req.resume(); + req.end(); + + req.on('end', common.mustCall(() => { + client.close(); + + if (!tests.length) { + server.close(); + } else { + runTest(tests.shift()); + } + })); +} From eddf3a6c70b81561e8bad2dfac7e4bec0a65273f Mon Sep 17 00:00:00 2001 From: Sarat Addepalli Date: Fri, 16 Mar 2018 17:29:47 +0530 Subject: [PATCH 209/218] http2: destroy() stream, upon errnoException First steps towards #19060 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/19389 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Matteo Collina --- lib/internal/http2/core.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 353636c224cecb..315ea280bfb81a 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -1644,7 +1644,7 @@ class Http2Stream extends Duplex { req.async = false; const err = createWriteReq(req, handle, data, encoding); if (err) - throw util._errnoException(err, 'write', req.error); + return this.destroy(util._errnoException(err, 'write', req.error), cb); trackWriteState(this, req.bytes); } @@ -1687,7 +1687,7 @@ class Http2Stream extends Duplex { } const err = handle.writev(req, chunks); if (err) - throw util._errnoException(err, 'write', req.error); + return this.destroy(util._errnoException(err, 'write', req.error), cb); trackWriteState(this, req.bytes); } From 1ff3544a4b95114d7e2934cf221c01a9e42a5361 Mon Sep 17 00:00:00 2001 From: Vse Mozhet Byt Date: Wed, 28 Mar 2018 06:02:50 +0300 Subject: [PATCH 210/218] doc: guard against md list parsing edge case Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/19647 Reviewed-By: Trivikram Kamat Reviewed-By: Rich Trott Reviewed-By: Chen Gang Reviewed-By: Tiancheng "Timothy" Gu Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- doc/api/http2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index c3f5f54fd132be..5259f567b22a09 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -2055,8 +2055,8 @@ properties. * `maxConcurrentStreams` {number} Specifies the maximum number of concurrent streams permitted on an `Http2Session`. There is no default value which implies, at least theoretically, 231-1 streams may be open - concurrently at any given time in an `Http2Session`. The minimum value is - 0. The maximum allowed value is 231-1. + concurrently at any given time in an `Http2Session`. The minimum value + is 0. The maximum allowed value is 231-1. * `maxHeaderListSize` {number} Specifies the maximum size (uncompressed octets) of header list that will be accepted. The minimum allowed value is 0. The maximum allowed value is 232-1. **Default:** 65535. From 4dbdb8ae4e2a22c39f405716188ca80203f5a223 Mon Sep 17 00:00:00 2001 From: Trivikram <16024985+trivikr@users.noreply.github.com> Date: Sun, 18 Feb 2018 00:11:06 -0800 Subject: [PATCH 211/218] test: http2 errors on req.close() Backport-PR-URL: https://github.com/nodejs/node/pull/19579 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/18854 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig --- ...t-http2-client-rststream-before-connect.js | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/parallel/test-http2-client-rststream-before-connect.js b/test/parallel/test-http2-client-rststream-before-connect.js index b0faaa5de2a398..7909fd97fc313b 100644 --- a/test/parallel/test-http2-client-rststream-before-connect.js +++ b/test/parallel/test-http2-client-rststream-before-connect.js @@ -16,18 +16,30 @@ server.on('stream', (stream) => { server.listen(0, common.mustCall(() => { const client = h2.connect(`http://localhost:${server.address().port}`); const req = client.request(); - req.close(1); + const closeCode = 1; + + common.expectsError( + () => req.close(2 ** 32), + { + type: RangeError, + code: 'ERR_OUT_OF_RANGE', + message: 'The "code" argument is out of range' + } + ); + assert.strictEqual(req.closed, false); + + req.close(closeCode, common.mustCall()); assert.strictEqual(req.closed, true); // make sure that destroy is called req._destroy = common.mustCall(req._destroy.bind(req)); - // second call doesn't do anything - assert.doesNotThrow(() => req.close(8)); + // Second call doesn't do anything. + req.close(closeCode + 1); req.on('close', common.mustCall((code) => { assert.strictEqual(req.destroyed, true); - assert.strictEqual(code, 1); + assert.strictEqual(code, closeCode); server.close(); client.close(); })); @@ -35,7 +47,7 @@ server.listen(0, common.mustCall(() => { req.on('error', common.expectsError({ code: 'ERR_HTTP2_STREAM_ERROR', type: Error, - message: 'Stream closed with error code 1' + message: `Stream closed with error code ${closeCode}` })); req.on('response', common.mustCall()); From 6d6e2e2454615b9466db2b1649a8f83b67d3fb9a Mon Sep 17 00:00:00 2001 From: Trivikram <16024985+trivikr@users.noreply.github.com> Date: Wed, 28 Feb 2018 20:48:29 +0530 Subject: [PATCH 212/218] http2: callback valid check before closing request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not close the request if callback is not a function, and throw ERR_INVALID_CALLBACK TypeError Backport-PR-URL: https://github.com/nodejs/node/pull/19229 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/19061 Fixes: https://github.com/nodejs/node/issues/18855 Reviewed-By: Matteo Collina Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Ruben Bridgewater Reviewed-By: Shingo Inoue Reviewed-By: Tobias Nießen --- lib/internal/http2/core.js | 4 ++-- .../test-http2-client-rststream-before-connect.js | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 315ea280bfb81a..09f1f993c38d07 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -1761,6 +1761,8 @@ class Http2Stream extends Duplex { throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'code', 'number'); if (code < 0 || code > kMaxInt) throw new errors.RangeError('ERR_OUT_OF_RANGE', 'code'); + if (callback !== undefined && typeof callback !== 'function') + throw new errors.TypeError('ERR_INVALID_CALLBACK'); // Unenroll the timeout. unenroll(this); @@ -1778,8 +1780,6 @@ class Http2Stream extends Duplex { state.rstCode = code; if (callback !== undefined) { - if (typeof callback !== 'function') - throw new errors.TypeError('ERR_INVALID_CALLBACK'); this.once('close', callback); } diff --git a/test/parallel/test-http2-client-rststream-before-connect.js b/test/parallel/test-http2-client-rststream-before-connect.js index 7909fd97fc313b..aeb31949db074e 100644 --- a/test/parallel/test-http2-client-rststream-before-connect.js +++ b/test/parallel/test-http2-client-rststream-before-connect.js @@ -28,6 +28,18 @@ server.listen(0, common.mustCall(() => { ); assert.strictEqual(req.closed, false); + [true, 1, {}, [], null, 'test'].forEach((notFunction) => { + common.expectsError( + () => req.close(closeCode, notFunction), + { + type: TypeError, + code: 'ERR_INVALID_CALLBACK', + message: 'callback must be a function' + } + ); + assert.strictEqual(req.closed, false); + }); + req.close(closeCode, common.mustCall()); assert.strictEqual(req.closed, true); From 84fa6eb1730229022ca5ba47d11543cb916b0692 Mon Sep 17 00:00:00 2001 From: Chris Miller Date: Wed, 4 Apr 2018 13:32:18 +0100 Subject: [PATCH 213/218] doc, http2: add sections for server.close() Clarify current behavior of http2server.close() and http2secureServer.close() w.r.t. perceived differences when compared with httpServer.close(). Fixes: https://github.com/nodejs/node/issues/19711 Backport-PR-URL: https://github.com/nodejs/node/pull/20456 PR-URL: https://github.com/nodejs/node/pull/19802 Reviewed-By: Vse Mozhet Byt Reviewed-By: Trivikram Kamat Reviewed-By: James M Snell Reviewed-By: Matteo Collina --- doc/api/http2.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/api/http2.md b/doc/api/http2.md index 5259f567b22a09..052860566fd441 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -1544,6 +1544,18 @@ added: v8.4.0 The `'timeout'` event is emitted when there is no activity on the Server for a given number of milliseconds set using `http2server.setTimeout()`. +#### server.close([callback]) + +- `callback` {Function} + +Stops the server from accepting new connections. See [`net.Server.close()`][]. + +Note that this is not analogous to restricting new requests since HTTP/2 +connections are persistent. To achieve a similar graceful shutdown behavior, +consider also using [`http2session.close()`] on active sessions. + ### Class: Http2SecureServer +- `callback` {Function} + +Stops the server from accepting new connections. See [`tls.Server.close()`][]. + +Note that this is not analogous to restricting new requests since HTTP/2 +connections are persistent. To achieve a similar graceful shutdown behavior, +consider also using [`http2session.close()`] on active sessions. + ### http2.createServer(options[, onRequestHandler]) + +* {boolean} + +Will be `true` if this `Http2Session` instance is still connecting, will be set +to `false` before emitting `connect` event and/or calling the `http2.connect` +callback. + #### http2session.destroy([error,][code]) * Value: {string|undefined} @@ -298,7 +298,7 @@ property. #### http2session.close([callback]) * `callback` {Function} @@ -313,7 +313,7 @@ If specified, the `callback` function is registered as a handler for the #### http2session.closed * Value: {boolean} @@ -323,7 +323,7 @@ Will be `true` if this `Http2Session` instance has been closed, otherwise #### http2session.connecting * {boolean} @@ -366,7 +366,7 @@ longer be used, otherwise `false`. #### http2session.encrypted * Value: {boolean|undefined} @@ -378,7 +378,7 @@ or stream. #### http2session.goaway([code, [lastStreamID, [opaqueData]]]) * `code` {number} An HTTP/2 error code @@ -401,7 +401,7 @@ A prototype-less object describing the current local settings of this #### http2session.originSet * Value: {string[]|undefined} @@ -462,7 +462,7 @@ If the `payload` argument is not specified, the default payload will be the #### http2session.ref() Calls [`ref()`][`net.Socket.prototype.ref`] on this `Http2Session` @@ -572,7 +572,7 @@ client. #### http2session.unref() Calls [`unref()`][`net.Socket.prototype.unref`] on this `Http2Session` @@ -585,7 +585,7 @@ added: v8.4.0 #### serverhttp2session.altsvc(alt, originOrStream) * `alt` {string} A description of the alternative service configuration as @@ -656,7 +656,7 @@ added: v8.4.0 #### Event: 'altsvc' * `alt`: {string} @@ -906,7 +906,7 @@ connected HTTP/2 peer. #### http2stream.closed * Value: {boolean} @@ -925,7 +925,7 @@ usable. #### http2stream.pending * Value: {boolean} @@ -968,7 +968,7 @@ calling `http2stream.close()`, or `http2stream.destroy()`. Will be #### http2stream.sentHeaders * Value: {HTTP/2 Headers Object} @@ -977,7 +977,7 @@ An object containing the outbound headers sent for this `Http2Stream`. #### http2stream.sentInfoHeaders * Value: {HTTP/2 Headers Object[]} @@ -987,7 +987,7 @@ sent for this `Http2Stream`. #### http2stream.sentTrailers * Value: {HTTP/2 Headers Object} diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 7ac2ab39448273..e3e3242ef321bb 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -543,7 +543,7 @@ This API can be called even if there is a pending JavaScript exception. #### napi_fatal_exception ```C napi_status napi_fatal_exception(napi_env env, napi_value err); @@ -3491,7 +3491,7 @@ may be required when implementing custom async behavior that does not use ### *napi_open_callback_scope* ```C NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, @@ -3516,7 +3516,7 @@ the required scope. ### *napi_close_callback_scope* ```C NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 2f2910af93db34..ace0c48bacdda3 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -31,7 +31,7 @@ instance of this class is provided via the `performance` property. ### performance.clearEntries(name) Remove all performance entry objects with `entryType` equal to `name` from the diff --git a/doc/changelogs/CHANGELOG_V8.md b/doc/changelogs/CHANGELOG_V8.md index 960f97ebc6e289..11b17bb89afd9b 100644 --- a/doc/changelogs/CHANGELOG_V8.md +++ b/doc/changelogs/CHANGELOG_V8.md @@ -10,6 +10,7 @@ +8.11.2
8.11.1
8.11.0
8.10.0
@@ -53,6 +54,239 @@ [Node.js Long Term Support Plan](https://github.com/nodejs/LTS) and will be supported actively until April 2019 and maintained until December 2019. + +## 2018-05-15, Version 8.11.2 'Carbon' (LTS), @MylesBorins + +### Notable Changes + +* **deps**: + - update node-inspect to 1.11.3 (Jan Krems) [#18354](https://github.com/nodejs/node/pull/18354) + - update nghttp2 to 1.29.0 (James M Snell) [#17908](https://github.com/nodejs/node/pull/17908) +* **http2**: + - Sync with current release stream +* **n-api**: + - Sync with current release stream + +### Commits + +* [[`ce3866bdcc`](https://github.com/nodejs/node/commit/ce3866bdcc)] - **async_hooks**: clean up comments (Ali Ijaz Sheikh) [#18467](https://github.com/nodejs/node/pull/18467) +* [[`86e3c89ea4`](https://github.com/nodejs/node/commit/86e3c89ea4)] - **benchmark**: improve compare output (Ruben Bridgewater) [#18597](https://github.com/nodejs/node/pull/18597) +* [[`18be476116`](https://github.com/nodejs/node/commit/18be476116)] - **benchmark**: fix punycode test for --without-intl (Timothy Gu) [#16251](https://github.com/nodejs/node/pull/16251) +* [[`88d3028e22`](https://github.com/nodejs/node/commit/88d3028e22)] - **build**: no longer have v8-debug.h as dependency. (Yang Guo) [#18677](https://github.com/nodejs/node/pull/18677) +* [[`7b6d93c145`](https://github.com/nodejs/node/commit/7b6d93c145)] - **build**: add doc linting when runnning `make lint` (Camilo Gonzalez) [#18472](https://github.com/nodejs/node/pull/18472) +* [[`9bce14172a`](https://github.com/nodejs/node/commit/9bce14172a)] - **build**: do not suppress output in make doc-only (Joyee Cheung) [#18507](https://github.com/nodejs/node/pull/18507) +* [[`333d7dda84`](https://github.com/nodejs/node/commit/333d7dda84)] - **build**: make lint-js independent of local node (Joyee Cheung) [#18272](https://github.com/nodejs/node/pull/18272) +* [[`d537f45aaa`](https://github.com/nodejs/node/commit/d537f45aaa)] - **build**: make lint-md independent of local node (Joyee Cheung) [#18272](https://github.com/nodejs/node/pull/18272) +* [[`658dd409fd`](https://github.com/nodejs/node/commit/658dd409fd)] - **build**: refine static and shared lib build (Yihong Wang) [#17604](https://github.com/nodejs/node/pull/17604) +* [[`659b2a1821`](https://github.com/nodejs/node/commit/659b2a1821)] - **build**: allow x86\_64 as a dest\_cpu alias for x64 (Rod Vagg) [#18052](https://github.com/nodejs/node/pull/18052) +* [[`424703a556`](https://github.com/nodejs/node/commit/424703a556)] - **build**: add cflags for OpenBSD, remove stray comma. (Aaron Bieber) [#18448](https://github.com/nodejs/node/pull/18448) +* [[`ab4809f195`](https://github.com/nodejs/node/commit/ab4809f195)] - **build,win**: restore vcbuild TAG functionality (Rod Vagg) [#18031](https://github.com/nodejs/node/pull/18031) +* [[`bf4d0743be`](https://github.com/nodejs/node/commit/bf4d0743be)] - **cluster**: fix inspector port assignment (Santiago Gimeno) [#18696](https://github.com/nodejs/node/pull/18696) +* [[`16bf5fed69`](https://github.com/nodejs/node/commit/16bf5fed69)] - **crypto**: reuse variable instead of reevaluation (Tobias Nießen) [#17735](https://github.com/nodejs/node/pull/17735) +* [[`9acc7f3fbb`](https://github.com/nodejs/node/commit/9acc7f3fbb)] - **deps**: update nghttp2 to 1.29.0 (James M Snell) [#17908](https://github.com/nodejs/node/pull/17908) +* [[`ab005592be`](https://github.com/nodejs/node/commit/ab005592be)] - **deps**: V8: backport 76c3ac5 from upstream (Ali Ijaz Sheikh) [#18298](https://github.com/nodejs/node/pull/18298) +* [[`f12db24947`](https://github.com/nodejs/node/commit/f12db24947)] - **deps**: cherry-pick a803fad from upstream V8 (Michaël Zasso) [#19824](https://github.com/nodejs/node/pull/19824) +* [[`09f5e252bf`](https://github.com/nodejs/node/commit/09f5e252bf)] - **deps**: cherry-pick 7abdadc from upstream V8 (Michaël Zasso) [#19824](https://github.com/nodejs/node/pull/19824) +* [[`c97237bc10`](https://github.com/nodejs/node/commit/c97237bc10)] - **deps**: cherry-pick a4bddba from upstream V8 (Michaël Zasso) [#19824](https://github.com/nodejs/node/pull/19824) +* [[`d02b72e799`](https://github.com/nodejs/node/commit/d02b72e799)] - **deps**: V8: backport 596d55a from upstream (Myles Borins) [#19477](https://github.com/nodejs/node/pull/19477) +* [[`79a7a17312`](https://github.com/nodejs/node/commit/79a7a17312)] - **deps**: update node-inspect to 1.11.3 (Jan Krems) [#18354](https://github.com/nodejs/node/pull/18354) +* [[`5394bc5c42`](https://github.com/nodejs/node/commit/5394bc5c42)] - **deps,src**: align ssize\_t ABI between Node & nghttp2 (Anna Henningsen) [#18565](https://github.com/nodejs/node/pull/18565) +* [[`165d214a54`](https://github.com/nodejs/node/commit/165d214a54)] - **doc**: add Http2Session.connecting property (Pieter Mees) [#19842](https://github.com/nodejs/node/pull/19842) +* [[`1ff3544a4b`](https://github.com/nodejs/node/commit/1ff3544a4b)] - **doc**: guard against md list parsing edge case (Vse Mozhet Byt) [#19647](https://github.com/nodejs/node/pull/19647) +* [[`f59eab0165`](https://github.com/nodejs/node/commit/f59eab0165)] - **doc**: rename HTTP2 to HTTP/2 (Timothy Gu) [#19603](https://github.com/nodejs/node/pull/19603) +* [[`da185cec8f`](https://github.com/nodejs/node/commit/da185cec8f)] - **doc**: add note about browsers and HTTP/2 (Steven) [#19476](https://github.com/nodejs/node/pull/19476) +* [[`30070c7568`](https://github.com/nodejs/node/commit/30070c7568)] - **doc**: warn against concurrent http2stream.respondWithFD (Anna Henningsen) [#18762](https://github.com/nodejs/node/pull/18762) +* [[`39267e8bb0`](https://github.com/nodejs/node/commit/39267e8bb0)] - **doc**: fix typo in http2.md (Vse Mozhet Byt) [#18602](https://github.com/nodejs/node/pull/18602) +* [[`2da965c5b8`](https://github.com/nodejs/node/commit/2da965c5b8)] - **doc**: remove removed apis from http2 docs (Kelvin Jin) [#18439](https://github.com/nodejs/node/pull/18439) +* [[`9a4a8c127e`](https://github.com/nodejs/node/commit/9a4a8c127e)] - **doc**: unify type linkification (Vse Mozhet Byt) [#18407](https://github.com/nodejs/node/pull/18407) +* [[`15023c7d28`](https://github.com/nodejs/node/commit/15023c7d28)] - **doc**: fix documentation of http2Stream.pushstream() (Peter Dalgaard-Jensen) [#18258](https://github.com/nodejs/node/pull/18258) +* [[`fac76f9a6b`](https://github.com/nodejs/node/commit/fac76f9a6b)] - **doc**: fix typo in http2stream.close param default (Moritz Peters) [#18166](https://github.com/nodejs/node/pull/18166) +* [[`88babd5a23`](https://github.com/nodejs/node/commit/88babd5a23)] - **doc**: fix s/rstStream/close in example (James M Snell) [#18088](https://github.com/nodejs/node/pull/18088) +* [[`d9d0d0e98e`](https://github.com/nodejs/node/commit/d9d0d0e98e)] - **doc**: update pushStream docs to use err first (James M Snell) [#18088](https://github.com/nodejs/node/pull/18088) +* [[`940457394a`](https://github.com/nodejs/node/commit/940457394a)] - **doc**: compact eslint directives in common/README (Vse Mozhet Byt) [#17971](https://github.com/nodejs/node/pull/17971) +* [[`e9de5a976b`](https://github.com/nodejs/node/commit/e9de5a976b)] - **doc**: re-alphabetise sections in common/README.md (Vse Mozhet Byt) [#17971](https://github.com/nodejs/node/pull/17971) +* [[`c924adf33d`](https://github.com/nodejs/node/commit/c924adf33d)] - **doc**: fix code nits in common/README (Vse Mozhet Byt) [#17971](https://github.com/nodejs/node/pull/17971) +* [[`0205b3f0c1`](https://github.com/nodejs/node/commit/0205b3f0c1)] - **doc**: correct spelling (sreepurnajasti) [#17911](https://github.com/nodejs/node/pull/17911) +* [[`591f78bb0a`](https://github.com/nodejs/node/commit/591f78bb0a)] - **doc**: grammar fixes in http2.md (Rich Trott) [#17972](https://github.com/nodejs/node/pull/17972) +* [[`35ee8943da`](https://github.com/nodejs/node/commit/35ee8943da)] - **doc**: add docs for common/http2.js utility (James M Snell) [#17942](https://github.com/nodejs/node/pull/17942) +* [[`f0ba2c6ceb`](https://github.com/nodejs/node/commit/f0ba2c6ceb)] - **doc**: Add a missing comma (jiangq) [#19555](https://github.com/nodejs/node/pull/19555) +* [[`7c6fa183cb`](https://github.com/nodejs/node/commit/7c6fa183cb)] - **doc**: fix typos on n-api (Kyle Robinson Young) [#19385](https://github.com/nodejs/node/pull/19385) +* [[`1abb168838`](https://github.com/nodejs/node/commit/1abb168838)] - **doc**: fix n-api asynchronous threading docs (Eric Bickle) [#19073](https://github.com/nodejs/node/pull/19073) +* [[`87d0fd8212`](https://github.com/nodejs/node/commit/87d0fd8212)] - **doc**: mark NAPI\_AUTO\_LENGTH as code (Tobias Nießen) [#18697](https://github.com/nodejs/node/pull/18697) +* [[`58688d97dc`](https://github.com/nodejs/node/commit/58688d97dc)] - **doc**: fix exporting a function example (Aonghus O Nia) [#18661](https://github.com/nodejs/node/pull/18661) +* [[`4d43607474`](https://github.com/nodejs/node/commit/4d43607474)] - **doc**: fix typo in n-api.md (Vse Mozhet Byt) [#18590](https://github.com/nodejs/node/pull/18590) +* [[`9729278007`](https://github.com/nodejs/node/commit/9729278007)] - **doc**: small typo in n-api.md (iskore) [#18555](https://github.com/nodejs/node/pull/18555) +* [[`7ed1dfef28`](https://github.com/nodejs/node/commit/7ed1dfef28)] - **doc**: remove usage of you in n-api doc (Michael Dawson) [#18528](https://github.com/nodejs/node/pull/18528) +* [[`84e0a03727`](https://github.com/nodejs/node/commit/84e0a03727)] - **doc**: remove uannecessary Require (Michael Dawson) [#18184](https://github.com/nodejs/node/pull/18184) +* [[`51513fdf9e`](https://github.com/nodejs/node/commit/51513fdf9e)] - **doc**: napi: make header style consistent (Ali Ijaz Sheikh) [#18122](https://github.com/nodejs/node/pull/18122) +* [[`02ae3295d5`](https://github.com/nodejs/node/commit/02ae3295d5)] - **doc**: napi: fix unbalanced emphasis (Ali Ijaz Sheikh) [#18122](https://github.com/nodejs/node/pull/18122) +* [[`79ecc2c586`](https://github.com/nodejs/node/commit/79ecc2c586)] - **doc**: updates examples to use NULL (Michael Dawson) [#18008](https://github.com/nodejs/node/pull/18008) +* [[`b2213798a3`](https://github.com/nodejs/node/commit/b2213798a3)] - **doc**: fix MDN links to avoid redirections (Vse Mozhet Byt) [#18631](https://github.com/nodejs/node/pull/18631) +* [[`f4ddaaec0e`](https://github.com/nodejs/node/commit/f4ddaaec0e)] - **doc**: move Fedor to TSC Emeritus (Myles Borins) [#18752](https://github.com/nodejs/node/pull/18752) +* [[`b8f2acd2e8`](https://github.com/nodejs/node/commit/b8f2acd2e8)] - **doc**: add mmarchini to collaborators (Matheus Marchini) [#18740](https://github.com/nodejs/node/pull/18740) +* [[`16f9631475`](https://github.com/nodejs/node/commit/16f9631475)] - **doc**: add history for url.parse (Steven) [#18685](https://github.com/nodejs/node/pull/18685) +* [[`d30c3533ff`](https://github.com/nodejs/node/commit/d30c3533ff)] - **doc**: fix links to Style Guide and CPP Style Guide (Justin Lee) [#18683](https://github.com/nodejs/node/pull/18683) +* [[`176ed1e9b1`](https://github.com/nodejs/node/commit/176ed1e9b1)] - **doc**: add devsnek to collaborators (Gus Caplan) [#18679](https://github.com/nodejs/node/pull/18679) +* [[`25db460f03`](https://github.com/nodejs/node/commit/25db460f03)] - **doc**: expand on promises and async\_hooks (Ali Ijaz Sheikh) [#18540](https://github.com/nodejs/node/pull/18540) +* [[`73adadd56a`](https://github.com/nodejs/node/commit/73adadd56a)] - **doc**: add section for strategic initiatives (Michael Dawson) [#17104](https://github.com/nodejs/node/pull/17104) +* [[`8ca6d34801`](https://github.com/nodejs/node/commit/8ca6d34801)] - **doc**: add introduce about cli options (Weijia Wang) [#18475](https://github.com/nodejs/node/pull/18475) +* [[`1ea1970c37`](https://github.com/nodejs/node/commit/1ea1970c37)] - **doc**: modify the return value of request.write() (陈刚) [#18526](https://github.com/nodejs/node/pull/18526) +* [[`50bdf0ed78`](https://github.com/nodejs/node/commit/50bdf0ed78)] - **doc**: be more explicit in the sypnosis (Tim O. Peters) [#17977](https://github.com/nodejs/node/pull/17977) +* [[`f8ad381e61`](https://github.com/nodejs/node/commit/f8ad381e61)] - **doc**: add missing meta for createCipheriv (Tobias Nießen) [#18651](https://github.com/nodejs/node/pull/18651) +* [[`0071560eb4`](https://github.com/nodejs/node/commit/0071560eb4)] - **doc**: fix description of createDecipheriv (Tobias Nießen) [#18651](https://github.com/nodejs/node/pull/18651) +* [[`c89781583b`](https://github.com/nodejs/node/commit/c89781583b)] - **doc**: fix various nits (Vse Mozhet Byt) [#19743](https://github.com/nodejs/node/pull/19743) +* [[`1091dfc801`](https://github.com/nodejs/node/commit/1091dfc801)] - **doc**: linkify missing types (Vse Mozhet Byt) [#18444](https://github.com/nodejs/node/pull/18444) +* [[`1107a494e4`](https://github.com/nodejs/node/commit/1107a494e4)] - **doc**: shell option for the execFile and execFileSync functions (jvelezpo) [#18237](https://github.com/nodejs/node/pull/18237) +* [[`36ea472393`](https://github.com/nodejs/node/commit/36ea472393)] - **doc**: improve http.request documentation (Guangcong Luo) [#18289](https://github.com/nodejs/node/pull/18289) +* [[`e5d5137963`](https://github.com/nodejs/node/commit/e5d5137963)] - **doc**: streamline README intro (Rich Trott) [#18483](https://github.com/nodejs/node/pull/18483) +* [[`eec9334a2e`](https://github.com/nodejs/node/commit/eec9334a2e)] - **doc**: move Brian White to TSC Emeriti list (Rich Trott) [#18482](https://github.com/nodejs/node/pull/18482) +* [[`ac41aacb05`](https://github.com/nodejs/node/commit/ac41aacb05)] - **doc**: improve stream documentation (陈刚) [#18375](https://github.com/nodejs/node/pull/18375) +* [[`7feeb1574e`](https://github.com/nodejs/node/commit/7feeb1574e)] - **doc**: add Gibson Fahnestock to TSC (Rich Trott) [#18481](https://github.com/nodejs/node/pull/18481) +* [[`142ad8d450`](https://github.com/nodejs/node/commit/142ad8d450)] - **doc**: reorder section on updating PR branch (Ali Ijaz Sheikh) [#18355](https://github.com/nodejs/node/pull/18355) +* [[`39ea4f12c5`](https://github.com/nodejs/node/commit/39ea4f12c5)] - **doc**: fix manpage warnings (Roman Reiss) +* [[`5209f9e1e2`](https://github.com/nodejs/node/commit/5209f9e1e2)] - **doc**: warn about GCM authenticity (Tobias Nießen) [#18376](https://github.com/nodejs/node/pull/18376) +* [[`e84e9db6fe`](https://github.com/nodejs/node/commit/e84e9db6fe)] - **doc**: capitalize non-primitive types (Vse Mozhet Byt) [#18111](https://github.com/nodejs/node/pull/18111) +* [[`84fa6eb173`](https://github.com/nodejs/node/commit/84fa6eb173)] - **doc, http2**: add sections for server.close() (Chris Miller) [#19802](https://github.com/nodejs/node/pull/19802) +* [[`cbc8561949`](https://github.com/nodejs/node/commit/cbc8561949)] - **errors**: remove ERR\_OUTOFMEMORY (Tobias Nießen) [#17877](https://github.com/nodejs/node/pull/17877) +* [[`2995506bbf`](https://github.com/nodejs/node/commit/2995506bbf)] - **fs**: fix stack overflow in fs.readdirSync (Joyee Cheung) [#18647](https://github.com/nodejs/node/pull/18647) +* [[`a653f23dfc`](https://github.com/nodejs/node/commit/a653f23dfc)] - **fs**: fix `createReadStream(…, {end: n})` for non-seekable fds (Anna Henningsen) [#19329](https://github.com/nodejs/node/pull/19329) +* [[`6bfdba125f`](https://github.com/nodejs/node/commit/6bfdba125f)] - **http**: remove default 'drain' listener on upgrade (Luigi Pinca) [#18866](https://github.com/nodejs/node/pull/18866) +* [[`29c395d975`](https://github.com/nodejs/node/commit/29c395d975)] - **http**: allow \_httpMessage to be GC'ed (Luigi Pinca) [#18865](https://github.com/nodejs/node/pull/18865) +* [[`d2a884edf9`](https://github.com/nodejs/node/commit/d2a884edf9)] - **http**: fix parsing of binary upgrade response body (Ben Noordhuis) [#17806](https://github.com/nodejs/node/pull/17806) +* [[`1d88266543`](https://github.com/nodejs/node/commit/1d88266543)] - **http**: free the parser before emitting 'upgrade' (Luigi Pinca) [#18209](https://github.com/nodejs/node/pull/18209) +* [[`1455b1dec2`](https://github.com/nodejs/node/commit/1455b1dec2)] - **http2**: emit session connect on next tick (Pieter Mees) [#19842](https://github.com/nodejs/node/pull/19842) +* [[`6d6e2e2454`](https://github.com/nodejs/node/commit/6d6e2e2454)] - **http2**: callback valid check before closing request (Trivikram) [#19061](https://github.com/nodejs/node/pull/19061) +* [[`eddf3a6c70`](https://github.com/nodejs/node/commit/eddf3a6c70)] - **http2**: destroy() stream, upon errnoException (Sarat Addepalli) [#19389](https://github.com/nodejs/node/pull/19389) +* [[`e4c10e1201`](https://github.com/nodejs/node/commit/e4c10e1201)] - **http2**: remove some unnecessary next ticks (James M Snell) [#19451](https://github.com/nodejs/node/pull/19451) +* [[`c976cb5be5`](https://github.com/nodejs/node/commit/c976cb5be5)] - **http2**: no stream destroy while its data is on the wire (Anna Henningsen) [#19002](https://github.com/nodejs/node/pull/19002) +* [[`bfd7d6d0de`](https://github.com/nodejs/node/commit/bfd7d6d0de)] - **http2**: fix flaky test-http2-https-fallback (Matteo Collina) [#19093](https://github.com/nodejs/node/pull/19093) +* [[`b75897f982`](https://github.com/nodejs/node/commit/b75897f982)] - **http2**: fix endless loop when writing empty string (Anna Henningsen) [#18924](https://github.com/nodejs/node/pull/18924) +* [[`7e4a9c9fe2`](https://github.com/nodejs/node/commit/7e4a9c9fe2)] - **http2**: use original error for cancelling pending streams (Anna Henningsen) [#18988](https://github.com/nodejs/node/pull/18988) +* [[`2a04f57444`](https://github.com/nodejs/node/commit/2a04f57444)] - **http2**: send error text in case of ALPN mismatch (Anna Henningsen) [#18986](https://github.com/nodejs/node/pull/18986) +* [[`f366373ad8`](https://github.com/nodejs/node/commit/f366373ad8)] - **http2**: fix condition where data is lost (Matteo Collina) [#18895](https://github.com/nodejs/node/pull/18895) +* [[`20fb59fdc4`](https://github.com/nodejs/node/commit/20fb59fdc4)] - **http2**: use `\_final` instead of `on('finish')` (Anna Henningsen) [#18609](https://github.com/nodejs/node/pull/18609) +* [[`ac64b4f6a7`](https://github.com/nodejs/node/commit/ac64b4f6a7)] - **http2**: add checks for server close callback (James M Snell) [#18182](https://github.com/nodejs/node/pull/18182) +* [[`8b0a1b32de`](https://github.com/nodejs/node/commit/8b0a1b32de)] - **http2**: refactor read mechanism (Anna Henningsen) [#18030](https://github.com/nodejs/node/pull/18030) +* [[`a4d910c644`](https://github.com/nodejs/node/commit/a4d910c644)] - **http2**: remember sent headers (James M Snell) [#18045](https://github.com/nodejs/node/pull/18045) +* [[`3cd205431b`](https://github.com/nodejs/node/commit/3cd205431b)] - **http2**: use aliased buffer for perf stats, add stats (James M Snell) [#18020](https://github.com/nodejs/node/pull/18020) +* [[`46d1b331e0`](https://github.com/nodejs/node/commit/46d1b331e0)] - **http2**: verify flood error and unsolicited frames (James M Snell) [#17969](https://github.com/nodejs/node/pull/17969) +* [[`a85518ed22`](https://github.com/nodejs/node/commit/a85518ed22)] - **http2**: verify that a dependency cycle may exist (James M Snell) [#17968](https://github.com/nodejs/node/pull/17968) +* [[`9c85ada4e3`](https://github.com/nodejs/node/commit/9c85ada4e3)] - **http2**: implement maxSessionMemory (James M Snell) [#17967](https://github.com/nodejs/node/pull/17967) +* [[`9a6ea7eb02`](https://github.com/nodejs/node/commit/9a6ea7eb02)] - **http2**: properly handle already closed stream error (James M Snell) [#17942](https://github.com/nodejs/node/pull/17942) +* [[`0078a97793`](https://github.com/nodejs/node/commit/0078a97793)] - **http2**: add aligned padding strategy (James M Snell) [#17938](https://github.com/nodejs/node/pull/17938) +* [[`1c313e09d6`](https://github.com/nodejs/node/commit/1c313e09d6)] - **http2**: add initial support for originSet (James M Snell) [#17935](https://github.com/nodejs/node/pull/17935) +* [[`1a24feccb5`](https://github.com/nodejs/node/commit/1a24feccb5)] - **http2**: add altsvc support (James M Snell) [#17917](https://github.com/nodejs/node/pull/17917) +* [[`c915bc54d4`](https://github.com/nodejs/node/commit/c915bc54d4)] - **http2**: strictly limit number on concurrent streams (James M Snell) [#16766](https://github.com/nodejs/node/pull/16766) +* [[`dcc7f4d84c`](https://github.com/nodejs/node/commit/dcc7f4d84c)] - **http2**: perf\_hooks integration (James M Snell) [#17906](https://github.com/nodejs/node/pull/17906) +* [[`72b42de33a`](https://github.com/nodejs/node/commit/72b42de33a)] - **http2**: implement ref() and unref() on client sessions (Kelvin Jin) [#17620](https://github.com/nodejs/node/pull/17620) +* [[`55f6bdb698`](https://github.com/nodejs/node/commit/55f6bdb698)] - **http2**: keep session objects alive during Http2Scope (Anna Henningsen) [#17863](https://github.com/nodejs/node/pull/17863) +* [[`c61a54ec3d`](https://github.com/nodejs/node/commit/c61a54ec3d)] - **http2**: fix compiling with `--debug-http2` (Anna Henningsen) [#17863](https://github.com/nodejs/node/pull/17863) +* [[`04632214c1`](https://github.com/nodejs/node/commit/04632214c1)] - **http2**: convert Http2Settings to an AsyncWrap (James M Snell) [#17763](https://github.com/nodejs/node/pull/17763) +* [[`ea98fd573e`](https://github.com/nodejs/node/commit/ea98fd573e)] - **http2**: refactor outgoing write mechanism (Anna Henningsen) [#17718](https://github.com/nodejs/node/pull/17718) +* [[`05b823d4ad`](https://github.com/nodejs/node/commit/05b823d4ad)] - **http2**: remove redundant write indirection (Anna Henningsen) [#17718](https://github.com/nodejs/node/pull/17718) +* [[`fc40b7de46`](https://github.com/nodejs/node/commit/fc40b7de46)] - **http2**: cleanup Http2Stream/Http2Session destroy (James M Snell) [#17406](https://github.com/nodejs/node/pull/17406) +* [[`1d65f2b879`](https://github.com/nodejs/node/commit/1d65f2b879)] - **http2**: be sure to destroy the Http2Stream (James M Snell) [#17406](https://github.com/nodejs/node/pull/17406) +* [[`8431b4297c`](https://github.com/nodejs/node/commit/8431b4297c)] - **http2**: only schedule write when necessary (Anna Henningsen) [#17183](https://github.com/nodejs/node/pull/17183) +* [[`38cfb707bd`](https://github.com/nodejs/node/commit/38cfb707bd)] - **http2**: don't call into JS from GC (Anna Henningsen) [#17183](https://github.com/nodejs/node/pull/17183) +* [[`a1539e5731`](https://github.com/nodejs/node/commit/a1539e5731)] - **http2**: simplify onSelectPadding (Anna Henningsen) [#17717](https://github.com/nodejs/node/pull/17717) +* [[`9a4bac2081`](https://github.com/nodejs/node/commit/9a4bac2081)] - **http2,perf_hooks**: perf state using AliasedBuffer (Kyle Farnung) [#18300](https://github.com/nodejs/node/pull/18300) +* [[`9129bc4fde`](https://github.com/nodejs/node/commit/9129bc4fde)] - **lib**: set process.execPath on OpenBSD (Aaron Bieber) [#18543](https://github.com/nodejs/node/pull/18543) +* [[`2019b023a7`](https://github.com/nodejs/node/commit/2019b023a7)] - **lib**: refactor ES module loader for readability (Anna Henningsen) [#16579](https://github.com/nodejs/node/pull/16579) +* [[`3df0570c90`](https://github.com/nodejs/node/commit/3df0570c90)] - **lib**: fix spelling in comments (Tobias Nießen) [#18018](https://github.com/nodejs/node/pull/18018) +* [[`20844d1716`](https://github.com/nodejs/node/commit/20844d1716)] - **lib**: remove debugger dead code (Qingyan Li) [#18426](https://github.com/nodejs/node/pull/18426) +* [[`07a6770614`](https://github.com/nodejs/node/commit/07a6770614)] - **n-api**: add more `int64\_t` tests (Kyle Farnung) [#19402](https://github.com/nodejs/node/pull/19402) +* [[`8b3ef4660a`](https://github.com/nodejs/node/commit/8b3ef4660a)] - **n-api**: back up env before finalize (Gabriel Schulhof) [#19718](https://github.com/nodejs/node/pull/19718) +* [[`92f699e021`](https://github.com/nodejs/node/commit/92f699e021)] - **n-api**: ensure in-module exceptions are propagated (Gabriel Schulhof) [#19537](https://github.com/nodejs/node/pull/19537) +* [[`367113f5d7`](https://github.com/nodejs/node/commit/367113f5d7)] - **n-api**: bump version of n-api supported (Michael Dawson) [#19497](https://github.com/nodejs/node/pull/19497) +* [[`24b8bb6708`](https://github.com/nodejs/node/commit/24b8bb6708)] - **n-api**: re-write test\_make\_callback (Gabriel Schulhof) [#19448](https://github.com/nodejs/node/pull/19448) +* [[`3a6b7e610d`](https://github.com/nodejs/node/commit/3a6b7e610d)] - **n-api**: add napi\_fatal\_exception (Mathias Buus) [#19337](https://github.com/nodejs/node/pull/19337) +* [[`9949d55ae9`](https://github.com/nodejs/node/commit/9949d55ae9)] - **n-api**: separate out async\_hooks test (Gabriel Schulhof) [#19392](https://github.com/nodejs/node/pull/19392) +* [[`f29d8e0e8d`](https://github.com/nodejs/node/commit/f29d8e0e8d)] - **n-api**: add missing exception checking (Michael Dawson) [#19362](https://github.com/nodejs/node/pull/19362) +* [[`faf94b1c49`](https://github.com/nodejs/node/commit/faf94b1c49)] - **n-api**: resolve promise in test (Gabriel Schulhof) [#19245](https://github.com/nodejs/node/pull/19245) +* [[`df63adf7aa`](https://github.com/nodejs/node/commit/df63adf7aa)] - **n-api**: update documentation (Gabriel Schulhof) [#19078](https://github.com/nodejs/node/pull/19078) +* [[`b26410e86f`](https://github.com/nodejs/node/commit/b26410e86f)] - **n-api**: update reference test (Gabriel Schulhof) [#19086](https://github.com/nodejs/node/pull/19086) +* [[`cb3f90a1a9`](https://github.com/nodejs/node/commit/cb3f90a1a9)] - **n-api**: fix object test (Gabriel Schulhof) [#19039](https://github.com/nodejs/node/pull/19039) +* [[`9244e1d234`](https://github.com/nodejs/node/commit/9244e1d234)] - **n-api**: remove extra reference from test (Gabriel Schulhof) [#18542](https://github.com/nodejs/node/pull/18542) +* [[`927fc0b19f`](https://github.com/nodejs/node/commit/927fc0b19f)] - **n-api**: add methods to open/close callback scope (Michael Dawson) [#18089](https://github.com/nodejs/node/pull/18089) +* [[`969a520990`](https://github.com/nodejs/node/commit/969a520990)] - **n-api**: wrap control flow macro in do/while (Ben Noordhuis) [#18532](https://github.com/nodejs/node/pull/18532) +* [[`d89f5937eb`](https://github.com/nodejs/node/commit/d89f5937eb)] - **n-api**: implement wrapping using private properties (Gabriel Schulhof) [#18311](https://github.com/nodejs/node/pull/18311) +* [[`af655f586c`](https://github.com/nodejs/node/commit/af655f586c)] - **n-api**: change assert ok check to notStrictEqual. (Aaron Kau) [#18414](https://github.com/nodejs/node/pull/18414) +* [[`ca10fda064`](https://github.com/nodejs/node/commit/ca10fda064)] - **n-api**: throw RangeError napi\_create\_typedarray() (Jinho Bang) [#18037](https://github.com/nodejs/node/pull/18037) +* [[`853b4d593c`](https://github.com/nodejs/node/commit/853b4d593c)] - **n-api**: expose n-api version in process.versions (Michael Dawson) [#18067](https://github.com/nodejs/node/pull/18067) +* [[`48be8a4793`](https://github.com/nodejs/node/commit/48be8a4793)] - **n-api**: throw RangeError in napi\_create\_dataview() with invalid range (Jinho Bang) [#17869](https://github.com/nodejs/node/pull/17869) +* [[`a744535f99`](https://github.com/nodejs/node/commit/a744535f99)] - **n-api**: fix memory leak in napi\_async\_destroy() (alnyan) [#17714](https://github.com/nodejs/node/pull/17714) +* [[`584fadc605`](https://github.com/nodejs/node/commit/584fadc605)] - **n-api,test**: add int64 bounds tests (Kyle Farnung) [#19309](https://github.com/nodejs/node/pull/19309) +* [[`4c1181dc02`](https://github.com/nodejs/node/commit/4c1181dc02)] - **n-api,test**: add a new.target test to addons-napi (Taylor Woll) [#19236](https://github.com/nodejs/node/pull/19236) +* [[`3225601ffc`](https://github.com/nodejs/node/commit/3225601ffc)] - **net**: remove Socket.prototoype.read (Anna Henningsen) [#18568](https://github.com/nodejs/node/pull/18568) +* [[`35aaee1059`](https://github.com/nodejs/node/commit/35aaee1059)] - **net**: remove redundant code from \_writeGeneric() (Luigi Pinca) [#18429](https://github.com/nodejs/node/pull/18429) +* [[`54442efcd2`](https://github.com/nodejs/node/commit/54442efcd2)] - **perf_hooks**: refactor internals (James M Snell) [#17822](https://github.com/nodejs/node/pull/17822) +* [[`6bdfb1f8f0`](https://github.com/nodejs/node/commit/6bdfb1f8f0)] - **perf_hooks,http2**: add performance.clear() (James M Snell) [#18046](https://github.com/nodejs/node/pull/18046) +* [[`1faae90b74`](https://github.com/nodejs/node/commit/1faae90b74)] - **readline**: use Date.now() and move test to parallel (Anatoli Papirovski) [#18563](https://github.com/nodejs/node/pull/18563) +* [[`965b56a34e`](https://github.com/nodejs/node/commit/965b56a34e)] - **readline**: update references to archived repository (Tobias Nießen) [#17924](https://github.com/nodejs/node/pull/17924) +* [[`801a49935b`](https://github.com/nodejs/node/commit/801a49935b)] - **src**: add nullptr check for session in DEBUG macro (Daniel Bevenius) [#18815](https://github.com/nodejs/node/pull/18815) +* [[`4e807d648e`](https://github.com/nodejs/node/commit/4e807d648e)] - **src**: introduce internal buffer slice constructor (Anna Henningsen) [#18030](https://github.com/nodejs/node/pull/18030) +* [[`0b828e5125`](https://github.com/nodejs/node/commit/0b828e5125)] - **src**: remove declarations for missing functions (Anna Henningsen) [#18134](https://github.com/nodejs/node/pull/18134) +* [[`3766e04f31`](https://github.com/nodejs/node/commit/3766e04f31)] - **src**: silence http2 -Wunused-result warnings (cjihrig) [#17954](https://github.com/nodejs/node/pull/17954) +* [[`2b7732788a`](https://github.com/nodejs/node/commit/2b7732788a)] - **src**: add optional keep-alive object to SetImmediate (Anna Henningsen) [#17183](https://github.com/nodejs/node/pull/17183) +* [[`f3e082c4ea`](https://github.com/nodejs/node/commit/f3e082c4ea)] - **src**: replace SetAccessor w/ SetAccessorProperty (Jure Triglav) [#17665](https://github.com/nodejs/node/pull/17665) +* [[`45e28a8628`](https://github.com/nodejs/node/commit/45e28a8628)] - **src**: minor refactoring to StreamBase writes (Anna Henningsen) [#17564](https://github.com/nodejs/node/pull/17564) +* [[`42b4f3ce0b`](https://github.com/nodejs/node/commit/42b4f3ce0b)] - **src**: fix abort when taking a heap snapshot (Ben Noordhuis) [#18898](https://github.com/nodejs/node/pull/18898) +* [[`b48ca0a140`](https://github.com/nodejs/node/commit/b48ca0a140)] - **src**: fix crypto.pbkdf2 callback error argument (BufoViridis) [#18458](https://github.com/nodejs/node/pull/18458) +* [[`973488b77b`](https://github.com/nodejs/node/commit/973488b77b)] - **src**: replace var for let / const. (alejandro estrada) [#18649](https://github.com/nodejs/node/pull/18649) +* [[`9ac91b14de`](https://github.com/nodejs/node/commit/9ac91b14de)] - **src**: fix util abort (Ruben Bridgewater) [#19224](https://github.com/nodejs/node/pull/19224) +* [[`c0f40be23b`](https://github.com/nodejs/node/commit/c0f40be23b)] - **src**: free memory before re-setting URLHost value (Ivan Filenko) [#18357](https://github.com/nodejs/node/pull/18357) +* [[`5e4f9b37ba`](https://github.com/nodejs/node/commit/5e4f9b37ba)] - **src,doc,test**: Fix common misspellings (Roman Reiss) [#18151](https://github.com/nodejs/node/pull/18151) +* [[`10231a9e44`](https://github.com/nodejs/node/commit/10231a9e44)] - **stream**: cleanup() when unpiping all streams. (陈刚) [#18266](https://github.com/nodejs/node/pull/18266) +* [[`bf523822ba`](https://github.com/nodejs/node/commit/bf523822ba)] - **stream**: simplify `src.\_readableState` to `state` (陈刚) [#18264](https://github.com/nodejs/node/pull/18264) +* [[`37e594ed4a`](https://github.com/nodejs/node/commit/37e594ed4a)] - **stream**: remove unreachable code (Luigi Pinca) [#18239](https://github.com/nodejs/node/pull/18239) +* [[`f96b0bf494`](https://github.com/nodejs/node/commit/f96b0bf494)] - **string_decoder**: reset decoder on end (Justin Ridgewell) [#18494](https://github.com/nodejs/node/pull/18494) +* [[`4dbdb8ae4e`](https://github.com/nodejs/node/commit/4dbdb8ae4e)] - **test**: http2 errors on req.close() (Trivikram) [#18854](https://github.com/nodejs/node/pull/18854) +* [[`83d8ad351c`](https://github.com/nodejs/node/commit/83d8ad351c)] - **test**: http2 stream.respond() error checks (Trivikram) [#18861](https://github.com/nodejs/node/pull/18861) +* [[`b0664426f5`](https://github.com/nodejs/node/commit/b0664426f5)] - **test**: check endless loop while writing empty string (XadillaX) [#18924](https://github.com/nodejs/node/pull/18924) +* [[`7eba62e028`](https://github.com/nodejs/node/commit/7eba62e028)] - **test**: make test-tls-external-accessor agnostic (Rich Trott) [#16272](https://github.com/nodejs/node/pull/16272) +* [[`9e68947ff4`](https://github.com/nodejs/node/commit/9e68947ff4)] - **test**: add hasCrypto when using binding('crypto') (Daniel Bevenius) [#17867](https://github.com/nodejs/node/pull/17867) +* [[`6129ff4b99`](https://github.com/nodejs/node/commit/6129ff4b99)] - **test**: remove unnecessary timer (cjihrig) [#18719](https://github.com/nodejs/node/pull/18719) +* [[`2838f9b150`](https://github.com/nodejs/node/commit/2838f9b150)] - **test**: convert new tests to use error types (Jack Horton) [#18581](https://github.com/nodejs/node/pull/18581) +* [[`5c0983e5a2`](https://github.com/nodejs/node/commit/5c0983e5a2)] - **test**: improve error message output (Bhavani Shankar) [#18498](https://github.com/nodejs/node/pull/18498) +* [[`bebcdfe382`](https://github.com/nodejs/node/commit/bebcdfe382)] - **test**: show pending exception error in napi tests (Ben Wilcox) [#18413](https://github.com/nodejs/node/pull/18413) +* [[`5b1b74c5a5`](https://github.com/nodejs/node/commit/5b1b74c5a5)] - **test**: refactor addons-napi/test\_exception/test.js (Rich Trott) [#18340](https://github.com/nodejs/node/pull/18340) +* [[`8cfa87832d`](https://github.com/nodejs/node/commit/8cfa87832d)] - **test**: fixed typos in napi test (furstenheim) [#18148](https://github.com/nodejs/node/pull/18148) +* [[`ad8c079af7`](https://github.com/nodejs/node/commit/ad8c079af7)] - **test**: remove ambiguous error messages from test\_error (Nicholas Drane) [#17812](https://github.com/nodejs/node/pull/17812) +* [[`2e100c82be`](https://github.com/nodejs/node/commit/2e100c82be)] - **test**: remove literals that obscure assert messages (Rich Trott) [#17642](https://github.com/nodejs/node/pull/17642) +* [[`077e1870ae`](https://github.com/nodejs/node/commit/077e1870ae)] - **test**: add unhandled rejection guard (babygoat) [#17275](https://github.com/nodejs/node/pull/17275) +* [[`9236332cc3`](https://github.com/nodejs/node/commit/9236332cc3)] - **test**: update a few tests to work on OpenBSD (Aaron Bieber) [#18543](https://github.com/nodejs/node/pull/18543) +* [[`cbd698a521`](https://github.com/nodejs/node/commit/cbd698a521)] - **test**: refactor test-http-abort-before-end (cjihrig) [#18508](https://github.com/nodejs/node/pull/18508) +* [[`ab8edc9d48`](https://github.com/nodejs/node/commit/ab8edc9d48)] - **test**: fix flaky timers-block-eventloop test (Anatoli Papirovski) [#18567](https://github.com/nodejs/node/pull/18567) +* [[`53b702fdba`](https://github.com/nodejs/node/commit/53b702fdba)] - **test**: remove common.PORT from parallel tests (Rich Trott) [#17410](https://github.com/nodejs/node/pull/17410) +* [[`da162278de`](https://github.com/nodejs/node/commit/da162278de)] - **test**: mock the lookup function in parallel tests (Joyee Cheung) [#17296](https://github.com/nodejs/node/pull/17296) +* [[`34af49401b`](https://github.com/nodejs/node/commit/34af49401b)] - **test**: add common.dns.errorLookupMock (Joyee Cheung) [#17296](https://github.com/nodejs/node/pull/17296) +* [[`bff7258535`](https://github.com/nodejs/node/commit/bff7258535)] - **test**: do not check TXT content in test-dns-any (Joyee Cheung) [#18547](https://github.com/nodejs/node/pull/18547) +* [[`daeb6de8ec`](https://github.com/nodejs/node/commit/daeb6de8ec)] - **test**: use internet.addresses in internet tests (Joyee Cheung) [#16390](https://github.com/nodejs/node/pull/16390) +* [[`7813a0de0a`](https://github.com/nodejs/node/commit/7813a0de0a)] - **test**: introduce test/common/internet.addresses (Joyee Cheung) [#16390](https://github.com/nodejs/node/pull/16390) +* [[`745600a0b3`](https://github.com/nodejs/node/commit/745600a0b3)] - **test**: remove orphaned entries from status (Kyle Farnung) [#18092](https://github.com/nodejs/node/pull/18092) +* [[`e42ab10957`](https://github.com/nodejs/node/commit/e42ab10957)] - **test**: add assertions for TextEncoder/Decoder (Sho Miyamoto) [#18132](https://github.com/nodejs/node/pull/18132) +* [[`a1b0d5da07`](https://github.com/nodejs/node/commit/a1b0d5da07)] - **test**: move tmpdir to submodule of common (Rich Trott) [#17856](https://github.com/nodejs/node/pull/17856) +* [[`5155d8e7c3`](https://github.com/nodejs/node/commit/5155d8e7c3)] - **test**: update references to archived repository (Tobias Nießen) [#17924](https://github.com/nodejs/node/pull/17924) +* [[`1043b6fd7c`](https://github.com/nodejs/node/commit/1043b6fd7c)] - **test**: fix spelling in test case comments (Tobias Nießen) [#18018](https://github.com/nodejs/node/pull/18018) +* [[`fdb4dbc04b`](https://github.com/nodejs/node/commit/fdb4dbc04b)] - **test**: remove destructor from node\_test\_fixture (Daniel Bevenius) [#18524](https://github.com/nodejs/node/pull/18524) +* [[`e55e08c80e`](https://github.com/nodejs/node/commit/e55e08c80e)] - **test**: verify the shell option works properly on execFile (jvelezpo) [#18384](https://github.com/nodejs/node/pull/18384) +* [[`0cf9d0483f`](https://github.com/nodejs/node/commit/0cf9d0483f)] - **test**: add test for tls benchmarks (Anatoli Papirovski) [#18489](https://github.com/nodejs/node/pull/18489) +* [[`d630874250`](https://github.com/nodejs/node/commit/d630874250)] - **test**: speed up parallel/test-tls-session-cache (Anna Henningsen) [#18424](https://github.com/nodejs/node/pull/18424) +* [[`a1fb263880`](https://github.com/nodejs/node/commit/a1fb263880)] - **test**: fix flaky test-http-dns-error (Bryan English) [#16534](https://github.com/nodejs/node/pull/16534) +* [[`6f3fb46541`](https://github.com/nodejs/node/commit/6f3fb46541)] - **test**: use correct size in test-stream-buffer-list (Luigi Pinca) [#18239](https://github.com/nodejs/node/pull/18239) +* [[`a52f15efae`](https://github.com/nodejs/node/commit/a52f15efae)] - **timers**: fix a bug in error handling (Anatoli Papirovski) [#20497](https://github.com/nodejs/node/pull/20497) +* [[`e15f57745d`](https://github.com/nodejs/node/commit/e15f57745d)] - **timers**: allow Immediates to be unrefed (Anatoli Papirovski) [#18139](https://github.com/nodejs/node/pull/18139) +* [[`95c1e2d606`](https://github.com/nodejs/node/commit/95c1e2d606)] - **tls**: set servername on client side too (James M Snell) [#17935](https://github.com/nodejs/node/pull/17935) +* [[`d4bccccf23`](https://github.com/nodejs/node/commit/d4bccccf23)] - **tools**: add fixer for prefer-assert-iferror.js (Shobhit Chittora) [#16648](https://github.com/nodejs/node/pull/16648) +* [[`016a28ac08`](https://github.com/nodejs/node/commit/016a28ac08)] - **tools**: non-Ascii linter for /lib only (Sarat Addepalli) [#18043](https://github.com/nodejs/node/pull/18043) +* [[`a0a45fc3b6`](https://github.com/nodejs/node/commit/a0a45fc3b6)] - **tools**: add .mjs linting for Windows (Vse Mozhet Byt) [#18569](https://github.com/nodejs/node/pull/18569) +* [[`e0d2842b29`](https://github.com/nodejs/node/commit/e0d2842b29)] - **tools**: add check for using process.binding crypto (Daniel Bevenius) [#17867](https://github.com/nodejs/node/pull/17867) +* [[`a8b5a96d15`](https://github.com/nodejs/node/commit/a8b5a96d15)] - **tools**: auto fix custom eslint rule (Shobhit Chittora) [#16652](https://github.com/nodejs/node/pull/16652) +* [[`5d03c8219a`](https://github.com/nodejs/node/commit/5d03c8219a)] - **url**: simplify loop in parser (Tobias Nießen) [#18468](https://github.com/nodejs/node/pull/18468) +* [[`e0e0ef7bab`](https://github.com/nodejs/node/commit/e0e0ef7bab)] - **util**: escaping object keys in util.inspect() (buji) [#16986](https://github.com/nodejs/node/pull/16986) +* [[`8ac69c457b`](https://github.com/nodejs/node/commit/8ac69c457b)] - **v8**: add missing ',' in OpenBSD's 'sources' section. (Aaron Bieber) [#18448](https://github.com/nodejs/node/pull/18448) +* [[`c61754fad9`](https://github.com/nodejs/node/commit/c61754fad9)] - **win, build**: fix intl-none option (Birunthan Mohanathas) [#18292](https://github.com/nodejs/node/pull/18292) + ## 2018-03-29, Version 8.11.1 'Carbon' (LTS), @MylesBorins diff --git a/src/node_version.h b/src/node_version.h index 4d5113766807f1..80125c2384e083 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -29,7 +29,7 @@ #define NODE_VERSION_IS_LTS 1 #define NODE_VERSION_LTS_CODENAME "Carbon" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)