diff --git a/.gitignore b/.gitignore index 7be817acce2349..ebeedeac6b8fe0 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ deps/v8/src/Debug/ deps/v8/src/Release/ deps/v8/src/inspector/Debug/ deps/v8/src/inspector/Release/ +deps/v8/third_party/eu-strip/ diff --git a/Makefile b/Makefile index 6736d601685dce..b3e65cec6030a9 100644 --- a/Makefile +++ b/Makefile @@ -652,7 +652,7 @@ tools/doc/node_modules/js-yaml/package.json: gen-json = tools/doc/generate.js --format=json $< > $@ gen-html = tools/doc/generate.js --node-version=$(FULLVERSION) --format=html \ - --template=doc/template.html --analytics=$(DOCS_ANALYTICS) $< > $@ + --analytics=$(DOCS_ANALYTICS) $< > $@ out/doc/api/%.json: doc/api/%.md $(call available-node, $(gen-json)) diff --git a/README.md b/README.md index d39c7639e20d1a..ef733c277619b5 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ search these unofficial resources: * [Questions tagged 'node.js' on StackOverflow][] * [#node.js channel on chat.freenode.net][]. See for more information. +* [Node.js Slack Community](https://node-js.slack.com/): Visit + [nodeslackers.com](http://www.nodeslackers.com/) to register. GitHub issues are meant for tracking enhancements and bugs, not general support. diff --git a/benchmark/http/headers.js b/benchmark/http/headers.js new file mode 100644 index 00000000000000..748865afbf3a04 --- /dev/null +++ b/benchmark/http/headers.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common.js'); +const http = require('http'); + +const bench = common.createBenchmark(main, { + duplicates: [1, 100], + n: [10, 1000], +}); + +function main({ duplicates, n }) { + const headers = { + 'Connection': 'keep-alive', + 'Transfer-Encoding': 'chunked', + }; + + for (var i = 0; i < n / duplicates; i++) { + headers[`foo${i}`] = []; + for (var j = 0; j < duplicates; j++) { + headers[`foo${i}`].push(`some header value ${i}`); + } + } + + const server = http.createServer(function(req, res) { + res.writeHead(200, headers); + res.end(); + }); + server.listen(common.PORT, function() { + bench.http({ + path: '/', + connections: 10 + }, function() { + server.close(); + }); + }); +} diff --git a/benchmark/zlib/pipe.js b/benchmark/zlib/pipe.js new file mode 100644 index 00000000000000..9b05749bbb824d --- /dev/null +++ b/benchmark/zlib/pipe.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common.js'); +const fs = require('fs'); +const zlib = require('zlib'); + +const bench = common.createBenchmark(main, { + inputLen: [1024], + duration: [5], + type: ['string', 'buffer'] +}); + +function main({ inputLen, duration, type }) { + const buffer = Buffer.alloc(inputLen, fs.readFileSync(__filename)); + const chunk = type === 'buffer' ? buffer : buffer.toString('utf8'); + + const input = zlib.createGzip(); + const output = zlib.createGunzip(); + + let readFromOutput = 0; + input.pipe(output); + if (type === 'string') + output.setEncoding('utf8'); + output.on('data', (chunk) => readFromOutput += chunk.length); + + function write() { + input.write(chunk, write); + } + + bench.start(); + write(); + + setTimeout(() => { + // Give result in GBit/s, like the net benchmarks do + bench.end(readFromOutput * 8 / (1024 ** 3)); + + // Cut off writing the easy way. + input.write = () => {}; + }, duration * 1000); +} diff --git a/configure b/configure index c1170f9a132a16..17e13a48d47e5a 100755 --- a/configure +++ b/configure @@ -501,11 +501,6 @@ parser.add_option('--without-node-options', dest='without_node_options', help='build without NODE_OPTIONS support') -parser.add_option('--xcode', - action='store_true', - dest='use_xcode', - help='generate build files for use with xcode') - parser.add_option('--ninja', action='store_true', dest='use_ninja', @@ -1005,9 +1000,6 @@ def configure_node(o): o['variables']['asan'] = int(options.enable_asan or 0) - if options.use_xcode and options.use_ninja: - raise Exception('--xcode and --ninja cannot be used together.') - if options.coverage: o['variables']['coverage'] = 'true' else: @@ -1530,7 +1522,6 @@ write('config.gypi', do_not_edit + config = { 'BUILDTYPE': 'Debug' if options.debug else 'Release', - 'USE_XCODE': str(int(options.use_xcode or 0)), 'PYTHON': sys.executable, 'NODE_TARGET_TYPE': variables['node_target_type'], } @@ -1549,9 +1540,7 @@ write('config.mk', do_not_edit + config) gyp_args = ['--no-parallel'] -if options.use_xcode: - gyp_args += ['-f', 'xcode'] -elif options.use_ninja: +if options.use_ninja: gyp_args += ['-f', 'ninja'] elif flavor == 'win' and sys.platform != 'msys': gyp_args += ['-f', 'msvs', '-G', 'msvs_version=auto'] diff --git a/deps/v8/third_party/eu-strip/README.v8 b/deps/v8/third_party/eu-strip/README.v8 deleted file mode 100644 index e84974d92b9cec..00000000000000 --- a/deps/v8/third_party/eu-strip/README.v8 +++ /dev/null @@ -1,24 +0,0 @@ -Name: eu-strip -URL: https://sourceware.org/elfutils/ -Version: 0.158 -Security Critical: no -License: LGPL 3 -License File: NOT_SHIPPED - -Description: - -Patched eu-strip from elfutils. - -Build instructions (on Trusty; note that this will build the -Ubuntu-patched version of elfutils): -$ mkdir elfutils -$ cd elfutils -$ apt-get source elfutils -$ cd elfutils-0.158 -[ Edit libelf/elf_end.c and remove the free() on line 164. ] -$ ./configure -$ make -$ gcc -std=gnu99 -Wall -Wshadow -Wunused -Wextra -fgnu89-inline - -Wformat=2 -Werror -g -O2 -Wl,-rpath-link,libelf:libdw -o eu-strip - src/strip.o libebl/libebl.a libelf/libelf.a lib/libeu.a -ldl -$ eu-strip ./eu-strip # Keep the binary small, please. diff --git a/deps/v8/third_party/eu-strip/bin/eu-strip b/deps/v8/third_party/eu-strip/bin/eu-strip deleted file mode 100755 index 994e2263b9185f..00000000000000 Binary files a/deps/v8/third_party/eu-strip/bin/eu-strip and /dev/null differ diff --git a/doc/api/addons.md b/doc/api/addons.md index 3c4c7b39bedfeb..5828574893a89d 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -217,7 +217,6 @@ Addon developers are recommended to use to keep compatibility between past and future releases of V8 and Node.js. See the `nan` [examples][] for an illustration of how it can be used. - ## N-API > Stability: 1 - Experimental @@ -307,7 +306,6 @@ built using `node-gyp`: $ node-gyp configure build ``` - ### Function arguments Addons will typically expose objects and functions that can be accessed from @@ -381,7 +379,6 @@ const addon = require('./build/Release/addon'); console.log('This should be eight:', addon.add(3, 5)); ``` - ### Callbacks It is common practice within Addons to pass JavaScript functions to a C++ @@ -488,7 +485,6 @@ console.log(obj1.msg, obj2.msg); // Prints: 'hello world' ``` - ### Function factory Another common scenario is creating JavaScript functions that wrap C++ @@ -546,7 +542,6 @@ console.log(fn()); // Prints: 'hello world' ``` - ### Wrapping C++ objects It is also possible to wrap C++ objects/classes in a way that allows new @@ -713,6 +708,13 @@ console.log(obj.plusOne()); // Prints: 13 ``` +The garbage collector can execute forcefully using V8 command line flags +` --gc_global ` and ` --gc_interval `, where ` --gc_global ` forces V8 to +perform a full garbage collection and ` --gc_interval ` forces V8 to +perform garbage collection after a given amount of allocations. Although, +it is recommended to limit V8 command line flags for testing purposes +only, since these are primarily debug flags. + ### Factory of wrapped objects Alternatively, it is possible to use a factory pattern to avoid explicitly @@ -916,7 +918,6 @@ console.log(obj2.plusOne()); // Prints: 23 ``` - ### Passing wrapped objects around In addition to wrapping and returning C++ objects, it is possible to pass diff --git a/doc/api/assert.md b/doc/api/assert.md index ab4aa9c7cd2499..468293b208a90d 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -102,31 +102,25 @@ It can be accessed using: const assert = require('assert').strict; ``` -Example error diff (the `expected`, `actual`, and `Lines skipped` will be on a -single row): +Example error diff: ```js const assert = require('assert').strict; assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); -``` - -```diff -AssertionError [ERR_ASSERTION]: Input A expected to deepStrictEqual input B: -+ expected -- actual -... Lines skipped - - [ - [ -... - 2, -- 3 -+ '3' - ], -... - 5 - ] +// AssertionError: Input A expected to strictly deep-equal input B: +// + expected - actual ... Lines skipped +// +// [ +// [ +// ... +// 2, +// - 3 +// + '3' +// ], +// ... +// 5 +// ] ``` To deactivate the colors, use the `NODE_DISABLE_COLORS` environment variable. @@ -319,9 +313,14 @@ are recursively evaluated also by the following rules. ```js const assert = require('assert').strict; +// This fails because 1 !== '1'. assert.deepStrictEqual({ a: 1 }, { a: '1' }); -// AssertionError: { a: 1 } deepStrictEqual { a: '1' } -// because 1 !== '1' using SameValue comparison +// AssertionError: Input A expected to strictly deep-equal input B: +// + expected - actual +// { +// - a: 1 +// + a: '1' +// } // The following objects don't have own properties const date = new Date(); @@ -329,33 +328,52 @@ const object = {}; const fakeDate = {}; Object.setPrototypeOf(fakeDate, Date.prototype); +// Different [[Prototype]]: assert.deepStrictEqual(object, fakeDate); -// AssertionError: {} deepStrictEqual Date {} -// Different [[Prototype]] +// AssertionError: Input A expected to strictly deep-equal input B: +// + expected - actual +// - {} +// + Date {} +// Different type tags: assert.deepStrictEqual(date, fakeDate); -// AssertionError: 2017-03-11T14:25:31.849Z deepStrictEqual Date {} -// Different type tags +// AssertionError: Input A expected to strictly deep-equal input B: +// + expected - actual +// - 2018-04-26T00:49:08.604Z +// + Date {} assert.deepStrictEqual(NaN, NaN); // OK, because of the SameValue comparison +// Different unwrapped numbers: assert.deepStrictEqual(new Number(1), new Number(2)); -// Fails because the wrapped number is unwrapped and compared as well. +// AssertionError: Input A expected to strictly deep-equal input B: +// + expected - actual +// - [Number: 1] +// + [Number: 2] + assert.deepStrictEqual(new String('foo'), Object('foo')); // OK because the object and the string are identical when unwrapped. assert.deepStrictEqual(-0, -0); // OK + +// Different zeros using the SameValue Comparison: assert.deepStrictEqual(0, -0); -// AssertionError: 0 deepStrictEqual -0 +// AssertionError: Input A expected to strictly deep-equal input B: +// + expected - actual +// - 0 +// + -0 const symbol1 = Symbol(); const symbol2 = Symbol(); assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol1]: 1 }); // OK, because it is the same symbol on both objects. assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 }); -// Fails because symbol1 !== symbol2! +// AssertionError [ERR_ASSERTION]: Input objects not identical: +// { +// [Symbol()]: 1 +// } const weakMap1 = new WeakMap(); const weakMap2 = new WeakMap([[{}, {}]]); @@ -364,8 +382,16 @@ weakMap3.unequal = true; assert.deepStrictEqual(weakMap1, weakMap2); // OK, because it is impossible to compare the entries + +// Fails because weakMap3 has a property that weakMap1 does not contain: assert.deepStrictEqual(weakMap1, weakMap3); -// Fails because weakMap3 has a property that weakMap1 does not contain! +// AssertionError: Input A expected to strictly deep-equal input B: +// + expected - actual +// WeakMap { +// - [items unknown] +// + [items unknown], +// + unequal: true +// } ``` If the values are not equal, an `AssertionError` is thrown with a `message` @@ -639,7 +665,9 @@ changes: * `value` {any} Throws `value` if `value` is not `undefined` or `null`. This is useful when -testing the `error` argument in callbacks. +testing the `error` argument in callbacks. The stack trace contains all frames +from the error passed to `ifError()` including the potential new frames for +`ifError()` itself. See below for an example. ```js const assert = require('assert').strict; @@ -652,6 +680,19 @@ assert.ifError('error'); // AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 'error' assert.ifError(new Error()); // AssertionError [ERR_ASSERTION]: ifError got unwanted exception: Error + +// Create some random error frames. +let err; +(function errorFrame() { + err = new Error('test error'); +})(); + +(function ifErrorFrame() { + assert.ifError(err); +})(); +// AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error +// at ifErrorFrame +// at errorFrame ``` ## assert.notDeepEqual(actual, expected[, message]) @@ -834,7 +875,7 @@ assert.notStrictEqual(1, 2); // OK assert.notStrictEqual(1, 1); -// AssertionError: 1 notStrictEqual 1 +// AssertionError [ERR_ASSERTION]: Identical input passed to notStrictEqual: 1 assert.notStrictEqual(1, '1'); // OK @@ -880,40 +921,34 @@ assert.ok(1); // OK assert.ok(); -// throws: -// "AssertionError: No value argument passed to `assert.ok`. +// AssertionError: No value argument passed to `assert.ok()` assert.ok(false, 'it\'s false'); -// throws "AssertionError: it's false" +// AssertionError: it's false // In the repl: assert.ok(typeof 123 === 'string'); -// throws: -// "AssertionError: false == true +// AssertionError: false == true // In a file (e.g. test.js): assert.ok(typeof 123 === 'string'); -// throws: -// "AssertionError: The expression evaluated to a falsy value: +// AssertionError: The expression evaluated to a falsy value: // // assert.ok(typeof 123 === 'string') assert.ok(false); -// throws: -// "AssertionError: The expression evaluated to a falsy value: +// AssertionError: The expression evaluated to a falsy value: // // assert.ok(false) assert.ok(0); -// throws: -// "AssertionError: The expression evaluated to a falsy value: +// AssertionError: The expression evaluated to a falsy value: // // assert.ok(0) // Using `assert()` works the same: assert(0); -// throws: -// "AssertionError: The expression evaluated to a falsy value: +// AssertionError: The expression evaluated to a falsy value: // // assert(0) ``` @@ -995,13 +1030,19 @@ determined by the [SameValue Comparison][]. const assert = require('assert').strict; assert.strictEqual(1, 2); -// AssertionError: 1 strictEqual 2 +// AssertionError [ERR_ASSERTION]: Input A expected to strictly equal input B: +// + expected - actual +// - 1 +// + 2 assert.strictEqual(1, 1); // OK assert.strictEqual(1, '1'); -// AssertionError: 1 strictEqual '1' +// AssertionError [ERR_ASSERTION]: Input A expected to strictly equal input B: +// + expected - actual +// - 1 +// + '1' ``` If the values are not strictly equal, an `AssertionError` is thrown with a @@ -1035,6 +1076,34 @@ each property will be tested for including the non-enumerable `message` and If specified, `message` will be the message provided by the `AssertionError` if the block fails to throw. +Custom error object / error instance: + +```js +const err = new TypeError('Wrong value'); +err.code = 404; + +assert.throws( + () => { + throw err; + }, + { + name: 'TypeError', + message: 'Wrong value' + // Note that only properties on the error object will be tested! + } +); + +// Fails due to the different `message` and `name` properties: +assert.throws( + () => { + const otherErr = new Error('Not found'); + otherErr.code = 404; + throw otherErr; + }, + err // This tests for `message`, `name` and `code`. +); +``` + Validate instanceof using constructor: ```js @@ -1076,39 +1145,12 @@ assert.throws( ); ``` -Custom error object / error instance: - -```js -const err = new TypeError('Wrong value'); -err.code = 404; - -assert.throws( - () => { - throw err; - }, - { - name: 'TypeError', - message: 'Wrong value' - // Note that only properties on the error object will be tested! - } -); - -// Fails due to the different `message` and `name` properties: -assert.throws( - () => { - const otherErr = new Error('Not found'); - otherErr.code = 404; - throw otherErr; - }, - err // This tests for `message`, `name` and `code`. -); -``` - Note that `error` cannot be a string. If a string is provided as the second argument, then `error` is assumed to be omitted and the string will be used for -`message` instead. This can lead to easy-to-miss mistakes. Please read the -example below carefully if using a string as the second argument gets -considered: +`message` instead. This can lead to easy-to-miss mistakes. Using the same +message as the thrown error message is going to result in an +`ERR_AMBIGUOUS_ARGUMENT` error. Please read the example below carefully if using +a string as the second argument gets considered: ```js @@ -1121,10 +1163,15 @@ function throwingSecond() { function notThrowing() {} // The second argument is a string and the input function threw an Error. -// In that case both cases do not throw as neither is going to try to -// match for the error message thrown by the input function! +// The first case will not throw as it does not match for the error message +// thrown by the input function! assert.throws(throwingFirst, 'Second'); +// In the next example the message has no benefit over the message from the +// error and since it is not clear if the user intended to actually match +// against the error message, Node.js thrown an `ERR_AMBIGUOUS_ARGUMENT` error. assert.throws(throwingSecond, 'Second'); +// Throws an error: +// TypeError [ERR_AMBIGUOUS_ARGUMENT] // The string is only used (as message) in case the function does not throw: assert.throws(notThrowing, 'Second'); @@ -1134,7 +1181,7 @@ assert.throws(notThrowing, 'Second'); assert.throws(throwingSecond, /Second$/); // Does not throw because the error messages match. assert.throws(throwingFirst, /Second$/); -// Throws a error: +// Throws an error: // Error: First // at throwingFirst (repl:2:9) ``` diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 2628cc290a5660..601dad93e75a57 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -141,7 +141,6 @@ future. This is subject to change in the future if a comprehensive analysis is performed to ensure an exception can follow the normal control flow without unintentional side effects. - ##### Printing in AsyncHooks callbacks Because printing to the console is an asynchronous operation, `console.log()` @@ -257,7 +256,6 @@ the new resource to initialize and that caused `init` to call. This is different from `async_hooks.executionAsyncId()` that only shows *when* a resource was created, while `triggerAsyncId` shows *why* a resource was created. - The following is a simple demonstration of `triggerAsyncId`: ```js @@ -395,7 +393,6 @@ API the user's callback is placed in a `process.nextTick()`. The graph only shows *when* a resource was created, not *why*, so to track the *why* use `triggerAsyncId`. - ##### before(asyncId) * `asyncId` {number} @@ -413,7 +410,6 @@ asynchronous resources like a TCP server will typically call the `before` callback multiple times, while other operations like `fs.open()` will call it only once. - ##### after(asyncId) * `asyncId` {number} @@ -424,7 +420,6 @@ If an uncaught exception occurs during execution of the callback, then `after` will run *after* the `'uncaughtException'` event is emitted or a `domain`'s handler runs. - ##### destroy(asyncId) * `asyncId` {number} diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 6e26cd8be8d064..62b2355ee38803 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -456,7 +456,6 @@ ls.on('close', (code) => { }); ``` - Example: A very elaborate way to run `ps ax | grep ssh` ```js @@ -494,7 +493,6 @@ grep.on('close', (code) => { }); ``` - Example of checking for failed `spawn`: ```js diff --git a/doc/api/cli.md b/doc/api/cli.md index 164e370fff5e15..817f49142cb4c6 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -8,7 +8,6 @@ debugging, multiple ways to execute scripts, and other helpful runtime options. To view this documentation as a manual page in a terminal, run `man node`. - ## Synopsis `node [options] [V8 options] [script.js | -e "script" | -] [--] [arguments]` @@ -21,7 +20,6 @@ Execute without arguments to start the [REPL][]. _For more info about `node debug`, please see the [debugger][] documentation._ - ## Options ### `-` @@ -33,7 +31,6 @@ Alias for stdin, analogous to the use of - in other command line utilities, meaning that the script will be read from stdin, and the rest of the options are passed to that script. - ### `--` -Enable loading native modules compiled with the ABI-stable Node.js API (N-API) -(experimental). - +This option is a no-op. It is kept for compatibility. ### `--no-deprecation` - The `Console` class can be used to create a simple logger with configurable @@ -354,7 +353,7 @@ added: v10.0.0 * `properties` {string[]} Alternate properties for constructing the table. Try to construct a table with the columns of the properties of `tabularData` -(or use `properties`) and rows of `tabularData` and logit. Falls back to just +(or use `properties`) and rows of `tabularData` and log it. Falls back to just logging the argument if it can’t be parsed as tabular. ```js @@ -364,9 +363,7 @@ console.table(Symbol()); console.table(undefined); // undefined -``` -```js console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]); // ┌─────────┬─────┬─────┐ // │ (index) │ a │ b │ @@ -374,9 +371,7 @@ console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]); // │ 0 │ 1 │ 'Y' │ // │ 1 │ 'Z' │ 2 │ // └─────────┴─────┴─────┘ -``` -```js console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']); // ┌─────────┬─────┐ // │ (index) │ a │ @@ -495,17 +490,6 @@ current JavaScript CPU profiling session if one has been started and prints the report to the **Profiles** panel of the inspector. See [`console.profile()`][] for an example. -### console.table(array[, columns]) - -* `array` {Array|Object} -* `columns` {string[]} Display only selected properties of objects in the - `array`. - -This method does not display anything unless used in the inspector. Prints to -`stdout` the array `array` formatted as a table. - ### console.timeStamp([label]) > Stability: 0 - Deprecated: Use [`crypto.createCipheriv()`][] instead. @@ -1336,7 +1341,9 @@ Creates and returns a `Cipher` object that uses the given `algorithm` and The `options` argument controls stream behavior and is optional except when a cipher in CCM mode is used (e.g. `'aes-128-ccm'`). In that case, the `authTagLength` option is required and specifies the length of the -authentication tag in bytes, see [CCM mode][]. +authentication tag in bytes, see [CCM mode][]. In GCM mode, the `authTagLength` +option is not required but can be used to set the length of the authentication +tag that will be returned by `getAuthTag()` and defaults to 16 bytes. The `algorithm` is dependent on OpenSSL, examples are `'aes192'`, etc. On recent OpenSSL releases, `openssl list-cipher-algorithms` will display the @@ -1366,6 +1373,10 @@ Adversaries][] for details. + +* `eventName` {string|symbol} +* `listener` {Function} +* Returns: {EventEmitter} + +Alias for [`emitter.removeListener()`][]. + ### emitter.on(eventName, listener) -Emitted when the request has been aborted and the network socket has closed. +Emitted when the request has been aborted. ### Event: 'close' + +* {boolean} + +The `message.aborted` property will be `true` if the request has +been aborted. + ### message.destroy([error]) +* `type` {integer} The frame type. +* `code` {integer} The error code. +* `id` {integer} The stream id (or `0` if the frame isn't associated with a + stream). + The `'frameError'` event is emitted when an error occurs while attempting to send a frame on the session. If the frame that could not be sent is associated with a specific `Http2Stream`, an attempt to emit `'frameError'` event on the `Http2Stream` is made. -When invoked, the handler function will receive three arguments: - -* An integer identifying the frame type. -* An integer identifying the error code. -* An integer identifying the stream (or 0 if the frame is not associated with - a stream). - 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 shut down @@ -181,15 +179,14 @@ immediately following the `'frameError'` event. added: v8.4.0 --> -The `'goaway'` event is emitted when a `GOAWAY` frame is received. When invoked, -the handler function will receive three arguments: - * `errorCode` {number} The HTTP/2 error code specified in the `GOAWAY` frame. * `lastStreamID` {number} The ID of the last stream the remote peer successfully processed (or `0` if no ID is specified). * `opaqueData` {Buffer} If additional opaque data was included in the `GOAWAY` frame, a `Buffer` instance will be passed containing that data. +The `'goaway'` event is emitted when a `GOAWAY` frame is received. + The `Http2Session` instance will be shut down automatically when the `'goaway'` event is emitted. @@ -2419,6 +2416,16 @@ added: v8.4.0 Indicates that the underlying [`Http2Stream`][] was closed. Just like `'end'`, this event occurs only once per response. +#### request.aborted + + +* {boolean} + +The `request.aborted` property will be `true` if the request has +been aborted. + #### request.destroy([error]) * Returns: {integer} The `os.uptime()` method returns the system uptime in number of seconds. -On Windows the returned value includes fractions of a second. Use `Math.floor()` -to get whole seconds. - ## os.userInfo([options]) +* `code` {integer} + The `'exit'` event is emitted when the Node.js process is about to exit as a result of either: @@ -56,7 +58,7 @@ all `'exit'` listeners have finished running the Node.js process will terminate. The listener callback function is invoked with the exit code specified either by the [`process.exitCode`][] property, or the `exitCode` argument passed to the -[`process.exit()`] method, as the only argument. +[`process.exit()`] method. ```js process.on('exit', (code) => { @@ -82,16 +84,15 @@ process.on('exit', (code) => { added: v0.5.10 --> +* `message` {Object} a parsed JSON object or primitive value. +* `sendHandle` {net.Server|net.Socket} a [`net.Server`][] or [`net.Socket`][] + object, or undefined. + If the Node.js process is spawned with an IPC channel (see the [Child Process][] and [Cluster][] documentation), the `'message'` event is emitted whenever a message sent by a parent process using [`childprocess.send()`][] is received by the child process. -The listener callback is invoked with the following arguments: -* `message` {Object} a parsed JSON object or primitive value. -* `sendHandle` {net.Server|net.Socket} a [`net.Server`][] or [`net.Socket`][] - object, or undefined. - The message goes through serialization and parsing. The resulting message might not be the same as what is originally sent. @@ -100,13 +101,12 @@ not be the same as what is originally sent. added: v1.4.1 --> +* `promise` {Promise} The late handled promise. + The `'rejectionHandled'` event is emitted whenever a `Promise` has been rejected and an error handler was attached to it (using [`promise.catch()`][], for example) later than one turn of the Node.js event loop. -The listener callback is invoked with a reference to the rejected `Promise` as -the only argument. - The `Promise` object would have previously been emitted in an `'unhandledRejection'` event, but during the course of processing gained a rejection handler. @@ -129,11 +129,11 @@ when the list of unhandled rejections shrinks. ```js const unhandledRejections = new Map(); -process.on('unhandledRejection', (reason, p) => { - unhandledRejections.set(p, reason); +process.on('unhandledRejection', (reason, promise) => { + unhandledRejections.set(promise, reason); }); -process.on('rejectionHandled', (p) => { - unhandledRejections.delete(p); +process.on('rejectionHandled', (promise) => { + unhandledRejections.delete(promise); }); ``` @@ -261,6 +261,12 @@ being emitted. Alternatively, the [`'rejectionHandled'`][] event may be used. added: v6.0.0 --> +* `warning` {Error} Key properties of the warning are: + * `name` {string} The name of the warning. **Default:** `'Warning'`. + * `message` {string} A system-provided description of the warning. + * `stack` {string} A stack trace to the location in the code where the warning + was issued. + The `'warning'` event is emitted whenever Node.js emits a process warning. A process warning is similar to an error in that it describes exceptional @@ -269,14 +275,6 @@ are not part of the normal Node.js and JavaScript error handling flow. Node.js can emit warnings whenever it detects bad coding practices that could lead to sub-optimal application performance, bugs, or security vulnerabilities. -The listener function is called with a single `warning` argument whose value is -an `Error` object. There are three key properties that describe the warning: - -* `name` {string} The name of the warning (currently `'Warning'` by default). -* `message` {string} A system-provided description of the warning. -* `stack` {string} A stack trace to the location in the code where the warning - was issued. - ```js process.on('warning', (warning) => { console.warn(warning.name); // Print the warning name @@ -966,7 +964,6 @@ that started the Node.js process. '/usr/local/bin/node' ``` - ## process.exit([code]) * `str` {string} - The `querystring.unescape()` method performs decoding of URL percent-encoded characters on the given `str`. diff --git a/doc/api/readline.md b/doc/api/readline.md index c1e50ef7eee350..d3afe5d9bf5ba2 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -320,7 +320,6 @@ added: v0.7.7 The `readline.clearLine()` method clears current line of given [TTY][] stream in a specified direction identified by `dir`. - ## readline.clearScreenDown(stream) -* Returns: {Immediate} +* Returns: {Immediate} a reference to `immediate` When called, requests that the Node.js event loop *not* exit so long as the `Immediate` is active. Calling `immediate.ref()` multiple times will have no @@ -37,22 +37,18 @@ effect. By default, all `Immediate` objects are "ref'ed", making it normally unnecessary to call `immediate.ref()` unless `immediate.unref()` had been called previously. -Returns a reference to the `Immediate`. - ### immediate.unref() -* Returns: {Immediate} +* Returns: {Immediate} a reference to `immediate` When called, the active `Immediate` object will not require the Node.js event loop to remain active. If there is no other activity keeping the event loop running, the process may exit before the `Immediate` object's callback is invoked. Calling `immediate.unref()` multiple times will have no effect. -Returns a reference to the `Immediate`. - ## Class: Timeout This object is created internally and is returned from [`setTimeout()`][] and @@ -70,7 +66,7 @@ control this default behavior. added: v0.9.1 --> -* Returns: {Timeout} +* Returns: {Timeout} a reference to `timeout` When called, requests that the Node.js event loop *not* exit so long as the `Timeout` is active. Calling `timeout.ref()` multiple times will have no effect. @@ -78,14 +74,12 @@ When called, requests that the Node.js event loop *not* exit so long as the By default, all `Timeout` objects are "ref'ed", making it normally unnecessary to call `timeout.ref()` unless `timeout.unref()` had been called previously. -Returns a reference to the `Timeout`. - ### timeout.unref() -* Returns: {Timeout} +* Returns: {Timeout} a reference to `timeout` When called, the active `Timeout` object will not require the Node.js event loop to remain active. If there is no other activity keeping the event loop running, @@ -96,8 +90,6 @@ Calling `timeout.unref()` creates an internal timer that will wake the Node.js event loop. Creating too many of these can adversely impact performance of the Node.js application. -Returns a reference to the `Timeout`. - ## Scheduling Timers A timer in Node.js is an internal construct that calls a given function after @@ -113,9 +105,10 @@ added: v0.9.1 * `callback` {Function} The function to call at the end of this turn of [the Node.js Event Loop] * `...args` {any} Optional arguments to pass when the `callback` is called. +* Returns: {Immediate} for use with [`clearImmediate()`][] Schedules the "immediate" execution of the `callback` after I/O events' -callbacks. Returns an `Immediate` for use with [`clearImmediate()`][]. +callbacks. When multiple calls to `setImmediate()` are made, the `callback` functions are queued for execution in the order in which they are created. The entire callback @@ -155,10 +148,9 @@ added: v0.0.1 * `delay` {number} The number of milliseconds to wait before calling the `callback`. * `...args` {any} Optional arguments to pass when the `callback` is called. -* Returns: {Timeout} +* Returns: {Timeout} for use with [`clearInterval()`][] Schedules repeated execution of `callback` every `delay` milliseconds. -Returns a `Timeout` for use with [`clearInterval()`][]. When `delay` is larger than `2147483647` or less than `1`, the `delay` will be set to `1`. @@ -174,10 +166,9 @@ added: v0.0.1 * `delay` {number} The number of milliseconds to wait before calling the `callback`. * `...args` {any} Optional arguments to pass when the `callback` is called. -* Returns: {Timeout} +* Returns: {Timeout} for use with [`clearTimeout()`][] Schedules execution of a one-time `callback` after `delay` milliseconds. -Returns a `Timeout` for use with [`clearTimeout()`][]. The `callback` will likely not be invoked in precisely `delay` milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, @@ -239,7 +230,6 @@ added: v0.0.1 Cancels a `Timeout` object created by [`setTimeout()`][]. - [`TypeError`]: errors.html#errors_class_typeerror [`clearImmediate()`]: timers.html#timers_clearimmediate_immediate [`clearInterval()`]: timers.html#timers_clearinterval_timeout diff --git a/doc/api/tls.md b/doc/api/tls.md index 0f9e46f2474d57..beb2ec679e9f11 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -428,7 +428,6 @@ more information on how it is used. Changes to the ticket keys are effective only for future server connections. Existing or currently pending server connections will use the previous keys. - ## Class: tls.TLSSocket -The `tty.WriteStream` class is a subclass of `net.Socket` that represents the -writable side of a TTY. In normal circumstances, [`process.stdout`][] and +The `tty.WriteStream` class is a subclass of [`net.Socket`][] that represents +the writable side of a TTY. In normal circumstances, [`process.stdout`][] and [`process.stderr`][] will be the only `tty.WriteStream` instances created for a Node.js process and there should be no reason to create additional instances. diff --git a/doc/api/url.md b/doc/api/url.md index 35a72da8ee681f..f8a22fefac3ddf 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -132,8 +132,6 @@ and a `base` is provided, it is advised to validate that the `origin` of the `URL` object is what is expected. ```js -const { URL } = require('url'); - let myURL = new URL('http://anotherExample.org/', 'https://example.org/'); // http://anotherexample.org/ @@ -1063,7 +1061,6 @@ The formatting process operates as follows: string, an [`Error`][] is thrown. * `result` is returned. - ### url.parse(urlString[, parseQueryString[, slashesDenoteHost]]) ``` Please try to do your best at filling out the details, but feel free to skip diff --git a/doc/node.1 b/doc/node.1 index 24d8260b8765ac..c3572acee7bf6b 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -122,8 +122,7 @@ V8 Inspector integration allows attaching Chrome DevTools and IDEs to Node.js in It uses the Chrome DevTools Protocol. . .It Fl -napi-modules -Enable loading native modules compiled with the ABI-stable Node.js API (N-API) -(experimental). +This option is a no-op. It is kept for compatibility. . .It Fl -no-deprecation Silence deprecation warnings. diff --git a/doc/onboarding-extras.md b/doc/onboarding-extras.md index 080918049f66c0..ffc316d7a670b6 100644 --- a/doc/onboarding-extras.md +++ b/doc/onboarding-extras.md @@ -91,7 +91,7 @@ need to be attached anymore, as only important bugfixes will be included. to update from nodejs/node: * `git checkout master` -* `git remote update -p` OR `git fetch --all` (I prefer the former) +* `git remote update -p` OR `git fetch --all` * `git merge --ff-only upstream/master` (or `REMOTENAME/BRANCH`) ## Best practices diff --git a/lib/_http_client.js b/lib/_http_client.js index 22ae85c92782ec..1985c617bec537 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -37,12 +37,11 @@ const { OutgoingMessage } = require('_http_outgoing'); const Agent = require('_http_agent'); const { Buffer } = require('buffer'); const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); -const { urlToOptions, searchParamsSymbol } = require('internal/url'); +const { URL, urlToOptions, searchParamsSymbol } = require('internal/url'); const { outHeadersKey, ondrain } = require('internal/http'); const { ERR_HTTP_HEADERS_SENT, ERR_INVALID_ARG_TYPE, - ERR_INVALID_DOMAIN_NAME, ERR_INVALID_HTTP_TOKEN, ERR_INVALID_PROTOCOL, ERR_UNESCAPED_CHARACTERS @@ -60,13 +59,26 @@ function validateHost(host, name) { return host; } +let urlWarningEmitted = false; function ClientRequest(options, cb) { OutgoingMessage.call(this); if (typeof options === 'string') { - options = url.parse(options); - if (!options.hostname) { - throw new ERR_INVALID_DOMAIN_NAME(); + const urlStr = options; + try { + options = urlToOptions(new URL(urlStr)); + } catch (err) { + options = url.parse(urlStr); + if (!options.hostname) { + throw err; + } + if (!urlWarningEmitted && !process.noDeprecation) { + urlWarningEmitted = true; + process.emitWarning( + `The provided URL ${urlStr} is not a valid URL, and is supported ` + + 'in the http module solely for compatibility.', + 'DeprecationWarning', 'DEP0109'); + } } } else if (options && options[searchParamsSymbol] && options[searchParamsSymbol][searchParamsSymbol]) { @@ -279,6 +291,7 @@ ClientRequest.prototype.abort = function abort() { if (!this.aborted) { process.nextTick(emitAbortNT.bind(this)); } + // Mark as aborting so we can avoid sending queued request data // This is used as a truthy flag elsewhere. The use of Date.now is for // debugging purposes only. @@ -330,7 +343,10 @@ function socketCloseListener() { var parser = socket.parser; if (req.res && req.res.readable) { // Socket closed before we emitted 'end' below. - if (!req.res.complete) req.res.emit('aborted'); + if (!req.res.complete) { + req.res.aborted = true; + req.res.emit('aborted'); + } var res = req.res; res.on('end', function() { res.emit('close'); diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index 55c196399c5e6b..23ac4d54be1ec5 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -54,6 +54,8 @@ function IncomingMessage(socket) { this.readable = true; + this.aborted = false; + this.upgrade = null; // request (server) only diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 770e555d1ead8a..f075e0ecad7a2b 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -52,6 +52,8 @@ const { utcDate } = internalHttp; const kIsCorked = Symbol('isCorked'); +const hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + var RE_CONN_CLOSE = /(?:^|\W)close(?:$|\W)/i; var RE_TE_CHUNKED = common.chunkExpression; @@ -116,7 +118,7 @@ Object.defineProperty(OutgoingMessage.prototype, '_headers', { if (val == null) { this[outHeadersKey] = null; } else if (typeof val === 'object') { - const headers = this[outHeadersKey] = {}; + const headers = this[outHeadersKey] = Object.create(null); const keys = Object.keys(val); for (var i = 0; i < keys.length; ++i) { const name = keys[i]; @@ -129,7 +131,7 @@ Object.defineProperty(OutgoingMessage.prototype, '_headers', { Object.defineProperty(OutgoingMessage.prototype, '_headerNames', { get: function() { const headers = this[outHeadersKey]; - if (headers) { + if (headers !== null) { const out = Object.create(null); const keys = Object.keys(headers); for (var i = 0; i < keys.length; ++i) { @@ -138,9 +140,8 @@ Object.defineProperty(OutgoingMessage.prototype, '_headerNames', { out[key] = val; } return out; - } else { - return headers; } + return null; }, set: function(val) { if (typeof val === 'object' && val !== null) { @@ -164,14 +165,14 @@ OutgoingMessage.prototype._renderHeaders = function _renderHeaders() { } var headersMap = this[outHeadersKey]; - if (!headersMap) return {}; - - var headers = {}; - var keys = Object.keys(headersMap); + const headers = {}; - for (var i = 0, l = keys.length; i < l; i++) { - var key = keys[i]; - headers[headersMap[key][0]] = headersMap[key][1]; + if (headersMap !== null) { + const keys = Object.keys(headersMap); + for (var i = 0, l = keys.length; i < l; i++) { + const key = keys[i]; + headers[headersMap[key][0]] = headersMap[key][1]; + } } return headers; }; @@ -285,72 +286,40 @@ OutgoingMessage.prototype._storeHeader = _storeHeader; function _storeHeader(firstLine, headers) { // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n' // in the case of response it is: 'HTTP/1.1 200 OK\r\n' - var state = { + const state = { connection: false, contLen: false, te: false, date: false, expect: false, trailer: false, - upgrade: false, header: firstLine }; - var field; var key; - var value; - var i; - var j; if (headers === this[outHeadersKey]) { for (key in headers) { - var entry = headers[key]; - field = entry[0]; - value = entry[1]; - - if (value instanceof Array) { - if (value.length < 2 || !isCookieField(field)) { - for (j = 0; j < value.length; j++) - storeHeader(this, state, field, value[j], false); - continue; - } - value = value.join('; '); - } - storeHeader(this, state, field, value, false); + const entry = headers[key]; + processHeader(this, state, entry[0], entry[1], false); } - } else if (headers instanceof Array) { - for (i = 0; i < headers.length; i++) { - field = headers[i][0]; - value = headers[i][1]; - - if (value instanceof Array) { - for (j = 0; j < value.length; j++) { - storeHeader(this, state, field, value[j], true); - } - } else { - storeHeader(this, state, field, value, true); - } + } else if (Array.isArray(headers)) { + for (var i = 0; i < headers.length; i++) { + const entry = headers[i]; + processHeader(this, state, entry[0], entry[1], true); } } else if (headers) { - var keys = Object.keys(headers); - for (i = 0; i < keys.length; i++) { - field = keys[i]; - value = headers[field]; - - if (value instanceof Array) { - if (value.length < 2 || !isCookieField(field)) { - for (j = 0; j < value.length; j++) - storeHeader(this, state, field, value[j], true); - continue; - } - value = value.join('; '); + for (key in headers) { + if (hasOwnProperty(headers, key)) { + processHeader(this, state, key, headers[key], true); } - storeHeader(this, state, field, value, true); } } + let { header } = state; + // Date header if (this.sendDate && !state.date) { - state.header += 'Date: ' + utcDate() + CRLF; + header += 'Date: ' + utcDate() + CRLF; } // Force the connection to close when the response is a 204 No Content or @@ -364,9 +333,9 @@ function _storeHeader(firstLine, headers) { // It was pointed out that this might confuse reverse proxies to the point // of creating security liabilities, so suppress the zero chunk and force // the connection to close. - var statusCode = this.statusCode; - if ((statusCode === 204 || statusCode === 304) && this.chunkedEncoding) { - debug(statusCode + ' response should not use chunked encoding,' + + if (this.chunkedEncoding && (this.statusCode === 204 || + this.statusCode === 304)) { + debug(this.statusCode + ' response should not use chunked encoding,' + ' closing connection.'); this.chunkedEncoding = false; this.shouldKeepAlive = false; @@ -377,13 +346,13 @@ function _storeHeader(firstLine, headers) { this._last = true; this.shouldKeepAlive = false; } else if (!state.connection) { - var shouldSendKeepAlive = this.shouldKeepAlive && + const shouldSendKeepAlive = this.shouldKeepAlive && (state.contLen || this.useChunkedEncodingByDefault || this.agent); if (shouldSendKeepAlive) { - state.header += 'Connection: keep-alive\r\n'; + header += 'Connection: keep-alive\r\n'; } else { this._last = true; - state.header += 'Connection: close\r\n'; + header += 'Connection: close\r\n'; } } @@ -396,9 +365,9 @@ function _storeHeader(firstLine, headers) { } else if (!state.trailer && !this._removedContLen && typeof this._contentLength === 'number') { - state.header += 'Content-Length: ' + this._contentLength + CRLF; + header += 'Content-Length: ' + this._contentLength + CRLF; } else if (!this._removedTE) { - state.header += 'Transfer-Encoding: chunked\r\n'; + header += 'Transfer-Encoding: chunked\r\n'; this.chunkedEncoding = true; } else { // We should only be able to get here if both Content-Length and @@ -416,7 +385,7 @@ function _storeHeader(firstLine, headers) { throw new ERR_HTTP_TRAILER_INVALID(); } - this._header = state.header + CRLF; + this._header = header + CRLF; this._headerSent = false; // wait until the first body chunk, or close(), is sent to flush, @@ -424,10 +393,23 @@ function _storeHeader(firstLine, headers) { if (state.expect) this._send(''); } -function storeHeader(self, state, key, value, validate) { - if (validate) { - validateHeader(key, value); +function processHeader(self, state, key, value, validate) { + if (validate) + validateHeaderName(key); + if (Array.isArray(value)) { + if (value.length < 2 || !isCookieField(key)) { + for (var i = 0; i < value.length; i++) + storeHeader(self, state, key, value[i], validate); + return; + } + value = value.join('; '); } + storeHeader(self, state, key, value, validate); +} + +function storeHeader(self, state, key, value, validate) { + if (validate) + validateHeaderValue(key, value); state.header += key + ': ' + escapeHeaderValue(value) + CRLF; matchHeader(self, state, key, value); } @@ -439,6 +421,7 @@ function matchHeader(self, state, field, value) { switch (field) { case 'connection': state.connection = true; + self._removedConnection = false; if (RE_CONN_CLOSE.test(value)) self._last = true; else @@ -446,32 +429,39 @@ function matchHeader(self, state, field, value) { break; case 'transfer-encoding': state.te = true; + self._removedTE = false; if (RE_TE_CHUNKED.test(value)) self.chunkedEncoding = true; break; case 'content-length': state.contLen = true; + self._removedContLen = false; break; case 'date': case 'expect': case 'trailer': - case 'upgrade': state[field] = true; break; } } -function validateHeader(name, value) { - let err; +function validateHeaderName(name) { if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) { - err = new ERR_INVALID_HTTP_TOKEN('Header name', name); - } else if (value === undefined) { + const err = new ERR_INVALID_HTTP_TOKEN('Header name', name); + Error.captureStackTrace(err, validateHeaderName); + throw err; + } +} + +function validateHeaderValue(name, value) { + let err; + if (value === undefined) { err = new ERR_HTTP_INVALID_HEADER_VALUE(value, name); } else if (checkInvalidHeaderChar(value)) { debug('Header "%s" contains invalid characters', name); err = new ERR_INVALID_CHAR('header content', name); } if (err !== undefined) { - Error.captureStackTrace(err, validateHeader); + Error.captureStackTrace(err, validateHeaderValue); throw err; } } @@ -480,25 +470,14 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) { if (this._header) { throw new ERR_HTTP_HEADERS_SENT('set'); } - validateHeader(name, value); + validateHeaderName(name); + validateHeaderValue(name, value); - if (!this[outHeadersKey]) - this[outHeadersKey] = {}; + let headers = this[outHeadersKey]; + if (headers === null) + this[outHeadersKey] = headers = Object.create(null); - const key = name.toLowerCase(); - this[outHeadersKey][key] = [name, value]; - - switch (key) { - case 'connection': - this._removedConnection = false; - break; - case 'content-length': - this._removedContLen = false; - break; - case 'transfer-encoding': - this._removedTE = false; - break; - } + headers[name.toLowerCase()] = [name, value]; }; @@ -507,18 +486,18 @@ OutgoingMessage.prototype.getHeader = function getHeader(name) { throw new ERR_INVALID_ARG_TYPE('name', 'string', name); } - if (!this[outHeadersKey]) return; - - var entry = this[outHeadersKey][name.toLowerCase()]; - if (!entry) + const headers = this[outHeadersKey]; + if (headers === null) return; - return entry[1]; + + const entry = headers[name.toLowerCase()]; + return entry && entry[1]; }; // Returns an array of the names of the current outgoing headers. OutgoingMessage.prototype.getHeaderNames = function getHeaderNames() { - return (this[outHeadersKey] ? Object.keys(this[outHeadersKey]) : []); + return this[outHeadersKey] !== null ? Object.keys(this[outHeadersKey]) : []; }; @@ -543,7 +522,8 @@ OutgoingMessage.prototype.hasHeader = function hasHeader(name) { throw new ERR_INVALID_ARG_TYPE('name', 'string', name); } - return !!(this[outHeadersKey] && this[outHeadersKey][name.toLowerCase()]); + return this[outHeadersKey] !== null && + !!this[outHeadersKey][name.toLowerCase()]; }; @@ -573,7 +553,7 @@ OutgoingMessage.prototype.removeHeader = function removeHeader(name) { break; } - if (this[outHeadersKey]) { + if (this[outHeadersKey] !== null) { delete this[outHeadersKey][key]; } }; diff --git a/lib/_http_server.js b/lib/_http_server.js index bf228de643422e..9c8b5cb8fbfe8e 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -440,6 +440,7 @@ function socketOnClose(socket, state) { function abortIncoming(incoming) { while (incoming.length) { var req = incoming.shift(); + req.aborted = true; req.emit('aborted'); req.emit('close'); } diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js index b123cdcb4d776f..7059757dbd44b1 100644 --- a/lib/_stream_duplex.js +++ b/lib/_stream_duplex.js @@ -50,17 +50,19 @@ function Duplex(options) { Readable.call(this, options); Writable.call(this, options); + this.allowHalfOpen = true; - if (options && options.readable === false) - this.readable = false; + if (options) { + if (options.readable === false) + this.readable = false; - if (options && options.writable === false) - this.writable = false; + if (options.writable === false) + this.writable = false; - this.allowHalfOpen = true; - if (options && options.allowHalfOpen === false) { - this.allowHalfOpen = false; - this.once('end', onend); + if (options.allowHalfOpen === false) { + this.allowHalfOpen = false; + this.once('end', onend); + } } } diff --git a/lib/_tls_common.js b/lib/_tls_common.js index a9fe0d8f06aa0d..d8f6afed0bd8fb 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -56,11 +56,10 @@ function SecureContext(secureProtocol, secureOptions, context) { if (secureOptions) this.context.setOptions(secureOptions); } -function validateKeyCert(value, type) { +function validateKeyCert(name, value) { if (typeof value !== 'string' && !isArrayBufferView(value)) { throw new ERR_INVALID_ARG_TYPE( - // TODO(BridgeAR): Change this to `options.${type}` - type, + `options.${name}`, ['string', 'Buffer', 'TypedArray', 'DataView'], value ); @@ -100,11 +99,11 @@ exports.createSecureContext = function createSecureContext(options, context) { if (Array.isArray(ca)) { for (i = 0; i < ca.length; ++i) { val = ca[i]; - validateKeyCert(val, 'ca'); + validateKeyCert('ca', val); c.context.addCACert(val); } } else { - validateKeyCert(ca, 'ca'); + validateKeyCert('ca', ca); c.context.addCACert(ca); } } else { @@ -116,11 +115,11 @@ exports.createSecureContext = function createSecureContext(options, context) { if (Array.isArray(cert)) { for (i = 0; i < cert.length; ++i) { val = cert[i]; - validateKeyCert(val, 'cert'); + validateKeyCert('cert', val); c.context.setCert(val); } } else { - validateKeyCert(cert, 'cert'); + validateKeyCert('cert', cert); c.context.setCert(cert); } } @@ -137,11 +136,11 @@ exports.createSecureContext = function createSecureContext(options, context) { val = key[i]; // eslint-disable-next-line eqeqeq const pem = (val != undefined && val.pem !== undefined ? val.pem : val); - validateKeyCert(pem, 'key'); + validateKeyCert('key', pem); c.context.setKey(pem, val.passphrase || passphrase); } } else { - validateKeyCert(key, 'key'); + validateKeyCert('key', key); c.context.setKey(key, passphrase); } } diff --git a/lib/console.js b/lib/console.js index 39bcf701bf83eb..a0158ec6643782 100644 --- a/lib/console.js +++ b/lib/console.js @@ -363,9 +363,7 @@ Console.prototype.table = function(tabularData, properties) { tabularData = previewSetIterator(tabularData); const setlike = setIter || isSet(tabularData); - if (setlike || - (properties === undefined && - (isArray(tabularData) || isTypedArray(tabularData)))) { + if (setlike) { const values = []; let length = 0; for (const v of tabularData) { diff --git a/lib/fs.js b/lib/fs.js index d71e31565fbad6..50913866f9fdfd 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -51,7 +51,6 @@ const internalUtil = require('internal/util'); const { copyObject, getOptions, - isUint32, modeNum, nullCheck, preprocessSymlinkDestination, @@ -61,16 +60,19 @@ const { stringToSymlinkType, toUnixTimestamp, validateBuffer, - validateLen, validateOffsetLengthRead, validateOffsetLengthWrite, - validatePath, - validateUint32 + validatePath } = internalFS; const { CHAR_FORWARD_SLASH, CHAR_BACKWARD_SLASH, } = require('internal/constants'); +const { + isUint32, + validateInt32, + validateUint32 +} = require('internal/validators'); Object.defineProperty(exports, 'constants', { configurable: false, @@ -151,9 +153,7 @@ function makeStatsCallback(cb) { }; } -function isFd(path) { - return (path >>> 0) === path; -} +const isFd = isUint32; fs.Stats = Stats; @@ -755,8 +755,8 @@ fs.ftruncate = function(fd, len = 0, callback) { // TODO(BridgeAR): This does not seem right. // There does not seem to be any validation before and if there is any, it // should work similar to validateUint32 or not have a upper cap at all. - // This applies to all usage of `validateLen`. - validateLen(len); + // This applies to all usage of `validateInt32(len, 'len')`. + validateInt32(len, 'len'); len = Math.max(0, len); const req = new FSReqWrap(); req.oncomplete = makeCallback(callback); @@ -765,7 +765,7 @@ fs.ftruncate = function(fd, len = 0, callback) { fs.ftruncateSync = function(fd, len = 0) { validateUint32(fd, 'fd'); - validateLen(len); + validateInt32(len, 'len'); len = Math.max(0, len); const ctx = {}; binding.ftruncate(fd, len, undefined, ctx); diff --git a/lib/fs/promises.js b/lib/fs/promises.js index ba6c2b7aa64855..6ccff6933bf106 100644 --- a/lib/fs/promises.js +++ b/lib/fs/promises.js @@ -24,7 +24,6 @@ const { copyObject, getOptions, getStatsFromBinding, - isUint32, modeNum, nullCheck, preprocessSymlinkDestination, @@ -32,12 +31,15 @@ const { stringToSymlinkType, toUnixTimestamp, validateBuffer, - validateLen, validateOffsetLengthRead, validateOffsetLengthWrite, - validatePath, - validateUint32 + validatePath } = require('internal/fs'); +const { + isUint32, + validateInt32, + validateUint32 +} = require('internal/validators'); const pathModule = require('path'); const kHandle = Symbol('handle'); @@ -275,7 +277,7 @@ async function truncate(path, len = 0) { async function ftruncate(handle, len = 0) { validateFileHandle(handle); - validateLen(len); + validateInt32(len, 'len'); len = Math.max(0, len); return binding.ftruncate(handle.fd, len, kUsePromises); } diff --git a/lib/https.js b/lib/https.js index 4cca2fb9eeea88..ead7a2bb927dad 100644 --- a/lib/https.js +++ b/lib/https.js @@ -34,8 +34,7 @@ const { const { ClientRequest } = require('_http_client'); const { inherits } = util; const debug = util.debuglog('https'); -const { urlToOptions, searchParamsSymbol } = require('internal/url'); -const { ERR_INVALID_DOMAIN_NAME } = require('internal/errors').codes; +const { URL, urlToOptions, searchParamsSymbol } = require('internal/url'); const { IncomingMessage, ServerResponse } = require('http'); const { kIncomingMessage } = require('_http_common'); const { kServerResponse } = require('_http_server'); @@ -254,11 +253,24 @@ Agent.prototype._evictSession = function _evictSession(key) { const globalAgent = new Agent(); +let urlWarningEmitted = false; function request(options, cb) { if (typeof options === 'string') { - options = url.parse(options); - if (!options.hostname) { - throw new ERR_INVALID_DOMAIN_NAME(); + const urlStr = options; + try { + options = urlToOptions(new URL(urlStr)); + } catch (err) { + options = url.parse(urlStr); + if (!options.hostname) { + throw err; + } + if (!urlWarningEmitted && !process.noDeprecation) { + urlWarningEmitted = true; + process.emitWarning( + `The provided URL ${urlStr} is not a valid URL, and is supported ` + + 'in the https module solely for compatibility.', + 'DeprecationWarning', 'DEP0109'); + } } } else if (options && options[searchParamsSymbol] && options[searchParamsSymbol][searchParamsSymbol]) { diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index f9bed03d269fb2..5993cc71f7d404 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -41,6 +41,7 @@ NativeModule.require('internal/process/warning').setup(); NativeModule.require('internal/process/next_tick').setup(); NativeModule.require('internal/process/stdio').setup(); + NativeModule.require('internal/process/methods').setup(); const perf = process.binding('performance'); const { diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index 085a82265a95de..54e13ff30bcb4e 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -59,6 +59,8 @@ function boundsError(value, length, type) { // Read integers. function readUIntLE(offset, byteLength) { + if (offset === undefined) + throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); if (byteLength === 6) return readUInt48LE(this, offset); if (byteLength === 5) @@ -69,7 +71,7 @@ function readUIntLE(offset, byteLength) { return this.readUInt32LE(offset); if (byteLength === 2) return this.readUInt16LE(offset); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return this.readUInt8(offset); boundsError(byteLength, 6, 'byteLength'); @@ -146,6 +148,8 @@ function readUInt8(offset = 0) { } function readUIntBE(offset, byteLength) { + if (offset === undefined) + throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); if (byteLength === 6) return readUInt48BE(this, offset); if (byteLength === 5) @@ -156,7 +160,7 @@ function readUIntBE(offset, byteLength) { return this.readUInt32BE(offset); if (byteLength === 2) return this.readUInt16BE(offset); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return this.readUInt8(offset); boundsError(byteLength, 6, 'byteLength'); @@ -224,6 +228,8 @@ function readUInt16BE(offset = 0) { } function readIntLE(offset, byteLength) { + if (offset === undefined) + throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); if (byteLength === 6) return readInt48LE(this, offset); if (byteLength === 5) @@ -234,7 +240,7 @@ function readIntLE(offset, byteLength) { return this.readInt32LE(offset); if (byteLength === 2) return this.readInt16LE(offset); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return this.readInt8(offset); boundsError(byteLength, 6, 'byteLength'); @@ -314,6 +320,8 @@ function readInt8(offset = 0) { } function readIntBE(offset, byteLength) { + if (offset === undefined) + throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); if (byteLength === 6) return readInt48BE(this, offset); if (byteLength === 5) @@ -324,7 +332,7 @@ function readIntBE(offset, byteLength) { return this.readInt32BE(offset); if (byteLength === 2) return this.readInt16BE(offset); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return this.readInt8(offset); boundsError(byteLength, 6, 'byteLength'); @@ -460,7 +468,7 @@ function readDoubleForwards(offset = 0) { } // Write integers. -function writeUIntLE(value, offset = 0, byteLength) { +function writeUIntLE(value, offset, byteLength) { if (byteLength === 6) return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff); if (byteLength === 5) @@ -471,7 +479,7 @@ function writeUIntLE(value, offset = 0, byteLength) { return writeU_Int32LE(this, value, offset, 0, 0xffffffff); if (byteLength === 2) return writeU_Int16LE(this, value, offset, 0, 0xffff); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return writeU_Int8(this, value, offset, 0, 0xff); boundsError(byteLength, 6, 'byteLength'); @@ -571,7 +579,7 @@ function writeUInt8(value, offset = 0) { return writeU_Int8(this, value, offset, 0, 0xff); } -function writeUIntBE(value, offset = 0, byteLength) { +function writeUIntBE(value, offset, byteLength) { if (byteLength === 6) return writeU_Int48BE(this, value, offset, 0, 0xffffffffffffff); if (byteLength === 5) @@ -582,7 +590,7 @@ function writeUIntBE(value, offset = 0, byteLength) { return writeU_Int32BE(this, value, offset, 0, 0xffffffff); if (byteLength === 2) return writeU_Int16BE(this, value, offset, 0, 0xffff); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return writeU_Int8(this, value, offset, 0, 0xff); boundsError(byteLength, 6, 'byteLength'); @@ -663,7 +671,7 @@ function writeUInt16BE(value, offset = 0) { return writeU_Int16BE(this, value, offset, 0, 0xffffffff); } -function writeIntLE(value, offset = 0, byteLength) { +function writeIntLE(value, offset, byteLength) { if (byteLength === 6) return writeU_Int48LE(this, value, offset, -0x800000000000, 0x7fffffffffff); if (byteLength === 5) @@ -674,7 +682,7 @@ function writeIntLE(value, offset = 0, byteLength) { return writeU_Int32LE(this, value, offset, -0x80000000, 0x7fffffff); if (byteLength === 2) return writeU_Int16LE(this, value, offset, -0x8000, 0x7fff); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return writeU_Int8(this, value, offset, -0x80, 0x7f); boundsError(byteLength, 6, 'byteLength'); @@ -692,7 +700,7 @@ function writeInt8(value, offset = 0) { return writeU_Int8(this, value, offset, -0x80, 0x7f); } -function writeIntBE(value, offset = 0, byteLength) { +function writeIntBE(value, offset, byteLength) { if (byteLength === 6) return writeU_Int48BE(this, value, offset, -0x800000000000, 0x7fffffffffff); if (byteLength === 5) @@ -703,7 +711,7 @@ function writeIntBE(value, offset = 0, byteLength) { return writeU_Int32BE(this, value, offset, -0x80000000, 0x7fffffff); if (byteLength === 2) return writeU_Int16BE(this, value, offset, -0x8000, 0x7fff); - if (byteLength === 1 || byteLength === undefined) + if (byteLength === 1) return writeU_Int8(this, value, offset, -0x80, 0x7f); boundsError(byteLength, 6, 'byteLength'); diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 8cca1c2f6b0443..a630cff717bcad 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -31,6 +31,8 @@ const SocketList = require('internal/socket_list'); const { convertToValidSignal } = require('internal/util'); const { isUint8Array } = require('internal/util/types'); const spawn_sync = process.binding('spawn_sync'); +const { HTTPParser } = process.binding('http_parser'); +const { freeParser } = require('_http_common'); const { UV_EACCES, @@ -107,6 +109,14 @@ const handleConversion = { if (!options.keepOpen) { handle.onread = nop; socket._handle = null; + socket.setTimeout(0); + // In case of an HTTP connection socket, release the associated + // resources + if (socket.parser && socket.parser instanceof HTTPParser) { + freeParser(socket.parser, null, socket); + if (socket._httpMessage) + socket._httpMessage.detachSocket(socket); + } } return handle; diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 329add6d4d7cd5..dad7a903b26a5e 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -219,21 +219,15 @@ function encode(buffer, encoding) { } function getFormat(format) { - let f; if (format) { if (format === 'compressed') - f = POINT_CONVERSION_COMPRESSED; - else if (format === 'hybrid') - f = POINT_CONVERSION_HYBRID; - // Default - else if (format === 'uncompressed') - f = POINT_CONVERSION_UNCOMPRESSED; - else + return POINT_CONVERSION_COMPRESSED; + if (format === 'hybrid') + return POINT_CONVERSION_HYBRID; + if (format !== 'uncompressed') throw new ERR_CRYPTO_ECDH_INVALID_FORMAT(format); - } else { - f = POINT_CONVERSION_UNCOMPRESSED; } - return f; + return POINT_CONVERSION_UNCOMPRESSED; } module.exports = { diff --git a/lib/internal/crypto/pbkdf2.js b/lib/internal/crypto/pbkdf2.js index 4a7f26b509b521..82ea9feb852649 100644 --- a/lib/internal/crypto/pbkdf2.js +++ b/lib/internal/crypto/pbkdf2.js @@ -7,10 +7,10 @@ const { ERR_OUT_OF_RANGE } = require('internal/errors').codes; const { + checkIsArrayBufferView, getDefaultEncoding, toBuf } = require('internal/crypto/util'); -const { isArrayBufferView } = require('internal/util/types'); const { PBKDF2 } = process.binding('crypto'); @@ -39,19 +39,8 @@ function _pbkdf2(password, salt, iterations, keylen, digest, callback) { if (digest !== null && typeof digest !== 'string') throw new ERR_INVALID_ARG_TYPE('digest', ['string', 'null'], digest); - password = toBuf(password); - salt = toBuf(salt); - - if (!isArrayBufferView(password)) { - throw new ERR_INVALID_ARG_TYPE('password', - ['string', 'Buffer', 'TypedArray'], - password); - } - - if (!isArrayBufferView(salt)) { - throw new ERR_INVALID_ARG_TYPE('salt', - ['string', 'Buffer', 'TypedArray'], salt); - } + password = checkIsArrayBufferView('password', toBuf(password)); + salt = checkIsArrayBufferView('salt', toBuf(salt)); if (typeof iterations !== 'number') throw new ERR_INVALID_ARG_TYPE('iterations', 'number', iterations); diff --git a/lib/internal/crypto/sig.js b/lib/internal/crypto/sig.js index aed679e99b9b8f..1073b83d720098 100644 --- a/lib/internal/crypto/sig.js +++ b/lib/internal/crypto/sig.js @@ -14,10 +14,10 @@ const { RSA_PKCS1_PADDING } = process.binding('constants').crypto; const { + checkIsArrayBufferView, getDefaultEncoding, toBuf } = require('internal/crypto/util'); -const { isArrayBufferView } = require('internal/util/types'); const { Writable } = require('stream'); const { inherits } = require('util'); @@ -41,18 +41,30 @@ Sign.prototype._write = function _write(chunk, encoding, callback) { Sign.prototype.update = function update(data, encoding) { encoding = encoding || getDefaultEncoding(); - data = toBuf(data, encoding); - if (!isArrayBufferView(data)) { - throw new ERR_INVALID_ARG_TYPE( - 'data', - ['string', 'Buffer', 'TypedArray', 'DataView'], - data - ); - } + data = checkIsArrayBufferView('data', toBuf(data, encoding)); this._handle.update(data); return this; }; +function getPadding(options) { + return getIntOption('padding', RSA_PKCS1_PADDING, options); +} + +function getSaltLength(options) { + return getIntOption('saltLength', RSA_PSS_SALTLEN_AUTO, options); +} + +function getIntOption(name, defaultValue, options) { + if (options.hasOwnProperty(name)) { + if (options[name] === options[name] >> 0) { + return options[name]; + } else { + throw new ERR_INVALID_OPT_VALUE(name, options[name]); + } + } + return defaultValue; +} + Sign.prototype.sign = function sign(options, encoding) { if (!options) throw new ERR_CRYPTO_SIGN_KEY_REQUIRED(); @@ -61,32 +73,11 @@ Sign.prototype.sign = function sign(options, encoding) { var passphrase = options.passphrase || null; // Options specific to RSA - var rsaPadding = RSA_PKCS1_PADDING; - if (options.hasOwnProperty('padding')) { - if (options.padding === options.padding >> 0) { - rsaPadding = options.padding; - } else { - throw new ERR_INVALID_OPT_VALUE('padding', options.padding); - } - } + var rsaPadding = getPadding(options); - var pssSaltLength = RSA_PSS_SALTLEN_AUTO; - if (options.hasOwnProperty('saltLength')) { - if (options.saltLength === options.saltLength >> 0) { - pssSaltLength = options.saltLength; - } else { - throw new ERR_INVALID_OPT_VALUE('saltLength', options.saltLength); - } - } + var pssSaltLength = getSaltLength(options); - key = toBuf(key); - if (!isArrayBufferView(key)) { - throw new ERR_INVALID_ARG_TYPE( - 'key', - ['string', 'Buffer', 'TypedArray', 'DataView'], - key - ); - } + key = checkIsArrayBufferView('key', toBuf(key)); var ret = this._handle.sign(key, passphrase, rsaPadding, pssSaltLength); @@ -119,41 +110,14 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) { sigEncoding = sigEncoding || getDefaultEncoding(); // Options specific to RSA - var rsaPadding = RSA_PKCS1_PADDING; - if (options.hasOwnProperty('padding')) { - if (options.padding === options.padding >> 0) { - rsaPadding = options.padding; - } else { - throw new ERR_INVALID_OPT_VALUE('padding', options.padding); - } - } + var rsaPadding = getPadding(options); - var pssSaltLength = RSA_PSS_SALTLEN_AUTO; - if (options.hasOwnProperty('saltLength')) { - if (options.saltLength === options.saltLength >> 0) { - pssSaltLength = options.saltLength; - } else { - throw new ERR_INVALID_OPT_VALUE('saltLength', options.saltLength); - } - } + var pssSaltLength = getSaltLength(options); - key = toBuf(key); - if (!isArrayBufferView(key)) { - throw new ERR_INVALID_ARG_TYPE( - 'key', - ['string', 'Buffer', 'TypedArray', 'DataView'], - key - ); - } + key = checkIsArrayBufferView('key', toBuf(key)); - signature = toBuf(signature, sigEncoding); - if (!isArrayBufferView(signature)) { - throw new ERR_INVALID_ARG_TYPE( - 'signature', - ['string', 'Buffer', 'TypedArray', 'DataView'], - signature - ); - } + signature = checkIsArrayBufferView('signature', + toBuf(signature, sigEncoding)); return this._handle.verify(key, signature, rsaPadding, pssSaltLength); }; diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index 095ca0478bd99f..59a5d57a1e4f39 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -83,7 +83,19 @@ function timingSafeEqual(buf1, buf2) { return _timingSafeEqual(buf1, buf2); } +function checkIsArrayBufferView(name, buffer) { + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + name, + ['string', 'Buffer', 'TypedArray', 'DataView'], + buffer + ); + } + return buffer; +} + module.exports = { + checkIsArrayBufferView, getCiphers, getCurves, getDefaultEncoding, diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 09f58506c44ea0..5299b66c831061 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -15,6 +15,7 @@ const kInfo = Symbol('info'); const messages = new Map(); const codes = {}; +let blue = ''; let green = ''; let red = ''; let white = ''; @@ -259,7 +260,7 @@ function createErrDiff(actual, expected, operator) { const expectedLines = inspectValue(expected); const msg = READABLE_OPERATOR[operator] + `:\n${green}+ expected${white} ${red}- actual${white}`; - const skippedMsg = ' ... Lines skipped'; + const skippedMsg = ` ${blue}...${white} Lines skipped`; // Remove all ending lines that match (this optimizes the output for // readability by reducing the number of total changed lines). @@ -280,7 +281,7 @@ function createErrDiff(actual, expected, operator) { b = expectedLines[expectedLines.length - 1]; } if (i > 3) { - end = `\n...${end}`; + end = `\n${blue}...${white}${end}`; skipped = true; } if (other !== '') { @@ -297,7 +298,7 @@ function createErrDiff(actual, expected, operator) { if (actualLines.length < i + 1) { if (cur > 1 && i > 2) { if (cur > 4) { - res += '\n...'; + res += `\n${blue}...${white}`; skipped = true; } else if (cur > 3) { res += `\n ${expectedLines[i - 2]}`; @@ -313,7 +314,7 @@ function createErrDiff(actual, expected, operator) { } else if (expectedLines.length < i + 1) { if (cur > 1 && i > 2) { if (cur > 4) { - res += '\n...'; + res += `\n${blue}...${white}`; skipped = true; } else if (cur > 3) { res += `\n ${actualLines[i - 2]}`; @@ -329,7 +330,7 @@ function createErrDiff(actual, expected, operator) { } else if (actualLines[i] !== expectedLines[i]) { if (cur > 1 && i > 2) { if (cur > 4) { - res += '\n...'; + res += `\n${blue}...${white}`; skipped = true; } else if (cur > 3) { res += `\n ${actualLines[i - 2]}`; @@ -354,19 +355,17 @@ function createErrDiff(actual, expected, operator) { } // Inspected object to big (Show ~20 rows max) if (printedLines > 20 && i < maxLines - 2) { - return `${msg}${skippedMsg}\n${res}\n...${other}\n...`; + return `${msg}${skippedMsg}\n${res}\n${blue}...${white}${other}\n` + + `${blue}...${white}`; } } // Strict equal with identical objects that are not identical by reference. if (identical === maxLines) { - let base = 'Input object identical but not reference equal:'; - - if (operator !== 'strictEqual') { - // This code path should not be possible to reach. - // The output is identical but it is not clear why. - base = 'Input objects not identical:'; - } + // E.g., assert.deepStrictEqual(Symbol(), Symbol()) + const base = operator === 'strictEqual' ? + 'Input objects identical but not reference equal:' : + 'Input objects not identical:'; // We have to get the result again. The lines were all removed before. const actualLines = inspectValue(actual); @@ -374,13 +373,13 @@ function createErrDiff(actual, expected, operator) { // Only remove lines in case it makes sense to collapse those. // TODO: Accept env to always show the full error. if (actualLines.length > 30) { - actualLines[26] = '...'; + actualLines[26] = `${blue}...${white}`; while (actualLines.length > 27) { actualLines.pop(); } } - return `${base}\n\n ${actualLines.join('\n ')}\n`; + return `${base}\n\n${actualLines.join('\n')}\n`; } return `${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}`; } @@ -405,10 +404,12 @@ class AssertionError extends Error { // Reset on each call to make sure we handle dynamically set environment // variables correct. if (process.stdout.getColorDepth() !== 1) { + blue = '\u001b[34m'; green = '\u001b[32m'; white = '\u001b[39m'; red = '\u001b[31m'; } else { + blue = ''; green = ''; white = ''; red = ''; @@ -438,7 +439,7 @@ class AssertionError extends Error { // Only remove lines in case it makes sense to collapse those. // TODO: Accept env to always show the full error. if (res.length > 30) { - res[26] = '...'; + res[26] = `${blue}...${white}`; while (res.length > 27) { res.pop(); } @@ -448,7 +449,7 @@ class AssertionError extends Error { if (res.length === 1) { super(`${base} ${res[0]}`); } else { - super(`${base}\n\n ${res.join('\n ')}\n`); + super(`${base}\n\n${res.join('\n')}\n`); } } else { let res = util.inspect(actual); @@ -881,7 +882,6 @@ E('ERR_INVALID_CALLBACK', 'Callback must be a function', TypeError); E('ERR_INVALID_CHAR', invalidChar, TypeError); E('ERR_INVALID_CURSOR_POS', 'Cannot set cursor row without setting its column', TypeError); -E('ERR_INVALID_DOMAIN_NAME', 'Unable to determine the domain name', TypeError); E('ERR_INVALID_FD', '"fd" must be a positive integer: %s', RangeError); E('ERR_INVALID_FD_TYPE', 'Unsupported fd type: %s', TypeError); @@ -952,7 +952,7 @@ E('ERR_NO_LONGER_SUPPORTED', '%s is no longer supported', Error); E('ERR_OUT_OF_RANGE', outOfRange, RangeError); E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s', Error); E('ERR_SCRIPT_EXECUTION_INTERRUPTED', - 'Script execution was interrupted by `SIGINT`.', Error); + 'Script execution was interrupted by `SIGINT`', Error); E('ERR_SERVER_ALREADY_LISTEN', 'Listen method has been called more than once without closing.', Error); E('ERR_SERVER_NOT_RUNNING', 'Server is not running.', Error); @@ -1017,6 +1017,7 @@ E('ERR_UNHANDLED_ERROR', if (err === undefined) return msg; return `${msg} (${err})`; }, Error); +E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error); E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError); // This should probably be a `TypeError`. diff --git a/lib/internal/fs.js b/lib/internal/fs.js index 0cfb89a9d3d084..2f7a8d8ced176e 100644 --- a/lib/internal/fs.js +++ b/lib/internal/fs.js @@ -70,9 +70,6 @@ function getOptions(options, defaultOptions) { return options; } -function isInt32(n) { return n === (n | 0); } -function isUint32(n) { return n === (n >>> 0); } - function modeNum(m, def) { if (typeof m === 'number') return m; @@ -341,26 +338,6 @@ function validateBuffer(buffer) { } } -function validateLen(len) { - let err; - - if (!isInt32(len)) { - if (typeof len !== 'number') { - err = new ERR_INVALID_ARG_TYPE('len', 'number', len); - } else if (!Number.isInteger(len)) { - err = new ERR_OUT_OF_RANGE('len', 'an integer', len); - } else { - // 2 ** 31 === 2147483648 - err = new ERR_OUT_OF_RANGE('len', '> -2147483649 && < 2147483648', len); - } - } - - if (err !== undefined) { - Error.captureStackTrace(err, validateLen); - throw err; - } -} - function validateOffsetLengthRead(offset, length, bufferLength) { let err; @@ -410,28 +387,10 @@ function validatePath(path, propName = 'path') { } } -function validateUint32(value, propName) { - if (!isUint32(value)) { - let err; - if (typeof value !== 'number') { - err = new ERR_INVALID_ARG_TYPE(propName, 'number', value); - } else if (!Number.isInteger(value)) { - err = new ERR_OUT_OF_RANGE(propName, 'an integer', value); - } else { - // 2 ** 32 === 4294967296 - err = new ERR_OUT_OF_RANGE(propName, '>= 0 && < 4294967296', value); - } - Error.captureStackTrace(err, validateUint32); - throw err; - } -} - module.exports = { assertEncoding, copyObject, getOptions, - isInt32, - isUint32, modeNum, nullCheck, preprocessSymlinkDestination, @@ -443,9 +402,7 @@ module.exports = { SyncWriteStream, toUnixTimestamp, validateBuffer, - validateLen, validateOffsetLengthRead, validateOffsetLengthWrite, - validatePath, - validateUint32 + validatePath }; diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 0bdbe7b69fdf10..d2aaa8838e2cbc 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -31,6 +31,7 @@ const kTrailers = Symbol('trailers'); const kRawTrailers = Symbol('rawTrailers'); const kProxySocket = Symbol('proxySocket'); const kSetHeader = Symbol('setHeader'); +const kAborted = Symbol('aborted'); const { HTTP2_HEADER_AUTHORITY, @@ -137,6 +138,7 @@ function onStreamDrain() { function onStreamAbortedRequest() { const request = this[kRequest]; if (request !== undefined && request[kState].closed === false) { + request[kAborted] = true; request.emit('aborted'); } } @@ -233,6 +235,7 @@ class Http2ServerRequest extends Readable { this[kTrailers] = {}; this[kRawTrailers] = []; this[kStream] = stream; + this[kAborted] = false; stream[kProxySocket] = null; stream[kRequest] = this; @@ -248,6 +251,10 @@ class Http2ServerRequest extends Readable { this.on('resume', onRequestResume); } + get aborted() { + return this[kAborted]; + } + get complete() { return this._readableState.ended || this[kState].closed || diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 495fa60ba39ce3..a9fdcb659544e5 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -206,6 +206,7 @@ const STREAM_FLAGS_CLOSED = 0x2; const STREAM_FLAGS_HEADERS_SENT = 0x4; const STREAM_FLAGS_HEAD_REQUEST = 0x8; const STREAM_FLAGS_ABORTED = 0x10; +const STREAM_FLAGS_HAS_TRAILERS = 0x20; const SESSION_FLAGS_PENDING = 0x0; const SESSION_FLAGS_READY = 0x1; @@ -330,26 +331,13 @@ function onStreamClose(code) { if (stream.destroyed) return; - const state = stream[kState]; - debug(`Http2Stream ${stream[kID]} [Http2Session ` + `${sessionName(stream[kSession][kType])}]: closed with code ${code}`); - if (!stream.closed) { - // Clear timeout and remove timeout listeners - stream.setTimeout(0); - stream.removeAllListeners('timeout'); + if (!stream.closed) + closeStream(stream, code, false); - // Set the state flags - state.flags |= STREAM_FLAGS_CLOSED; - state.rstCode = code; - - // Close the writable side of the stream - abort(stream); - stream.end(); - } - - state.fd = -1; + stream[kState].fd = -1; // Defer destroy we actually emit end. if (stream._readableState.endEmitted || code !== NGHTTP2_NO_ERROR) { // If errored or ended, we can destroy immediately. @@ -504,7 +492,7 @@ function requestOnConnect(headers, options) { // At this point, the stream should have already been destroyed during // the session.destroy() method. Do nothing else. - if (session.destroyed) + if (session === undefined || session.destroyed) return; // If the session was closed while waiting for the connect, destroy @@ -716,8 +704,11 @@ const proxySocketHandler = { // data received on the PING acknowlegement. function pingCallback(cb) { return function pingCallback(ack, duration, payload) { - const err = ack ? null : new ERR_HTTP2_PING_CANCEL(); - cb(err, duration, payload); + if (ack) { + cb(null, duration, payload); + } else { + cb(new ERR_HTTP2_PING_CANCEL()); + } }; } @@ -1412,6 +1403,9 @@ class ClientHttp2Session extends Http2Session { if (options.endStream) stream.end(); + if (options.waitForTrailers) + stream[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; + const onConnect = requestOnConnect.bind(stream, headersList, options); if (this.connecting) { this.on('connect', onConnect); @@ -1445,8 +1439,11 @@ function afterDoStreamWrite(status, handle) { } function streamOnResume() { - if (!this.destroyed && !this.pending) + if (!this.destroyed && !this.pending) { + if (!this[kState].didRead) + this[kState].didRead = true; this[kHandle].readStart(); + } } function streamOnPause() { @@ -1454,16 +1451,6 @@ function streamOnPause() { this[kHandle].readStop(); } -// If the writable side of the Http2Stream is still open, emit the -// 'aborted' event and set the aborted flag. -function abort(stream) { - if (!stream.aborted && - !(stream._writableState.ended || stream._writableState.ending)) { - stream[kState].flags |= STREAM_FLAGS_ABORTED; - stream.emit('aborted'); - } -} - function afterShutdown() { this.callback(); const stream = this.handle[kOwner]; @@ -1471,6 +1458,51 @@ function afterShutdown() { stream[kMaybeDestroy](); } +function closeStream(stream, code, shouldSubmitRstStream = true) { + const state = stream[kState]; + state.flags |= STREAM_FLAGS_CLOSED; + state.rstCode = code; + + // Clear timeout and remove timeout listeners + stream.setTimeout(0); + stream.removeAllListeners('timeout'); + + const { ending, finished } = stream._writableState; + + if (!ending) { + // If the writable side of the Http2Stream is still open, emit the + // 'aborted' event and set the aborted flag. + if (!stream.aborted) { + state.flags |= STREAM_FLAGS_ABORTED; + stream.emit('aborted'); + } + + // Close the writable side. + stream.end(); + } + + if (shouldSubmitRstStream) { + const finishFn = finishCloseStream.bind(stream, code); + if (!ending || finished || code !== NGHTTP2_NO_ERROR) + finishFn(); + else + stream.once('finish', finishFn); + } +} + +function finishCloseStream(code) { + const rstStreamFn = submitRstStream.bind(this, code); + // If the handle has not yet been assigned, queue up the request to + // ensure that the RST_STREAM frame is sent after the stream ID has + // been determined. + if (this.pending) { + this.push(null); + this.once('ready', rstStreamFn); + return; + } + rstStreamFn(); +} + // An Http2Stream is a Duplex stream that is backed by a // node::http2::Http2Stream handle implementing StreamBase. class Http2Stream extends Duplex { @@ -1490,6 +1522,7 @@ class Http2Stream extends Duplex { this[kTimeout] = null; this[kState] = { + didRead: false, flags: STREAM_FLAGS_PENDING, rstCode: NGHTTP2_NO_ERROR, writeQueueSize: 0, @@ -1756,6 +1789,8 @@ class Http2Stream extends Duplex { throw headersList; this[kSentTrailers] = headers; + this[kState].flags &= ~STREAM_FLAGS_HAS_TRAILERS; + const ret = this[kHandle].trailers(headersList); if (ret < 0) this.destroy(new NghttpError(ret)); @@ -1786,38 +1821,13 @@ class Http2Stream extends Duplex { if (callback !== undefined && typeof callback !== 'function') throw new ERR_INVALID_CALLBACK(); - // Clear timeout and remove timeout listeners - this.setTimeout(0); - this.removeAllListeners('timeout'); - - // Close the writable - abort(this); - this.end(); - if (this.closed) return; - const state = this[kState]; - state.flags |= STREAM_FLAGS_CLOSED; - state.rstCode = code; - - if (callback !== undefined) { + if (callback !== undefined) this.once('close', callback); - } - - if (this[kHandle] === undefined) - return; - const rstStreamFn = submitRstStream.bind(this, code); - // If the handle has not yet been assigned, queue up the request to - // ensure that the RST_STREAM frame is sent after the stream ID has - // been determined. - if (this.pending) { - this.push(null); - this.once('ready', rstStreamFn); - return; - } - rstStreamFn(); + closeStream(this, code); } // Called by this.destroy(). @@ -1832,26 +1842,19 @@ class Http2Stream extends Duplex { debug(`Http2Stream ${this[kID] || ''} [Http2Session ` + `${sessionName(session[kType])}]: destroying stream`); const state = this[kState]; - const code = state.rstCode = - err != null ? - NGHTTP2_INTERNAL_ERROR : - state.rstCode || NGHTTP2_NO_ERROR; - if (handle !== undefined) { - // If the handle exists, we need to close, then destroy the handle - this.close(code); - if (!this._readableState.ended && !this._readableState.ending) - this.push(null); + const code = err != null ? + NGHTTP2_INTERNAL_ERROR : (state.rstCode || NGHTTP2_NO_ERROR); + + const hasHandle = handle !== undefined; + + if (!this.closed) + closeStream(this, code, hasHandle); + this.push(null); + + if (hasHandle) { handle.destroy(); session[kState].streams.delete(id); } else { - // Clear timeout and remove timeout listeners - this.setTimeout(0); - this.removeAllListeners('timeout'); - - state.flags |= STREAM_FLAGS_CLOSED; - abort(this); - this.end(); - this.push(null); session[kState].pendingStreams.delete(this); } @@ -1884,13 +1887,23 @@ class Http2Stream extends Duplex { } // TODO(mcollina): remove usage of _*State properties - if (this._readableState.ended && - this._writableState.ended && - this._writableState.pendingcb === 0 && - this.closed) { - this.destroy(); - // This should return, but eslint complains. - // return + if (this._writableState.ended && this._writableState.pendingcb === 0) { + if (this._readableState.ended && this.closed) { + this.destroy(); + return; + } + + // We've submitted a response from our server session, have not attempted + // to process any incoming data, and have no trailers. This means we can + // attempt to gracefully close the session. + const state = this[kState]; + if (this.headersSent && + this[kSession][kType] === NGHTTP2_SESSION_SERVER && + !(state.flags & STREAM_FLAGS_HAS_TRAILERS) && + !state.didRead && + !this._readableState.resumeScheduled) { + this.close(); + } } } } @@ -2095,7 +2108,6 @@ function afterOpen(session, options, headers, streamOptions, err, fd) { } if (this.destroyed || this.closed) { tryClose(fd); - abort(this); return; } state.fd = fd; @@ -2224,8 +2236,10 @@ class ServerHttp2Stream extends Http2Stream { if (options.endStream) streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD; - if (options.waitForTrailers) + if (options.waitForTrailers) { streamOptions |= STREAM_OPTION_GET_TRAILERS; + state.flags |= STREAM_FLAGS_HAS_TRAILERS; + } headers = processHeaders(headers); const statusCode = headers[HTTP2_HEADER_STATUS] |= 0; @@ -2285,8 +2299,10 @@ class ServerHttp2Stream extends Http2Stream { } let streamOptions = 0; - if (options.waitForTrailers) + if (options.waitForTrailers) { streamOptions |= STREAM_OPTION_GET_TRAILERS; + this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; + } if (typeof fd !== 'number') throw new ERR_INVALID_ARG_TYPE('fd', 'number', fd); @@ -2346,8 +2362,10 @@ class ServerHttp2Stream extends Http2Stream { } let streamOptions = 0; - if (options.waitForTrailers) + if (options.waitForTrailers) { streamOptions |= STREAM_OPTION_GET_TRAILERS; + this[kState].flags |= STREAM_FLAGS_HAS_TRAILERS; + } const session = this[kSession]; debug(`Http2Stream ${this[kID]} [Http2Session ` + diff --git a/lib/internal/process/methods.js b/lib/internal/process/methods.js new file mode 100644 index 00000000000000..503fd317f60395 --- /dev/null +++ b/lib/internal/process/methods.js @@ -0,0 +1,137 @@ +'use strict'; + +const { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_UNKNOWN_CREDENTIAL +} = require('internal/errors').codes; +const { + validateUint32 +} = require('internal/validators'); + +function setupProcessMethods() { + // Non-POSIX platforms like Windows don't have certain methods. + if (process.setgid !== undefined) { + setupPosixMethods(); + } + + const { + chdir: _chdir, + umask: _umask, + } = process; + + process.chdir = chdir; + process.umask = umask; + + function chdir(directory) { + if (typeof directory !== 'string') { + throw new ERR_INVALID_ARG_TYPE('directory', 'string', directory); + } + return _chdir(directory); + } + + const octalReg = /^[0-7]+$/; + function umask(mask) { + if (typeof mask === 'undefined') { + return _umask(mask); + } + + if (typeof mask === 'number') { + validateUint32(mask, 'mask'); + return _umask(mask); + } + + if (typeof mask === 'string') { + if (!octalReg.test(mask)) { + throw new ERR_INVALID_ARG_VALUE('mask', mask, + 'must be an octal string'); + } + const octal = Number.parseInt(mask, 8); + validateUint32(octal, 'mask'); + return _umask(octal); + } + + throw new ERR_INVALID_ARG_TYPE('mask', ['number', 'string', 'undefined'], + mask); + } +} + +function setupPosixMethods() { + const { + initgroups: _initgroups, + setegid: _setegid, + seteuid: _seteuid, + setgid: _setgid, + setuid: _setuid, + setgroups: _setgroups + } = process; + + process.initgroups = initgroups; + process.setegid = setegid; + process.seteuid = seteuid; + process.setgid = setgid; + process.setuid = setuid; + process.setgroups = setgroups; + + function initgroups(user, extraGroup) { + validateId(user, 'user'); + validateId(extraGroup, 'extraGroup'); + // Result is 0 on success, 1 if user is unknown, 2 if group is unknown. + const result = _initgroups(user, extraGroup); + if (result === 1) { + throw new ERR_UNKNOWN_CREDENTIAL('User', user); + } else if (result === 2) { + throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup); + } + } + + function setegid(id) { + return execId(id, 'Group', _setegid); + } + + function seteuid(id) { + return execId(id, 'User', _seteuid); + } + + function setgid(id) { + return execId(id, 'Group', _setgid); + } + + function setuid(id) { + return execId(id, 'User', _setuid); + } + + function setgroups(groups) { + if (!Array.isArray(groups)) { + throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups); + } + for (var i = 0; i < groups.length; i++) { + validateId(groups[i], `groups[${i}]`); + } + // Result is 0 on success. A positive integer indicates that the + // corresponding group was not found. + const result = _setgroups(groups); + if (result > 0) { + throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]); + } + } + + function execId(id, type, method) { + validateId(id, 'id'); + // Result is 0 on success, 1 if credential is unknown. + const result = method(id); + if (result === 1) { + throw new ERR_UNKNOWN_CREDENTIAL(type, id); + } + } + + function validateId(id, name) { + if (typeof id === 'number') { + validateUint32(id, name); + } else if (typeof id !== 'string') { + throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id); + } + } +} + +exports.setup = setupProcessMethods; diff --git a/lib/internal/streams/async_iterator.js b/lib/internal/streams/async_iterator.js index 9ca8e5ebe23b15..0e34573d877aee 100644 --- a/lib/internal/streams/async_iterator.js +++ b/lib/internal/streams/async_iterator.js @@ -58,7 +58,7 @@ function onError(iter, err) { iter[kLastReject] = null; reject(err); } - iter.error = err; + iter[kError] = err; } function wrapForNext(lastPromise, iter) { diff --git a/lib/internal/util.js b/lib/internal/util.js index ce25317b778a3f..071563a737815b 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -356,16 +356,17 @@ function isInsideNodeModules() { // Iterate over all stack frames and look for the first one not coming // from inside Node.js itself: - for (const frame of stack) { - const filename = frame.getFileName(); - // If a filename does not start with / or contain \, - // it's likely from Node.js core. - if (!/^\/|\\/.test(filename)) - continue; - return kNodeModulesRE.test(filename); + if (Array.isArray(stack)) { + for (const frame of stack) { + const filename = frame.getFileName(); + // If a filename does not start with / or contain \, + // it's likely from Node.js core. + if (!/^\/|\\/.test(filename)) + continue; + return kNodeModulesRE.test(filename); + } } - - return false; // This should be unreachable. + return false; } diff --git a/lib/internal/validators.js b/lib/internal/validators.js new file mode 100644 index 00000000000000..556bfb2dc08f5f --- /dev/null +++ b/lib/internal/validators.js @@ -0,0 +1,58 @@ +'use strict'; + +const { + ERR_INVALID_ARG_TYPE, + ERR_OUT_OF_RANGE +} = require('internal/errors').codes; + +function isInt32(value) { + return value === (value | 0); +} + +function isUint32(value) { + return value === (value >>> 0); +} + +function validateInt32(value, name) { + if (!isInt32(value)) { + let err; + if (typeof value !== 'number') { + err = new ERR_INVALID_ARG_TYPE(name, 'number', value); + } else if (!Number.isInteger(value)) { + err = new ERR_OUT_OF_RANGE(name, 'an integer', value); + } else { + // 2 ** 31 === 2147483648 + err = new ERR_OUT_OF_RANGE(name, '> -2147483649 && < 2147483648', value); + } + Error.captureStackTrace(err, validateInt32); + throw err; + } +} + +function validateUint32(value, name, positive) { + if (!isUint32(value)) { + let err; + if (typeof value !== 'number') { + err = new ERR_INVALID_ARG_TYPE(name, 'number', value); + } else if (!Number.isInteger(value)) { + err = new ERR_OUT_OF_RANGE(name, 'an integer', value); + } else { + const min = positive ? 1 : 0; + // 2 ** 32 === 4294967296 + err = new ERR_OUT_OF_RANGE(name, `>= ${min} && < 4294967296`, value); + } + Error.captureStackTrace(err, validateUint32); + throw err; + } else if (positive && value === 0) { + const err = new ERR_OUT_OF_RANGE(name, '>= 1 && < 4294967296', value); + Error.captureStackTrace(err, validateUint32); + throw err; + } +} + +module.exports = { + isInt32, + isUint32, + validateInt32, + validateUint32 +}; diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 7284c8bd619901..9e945c45c3de8a 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -6,7 +6,6 @@ const { URL } = require('internal/url'); const { isContext } = process.binding('contextify'); const { ERR_INVALID_ARG_TYPE, - ERR_OUT_OF_RANGE, ERR_VM_MODULE_ALREADY_LINKED, ERR_VM_MODULE_DIFFERENT_CONTEXT, ERR_VM_MODULE_LINKING_ERRORED, @@ -19,6 +18,7 @@ const { customInspectSymbol, } = require('internal/util'); const { SafePromise } = require('internal/safe_globals'); +const { validateInt32, validateUint32 } = require('internal/validators'); const { ModuleWrap, @@ -92,8 +92,8 @@ class Module { perContextModuleId.set(context, 1); } - validateInteger(lineOffset, 'options.lineOffset'); - validateInteger(columnOffset, 'options.columnOffset'); + validateInt32(lineOffset, 'options.lineOffset'); + validateInt32(columnOffset, 'options.columnOffset'); if (initializeImportMeta !== undefined) { if (typeof initializeImportMeta === 'function') { @@ -203,9 +203,8 @@ class Module { let timeout = options.timeout; if (timeout === undefined) { timeout = -1; - } else if (!Number.isInteger(timeout) || timeout <= 0) { - throw new ERR_INVALID_ARG_TYPE('options.timeout', 'a positive integer', - timeout); + } else { + validateUint32(timeout, 'options.timeout', true); } const { breakOnSigint = false } = options; @@ -243,15 +242,6 @@ class Module { } } -function validateInteger(prop, propName) { - if (!Number.isInteger(prop)) { - throw new ERR_INVALID_ARG_TYPE(propName, 'integer', prop); - } - if ((prop >> 0) !== prop) { - throw new ERR_OUT_OF_RANGE(propName, '32-bit integer', prop); - } -} - module.exports = { Module, initImportMetaMap, diff --git a/lib/timers.js b/lib/timers.js index 15700f5a1212ab..30bffb432ac26b 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -285,15 +285,18 @@ function tryOnTimeout(timer, start) { var threw = true; if (timerAsyncId !== null) emitBefore(timerAsyncId, timer[trigger_async_id_symbol]); + if (start === undefined && timer._repeat) + start = TimerWrap.now(); try { - ontimeout(timer, start); + ontimeout(timer); threw = false; } finally { if (timerAsyncId !== null) { if (!threw) emitAfter(timerAsyncId); - if ((threw || !timer._repeat) && destroyHooksExist() && - !timer._destroyed) { + if (timer._repeat) { + rearm(timer, start); + } else if (destroyHooksExist() && !timer._destroyed) { emitDestroy(timerAsyncId); timer._destroyed = true; } @@ -417,18 +420,14 @@ setTimeout[internalUtil.promisify.custom] = function(after, value) { exports.setTimeout = setTimeout; -function ontimeout(timer, start) { +function ontimeout(timer) { const args = timer._timerArgs; if (typeof timer._onTimeout !== 'function') return promiseResolve(timer._onTimeout, args[0]); - if (start === undefined && timer._repeat) - start = TimerWrap.now(); if (!args) timer._onTimeout(); else Reflect.apply(timer._onTimeout, timer, args); - if (timer._repeat) - rearm(timer, start); } function rearm(timer, start = TimerWrap.now()) { diff --git a/lib/tls.js b/lib/tls.js index 1e444d5d8898c2..28cd302674b04c 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -32,6 +32,7 @@ const url = require('url'); const binding = process.binding('crypto'); const { Buffer } = require('buffer'); const EventEmitter = require('events'); +const { URL } = require('internal/url'); const DuplexPair = require('internal/streams/duplexpair'); const { canonicalizeIP } = process.binding('cares_wrap'); const _tls_common = require('_tls_common'); @@ -169,6 +170,7 @@ function check(hostParts, pattern, wildcards) { return true; } +let urlWarningEmitted = false; exports.checkServerIdentity = function checkServerIdentity(host, cert) { const subject = cert.subject; const altNames = cert.subjectaltname; @@ -183,7 +185,21 @@ exports.checkServerIdentity = function checkServerIdentity(host, cert) { if (name.startsWith('DNS:')) { dnsNames.push(name.slice(4)); } else if (name.startsWith('URI:')) { - const uri = url.parse(name.slice(4)); + let uri; + try { + uri = new URL(name.slice(4)); + } catch (err) { + uri = url.parse(name.slice(4)); + if (!urlWarningEmitted && !process.noDeprecation) { + urlWarningEmitted = true; + process.emitWarning( + `The URI ${name.slice(4)} found in cert.subjectaltname ` + + 'is not a valid URI, and is supported in the tls module ' + + 'solely for compatibility.', + 'DeprecationWarning', 'DEP0109'); + } + } + uriNames.push(uri.hostname); // TODO(bnoordhuis) Also use scheme. } else if (name.startsWith('IP Address:')) { ips.push(canonicalizeIP(name.slice(11))); diff --git a/lib/url.js b/lib/url.js index ac9879a650fce6..e4326e80b5d948 100644 --- a/lib/url.js +++ b/lib/url.js @@ -281,9 +281,6 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { // http://a@b@c/ => user:a@b host:c // http://a@b?@c => user:a host:b path:/?@c - // v0.12 TODO(isaacs): This is not quite how Chrome does things. - // Review our test case against browsers more comprehensively. - var hostEnd = -1; var atSign = -1; var nonHost = -1; diff --git a/lib/vm.js b/lib/vm.js index 5a5130d7c9c328..3ab50c81580365 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -28,11 +28,9 @@ const { isContext: _isContext, } = process.binding('contextify'); -const { - ERR_INVALID_ARG_TYPE, - ERR_OUT_OF_RANGE -} = require('internal/errors').codes; +const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; const { isUint8Array } = require('internal/util/types'); +const { validateInt32, validateUint32 } = require('internal/validators'); class Script extends ContextifyScript { constructor(code, options = {}) { @@ -56,8 +54,8 @@ class Script extends ContextifyScript { if (typeof filename !== 'string') { throw new ERR_INVALID_ARG_TYPE('options.filename', 'string', filename); } - validateInteger(lineOffset, 'options.lineOffset'); - validateInteger(columnOffset, 'options.columnOffset'); + validateInt32(lineOffset, 'options.lineOffset'); + validateInt32(columnOffset, 'options.columnOffset'); if (cachedData !== undefined && !isUint8Array(cachedData)) { throw new ERR_INVALID_ARG_TYPE('options.cachedData', ['Buffer', 'Uint8Array'], cachedData); @@ -119,15 +117,6 @@ function validateContext(sandbox) { } } -function validateInteger(prop, propName) { - if (!Number.isInteger(prop)) { - throw new ERR_INVALID_ARG_TYPE(propName, 'integer', prop); - } - if ((prop >> 0) !== prop) { - throw new ERR_OUT_OF_RANGE(propName, '32-bit integer', prop); - } -} - function validateString(prop, propName) { if (prop !== undefined && typeof prop !== 'string') throw new ERR_INVALID_ARG_TYPE(propName, 'string', prop); @@ -151,9 +140,8 @@ function getRunInContextArgs(options = {}) { let timeout = options.timeout; if (timeout === undefined) { timeout = -1; - } else if (!Number.isInteger(timeout) || timeout <= 0) { - throw new ERR_INVALID_ARG_TYPE('options.timeout', 'a positive integer', - timeout); + } else { + validateUint32(timeout, 'options.timeout', true); } const { diff --git a/node.gyp b/node.gyp index 8347beb18245ca..78a15bd7b838a7 100644 --- a/node.gyp +++ b/node.gyp @@ -118,6 +118,7 @@ 'lib/internal/net.js', 'lib/internal/os.js', 'lib/internal/process/esm_loader.js', + 'lib/internal/process/methods.js', 'lib/internal/process/next_tick.js', 'lib/internal/process/promises.js', 'lib/internal/process/stdio.js', @@ -146,6 +147,7 @@ 'lib/internal/v8.js', 'lib/internal/v8_prof_polyfill.js', 'lib/internal/v8_prof_processor.js', + 'lib/internal/validators.js', 'lib/internal/stream_base_commons.js', 'lib/internal/vm/module.js', 'lib/internal/streams/lazy_transform.js', @@ -680,13 +682,13 @@ 'toolsets': ['host'], 'conditions': [ [ 'v8_enable_inspector==1', { - 'actions': [ + 'copies': [ { - 'action_name': 'v8_inspector_copy_protocol_to_intermediate_folder', - 'inputs': [ 'deps/v8/src/inspector/js_protocol.pdl' ], - 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/js_protocol.pdl' ], - 'action': [ 'cp', '<@(_inputs)', '<(SHARED_INTERMEDIATE_DIR)' ], - }, + 'destination': '<(SHARED_INTERMEDIATE_DIR)', + 'files': ['deps/v8/src/inspector/js_protocol.pdl'] + } + ], + 'actions': [ { 'action_name': 'v8_inspector_convert_protocol_to_json', 'inputs': [ @@ -961,6 +963,7 @@ 'test/cctest/test_base64.cc', 'test/cctest/test_node_postmortem_metadata.cc', 'test/cctest/test_environment.cc', + 'test/cctest/test_platform.cc', 'test/cctest/test_util.cc', 'test/cctest/test_url.cc' ], diff --git a/src/env.cc b/src/env.cc index 1f47ea21af21b8..08d719a51011d1 100644 --- a/src/env.cc +++ b/src/env.cc @@ -28,44 +28,46 @@ IsolateData::IsolateData(Isolate* isolate, uv_loop_t* event_loop, MultiIsolatePlatform* platform, uint32_t* zero_fill_field) : - -// Create string and private symbol properties as internalized one byte strings. -// -// Internalized because it makes property lookups a little faster and because -// the string is created in the old space straight away. It's going to end up -// in the old space sooner or later anyway but now it doesn't go through -// v8::Eternal's new space handling first. -// -// One byte because our strings are ASCII and we can safely skip V8's UTF-8 -// decoding step. It's a one-time cost, but why pay it when you don't have to? -#define V(PropertyName, StringValue) \ - PropertyName ## _( \ - isolate, \ - Private::New( \ - isolate, \ - String::NewFromOneByte( \ - isolate, \ - reinterpret_cast(StringValue), \ - v8::NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked())), - PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) -#undef V -#define V(PropertyName, StringValue) \ - PropertyName ## _( \ - isolate, \ - String::NewFromOneByte( \ - isolate, \ - reinterpret_cast(StringValue), \ - v8::NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked()), - PER_ISOLATE_STRING_PROPERTIES(V) -#undef V isolate_(isolate), event_loop_(event_loop), zero_fill_field_(zero_fill_field), platform_(platform) { if (platform_ != nullptr) platform_->RegisterIsolate(this, event_loop); + + // Create string and private symbol properties as internalized one byte + // strings after the platform is properly initialized. + // + // Internalized because it makes property lookups a little faster and + // because the string is created in the old space straight away. It's going + // to end up in the old space sooner or later anyway but now it doesn't go + // through v8::Eternal's new space handling first. + // + // One byte because our strings are ASCII and we can safely skip V8's UTF-8 + // decoding step. + +#define V(PropertyName, StringValue) \ + PropertyName ## _.Set( \ + isolate, \ + Private::New( \ + isolate, \ + String::NewFromOneByte( \ + isolate, \ + reinterpret_cast(StringValue), \ + v8::NewStringType::kInternalized, \ + sizeof(StringValue) - 1).ToLocalChecked())); + PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) +#undef V +#define V(PropertyName, StringValue) \ + PropertyName ## _.Set( \ + isolate, \ + String::NewFromOneByte( \ + isolate, \ + reinterpret_cast(StringValue), \ + v8::NewStringType::kInternalized, \ + sizeof(StringValue) - 1).ToLocalChecked()); + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V } IsolateData::~IsolateData() { diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index e143d316d2e6fa..4e0c04a7b95527 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -189,9 +189,9 @@ const int CONTEXT_GROUP_ID = 1; class ChannelImpl final : public v8_inspector::V8Inspector::Channel { public: - explicit ChannelImpl(V8Inspector* inspector, - InspectorSessionDelegate* delegate) - : delegate_(delegate) { + explicit ChannelImpl(const std::unique_ptr& inspector, + std::unique_ptr delegate) + : delegate_(std::move(delegate)) { session_ = inspector->connect(1, this, StringView()); } @@ -201,19 +201,11 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel { session_->dispatchProtocolMessage(message); } - bool waitForFrontendMessage() { - return delegate_->WaitForFrontendMessageWhilePaused(); - } - void schedulePauseOnNextStatement(const std::string& reason) { std::unique_ptr buffer = Utf8ToStringView(reason); session_->schedulePauseOnNextStatement(buffer->string(), buffer->string()); } - InspectorSessionDelegate* delegate() { - return delegate_; - } - private: void sendResponse( int callId, @@ -232,7 +224,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel { delegate_->SendMessageToFrontend(message); } - InspectorSessionDelegate* const delegate_; + std::unique_ptr delegate_; std::unique_ptr session_; }; @@ -300,8 +292,7 @@ class InspectorTimerHandle { class NodeInspectorClient : public V8InspectorClient { public: NodeInspectorClient(node::Environment* env, node::NodePlatform* platform) - : env_(env), platform_(platform), terminated_(false), - running_nested_loop_(false) { + : env_(env), platform_(platform) { client_ = V8Inspector::create(env->isolate(), this); // TODO(bnoordhuis) Make name configurable from src/node.cc. ContextInfo info(GetHumanReadableProcessName()); @@ -310,18 +301,28 @@ class NodeInspectorClient : public V8InspectorClient { } void runMessageLoopOnPause(int context_group_id) override { - CHECK_NE(channel_, nullptr); + runMessageLoop(false); + } + + void runMessageLoop(bool ignore_terminated) { if (running_nested_loop_) return; terminated_ = false; running_nested_loop_ = true; - while (!terminated_ && channel_->waitForFrontendMessage()) { - platform_->FlushForegroundTasks(env_->isolate()); + while ((ignore_terminated || !terminated_) && waitForFrontendEvent()) { + while (platform_->FlushForegroundTasks(env_->isolate())) {} } terminated_ = false; running_nested_loop_ = false; } + bool waitForFrontendEvent() { + InspectorIo* io = env_->inspector_agent()->io(); + if (io == nullptr) + return false; + return io->WaitForFrontendEvent(); + } + double currentTimeMS() override { return uv_hrtime() * 1.0 / NANOS_PER_MSEC; } @@ -363,20 +364,23 @@ class NodeInspectorClient : public V8InspectorClient { terminated_ = true; } - void connectFrontend(InspectorSessionDelegate* delegate) { - CHECK_EQ(channel_, nullptr); - channel_ = std::unique_ptr( - new ChannelImpl(client_.get(), delegate)); + int connectFrontend(std::unique_ptr delegate) { + events_dispatched_ = true; + int session_id = next_session_id_++; + // TODO(addaleax): Revert back to using make_unique once we get issues + // with CI resolved (i.e. revert the patch that added this comment). + channels_[session_id].reset(new ChannelImpl(client_, std::move(delegate))); + return session_id; } - void disconnectFrontend() { - quitMessageLoopOnPause(); - channel_.reset(); + void disconnectFrontend(int session_id) { + events_dispatched_ = true; + channels_.erase(session_id); } - void dispatchMessageFromFrontend(const StringView& message) { - CHECK_NE(channel_, nullptr); - channel_->dispatchProtocolMessage(message); + void dispatchMessageFromFrontend(int session_id, const StringView& message) { + events_dispatched_ = true; + channels_[session_id]->dispatchProtocolMessage(message); } Local ensureDefaultContextInGroup(int contextGroupId) override { @@ -426,10 +430,6 @@ class NodeInspectorClient : public V8InspectorClient { script_id); } - ChannelImpl* channel() { - return channel_.get(); - } - void startRepeatingTimer(double interval_s, TimerCallback callback, void* data) override { @@ -464,20 +464,31 @@ class NodeInspectorClient : public V8InspectorClient { client_->allAsyncTasksCanceled(); } + void schedulePauseOnNextStatement(const std::string& reason) { + for (const auto& id_channel : channels_) { + id_channel.second->schedulePauseOnNextStatement(reason); + } + } + + bool hasConnectedSessions() { + return !channels_.empty(); + } + private: node::Environment* env_; node::NodePlatform* platform_; - bool terminated_; - bool running_nested_loop_; + bool terminated_ = false; + bool running_nested_loop_ = false; std::unique_ptr client_; - std::unique_ptr channel_; + std::unordered_map> channels_; std::unordered_map timers_; + int next_session_id_ = 1; + bool events_dispatched_ = false; }; Agent::Agent(Environment* env) : parent_env_(env), client_(nullptr), platform_(nullptr), - enabled_(false), pending_enable_async_hook_(false), pending_disable_async_hook_(false) {} @@ -491,7 +502,7 @@ bool Agent::Start(node::NodePlatform* platform, const char* path, path_ = path == nullptr ? "" : path; debug_options_ = options; client_ = - std::unique_ptr( + std::shared_ptr( new NodeInspectorClient(parent_env_, platform)); platform_ = platform; CHECK_EQ(0, uv_async_init(uv_default_loop(), @@ -515,7 +526,6 @@ bool Agent::StartIoThread(bool wait_for_connect) { CHECK_NE(client_, nullptr); - enabled_ = true; io_ = std::unique_ptr( new InspectorIo(parent_env_, platform_, path_, debug_options_, wait_for_connect)); @@ -554,13 +564,14 @@ void Agent::Stop() { if (io_ != nullptr) { io_->Stop(); io_.reset(); - enabled_ = false; } } -void Agent::Connect(InspectorSessionDelegate* delegate) { - enabled_ = true; - client_->connectFrontend(delegate); +std::unique_ptr Agent::Connect( + std::unique_ptr delegate) { + int session_id = client_->connectFrontend(std::move(delegate)); + return std::unique_ptr( + new InspectorSession(session_id, client_)); } void Agent::WaitForDisconnect() { @@ -568,6 +579,11 @@ void Agent::WaitForDisconnect() { client_->contextDestroyed(parent_env_->context()); if (io_ != nullptr) { io_->WaitForDisconnect(); + // There is a bug in V8 Inspector (https://crbug.com/834056) that + // calls V8InspectorClient::quitMessageLoopOnPause when a session + // disconnects. We are using this flag to ignore those calls so the message + // loop is spinning as long as there's a reason to expect inspector messages + client_->runMessageLoop(true); } } @@ -578,33 +594,8 @@ void Agent::FatalException(Local error, Local message) { WaitForDisconnect(); } -void Agent::Dispatch(const StringView& message) { - CHECK_NE(client_, nullptr); - client_->dispatchMessageFromFrontend(message); -} - -void Agent::Disconnect() { - CHECK_NE(client_, nullptr); - client_->disconnectFrontend(); -} - -void Agent::RunMessageLoop() { - CHECK_NE(client_, nullptr); - client_->runMessageLoopOnPause(CONTEXT_GROUP_ID); -} - -InspectorSessionDelegate* Agent::delegate() { - CHECK_NE(client_, nullptr); - ChannelImpl* channel = client_->channel(); - if (channel == nullptr) - return nullptr; - return channel->delegate(); -} - void Agent::PauseOnNextJavascriptStatement(const std::string& reason) { - ChannelImpl* channel = client_->channel(); - if (channel != nullptr) - channel->schedulePauseOnNextStatement(reason); + client_->schedulePauseOnNextStatement(reason); } void Agent::RegisterAsyncHook(Isolate* isolate, @@ -699,5 +690,20 @@ bool Agent::IsWaitingForConnect() { return debug_options_.wait_for_connect(); } +bool Agent::HasConnectedSessions() { + return client_->hasConnectedSessions(); +} + +InspectorSession::InspectorSession(int session_id, + std::shared_ptr client) + : session_id_(session_id), client_(client) {} + +InspectorSession::~InspectorSession() { + client_->disconnectFrontend(session_id_); +} + +void InspectorSession::Dispatch(const StringView& message) { + client_->dispatchMessageFromFrontend(session_id_, message); +} } // namespace inspector } // namespace node diff --git a/src/inspector_agent.h b/src/inspector_agent.h index 56fb407930fac5..64e4202ee88e13 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -23,18 +23,26 @@ class Environment; struct ContextInfo; namespace inspector { +class InspectorIo; +class NodeInspectorClient; + +class InspectorSession { + public: + InspectorSession(int session_id, std::shared_ptr client); + ~InspectorSession(); + void Dispatch(const v8_inspector::StringView& message); + private: + int session_id_; + std::shared_ptr client_; +}; class InspectorSessionDelegate { public: virtual ~InspectorSessionDelegate() = default; - virtual bool WaitForFrontendMessageWhilePaused() = 0; virtual void SendMessageToFrontend(const v8_inspector::StringView& message) = 0; }; -class InspectorIo; -class NodeInspectorClient; - class Agent { public: explicit Agent(node::Environment* env); @@ -66,19 +74,19 @@ class Agent { void RegisterAsyncHook(v8::Isolate* isolate, v8::Local enable_function, v8::Local disable_function); + void EnableAsyncHook(); + void DisableAsyncHook(); - // These methods are called by the WS protocol and JS binding to create - // inspector sessions. The inspector responds by using the delegate to send - // messages back. - void Connect(InspectorSessionDelegate* delegate); - void Disconnect(); - void Dispatch(const v8_inspector::StringView& message); - InspectorSessionDelegate* delegate(); + // Called by the WS protocol and JS binding to create inspector sessions. + // The inspector responds by using the delegate to send messages back. + std::unique_ptr Connect( + std::unique_ptr delegate); - void RunMessageLoop(); - bool enabled() { return enabled_; } void PauseOnNextJavascriptStatement(const std::string& reason); + // Returns true as long as there is at least one connected session. + bool HasConnectedSessions(); + InspectorIo* io() { return io_.get(); } @@ -92,18 +100,14 @@ class Agent { DebugOptions& options() { return debug_options_; } void ContextCreated(v8::Local context, const ContextInfo& info); - void EnableAsyncHook(); - void DisableAsyncHook(); - private: void ToggleAsyncHook(v8::Isolate* isolate, const Persistent& fn); node::Environment* parent_env_; - std::unique_ptr client_; + std::shared_ptr client_; std::unique_ptr io_; v8::Platform* platform_; - bool enabled_; std::string path_; DebugOptions debug_options_; diff --git a/src/inspector_io.cc b/src/inspector_io.cc index 01ddc296b08693..38d88d7ab890c9 100644 --- a/src/inspector_io.cc +++ b/src/inspector_io.cc @@ -122,11 +122,11 @@ std::unique_ptr Utf8ToStringView(const std::string& message) { class IoSessionDelegate : public InspectorSessionDelegate { public: - explicit IoSessionDelegate(InspectorIo* io) : io_(io) { } - bool WaitForFrontendMessageWhilePaused() override; + explicit IoSessionDelegate(InspectorIo* io, int id) : io_(io), id_(id) { } void SendMessageToFrontend(const v8_inspector::StringView& message) override; private: InspectorIo* io_; + int id_; }; // Passed to InspectorSocketServer to handle WS inspector protocol events, @@ -190,8 +190,7 @@ InspectorIo::InspectorIo(Environment* env, v8::Platform* platform, : options_(options), thread_(), delegate_(nullptr), state_(State::kNew), parent_env_(env), thread_req_(), platform_(platform), - dispatching_messages_(false), session_id_(0), - script_name_(path), + dispatching_messages_(false), script_name_(path), wait_for_connect_(wait_for_connect), port_(-1) { main_thread_req_ = new AsyncAndAgent({uv_async_t(), env->inspector_agent()}); CHECK_EQ(0, uv_async_init(env->event_loop(), &main_thread_req_->first, @@ -222,7 +221,7 @@ bool InspectorIo::Start() { } void InspectorIo::Stop() { - CHECK(state_ == State::kAccepting || state_ == State::kConnected); + CHECK(state_ == State::kAccepting || !sessions_.empty()); Write(TransportAction::kKill, 0, StringView()); int err = uv_thread_join(&thread_); CHECK_EQ(err, 0); @@ -237,12 +236,11 @@ bool InspectorIo::IsStarted() { void InspectorIo::WaitForDisconnect() { if (state_ == State::kAccepting) state_ = State::kDone; - if (state_ == State::kConnected) { + if (!sessions_.empty()) { state_ = State::kShutDown; Write(TransportAction::kStop, 0, StringView()); fprintf(stderr, "Waiting for the debugger to disconnect...\n"); fflush(stderr); - parent_env_->inspector_agent()->RunMessageLoop(); } } @@ -348,45 +346,23 @@ void InspectorIo::PostIncomingMessage(InspectorAction action, int session_id, isolate->RequestInterrupt(InterruptCallback, agent); CHECK_EQ(0, uv_async_send(&main_thread_req_->first)); } - NotifyMessageReceived(); + Mutex::ScopedLock scoped_lock(state_lock_); + incoming_message_cond_.Broadcast(scoped_lock); } std::vector InspectorIo::GetTargetIds() const { return delegate_ ? delegate_->GetTargetIds() : std::vector(); } -void InspectorIo::WaitForFrontendMessageWhilePaused() { - dispatching_messages_ = false; - Mutex::ScopedLock scoped_lock(state_lock_); - if (incoming_message_queue_.empty()) - incoming_message_cond_.Wait(scoped_lock); -} - -void InspectorIo::NotifyMessageReceived() { - Mutex::ScopedLock scoped_lock(state_lock_); - incoming_message_cond_.Broadcast(scoped_lock); -} - TransportAction InspectorIo::Attach(int session_id) { Agent* agent = parent_env_->inspector_agent(); - if (agent->delegate() != nullptr) - return TransportAction::kDeclineSession; - - CHECK_EQ(session_delegate_, nullptr); - session_id_ = session_id; - state_ = State::kConnected; fprintf(stderr, "Debugger attached.\n"); - session_delegate_ = std::unique_ptr( - new IoSessionDelegate(this)); - agent->Connect(session_delegate_.get()); + sessions_[session_id] = agent->Connect(std::unique_ptr( + new IoSessionDelegate(this, session_id))); return TransportAction::kAcceptSession; } void InspectorIo::DispatchMessages() { - // This function can be reentered if there was an incoming message while - // V8 was processing another inspector request (e.g. if the user is - // evaluating a long-running JS code snippet). This can happen only at - // specific points (e.g. the lines that call inspector_ methods) if (dispatching_messages_) return; dispatching_messages_ = true; @@ -409,17 +385,20 @@ void InspectorIo::DispatchMessages() { Attach(id); break; case InspectorAction::kEndSession: - CHECK_NE(session_delegate_, nullptr); + sessions_.erase(id); + if (!sessions_.empty()) + continue; if (state_ == State::kShutDown) { state_ = State::kDone; } else { state_ = State::kAccepting; } - parent_env_->inspector_agent()->Disconnect(); - session_delegate_.reset(); break; case InspectorAction::kSendMessage: - parent_env_->inspector_agent()->Dispatch(message); + auto session = sessions_.find(id); + if (session != sessions_.end() && session->second) { + session->second->Dispatch(message); + } break; } } @@ -445,6 +424,20 @@ void InspectorIo::Write(TransportAction action, int session_id, CHECK_EQ(0, err); } +bool InspectorIo::WaitForFrontendEvent() { + // We allow DispatchMessages reentry as we enter the pause. This is important + // to support debugging the code invoked by an inspector call, such + // as Runtime.evaluate + dispatching_messages_ = false; + Mutex::ScopedLock scoped_lock(state_lock_); + if (sessions_.empty()) + return false; + if (dispatching_message_queue_.empty() && incoming_message_queue_.empty()) { + incoming_message_cond_.Wait(scoped_lock); + } + return true; +} + InspectorIoDelegate::InspectorIoDelegate(InspectorIo* io, const std::string& script_path, const std::string& script_name, @@ -502,14 +495,9 @@ std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) { return "file://" + script_path_; } -bool IoSessionDelegate::WaitForFrontendMessageWhilePaused() { - io_->WaitForFrontendMessageWhilePaused(); - return true; -} - void IoSessionDelegate::SendMessageToFrontend( const v8_inspector::StringView& message) { - io_->Write(TransportAction::kSendMessage, io_->session_id_, message); + io_->Write(TransportAction::kSendMessage, id_, message); } } // namespace inspector diff --git a/src/inspector_io.h b/src/inspector_io.h index 79ccc6095ffec3..276c78056cb0a1 100644 --- a/src/inspector_io.h +++ b/src/inspector_io.h @@ -7,6 +7,7 @@ #include "uv.h" #include +#include #include #include @@ -76,6 +77,7 @@ class InspectorIo { void ServerDone() { uv_close(reinterpret_cast(&thread_req_), nullptr); } + bool WaitForFrontendEvent(); int port() const { return port_; } std::string host() const { return options_.host_name(); } @@ -89,7 +91,6 @@ class InspectorIo { enum class State { kNew, kAccepting, - kConnected, kDone, kError, kShutDown @@ -107,7 +108,6 @@ class InspectorIo { // messages from outgoing_message_queue to the InspectorSockerServer template static void IoThreadAsyncCb(uv_async_t* async); - void SetConnected(bool connected); void DispatchMessages(); // Write action to outgoing_message_queue, and wake the thread void Write(TransportAction action, int session_id, @@ -122,10 +122,6 @@ class InspectorIo { template void SwapBehindLock(MessageQueue* vector1, MessageQueue* vector2); - // Wait on incoming_message_cond_ - void WaitForFrontendMessageWhilePaused(); - // Broadcast incoming_message_cond_ - void NotifyMessageReceived(); // Attach session to an inspector. Either kAcceptSession or kDeclineSession TransportAction Attach(int session_id); @@ -147,7 +143,6 @@ class InspectorIo { // Note that this will live while the async is being closed - likely, past // the parent object lifespan std::pair* main_thread_req_; - std::unique_ptr session_delegate_; v8::Platform* platform_; // Message queues @@ -155,15 +150,17 @@ class InspectorIo { Mutex state_lock_; // Locked before mutating either queue. MessageQueue incoming_message_queue_; MessageQueue outgoing_message_queue_; + // This queue is to maintain the order of the messages for the cases + // when we reenter the DispatchMessages function. MessageQueue dispatching_message_queue_; bool dispatching_messages_; - int session_id_; std::string script_name_; std::string script_path_; const bool wait_for_connect_; int port_; + std::unordered_map> sessions_; friend class DispatchMessagesTask; friend class IoSessionDelegate; diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index 9a380bb2df2169..37cdcecd61dabb 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -42,10 +42,6 @@ class JSBindingsConnection : public AsyncWrap { connection_(connection) { } - bool WaitForFrontendMessageWhilePaused() override { - return false; - } - void SendMessageToFrontend(const v8_inspector::StringView& message) override { Isolate* isolate = env_->isolate(); @@ -58,12 +54,6 @@ class JSBindingsConnection : public AsyncWrap { connection_->OnMessage(argument); } - void Disconnect() { - Agent* agent = env_->inspector_agent(); - if (agent->delegate() == this) - agent->Disconnect(); - } - private: Environment* env_; JSBindingsConnection* connection_; @@ -73,31 +63,17 @@ class JSBindingsConnection : public AsyncWrap { Local wrap, Local callback) : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING), - delegate_(env, this), callback_(env->isolate(), callback) { Wrap(wrap, this); - Agent* inspector = env->inspector_agent(); - if (inspector->delegate() != nullptr) { - // This signals JS code that it has to throw an error. - Local session_attached = - FIXED_ONE_BYTE_STRING(env->isolate(), "sessionAttached"); - wrap->Set(env->context(), session_attached, - Boolean::New(env->isolate(), true)).ToChecked(); - return; - } - inspector->Connect(&delegate_); + session_ = inspector->Connect(std::unique_ptr( + new JSBindingsSessionDelegate(env, this))); } void OnMessage(Local value) { MakeCallback(callback_.Get(env()->isolate()), 1, &value); } - void CheckIsCurrent() { - Agent* inspector = env()->inspector_agent(); - CHECK_EQ(&delegate_, inspector->delegate()); - } - static void New(const FunctionCallbackInfo& info) { Environment* env = Environment::GetCurrent(info); CHECK(info[0]->IsFunction()); @@ -106,7 +82,7 @@ class JSBindingsConnection : public AsyncWrap { } void Disconnect() { - delegate_.Disconnect(); + session_.reset(); if (!persistent().IsEmpty()) { ClearWrap(object()); } @@ -125,18 +101,23 @@ class JSBindingsConnection : public AsyncWrap { ASSIGN_OR_RETURN_UNWRAP(&session, info.Holder()); CHECK(info[0]->IsString()); - session->CheckIsCurrent(); - Agent* inspector = env->inspector_agent(); - inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string()); + if (session->session_) { + session->session_->Dispatch( + ToProtocolString(env->isolate(), info[0])->string()); + } } size_t self_size() const override { return sizeof(*this); } private: - JSBindingsSessionDelegate delegate_; + std::unique_ptr session_; Persistent callback_; }; +static bool InspectorEnabled(Environment* env) { + Agent* agent = env->inspector_agent(); + return agent->io() != nullptr || agent->HasConnectedSessions(); +} void AddCommandLineAPI(const FunctionCallbackInfo& info) { auto env = Environment::GetCurrent(info); @@ -178,7 +159,7 @@ void InspectorConsoleCall(const FunctionCallbackInfo& info) { call_args.push_back(info[i]); } Environment* env = Environment::GetCurrent(isolate); - if (env->inspector_agent()->enabled()) { + if (InspectorEnabled(env)) { Local inspector_method = info[0]; CHECK(inspector_method->IsFunction()); Local config_value = info[2]; @@ -256,7 +237,7 @@ static void RegisterAsyncHookWrapper(const FunctionCallbackInfo& args) { void IsEnabled(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - args.GetReturnValue().Set(env->inspector_agent()->enabled()); + args.GetReturnValue().Set(InspectorEnabled(env)); } void Open(const FunctionCallbackInfo& args) { diff --git a/src/inspector_socket_server.cc b/src/inspector_socket_server.cc index 3725c19e93fb86..e890f66a38b53f 100644 --- a/src/inspector_socket_server.cc +++ b/src/inspector_socket_server.cc @@ -373,27 +373,17 @@ void InspectorSocketServer::SendListResponse(InspectorSocket* socket, target_map["url"] = delegate_->GetTargetUrl(id); Escape(&target_map["url"]); - bool connected = false; - for (const auto& session : connected_sessions_) { - if (session.second.first == id) { - connected = true; - break; - } - } - if (!connected) { - std::string detected_host = host; - if (detected_host.empty()) { - detected_host = FormatHostPort(socket->GetHost(), - session->server_port()); - } - std::ostringstream frontend_url; - frontend_url << "chrome-devtools://devtools/bundled"; - frontend_url << "/inspector.html?experiments=true&v8only=true&ws="; - frontend_url << FormatAddress(detected_host, id, false); - target_map["devtoolsFrontendUrl"] += frontend_url.str(); - target_map["webSocketDebuggerUrl"] = - FormatAddress(detected_host, id, true); + std::string detected_host = host; + if (detected_host.empty()) { + detected_host = FormatHostPort(socket->GetHost(), + session->server_port()); } + std::ostringstream frontend_url; + frontend_url << "chrome-devtools://devtools/bundled"; + frontend_url << "/inspector.html?experiments=true&v8only=true&ws="; + frontend_url << FormatAddress(detected_host, id, false); + target_map["devtoolsFrontendUrl"] += frontend_url.str(); + target_map["webSocketDebuggerUrl"] = FormatAddress(detected_host, id, true); } SendHttpResponse(socket, MapsToString(response)); } @@ -587,7 +577,8 @@ int ServerSocket::Listen(InspectorSocketServer* inspector_server, CHECK_EQ(0, uv_tcp_init(loop, server)); int err = uv_tcp_bind(server, addr, 0); if (err == 0) { - err = uv_listen(reinterpret_cast(server), 1, + // 511 is the value used by a 'net' module by default + err = uv_listen(reinterpret_cast(server), 511, ServerSocket::SocketConnectedCallback); } if (err == 0) { diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 9bcdb4dce75ff2..f88c113ae0b93f 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -286,9 +286,9 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo& args) { // which this timeout is nested, so check whether one of the watchdogs // from this invocation is responsible for termination. if (timed_out) { - env->ThrowError("Script execution timed out."); + THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout); } else if (received_signal) { - env->ThrowError("Script execution interrupted."); + THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env); } } diff --git a/src/node.cc b/src/node.cc index 032963bff41a69..ee114257d5d126 100644 --- a/src/node.cc +++ b/src/node.cc @@ -164,6 +164,7 @@ using v8::ScriptOrigin; using v8::SealHandleScope; using v8::String; using v8::TryCatch; +using v8::Uint32; using v8::Uint32Array; using v8::Undefined; using v8::V8; @@ -1580,10 +1581,8 @@ static void Abort(const FunctionCallbackInfo& args) { static void Chdir(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (args.Length() != 1 || !args[0]->IsString()) { - return env->ThrowTypeError("Bad argument."); - } - + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsString()); node::Utf8Value path(args.GetIsolate(), args[0]); int err = uv_chdir(*path); if (err) { @@ -1616,32 +1615,16 @@ static void Cwd(const FunctionCallbackInfo& args) { static void Umask(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); uint32_t old; - if (args.Length() < 1 || args[0]->IsUndefined()) { + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsUndefined() || args[0]->IsUint32()); + + if (args[0]->IsUndefined()) { old = umask(0); umask(static_cast(old)); - } else if (!args[0]->IsInt32() && !args[0]->IsString()) { - return env->ThrowTypeError("argument must be an integer or octal string."); } else { - int oct; - if (args[0]->IsInt32()) { - oct = args[0]->Uint32Value(); - } else { - oct = 0; - node::Utf8Value str(env->isolate(), args[0]); - - // Parse the octal string. - for (size_t i = 0; i < str.length(); i++) { - char c = (*str)[i]; - if (c > '7' || c < '0') { - return env->ThrowTypeError("invalid octal string"); - } - oct *= 8; - oct += c - '0'; - } - } + int oct = args[0].As()->Value(); old = umask(static_cast(oct)); } @@ -1779,18 +1762,18 @@ static void GetEGid(const FunctionCallbackInfo& args) { static void SetGid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setgid argument must be a number or a string"); - } + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsUint32() || args[0]->IsString()); gid_t gid = gid_by_name(env->isolate(), args[0]); if (gid == gid_not_found) { - return env->ThrowError("setgid group id does not exist"); - } - - if (setgid(gid)) { - return env->ThrowErrnoException(errno, "setgid"); + // Tells JS to throw ERR_INVALID_CREDENTIAL + args.GetReturnValue().Set(1); + } else if (setgid(gid)) { + env->ThrowErrnoException(errno, "setgid"); + } else { + args.GetReturnValue().Set(0); } } @@ -1798,18 +1781,18 @@ static void SetGid(const FunctionCallbackInfo& args) { static void SetEGid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setegid argument must be a number or string"); - } + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsUint32() || args[0]->IsString()); gid_t gid = gid_by_name(env->isolate(), args[0]); if (gid == gid_not_found) { - return env->ThrowError("setegid group id does not exist"); - } - - if (setegid(gid)) { - return env->ThrowErrnoException(errno, "setegid"); + // Tells JS to throw ERR_INVALID_CREDENTIAL + args.GetReturnValue().Set(1); + } else if (setegid(gid)) { + env->ThrowErrnoException(errno, "setegid"); + } else { + args.GetReturnValue().Set(0); } } @@ -1817,18 +1800,18 @@ static void SetEGid(const FunctionCallbackInfo& args) { static void SetUid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setuid argument must be a number or a string"); - } + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsUint32() || args[0]->IsString()); uid_t uid = uid_by_name(env->isolate(), args[0]); if (uid == uid_not_found) { - return env->ThrowError("setuid user id does not exist"); - } - - if (setuid(uid)) { - return env->ThrowErrnoException(errno, "setuid"); + // Tells JS to throw ERR_INVALID_CREDENTIAL + args.GetReturnValue().Set(1); + } else if (setuid(uid)) { + env->ThrowErrnoException(errno, "setuid"); + } else { + args.GetReturnValue().Set(0); } } @@ -1836,18 +1819,18 @@ static void SetUid(const FunctionCallbackInfo& args) { static void SetEUid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("seteuid argument must be a number or string"); - } + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsUint32() || args[0]->IsString()); uid_t uid = uid_by_name(env->isolate(), args[0]); if (uid == uid_not_found) { - return env->ThrowError("seteuid user id does not exist"); - } - - if (seteuid(uid)) { - return env->ThrowErrnoException(errno, "seteuid"); + // Tells JS to throw ERR_INVALID_CREDENTIAL + args.GetReturnValue().Set(1); + } else if (seteuid(uid)) { + env->ThrowErrnoException(errno, "seteuid"); + } else { + args.GetReturnValue().Set(0); } } @@ -1893,9 +1876,8 @@ static void GetGroups(const FunctionCallbackInfo& args) { static void SetGroups(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsArray()) { - return env->ThrowTypeError("argument 1 must be an array"); - } + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsArray()); Local groups_list = args[0].As(); size_t size = groups_list->Length(); @@ -1906,7 +1888,9 @@ static void SetGroups(const FunctionCallbackInfo& args) { if (gid == gid_not_found) { delete[] groups; - return env->ThrowError("group name not found"); + // Tells JS to throw ERR_INVALID_CREDENTIAL + args.GetReturnValue().Set(static_cast(i + 1)); + return; } groups[i] = gid; @@ -1918,19 +1902,17 @@ static void SetGroups(const FunctionCallbackInfo& args) { if (rc == -1) { return env->ThrowErrnoException(errno, "setgroups"); } + + args.GetReturnValue().Set(0); } static void InitGroups(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("argument 1 must be a number or a string"); - } - - if (!args[1]->IsUint32() && !args[1]->IsString()) { - return env->ThrowTypeError("argument 2 must be a number or a string"); - } + CHECK_EQ(args.Length(), 2); + CHECK(args[0]->IsUint32() || args[0]->IsString()); + CHECK(args[1]->IsUint32() || args[1]->IsString()); node::Utf8Value arg0(env->isolate(), args[0]); gid_t extra_group; @@ -1946,7 +1928,8 @@ static void InitGroups(const FunctionCallbackInfo& args) { } if (user == nullptr) { - return env->ThrowError("initgroups user not found"); + // Tells JS to throw ERR_INVALID_CREDENTIAL + return args.GetReturnValue().Set(1); } extra_group = gid_by_name(env->isolate(), args[1]); @@ -1954,7 +1937,8 @@ static void InitGroups(const FunctionCallbackInfo& args) { if (extra_group == gid_not_found) { if (must_free) free(user); - return env->ThrowError("initgroups extra group not found"); + // Tells JS to throw ERR_INVALID_CREDENTIAL + return args.GetReturnValue().Set(2); } int rc = initgroups(user, extra_group); @@ -1966,6 +1950,8 @@ static void InitGroups(const FunctionCallbackInfo& args) { if (rc) { return env->ThrowErrnoException(errno, "initgroups"); } + + args.GetReturnValue().Set(0); } #endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__) @@ -1973,7 +1959,7 @@ static void InitGroups(const FunctionCallbackInfo& args) { static void WaitForInspectorDisconnect(Environment* env) { #if HAVE_INSPECTOR - if (env->inspector_agent()->delegate() != nullptr) { + if (env->inspector_agent()->HasConnectedSessions()) { // Restore signal dispositions, the app is done and is no longer // capable of handling signals. #if defined(__POSIX__) && !defined(NODE_SHARED_MODE) @@ -2388,39 +2374,30 @@ void FatalException(Isolate* isolate, Local fatal_exception_function = process_object->Get(fatal_exception_string).As(); - int exit_code = 0; if (!fatal_exception_function->IsFunction()) { - // failed before the process._fatalException function was added! + // Failed before the process._fatalException function was added! // this is probably pretty bad. Nothing to do but report and exit. ReportException(env, error, message); - exit_code = 6; - } - - if (exit_code == 0) { + exit(6); + } else { TryCatch fatal_try_catch(isolate); // Do not call FatalException when _fatalException handler throws fatal_try_catch.SetVerbose(false); - // this will return true if the JS layer handled it, false otherwise + // This will return true if the JS layer handled it, false otherwise Local caught = fatal_exception_function->Call(process_object, 1, &error); if (fatal_try_catch.HasCaught()) { - // the fatal exception function threw, so we must exit + // The fatal exception function threw, so we must exit ReportException(env, fatal_try_catch); - exit_code = 7; - } - - if (exit_code == 0 && false == caught->BooleanValue()) { + exit(7); + } else if (caught->IsFalse()) { ReportException(env, error, message); - exit_code = 1; + exit(1); } } - - if (exit_code) { - exit(exit_code); - } } diff --git a/src/node.stp b/src/node.stp index 3369968c205146..ebc40d574fc2be 100644 --- a/src/node.stp +++ b/src/node.stp @@ -125,7 +125,7 @@ probe node_gc_start = process("node").mark("gc__start") flags); } -probe node_gc_stop = process("node").mark("gc__stop") +probe node_gc_stop = process("node").mark("gc__done") { scavenge = 1 << 0; compact = 1 << 1; diff --git a/src/node_contextify.cc b/src/node_contextify.cc index e07d5ebcd29d0d..ca58d1897e68b2 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -19,6 +19,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +#include "node_errors.h" #include "node_internals.h" #include "node_watchdog.h" #include "base_object-inl.h" @@ -858,9 +859,9 @@ class ContextifyScript : public BaseObject { // which this timeout is nested, so check whether one of the watchdogs // from this invocation is responsible for termination. if (timed_out) { - env->ThrowError("Script execution timed out."); + node::THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout); } else if (received_signal) { - env->ThrowError("Script execution interrupted."); + node::THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env); } } diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 04a5924c097296..01b6b5c8ea7fb5 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -72,6 +72,7 @@ using v8::External; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; +using v8::Int32; using v8::Integer; using v8::Isolate; using v8::Local; @@ -84,6 +85,7 @@ using v8::PropertyAttribute; using v8::ReadOnly; using v8::Signature; using v8::String; +using v8::Uint32; using v8::Value; @@ -338,19 +340,6 @@ void SecureContext::Initialize(Environment* env, Local target) { t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kTicketKeyIVIndex"), Integer::NewFromUnsigned(env->isolate(), kTicketKeyIVIndex)); - Local ctx_getter_templ = - FunctionTemplate::New(env->isolate(), - CtxGetter, - env->as_external(), - Signature::New(env->isolate(), t)); - - - t->PrototypeTemplate()->SetAccessorProperty( - FIXED_ONE_BYTE_STRING(env->isolate(), "_external"), - ctx_getter_templ, - Local(), - static_cast(ReadOnly | DontDelete)); - target->Set(secureContextString, t->GetFunction()); env->set_secure_context_constructor_template(t); } @@ -1350,14 +1339,6 @@ int SecureContext::TicketCompatibilityCallback(SSL* ssl, } -void SecureContext::CtxGetter(const FunctionCallbackInfo& info) { - SecureContext* sc; - ASSIGN_OR_RETURN_UNWRAP(&sc, info.This()); - Local ext = External::New(info.GetIsolate(), sc->ctx_); - info.GetReturnValue().Set(ext); -} - - template void SecureContext::GetCertificate(const FunctionCallbackInfo& args) { SecureContext* wrap; @@ -2117,7 +2098,8 @@ void SSLWrap::GetEphemeralKeyInfo( EVP_PKEY* key; if (SSL_get_server_tmp_key(w->ssl_, &key)) { - switch (EVP_PKEY_id(key)) { + int kid = EVP_PKEY_id(key); + switch (kid) { case EVP_PKEY_DH: info->Set(context, env->type_string(), FIXED_ONE_BYTE_STRING(env->isolate(), "DH")).FromJust(); @@ -2125,19 +2107,29 @@ void SSLWrap::GetEphemeralKeyInfo( Integer::New(env->isolate(), EVP_PKEY_bits(key))).FromJust(); break; case EVP_PKEY_EC: + // TODO(shigeki) Change this to EVP_PKEY_X25519 and add EVP_PKEY_X448 + // after upgrading to 1.1.1. + case NID_X25519: { - EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key); - int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - EC_KEY_free(ec); + const char* curve_name; + if (kid == EVP_PKEY_EC) { + EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key); + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + curve_name = OBJ_nid2sn(nid); + EC_KEY_free(ec); + } else { + curve_name = OBJ_nid2sn(kid); + } info->Set(context, env->type_string(), FIXED_ONE_BYTE_STRING(env->isolate(), "ECDH")).FromJust(); info->Set(context, env->name_string(), OneByteString(args.GetIsolate(), - OBJ_nid2sn(nid))).FromJust(); + curve_name)).FromJust(); info->Set(context, env->size_string(), Integer::New(env->isolate(), EVP_PKEY_bits(key))).FromJust(); } + break; } EVP_PKEY_free(key); } @@ -2629,7 +2621,7 @@ void CipherBase::New(const FunctionCallbackInfo& args) { void CipherBase::Init(const char* cipher_type, const char* key_buf, int key_buf_len, - int auth_tag_len) { + unsigned int auth_tag_len) { HandleScope scope(env()->isolate()); #ifdef NODE_FIPS_MODE @@ -2700,10 +2692,16 @@ void CipherBase::Init(const FunctionCallbackInfo& args) { const node::Utf8Value cipher_type(args.GetIsolate(), args[0]); const char* key_buf = Buffer::Data(args[1]); ssize_t key_buf_len = Buffer::Length(args[1]); - CHECK(args[2]->IsInt32()); + // Don't assign to cipher->auth_tag_len_ directly; the value might not // represent a valid length at this point. - int auth_tag_len = args[2].As()->Value(); + unsigned int auth_tag_len; + if (args[2]->IsUint32()) { + auth_tag_len = args[2].As()->Value(); + } else { + CHECK(args[2]->IsInt32() && args[2].As()->Value() == -1); + auth_tag_len = kNoAuthTagLength; + } cipher->Init(*cipher_type, key_buf, key_buf_len, auth_tag_len); } @@ -2714,7 +2712,7 @@ void CipherBase::InitIv(const char* cipher_type, int key_len, const char* iv, int iv_len, - int auth_tag_len) { + unsigned int auth_tag_len) { HandleScope scope(env()->isolate()); const EVP_CIPHER* const cipher = EVP_get_cipherbyname(cipher_type); @@ -2788,10 +2786,16 @@ void CipherBase::InitIv(const FunctionCallbackInfo& args) { iv_buf = Buffer::Data(args[2]); iv_len = Buffer::Length(args[2]); } - CHECK(args[3]->IsInt32()); + // Don't assign to cipher->auth_tag_len_ directly; the value might not // represent a valid length at this point. - int auth_tag_len = args[3].As()->Value(); + unsigned int auth_tag_len; + if (args[3]->IsUint32()) { + auth_tag_len = args[3].As()->Value(); + } else { + CHECK(args[3]->IsInt32() && args[3].As()->Value() == -1); + auth_tag_len = kNoAuthTagLength; + } cipher->InitIv(*cipher_type, key_buf, key_len, iv_buf, iv_len, auth_tag_len); } @@ -2802,7 +2806,7 @@ static bool IsValidGCMTagLength(unsigned int tag_len) { } bool CipherBase::InitAuthenticated(const char *cipher_type, int iv_len, - int auth_tag_len) { + unsigned int auth_tag_len) { CHECK(IsAuthenticatedMode()); // TODO(tniessen) Use EVP_CTRL_AEAD_SET_IVLEN when migrating to OpenSSL 1.1.0 @@ -2815,7 +2819,7 @@ bool CipherBase::InitAuthenticated(const char *cipher_type, int iv_len, const int mode = EVP_CIPHER_CTX_mode(ctx_); if (mode == EVP_CIPH_CCM_MODE) { - if (auth_tag_len < 0) { + if (auth_tag_len == kNoAuthTagLength) { char msg[128]; snprintf(msg, sizeof(msg), "authTagLength required for %s", cipher_type); env()->ThrowError(msg); @@ -2850,7 +2854,7 @@ bool CipherBase::InitAuthenticated(const char *cipher_type, int iv_len, } else { CHECK_EQ(mode, EVP_CIPH_GCM_MODE); - if (auth_tag_len >= 0) { + if (auth_tag_len != kNoAuthTagLength) { if (!IsValidGCMTagLength(auth_tag_len)) { char msg[50]; snprintf(msg, sizeof(msg), @@ -2990,7 +2994,7 @@ void CipherBase::SetAAD(const FunctionCallbackInfo& args) { CHECK_EQ(args.Length(), 2); CHECK(args[1]->IsInt32()); - int plaintext_len = args[1].As()->Value(); + int plaintext_len = args[1].As()->Value(); if (!cipher->SetAAD(Buffer::Data(args[0]), Buffer::Length(args[0]), plaintext_len)) @@ -3113,9 +3117,10 @@ bool CipherBase::Final(unsigned char** out, int *out_len) { ok = EVP_CipherFinal_ex(ctx_, *out, out_len) == 1; if (ok && kind_ == kCipher && IsAuthenticatedMode()) { - // For GCM, the tag length is static (16 bytes), while the CCM tag length - // must be specified in advance. - if (mode == EVP_CIPH_GCM_MODE) + // In GCM mode, the authentication tag length can be specified in advance, + // but defaults to 16 bytes when encrypting. In CCM mode, it must always + // be given by the user. + if (mode == EVP_CIPH_GCM_MODE && auth_tag_len_ == kNoAuthTagLength) auth_tag_len_ = sizeof(auth_tag_); // TOOD(tniessen) Use EVP_CTRL_AEAP_GET_TAG in OpenSSL 1.1.0 static_assert(EVP_CTRL_CCM_GET_TAG == EVP_CTRL_GCM_GET_TAG, diff --git a/src/node_crypto.h b/src/node_crypto.h index 3c166f5dcc89fc..3963f7050f2982 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -44,6 +44,8 @@ #endif // !OPENSSL_NO_ENGINE #include #include +// TODO(shigeki) Remove this after upgrading to 1.1.1 +#include #include #include #include @@ -148,7 +150,6 @@ class SecureContext : public BaseObject { const v8::FunctionCallbackInfo& args); static void EnableTicketKeyCallback( const v8::FunctionCallbackInfo& args); - static void CtxGetter(const v8::FunctionCallbackInfo& info); template static void GetCertificate(const v8::FunctionCallbackInfo& args); @@ -364,14 +365,15 @@ class CipherBase : public BaseObject { void Init(const char* cipher_type, const char* key_buf, int key_buf_len, - int auth_tag_len); + unsigned int auth_tag_len); void InitIv(const char* cipher_type, const char* key, int key_len, const char* iv, int iv_len, - int auth_tag_len); - bool InitAuthenticated(const char *cipher_type, int iv_len, int auth_tag_len); + unsigned int auth_tag_len); + bool InitAuthenticated(const char *cipher_type, int iv_len, + unsigned int auth_tag_len); bool CheckCCMMessageLength(int message_len); UpdateResult Update(const char* data, int len, unsigned char** out, int* out_len); diff --git a/src/node_errors.h b/src/node_errors.h index 0f91872474148d..eb120c62807cf6 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -8,6 +8,10 @@ #include "env-inl.h" #include "v8.h" +// Use ostringstream to print exact-width integer types +// because the format specifiers are not available on AIX. +#include + namespace node { // Helpers to construct errors similar to the ones provided by @@ -24,6 +28,8 @@ namespace node { V(ERR_MEMORY_ALLOCATION_FAILED, Error) \ V(ERR_MISSING_ARGS, TypeError) \ V(ERR_MISSING_MODULE, Error) \ + V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \ + V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \ V(ERR_STRING_TOO_LONG, Error) \ V(ERR_BUFFER_TOO_LARGE, Error) @@ -49,7 +55,9 @@ namespace node { #define PREDEFINED_ERROR_MESSAGES(V) \ V(ERR_INDEX_OUT_OF_RANGE, "Index out of range") \ - V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") + V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \ + V(ERR_SCRIPT_EXECUTION_INTERRUPTED, \ + "Script execution was interrupted by `SIGINT`") #define V(code, message) \ inline v8::Local code(v8::Isolate* isolate) { \ @@ -62,6 +70,13 @@ namespace node { #undef V // Errors with predefined non-static messages +inline void THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(Environment* env, + int64_t timeout) { + std::ostringstream message; + message << "Script execution timed out after "; + message << timeout << "ms"; + THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, message.str().c_str()); +} inline v8::Local ERR_BUFFER_TOO_LARGE(v8::Isolate *isolate) { char message[128]; diff --git a/src/node_http2.cc b/src/node_http2.cc index 1c5c68f09471f3..05d9243ee30ec0 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1364,16 +1364,35 @@ void Http2Session::MaybeScheduleWrite() { // storage for data and metadata that was associated with these writes. void Http2Session::ClearOutgoing(int status) { CHECK_NE(flags_ & SESSION_STATE_SENDING, 0); - flags_ &= ~SESSION_STATE_SENDING; - for (const nghttp2_stream_write& wr : outgoing_buffers_) { - WriteWrap* wrap = wr.req_wrap; - if (wrap != nullptr) - wrap->Done(status); + if (outgoing_buffers_.size() > 0) { + outgoing_storage_.clear(); + + for (const nghttp2_stream_write& wr : outgoing_buffers_) { + WriteWrap* wrap = wr.req_wrap; + if (wrap != nullptr) + wrap->Done(status); + } + + outgoing_buffers_.clear(); } - outgoing_buffers_.clear(); - outgoing_storage_.clear(); + flags_ &= ~SESSION_STATE_SENDING; + + // Now that we've finished sending queued data, if there are any pending + // RstStreams we should try sending again and then flush them one by one. + if (pending_rst_streams_.size() > 0) { + std::vector current_pending_rst_streams; + pending_rst_streams_.swap(current_pending_rst_streams); + + SendPendingData(); + + for (int32_t stream_id : current_pending_rst_streams) { + Http2Stream* stream = FindStream(stream_id); + if (stream != nullptr) + stream->FlushRstStream(); + } + } } // Queue a given block of data for sending. This always creates a copy, @@ -1397,18 +1416,19 @@ void Http2Session::CopyDataIntoOutgoing(const uint8_t* src, size_t src_length) { // chunk out to the i/o socket to be sent. This is a particularly hot method // that will generally be called at least twice be event loop iteration. // This is a potential performance optimization target later. -void Http2Session::SendPendingData() { +// Returns non-zero value if a write is already in progress. +uint8_t Http2Session::SendPendingData() { DEBUG_HTTP2SESSION(this, "sending pending data"); // Do not attempt to send data on the socket if the destroying flag has // been set. That means everything is shutting down and the socket // will not be usable. if (IsDestroyed()) - return; + return 0; flags_ &= ~SESSION_STATE_WRITE_SCHEDULED; // SendPendingData should not be called recursively. if (flags_ & SESSION_STATE_SENDING) - return; + return 1; // This is cleared by ClearOutgoing(). flags_ |= SESSION_STATE_SENDING; @@ -1432,15 +1452,15 @@ void Http2Session::SendPendingData() { // does take care of things like closing the individual streams after // a socket has been torn down, so we still need to call it. ClearOutgoing(UV_ECANCELED); - return; + return 0; } // Part Two: Pass Data to the underlying stream size_t count = outgoing_buffers_.size(); if (count == 0) { - flags_ &= ~SESSION_STATE_SENDING; - return; + ClearOutgoing(0); + return 0; } MaybeStackBuffer bufs; bufs.AllocateSufficientStorage(count); @@ -1471,6 +1491,8 @@ void Http2Session::SendPendingData() { DEBUG_HTTP2SESSION2(this, "wants data in return? %d", nghttp2_session_want_read(session_)); + + return 0; } @@ -1830,12 +1852,25 @@ int Http2Stream::SubmitPriority(nghttp2_priority_spec* prispec, // peer. void Http2Stream::SubmitRstStream(const uint32_t code) { CHECK(!this->IsDestroyed()); + code_ = code; + // If possible, force a purge of any currently pending data here to make sure + // it is sent before closing the stream. If it returns non-zero then we need + // to wait until the current write finishes and try again to avoid nghttp2 + // behaviour where it prioritizes RstStream over everything else. + if (session_->SendPendingData() != 0) { + session_->AddPendingRstStream(id_); + return; + } + + FlushRstStream(); +} + +void Http2Stream::FlushRstStream() { + if (IsDestroyed()) + return; Http2Scope h2scope(this); - // Force a purge of any currently pending data here to make sure - // it is sent before closing the stream. - session_->SendPendingData(); CHECK_EQ(nghttp2_submit_rst_stream(**session_, NGHTTP2_FLAG_NONE, - id_, code), 0); + id_, code_), 0); } diff --git a/src/node_http2.h b/src/node_http2.h index 87c929cdea12f9..da404841450f27 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -591,6 +591,8 @@ class Http2Stream : public AsyncWrap, // Submits an RST_STREAM frame using the given code void SubmitRstStream(const uint32_t code); + void FlushRstStream(); + // Submits a PUSH_PROMISE frame with this stream as the parent. Http2Stream* SubmitPushPromise( nghttp2_nv* nva, @@ -797,7 +799,7 @@ class Http2Session : public AsyncWrap, public StreamListener { bool Ping(v8::Local function); - void SendPendingData(); + uint8_t SendPendingData(); // Submits a new request. If the request is a success, assigned // will be a pointer to the Http2Stream instance assigned. @@ -845,6 +847,11 @@ class Http2Session : public AsyncWrap, public StreamListener { size_t self_size() const override { return sizeof(*this); } + // Schedule an RstStream for after the current write finishes. + inline void AddPendingRstStream(int32_t stream_id) { + pending_rst_streams_.emplace_back(stream_id); + } + // Handle reads/writes from the underlying network transport. void OnStreamRead(ssize_t nread, const uv_buf_t& buf) override; void OnStreamAfterWrite(WriteWrap* w, int status) override; @@ -1049,6 +1056,7 @@ class Http2Session : public AsyncWrap, public StreamListener { std::vector outgoing_buffers_; std::vector outgoing_storage_; + std::vector pending_rst_streams_; void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length); void ClearOutgoing(int status); diff --git a/src/node_platform.cc b/src/node_platform.cc index d96db086925c01..b8f1344727cfd6 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc @@ -95,7 +95,7 @@ void PerIsolatePlatformData::PostDelayedTask( } PerIsolatePlatformData::~PerIsolatePlatformData() { - FlushForegroundTasksInternal(); + while (FlushForegroundTasksInternal()) {} CancelPendingDelayedTasks(); uv_close(reinterpret_cast(flush_tasks_), @@ -223,7 +223,13 @@ bool PerIsolatePlatformData::FlushForegroundTasksInternal() { }); }); } - while (std::unique_ptr task = foreground_tasks_.Pop()) { + // Move all foreground tasks into a separate queue and flush that queue. + // This way tasks that are posted while flushing the queue will be run on the + // next call of FlushForegroundTasksInternal. + std::queue> tasks = foreground_tasks_.PopAll(); + while (!tasks.empty()) { + std::unique_ptr task = std::move(tasks.front()); + tasks.pop(); did_work = true; RunForegroundTask(std::move(task)); } @@ -254,8 +260,8 @@ void NodePlatform::CallDelayedOnForegroundThread(Isolate* isolate, std::unique_ptr(task), delay_in_seconds); } -void NodePlatform::FlushForegroundTasks(v8::Isolate* isolate) { - ForIsolate(isolate)->FlushForegroundTasksInternal(); +bool NodePlatform::FlushForegroundTasks(v8::Isolate* isolate) { + return ForIsolate(isolate)->FlushForegroundTasksInternal(); } void NodePlatform::CancelPendingDelayedTasks(v8::Isolate* isolate) { @@ -348,4 +354,12 @@ void TaskQueue::Stop() { tasks_available_.Broadcast(scoped_lock); } +template +std::queue> TaskQueue::PopAll() { + Mutex::ScopedLock scoped_lock(lock_); + std::queue> result; + result.swap(task_queue_); + return result; +} + } // namespace node diff --git a/src/node_platform.h b/src/node_platform.h index b7546057871a1d..8f6ff89f491fe3 100644 --- a/src/node_platform.h +++ b/src/node_platform.h @@ -26,6 +26,7 @@ class TaskQueue { void Push(std::unique_ptr task); std::unique_ptr Pop(); std::unique_ptr BlockingPop(); + std::queue> PopAll(); void NotifyOfCompletion(); void BlockingDrain(); void Stop(); @@ -65,7 +66,9 @@ class PerIsolatePlatformData : void ref(); int unref(); - // Returns true iff work was dispatched or executed. + // Returns true if work was dispatched or executed. New tasks that are + // posted during flushing of the queue are postponed until the next + // flushing. bool FlushForegroundTasksInternal(); void CancelPendingDelayedTasks(); @@ -130,7 +133,10 @@ class NodePlatform : public MultiIsolatePlatform { double CurrentClockTimeMillis() override; v8::TracingController* GetTracingController() override; - void FlushForegroundTasks(v8::Isolate* isolate); + // Returns true if work was dispatched or executed. New tasks that are + // posted during flushing of the queue are postponed until the next + // flushing. + bool FlushForegroundTasks(v8::Isolate* isolate); void RegisterIsolate(IsolateData* isolate_data, uv_loop_t* loop) override; void UnregisterIsolate(IsolateData* isolate_data) override; diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index 1e02048d1f0843..f37537d54544a2 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -63,7 +63,7 @@ void NodeCategorySet::Enable(const FunctionCallbackInfo& args) { NodeCategorySet* category_set; ASSIGN_OR_RETURN_UNWRAP(&category_set, args.Holder()); CHECK_NE(category_set, nullptr); - auto categories = category_set->GetCategories(); + const auto& categories = category_set->GetCategories(); if (!category_set->enabled_ && !categories.empty()) { env->tracing_agent()->Enable(categories); category_set->enabled_ = true; @@ -75,7 +75,7 @@ void NodeCategorySet::Disable(const FunctionCallbackInfo& args) { NodeCategorySet* category_set; ASSIGN_OR_RETURN_UNWRAP(&category_set, args.Holder()); CHECK_NE(category_set, nullptr); - auto categories = category_set->GetCategories(); + const auto& categories = category_set->GetCategories(); if (category_set->enabled_ && !categories.empty()) { env->tracing_agent()->Disable(categories); category_set->enabled_ = false; diff --git a/test/addons-napi/1_hello_world/binding.c b/test/addons-napi/1_hello_world/binding.c index 6efc14ee66e18f..02cd5dd2f19418 100644 --- a/test/addons-napi/1_hello_world/binding.c +++ b/test/addons-napi/1_hello_world/binding.c @@ -2,7 +2,7 @@ #include "../common.h" #include -napi_value Method(napi_env env, napi_callback_info info) { +static napi_value Method(napi_env env, napi_callback_info info) { napi_value world; const char* str = "world"; size_t str_len = strlen(str); diff --git a/test/addons-napi/2_function_arguments/binding.c b/test/addons-napi/2_function_arguments/binding.c index c45ca0871db8ec..7d88c3d9e4ff19 100644 --- a/test/addons-napi/2_function_arguments/binding.c +++ b/test/addons-napi/2_function_arguments/binding.c @@ -1,7 +1,7 @@ #include #include "../common.h" -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, NULL, NULL)); @@ -29,7 +29,7 @@ napi_value Add(napi_env env, napi_callback_info info) { return sum; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc = DECLARE_NAPI_PROPERTY("add", Add); NAPI_CALL(env, napi_define_properties(env, exports, 1, &desc)); return exports; diff --git a/test/addons-napi/3_callbacks/binding.c b/test/addons-napi/3_callbacks/binding.c index 7ebacf1d0653e9..1c0dd8126ce4ef 100644 --- a/test/addons-napi/3_callbacks/binding.c +++ b/test/addons-napi/3_callbacks/binding.c @@ -2,7 +2,7 @@ #include "../common.h" #include -napi_value RunCallback(napi_env env, napi_callback_info info) { +static napi_value RunCallback(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, NULL, NULL)); @@ -34,7 +34,7 @@ napi_value RunCallback(napi_env env, napi_callback_info info) { return NULL; } -napi_value RunCallbackWithRecv(napi_env env, napi_callback_info info) { +static napi_value RunCallbackWithRecv(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, NULL, NULL)); @@ -45,7 +45,7 @@ napi_value RunCallbackWithRecv(napi_env env, napi_callback_info info) { return NULL; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[2] = { DECLARE_NAPI_PROPERTY("RunCallback", RunCallback), DECLARE_NAPI_PROPERTY("RunCallbackWithRecv", RunCallbackWithRecv), diff --git a/test/addons-napi/4_object_factory/binding.c b/test/addons-napi/4_object_factory/binding.c index 38b8ec8e1cab48..0ed95e93512a0b 100644 --- a/test/addons-napi/4_object_factory/binding.c +++ b/test/addons-napi/4_object_factory/binding.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value CreateObject(napi_env env, napi_callback_info info) { +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, NULL, NULL)); @@ -14,7 +14,7 @@ napi_value CreateObject(napi_env env, napi_callback_info info) { return obj; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { NAPI_CALL(env, napi_create_function(env, "exports", -1, CreateObject, NULL, &exports)); return exports; diff --git a/test/addons-napi/5_function_factory/binding.c b/test/addons-napi/5_function_factory/binding.c index 8cc41f6aac5c3d..19460b9ddc55f2 100644 --- a/test/addons-napi/5_function_factory/binding.c +++ b/test/addons-napi/5_function_factory/binding.c @@ -1,20 +1,20 @@ #include #include "../common.h" -napi_value MyFunction(napi_env env, napi_callback_info info) { +static napi_value MyFunction(napi_env env, napi_callback_info info) { napi_value str; NAPI_CALL(env, napi_create_string_utf8(env, "hello world", -1, &str)); return str; } -napi_value CreateFunction(napi_env env, napi_callback_info info) { +static napi_value CreateFunction(napi_env env, napi_callback_info info) { napi_value fn; NAPI_CALL(env, napi_create_function(env, "theFunction", -1, MyFunction, NULL, &fn)); return fn; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { NAPI_CALL(env, napi_create_function(env, "exports", -1, CreateFunction, NULL, &exports)); return exports; diff --git a/test/addons-napi/test_array/test_array.c b/test/addons-napi/test_array/test_array.c index f13867ca74f848..bd4f867c0c9117 100644 --- a/test/addons-napi/test_array/test_array.c +++ b/test/addons-napi/test_array/test_array.c @@ -2,7 +2,7 @@ #include #include "../common.h" -napi_value TestGetElement(napi_env env, napi_callback_info info) { +static napi_value TestGetElement(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, NULL, NULL)); @@ -45,7 +45,7 @@ napi_value TestGetElement(napi_env env, napi_callback_info info) { return ret; } -napi_value TestHasElement(napi_env env, napi_callback_info info) { +static napi_value TestHasElement(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, NULL, NULL)); @@ -84,7 +84,7 @@ napi_value TestHasElement(napi_env env, napi_callback_info info) { return ret; } -napi_value TestDeleteElement(napi_env env, napi_callback_info info) { +static napi_value TestDeleteElement(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2]; @@ -119,7 +119,7 @@ napi_value TestDeleteElement(napi_env env, napi_callback_info info) { return ret; } -napi_value New(napi_env env, napi_callback_info info) { +static napi_value New(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)); @@ -147,7 +147,7 @@ napi_value New(napi_env env, napi_callback_info info) { return ret; } -napi_value NewWithLength(napi_env env, napi_callback_info info) { +static napi_value NewWithLength(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)); @@ -169,7 +169,7 @@ napi_value NewWithLength(napi_env env, napi_callback_info info) { return ret; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("TestGetElement", TestGetElement), DECLARE_NAPI_PROPERTY("TestHasElement", TestHasElement), diff --git a/test/addons-napi/test_buffer/test_buffer.c b/test/addons-napi/test_buffer/test_buffer.c index 657eb7b3a051bf..552d280615739f 100644 --- a/test/addons-napi/test_buffer/test_buffer.c +++ b/test/addons-napi/test_buffer/test_buffer.c @@ -20,7 +20,7 @@ static void noopDeleter(napi_env env, void* data, void* finalize_hint) { deleterCallCount++; } -napi_value newBuffer(napi_env env, napi_callback_info info) { +static napi_value newBuffer(napi_env env, napi_callback_info info) { napi_value theBuffer; char* theCopy; const unsigned int kBufferSize = sizeof(theText); @@ -37,7 +37,7 @@ napi_value newBuffer(napi_env env, napi_callback_info info) { return theBuffer; } -napi_value newExternalBuffer(napi_env env, napi_callback_info info) { +static napi_value newExternalBuffer(napi_env env, napi_callback_info info) { napi_value theBuffer; char* theCopy = strdup(theText); NAPI_ASSERT(env, theCopy, "Failed to copy static text for newExternalBuffer"); @@ -53,20 +53,20 @@ napi_value newExternalBuffer(napi_env env, napi_callback_info info) { return theBuffer; } -napi_value getDeleterCallCount(napi_env env, napi_callback_info info) { +static napi_value getDeleterCallCount(napi_env env, napi_callback_info info) { napi_value callCount; NAPI_CALL(env, napi_create_int32(env, deleterCallCount, &callCount)); return callCount; } -napi_value copyBuffer(napi_env env, napi_callback_info info) { +static napi_value copyBuffer(napi_env env, napi_callback_info info) { napi_value theBuffer; NAPI_CALL(env, napi_create_buffer_copy( env, sizeof(theText), theText, NULL, &theBuffer)); return theBuffer; } -napi_value bufferHasInstance(napi_env env, napi_callback_info info) { +static napi_value bufferHasInstance(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)); @@ -85,7 +85,7 @@ napi_value bufferHasInstance(napi_env env, napi_callback_info info) { return returnValue; } -napi_value bufferInfo(napi_env env, napi_callback_info info) { +static napi_value bufferInfo(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)); @@ -106,7 +106,7 @@ napi_value bufferInfo(napi_env env, napi_callback_info info) { return returnValue; } -napi_value staticBuffer(napi_env env, napi_callback_info info) { +static napi_value staticBuffer(napi_env env, napi_callback_info info) { napi_value theBuffer; NAPI_CALL( env, @@ -119,7 +119,7 @@ napi_value staticBuffer(napi_env env, napi_callback_info info) { return theBuffer; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_value theValue; NAPI_CALL(env, diff --git a/test/addons-napi/test_constructor/test_constructor.c b/test/addons-napi/test_constructor/test_constructor.c index 4ee8323dd6ed40..8cc092049aef10 100644 --- a/test/addons-napi/test_constructor/test_constructor.c +++ b/test/addons-napi/test_constructor/test_constructor.c @@ -4,7 +4,7 @@ static double value_ = 1; static double static_value_ = 10; -napi_value GetValue(napi_env env, napi_callback_info info) { +static napi_value GetValue(napi_env env, napi_callback_info info) { size_t argc = 0; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); @@ -16,7 +16,7 @@ napi_value GetValue(napi_env env, napi_callback_info info) { return number; } -napi_value SetValue(napi_env env, napi_callback_info info) { +static napi_value SetValue(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)); @@ -28,7 +28,7 @@ napi_value SetValue(napi_env env, napi_callback_info info) { return NULL; } -napi_value Echo(napi_env env, napi_callback_info info) { +static napi_value Echo(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,14 +38,14 @@ napi_value Echo(napi_env env, napi_callback_info info) { return args[0]; } -napi_value New(napi_env env, napi_callback_info info) { +static napi_value New(napi_env env, napi_callback_info info) { napi_value _this; NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL)); return _this; } -napi_value GetStaticValue(napi_env env, napi_callback_info info) { +static napi_value GetStaticValue(napi_env env, napi_callback_info info) { size_t argc = 0; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); @@ -58,7 +58,7 @@ napi_value GetStaticValue(napi_env env, napi_callback_info info) { } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_value number; NAPI_CALL(env, napi_create_double(env, value_, &number)); diff --git a/test/addons-napi/test_constructor/test_constructor_name.c b/test/addons-napi/test_constructor/test_constructor_name.c index a5c89791f0f0cd..e12deb80d23af8 100644 --- a/test/addons-napi/test_constructor/test_constructor_name.c +++ b/test/addons-napi/test_constructor/test_constructor_name.c @@ -1,22 +1,17 @@ #include #include "../common.h" -napi_ref constructor_; - -napi_value New(napi_env env, napi_callback_info info) { +static napi_value New(napi_env env, napi_callback_info info) { napi_value _this; NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &_this, NULL)); return _this; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_value cons; NAPI_CALL(env, napi_define_class( env, "MyObject_Extra", 8, New, NULL, 0, NULL, &cons)); - - NAPI_CALL(env, - napi_create_reference(env, cons, 1, &constructor_)); return cons; } diff --git a/test/addons-napi/test_conversions/test_conversions.c b/test/addons-napi/test_conversions/test_conversions.c index 4f92bafa35b79d..845b7e7c56d7df 100644 --- a/test/addons-napi/test_conversions/test_conversions.c +++ b/test/addons-napi/test_conversions/test_conversions.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value AsBool(napi_env env, napi_callback_info info) { +static napi_value AsBool(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)); @@ -15,7 +15,7 @@ napi_value AsBool(napi_env env, napi_callback_info info) { return output; } -napi_value AsInt32(napi_env env, napi_callback_info info) { +static napi_value AsInt32(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)); @@ -29,7 +29,7 @@ napi_value AsInt32(napi_env env, napi_callback_info info) { return output; } -napi_value AsUInt32(napi_env env, napi_callback_info info) { +static napi_value AsUInt32(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)); @@ -43,7 +43,7 @@ napi_value AsUInt32(napi_env env, napi_callback_info info) { return output; } -napi_value AsInt64(napi_env env, napi_callback_info info) { +static napi_value AsInt64(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)); @@ -57,7 +57,7 @@ napi_value AsInt64(napi_env env, napi_callback_info info) { return output; } -napi_value AsDouble(napi_env env, napi_callback_info info) { +static napi_value AsDouble(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)); @@ -71,7 +71,7 @@ napi_value AsDouble(napi_env env, napi_callback_info info) { return output; } -napi_value AsString(napi_env env, napi_callback_info info) { +static napi_value AsString(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)); @@ -87,7 +87,7 @@ napi_value AsString(napi_env env, napi_callback_info info) { return output; } -napi_value ToBool(napi_env env, napi_callback_info info) { +static napi_value ToBool(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)); @@ -98,7 +98,7 @@ napi_value ToBool(napi_env env, napi_callback_info info) { return output; } -napi_value ToNumber(napi_env env, napi_callback_info info) { +static napi_value ToNumber(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)); @@ -109,7 +109,7 @@ napi_value ToNumber(napi_env env, napi_callback_info info) { return output; } -napi_value ToObject(napi_env env, napi_callback_info info) { +static napi_value ToObject(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)); @@ -120,7 +120,7 @@ napi_value ToObject(napi_env env, napi_callback_info info) { return output; } -napi_value ToString(napi_env env, napi_callback_info info) { +static napi_value ToString(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)); @@ -131,7 +131,7 @@ napi_value ToString(napi_env env, napi_callback_info info) { return output; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("asBool", AsBool), DECLARE_NAPI_PROPERTY("asInt32", AsInt32), diff --git a/test/addons-napi/test_dataview/test_dataview.c b/test/addons-napi/test_dataview/test_dataview.c index 4d29ed07e9e6f7..8d29743522022f 100644 --- a/test/addons-napi/test_dataview/test_dataview.c +++ b/test/addons-napi/test_dataview/test_dataview.c @@ -2,7 +2,7 @@ #include #include "../common.h" -napi_value CreateDataView(napi_env env, napi_callback_info info) { +static napi_value CreateDataView(napi_env env, napi_callback_info info) { size_t argc = 3; napi_value args [3]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); @@ -49,7 +49,7 @@ napi_value CreateDataView(napi_env env, napi_callback_info info) { return output_dataview; } -napi_value CreateDataViewFromJSDataView(napi_env env, napi_callback_info info) { +static napi_value CreateDataViewFromJSDataView(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)); @@ -85,7 +85,7 @@ napi_value CreateDataViewFromJSDataView(napi_env env, napi_callback_info info) { return output_dataview; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("CreateDataView", CreateDataView), DECLARE_NAPI_PROPERTY("CreateDataViewFromJSDataView", diff --git a/test/addons-napi/test_env_sharing/compare_env.c b/test/addons-napi/test_env_sharing/compare_env.c index 6a93ce52c64025..3326a34067219a 100644 --- a/test/addons-napi/test_env_sharing/compare_env.c +++ b/test/addons-napi/test_env_sharing/compare_env.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value compare(napi_env env, napi_callback_info info) { +static napi_value compare(napi_env env, napi_callback_info info) { napi_value external; size_t argc = 1; void* data; @@ -14,7 +14,7 @@ napi_value compare(napi_env env, napi_callback_info info) { return return_value; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { NAPI_CALL(env, napi_create_function( env, "exports", NAPI_AUTO_LENGTH, compare, NULL, &exports)); return exports; diff --git a/test/addons-napi/test_env_sharing/store_env.c b/test/addons-napi/test_env_sharing/store_env.c index 809f5f7a4b2eed..0559b178cba68b 100644 --- a/test/addons-napi/test_env_sharing/store_env.c +++ b/test/addons-napi/test_env_sharing/store_env.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_value external; NAPI_CALL(env, napi_create_external(env, env, NULL, NULL, &external)); return external; diff --git a/test/addons-napi/test_error/binding.gyp b/test/addons-napi/test_error/binding.gyp index c2defd9551a31b..2923e15adca1a1 100644 --- a/test/addons-napi/test_error/binding.gyp +++ b/test/addons-napi/test_error/binding.gyp @@ -2,7 +2,7 @@ "targets": [ { "target_name": "test_error", - "sources": [ "test_error.cc" ] + "sources": [ "test_error.c" ] } ] } diff --git a/test/addons-napi/test_error/test_error.cc b/test/addons-napi/test_error/test_error.c similarity index 87% rename from test/addons-napi/test_error/test_error.cc rename to test/addons-napi/test_error/test_error.c index 4ab20bd7522a3b..1cad38098c9917 100644 --- a/test/addons-napi/test_error/test_error.cc +++ b/test/addons-napi/test_error/test_error.c @@ -4,7 +4,7 @@ napi_value checkError(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)); + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); bool r; NAPI_CALL(env, napi_is_error(env, args[0], &r)); @@ -20,43 +20,43 @@ napi_value throwExistingError(napi_env env, napi_callback_info info) { napi_value error; NAPI_CALL(env, napi_create_string_utf8( env, "existing error", NAPI_AUTO_LENGTH, &message)); - NAPI_CALL(env, napi_create_error(env, nullptr, message, &error)); + NAPI_CALL(env, napi_create_error(env, NULL, message, &error)); NAPI_CALL(env, napi_throw(env, error)); - return nullptr; + return NULL; } napi_value throwError(napi_env env, napi_callback_info info) { - NAPI_CALL(env, napi_throw_error(env, nullptr, "error")); - return nullptr; + NAPI_CALL(env, napi_throw_error(env, NULL, "error")); + return NULL; } napi_value throwRangeError(napi_env env, napi_callback_info info) { - NAPI_CALL(env, napi_throw_range_error(env, nullptr, "range error")); - return nullptr; + NAPI_CALL(env, napi_throw_range_error(env, NULL, "range error")); + return NULL; } napi_value throwTypeError(napi_env env, napi_callback_info info) { - NAPI_CALL(env, napi_throw_type_error(env, nullptr, "type error")); - return nullptr; + NAPI_CALL(env, napi_throw_type_error(env, NULL, "type error")); + return NULL; } napi_value throwErrorCode(napi_env env, napi_callback_info info) { NAPI_CALL(env, napi_throw_error(env, "ERR_TEST_CODE", "Error [error]")); - return nullptr; + return NULL; } napi_value throwRangeErrorCode(napi_env env, napi_callback_info info) { NAPI_CALL(env, napi_throw_range_error(env, "ERR_TEST_CODE", "RangeError [range error]")); - return nullptr; + return NULL; } napi_value throwTypeErrorCode(napi_env env, napi_callback_info info) { NAPI_CALL(env, napi_throw_type_error(env, "ERR_TEST_CODE", "TypeError [type error]")); - return nullptr; + return NULL; } @@ -65,7 +65,7 @@ napi_value createError(napi_env env, napi_callback_info info) { napi_value message; NAPI_CALL(env, napi_create_string_utf8( env, "error", NAPI_AUTO_LENGTH, &message)); - NAPI_CALL(env, napi_create_error(env, nullptr, message, &result)); + NAPI_CALL(env, napi_create_error(env, NULL, message, &result)); return result; } @@ -74,7 +74,7 @@ napi_value createRangeError(napi_env env, napi_callback_info info) { napi_value message; NAPI_CALL(env, napi_create_string_utf8( env, "range error", NAPI_AUTO_LENGTH, &message)); - NAPI_CALL(env, napi_create_range_error(env, nullptr, message, &result)); + NAPI_CALL(env, napi_create_range_error(env, NULL, message, &result)); return result; } @@ -83,7 +83,7 @@ napi_value createTypeError(napi_env env, napi_callback_info info) { napi_value message; NAPI_CALL(env, napi_create_string_utf8( env, "type error", NAPI_AUTO_LENGTH, &message)); - NAPI_CALL(env, napi_create_type_error(env, nullptr, message, &result)); + NAPI_CALL(env, napi_create_type_error(env, NULL, message, &result)); return result; } diff --git a/test/addons-napi/test_fatal/test_fatal.c b/test/addons-napi/test_fatal/test_fatal.c index add8bebf3be7e3..b9248d40d49e6a 100644 --- a/test/addons-napi/test_fatal/test_fatal.c +++ b/test/addons-napi/test_fatal/test_fatal.c @@ -1,18 +1,18 @@ #include #include "../common.h" -napi_value Test(napi_env env, napi_callback_info info) { +static napi_value Test(napi_env env, napi_callback_info info) { napi_fatal_error("test_fatal::Test", NAPI_AUTO_LENGTH, "fatal message", NAPI_AUTO_LENGTH); return NULL; } -napi_value TestStringLength(napi_env env, napi_callback_info info) { +static napi_value TestStringLength(napi_env env, napi_callback_info info) { napi_fatal_error("test_fatal::TestStringLength", 16, "fatal message", 13); return NULL; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor properties[] = { DECLARE_NAPI_PROPERTY("Test", Test), DECLARE_NAPI_PROPERTY("TestStringLength", TestStringLength), diff --git a/test/addons-napi/test_fatal_exception/test_fatal_exception.c b/test/addons-napi/test_fatal_exception/test_fatal_exception.c index fd81c56d856db8..3cc810ccc0d10c 100644 --- a/test/addons-napi/test_fatal_exception/test_fatal_exception.c +++ b/test/addons-napi/test_fatal_exception/test_fatal_exception.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value Test(napi_env env, napi_callback_info info) { +static napi_value Test(napi_env env, napi_callback_info info) { napi_value err; size_t argc = 1; @@ -12,7 +12,7 @@ napi_value Test(napi_env env, napi_callback_info info) { return NULL; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor properties[] = { DECLARE_NAPI_PROPERTY("Test", Test), }; diff --git a/test/addons-napi/test_function/test_function.c b/test/addons-napi/test_function/test_function.c index b0350e6ee22876..2c361933cfa071 100644 --- a/test/addons-napi/test_function/test_function.c +++ b/test/addons-napi/test_function/test_function.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value TestCallFunction(napi_env env, napi_callback_info info) { +static napi_value TestCallFunction(napi_env env, napi_callback_info info) { size_t argc = 10; napi_value args[10]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); @@ -26,11 +26,11 @@ napi_value TestCallFunction(napi_env env, napi_callback_info info) { return result; } -napi_value TestFunctionName(napi_env env, napi_callback_info info) { +static napi_value TestFunctionName(napi_env env, napi_callback_info info) { return NULL; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_value fn1; NAPI_CALL(env, napi_create_function( env, NULL, NAPI_AUTO_LENGTH, TestCallFunction, NULL, &fn1)); diff --git a/test/addons-napi/test_general/test_general.c b/test/addons-napi/test_general/test_general.c index 05cdd76e3c1e64..8f429f939fb89e 100644 --- a/test/addons-napi/test_general/test_general.c +++ b/test/addons-napi/test_general/test_general.c @@ -2,7 +2,7 @@ #include #include "../common.h" -napi_value testStrictEquals(napi_env env, napi_callback_info info) { +static napi_value testStrictEquals(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, NULL, NULL)); @@ -15,7 +15,7 @@ napi_value testStrictEquals(napi_env env, napi_callback_info info) { return result; } -napi_value testGetPrototype(napi_env env, napi_callback_info info) { +static napi_value testGetPrototype(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)); @@ -26,7 +26,7 @@ napi_value testGetPrototype(napi_env env, napi_callback_info info) { return result; } -napi_value testGetVersion(napi_env env, napi_callback_info info) { +static napi_value testGetVersion(napi_env env, napi_callback_info info) { uint32_t version; napi_value result; NAPI_CALL(env, napi_get_version(env, &version)); @@ -34,7 +34,7 @@ napi_value testGetVersion(napi_env env, napi_callback_info info) { return result; } -napi_value testGetNodeVersion(napi_env env, napi_callback_info info) { +static napi_value testGetNodeVersion(napi_env env, napi_callback_info info) { const napi_node_version* node_version; napi_value result, major, minor, patch, release; NAPI_CALL(env, napi_get_node_version(env, &node_version)); @@ -53,7 +53,7 @@ napi_value testGetNodeVersion(napi_env env, napi_callback_info info) { return result; } -napi_value doInstanceOf(napi_env env, napi_callback_info info) { +static napi_value doInstanceOf(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, NULL, NULL)); @@ -67,19 +67,19 @@ napi_value doInstanceOf(napi_env env, napi_callback_info info) { return result; } -napi_value getNull(napi_env env, napi_callback_info info) { +static napi_value getNull(napi_env env, napi_callback_info info) { napi_value result; NAPI_CALL(env, napi_get_null(env, &result)); return result; } -napi_value getUndefined(napi_env env, napi_callback_info info) { +static napi_value getUndefined(napi_env env, napi_callback_info info) { napi_value result; NAPI_CALL(env, napi_get_undefined(env, &result)); return result; } -napi_value createNapiError(napi_env env, napi_callback_info info) { +static napi_value createNapiError(napi_env env, napi_callback_info info) { napi_value value; NAPI_CALL(env, napi_create_string_utf8(env, "xyz", 3, &value)); @@ -99,7 +99,7 @@ napi_value createNapiError(napi_env env, napi_callback_info info) { return NULL; } -napi_value testNapiErrorCleanup(napi_env env, napi_callback_info info) { +static napi_value testNapiErrorCleanup(napi_env env, napi_callback_info info) { const napi_extended_error_info *error_info = 0; NAPI_CALL(env, napi_get_last_error_info(env, &error_info)); @@ -110,7 +110,7 @@ napi_value testNapiErrorCleanup(napi_env env, napi_callback_info info) { return result; } -napi_value testNapiTypeof(napi_env env, napi_callback_info info) { +static napi_value testNapiTypeof(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)); @@ -157,7 +157,7 @@ static void deref_item(napi_env env, void* data, void* hint) { deref_item_called = true; } -napi_value deref_item_was_called(napi_env env, napi_callback_info info) { +static napi_value deref_item_was_called(napi_env env, napi_callback_info info) { napi_value it_was_called; NAPI_CALL(env, napi_get_boolean(env, deref_item_called, &it_was_called)); @@ -165,7 +165,7 @@ napi_value deref_item_was_called(napi_env env, napi_callback_info info) { return it_was_called; } -napi_value wrap(napi_env env, napi_callback_info info) { +static napi_value wrap(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value to_wrap; @@ -177,7 +177,7 @@ napi_value wrap(napi_env env, napi_callback_info info) { return NULL; } -napi_value remove_wrap(napi_env env, napi_callback_info info) { +static napi_value remove_wrap(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value wrapped; void* data; @@ -193,7 +193,7 @@ static void test_finalize(napi_env env, void* data, void* hint) { finalize_called = true; } -napi_value test_finalize_wrap(napi_env env, napi_callback_info info) { +static napi_value test_finalize_wrap(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value js_object; @@ -203,7 +203,7 @@ napi_value test_finalize_wrap(napi_env env, napi_callback_info info) { return NULL; } -napi_value finalize_was_called(napi_env env, napi_callback_info info) { +static napi_value finalize_was_called(napi_env env, napi_callback_info info) { napi_value it_was_called; NAPI_CALL(env, napi_get_boolean(env, finalize_called, &it_was_called)); @@ -211,7 +211,7 @@ napi_value finalize_was_called(napi_env env, napi_callback_info info) { return it_was_called; } -napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) { +static napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) { napi_value result; int64_t adjustedValue; @@ -221,7 +221,7 @@ napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) { return result; } -napi_value testNapiRun(napi_env env, napi_callback_info info) { +static napi_value testNapiRun(napi_env env, napi_callback_info info) { napi_value script, result; size_t argc = 1; @@ -232,7 +232,7 @@ napi_value testNapiRun(napi_env env, napi_callback_info info) { return result; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("testStrictEquals", testStrictEquals), DECLARE_NAPI_PROPERTY("testGetPrototype", testGetPrototype), diff --git a/test/addons-napi/test_handle_scope/test_handle_scope.c b/test/addons-napi/test_handle_scope/test_handle_scope.c index 31efbcf3dd4d7b..76f31f7882b61b 100644 --- a/test/addons-napi/test_handle_scope/test_handle_scope.c +++ b/test/addons-napi/test_handle_scope/test_handle_scope.c @@ -7,7 +7,7 @@ // the right right thing would be quite hard so we keep it // simple for now. -napi_value NewScope(napi_env env, napi_callback_info info) { +static napi_value NewScope(napi_env env, napi_callback_info info) { napi_handle_scope scope; napi_value output = NULL; @@ -17,7 +17,7 @@ napi_value NewScope(napi_env env, napi_callback_info info) { return NULL; } -napi_value NewScopeEscape(napi_env env, napi_callback_info info) { +static napi_value NewScopeEscape(napi_env env, napi_callback_info info) { napi_escapable_handle_scope scope; napi_value output = NULL; napi_value escapee = NULL; @@ -29,7 +29,7 @@ napi_value NewScopeEscape(napi_env env, napi_callback_info info) { return escapee; } -napi_value NewScopeEscapeTwice(napi_env env, napi_callback_info info) { +static napi_value NewScopeEscapeTwice(napi_env env, napi_callback_info info) { napi_escapable_handle_scope scope; napi_value output = NULL; napi_value escapee = NULL; @@ -44,7 +44,7 @@ napi_value NewScopeEscapeTwice(napi_env env, napi_callback_info info) { return NULL; } -napi_value NewScopeWithException(napi_env env, napi_callback_info info) { +static napi_value NewScopeWithException(napi_env env, napi_callback_info info) { napi_handle_scope scope; size_t argc; napi_value exception_function; @@ -68,7 +68,7 @@ napi_value NewScopeWithException(napi_env env, napi_callback_info info) { return NULL; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor properties[] = { DECLARE_NAPI_PROPERTY("NewScope", NewScope), DECLARE_NAPI_PROPERTY("NewScopeEscape", NewScopeEscape), diff --git a/test/addons-napi/test_make_callback/binding.c b/test/addons-napi/test_make_callback/binding.c index 23750f56b838fc..8eedd5b1b3b167 100644 --- a/test/addons-napi/test_make_callback/binding.c +++ b/test/addons-napi/test_make_callback/binding.c @@ -3,8 +3,7 @@ #define MAX_ARGUMENTS 10 -static -napi_value MakeCallback(napi_env env, napi_callback_info info) { +static napi_value MakeCallback(napi_env env, napi_callback_info info) { size_t argc = MAX_ARGUMENTS; size_t n; napi_value args[MAX_ARGUMENTS]; diff --git a/test/addons-napi/test_new_target/binding.c b/test/addons-napi/test_new_target/binding.c index a74d4bb2f877be..0c542ebaba693d 100644 --- a/test/addons-napi/test_new_target/binding.c +++ b/test/addons-napi/test_new_target/binding.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value BaseClass(napi_env env, napi_callback_info info) { +static napi_value BaseClass(napi_env env, napi_callback_info info) { napi_value newTargetArg; NAPI_CALL(env, napi_get_new_target(env, info, &newTargetArg)); napi_value thisArg; @@ -22,7 +22,7 @@ napi_value BaseClass(napi_env env, napi_callback_info info) { return thisArg; } -napi_value Constructor(napi_env env, napi_callback_info info) { +static napi_value Constructor(napi_env env, napi_callback_info info) { bool result; napi_value newTargetArg; NAPI_CALL(env, napi_get_new_target(env, info, &newTargetArg)); @@ -45,7 +45,7 @@ napi_value Constructor(napi_env env, napi_callback_info info) { return thisArg; } -napi_value OrdinaryFunction(napi_env env, napi_callback_info info) { +static napi_value OrdinaryFunction(napi_env env, napi_callback_info info) { napi_value newTargetArg; NAPI_CALL(env, napi_get_new_target(env, info, &newTargetArg)); @@ -56,7 +56,7 @@ napi_value OrdinaryFunction(napi_env env, napi_callback_info info) { return _true; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { const napi_property_descriptor desc[] = { DECLARE_NAPI_PROPERTY("BaseClass", BaseClass), DECLARE_NAPI_PROPERTY("OrdinaryFunction", OrdinaryFunction), diff --git a/test/addons-napi/test_number/test_number.c b/test/addons-napi/test_number/test_number.c index a1a70950083324..19b0ae83f051df 100644 --- a/test/addons-napi/test_number/test_number.c +++ b/test/addons-napi/test_number/test_number.c @@ -1,7 +1,7 @@ #include #include "../common.h" -napi_value Test(napi_env env, napi_callback_info info) { +static napi_value Test(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)); @@ -23,7 +23,7 @@ napi_value Test(napi_env env, napi_callback_info info) { return output; } -napi_value TestInt32Truncation(napi_env env, napi_callback_info info) { +static napi_value TestInt32Truncation(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)); @@ -45,7 +45,7 @@ napi_value TestInt32Truncation(napi_env env, napi_callback_info info) { return output; } -napi_value TestInt64Truncation(napi_env env, napi_callback_info info) { +static napi_value TestInt64Truncation(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)); @@ -67,7 +67,7 @@ napi_value TestInt64Truncation(napi_env env, napi_callback_info info) { return output; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("Test", Test), DECLARE_NAPI_PROPERTY("TestInt32Truncation", TestInt32Truncation), diff --git a/test/addons-napi/test_object/test_object.c b/test/addons-napi/test_object/test_object.c index ccf1573114a6f1..046f71fa414735 100644 --- a/test/addons-napi/test_object/test_object.c +++ b/test/addons-napi/test_object/test_object.c @@ -4,7 +4,7 @@ static int test_value = 3; -napi_value Get(napi_env env, napi_callback_info info) { +static napi_value Get(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, NULL, NULL)); @@ -30,7 +30,7 @@ napi_value Get(napi_env env, napi_callback_info info) { return output; } -napi_value Set(napi_env env, napi_callback_info info) { +static napi_value Set(napi_env env, napi_callback_info info) { size_t argc = 3; napi_value args[3]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); @@ -57,7 +57,7 @@ napi_value Set(napi_env env, napi_callback_info info) { return valuetrue; } -napi_value Has(napi_env env, napi_callback_info info) { +static napi_value Has(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, NULL, NULL)); @@ -85,7 +85,7 @@ napi_value Has(napi_env env, napi_callback_info info) { return ret; } -napi_value HasOwn(napi_env env, napi_callback_info info) { +static napi_value HasOwn(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, NULL, NULL)); @@ -113,7 +113,7 @@ napi_value HasOwn(napi_env env, napi_callback_info info) { return ret; } -napi_value Delete(napi_env env, napi_callback_info info) { +static napi_value Delete(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2]; @@ -138,7 +138,7 @@ napi_value Delete(napi_env env, napi_callback_info info) { return ret; } -napi_value New(napi_env env, napi_callback_info info) { +static napi_value New(napi_env env, napi_callback_info info) { napi_value ret; NAPI_CALL(env, napi_create_object(env, &ret)); @@ -157,7 +157,7 @@ napi_value New(napi_env env, napi_callback_info info) { return ret; } -napi_value Inflate(napi_env env, napi_callback_info info) { +static napi_value Inflate(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)); @@ -193,7 +193,7 @@ napi_value Inflate(napi_env env, napi_callback_info info) { return obj; } -napi_value Wrap(napi_env env, napi_callback_info info) { +static napi_value Wrap(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value arg; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &arg, NULL, NULL)); @@ -202,7 +202,7 @@ napi_value Wrap(napi_env env, napi_callback_info info) { return NULL; } -napi_value Unwrap(napi_env env, napi_callback_info info) { +static napi_value Unwrap(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value arg; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &arg, NULL, NULL)); @@ -216,7 +216,7 @@ napi_value Unwrap(napi_env env, napi_callback_info info) { return result; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("Get", Get), DECLARE_NAPI_PROPERTY("Set", Set), diff --git a/test/addons-napi/test_promise/test_promise.c b/test/addons-napi/test_promise/test_promise.c index aa89c022bc1a4a..3e2ec48572af4c 100644 --- a/test/addons-napi/test_promise/test_promise.c +++ b/test/addons-napi/test_promise/test_promise.c @@ -3,7 +3,7 @@ napi_deferred deferred = NULL; -napi_value createPromise(napi_env env, napi_callback_info info) { +static napi_value createPromise(napi_env env, napi_callback_info info) { napi_value promise; // We do not overwrite an existing deferred. @@ -16,7 +16,8 @@ napi_value createPromise(napi_env env, napi_callback_info info) { return promise; } -napi_value concludeCurrentPromise(napi_env env, napi_callback_info info) { +static napi_value +concludeCurrentPromise(napi_env env, napi_callback_info info) { napi_value argv[2]; size_t argc = 2; bool resolution; @@ -34,7 +35,7 @@ napi_value concludeCurrentPromise(napi_env env, napi_callback_info info) { return NULL; } -napi_value isPromise(napi_env env, napi_callback_info info) { +static napi_value isPromise(napi_env env, napi_callback_info info) { napi_value promise, result; size_t argc = 1; bool is_promise; @@ -46,7 +47,7 @@ napi_value isPromise(napi_env env, napi_callback_info info) { return result; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("createPromise", createPromise), DECLARE_NAPI_PROPERTY("concludeCurrentPromise", concludeCurrentPromise), diff --git a/test/addons-napi/test_properties/test_properties.c b/test/addons-napi/test_properties/test_properties.c index a95c7013c2cabb..2754812f4dad27 100644 --- a/test/addons-napi/test_properties/test_properties.c +++ b/test/addons-napi/test_properties/test_properties.c @@ -3,7 +3,7 @@ static double value_ = 1; -napi_value GetValue(napi_env env, napi_callback_info info) { +static napi_value GetValue(napi_env env, napi_callback_info info) { size_t argc = 0; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, NULL, NULL, NULL)); @@ -15,7 +15,7 @@ napi_value GetValue(napi_env env, napi_callback_info info) { return number; } -napi_value SetValue(napi_env env, napi_callback_info info) { +static napi_value SetValue(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)); @@ -27,7 +27,7 @@ napi_value SetValue(napi_env env, napi_callback_info info) { return NULL; } -napi_value Echo(napi_env env, napi_callback_info info) { +static napi_value Echo(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)); @@ -37,7 +37,7 @@ napi_value Echo(napi_env env, napi_callback_info info) { return args[0]; } -napi_value HasNamedProperty(napi_env env, napi_callback_info info) { +static napi_value HasNamedProperty(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, NULL, NULL)); @@ -59,7 +59,7 @@ napi_value HasNamedProperty(napi_env env, napi_callback_info info) { return result; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_value number; NAPI_CALL(env, napi_create_double(env, value_, &number)); diff --git a/test/addons-napi/test_reference/test_reference.c b/test/addons-napi/test_reference/test_reference.c index f34adc6693b6fe..75abc49ad3280e 100644 --- a/test/addons-napi/test_reference/test_reference.c +++ b/test/addons-napi/test_reference/test_reference.c @@ -5,20 +5,20 @@ static int test_value = 1; static int finalize_count = 0; static napi_ref test_reference = NULL; -napi_value GetFinalizeCount(napi_env env, napi_callback_info info) { +static napi_value GetFinalizeCount(napi_env env, napi_callback_info info) { napi_value result; NAPI_CALL(env, napi_create_int32(env, finalize_count, &result)); return result; } -void FinalizeExternal(napi_env env, void* data, void* hint) { +static void FinalizeExternal(napi_env env, void* data, void* hint) { int *actual_value = data; NAPI_ASSERT_RETURN_VOID(env, actual_value == &test_value, "The correct pointer was passed to the finalizer"); finalize_count++; } -napi_value CreateExternal(napi_env env, napi_callback_info info) { +static napi_value CreateExternal(napi_env env, napi_callback_info info) { int* data = &test_value; napi_value result; @@ -33,7 +33,8 @@ napi_value CreateExternal(napi_env env, napi_callback_info info) { return result; } -napi_value CreateExternalWithFinalize(napi_env env, napi_callback_info info) { +static napi_value +CreateExternalWithFinalize(napi_env env, napi_callback_info info) { napi_value result; NAPI_CALL(env, napi_create_external(env, @@ -46,7 +47,7 @@ napi_value CreateExternalWithFinalize(napi_env env, napi_callback_info info) { return result; } -napi_value CheckExternal(napi_env env, napi_callback_info info) { +static napi_value CheckExternal(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value arg; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &arg, NULL, NULL)); @@ -67,7 +68,7 @@ napi_value CheckExternal(napi_env env, napi_callback_info info) { return NULL; } -napi_value CreateReference(napi_env env, napi_callback_info info) { +static napi_value CreateReference(napi_env env, napi_callback_info info) { NAPI_ASSERT(env, test_reference == NULL, "The test allows only one reference at a time."); @@ -88,7 +89,7 @@ napi_value CreateReference(napi_env env, napi_callback_info info) { return NULL; } -napi_value DeleteReference(napi_env env, napi_callback_info info) { +static napi_value DeleteReference(napi_env env, napi_callback_info info) { NAPI_ASSERT(env, test_reference != NULL, "A reference must have been created."); @@ -97,7 +98,7 @@ napi_value DeleteReference(napi_env env, napi_callback_info info) { return NULL; } -napi_value IncrementRefcount(napi_env env, napi_callback_info info) { +static napi_value IncrementRefcount(napi_env env, napi_callback_info info) { NAPI_ASSERT(env, test_reference != NULL, "A reference must have been created."); @@ -109,7 +110,7 @@ napi_value IncrementRefcount(napi_env env, napi_callback_info info) { return result; } -napi_value DecrementRefcount(napi_env env, napi_callback_info info) { +static napi_value DecrementRefcount(napi_env env, napi_callback_info info) { NAPI_ASSERT(env, test_reference != NULL, "A reference must have been created."); @@ -121,7 +122,7 @@ napi_value DecrementRefcount(napi_env env, napi_callback_info info) { return result; } -napi_value GetReferenceValue(napi_env env, napi_callback_info info) { +static napi_value GetReferenceValue(napi_env env, napi_callback_info info) { NAPI_ASSERT(env, test_reference != NULL, "A reference must have been created."); @@ -130,7 +131,7 @@ napi_value GetReferenceValue(napi_env env, napi_callback_info info) { return result; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_GETTER("finalizeCount", GetFinalizeCount), DECLARE_NAPI_PROPERTY("createExternal", CreateExternal), diff --git a/test/addons-napi/test_string/test_string.c b/test/addons-napi/test_string/test_string.c index 99a3f7d3545a20..4e6da7bf86849f 100644 --- a/test/addons-napi/test_string/test_string.c +++ b/test/addons-napi/test_string/test_string.c @@ -2,7 +2,7 @@ #include #include "../common.h" -napi_value TestLatin1(napi_env env, napi_callback_info info) { +static napi_value TestLatin1(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)); @@ -28,7 +28,7 @@ napi_value TestLatin1(napi_env env, napi_callback_info info) { return output; } -napi_value TestUtf8(napi_env env, napi_callback_info info) { +static napi_value TestUtf8(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)); @@ -54,7 +54,7 @@ napi_value TestUtf8(napi_env env, napi_callback_info info) { return output; } -napi_value TestUtf16(napi_env env, napi_callback_info info) { +static napi_value TestUtf16(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)); @@ -80,7 +80,8 @@ napi_value TestUtf16(napi_env env, napi_callback_info info) { return output; } -napi_value TestLatin1Insufficient(napi_env env, napi_callback_info info) { +static napi_value +TestLatin1Insufficient(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)); @@ -106,7 +107,7 @@ napi_value TestLatin1Insufficient(napi_env env, napi_callback_info info) { return output; } -napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) { +static napi_value TestUtf8Insufficient(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)); @@ -132,7 +133,7 @@ napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) { return output; } -napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) { +static napi_value TestUtf16Insufficient(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)); @@ -158,7 +159,7 @@ napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) { return output; } -napi_value Utf16Length(napi_env env, napi_callback_info info) { +static napi_value Utf16Length(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)); @@ -180,7 +181,7 @@ napi_value Utf16Length(napi_env env, napi_callback_info info) { return output; } -napi_value Utf8Length(napi_env env, napi_callback_info info) { +static napi_value Utf8Length(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)); @@ -202,7 +203,7 @@ napi_value Utf8Length(napi_env env, napi_callback_info info) { return output; } -napi_value TestLargeUtf8(napi_env env, napi_callback_info info) { +static napi_value TestLargeUtf8(napi_env env, napi_callback_info info) { napi_value output; if (SIZE_MAX > INT_MAX) { NAPI_CALL(env, napi_create_string_utf8(env, "", ((size_t)INT_MAX) + 1, &output)); @@ -215,7 +216,7 @@ napi_value TestLargeUtf8(napi_env env, napi_callback_info info) { return output; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor properties[] = { DECLARE_NAPI_PROPERTY("TestLatin1", TestLatin1), DECLARE_NAPI_PROPERTY("TestLatin1Insufficient", TestLatin1Insufficient), diff --git a/test/addons-napi/test_symbol/test_symbol.c b/test/addons-napi/test_symbol/test_symbol.c index c91b6ae54f4932..e2c969b3b6aba6 100644 --- a/test/addons-napi/test_symbol/test_symbol.c +++ b/test/addons-napi/test_symbol/test_symbol.c @@ -1,32 +1,7 @@ #include #include "../common.h" -napi_value Test(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)); - - NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); - - napi_valuetype valuetype; - NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); - - NAPI_ASSERT(env, valuetype == napi_symbol, - "Wrong type of arguments. Expects a symbol."); - - char buffer[128]; - size_t buffer_size = 128; - - NAPI_CALL(env, napi_get_value_string_utf8( - env, args[0], buffer, buffer_size, NULL)); - - napi_value output; - NAPI_CALL(env, napi_create_string_utf8(env, buffer, buffer_size, &output)); - - return output; -} - -napi_value New(napi_env env, napi_callback_info info) { +static napi_value New(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)); @@ -48,7 +23,7 @@ napi_value New(napi_env env, napi_callback_info info) { return symbol; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor properties[] = { DECLARE_NAPI_PROPERTY("New", New), }; diff --git a/test/addons-napi/test_typedarray/test_typedarray.c b/test/addons-napi/test_typedarray/test_typedarray.c index 2758a6f53298fe..a0ac02665864f1 100644 --- a/test/addons-napi/test_typedarray/test_typedarray.c +++ b/test/addons-napi/test_typedarray/test_typedarray.c @@ -2,7 +2,7 @@ #include #include "../common.h" -napi_value Multiply(napi_env env, napi_callback_info info) { +static napi_value Multiply(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, NULL, NULL)); @@ -73,7 +73,7 @@ napi_value Multiply(napi_env env, napi_callback_info info) { return output_array; } -napi_value External(napi_env env, napi_callback_info info) { +static napi_value External(napi_env env, napi_callback_info info) { static int8_t externalData[] = {0, 1, 2}; napi_value output_buffer; @@ -96,7 +96,7 @@ napi_value External(napi_env env, napi_callback_info info) { return output_array; } -napi_value CreateTypedArray(napi_env env, napi_callback_info info) { +static napi_value CreateTypedArray(napi_env env, napi_callback_info info) { size_t argc = 4; napi_value args[4]; NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); @@ -165,7 +165,7 @@ napi_value CreateTypedArray(napi_env env, napi_callback_info info) { return output_array; } -napi_value Init(napi_env env, napi_value exports) { +static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("Multiply", Multiply), DECLARE_NAPI_PROPERTY("External", External), diff --git a/test/async-hooks/test-timers.setInterval.js b/test/async-hooks/test-timers.setInterval.js index a3434e134fab02..10b2bafb2a2598 100644 --- a/test/async-hooks/test-timers.setInterval.js +++ b/test/async-hooks/test-timers.setInterval.js @@ -9,7 +9,7 @@ const TIMEOUT = common.platformTimeout(100); const hooks = initHooks(); hooks.enable(); -setInterval(common.mustCall(ontimeout), TIMEOUT); +const interval = setInterval(common.mustCall(ontimeout, 2), TIMEOUT); const as = hooks.activitiesOfTypes('Timeout'); assert.strictEqual(as.length, 1); const t1 = as[0]; @@ -18,9 +18,18 @@ assert.strictEqual(typeof t1.uid, 'number'); assert.strictEqual(typeof t1.triggerAsyncId, 'number'); checkInvocations(t1, { init: 1 }, 't1: when timer installed'); +let iter = 0; function ontimeout() { - checkInvocations(t1, { init: 1, before: 1 }, 't1: when first timer fired'); - + if (iter === 0) { + checkInvocations(t1, { init: 1, before: 1 }, 't1: when first timer fired'); + } else { + checkInvocations(t1, { init: 1, before: 2, after: 1 }, + 't1: when first interval fired again'); + clearInterval(interval); + return; + } + + iter++; throw new Error('setInterval Error'); } @@ -32,6 +41,6 @@ process.on('exit', () => { hooks.disable(); hooks.sanityCheck('Timeout'); - checkInvocations(t1, { init: 1, before: 1, after: 1, destroy: 1 }, + checkInvocations(t1, { init: 1, before: 2, after: 2, destroy: 1 }, 't1: when process exits'); }); diff --git a/test/cctest/test_platform.cc b/test/cctest/test_platform.cc new file mode 100644 index 00000000000000..876547480b7032 --- /dev/null +++ b/test/cctest/test_platform.cc @@ -0,0 +1,55 @@ +#include "node_internals.h" +#include "libplatform/libplatform.h" + +#include +#include "gtest/gtest.h" +#include "node_test_fixture.h" + +// This task increments the given run counter and reposts itself until the +// repost counter reaches zero. +class RepostingTask : public v8::Task { + public: + explicit RepostingTask(int repost_count, + int* run_count, + v8::Isolate* isolate, + node::NodePlatform* platform) + : repost_count_(repost_count), + run_count_(run_count), + isolate_(isolate), + platform_(platform) {} + + // v8::Task implementation + void Run() final { + ++*run_count_; + if (repost_count_ > 0) { + --repost_count_; + platform_->CallOnForegroundThread(isolate_, + new RepostingTask(repost_count_, run_count_, isolate_, platform_)); + } + } + + private: + int repost_count_; + int* run_count_; + v8::Isolate* isolate_; + node::NodePlatform* platform_; +}; + +class PlatformTest : public EnvironmentTestFixture {}; + +TEST_F(PlatformTest, SkipNewTasksInFlushForegroundTasks) { + v8::Isolate::Scope isolate_scope(isolate_); + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env {handle_scope, argv}; + int run_count = 0; + platform->CallOnForegroundThread( + isolate_, new RepostingTask(2, &run_count, isolate_, platform.get())); + EXPECT_TRUE(platform->FlushForegroundTasks(isolate_)); + EXPECT_EQ(1, run_count); + EXPECT_TRUE(platform->FlushForegroundTasks(isolate_)); + EXPECT_EQ(2, run_count); + EXPECT_TRUE(platform->FlushForegroundTasks(isolate_)); + EXPECT_EQ(3, run_count); + EXPECT_FALSE(platform->FlushForegroundTasks(isolate_)); +} diff --git a/test/common/index.js b/test/common/index.js index 95bb8dd804881f..bbd2b62d7da768 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -708,6 +708,11 @@ exports.expectsError = function expectsError(fn, settings, exact) { } function innerFn(error) { + if (arguments.length !== 1) { + // Do not use `assert.strictEqual()` to prevent `util.inspect` from + // always being called. + assert.fail(`Expected one argument, got ${util.inspect(arguments)}`); + } const descriptor = Object.getOwnPropertyDescriptor(error, 'message'); assert.strictEqual(descriptor.enumerable, false, 'The error message should be non-enumerable'); diff --git a/test/common/inspector-helper.js b/test/common/inspector-helper.js index 1bc16e3714cffa..e590349f9c2b71 100644 --- a/test/common/inspector-helper.js +++ b/test/common/inspector-helper.js @@ -154,8 +154,9 @@ class InspectorSession { return this._terminationPromise; } - disconnect() { + async disconnect() { this._socket.destroy(); + return this.waitForServerDisconnect(); } _onMessage(message) { @@ -444,6 +445,7 @@ class NodeInstance { kill() { this._process.kill(); + return this.expectShutdown(); } scriptPath() { diff --git a/test/doctool/test-doctool-html.js b/test/doctool/test-doctool-html.js index bd1c5ddecac321..91037bfd6501bc 100644 --- a/test/doctool/test-doctool-html.js +++ b/test/doctool/test-doctool-html.js @@ -10,7 +10,6 @@ try { const assert = require('assert'); const fs = require('fs'); -const path = require('path'); const fixtures = require('../common/fixtures'); const processIncludes = require('../../tools/doc/preprocess.js'); const html = require('../../tools/doc/html.js'); @@ -107,7 +106,6 @@ testData.forEach((item) => { { input: preprocessed, filename: 'foo', - template: path.resolve(__dirname, '../../doc/template.html'), nodeVersion: process.version, analytics: item.analyticsId, }, diff --git a/test/fixtures/person-large.jpg b/test/fixtures/person-large.jpg new file mode 100644 index 00000000000000..3d0d0af42375c3 Binary files /dev/null and b/test/fixtures/person-large.jpg differ diff --git a/test/parallel/test-accessor-properties.js b/test/parallel/test-accessor-properties.js index 713be7eac203a6..064ef844c38e6c 100644 --- a/test/parallel/test-accessor-properties.js +++ b/test/parallel/test-accessor-properties.js @@ -1,6 +1,6 @@ 'use strict'; -const common = require('../common'); +require('../common'); // This tests that the accessor properties do not raise assertions // when called with incompatible receivers. @@ -50,19 +50,4 @@ const UDP = process.binding('udp_wrap').UDP; typeof Object.getOwnPropertyDescriptor(UDP.prototype, 'fd'), 'object' ); - - if (common.hasCrypto) { // eslint-disable-line node-core/crypto-check - // There are accessor properties in crypto too - const crypto = process.binding('crypto'); - - assert.throws(() => { - crypto.SecureContext.prototype._external; - }, TypeError); - - assert.strictEqual( - typeof Object.getOwnPropertyDescriptor( - crypto.SecureContext.prototype, '_external'), - 'object' - ); - } } diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index b871cd7265d288..d892122ed62ff2 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -599,14 +599,13 @@ assert.throws( }); // notDeepEqual tests - message = 'Identical input passed to notDeepStrictEqual:\n\n' + - ' [\n 1\n ]\n'; + message = 'Identical input passed to notDeepStrictEqual:\n\n[\n 1\n]\n'; assert.throws( () => assert.notDeepEqual([1], [1]), { message }); message = 'Identical input passed to notDeepStrictEqual:' + - `\n\n [${'\n 1,'.repeat(25)}\n ...\n`; + `\n\n[${'\n 1,'.repeat(25)}\n...\n`; const data = Array(31).fill(1); assert.throws( () => assert.notDeepEqual(data, data), @@ -901,7 +900,7 @@ assert.throws(() => { throw null; }, 'foo'); assert.throws( () => assert.strictEqual([], []), { - message: 'Input object identical but not reference equal:\n\n []\n' + message: 'Input objects identical but not reference equal:\n\n[]\n' } ); diff --git a/test/parallel/test-benchmark-zlib.js b/test/parallel/test-benchmark-zlib.js index 350d05552cda39..25b7d1a4d5f905 100644 --- a/test/parallel/test-benchmark-zlib.js +++ b/test/parallel/test-benchmark-zlib.js @@ -9,5 +9,10 @@ runBenchmark('zlib', 'method=deflate', 'n=1', 'options=true', - 'type=Deflate' - ]); + 'type=Deflate', + 'inputLen=1024', + 'duration=0.001' + ], + { + 'NODEJS_BENCHMARK_ZERO_ALLOWED': 1 + }); diff --git a/test/parallel/test-buffer-constructor-deprecation-error.js b/test/parallel/test-buffer-constructor-deprecation-error.js new file mode 100644 index 00000000000000..46535e103b33ee --- /dev/null +++ b/test/parallel/test-buffer-constructor-deprecation-error.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); + +const bufferWarning = 'Buffer() is deprecated due to security and usability ' + + 'issues. Please use the Buffer.alloc(), ' + + 'Buffer.allocUnsafe(), or Buffer.from() methods instead.'; + +common.expectWarning('DeprecationWarning', bufferWarning, 'DEP0005'); + +// This is used to make sure that a warning is only emitted once even though +// `new Buffer()` is called twice. +process.on('warning', common.mustCall()); + +Error.prepareStackTrace = (err, trace) => new Buffer(10); + +new Error().stack; diff --git a/test/parallel/test-buffer-read.js b/test/parallel/test-buffer-read.js index e6a4f872b83260..1fdfd0145fd702 100644 --- a/test/parallel/test-buffer-read.js +++ b/test/parallel/test-buffer-read.js @@ -51,23 +51,17 @@ read(buf, 'readUInt32LE', [1], 0xcfea48fd); read(buf, 'readUIntBE', [2, 2], 0x48ea); read(buf, 'readUIntLE', [2, 2], 0xea48); -// invalid byteLength parameter for readUIntBE() and readUIntLE() -common.expectsError(() => { buf.readUIntBE(2, 0); }, - { code: 'ERR_OUT_OF_RANGE' }); -common.expectsError(() => { buf.readUIntLE(2, 7); }, - { code: 'ERR_OUT_OF_RANGE' }); - -// attempt to overflow buffers, similar to previous bug in array buffers +// Attempt to overflow buffers, similar to previous bug in array buffers assert.throws(() => Buffer.allocUnsafe(8).readFloatBE(0xffffffff), RangeError); assert.throws(() => Buffer.allocUnsafe(8).readFloatLE(0xffffffff), RangeError); -// ensure negative values can't get past offset +// Ensure negative values can't get past offset assert.throws(() => Buffer.allocUnsafe(8).readFloatBE(-1), RangeError); assert.throws(() => Buffer.allocUnsafe(8).readFloatLE(-1), RangeError); -// offset checks +// Offset checks { const buf = Buffer.allocUnsafe(0); @@ -75,67 +69,26 @@ assert.throws(() => Buffer.allocUnsafe(8).readFloatLE(-1), RangeError); assert.throws(() => buf.readInt8(0), RangeError); } -{ - const buf = Buffer.from([0xFF]); - - assert.strictEqual(buf.readUInt8(0), 255); - assert.strictEqual(buf.readInt8(0), -1); -} - -[16, 32].forEach((bits) => { - const buf = Buffer.allocUnsafe(bits / 8 - 1); - - assert.throws(() => buf[`readUInt${bits}BE`](0), - RangeError, - `readUInt${bits}BE()`); - - assert.throws(() => buf[`readUInt${bits}LE`](0), - RangeError, - `readUInt${bits}LE()`); - - assert.throws(() => buf[`readInt${bits}BE`](0), - RangeError, - `readInt${bits}BE()`); - - assert.throws(() => buf[`readInt${bits}LE`](0), - RangeError, - `readInt${bits}LE()`); +[16, 32].forEach((bit) => { + const buf = Buffer.allocUnsafe(bit / 8 - 1); + [`Int${bit}B`, `Int${bit}L`, `UInt${bit}B`, `UInt${bit}L`].forEach((fn) => { + assert.throws( + () => buf[`read${fn}E`](0), + { + name: 'RangeError [ERR_BUFFER_OUT_OF_BOUNDS]', + message: 'Attempt to write outside buffer bounds' + } + ); + }); }); [16, 32].forEach((bits) => { const buf = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF]); + ['LE', 'BE'].forEach((endian) => { + assert.strictEqual(buf[`readUInt${bits}${endian}`](0), + (0xFFFFFFFF >>> (32 - bits))); - assert.strictEqual(buf[`readUInt${bits}BE`](0), - (0xFFFFFFFF >>> (32 - bits))); - - assert.strictEqual(buf[`readUInt${bits}LE`](0), - (0xFFFFFFFF >>> (32 - bits))); - - assert.strictEqual(buf[`readInt${bits}BE`](0), - (0xFFFFFFFF >> (32 - bits))); - - assert.strictEqual(buf[`readInt${bits}LE`](0), - (0xFFFFFFFF >> (32 - bits))); + assert.strictEqual(buf[`readInt${bits}${endian}`](0), + (0xFFFFFFFF >> (32 - bits))); + }); }); - -// Test for common read(U)IntLE/BE -{ - const buf = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); - - assert.strictEqual(buf.readUIntLE(0, 1), 0x01); - assert.strictEqual(buf.readUIntBE(0, 1), 0x01); - assert.strictEqual(buf.readUIntLE(0, 3), 0x030201); - assert.strictEqual(buf.readUIntBE(0, 3), 0x010203); - assert.strictEqual(buf.readUIntLE(0, 5), 0x0504030201); - assert.strictEqual(buf.readUIntBE(0, 5), 0x0102030405); - assert.strictEqual(buf.readUIntLE(0, 6), 0x060504030201); - assert.strictEqual(buf.readUIntBE(0, 6), 0x010203040506); - assert.strictEqual(buf.readIntLE(0, 1), 0x01); - assert.strictEqual(buf.readIntBE(0, 1), 0x01); - assert.strictEqual(buf.readIntLE(0, 3), 0x030201); - assert.strictEqual(buf.readIntBE(0, 3), 0x010203); - assert.strictEqual(buf.readIntLE(0, 5), 0x0504030201); - assert.strictEqual(buf.readIntBE(0, 5), 0x0102030405); - assert.strictEqual(buf.readIntLE(0, 6), 0x060504030201); - assert.strictEqual(buf.readIntBE(0, 6), 0x010203040506); -} diff --git a/test/parallel/test-buffer-readint.js b/test/parallel/test-buffer-readint.js index ce0e6681cec38b..d417f88b82fe73 100644 --- a/test/parallel/test-buffer-readint.js +++ b/test/parallel/test-buffer-readint.js @@ -46,18 +46,14 @@ const assert = require('assert'); // Test 8 bit signed integers { - const data = Buffer.alloc(4); + const data = Buffer.from([0x23, 0xab, 0x7c, 0xef]); - data[0] = 0x23; assert.strictEqual(data.readInt8(0), 0x23); data[0] = 0xff; assert.strictEqual(data.readInt8(0), -1); data[0] = 0x87; - data[1] = 0xab; - data[2] = 0x7c; - data[3] = 0xef; assert.strictEqual(data.readInt8(0), -121); assert.strictEqual(data.readInt8(1), -85); assert.strictEqual(data.readInt8(2), 124); @@ -66,10 +62,8 @@ const assert = require('assert'); // Test 16 bit integers { - const buffer = Buffer.alloc(6); + const buffer = Buffer.from([0x16, 0x79, 0x65, 0x6e, 0x69, 0x78]); - buffer[0] = 0x16; - buffer[1] = 0x79; assert.strictEqual(buffer.readInt16BE(0), 0x1679); assert.strictEqual(buffer.readInt16LE(0), 0x7916); @@ -80,10 +74,6 @@ const assert = require('assert'); buffer[0] = 0x77; buffer[1] = 0x65; - buffer[2] = 0x65; - buffer[3] = 0x6e; - buffer[4] = 0x69; - buffer[5] = 0x78; assert.strictEqual(buffer.readInt16BE(0), 0x7765); assert.strictEqual(buffer.readInt16BE(1), 0x6565); assert.strictEqual(buffer.readInt16BE(2), 0x656e); @@ -98,12 +88,8 @@ const assert = require('assert'); // Test 32 bit integers { - const buffer = Buffer.alloc(6); + const buffer = Buffer.from([0x43, 0x53, 0x16, 0x79, 0x36, 0x17]); - buffer[0] = 0x43; - buffer[1] = 0x53; - buffer[2] = 0x16; - buffer[3] = 0x79; assert.strictEqual(buffer.readInt32BE(0), 0x43531679); assert.strictEqual(buffer.readInt32LE(0), 0x79165343); @@ -118,8 +104,6 @@ const assert = require('assert'); buffer[1] = 0xc3; buffer[2] = 0x95; buffer[3] = 0xa9; - buffer[4] = 0x36; - buffer[5] = 0x17; assert.strictEqual(buffer.readInt32BE(0), 0x42c395a9); assert.strictEqual(buffer.readInt32BE(1), -1013601994); assert.strictEqual(buffer.readInt32BE(2), -1784072681); @@ -130,17 +114,24 @@ const assert = require('assert'); // Test Int { - const buffer = Buffer.alloc(8); + const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]); + + assert.strictEqual(buffer.readIntLE(0, 1), 0x01); + assert.strictEqual(buffer.readIntBE(0, 1), 0x01); + assert.strictEqual(buffer.readIntLE(0, 3), 0x030201); + assert.strictEqual(buffer.readIntBE(0, 3), 0x010203); + assert.strictEqual(buffer.readIntLE(0, 5), 0x0504030201); + assert.strictEqual(buffer.readIntBE(0, 5), 0x0102030405); + assert.strictEqual(buffer.readIntLE(0, 6), 0x060504030201); + assert.strictEqual(buffer.readIntBE(0, 6), 0x010203040506); + assert.strictEqual(buffer.readIntLE(1, 6), 0x070605040302); + assert.strictEqual(buffer.readIntBE(1, 6), 0x020304050607); + assert.strictEqual(buffer.readIntLE(2, 6), 0x080706050403); + assert.strictEqual(buffer.readIntBE(2, 6), 0x030405060708); // Check byteLength. ['readIntBE', 'readIntLE'].forEach((fn) => { - - // Verify that default offset & byteLength works fine. - buffer[fn](undefined, undefined); - buffer[fn](undefined); - buffer[fn](); - - ['', '0', null, {}, [], () => {}, true, false].forEach((len) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((len) => { assert.throws( () => buffer[fn](0, len), { code: 'ERR_INVALID_ARG_TYPE' }); @@ -171,7 +162,7 @@ const assert = require('assert'); // Test 1 to 6 bytes. for (let i = 1; i < 6; i++) { ['readIntBE', 'readIntLE'].forEach((fn) => { - ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => { assert.throws( () => buffer[fn](o, i), { diff --git a/test/parallel/test-buffer-readuint.js b/test/parallel/test-buffer-readuint.js new file mode 100644 index 00000000000000..d2c07e4de87f89 --- /dev/null +++ b/test/parallel/test-buffer-readuint.js @@ -0,0 +1,165 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Test OOB +{ + const buffer = Buffer.alloc(4); + + ['UInt8', 'UInt16BE', 'UInt16LE', 'UInt32BE', 'UInt32LE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[`read${fn}`](undefined); + buffer[`read${fn}`](); + + ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { + assert.throws( + () => buffer[`read${fn}`](o), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError [ERR_INVALID_ARG_TYPE]' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => buffer[`read${fn}`](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError [ERR_OUT_OF_RANGE]' + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[`read${fn}`](offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError [ERR_OUT_OF_RANGE]', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); +} + +// Test 8 bit unsigned integers +{ + const data = Buffer.from([0xff, 0x2a, 0x2a, 0x2a]); + assert.strictEqual(255, data.readUInt8(0)); + assert.strictEqual(42, data.readUInt8(1)); + assert.strictEqual(42, data.readUInt8(2)); + assert.strictEqual(42, data.readUInt8(3)); +} + +// Test 16 bit unsigned integers +{ + const data = Buffer.from([0x00, 0x2a, 0x42, 0x3f]); + assert.strictEqual(0x2a, data.readUInt16BE(0)); + assert.strictEqual(0x2a42, data.readUInt16BE(1)); + assert.strictEqual(0x423f, data.readUInt16BE(2)); + assert.strictEqual(0x2a00, data.readUInt16LE(0)); + assert.strictEqual(0x422a, data.readUInt16LE(1)); + assert.strictEqual(0x3f42, data.readUInt16LE(2)); + + data[0] = 0xfe; + data[1] = 0xfe; + assert.strictEqual(0xfefe, data.readUInt16BE(0)); + assert.strictEqual(0xfefe, data.readUInt16LE(0)); +} + +// Test 32 bit unsigned integers +{ + const data = Buffer.from([0x32, 0x65, 0x42, 0x56, 0x23, 0xff]); + assert.strictEqual(0x32654256, data.readUInt32BE(0)); + assert.strictEqual(0x65425623, data.readUInt32BE(1)); + assert.strictEqual(0x425623ff, data.readUInt32BE(2)); + assert.strictEqual(0x56426532, data.readUInt32LE(0)); + assert.strictEqual(0x23564265, data.readUInt32LE(1)); + assert.strictEqual(0xff235642, data.readUInt32LE(2)); +} + +// Test UInt +{ + const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]); + + assert.strictEqual(buffer.readUIntLE(0, 1), 0x01); + assert.strictEqual(buffer.readUIntBE(0, 1), 0x01); + assert.strictEqual(buffer.readUIntLE(0, 3), 0x030201); + assert.strictEqual(buffer.readUIntBE(0, 3), 0x010203); + assert.strictEqual(buffer.readUIntLE(0, 5), 0x0504030201); + assert.strictEqual(buffer.readUIntBE(0, 5), 0x0102030405); + assert.strictEqual(buffer.readUIntLE(0, 6), 0x060504030201); + assert.strictEqual(buffer.readUIntBE(0, 6), 0x010203040506); + assert.strictEqual(buffer.readUIntLE(1, 6), 0x070605040302); + assert.strictEqual(buffer.readUIntBE(1, 6), 0x020304050607); + assert.strictEqual(buffer.readUIntLE(2, 6), 0x080706050403); + assert.strictEqual(buffer.readUIntBE(2, 6), 0x030405060708); + + // Check byteLength. + ['readUIntBE', 'readUIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((len) => { + assert.throws( + () => buffer[fn](0, len), + { code: 'ERR_INVALID_ARG_TYPE' }); + }); + + [Infinity, -1].forEach((byteLength) => { + assert.throws( + () => buffer[fn](0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + message: 'The value of "byteLength" is out of range. ' + + `It must be >= 1 and <= 6. Received ${byteLength}` + }); + }); + + [NaN, 1.01].forEach((byteLength) => { + assert.throws( + () => buffer[fn](0, byteLength), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError [ERR_OUT_OF_RANGE]', + message: 'The value of "byteLength" is out of range. ' + + `It must be an integer. Received ${byteLength}` + }); + }); + }); + + // Test 1 to 6 bytes. + for (let i = 1; i < 6; i++) { + ['readUIntBE', 'readUIntLE'].forEach((fn) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => { + assert.throws( + () => buffer[fn](o, i), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError [ERR_INVALID_ARG_TYPE]' + }); + }); + + [Infinity, -1, -4294967295].forEach((offset) => { + assert.throws( + () => buffer[fn](offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError [ERR_OUT_OF_RANGE]', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= ${8 - i}. Received ${offset}` + }); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](offset, i), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError [ERR_OUT_OF_RANGE]', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); + } +} diff --git a/test/parallel/test-buffer-writeint.js b/test/parallel/test-buffer-writeint.js index 8f167044e388f1..fb91aa11b6f995 100644 --- a/test/parallel/test-buffer-writeint.js +++ b/test/parallel/test-buffer-writeint.js @@ -168,25 +168,19 @@ const errorOutOfBounds = common.expectsError({ // Check byteLength. ['writeIntBE', 'writeIntLE'].forEach((fn) => { - - // Verify that default offset & byteLength works fine. - data[fn](undefined, undefined); - data[fn](undefined); - data[fn](); - - ['', '0', null, {}, [], () => {}, true, false].forEach((bl) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((bl) => { assert.throws( () => data[fn](23, 0, bl), { code: 'ERR_INVALID_ARG_TYPE' }); }); - [Infinity, -1].forEach((offset) => { + [Infinity, -1].forEach((byteLength) => { assert.throws( - () => data[fn](23, 0, offset), + () => data[fn](23, 0, byteLength), { code: 'ERR_OUT_OF_RANGE', message: 'The value of "byteLength" is out of range. ' + - `It must be >= 1 and <= 6. Received ${offset}` + `It must be >= 1 and <= 6. Received ${byteLength}` } ); }); @@ -220,7 +214,7 @@ const errorOutOfBounds = common.expectsError({ }); }); - ['', '0', null, {}, [], () => {}, true, false].forEach((o) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((o) => { assert.throws( () => data[fn](min, o, i), { diff --git a/test/parallel/test-buffer-writeuint.js b/test/parallel/test-buffer-writeuint.js index 1f304993ff9cb0..8fe7783e71be04 100644 --- a/test/parallel/test-buffer-writeuint.js +++ b/test/parallel/test-buffer-writeuint.js @@ -117,25 +117,19 @@ const assert = require('assert'); // Check byteLength. ['writeUIntBE', 'writeUIntLE'].forEach((fn) => { - - // Verify that default offset & byteLength works fine. - data[fn](undefined, undefined); - data[fn](undefined); - data[fn](); - - ['', '0', null, {}, [], () => {}, true, false].forEach((bl) => { + ['', '0', null, {}, [], () => {}, true, false, undefined].forEach((bl) => { assert.throws( () => data[fn](23, 0, bl), { code: 'ERR_INVALID_ARG_TYPE' }); }); - [Infinity, -1].forEach((offset) => { + [Infinity, -1].forEach((byteLength) => { assert.throws( - () => data[fn](23, 0, offset), + () => data[fn](23, 0, byteLength), { code: 'ERR_OUT_OF_RANGE', message: 'The value of "byteLength" is out of range. ' + - `It must be >= 1 and <= 6. Received ${offset}` + `It must be >= 1 and <= 6. Received ${byteLength}` } ); }); diff --git a/test/parallel/test-child-process-exec-kill-throws.js b/test/parallel/test-child-process-exec-kill-throws.js index 35e5ff8b7bce0f..d6a0d4da19eae7 100644 --- a/test/parallel/test-child-process-exec-kill-throws.js +++ b/test/parallel/test-child-process-exec-kill-throws.js @@ -19,7 +19,8 @@ if (process.argv[2] === 'child') { }; const cmd = `"${process.execPath}" "${__filename}" child`; - const options = { maxBuffer: 0 }; + const options = { maxBuffer: 0, killSignal: 'SIGKILL' }; + const child = cp.exec(cmd, options, common.mustCall((err, stdout, stderr) => { // Verify that if ChildProcess#kill() throws, the error is reported. assert.strictEqual(err.message, 'mock error', err); diff --git a/test/parallel/test-child-process-http-socket-leak.js b/test/parallel/test-child-process-http-socket-leak.js new file mode 100644 index 00000000000000..30d8601b840626 --- /dev/null +++ b/test/parallel/test-child-process-http-socket-leak.js @@ -0,0 +1,56 @@ +// Flags: --expose_internals + +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { fork } = require('child_process'); +const http = require('http'); +const { kTimeout } = require('internal/timers'); + +if (process.argv[2] === 'child') { + process.once('message', (req, socket) => { + const res = new http.ServerResponse(req); + res.assignSocket(socket); + res.end(); + }); + + process.send('ready'); + return; +} + +let child; +let socket; + +const server = http.createServer(common.mustCall((req, res) => { + const r = { + method: req.method, + headers: req.headers, + path: req.path, + httpVersionMajor: req.httpVersionMajor, + query: req.query, + }; + + socket = res.socket; + child.send(r, socket); + server.close(); +})); + +server.listen(0, common.mustCall(() => { + child = fork(__filename, [ 'child' ]); + child.once('message', (msg) => { + assert.strictEqual(msg, 'ready'); + const req = http.request({ + port: server.address().port, + }, common.mustCall((res) => { + res.on('data', () => {}); + res.on('end', common.mustCall(() => { + assert.strictEqual(socket[kTimeout]._idleTimeout, -1); + assert.strictEqual(socket.parser, null); + assert.strictEqual(socket._httpMessage, null); + })); + })); + + req.end(); + }); +})); diff --git a/test/parallel/test-cluster-http-pipe.js b/test/parallel/test-cluster-http-pipe.js index 9e58fb297b28fe..9e039541cd26f1 100644 --- a/test/parallel/test-cluster-http-pipe.js +++ b/test/parallel/test-cluster-http-pipe.js @@ -45,7 +45,6 @@ if (cluster.isMaster) { http.createServer(common.mustCall((req, res) => { assert.strictEqual(req.connection.remoteAddress, undefined); assert.strictEqual(req.connection.localAddress, undefined); - // TODO common.PIPE? res.writeHead(200); res.end('OK'); diff --git a/test/parallel/test-console-table.js b/test/parallel/test-console-table.js index 39e9099dd717d4..64e2533f175e1d 100644 --- a/test/parallel/test-console-table.js +++ b/test/parallel/test-console-table.js @@ -41,13 +41,13 @@ test([1, 2, 3], ` `); test([Symbol(), 5, [10]], ` -┌─────────┬──────────┐ -│ (index) │ Values │ -├─────────┼──────────┤ -│ 0 │ Symbol() │ -│ 1 │ 5 │ -│ 2 │ [ 10 ] │ -└─────────┴──────────┘ +┌─────────┬────┬──────────┐ +│ (index) │ 0 │ Values │ +├─────────┼────┼──────────┤ +│ 0 │ │ Symbol() │ +│ 1 │ │ 5 │ +│ 2 │ 10 │ │ +└─────────┴────┴──────────┘ `); test([undefined, 5], ` @@ -182,10 +182,10 @@ test({ a: undefined }, ['x'], ` `); test([], ` -┌─────────┬────────┐ -│ (index) │ Values │ -├─────────┼────────┤ -└─────────┴────────┘ +┌─────────┐ +│ (index) │ +├─────────┤ +└─────────┘ `); test(new Map(), ` @@ -194,3 +194,12 @@ test(new Map(), ` ├───────────────────┼─────┼────────┤ └───────────────────┴─────┴────────┘ `); + +test([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ` +┌─────────┬─────┬─────┐ +│ (index) │ a │ b │ +├─────────┼─────┼─────┤ +│ 0 │ 1 │ 'Y' │ +│ 1 │ 'Z' │ 2 │ +└─────────┴─────┴─────┘ +`); diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js index 6c4153875264fd..e915db4e9d0991 100644 --- a/test/parallel/test-crypto-authenticated.js +++ b/test/parallel/test-crypto-authenticated.js @@ -727,6 +727,18 @@ for (const test of TEST_CASES) { message: `Invalid GCM authentication tag length: ${length}` }); + common.expectsError(() => { + crypto.createCipheriv('aes-256-gcm', + 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', + 'qkuZpJWCewa6Szih', + { + authTagLength: length + }); + }, { + type: Error, + message: `Invalid GCM authentication tag length: ${length}` + }); + common.expectsError(() => { crypto.createDecipheriv('aes-256-gcm', 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', @@ -741,6 +753,23 @@ for (const test of TEST_CASES) { } } +// Test that GCM can produce shorter authentication tags than 16 bytes. +{ + const fullTag = '1debb47b2c91ba2cea16fad021703070'; + for (const [authTagLength, e] of [[undefined, 16], [12, 12], [4, 4]]) { + const cipher = crypto.createCipheriv('aes-256-gcm', + 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', + 'qkuZpJWCewa6Szih', { + authTagLength + }); + cipher.setAAD(Buffer.from('abcd')); + cipher.update('01234567', 'hex'); + cipher.final(); + const tag = cipher.getAuthTag(); + assert.strictEqual(tag.toString('hex'), fullTag.substr(0, 2 * e)); + } +} + // Test that users can manually restrict the GCM tag length to a single value. { const decipher = crypto.createDecipheriv('aes-256-gcm', diff --git a/test/parallel/test-crypto-binary-default.js b/test/parallel/test-crypto-binary-default.js index 6cdc894753c3ac..228e2cc8abdc81 100644 --- a/test/parallel/test-crypto-binary-default.js +++ b/test/parallel/test-crypto-binary-default.js @@ -20,6 +20,8 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; +// Flags: --expose-internals + // This is the same as test/simple/test-crypto, but from before the shift // to use buffers by default. @@ -36,7 +38,7 @@ const tls = require('tls'); const fixtures = require('../common/fixtures'); const DH_NOT_SUITABLE_GENERATOR = crypto.constants.DH_NOT_SUITABLE_GENERATOR; -crypto.DEFAULT_ENCODING = 'latin1'; +require('internal/crypto/util').setDefaultEncoding('latin1'); // Test Certificates const certPem = fixtures.readSync('test_cert.pem', 'ascii'); diff --git a/test/parallel/test-crypto-certificate.js b/test/parallel/test-crypto-certificate.js index f94c82474e95aa..a426e0be003da0 100644 --- a/test/parallel/test-crypto-certificate.js +++ b/test/parallel/test-crypto-certificate.js @@ -29,16 +29,12 @@ const crypto = require('crypto'); const { Certificate } = crypto; const fixtures = require('../common/fixtures'); -crypto.DEFAULT_ENCODING = 'buffer'; - // Test Certificates const spkacValid = fixtures.readSync('spkac.valid'); const spkacFail = fixtures.readSync('spkac.fail'); const spkacPem = fixtures.readSync('spkac.pem'); -{ - // Test instance methods - const certificate = new Certificate(); +function checkMethods(certificate) { assert.strictEqual(certificate.verifySpkac(spkacValid), true); assert.strictEqual(certificate.verifySpkac(spkacFail), false); @@ -57,21 +53,13 @@ const spkacPem = fixtures.readSync('spkac.pem'); } { - // Test static methods - assert.strictEqual(Certificate.verifySpkac(spkacValid), true); - assert.strictEqual(Certificate.verifySpkac(spkacFail), false); - - assert.strictEqual( - stripLineEndings(Certificate.exportPublicKey(spkacValid).toString('utf8')), - stripLineEndings(spkacPem.toString('utf8')) - ); - assert.strictEqual(Certificate.exportPublicKey(spkacFail), ''); + // Test instance methods + checkMethods(new Certificate()); +} - assert.strictEqual( - Certificate.exportChallenge(spkacValid).toString('utf8'), - 'fb9ab814-6677-42a4-a60c-f905d1a6924d' - ); - assert.strictEqual(Certificate.exportChallenge(spkacFail), ''); +{ + // Test static methods + checkMethods(Certificate); } function stripLineEndings(obj) { diff --git a/test/parallel/test-crypto-ecb.js b/test/parallel/test-crypto-ecb.js index 4faf4af2c576fd..c88ebaabb05e7f 100644 --- a/test/parallel/test-crypto-ecb.js +++ b/test/parallel/test-crypto-ecb.js @@ -30,8 +30,6 @@ if (common.hasFipsCrypto) const assert = require('assert'); const crypto = require('crypto'); -crypto.DEFAULT_ENCODING = 'buffer'; - // Testing whether EVP_CipherInit_ex is functioning correctly. // Reference: bug#1997 diff --git a/test/parallel/test-crypto-padding-aes256.js b/test/parallel/test-crypto-padding-aes256.js index ea31c7e894ee53..d6c395f0a42d35 100644 --- a/test/parallel/test-crypto-padding-aes256.js +++ b/test/parallel/test-crypto-padding-aes256.js @@ -27,8 +27,6 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); -crypto.DEFAULT_ENCODING = 'buffer'; - const iv = Buffer.from('00000000000000000000000000000000', 'hex'); const key = Buffer.from('0123456789abcdef0123456789abcdef' + '0123456789abcdef0123456789abcdef', 'hex'); diff --git a/test/parallel/test-crypto-padding.js b/test/parallel/test-crypto-padding.js index 00b600efb735e8..d4a5b95cec9242 100644 --- a/test/parallel/test-crypto-padding.js +++ b/test/parallel/test-crypto-padding.js @@ -27,8 +27,6 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); -crypto.DEFAULT_ENCODING = 'buffer'; - // Input data. const ODD_LENGTH_PLAIN = 'Hello node world!'; const EVEN_LENGTH_PLAIN = 'Hello node world!AbC09876dDeFgHi'; diff --git a/test/parallel/test-crypto-pbkdf2.js b/test/parallel/test-crypto-pbkdf2.js index b4ecd3c6061158..f65176132ae5c4 100644 --- a/test/parallel/test-crypto-pbkdf2.js +++ b/test/parallel/test-crypto-pbkdf2.js @@ -123,7 +123,8 @@ assert.throws( }); [1, {}, [], true, undefined, null].forEach((input) => { - const msgPart2 = `Buffer, or TypedArray. Received type ${typeof input}`; + const msgPart2 = 'Buffer, TypedArray, or DataView.' + + ` Received type ${typeof input}`; assert.throws( () => crypto.pbkdf2(input, 'salt', 8, 8, 'sha256', common.mustNotCall()), { diff --git a/test/parallel/test-crypto-random.js b/test/parallel/test-crypto-random.js index 77801f6d53eef2..f00e474b6f2798 100644 --- a/test/parallel/test-crypto-random.js +++ b/test/parallel/test-crypto-random.js @@ -32,8 +32,6 @@ const { kMaxLength } = require('buffer'); const kMaxUint32 = Math.pow(2, 32) - 1; const kMaxPossibleLength = Math.min(kMaxLength, kMaxUint32); -crypto.DEFAULT_ENCODING = 'buffer'; - // bump, we register a lot of exit listeners process.setMaxListeners(256); diff --git a/test/parallel/test-crypto-verify-failure.js b/test/parallel/test-crypto-verify-failure.js index 72dfb926413d8b..7e9fda9f6791f6 100644 --- a/test/parallel/test-crypto-verify-failure.js +++ b/test/parallel/test-crypto-verify-failure.js @@ -29,8 +29,6 @@ const crypto = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); -crypto.DEFAULT_ENCODING = 'buffer'; - const certPem = fixtures.readSync('test_cert.pem', 'ascii'); const options = { diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js index 3047d37c962cff..9a93e8422fd0b4 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -30,8 +30,6 @@ const crypto = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); -crypto.DEFAULT_ENCODING = 'buffer'; - // Test Certificates const caPem = fixtures.readSync('test_ca.pem', 'ascii'); const certPem = fixtures.readSync('test_cert.pem', 'ascii'); diff --git a/test/parallel/test-fs-error-messages.js b/test/parallel/test-fs-error-messages.js index 61e51585a028c2..28141f33f62965 100644 --- a/test/parallel/test-fs-error-messages.js +++ b/test/parallel/test-fs-error-messages.js @@ -636,22 +636,21 @@ if (!common.isAIX) { ); } -// copyFile with invalid flags +// Check copyFile with invalid flags. { - const validateError = (err) => { - assert.strictEqual(err.message, - 'EINVAL: invalid argument, copyfile ' + - `'${existingFile}' -> '${nonexistentFile}'`); - assert.strictEqual(err.errno, uv.UV_EINVAL); - assert.strictEqual(err.code, 'EINVAL'); - assert.strictEqual(err.syscall, 'copyfile'); - return true; + const validateError = { + // TODO: Make sure the error message always also contains the src. + message: `EINVAL: invalid argument, copyfile -> '${nonexistentFile}'`, + errno: uv.UV_EINVAL, + code: 'EINVAL', + syscall: 'copyfile' }; - // TODO(joyeecheung): test fs.copyFile() when uv_fs_copyfile does not - // keep the loop open when the flags are invalid. - // See https://github.com/libuv/libuv/pull/1747 + fs.copyFile(existingFile, nonexistentFile, -1, + common.expectsError(validateError)); + validateError.message = 'EINVAL: invalid argument, copyfile ' + + `'${existingFile}' -> '${nonexistentFile}'`; assert.throws( () => fs.copyFileSync(existingFile, nonexistentFile, -1), validateError diff --git a/test/parallel/test-fs-promises-file-handle-write.js b/test/parallel/test-fs-promises-file-handle-write.js index 842095a214c2ef..b5c83a169021c2 100644 --- a/test/parallel/test-fs-promises-file-handle-write.js +++ b/test/parallel/test-fs-promises-file-handle-write.js @@ -35,6 +35,18 @@ async function validateEmptyWrite() { assert.deepStrictEqual(buffer, readFileData); } -validateWrite() - .then(validateEmptyWrite) - .then(common.mustCall()); +async function validateNonUint8ArrayWrite() { + const filePathForHandle = path.resolve(tmpDir, 'tmp-data-write.txt'); + const fileHandle = await open(filePathForHandle, 'w+'); + const buffer = Buffer.from('Hello world', 'utf8').toString('base64'); + + await fileHandle.write(buffer, 0, buffer.length); + const readFileData = fs.readFileSync(filePathForHandle); + assert.deepStrictEqual(Buffer.from(buffer, 'utf8'), readFileData); +} + +Promise.all([ + validateWrite(), + validateEmptyWrite(), + validateNonUint8ArrayWrite() +]).then(common.mustCall()); diff --git a/test/parallel/test-http-aborted.js b/test/parallel/test-http-aborted.js new file mode 100644 index 00000000000000..c3d7e4641f4501 --- /dev/null +++ b/test/parallel/test-http-aborted.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); + +const server = http.createServer(common.mustCall(function(req, res) { + req.on('aborted', common.mustCall(function() { + assert.strictEqual(this.aborted, true); + server.close(); + })); + assert.strictEqual(req.aborted, false); + res.write('hello'); +})); + +server.listen(0, common.mustCall(() => { + const req = http.get({ + port: server.address().port, + headers: { connection: 'keep-alive' } + }, common.mustCall((res) => { + res.on('aborted', common.mustCall(() => { + assert.strictEqual(res.aborted, true); + })); + req.abort(); + })); +})); diff --git a/test/parallel/test-http-correct-hostname.js b/test/parallel/test-http-correct-hostname.js new file mode 100644 index 00000000000000..b4279c1da4339f --- /dev/null +++ b/test/parallel/test-http-correct-hostname.js @@ -0,0 +1,28 @@ +/* eslint-disable node-core/crypto-check */ +// Flags: --expose-internals +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const { outHeadersKey } = require('internal/http'); + +const http = require('http'); +const modules = { http }; + +if (common.hasCrypto) { + const https = require('https'); + modules.https = https; +} + +Object.keys(modules).forEach((module) => { + const doNotCall = common.mustNotCall( + `${module}.request should not connect to ${module}://example.com%60x.example.com` + ); + const req = modules[module].request(`${module}://example.com%60x.example.com`, doNotCall); + assert.deepStrictEqual(req[outHeadersKey].host, [ + 'Host', + 'example.com`x.example.com', + ]); + req.abort(); +}); diff --git a/test/parallel/test-http-deprecated-urls.js b/test/parallel/test-http-deprecated-urls.js new file mode 100644 index 00000000000000..5bb9f66ebe7126 --- /dev/null +++ b/test/parallel/test-http-deprecated-urls.js @@ -0,0 +1,33 @@ +/* eslint-disable node-core/crypto-check */ + +'use strict'; + +const common = require('../common'); + +const http = require('http'); +const modules = { http }; + +const deprecations = [ + ['The provided URL http://[www.nodejs.org] is not a valid URL, and is supported ' + + 'in the http module solely for compatibility.', + 'DEP0109'], +]; + +if (common.hasCrypto) { + const https = require('https'); + modules.https = https; + deprecations.push( + ['The provided URL https://[www.nodejs.org] is not a valid URL, and is supported ' + + 'in the https module solely for compatibility.', + 'DEP0109'], + ); +} + +common.expectWarning('DeprecationWarning', deprecations); + +Object.keys(modules).forEach((module) => { + const doNotCall = common.mustNotCall( + `${module}.request should not connect to ${module}://[www.nodejs.org]` + ); + modules[module].request(`${module}://[www.nodejs.org]`, doNotCall).abort(); +}); diff --git a/test/parallel/test-http-invalid-urls.js b/test/parallel/test-http-invalid-urls.js index 0ddd72b374694c..7c9f67c4734810 100644 --- a/test/parallel/test-http-invalid-urls.js +++ b/test/parallel/test-http-invalid-urls.js @@ -21,7 +21,7 @@ function test(host) { const throws = () => { modules[module][fn](host, doNotCall); }; common.expectsError(throws, { type: TypeError, - code: 'ERR_INVALID_DOMAIN_NAME' + code: 'ERR_INVALID_URL' }); }); }); diff --git a/test/parallel/test-http-outgoing-internal-headers.js b/test/parallel/test-http-outgoing-internal-headers.js index e36917a970d987..de75a44e8a05d7 100644 --- a/test/parallel/test-http-outgoing-internal-headers.js +++ b/test/parallel/test-http-outgoing-internal-headers.js @@ -21,8 +21,10 @@ const { OutgoingMessage } = require('http'); Origin: 'localhost' }; - assert.deepStrictEqual(outgoingMessage[outHeadersKey], { - host: ['host', 'risingstack.com'], - origin: ['Origin', 'localhost'] - }); + assert.deepStrictEqual( + Object.entries(outgoingMessage[outHeadersKey]), + Object.entries({ + host: ['host', 'risingstack.com'], + origin: ['Origin', 'localhost'] + })); } diff --git a/test/parallel/test-http2-client-destroy.js b/test/parallel/test-http2-client-destroy.js index 09ca011c736c96..e641335e751287 100644 --- a/test/parallel/test-http2-client-destroy.js +++ b/test/parallel/test-http2-client-destroy.js @@ -124,3 +124,21 @@ const Countdown = require('../common/countdown'); req.on('error', () => {}); })); } + +// test destroy before connect +{ + const server = h2.createServer(); + server.on('stream', common.mustNotCall()); + + server.listen(0, common.mustCall(() => { + const client = h2.connect(`http://localhost:${server.address().port}`); + + server.on('connection', common.mustCall(() => { + server.close(); + client.close(); + })); + + const req = client.request(); + req.destroy(); + })); +} diff --git a/test/parallel/test-http2-client-rststream-before-connect.js b/test/parallel/test-http2-client-rststream-before-connect.js index 7bace941d1a0af..33e22130aad19b 100644 --- a/test/parallel/test-http2-client-rststream-before-connect.js +++ b/test/parallel/test-http2-client-rststream-before-connect.js @@ -63,8 +63,14 @@ server.listen(0, common.mustCall(() => { message: 'Stream closed with error code NGHTTP2_PROTOCOL_ERROR' })); - req.on('response', common.mustCall()); - req.resume(); + // The `response` event should not fire as the server should receive the + // RST_STREAM frame before it ever has a chance to reply. + req.on('response', common.mustNotCall()); + + // The `end` event should still fire as we close the readable stream by + // pushing a `null` chunk. req.on('end', common.mustCall()); + + req.resume(); req.end(); })); diff --git a/test/parallel/test-http2-client-upload-reject.js b/test/parallel/test-http2-client-upload-reject.js new file mode 100644 index 00000000000000..ece7cbdf233f1f --- /dev/null +++ b/test/parallel/test-http2-client-upload-reject.js @@ -0,0 +1,48 @@ +'use strict'; + +// Verifies that uploading data from a client works + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const http2 = require('http2'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); + +const loc = fixtures.path('person-large.jpg'); + +assert(fs.existsSync(loc)); + +fs.readFile(loc, common.mustCall((err, data) => { + assert.ifError(err); + + const server = http2.createServer(); + + server.on('stream', common.mustCall((stream) => { + stream.on('close', common.mustCall(() => { + assert.strictEqual(stream.rstCode, 0); + })); + + stream.respond({ ':status': 400 }); + stream.end(); + })); + + server.listen(0, common.mustCall(() => { + const client = http2.connect(`http://localhost:${server.address().port}`); + + const req = client.request({ ':method': 'POST' }); + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 400); + })); + + req.resume(); + req.on('end', common.mustCall(() => { + server.close(); + client.close(); + })); + + const str = fs.createReadStream(loc); + str.pipe(req); + })); +})); diff --git a/test/parallel/test-http2-client-upload.js b/test/parallel/test-http2-client-upload.js index 70a8ff3ced01c6..78c6d47cbb4f44 100644 --- a/test/parallel/test-http2-client-upload.js +++ b/test/parallel/test-http2-client-upload.js @@ -11,7 +11,7 @@ const fs = require('fs'); const fixtures = require('../common/fixtures'); const Countdown = require('../common/countdown'); -const loc = fixtures.path('person.jpg'); +const loc = fixtures.path('person-large.jpg'); let fileData; assert(fs.existsSync(loc)); diff --git a/test/parallel/test-http2-compat-aborted.js b/test/parallel/test-http2-compat-aborted.js new file mode 100644 index 00000000000000..01caf95f98688a --- /dev/null +++ b/test/parallel/test-http2-compat-aborted.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); +const assert = require('assert'); + + +const server = h2.createServer(common.mustCall(function(req, res) { + req.on('aborted', common.mustCall(function() { + assert.strictEqual(this.aborted, true); + })); + assert.strictEqual(req.aborted, false); + res.write('hello'); + server.close(); +})); + +server.listen(0, common.mustCall(function() { + const url = `http://localhost:${server.address().port}`; + const client = h2.connect(url, common.mustCall(() => { + const request = client.request(); + request.on('data', common.mustCall((chunk) => { + client.destroy(); + })); + })); +})); diff --git a/test/parallel/test-http2-create-client-session.js b/test/parallel/test-http2-create-client-session.js index 34e4e975d92d81..35de927ea7924e 100644 --- a/test/parallel/test-http2-create-client-session.js +++ b/test/parallel/test-http2-create-client-session.js @@ -55,9 +55,8 @@ server.on('listening', common.mustCall(() => { const req = client.request(); req.on('response', common.mustCall(function(headers) { - assert.strictEqual(headers[':status'], 200, 'status code is set'); - assert.strictEqual(headers['content-type'], 'text/html', - 'content type is set'); + assert.strictEqual(headers[':status'], 200); + assert.strictEqual(headers['content-type'], 'text/html'); assert(headers.date); })); diff --git a/test/parallel/test-http2-large-write-close.js b/test/parallel/test-http2-large-write-close.js new file mode 100644 index 00000000000000..f9dee357d6da7b --- /dev/null +++ b/test/parallel/test-http2-large-write-close.js @@ -0,0 +1,44 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +const content = Buffer.alloc(1e5, 0x44); + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}); +server.on('stream', common.mustCall((stream) => { + stream.respond({ + 'Content-Type': 'application/octet-stream', + 'Content-Length': (content.length.toString() * 2), + 'Vary': 'Accept-Encoding' + }); + + stream.write(content); + stream.write(content); + stream.end(); + stream.close(); +})); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, + { rejectUnauthorized: false }); + + const req = client.request({ ':path': '/' }); + req.end(); + + let receivedBufferLength = 0; + req.on('data', common.mustCallAtLeast((buf) => { + receivedBufferLength += buf.length; + }, 1)); + req.on('close', common.mustCall(() => { + assert.strictEqual(receivedBufferLength, content.length * 2); + client.close(); + server.close(); + })); +})); diff --git a/test/parallel/test-http2-perf_hooks.js b/test/parallel/test-http2-perf_hooks.js index 07d9c55ed7e0d2..5dd8ad0f6d883b 100644 --- a/test/parallel/test-http2-perf_hooks.js +++ b/test/parallel/test-http2-perf_hooks.js @@ -26,7 +26,7 @@ const obs = new PerformanceObserver(common.mustCall((items) => { switch (entry.type) { case 'server': assert.strictEqual(entry.streamCount, 1); - assert.strictEqual(entry.framesReceived, 5); + assert(entry.framesReceived >= 3); break; case 'client': assert.strictEqual(entry.streamCount, 1); diff --git a/test/parallel/test-https-options-boolean-check.js b/test/parallel/test-https-options-boolean-check.js index cefa9c1d0a3769..bed5fb03253f7f 100644 --- a/test/parallel/test-https-options-boolean-check.js +++ b/test/parallel/test-https-options-boolean-check.js @@ -88,7 +88,7 @@ const caArrDataView = toDataView(caCert); }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError [ERR_INVALID_ARG_TYPE]', - message: 'The "key" argument must be one of type string, Buffer, ' + + message: 'The "options.key" property must be one of type string, Buffer, ' + `TypedArray, or DataView. Received type ${type}` }); }); @@ -113,8 +113,8 @@ const caArrDataView = toDataView(caCert); }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError [ERR_INVALID_ARG_TYPE]', - message: 'The "cert" argument must be one of type string, Buffer, ' + - `TypedArray, or DataView. Received type ${type}` + message: 'The "options.cert" property must be one of type string, Buffer,' + + ` TypedArray, or DataView. Received type ${type}` }); }); @@ -147,7 +147,7 @@ const caArrDataView = toDataView(caCert); }, { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError [ERR_INVALID_ARG_TYPE]', - message: 'The "ca" argument must be one of type string, Buffer, ' + + message: 'The "options.ca" property must be one of type string, Buffer, ' + `TypedArray, or DataView. Received type ${type}` }); }); diff --git a/test/parallel/test-inspector-esm.js b/test/parallel/test-inspector-esm.js index 3171da58cf7a4c..62f0feefade88d 100644 --- a/test/parallel/test-inspector-esm.js +++ b/test/parallel/test-inspector-esm.js @@ -9,12 +9,6 @@ const { resolve: UrlResolve } = require('url'); const fixtures = require('../common/fixtures'); const { NodeInstance } = require('../common/inspector-helper.js'); -function assertNoUrlsWhileConnected(response) { - assert.strictEqual(response.length, 1); - assert.ok(!response[0].hasOwnProperty('devtoolsFrontendUrl')); - assert.ok(!response[0].hasOwnProperty('webSocketDebuggerUrl')); -} - function assertScopeValues({ result }, expected) { const unmatched = new Set(Object.keys(expected)); for (const actual of result) { @@ -110,7 +104,6 @@ async function runTest() { '', fixtures.path('es-modules/loop.mjs')); const session = await child.connectInspectorSession(); - assertNoUrlsWhileConnected(await child.httpGet(null, '/json/list')); await testBreakpointOnStart(session); await testBreakpoint(session); await session.runToCompletion(); diff --git a/test/parallel/test-inspector-multisession-js.js b/test/parallel/test-inspector-multisession-js.js new file mode 100644 index 00000000000000..58533f4cd6241e --- /dev/null +++ b/test/parallel/test-inspector-multisession-js.js @@ -0,0 +1,59 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const { Session } = require('inspector'); +const path = require('path'); + +function debugged() { + return 42; +} + +async function test() { + const session1 = new Session(); + const session2 = new Session(); + + session1.connect(); + session2.connect(); + + let session1Paused = false; + let session2Paused = false; + + session1.on('Debugger.paused', () => session1Paused = true); + session2.on('Debugger.paused', () => session2Paused = true); + + console.log('Connected'); + + session1.post('Debugger.enable'); + session2.post('Debugger.enable'); + console.log('Debugger was enabled'); + + await new Promise((resolve, reject) => { + session1.post('Debugger.setBreakpointByUrl', { + 'lineNumber': 9, + 'url': path.resolve(__dirname, __filename), + 'columnNumber': 0, + 'condition': '' + }, (error, result) => { + return error ? reject(error) : resolve(result); + }); + }); + console.log('Breakpoint was set'); + + debugged(); + + // Both sessions will receive the paused event + assert(session1Paused); + assert(session2Paused); + console.log('Breakpoint was hit'); + + session1.disconnect(); + session2.disconnect(); + console.log('Sessions were disconnected'); +} + +common.crashOnUnhandledRejection(); + +test(); diff --git a/test/parallel/test-inspector-multisession-ws.js b/test/parallel/test-inspector-multisession-ws.js new file mode 100644 index 00000000000000..d17adab2f486cc --- /dev/null +++ b/test/parallel/test-inspector-multisession-ws.js @@ -0,0 +1,75 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const { NodeInstance } = require('../common/inspector-helper.js'); + +// Sets up JS bindings session and runs till the "paused" event +const script = ` +const { Session } = require('inspector'); +const session = new Session(); +let done = false; +const interval = setInterval(() => { + if (done) + clearInterval(interval); +}, 150); +session.on('Debugger.paused', () => { + done = true; +}); +session.connect(); +session.post('Debugger.enable'); +console.log('Ready'); +`; + +async function setupSession(node) { + const session = await node.connectInspectorSession(); + await session.send([ + { 'method': 'Runtime.enable' }, + { 'method': 'Debugger.enable' }, + { 'method': 'Debugger.setPauseOnExceptions', + 'params': { 'state': 'none' } }, + { 'method': 'Debugger.setAsyncCallStackDepth', + 'params': { 'maxDepth': 0 } }, + { 'method': 'Profiler.enable' }, + { 'method': 'Profiler.setSamplingInterval', + 'params': { 'interval': 100 } }, + { 'method': 'Debugger.setBlackboxPatterns', + 'params': { 'patterns': [] } }, + { 'method': 'Runtime.runIfWaitingForDebugger' } + ]); + return session; +} + +async function testSuspend(sessionA, sessionB) { + console.log('[test]', 'Breaking in code and verifying events are fired'); + await sessionA.waitForNotification('Debugger.paused', 'Initial pause'); + sessionA.send({ 'method': 'Debugger.resume' }); + + await sessionA.waitForNotification('Runtime.consoleAPICalled', + 'Console output'); + sessionA.send({ 'method': 'Debugger.pause' }); + return Promise.all([ + sessionA.waitForNotification('Debugger.paused', 'SessionA paused'), + sessionB.waitForNotification('Debugger.paused', 'SessionB paused') + ]); +} + +async function runTest() { + const child = new NodeInstance(undefined, script); + + const [session1, session2] = + await Promise.all([setupSession(child), setupSession(child)]); + await testSuspend(session2, session1); + console.log('[test]', 'Should shut down after both sessions disconnect'); + + await session1.runToCompletion(); + await session2.send({ 'method': 'Debugger.disable' }); + await session2.disconnect(); + return child.expectShutdown(); +} + +common.crashOnUnhandledRejection(); + +runTest(); diff --git a/test/parallel/test-inspector-no-crash-ws-after-bindings.js b/test/parallel/test-inspector-no-crash-ws-after-bindings.js deleted file mode 100644 index 15fa96952ed125..00000000000000 --- a/test/parallel/test-inspector-no-crash-ws-after-bindings.js +++ /dev/null @@ -1,31 +0,0 @@ -// Flags: --expose-internals -'use strict'; -const common = require('../common'); -common.skipIfInspectorDisabled(); -common.crashOnUnhandledRejection(); -const { NodeInstance } = require('../common/inspector-helper.js'); -const assert = require('assert'); - -const expected = 'Can connect now!'; - -const script = ` - 'use strict'; - const { Session } = require('inspector'); - - const s = new Session(); - s.connect(); - console.error('${expected}'); - process.stdin.on('data', () => process.exit(0)); -`; - -async function runTests() { - const instance = new NodeInstance(['--inspect=0', '--expose-internals'], - script); - while (await instance.nextStderrString() !== expected); - assert.strictEqual(400, await instance.expectConnectionDeclined()); - instance.write('Stop!\n'); - assert.deepStrictEqual({ exitCode: 0, signal: null }, - await instance.expectShutdown()); -} - -runTests(); diff --git a/test/parallel/test-inspector-stress-http.js b/test/parallel/test-inspector-stress-http.js new file mode 100644 index 00000000000000..4787c35e32c899 --- /dev/null +++ b/test/parallel/test-inspector-stress-http.js @@ -0,0 +1,34 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const { NodeInstance } = require('../common/inspector-helper.js'); + +async function testHttp(child, number) { + try { + await child.httpGet(null, '/json/list'); + return true; + } catch (e) { + console.error(`Attempt ${number} failed`, e); + return false; + } +} + +async function runTest() { + const child = new NodeInstance(undefined, ''); + + const promises = []; + for (let i = 0; i < 100; i++) { + promises.push(testHttp(child, i)); + } + const result = await Promise.all(promises); + assert(!result.some((a) => !a), 'Some attempts failed'); + return child.kill(); +} + +common.crashOnUnhandledRejection(); + +runTest(); diff --git a/test/parallel/test-internal-errors.js b/test/parallel/test-internal-errors.js index cd2028c0f29b95..f3ca3c99ac87af 100644 --- a/test/parallel/test-internal-errors.js +++ b/test/parallel/test-internal-errors.js @@ -148,11 +148,6 @@ assert.strictEqual( 'Cannot render headers after they are sent to the client' ); -assert.strictEqual( - errors.message('ERR_INVALID_DOMAIN_NAME'), - 'Unable to determine the domain name' -); - assert.strictEqual( errors.message('ERR_INVALID_HTTP_TOKEN', ['Method', 'foo']), 'Method must be a valid HTTP token ["foo"]' diff --git a/test/parallel/test-process-chdir.js b/test/parallel/test-process-chdir.js index c0a245ffd3483b..998147dd43dfc8 100644 --- a/test/parallel/test-process-chdir.js +++ b/test/parallel/test-process-chdir.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); @@ -33,10 +33,9 @@ process.chdir('..'); assert.strictEqual(process.cwd().normalize(), path.resolve(tmpdir.path).normalize()); -const errMessage = /^TypeError: Bad argument\.$/; -assert.throws(function() { process.chdir({}); }, - errMessage, 'Bad argument.'); -assert.throws(function() { process.chdir(); }, - errMessage, 'Bad argument.'); -assert.throws(function() { process.chdir('x', 'y'); }, - errMessage, 'Bad argument.'); +const err = { + code: 'ERR_INVALID_ARG_TYPE', + message: /The "directory" argument must be of type string/ +}; +common.expectsError(function() { process.chdir({}); }, err); +common.expectsError(function() { process.chdir(); }, err); diff --git a/test/parallel/test-process-euid-egid.js b/test/parallel/test-process-euid-egid.js index adae58abebdfb7..84fbd03e327b05 100644 --- a/test/parallel/test-process-euid-egid.js +++ b/test/parallel/test-process-euid-egid.js @@ -13,11 +13,18 @@ if (common.isWindows) { assert.throws(() => { process.seteuid({}); -}, /^TypeError: seteuid argument must be a number or string$/); +}, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "id" argument must be one of type number or string. ' + + 'Received type object' +}); assert.throws(() => { - process.seteuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf'); -}, /^Error: seteuid user id does not exist$/); + process.seteuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveyb'); +}, { + code: 'ERR_UNKNOWN_CREDENTIAL', + message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb' +}); // If we're not running as super user... if (process.getuid() !== 0) { @@ -27,11 +34,11 @@ if (process.getuid() !== 0) { assert.throws(() => { process.setegid('nobody'); - }, /^Error: (?:EPERM, .+|setegid group id does not exist)$/); + }, /(?:EPERM, .+|Group identifier does not exist: nobody)$/); assert.throws(() => { process.seteuid('nobody'); - }, /^Error: (?:EPERM, .+|seteuid user id does not exist)$/); + }, /^Error: (?:EPERM, .+|User identifier does not exist: nobody)$/); return; } @@ -41,7 +48,7 @@ const oldgid = process.getegid(); try { process.setegid('nobody'); } catch (err) { - if (err.message !== 'setegid group id does not exist') { + if (err.message !== 'Group identifier does not exist: nobody') { throw err; } else { process.setegid('nogroup'); diff --git a/test/parallel/test-process-uid-gid.js b/test/parallel/test-process-uid-gid.js index d044c45a32783d..24751943092761 100644 --- a/test/parallel/test-process-uid-gid.js +++ b/test/parallel/test-process-uid-gid.js @@ -35,11 +35,18 @@ if (common.isWindows) { assert.throws(() => { process.setuid({}); -}, /^TypeError: setuid argument must be a number or a string$/); +}, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "id" argument must be one of type ' + + 'number or string. Received type object' +}); assert.throws(() => { - process.setuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveybvf'); -}, /^Error: setuid user id does not exist$/); + process.setuid('fhqwhgadshgnsdhjsdbkhsdabkfabkveyb'); +}, { + code: 'ERR_UNKNOWN_CREDENTIAL', + message: 'User identifier does not exist: fhqwhgadshgnsdhjsdbkhsdabkfabkveyb' +}); // If we're not running as super user... if (process.getuid() !== 0) { @@ -49,12 +56,12 @@ if (process.getuid() !== 0) { assert.throws( () => { process.setgid('nobody'); }, - /^Error: (?:EPERM, .+|setgid group id does not exist)$/ + /(?:EPERM, .+|Group identifier does not exist: nobody)$/ ); assert.throws( () => { process.setuid('nobody'); }, - /^Error: (?:EPERM, .+|setuid user id does not exist)$/ + /(?:EPERM, .+|User identifier does not exist: nobody)$/ ); return; } diff --git a/test/parallel/test-umask.js b/test/parallel/test-process-umask.js similarity index 82% rename from test/parallel/test-umask.js rename to test/parallel/test-process-umask.js index 1ac32cb7018906..869619f191a78b 100644 --- a/test/parallel/test-umask.js +++ b/test/parallel/test-process-umask.js @@ -43,8 +43,17 @@ assert.strictEqual(old, process.umask()); assert.throws(() => { process.umask({}); -}, /argument must be an integer or octal string/); +}, { + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "mask" argument must be one of type number, string, or ' + + 'undefined. Received type object' +}); -assert.throws(() => { - process.umask('123x'); -}, /invalid octal string/); +['123x', 'abc', '999'].forEach((value) => { + assert.throws(() => { + process.umask(value); + }, { + code: 'ERR_INVALID_ARG_VALUE', + message: `The argument 'mask' must be an octal string. Received '${value}'` + }); +}); diff --git a/test/parallel/test-readuint.js b/test/parallel/test-readuint.js deleted file mode 100644 index d5a1ba8fe2f524..00000000000000 --- a/test/parallel/test-readuint.js +++ /dev/null @@ -1,110 +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. - -'use strict'; -/* - * A battery of tests to help us read a series of uints - */ - -require('../common'); -const assert = require('assert'); - -/* - * We need to check the following things: - * - We are correctly resolving big endian (doesn't mean anything for 8 bit) - * - Correctly resolving little endian (doesn't mean anything for 8 bit) - * - Correctly using the offsets - * - Correctly interpreting values that are beyond the signed range as unsigned - */ -function test8(clazz) { - const data = new clazz(4); - - data[0] = 23; - data[1] = 23; - data[2] = 23; - data[3] = 23; - assert.strictEqual(23, data.readUInt8(0)); - assert.strictEqual(23, data.readUInt8(1)); - assert.strictEqual(23, data.readUInt8(2)); - assert.strictEqual(23, data.readUInt8(3)); - - data[0] = 255; /* If it became a signed int, would be -1 */ - assert.strictEqual(255, data.readUInt8(0)); -} - - -/* - * Test 16 bit unsigned integers. We need to verify the same set as 8 bit, only - * now some of the issues actually matter: - * - We are correctly resolving big endian - * - Correctly resolving little endian - * - Correctly using the offsets - * - Correctly interpreting values that are beyond the signed range as unsigned - */ -function test16(clazz) { - const data = new clazz(4); - - data[0] = 0; - data[1] = 0x23; - data[2] = 0x42; - data[3] = 0x3f; - assert.strictEqual(0x23, data.readUInt16BE(0)); - assert.strictEqual(0x2342, data.readUInt16BE(1)); - assert.strictEqual(0x423f, data.readUInt16BE(2)); - assert.strictEqual(0x2300, data.readUInt16LE(0)); - assert.strictEqual(0x4223, data.readUInt16LE(1)); - assert.strictEqual(0x3f42, data.readUInt16LE(2)); - - data[0] = 0xfe; - data[1] = 0xfe; - assert.strictEqual(0xfefe, data.readUInt16BE(0)); - assert.strictEqual(0xfefe, data.readUInt16LE(0)); -} - - -/* - * Test 32 bit unsigned integers. We need to verify the same set as 8 bit, only - * now some of the issues actually matter: - * - We are correctly resolving big endian - * - Correctly using the offsets - * - Correctly interpreting values that are beyond the signed range as unsigned - */ -function test32(clazz) { - const data = new clazz(8); - - data[0] = 0x32; - data[1] = 0x65; - data[2] = 0x42; - data[3] = 0x56; - data[4] = 0x23; - data[5] = 0xff; - assert.strictEqual(0x32654256, data.readUInt32BE(0)); - assert.strictEqual(0x65425623, data.readUInt32BE(1)); - assert.strictEqual(0x425623ff, data.readUInt32BE(2)); - assert.strictEqual(0x56426532, data.readUInt32LE(0)); - assert.strictEqual(0x23564265, data.readUInt32LE(1)); - assert.strictEqual(0xff235642, data.readUInt32LE(2)); -} - - -test8(Buffer); -test16(Buffer); -test32(Buffer); diff --git a/test/parallel/test-repl-sigint-nested-eval.js b/test/parallel/test-repl-sigint-nested-eval.js index 7f15b7dfeb8b9d..ea07393527b828 100644 --- a/test/parallel/test-repl-sigint-nested-eval.js +++ b/test/parallel/test-repl-sigint-nested-eval.js @@ -34,10 +34,10 @@ child.stdout.once('data', common.mustCall(() => { })); child.on('close', function(code) { - assert.strictEqual(code, 0); + const expected = 'Script execution was interrupted by `SIGINT`'; assert.ok( - stdout.includes('Script execution interrupted.'), - `Expected stdout to contain "Script execution interrupted.", got ${stdout}` + stdout.includes(expected), + `Expected stdout to contain "${expected}", got ${stdout}` ); assert.ok( stdout.includes('foobar'), diff --git a/test/parallel/test-repl-sigint.js b/test/parallel/test-repl-sigint.js index 818111c39bf578..14cafd0463709f 100644 --- a/test/parallel/test-repl-sigint.js +++ b/test/parallel/test-repl-sigint.js @@ -35,9 +35,10 @@ child.stdout.once('data', common.mustCall(() => { child.on('close', function(code) { assert.strictEqual(code, 0); + const expected = 'Script execution was interrupted by `SIGINT`'; assert.ok( - stdout.includes('Script execution interrupted.\n'), - `Expected stdout to contain "Script execution interrupted.", got ${stdout}` + stdout.includes(expected), + `Expected stdout to contain "${expected}", got ${stdout}` ); assert.ok( stdout.includes('42042\n'), diff --git a/test/parallel/test-repl-top-level-await.js b/test/parallel/test-repl-top-level-await.js index 91f5758c210a36..5a07f5ced14450 100644 --- a/test/parallel/test-repl-top-level-await.js +++ b/test/parallel/test-repl-top-level-await.js @@ -161,7 +161,7 @@ async function ctrlCTest() { ]), [ 'await timeout(100000)\r', 'Thrown: Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' + - 'Script execution was interrupted by `SIGINT`.', + 'Script execution was interrupted by `SIGINT`', PROMPT ]); } diff --git a/test/parallel/test-stream-readable-async-iterators.js b/test/parallel/test-stream-readable-async-iterators.js index b1801a1db3e580..39761b413260f1 100644 --- a/test/parallel/test-stream-readable-async-iterators.js +++ b/test/parallel/test-stream-readable-async-iterators.js @@ -115,6 +115,18 @@ async function tests() { readable.destroy(new Error('kaboom')); })(); + await (async function() { + console.log('call next() after error'); + const readable = new Readable({ + read() {} + }); + const iterator = readable[Symbol.asyncIterator](); + + const err = new Error('kaboom'); + readable.destroy(new Error('kaboom')); + await assert.rejects(iterator.next.bind(iterator), err); + })(); + await (async function() { console.log('read object mode'); const max = 42; diff --git a/test/parallel/test-timers-interval-throw.js b/test/parallel/test-timers-interval-throw.js new file mode 100644 index 00000000000000..876f0de55aa27e --- /dev/null +++ b/test/parallel/test-timers-interval-throw.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +// To match browser behaviour, interval should continue +// being rescheduled even if it throws. + +let count = 2; +const interval = setInterval(() => { throw new Error('IntervalError'); }, 1); + +process.on('uncaughtException', common.mustCall((err) => { + assert.strictEqual(err.message, 'IntervalError'); + if (--count === 0) { + clearInterval(interval); + } +}, 2)); diff --git a/test/parallel/test-tls-check-server-identity.js b/test/parallel/test-tls-check-server-identity.js index 1623e70a2af2ec..fe27770c1ebc84 100644 --- a/test/parallel/test-tls-check-server-identity.js +++ b/test/parallel/test-tls-check-server-identity.js @@ -30,6 +30,13 @@ const util = require('util'); const tls = require('tls'); +common.expectWarning('DeprecationWarning', [ + ['The URI http://[a.b.a.com]/ found in cert.subjectaltname ' + + 'is not a valid URI, and is supported in the tls module ' + + 'solely for compatibility.', + 'DEP0109'], +]); + const tests = [ // False-y values. { @@ -218,6 +225,13 @@ const tests = [ error: 'Host: a.b.a.com. is not in the cert\'s altnames: ' + 'URI:http://*.b.a.com/' }, + // Invalid URI + { + host: 'a.b.a.com', cert: { + subjectaltname: 'URI:http://[a.b.a.com]/', + subject: {} + } + }, // IP addresses { host: 'a.b.a.com', cert: { diff --git a/test/parallel/test-tls-client-getephemeralkeyinfo.js b/test/parallel/test-tls-client-getephemeralkeyinfo.js index be6777b1ae0049..9432a277ac0fd0 100644 --- a/test/parallel/test-tls-client-getephemeralkeyinfo.js +++ b/test/parallel/test-tls-client-getephemeralkeyinfo.js @@ -82,7 +82,12 @@ function testECDHE256() { } function testECDHE512() { - test(521, 'ECDH', 'secp521r1', null); + test(521, 'ECDH', 'secp521r1', testX25519); + ntests++; +} + +function testX25519() { + test(253, 'ECDH', 'X25519', null); ntests++; } @@ -90,5 +95,5 @@ testNOT_PFS(); process.on('exit', function() { assert.strictEqual(ntests, nsuccess); - assert.strictEqual(ntests, 5); + assert.strictEqual(ntests, 6); }); diff --git a/test/parallel/test-tls-external-accessor.js b/test/parallel/test-tls-external-accessor.js deleted file mode 100644 index 33d371923a600c..00000000000000 --- a/test/parallel/test-tls-external-accessor.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -const common = require('../common'); -if (!common.hasCrypto) - common.skip('missing crypto'); - -const assert = require('assert'); -const tls = require('tls'); - -// Ensure accessing ._external doesn't hit an assert in the accessor method. -{ - const pctx = tls.createSecureContext().context; - const cctx = Object.create(pctx); - assert.throws(() => cctx._external, TypeError); - pctx._external; -} -{ - const pctx = tls.createSecurePair().credentials.context; - const cctx = Object.create(pctx); - assert.throws(() => cctx._external, TypeError); - pctx._external; -} diff --git a/test/parallel/test-tls-options-boolean-check.js b/test/parallel/test-tls-options-boolean-check.js index de21da63ff4983..242df1140e41c2 100644 --- a/test/parallel/test-tls-options-boolean-check.js +++ b/test/parallel/test-tls-options-boolean-check.js @@ -86,7 +86,7 @@ const caArrDataView = toDataView(caCert); }, { code: 'ERR_INVALID_ARG_TYPE', type: TypeError, - message: 'The "key" argument must be one of type string, Buffer, ' + + message: 'The "options.key" property must be one of type string, Buffer, ' + `TypedArray, or DataView. Received type ${type}` }); }); @@ -111,8 +111,8 @@ const caArrDataView = toDataView(caCert); }, { code: 'ERR_INVALID_ARG_TYPE', type: TypeError, - message: 'The "cert" argument must be one of type string, Buffer, ' + - `TypedArray, or DataView. Received type ${type}` + message: 'The "options.cert" property must be one of type string, Buffer,' + + ` TypedArray, or DataView. Received type ${type}` }); }); @@ -145,7 +145,7 @@ const caArrDataView = toDataView(caCert); }, { code: 'ERR_INVALID_ARG_TYPE', type: TypeError, - message: 'The "ca" argument must be one of type string, Buffer, ' + + message: 'The "options.ca" property must be one of type string, Buffer, ' + `TypedArray, or DataView. Received type ${type}` }); }); diff --git a/test/parallel/test-util-isDeepStrictEqual.js b/test/parallel/test-util-isDeepStrictEqual.js index 356a9a71324971..938781a43084a5 100644 --- a/test/parallel/test-util-isDeepStrictEqual.js +++ b/test/parallel/test-util-isDeepStrictEqual.js @@ -419,8 +419,6 @@ notUtilIsDeepStrict([1, , , 3], [1, , , 3, , , ]); const err3 = new TypeError('foo1'); notUtilIsDeepStrict(err1, err2, assert.AssertionError); notUtilIsDeepStrict(err1, err3, assert.AssertionError); - // TODO: evaluate if this should throw or not. The same applies for RegExp - // Date and any object that has the same keys but not the same prototype. notUtilIsDeepStrict(err1, {}, assert.AssertionError); } diff --git a/test/parallel/test-vm-options-validation.js b/test/parallel/test-vm-options-validation.js index e00a14e4b9e603..67a27d58fff805 100644 --- a/test/parallel/test-vm-options-validation.js +++ b/test/parallel/test-vm-options-validation.js @@ -17,7 +17,7 @@ common.expectsError(() => { new vm.Script('void 0', 42); }, invalidArgType); -[null, {}, [1], 'bad', true, 0.1].forEach((value) => { +[null, {}, [1], 'bad', true].forEach((value) => { common.expectsError(() => { new vm.Script('void 0', { lineOffset: value }); }, invalidArgType); @@ -27,6 +27,16 @@ common.expectsError(() => { }, invalidArgType); }); +[0.1, 2 ** 32].forEach((value) => { + common.expectsError(() => { + new vm.Script('void 0', { lineOffset: value }); + }, outOfRange); + + common.expectsError(() => { + new vm.Script('void 0', { columnOffset: value }); + }, outOfRange); +}); + common.expectsError(() => { new vm.Script('void 0', { lineOffset: Number.MAX_SAFE_INTEGER }); }, outOfRange); @@ -53,26 +63,31 @@ common.expectsError(() => { const script = new vm.Script('void 0'); const sandbox = vm.createContext(); - function assertErrors(options) { + function assertErrors(options, errCheck) { common.expectsError(() => { script.runInThisContext(options); - }, invalidArgType); + }, errCheck); common.expectsError(() => { script.runInContext(sandbox, options); - }, invalidArgType); + }, errCheck); common.expectsError(() => { script.runInNewContext({}, options); - }, invalidArgType); + }, errCheck); } - [null, 'bad', 42].forEach(assertErrors); - [{}, [1], 'bad', null, -1, 0, NaN].forEach((value) => { - assertErrors({ timeout: value }); + [null, 'bad', 42].forEach((value) => { + assertErrors(value, invalidArgType); + }); + [{}, [1], 'bad', null].forEach((value) => { + assertErrors({ timeout: value }, invalidArgType); + }); + [-1, 0, NaN].forEach((value) => { + assertErrors({ timeout: value }, outOfRange); }); [{}, [1], 'bad', 1, null].forEach((value) => { - assertErrors({ displayErrors: value }); - assertErrors({ breakOnSigint: value }); + assertErrors({ displayErrors: value }, invalidArgType); + assertErrors({ breakOnSigint: value }, invalidArgType); }); } diff --git a/test/parallel/test-vm-sigint-existing-handler.js b/test/parallel/test-vm-sigint-existing-handler.js index 79a4d556ac05d6..13fccd9637c7fb 100644 --- a/test/parallel/test-vm-sigint-existing-handler.js +++ b/test/parallel/test-vm-sigint-existing-handler.js @@ -36,8 +36,12 @@ if (process.argv[2] === 'child') { []; const options = { breakOnSigint: true }; - assert.throws(() => { vm[method](script, ...args, options); }, - /^Error: Script execution interrupted\.$/); + common.expectsError( + () => { vm[method](script, ...args, options); }, + { + code: 'ERR_SCRIPT_EXECUTION_INTERRUPTED', + message: 'Script execution was interrupted by `SIGINT`' + }); assert.strictEqual(firstHandlerCalled, 0); assert.strictEqual(onceHandlerCalled, 0); diff --git a/test/parallel/test-vm-sigint.js b/test/parallel/test-vm-sigint.js index 9935b3d04b57a8..0fecfbace68049 100644 --- a/test/parallel/test-vm-sigint.js +++ b/test/parallel/test-vm-sigint.js @@ -24,8 +24,12 @@ if (process.argv[2] === 'child') { for (let i = 0; i < listeners; i++) process.on('SIGINT', common.mustNotCall()); - assert.throws(() => { vm[method](script, ...args, options); }, - /^Error: Script execution interrupted\.$/); + common.expectsError( + () => { vm[method](script, ...args, options); }, + { + code: 'ERR_SCRIPT_EXECUTION_INTERRUPTED', + message: 'Script execution was interrupted by `SIGINT`' + }); return; } diff --git a/test/parallel/test-vm-timeout.js b/test/parallel/test-vm-timeout.js index 859992e99ba36f..426d1e06925514 100644 --- a/test/parallel/test-vm-timeout.js +++ b/test/parallel/test-vm-timeout.js @@ -25,35 +25,50 @@ const assert = require('assert'); const vm = require('vm'); // Timeout of 100ms executing endless loop -assert.throws(function() { - vm.runInThisContext('while(true) {}', { timeout: 100 }); -}, /^Error: Script execution timed out\.$/); +assert.throws( + function() { + vm.runInThisContext('while(true) {}', { timeout: 100 }); + }, + { + code: 'ERR_SCRIPT_EXECUTION_TIMEOUT', + message: 'Script execution timed out after 100ms' + }); // Timeout of 1000ms, script finishes first vm.runInThisContext('', { timeout: 1000 }); // Nested vm timeouts, inner timeout propagates out -assert.throws(function() { - const context = { - log: console.log, - runInVM: function(timeout) { - vm.runInNewContext('while(true) {}', context, { timeout }); - } - }; - vm.runInNewContext('runInVM(10)', context, { timeout: 10000 }); - throw new Error('Test 5 failed'); -}, /Script execution timed out\./); +assert.throws( + function() { + const context = { + log: console.log, + runInVM: function(timeout) { + vm.runInNewContext('while(true) {}', context, { timeout }); + } + }; + vm.runInNewContext('runInVM(10)', context, { timeout: 10000 }); + throw new Error('Test 5 failed'); + }, + { + code: 'ERR_SCRIPT_EXECUTION_TIMEOUT', + message: 'Script execution timed out after 10ms' + }); // Nested vm timeouts, outer timeout is shorter and fires first. -assert.throws(function() { - const context = { - runInVM: function(timeout) { - vm.runInNewContext('while(true) {}', context, { timeout }); - } - }; - vm.runInNewContext('runInVM(10000)', context, { timeout: 100 }); - throw new Error('Test 6 failed'); -}, /Script execution timed out\./); +assert.throws( + function() { + const context = { + runInVM: function(timeout) { + vm.runInNewContext('while(true) {}', context, { timeout }); + } + }; + vm.runInNewContext('runInVM(10000)', context, { timeout: 100 }); + throw new Error('Test 6 failed'); + }, + { + code: 'ERR_SCRIPT_EXECUTION_TIMEOUT', + message: 'Script execution timed out after 100ms' + }); // Nested vm timeouts, inner script throws an error. assert.throws(function() { diff --git a/test/pseudo-tty/test-assert-colors.js b/test/pseudo-tty/test-assert-colors.js index 39bee740d2ea15..75d3af55796e1b 100644 --- a/test/pseudo-tty/test-assert-colors.js +++ b/test/pseudo-tty/test-assert-colors.js @@ -5,13 +5,16 @@ const assert = require('assert').strict; try { // Activate colors even if the tty does not support colors. process.env.COLORTERM = '1'; - assert.deepStrictEqual([1, 2], [2, 2]); + assert.deepStrictEqual([1, 2, 2, 2], [2, 2, 2, 2]); } catch (err) { const expected = 'Input A expected to strictly deep-equal input B:\n' + - '\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m\n\n' + + '\u001b[32m+ expected\u001b[39m \u001b[31m- actual\u001b[39m' + + ' \u001b[34m...\u001b[39m Lines skipped\n\n' + ' [\n' + '\u001b[31m-\u001b[39m 1,\n' + '\u001b[32m+\u001b[39m 2,\n' + + ' 2,\n' + + '\u001b[34m...\u001b[39m\n' + ' 2\n' + ' ]'; assert.strictEqual(err.message, expected); diff --git a/test/sequential/test-benchmark-http.js b/test/sequential/test-benchmark-http.js index 13d0e4d16aaf8f..22efce51909481 100644 --- a/test/sequential/test-benchmark-http.js +++ b/test/sequential/test-benchmark-http.js @@ -18,6 +18,7 @@ runBenchmark('http', 'chunkedEnc=true', 'chunks=0', 'dur=0.1', + 'duplicates=1', 'input=keep-alive', 'key=""', 'len=1', diff --git a/test/sequential/test-inspector-bindings.js b/test/sequential/test-inspector-bindings.js index bf97d8b5124e86..c23c8520e0601d 100644 --- a/test/sequential/test-inspector-bindings.js +++ b/test/sequential/test-inspector-bindings.js @@ -76,15 +76,6 @@ function testSampleDebugSession() { }; const session = new inspector.Session(); session.connect(); - let secondSessionOpened = false; - const secondSession = new inspector.Session(); - try { - secondSession.connect(); - secondSessionOpened = true; - } catch (error) { - // expected as the session already exists - } - assert.strictEqual(secondSessionOpened, false); session.on('Debugger.paused', (notification) => debuggerPausedCallback(session, notification)); let cbAsSecondArgCalled = false; diff --git a/test/sequential/test-inspector-module.js b/test/sequential/test-inspector-module.js index 2435347e798248..eaecd49c982311 100644 --- a/test/sequential/test-inspector-module.js +++ b/test/sequential/test-inspector-module.js @@ -55,16 +55,6 @@ common.expectsError( } ); -const session2 = new Session(); -common.expectsError( - () => session2.connect(), - { - code: 'ERR_INSPECTOR_ALREADY_CONNECTED', - type: Error, - message: 'Another inspector session is already connected' - } -); - session.disconnect(); // Calling disconnect twice should not throw. session.disconnect(); diff --git a/test/sequential/test-inspector.js b/test/sequential/test-inspector.js index 4e0315807b628a..0a9b2ccd1abd3d 100644 --- a/test/sequential/test-inspector.js +++ b/test/sequential/test-inspector.js @@ -37,12 +37,6 @@ function checkException(message) { 'An exception occurred during execution'); } -function assertNoUrlsWhileConnected(response) { - assert.strictEqual(1, response.length); - assert.ok(!response[0].hasOwnProperty('devtoolsFrontendUrl')); - assert.ok(!response[0].hasOwnProperty('webSocketDebuggerUrl')); -} - function assertScopeValues({ result }, expected) { const unmatched = new Set(Object.keys(expected)); for (const actual of result) { @@ -290,7 +284,6 @@ async function runTest() { await child.httpGet(null, '/json/badpath').catch(checkBadPath); const session = await child.connectInspectorSession(); - assertNoUrlsWhileConnected(await child.httpGet(null, '/json/list')); await testBreakpointOnStart(session); await testBreakpoint(session); await testI18NCharacters(session); diff --git a/test/sequential/test-vm-timeout-rethrow.js b/test/sequential/test-vm-timeout-rethrow.js index a7aa83e513d03c..c38460c4c89925 100644 --- a/test/sequential/test-vm-timeout-rethrow.js +++ b/test/sequential/test-vm-timeout-rethrow.js @@ -39,6 +39,6 @@ if (process.argv[2] === 'child') { }); process.on('exit', function() { - assert.ok(/Script execution timed out/.test(err)); + assert.ok(/Script execution timed out after 1ms/.test(err)); }); } diff --git a/tools/doc/generate.js b/tools/doc/generate.js index 0da9dba4e6558f..9f217b19c7225f 100644 --- a/tools/doc/generate.js +++ b/tools/doc/generate.js @@ -29,7 +29,6 @@ const fs = require('fs'); const args = process.argv.slice(2); let format = 'json'; -let template = null; let filename = null; let nodeVersion = null; let analytics = null; @@ -39,8 +38,6 @@ args.forEach(function(arg) { filename = arg; } else if (arg.startsWith('--format=')) { format = arg.replace(/^--format=/, ''); - } else if (arg.startsWith('--template=')) { - template = arg.replace(/^--template=/, ''); } else if (arg.startsWith('--node-version=')) { nodeVersion = arg.replace(/^--node-version=/, ''); } else if (arg.startsWith('--analytics=')) { @@ -71,7 +68,7 @@ function next(er, input) { break; case 'html': - require('./html')({ input, filename, template, nodeVersion, analytics }, + require('./html')({ input, filename, nodeVersion, analytics }, (err, html) => { if (err) throw err; console.log(html); diff --git a/tools/doc/html.js b/tools/doc/html.js index ff0230309ee99a..439fc057012ca7 100644 --- a/tools/doc/html.js +++ b/tools/doc/html.js @@ -25,7 +25,6 @@ const common = require('./common.js'); const fs = require('fs'); const marked = require('marked'); const path = require('path'); -const preprocess = require('./preprocess.js'); const typeParser = require('./type-parser.js'); module.exports = toHTML; @@ -42,76 +41,36 @@ marked.setOptions({ renderer: renderer }); -// TODO(chrisdickinson): never stop vomiting / fix this. -const gtocPath = path.resolve(path.join( - __dirname, - '..', - '..', - 'doc', - 'api', - '_toc.md' -)); -var gtocLoading = null; -var gtocData = null; +const docPath = path.resolve(__dirname, '..', '..', 'doc'); + +const gtocPath = path.join(docPath, 'api', '_toc.md'); +const gtocMD = fs.readFileSync(gtocPath, 'utf8').replace(/^@\/\/.*$/gm, ''); +const gtocHTML = marked(gtocMD).replace( + / `