-8.11.1
+8.11.2
+8.11.1 8.11.0 8.10.0 8.9.4
diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md
index 584e598c2ff38c..08b506f45683b8 100644
--- a/COLLABORATOR_GUIDE.md
+++ b/COLLABORATOR_GUIDE.md
@@ -593,20 +593,20 @@ Validate that the commit message is properly formatted using
$ git rev-list upstream/master...HEAD | xargs core-validate-commit
```
+Optional: When landing your own commits, force push the amended commit to the
+branch you used to open the pull request. If your branch is called `bugfix`,
+then the command would be `git push --force-with-lease origin master:bugfix`.
+When the pull request is closed, this will cause the pull request to
+show the purple merged status rather than the red closed status that is
+usually used for pull requests that weren't merged.
+
Time to push it:
```text
$ git push upstream master
```
-* Optional: Force push the amended commit to the branch you used to
-open the pull request. If your branch is called `bugfix`, then the
-command would be `git push --force-with-lease origin master:bugfix`.
-When the pull request is closed, this will cause the pull request to
-show the purple merged status rather than the red closed status that is
-usually used for pull requests that weren't merged. Only do this when
-landing your own contributions.
-* Close the pull request with a "Landed in ``" comment. If
+Close the pull request with a "Landed in ``" comment. If
your pull request shows the purple merged status then you should still
add the "Landed in .." comment if you added
multiple commits.
diff --git a/Makefile b/Makefile
index 9ea385e46a362d..2c62bf29cd2334 100644
--- a/Makefile
+++ b/Makefile
@@ -558,7 +558,7 @@ doc-only: $(apidoc_dirs) $(apiassets)
if [ ! -d doc/api/assets ]; then \
$(MAKE) tools/doc/node_modules/js-yaml/package.json; \
fi;
- @$(MAKE) -s $(apidocs_html) $(apidocs_json)
+ @$(MAKE) $(apidocs_html) $(apidocs_json)
doc: $(NODE_EXE) doc-only
@@ -1008,26 +1008,31 @@ lint-md-clean:
lint-md-build:
@if [ ! -d tools/remark-cli/node_modules ]; then \
echo "Markdown linter: installing remark-cli into tools/"; \
- cd tools/remark-cli && ../../$(NODE) ../../$(NPM) install; fi
+ cd tools/remark-cli && $(call available-node,$(run-npm-install)) fi
@if [ ! -d tools/remark-preset-lint-node/node_modules ]; then \
echo "Markdown linter: installing remark-preset-lint-node into tools/"; \
- cd tools/remark-preset-lint-node && ../../$(NODE) ../../$(NPM) install; fi
+ cd tools/remark-preset-lint-node && $(call available-node,$(run-npm-install)) fi
+
ifneq ("","$(wildcard tools/remark-cli/node_modules/)")
-LINT_MD_TARGETS = src lib benchmark tools/doc tools/icu
-LINT_MD_ROOT_DOCS := $(wildcard *.md)
-LINT_MD_FILES := $(shell find $(LINT_MD_TARGETS) -type f \
- -not -path '*node_modules*' -name '*.md') $(LINT_MD_ROOT_DOCS)
-LINT_DOC_MD_FILES = $(shell ls doc/**/*.md)
-tools/.docmdlintstamp: $(LINT_DOC_MD_FILES)
+LINT_MD_DOC_FILES = $(shell ls doc/**/*.md)
+run-lint-doc-md = tools/remark-cli/cli.js -q -f $(LINT_MD_DOC_FILES)
+# Lint all changed markdown files under doc/
+tools/.docmdlintstamp: $(LINT_MD_DOC_FILES)
@echo "Running Markdown linter on docs..."
- @$(NODE) tools/remark-cli/cli.js -q -f $(LINT_DOC_MD_FILES)
+ @$(call available-node,$(run-lint-doc-md))
@touch $@
-tools/.miscmdlintstamp: $(LINT_MD_FILES)
+LINT_MD_TARGETS = src lib benchmark tools/doc tools/icu
+LINT_MD_ROOT_DOCS := $(wildcard *.md)
+LINT_MD_MISC_FILES := $(shell find $(LINT_MD_TARGETS) -type f \
+ -not -path '*node_modules*' -name '*.md') $(LINT_MD_ROOT_DOCS)
+run-lint-misc-md = tools/remark-cli/cli.js -q -f $(LINT_MD_MISC_FILES)
+# Lint other changed markdown files maintained by us
+tools/.miscmdlintstamp: $(LINT_MD_MISC_FILES)
@echo "Running Markdown linter on misc docs..."
- @$(NODE) tools/remark-cli/cli.js -q -f $(LINT_MD_FILES)
+ @$(call available-node,$(run-lint-misc-md))
@touch $@
tools/.mdlintstamp: tools/.miscmdlintstamp tools/.docmdlintstamp
@@ -1040,37 +1045,29 @@ lint-md:
endif
LINT_JS_TARGETS = benchmark doc lib test tools
-LINT_JS_CMD = tools/eslint/bin/eslint.js --cache \
- --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md \
- $(LINT_JS_TARGETS)
+
+run-lint-js = tools/eslint/bin/eslint.js --cache \
+ --rulesdir=tools/eslint-rules --ext=.js,.mjs,.md $(LINT_JS_TARGETS)
+run-lint-js-fix = $(run-lint-js) --fix
lint-js-fix:
- @if [ -x $(NODE) ]; then \
- $(NODE) $(LINT_JS_CMD) --fix; \
- else \
- node $(LINT_JS_CMD) --fix; \
- fi
+ @$(call available-node,$(run-lint-js-fix))
lint-js:
@echo "Running JS linter..."
- @if [ -x $(NODE) ]; then \
- $(NODE) $(LINT_JS_CMD); \
- else \
- node $(LINT_JS_CMD); \
- fi
+ @$(call available-node,$(run-lint-js))
jslint: lint-js
@echo "Please use lint-js instead of jslint"
+run-lint-js-ci = tools/lint-js.js $(PARALLEL_ARGS) -f tap -o test-eslint.tap \
+ $(LINT_JS_TARGETS)
+
+.PHONY: lint-js-ci
+# On the CI the output is emitted in the TAP format.
lint-js-ci:
@echo "Running JS linter..."
- @if [ -x $(NODE) ]; then \
- $(NODE) tools/lint-js.js $(PARALLEL_ARGS) -f tap -o test-eslint.tap \
- $(LINT_JS_TARGETS); \
- else \
- node tools/lint-js.js $(PARALLEL_ARGS) -f tap -o test-eslint.tap \
- $(LINT_JS_TARGETS); \
- fi
+ @$(call available-node,$(run-lint-js-ci))
jslint-ci: lint-js-ci
@echo "Please use lint-js-ci instead of jslint-ci"
@@ -1128,6 +1125,7 @@ lint: ## Run JS, C++, MD and doc linters.
$(MAKE) lint-cpp || EXIT_STATUS=$$? ; \
$(MAKE) lint-md || EXIT_STATUS=$$? ; \
$(MAKE) lint-addon-docs || EXIT_STATUS=$$? ; \
+ $(MAKE) lint-md || EXIT_STATUS=$$? ; \
exit $$EXIT_STATUS
CONFLICT_RE=^>>>>>>> [0-9A-Fa-f]+|^<<<<<<< [A-Za-z]+
lint-ci: lint-js-ci lint-cpp lint-md lint-addon-docs
diff --git a/README.md b/README.md
index 89f9e3998a2827..e537407c8685bd 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,9 @@
-Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js
-uses an event-driven, non-blocking I/O model that makes it lightweight and
-efficient. The Node.js package ecosystem, [npm][], is the largest ecosystem of
-open source libraries in the world.
+Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. For
+more information on using Node.js, see the
+[Node.js Website][].
The Node.js project is supported by the
[Node.js Foundation](https://nodejs.org/en/foundation/). Contributions,
@@ -245,8 +244,8 @@ For more information about the governance of the Node.js project, see
**Franziska Hinkelmann** <franziska.hinkelmann@gmail.com> (she/her)
* [Fishrock123](https://github.com/Fishrock123) -
**Jeremiah Senkpiel** <fishrock123@rocketmail.com>
-* [indutny](https://github.com/indutny) -
-**Fedor Indutny** <fedor.indutny@gmail.com>
+* [gibfahn](https://github.com/gibfahn) -
+**Gibson Fahnestock** <gibfahn@gmail.com> (he/him)
* [jasnell](https://github.com/jasnell) -
**James M Snell** <jasnell@gmail.com> (he/him)
* [joyeecheung](https://github.com/joyeecheung) -
@@ -255,8 +254,6 @@ For more information about the governance of the Node.js project, see
**Matteo Collina** <matteo.collina@gmail.com> (he/him)
* [mhdawson](https://github.com/mhdawson) -
**Michael Dawson** <michael_dawson@ca.ibm.com> (he/him)
-* [mscdex](https://github.com/mscdex) -
-**Brian White** <mscdex@mscdex.net>
* [MylesBorins](https://github.com/MylesBorins) -
**Myles Borins** <myles.borins@gmail.com> (he/him)
* [ofrobots](https://github.com/ofrobots) -
@@ -278,10 +275,14 @@ For more information about the governance of the Node.js project, see
**Ben Noordhuis** <info@bnoordhuis.nl>
* [chrisdickinson](https://github.com/chrisdickinson) -
**Chris Dickinson** <christopher.s.dickinson@gmail.com>
+* [indutny](https://github.com/indutny) -
+**Fedor Indutny** <fedor.indutny@gmail.com>
* [isaacs](https://github.com/isaacs) -
**Isaac Z. Schlueter** <i@izs.me>
* [joshgav](https://github.com/joshgav) -
**Josh Gavant** <josh.gavant@outlook.com>
+* [mscdex](https://github.com/mscdex) -
+**Brian White** <mscdex@mscdex.net>
* [nebrius](https://github.com/nebrius) -
**Bryan Hughes** <bryan@nebri.us>
* [orangemocha](https://github.com/orangemocha) -
@@ -339,6 +340,8 @@ For more information about the governance of the Node.js project, see
**Daniel Bevenius** <daniel.bevenius@gmail.com>
* [DavidCai1993](https://github.com/DavidCai1993) -
**David Cai** <davidcai1993@yahoo.com> (he/him)
+* [devsnek](https://github.com/devsnek) -
+**Gus Caplan** <me@gus.host> (he/him)
* [edsadr](https://github.com/edsadr) -
**Adrian Estrada** <edsadr@gmail.com> (he/him)
* [eljefedelrodeodeljefe](https://github.com/eljefedelrodeodeljefe) -
@@ -423,6 +426,8 @@ For more information about the governance of the Node.js project, see
**Mikeal Rogers** <mikeal.rogers@gmail.com>
* [misterdjules](https://github.com/misterdjules) -
**Julien Gilli** <jgilli@nodejs.org>
+* [mmarchini](https://github.com/mmarchini) -
+**Matheus Marchini** <matheus@sthima.com>
* [mscdex](https://github.com/mscdex) -
**Brian White** <mscdex@mscdex.net>
* [MylesBorins](https://github.com/MylesBorins) -
@@ -590,12 +595,13 @@ Previous releases may also have been signed with one of the following GPG keys:
* [Contributing to the project][]
* [Working Groups][]
+* [Strategic Initiatives][]
-[npm]: https://www.npmjs.com
[Code of Conduct]: https://github.com/nodejs/admin/blob/master/CODE_OF_CONDUCT.md
[Contributing to the project]: CONTRIBUTING.md
[Node.js Help]: https://github.com/nodejs/help
[Node.js Website]: https://nodejs.org/en/
[Questions tagged 'node.js' on StackOverflow]: https://stackoverflow.com/questions/tagged/node.js
[Working Groups]: https://github.com/nodejs/TSC/blob/master/WORKING_GROUPS.md
+[Strategic Initiatives]: https://github.com/nodejs/TSC/blob/master/Strategic-Initiatives.md
[#node.js channel on chat.freenode.net]: https://webchat.freenode.net?channels=node.js&uio=d4
diff --git a/benchmark/compare.js b/benchmark/compare.js
index 6b51a70eb9a41b..e7866b60e36418 100644
--- a/benchmark/compare.js
+++ b/benchmark/compare.js
@@ -1,6 +1,7 @@
'use strict';
-const fork = require('child_process').fork;
+const { fork } = require('child_process');
+const { inspect } = require('util');
const path = require('path');
const CLI = require('./_cli.js');
const BenchmarkProgress = require('./_benchmark_progress.js');
@@ -76,7 +77,7 @@ if (showProgress) {
// Construct configuration string, " A=a, B=b, ..."
let conf = '';
for (const key of Object.keys(data.conf)) {
- conf += ` ${key}=${JSON.stringify(data.conf[key])}`;
+ conf += ` ${key}=${inspect(data.conf[key])}`;
}
conf = conf.slice(1);
// Escape quotes (") for correct csv formatting
diff --git a/benchmark/http/http_server_for_chunky_client.js b/benchmark/http/http_server_for_chunky_client.js
index f079544e03d48e..1e5a4583669c0f 100644
--- a/benchmark/http/http_server_for_chunky_client.js
+++ b/benchmark/http/http_server_for_chunky_client.js
@@ -2,22 +2,15 @@
const assert = require('assert');
const http = require('http');
-const fs = require('fs');
const { fork } = require('child_process');
const common = require('../common.js');
-const { PIPE, tmpDir } = require('../../test/common');
+const { PIPE } = require('../../test/common');
+const tmpdir = require('../../test/common/tmpdir');
process.env.PIPE_NAME = PIPE;
-try {
- fs.accessSync(tmpDir, fs.F_OK);
-} catch (e) {
- fs.mkdirSync(tmpDir);
-}
+tmpdir.refresh();
var server;
-try {
- fs.unlinkSync(process.env.PIPE_NAME);
-} catch (e) { /* ignore */ }
server = http.createServer(function(req, res) {
const headers = {
diff --git a/benchmark/misc/punycode.js b/benchmark/misc/punycode.js
index 630aea3195f098..40bcd70302003c 100644
--- a/benchmark/misc/punycode.js
+++ b/benchmark/misc/punycode.js
@@ -1,11 +1,14 @@
'use strict';
const common = require('../common.js');
-const icu = process.binding('icu');
+let icu;
+try {
+ icu = process.binding('icu');
+} catch (err) {}
const punycode = require('punycode');
const bench = common.createBenchmark(main, {
- method: ['punycode', 'icu'],
+ method: ['punycode'].concat(icu !== undefined ? ['icu'] : []),
n: [1024],
val: [
'افغانستا.icom.museum',
@@ -69,8 +72,11 @@ function main(conf) {
runPunycode(n, val);
break;
case 'icu':
- runICU(n, val);
- break;
+ if (icu !== undefined) {
+ runICU(n, val);
+ break;
+ }
+ // fallthrough
default:
throw new Error('Unexpected method');
}
diff --git a/benchmark/module/module-loader.js b/benchmark/module/module-loader.js
index cca5fc2c229038..a0b8f7b6892633 100644
--- a/benchmark/module/module-loader.js
+++ b/benchmark/module/module-loader.js
@@ -3,8 +3,8 @@ const fs = require('fs');
const path = require('path');
const common = require('../common.js');
-const { refreshTmpDir, tmpDir } = require('../../test/common');
-const benchmarkDirectory = path.join(tmpDir, 'nodejs-benchmark-module');
+const tmpdir = require('../../test/common/tmpdir');
+const benchmarkDirectory = path.join(tmpdir.path, 'nodejs-benchmark-module');
const bench = common.createBenchmark(main, {
thousands: [50],
@@ -15,7 +15,7 @@ const bench = common.createBenchmark(main, {
function main(conf) {
const n = +conf.thousands * 1e3;
- refreshTmpDir();
+ tmpdir.refresh();
try { fs.mkdirSync(benchmarkDirectory); } catch (e) {}
for (var i = 0; i <= n; i++) {
@@ -35,7 +35,7 @@ function main(conf) {
else
measureDir(n, conf.useCache === 'true');
- refreshTmpDir();
+ tmpdir.refresh();
}
function measureFull(n, useCache) {
diff --git a/benchmark/tls/throughput.js b/benchmark/tls/throughput.js
index 51feb85cbaccc1..52907a3343fb1f 100644
--- a/benchmark/tls/throughput.js
+++ b/benchmark/tls/throughput.js
@@ -45,11 +45,11 @@ function main(conf) {
};
server = tls.createServer(options, onConnection);
- setTimeout(done, dur * 1000);
var conn;
server.listen(common.PORT, function() {
const opt = { port: common.PORT, rejectUnauthorized: false };
conn = tls.connect(opt, function() {
+ setTimeout(done, dur * 1000);
bench.start();
conn.on('drain', write);
write();
diff --git a/common.gypi b/common.gypi
index 99351610eb447b..b89a8afff03a63 100644
--- a/common.gypi
+++ b/common.gypi
@@ -282,7 +282,7 @@
],
}],
[ 'OS in "linux freebsd openbsd solaris aix"', {
- 'cflags': [ '-pthread', ],
+ 'cflags': [ '-pthread' ],
'ldflags': [ '-pthread' ],
}],
[ 'OS in "linux freebsd openbsd solaris android aix"', {
@@ -295,6 +295,7 @@
'standalone_static_library': 1,
}],
['OS=="openbsd"', {
+ 'cflags': [ '-I/usr/local/include' ],
'ldflags': [ '-Wl,-z,wxneeded' ],
}],
],
diff --git a/configure b/configure
index 5a242a1afeec0e..e6b55c6da06f31 100755
--- a/configure
+++ b/configure
@@ -61,7 +61,7 @@ parser = optparse.OptionParser()
valid_os = ('win', 'mac', 'solaris', 'freebsd', 'openbsd', 'linux',
'android', 'aix')
valid_arch = ('arm', 'arm64', 'ia32', 'mips', 'mipsel', 'mips64el', 'ppc',
- 'ppc64', 'x32','x64', 'x86', 's390', 's390x')
+ 'ppc64', 'x32','x64', 'x86', 'x86_64', 's390', 's390x')
valid_arm_float_abi = ('soft', 'softfp', 'hard')
valid_arm_fpu = ('vfp', 'vfpv3', 'vfpv3-d16', 'neon')
valid_mips_arch = ('loongson', 'r1', 'r2', 'r6', 'rx')
@@ -861,6 +861,9 @@ def configure_node(o):
# the Makefile resets this to x86 afterward
if target_arch == 'x86':
target_arch = 'ia32'
+ # x86_64 is common across linuxes, allow it as an alias for x64
+ if target_arch == 'x86_64':
+ target_arch = 'x64'
o['variables']['host_arch'] = host_arch
o['variables']['target_arch'] = target_arch
o['variables']['node_byteorder'] = sys.byteorder
@@ -878,7 +881,6 @@ def configure_node(o):
configure_mips(o)
if flavor == 'aix':
- o['variables']['node_core_target_name'] = 'node_base'
o['variables']['node_target_type'] = 'static_library'
if target_arch in ('x86', 'x64', 'ia32', 'x32'):
@@ -988,6 +990,13 @@ def configure_node(o):
else:
o['variables']['coverage'] = 'false'
+ if options.shared:
+ o['variables']['node_target_type'] = 'shared_library'
+ elif options.enable_static:
+ o['variables']['node_target_type'] = 'static_library'
+ else:
+ o['variables']['node_target_type'] = 'executable'
+
def configure_library(lib, output):
shared_lib = 'shared_' + lib
output['variables']['node_' + shared_lib] = b(getattr(options, shared_lib))
@@ -1484,8 +1493,7 @@ 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'] if options.enable_static \
- else '',
+ 'NODE_TARGET_TYPE': variables['node_target_type'],
}
if options.prefix:
diff --git a/deps/nghttp2/lib/CMakeLists.txt b/deps/nghttp2/lib/CMakeLists.txt
index 7ef37ed85cc628..0846d06789a0f1 100644
--- a/deps/nghttp2/lib/CMakeLists.txt
+++ b/deps/nghttp2/lib/CMakeLists.txt
@@ -44,6 +44,10 @@ set_target_properties(nghttp2 PROPERTIES
VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
C_VISIBILITY_PRESET hidden
)
+target_include_directories(nghttp2 INTERFACE
+ "${CMAKE_CURRENT_BINARY_DIR}/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/includes"
+ )
if(HAVE_CUNIT)
# Static library (for unittests because of symbol visibility)
diff --git a/deps/nghttp2/lib/includes/config.h b/deps/nghttp2/lib/includes/config.h
index 0346e0614fdb8d..242bbcfb62ff7a 100644
--- a/deps/nghttp2/lib/includes/config.h
+++ b/deps/nghttp2/lib/includes/config.h
@@ -1,8 +1,18 @@
/* Hint to the compiler that a function never returns */
#define NGHTTP2_NORETURN
-/* Define to `int' if does not define. */
-#define ssize_t int
+/* Edited to match src/node.h. */
+#include
+
+#ifdef _WIN32
+#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
+typedef intptr_t ssize_t;
+# define _SSIZE_T_
+# define _SSIZE_T_DEFINED
+#endif
+#else // !_WIN32
+# include // size_t, ssize_t
+#endif // _WIN32
/* Define to 1 if you have the `std::map::emplace`. */
#define HAVE_STD_MAP_EMPLACE 1
diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h
index 5696a2ef633653..13cda9f29e28f5 100644
--- a/deps/nghttp2/lib/includes/nghttp2/nghttp2.h
+++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2.h
@@ -387,6 +387,11 @@ typedef enum {
* Indicates that a processing was canceled.
*/
NGHTTP2_ERR_CANCEL = -535,
+ /**
+ * When a local endpoint expects to receive SETTINGS frame, it
+ * receives an other type of frame.
+ */
+ NGHTTP2_ERR_SETTINGS_EXPECTED = -536,
/**
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
* under unexpected condition and processing was terminated (e.g.,
@@ -1987,6 +1992,9 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
* of length |len|. |len| does not include the sentinel NULL
* character.
*
+ * This function is deprecated. The new application should use
+ * :type:`nghttp2_error_callback2`.
+ *
* The format of error message may change between nghttp2 library
* versions. The application should not depend on the particular
* format.
@@ -2003,6 +2011,33 @@ typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session,
typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg,
size_t len, void *user_data);
+/**
+ * @functypedef
+ *
+ * Callback function invoked when library provides the error code, and
+ * message. This callback is solely for debugging purpose.
+ * |lib_error_code| is one of error code defined in
+ * :enum:`nghttp2_error`. The |msg| is typically NULL-terminated
+ * string of length |len|, and intended for human consumption. |len|
+ * does not include the sentinel NULL character.
+ *
+ * The format of error message may change between nghttp2 library
+ * versions. The application should not depend on the particular
+ * format.
+ *
+ * Normally, application should return 0 from this callback. If fatal
+ * error occurred while doing something in this callback, application
+ * should return :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. In this case,
+ * library will return immediately with return value
+ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if nonzero value
+ * is returned from this callback, they are treated as
+ * :enum:`NGHTTP2_ERR_CALLBACK_FAILURE`, but application should not
+ * rely on this details.
+ */
+typedef int (*nghttp2_error_callback2)(nghttp2_session *session,
+ int lib_error_code, const char *msg,
+ size_t len, void *user_data);
+
struct nghttp2_session_callbacks;
/**
@@ -2267,10 +2302,30 @@ nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
*
* Sets callback function invoked when library tells error message to
* the application.
+ *
+ * This function is deprecated. The new application should use
+ * `nghttp2_session_callbacks_set_error_callback2()`.
+ *
+ * If both :type:`nghttp2_error_callback` and
+ * :type:`nghttp2_error_callback2` are set, the latter takes
+ * precedence.
*/
NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback(
nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback);
+/**
+ * @function
+ *
+ * Sets callback function invoked when library tells error code, and
+ * message to the application.
+ *
+ * If both :type:`nghttp2_error_callback` and
+ * :type:`nghttp2_error_callback2` are set, the latter takes
+ * precedence.
+ */
+NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2(
+ nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2);
+
/**
* @functypedef
*
@@ -4702,8 +4757,8 @@ nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
*
* After this function returns, it is safe to delete the |nva|.
*
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
+ * This function returns the number of bytes written to |buf| if it
+ * succeeds, or one of the following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
@@ -4734,8 +4789,8 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater,
*
* After this function returns, it is safe to delete the |nva|.
*
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
+ * This function returns the number of bytes written to |vec| if it
+ * succeeds, or one of the following negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
diff --git a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h
index 38c48bf041f1e8..455706a5868b3a 100644
--- a/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h
+++ b/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h
@@ -29,7 +29,7 @@
* @macro
* Version number of the nghttp2 library release
*/
-#define NGHTTP2_VERSION "1.25.0"
+#define NGHTTP2_VERSION "1.29.0"
/**
* @macro
@@ -37,6 +37,6 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
-#define NGHTTP2_VERSION_NUM 0x011900
+#define NGHTTP2_VERSION_NUM 0x011d00
#endif /* NGHTTP2VER_H */
diff --git a/deps/nghttp2/lib/nghttp2_buf.h b/deps/nghttp2/lib/nghttp2_buf.h
index 06ab1e4c630cc3..9f484a221acb5f 100644
--- a/deps/nghttp2/lib/nghttp2_buf.h
+++ b/deps/nghttp2/lib/nghttp2_buf.h
@@ -398,7 +398,7 @@ int nghttp2_bufs_advance(nghttp2_bufs *bufs);
void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs);
/*
- * Returns nonzero if bufs->cur->next is not emtpy.
+ * Returns nonzero if bufs->cur->next is not empty.
*/
int nghttp2_bufs_next_present(nghttp2_bufs *bufs);
diff --git a/deps/nghttp2/lib/nghttp2_callbacks.c b/deps/nghttp2/lib/nghttp2_callbacks.c
index b6cf5957f01b59..3c38214859b17a 100644
--- a/deps/nghttp2/lib/nghttp2_callbacks.c
+++ b/deps/nghttp2/lib/nghttp2_callbacks.c
@@ -168,3 +168,8 @@ void nghttp2_session_callbacks_set_error_callback(
nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) {
cbs->error_callback = error_callback;
}
+
+void nghttp2_session_callbacks_set_error_callback2(
+ nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) {
+ cbs->error_callback2 = error_callback2;
+}
diff --git a/deps/nghttp2/lib/nghttp2_callbacks.h b/deps/nghttp2/lib/nghttp2_callbacks.h
index 5967524e0c6493..b607bbb58b8e3d 100644
--- a/deps/nghttp2/lib/nghttp2_callbacks.h
+++ b/deps/nghttp2/lib/nghttp2_callbacks.h
@@ -119,6 +119,7 @@ struct nghttp2_session_callbacks {
nghttp2_unpack_extension_callback unpack_extension_callback;
nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
nghttp2_error_callback error_callback;
+ nghttp2_error_callback2 error_callback2;
};
#endif /* NGHTTP2_CALLBACKS_H */
diff --git a/deps/nghttp2/lib/nghttp2_frame.h b/deps/nghttp2/lib/nghttp2_frame.h
index 891289f61bf5e7..35ca214a4a7a59 100644
--- a/deps/nghttp2/lib/nghttp2_frame.h
+++ b/deps/nghttp2/lib/nghttp2_frame.h
@@ -70,7 +70,9 @@
#define NGHTTP2_MAX_PADLEN 256
/* Union of extension frame payload */
-typedef union { nghttp2_ext_altsvc altsvc; } nghttp2_ext_frame_payload;
+typedef union {
+ nghttp2_ext_altsvc altsvc;
+} nghttp2_ext_frame_payload;
void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd);
diff --git a/deps/nghttp2/lib/nghttp2_hd.h b/deps/nghttp2/lib/nghttp2_hd.h
index 458edafe4d5847..760bfbc357efdc 100644
--- a/deps/nghttp2/lib/nghttp2_hd.h
+++ b/deps/nghttp2/lib/nghttp2_hd.h
@@ -211,7 +211,9 @@ typedef struct {
#define HD_MAP_SIZE 128
-typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map;
+typedef struct {
+ nghttp2_hd_entry *table[HD_MAP_SIZE];
+} nghttp2_hd_map;
struct nghttp2_hd_deflater {
nghttp2_hd_context ctx;
@@ -313,7 +315,7 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater);
*
* This function expands |bufs| as necessary to store the result. If
* buffers is full and the process still requires more space, this
- * funtion fails and returns NGHTTP2_ERR_HEADER_COMP.
+ * function fails and returns NGHTTP2_ERR_HEADER_COMP.
*
* After this function returns, it is safe to delete the |nva|.
*
diff --git a/deps/nghttp2/lib/nghttp2_helper.c b/deps/nghttp2/lib/nghttp2_helper.c
index b00c9073a92a13..3b282c7301f95b 100644
--- a/deps/nghttp2/lib/nghttp2_helper.c
+++ b/deps/nghttp2/lib/nghttp2_helper.c
@@ -322,6 +322,9 @@ const char *nghttp2_strerror(int error_code) {
return "Internal error";
case NGHTTP2_ERR_CANCEL:
return "Cancel";
+ case NGHTTP2_ERR_SETTINGS_EXPECTED:
+ return "When a local endpoint expects to receive SETTINGS frame, it "
+ "receives an other type of frame";
case NGHTTP2_ERR_NOMEM:
return "Out of memory";
case NGHTTP2_ERR_CALLBACK_FAILURE:
diff --git a/deps/nghttp2/lib/nghttp2_outbound_item.h b/deps/nghttp2/lib/nghttp2_outbound_item.h
index 8bda776bfe2728..89a8a92668dd5c 100644
--- a/deps/nghttp2/lib/nghttp2_outbound_item.h
+++ b/deps/nghttp2/lib/nghttp2_outbound_item.h
@@ -112,7 +112,7 @@ struct nghttp2_outbound_item {
nghttp2_ext_frame_payload ext_frame_payload;
nghttp2_aux_data aux_data;
/* The priority used in priority comparion. Smaller is served
- ealier. For PING, SETTINGS and non-DATA frames (excluding
+ earlier. For PING, SETTINGS and non-DATA frames (excluding
response HEADERS frame) have dedicated cycle value defined above.
For DATA frame, cycle is computed by taking into account of
effective weight and frame payload length previously sent, so
diff --git a/deps/nghttp2/lib/nghttp2_pq.h b/deps/nghttp2/lib/nghttp2_pq.h
index 1426bef760132c..71cf96a14e0c77 100644
--- a/deps/nghttp2/lib/nghttp2_pq.h
+++ b/deps/nghttp2/lib/nghttp2_pq.h
@@ -35,7 +35,9 @@
/* Implementation of priority queue */
-typedef struct { size_t index; } nghttp2_pq_entry;
+typedef struct {
+ size_t index;
+} nghttp2_pq_entry;
typedef struct {
/* The pointer to the pointer to the item stored */
@@ -71,7 +73,7 @@ void nghttp2_pq_free(nghttp2_pq *pq);
/*
* Adds |item| to the priority queue |pq|.
*
- * This function returns 0 if it succeds, or one of the following
+ * This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
diff --git a/deps/nghttp2/lib/nghttp2_queue.h b/deps/nghttp2/lib/nghttp2_queue.h
index d872b07bde961c..c7eb753ca92182 100644
--- a/deps/nghttp2/lib/nghttp2_queue.h
+++ b/deps/nghttp2/lib/nghttp2_queue.h
@@ -36,7 +36,9 @@ typedef struct nghttp2_queue_cell {
struct nghttp2_queue_cell *next;
} nghttp2_queue_cell;
-typedef struct { nghttp2_queue_cell *front, *back; } nghttp2_queue;
+typedef struct {
+ nghttp2_queue_cell *front, *back;
+} nghttp2_queue;
void nghttp2_queue_init(nghttp2_queue *queue);
void nghttp2_queue_free(nghttp2_queue *queue);
diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c
index 4bc94cbb1982ad..b14ed77a25c293 100644
--- a/deps/nghttp2/lib/nghttp2_session.c
+++ b/deps/nghttp2/lib/nghttp2_session.c
@@ -148,14 +148,16 @@ static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) {
}
static int session_call_error_callback(nghttp2_session *session,
- const char *fmt, ...) {
+ int lib_error_code, const char *fmt,
+ ...) {
size_t bufsize;
va_list ap;
char *buf;
int rv;
nghttp2_mem *mem;
- if (!session->callbacks.error_callback) {
+ if (!session->callbacks.error_callback &&
+ !session->callbacks.error_callback2) {
return 0;
}
@@ -189,8 +191,13 @@ static int session_call_error_callback(nghttp2_session *session,
return 0;
}
- rv = session->callbacks.error_callback(session, buf, (size_t)rv,
- session->user_data);
+ if (session->callbacks.error_callback2) {
+ rv = session->callbacks.error_callback2(session, lib_error_code, buf,
+ (size_t)rv, session->user_data);
+ } else {
+ rv = session->callbacks.error_callback(session, buf, (size_t)rv,
+ session->user_data);
+ }
nghttp2_mem_free(mem, buf);
@@ -541,9 +548,8 @@ static int session_new(nghttp2_session **session_ptr,
if (nghttp2_enable_strict_preface) {
nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe;
- if (server &&
- ((*session_ptr)->opt_flags & NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) ==
- 0) {
+ if (server && ((*session_ptr)->opt_flags &
+ NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) {
iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC;
iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN;
} else {
@@ -2183,7 +2189,7 @@ static int session_prep_frame(nghttp2_session *session,
closed. */
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
- /* predicte should fail if stream is NULL. */
+ /* predicate should fail if stream is NULL. */
rv = session_predicate_push_promise_send(session, stream);
if (rv != 0) {
return rv;
@@ -2411,19 +2417,16 @@ static int session_close_stream_on_goaway(nghttp2_session *session,
nghttp2_stream *stream, *next_stream;
nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id,
incoming};
- uint32_t error_code;
rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg);
assert(rv == 0);
- error_code =
- session->server && incoming ? NGHTTP2_REFUSED_STREAM : NGHTTP2_CANCEL;
-
stream = arg.head;
while (stream) {
next_stream = stream->closed_next;
stream->closed_next = NULL;
- rv = nghttp2_session_close_stream(session, stream->stream_id, error_code);
+ rv = nghttp2_session_close_stream(session, stream->stream_id,
+ NGHTTP2_REFUSED_STREAM);
/* stream may be deleted here */
@@ -3608,7 +3611,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
nv.name->base, (int)nv.value->len, nv.value->base);
rv2 = session_call_error_callback(
- session,
+ session, NGHTTP2_ERR_HTTP_HEADER,
"Ignoring received invalid HTTP header field: frame type: "
"%u, stream: %d, name: [%.*s], value: [%.*s]",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
@@ -3626,8 +3629,9 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
nv.name->base, (int)nv.value->len, nv.value->base);
rv = session_call_error_callback(
- session, "Invalid HTTP header field was received: frame type: "
- "%u, stream: %d, name: [%.*s], value: [%.*s]",
+ session, NGHTTP2_ERR_HTTP_HEADER,
+ "Invalid HTTP header field was received: frame type: "
+ "%u, stream: %d, name: [%.*s], value: [%.*s]",
frame->hd.type, frame->hd.stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base);
@@ -3781,7 +3785,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0");
}
- /* If client recieves idle stream from server, it is invalid
+ /* If client receives idle stream from server, it is invalid
regardless stream ID is even or odd. This is because client is
not expected to receive request from server. */
if (!session->server) {
@@ -5345,9 +5349,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->state = NGHTTP2_IB_IGN_ALL;
rv = session_call_error_callback(
- session, "Remote peer returned unexpected data while we expected "
- "SETTINGS frame. Perhaps, peer does not support HTTP/2 "
- "properly.");
+ session, NGHTTP2_ERR_SETTINGS_EXPECTED,
+ "Remote peer returned unexpected data while we expected "
+ "SETTINGS frame. Perhaps, peer does not support HTTP/2 "
+ "properly.");
if (nghttp2_is_fatal(rv)) {
return rv;
@@ -5588,13 +5593,13 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
if (iframe->payloadleft) {
nghttp2_settings_entry *min_header_table_size_entry;
- /* We allocate iv with addtional one entry, to store the
+ /* We allocate iv with additional one entry, to store the
minimum header table size. */
iframe->max_niv =
iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1;
- iframe->iv = nghttp2_mem_malloc(
- mem, sizeof(nghttp2_settings_entry) * iframe->max_niv);
+ iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) *
+ iframe->max_niv);
if (!iframe->iv) {
return NGHTTP2_ERR_NOMEM;
diff --git a/deps/nghttp2/lib/nghttp2_session.h b/deps/nghttp2/lib/nghttp2_session.h
index 3e1467f6a356d7..c7cb27d77c1e25 100644
--- a/deps/nghttp2/lib/nghttp2_session.h
+++ b/deps/nghttp2/lib/nghttp2_session.h
@@ -319,7 +319,7 @@ struct nghttp2_session {
uint8_t pending_enable_push;
/* Nonzero if the session is server side. */
uint8_t server;
- /* Flags indicating GOAWAY is sent and/or recieved. The flags are
+ /* Flags indicating GOAWAY is sent and/or received. The flags are
composed by bitwise OR-ing nghttp2_goaway_flag. */
uint8_t goaway_flags;
/* This flag is used to reduce excessive queuing of WINDOW_UPDATE to
@@ -722,7 +722,7 @@ int nghttp2_session_on_goaway_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
- * Called when WINDOW_UPDATE is recieved, assuming |frame| is properly
+ * Called when WINDOW_UPDATE is received, assuming |frame| is properly
* initialized.
*
* This function returns 0 if it succeeds, or one of the following
@@ -737,7 +737,7 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
nghttp2_frame *frame);
/*
- * Called when ALTSVC is recieved, assuming |frame| is properly
+ * Called when ALTSVC is received, assuming |frame| is properly
* initialized.
*
* This function returns 0 if it succeeds, or one of the following
diff --git a/deps/nghttp2/lib/nghttp2_stream.c b/deps/nghttp2/lib/nghttp2_stream.c
index 8dee6ef660983c..eccd3174ef7bda 100644
--- a/deps/nghttp2/lib/nghttp2_stream.c
+++ b/deps/nghttp2/lib/nghttp2_stream.c
@@ -366,8 +366,9 @@ static void check_queued(nghttp2_stream *stream) {
}
}
if (queued == 0) {
- fprintf(stderr, "stream(%p)=%d, stream->queued == 1, and "
- "!stream_active(), but no descendants is queued\n",
+ fprintf(stderr,
+ "stream(%p)=%d, stream->queued == 1, and "
+ "!stream_active(), but no descendants is queued\n",
stream, stream->stream_id);
assert(0);
}
@@ -378,9 +379,10 @@ static void check_queued(nghttp2_stream *stream) {
}
} else {
if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {
- fprintf(stderr, "stream(%p) = %d, stream->queued == 0, but "
- "stream_active(stream) == %d and "
- "nghttp2_pq_size(&stream->obq) = %zu\n",
+ fprintf(stderr,
+ "stream(%p) = %d, stream->queued == 0, but "
+ "stream_active(stream) == %d and "
+ "nghttp2_pq_size(&stream->obq) = %zu\n",
stream, stream->stream_id, stream_active(stream),
nghttp2_pq_size(&stream->obq));
assert(0);
diff --git a/deps/node-inspect/CHANGELOG.md b/deps/node-inspect/CHANGELOG.md
index 41ed928e781ff6..0db3a7842eb15d 100644
--- a/deps/node-inspect/CHANGELOG.md
+++ b/deps/node-inspect/CHANGELOG.md
@@ -1,3 +1,12 @@
+### 1.11.3
+
+* [`93caa0f`](https://github.com/nodejs/node-inspect/commit/93caa0f5267c7ab452b258d3b03329a0bb5ac7f7) **docs:** Add missing oc in protocol
+* [`2d87cbe`](https://github.com/nodejs/node-inspect/commit/2d87cbe76aa968dfc1ac69d9571af1be81abd8e0) **fix:** Make --inspect-port=0 work
+* [`ebfd02e`](https://github.com/nodejs/node-inspect/commit/ebfd02ece9b642586023f7791da71defeb13d746) **chore:** Bump tap to 10.7
+* [`c07adb1`](https://github.com/nodejs/node-inspect/commit/c07adb17b164c1cf3da8d38659ea9f5d7ff42e9c) **test:** Use useful break location
+* [`94f0bf9`](https://github.com/nodejs/node-inspect/commit/94f0bf97d24c376baf3ecced2088d81715a73464) **fix:** Fix `takeHeapSnapshot()` truncation bug
+
+
### 1.11.2
* [`42e0cd1`](https://github.com/nodejs/node-inspect/commit/42e0cd111d89ed09faba1c0ec45089b0b44de011) **fix:** look for generic hint text
diff --git a/deps/node-inspect/README.md b/deps/node-inspect/README.md
index ecd939b3ea26a8..b52cc188a62f5b 100644
--- a/deps/node-inspect/README.md
+++ b/deps/node-inspect/README.md
@@ -10,7 +10,7 @@ node has two options:
1. `node --debug `: Start `file` with remote debugging enabled.
2. `node debug `: Start an interactive CLI debugger for ``.
-But for the Chrome inspector protol,
+But for the Chrome inspector protocol,
there's only one: `node --inspect `.
This project tries to provide the missing second option
diff --git a/deps/node-inspect/lib/_inspect.js b/deps/node-inspect/lib/_inspect.js
index 26912274cdaec4..d846efbe6a4a52 100644
--- a/deps/node-inspect/lib/_inspect.js
+++ b/deps/node-inspect/lib/_inspect.js
@@ -42,18 +42,9 @@ const [ InspectClient, createRepl ] =
const debuglog = util.debuglog('inspect');
-const DEBUG_PORT_PATTERN = /^--(?:debug|inspect)(?:-port|-brk)?=(\d{1,5})$/;
-function getDefaultPort() {
- for (const arg of process.execArgv) {
- const match = arg.match(DEBUG_PORT_PATTERN);
- if (match) {
- return +match[1];
- }
- }
- return 9229;
-}
-
function portIsFree(host, port, timeout = 2000) {
+ if (port === 0) return Promise.resolve(); // Binding to a random port.
+
const retryDelay = 150;
let didTimeOut = false;
@@ -110,9 +101,11 @@ function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) {
let output = '';
function waitForListenHint(text) {
output += text;
- if (/Debugger listening on/.test(output)) {
+ if (/Debugger listening on ws:\/\/\[?(.+?)\]?:(\d+)\//.test(output)) {
+ const host = RegExp.$1;
+ const port = Number.parseInt(RegExp.$2);
child.stderr.removeListener('data', waitForListenHint);
- resolve(child);
+ resolve([child, port, host]);
}
}
@@ -160,10 +153,11 @@ class NodeInspector {
options.port,
this.childPrint.bind(this));
} else {
- this._runScript = () => Promise.resolve(null);
+ this._runScript =
+ () => Promise.resolve([null, options.port, options.host]);
}
- this.client = new InspectClient(options.port, options.host);
+ this.client = new InspectClient();
this.domainNames = ['Debugger', 'HeapProfiler', 'Profiler', 'Runtime'];
this.domainNames.forEach((domain) => {
@@ -223,9 +217,8 @@ class NodeInspector {
run() {
this.killChild();
- const { host, port } = this.options;
- return this._runScript().then((child) => {
+ return this._runScript().then(([child, port, host]) => {
this.child = child;
let connectionAttempts = 0;
@@ -233,7 +226,7 @@ class NodeInspector {
++connectionAttempts;
debuglog('connection attempt #%d', connectionAttempts);
this.stdout.write('.');
- return this.client.connect()
+ return this.client.connect(port, host)
.then(() => {
debuglog('connection established');
this.stdout.write(' ok');
@@ -288,7 +281,7 @@ class NodeInspector {
function parseArgv([target, ...args]) {
let host = '127.0.0.1';
- let port = getDefaultPort();
+ let port = 9229;
let isRemote = false;
let script = target;
let scriptArgs = args;
diff --git a/deps/node-inspect/lib/internal/inspect_client.js b/deps/node-inspect/lib/internal/inspect_client.js
index c247e2add87706..9b8529de21aae2 100644
--- a/deps/node-inspect/lib/internal/inspect_client.js
+++ b/deps/node-inspect/lib/internal/inspect_client.js
@@ -164,12 +164,12 @@ function decodeFrameHybi17(data) {
}
class Client extends EventEmitter {
- constructor(port, host) {
+ constructor() {
super();
this.handleChunk = this._handleChunk.bind(this);
- this._port = port;
- this._host = host;
+ this._port = undefined;
+ this._host = undefined;
this.reset();
}
@@ -284,7 +284,9 @@ class Client extends EventEmitter {
});
}
- connect() {
+ connect(port, host) {
+ this._port = port;
+ this._host = host;
return this._discoverWebsocketPath()
.then((urlPath) => this._connectWebsocket(urlPath));
}
diff --git a/deps/node-inspect/lib/internal/inspect_repl.js b/deps/node-inspect/lib/internal/inspect_repl.js
index 937c1843d3a3ee..38fe4684cf6d71 100644
--- a/deps/node-inspect/lib/internal/inspect_repl.js
+++ b/deps/node-inspect/lib/internal/inspect_repl.js
@@ -900,10 +900,8 @@ function createRepl(inspector) {
return new Promise((resolve, reject) => {
const absoluteFile = Path.resolve(filename);
const writer = FS.createWriteStream(absoluteFile);
- let totalSize;
let sizeWritten = 0;
function onProgress({ done, total, finished }) {
- totalSize = total;
if (finished) {
print('Heap snaphost prepared.');
} else {
@@ -913,13 +911,18 @@ function createRepl(inspector) {
function onChunk({ chunk }) {
sizeWritten += chunk.length;
writer.write(chunk);
- print(`Writing snapshot: ${sizeWritten}/${totalSize}`, true);
- if (sizeWritten >= totalSize) {
- writer.end();
+ print(`Writing snapshot: ${sizeWritten}`, true);
+ }
+ function onResolve() {
+ writer.end(() => {
teardown();
print(`Wrote snapshot: ${absoluteFile}`);
resolve();
- }
+ });
+ }
+ function onReject(error) {
+ teardown();
+ reject(error);
}
function teardown() {
HeapProfiler.removeListener(
@@ -932,10 +935,7 @@ function createRepl(inspector) {
print('Heap snapshot: 0/0', true);
HeapProfiler.takeHeapSnapshot({ reportProgress: true })
- .then(null, (error) => {
- teardown();
- reject(error);
- });
+ .then(onResolve, onReject);
});
},
diff --git a/deps/node-inspect/package.json b/deps/node-inspect/package.json
index 070abfa8fe51be..d25376b5d4bb96 100644
--- a/deps/node-inspect/package.json
+++ b/deps/node-inspect/package.json
@@ -1,6 +1,6 @@
{
"name": "node-inspect",
- "version": "1.11.2",
+ "version": "1.11.3",
"description": "Node Inspect",
"license": "MIT",
"main": "lib/_inspect.js",
@@ -29,7 +29,7 @@
"devDependencies": {
"eslint": "^3.10.2",
"nlm": "^3.0.0",
- "tap": "^7.1.2"
+ "tap": "^10.7.0"
},
"author": {
"name": "Jan Krems",
diff --git a/deps/node-inspect/test/cli/break.test.js b/deps/node-inspect/test/cli/break.test.js
index 59b12cde388c01..ce8c8d6d7d99bd 100644
--- a/deps/node-inspect/test/cli/break.test.js
+++ b/deps/node-inspect/test/cli/break.test.js
@@ -134,7 +134,7 @@ test('sb before loading file', (t) => {
return cli.waitForInitialBreak()
.then(() => cli.waitForPrompt())
- .then(() => cli.command('sb("other.js", 3)'))
+ .then(() => cli.command('sb("other.js", 2)'))
.then(() => {
t.match(
cli.output,
@@ -145,7 +145,7 @@ test('sb before loading file', (t) => {
.then(() => {
t.match(
cli.output,
- `break in ${otherScript}:3`,
+ `break in ${otherScript}:2`,
'found breakpoint in file that was not loaded yet');
})
.then(() => cli.quit())
diff --git a/deps/node-inspect/test/cli/heap-profiler.test.js b/deps/node-inspect/test/cli/heap-profiler.test.js
new file mode 100644
index 00000000000000..ebd734e03cb06d
--- /dev/null
+++ b/deps/node-inspect/test/cli/heap-profiler.test.js
@@ -0,0 +1,34 @@
+'use strict';
+const { test } = require('tap');
+const { readFileSync, unlinkSync } = require('fs');
+
+const startCLI = require('./start-cli');
+const filename = 'node.heapsnapshot';
+
+function cleanup() {
+ try {
+ unlinkSync(filename);
+ } catch (_) {
+ // Ignore.
+ }
+}
+
+cleanup();
+
+test('Heap profiler take snapshot', (t) => {
+ const cli = startCLI(['examples/empty.js']);
+
+ function onFatal(error) {
+ cli.quit();
+ throw error;
+ }
+
+ // Check that the snapshot is valid JSON.
+ return cli.waitForInitialBreak()
+ .then(() => cli.waitForPrompt())
+ .then(() => cli.command('takeHeapSnapshot()'))
+ .then(() => JSON.parse(readFileSync(filename, 'utf8')))
+ .then(() => cleanup())
+ .then(() => cli.quit())
+ .then(null, onFatal);
+});
diff --git a/deps/node-inspect/test/cli/launch.test.js b/deps/node-inspect/test/cli/launch.test.js
index f7efc6eb3f2139..8808d47a08b900 100644
--- a/deps/node-inspect/test/cli/launch.test.js
+++ b/deps/node-inspect/test/cli/launch.test.js
@@ -26,6 +26,46 @@ test('custom port', (t) => {
});
});
+test('random port', (t) => {
+ const script = Path.join('examples', 'three-lines.js');
+
+ const cli = startCLI(['--port=0', script]);
+
+ return cli.waitForInitialBreak()
+ .then(() => cli.waitForPrompt())
+ .then(() => {
+ t.match(cli.output, 'debug>', 'prints a prompt');
+ t.match(
+ cli.output,
+ /< Debugger listening on /,
+ 'forwards child output');
+ })
+ .then(() => cli.quit())
+ .then((code) => {
+ t.equal(code, 0, 'exits with success');
+ });
+});
+
+test('random port with --inspect-port=0', (t) => {
+ const script = Path.join('examples', 'three-lines.js');
+
+ const cli = startCLI([script], ['--inspect-port=0']);
+
+ return cli.waitForInitialBreak()
+ .then(() => cli.waitForPrompt())
+ .then(() => {
+ t.match(cli.output, 'debug>', 'prints a prompt');
+ t.match(
+ cli.output,
+ /< Debugger listening on /,
+ 'forwards child output');
+ })
+ .then(() => cli.quit())
+ .then((code) => {
+ t.equal(code, 0, 'exits with success');
+ });
+});
+
test('examples/three-lines.js', (t) => {
const script = Path.join('examples', 'three-lines.js');
const cli = startCLI([script]);
diff --git a/deps/node-inspect/test/cli/start-cli.js b/deps/node-inspect/test/cli/start-cli.js
index ae904308e02270..b086dcd8ba218d 100644
--- a/deps/node-inspect/test/cli/start-cli.js
+++ b/deps/node-inspect/test/cli/start-cli.js
@@ -16,8 +16,8 @@ const BREAK_MESSAGE = new RegExp('(?:' + [
'exception', 'other', 'promiseRejection',
].join('|') + ') in', 'i');
-function startCLI(args) {
- const child = spawn(process.execPath, [CLI, ...args]);
+function startCLI(args, flags = []) {
+ const child = spawn(process.execPath, [...flags, CLI, ...args]);
let isFirstStdoutChunk = true;
const outputBuffer = [];
diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h
index 25ce5d071e71c3..84c6717e6b486d 100644
--- a/deps/v8/include/v8-version.h
+++ b/deps/v8/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 6
#define V8_MINOR_VERSION 2
#define V8_BUILD_NUMBER 414
-#define V8_PATCH_LEVEL 50
+#define V8_PATCH_LEVEL 54
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc
index 078796f5dbb4f2..76b095eb422712 100644
--- a/deps/v8/src/api.cc
+++ b/deps/v8/src/api.cc
@@ -5190,6 +5190,7 @@ Local v8::Object::CreationContext() {
int v8::Object::GetIdentityHash() {
+ i::DisallowHeapAllocation no_gc;
auto isolate = Utils::OpenHandle(this)->GetIsolate();
i::HandleScope scope(isolate);
auto self = Utils::OpenHandle(this);
diff --git a/deps/v8/src/code-stub-assembler.cc b/deps/v8/src/code-stub-assembler.cc
index 35261955db2576..915f507b12689c 100644
--- a/deps/v8/src/code-stub-assembler.cc
+++ b/deps/v8/src/code-stub-assembler.cc
@@ -1187,8 +1187,8 @@ TNode CodeStubAssembler::LoadHashForJSObject(
{
Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField(
properties_or_hash, PropertyArray::kLengthAndHashOffset);
- var_hash.Bind(Word32And(length_and_hash_int32,
- Int32Constant(PropertyArray::kHashMask)));
+ var_hash.Bind(
+ DecodeWord32(length_and_hash_int32));
Goto(&done);
}
@@ -2508,7 +2508,8 @@ void CodeStubAssembler::InitializePropertyArrayLength(Node* property_array,
CSA_ASSERT(
this,
IntPtrOrSmiLessThanOrEqual(
- length, IntPtrOrSmiConstant(PropertyArray::kMaxLength, mode), mode));
+ length, IntPtrOrSmiConstant(PropertyArray::LengthField::kMax, mode),
+ mode));
StoreObjectFieldNoWriteBarrier(
property_array, PropertyArray::kLengthAndHashOffset,
ParameterToTagged(length, mode), MachineRepresentation::kTaggedSigned);
diff --git a/deps/v8/src/compiler/js-native-context-specialization.cc b/deps/v8/src/compiler/js-native-context-specialization.cc
index dbe3fc9608216b..57d23589ff4f53 100644
--- a/deps/v8/src/compiler/js-native-context-specialization.cc
+++ b/deps/v8/src/compiler/js-native-context-specialization.cc
@@ -2255,14 +2255,18 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
jsgraph()->SmiConstant(PropertyArray::kNoHashSentinel));
hash = graph()->NewNode(common()->TypeGuard(Type::SignedSmall()), hash,
control);
+ hash =
+ graph()->NewNode(simplified()->NumberShiftLeft(), hash,
+ jsgraph()->Constant(PropertyArray::HashField::kShift));
} else {
hash = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForPropertyArrayLengthAndHash()),
properties, effect, control);
effect = graph()->NewNode(
common()->BeginRegion(RegionObservability::kNotObservable), effect);
- hash = graph()->NewNode(simplified()->NumberBitwiseAnd(), hash,
- jsgraph()->Constant(JSReceiver::kHashMask));
+ hash =
+ graph()->NewNode(simplified()->NumberBitwiseAnd(), hash,
+ jsgraph()->Constant(PropertyArray::HashField::kMask));
}
Node* new_length_and_hash = graph()->NewNode(
diff --git a/deps/v8/src/deoptimizer.cc b/deps/v8/src/deoptimizer.cc
index 93a21a7b3adf5c..21b36ba49c9149 100644
--- a/deps/v8/src/deoptimizer.cc
+++ b/deps/v8/src/deoptimizer.cc
@@ -144,6 +144,50 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm,
generator.Generate();
}
+namespace {
+class ActivationsFinder : public ThreadVisitor {
+ public:
+ explicit ActivationsFinder(std::set* codes,
+ Code* topmost_optimized_code,
+ bool safe_to_deopt_topmost_optimized_code)
+ : codes_(codes) {
+#ifdef DEBUG
+ topmost_ = topmost_optimized_code;
+ safe_to_deopt_ = safe_to_deopt_topmost_optimized_code;
+#endif
+ }
+
+ // Find the frames with activations of codes marked for deoptimization, search
+ // for the trampoline to the deoptimizer call respective to each code, and use
+ // it to replace the current pc on the stack.
+ void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
+ for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
+ if (it.frame()->type() == StackFrame::OPTIMIZED) {
+ Code* code = it.frame()->LookupCode();
+ if (code->kind() == Code::OPTIMIZED_FUNCTION &&
+ code->marked_for_deoptimization()) {
+ codes_->erase(code);
+ // Obtain the trampoline to the deoptimizer call.
+ SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc());
+ int trampoline_pc = safepoint.trampoline_pc();
+ DCHECK_IMPLIES(code == topmost_, safe_to_deopt_);
+ // Replace the current pc on the stack with the trampoline.
+ it.frame()->set_pc(code->instruction_start() + trampoline_pc);
+ }
+ }
+ }
+ }
+
+ private:
+ std::set* codes_;
+
+#ifdef DEBUG
+ Code* topmost_;
+ bool safe_to_deopt_;
+#endif
+};
+} // namespace
+
void Deoptimizer::VisitAllOptimizedFunctionsForContext(
Context* context, OptimizedFunctionVisitor* visitor) {
DisallowHeapAllocation no_allocation;
@@ -264,9 +308,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
VisitAllOptimizedFunctionsForContext(context, &unlinker);
Isolate* isolate = context->GetHeap()->isolate();
-#ifdef DEBUG
Code* topmost_optimized_code = NULL;
bool safe_to_deopt_topmost_optimized_code = false;
+#ifdef DEBUG
// Make sure all activations of optimized code can deopt at their current PC.
// The topmost optimized code has special handling because it cannot be
// deoptimized due to weak object dependency.
@@ -304,6 +348,10 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
}
#endif
+ // We will use this set to mark those Code objects that are marked for
+ // deoptimization and have not been found in stack frames.
+ std::set codes;
+
// Move marked code from the optimized code list to the deoptimized
// code list.
// Walk over all optimized code objects in this native context.
@@ -335,25 +383,14 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
element = next;
}
- // Finds the with activations of codes marked for deoptimization, search for
- // the trampoline to the deoptimizer call respective to each code, and use it
- // to replace the current pc on the stack.
- for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done();
- it.Advance()) {
- if (it.frame()->type() == StackFrame::OPTIMIZED) {
- Code* code = it.frame()->LookupCode();
- if (code->kind() == Code::OPTIMIZED_FUNCTION &&
- code->marked_for_deoptimization()) {
- // Obtain the trampoline to the deoptimizer call.
- SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc());
- int trampoline_pc = safepoint.trampoline_pc();
- DCHECK_IMPLIES(code == topmost_optimized_code,
- safe_to_deopt_topmost_optimized_code);
- // Replace the current pc on the stack with the trampoline.
- it.frame()->set_pc(code->instruction_start() + trampoline_pc);
- }
- }
- }
+ ActivationsFinder visitor(&codes, topmost_optimized_code,
+ safe_to_deopt_topmost_optimized_code);
+ // Iterate over the stack of this thread.
+ visitor.VisitThread(isolate, isolate->thread_local_top());
+ // In addition to iterate over the stack of this thread, we also
+ // need to consider all the other threads as they may also use
+ // the code currently beings deoptimized.
+ isolate->thread_manager()->IterateArchivedThreads(&visitor);
}
diff --git a/deps/v8/src/ic/accessor-assembler.cc b/deps/v8/src/ic/accessor-assembler.cc
index 1dee1303366e74..8985bad15e9f64 100644
--- a/deps/v8/src/ic/accessor-assembler.cc
+++ b/deps/v8/src/ic/accessor-assembler.cc
@@ -1091,7 +1091,7 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object,
// TODO(gsathya): Clean up the type conversions by creating smarter
// helpers that do the correct op based on the mode.
VARIABLE(var_properties, MachineRepresentation::kTaggedPointer);
- VARIABLE(var_hash, MachineRepresentation::kWord32);
+ VARIABLE(var_encoded_hash, MachineRepresentation::kWord32);
VARIABLE(var_length, ParameterRepresentation(mode));
Node* properties = LoadObjectField(object, JSObject::kPropertiesOrHashOffset);
@@ -1102,7 +1102,10 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object,
BIND(&if_smi_hash);
{
- var_hash.Bind(SmiToWord32(properties));
+ Node* hash = SmiToWord32(properties);
+ Node* encoded_hash =
+ Word32Shl(hash, Int32Constant(PropertyArray::HashField::kShift));
+ var_encoded_hash.Bind(encoded_hash);
var_length.Bind(IntPtrOrSmiConstant(0, mode));
var_properties.Bind(EmptyFixedArrayConstant());
Goto(&extend_store);
@@ -1112,10 +1115,11 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object,
{
Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField(
var_properties.value(), PropertyArray::kLengthAndHashOffset);
- var_hash.Bind(Word32And(length_and_hash_int32,
- Int32Constant(PropertyArray::kHashMask)));
- Node* length_intptr = ChangeInt32ToIntPtr(Word32And(
- length_and_hash_int32, Int32Constant(PropertyArray::kLengthMask)));
+ var_encoded_hash.Bind(Word32And(
+ length_and_hash_int32, Int32Constant(PropertyArray::HashField::kMask)));
+ Node* length_intptr = ChangeInt32ToIntPtr(
+ Word32And(length_and_hash_int32,
+ Int32Constant(PropertyArray::LengthField::kMask)));
Node* length = WordToParameter(length_intptr, mode);
var_length.Bind(length);
Goto(&extend_store);
@@ -1161,7 +1165,7 @@ void AccessorAssembler::ExtendPropertiesBackingStore(Node* object,
Node* new_capacity_int32 =
TruncateWordToWord32(ParameterToWord(new_capacity, mode));
Node* new_length_and_hash_int32 =
- Word32Or(var_hash.value(), new_capacity_int32);
+ Word32Or(var_encoded_hash.value(), new_capacity_int32);
StoreObjectField(new_properties, PropertyArray::kLengthAndHashOffset,
SmiFromWord32(new_length_and_hash_int32));
StoreObjectField(object, JSObject::kPropertiesOrHashOffset, new_properties);
diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h
index 82b7eb05a6e7b4..91fd08a2dd58c8 100644
--- a/deps/v8/src/objects-inl.h
+++ b/deps/v8/src/objects-inl.h
@@ -2679,33 +2679,31 @@ SYNCHRONIZED_SMI_ACCESSORS(FixedArrayBase, length, kLengthOffset)
int PropertyArray::length() const {
Object* value_obj = READ_FIELD(this, kLengthAndHashOffset);
int value = Smi::ToInt(value_obj);
- return value & kLengthMask;
+ return LengthField::decode(value);
}
void PropertyArray::initialize_length(int len) {
SLOW_DCHECK(len >= 0);
- SLOW_DCHECK(len < kMaxLength);
+ SLOW_DCHECK(len < LengthField::kMax);
WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(len));
}
int PropertyArray::synchronized_length() const {
Object* value_obj = ACQUIRE_READ_FIELD(this, kLengthAndHashOffset);
int value = Smi::ToInt(value_obj);
- return value & kLengthMask;
+ return LengthField::decode(value);
}
int PropertyArray::Hash() const {
Object* value_obj = READ_FIELD(this, kLengthAndHashOffset);
int value = Smi::ToInt(value_obj);
- int hash = value & kHashMask;
- return hash;
+ return HashField::decode(value);
}
-void PropertyArray::SetHash(int masked_hash) {
- DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash);
+void PropertyArray::SetHash(int hash) {
Object* value_obj = READ_FIELD(this, kLengthAndHashOffset);
int value = Smi::ToInt(value_obj);
- value = (value & kLengthMask) | masked_hash;
+ value = HashField::update(value, hash);
WRITE_FIELD(this, kLengthAndHashOffset, Smi::FromInt(value));
}
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 28c1cd681ffd46..b2b23c1f68f216 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -2301,6 +2301,7 @@ namespace {
// objects. This avoids a double lookup in the cases where we know we will
// add the hash to the JSObject if it does not already exist.
Object* GetSimpleHash(Object* object) {
+ DisallowHeapAllocation no_gc;
// The object is either a Smi, a HeapNumber, a name, an odd-ball, a real JS
// object, or a Harmony proxy.
if (object->IsSmi()) {
@@ -2333,10 +2334,10 @@ Object* GetSimpleHash(Object* object) {
} // namespace
Object* Object::GetHash() {
+ DisallowHeapAllocation no_gc;
Object* hash = GetSimpleHash(this);
if (hash->IsSmi()) return hash;
- DisallowHeapAllocation no_gc;
DCHECK(IsJSReceiver());
JSReceiver* receiver = JSReceiver::cast(this);
Isolate* isolate = receiver->GetIsolate();
@@ -2345,10 +2346,12 @@ Object* Object::GetHash() {
// static
Smi* Object::GetOrCreateHash(Isolate* isolate, Object* key) {
+ DisallowHeapAllocation no_gc;
return key->GetOrCreateHash(isolate);
}
Smi* Object::GetOrCreateHash(Isolate* isolate) {
+ DisallowHeapAllocation no_gc;
Object* hash = GetSimpleHash(this);
if (hash->IsSmi()) return Smi::cast(hash);
@@ -6266,26 +6269,27 @@ Handle JSObject::NormalizeElements(
namespace {
-Object* SetHashAndUpdateProperties(HeapObject* properties, int masked_hash) {
- DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash);
- DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash);
+Object* SetHashAndUpdateProperties(HeapObject* properties, int hash) {
+ DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
+ DCHECK(PropertyArray::HashField::is_valid(hash));
if (properties == properties->GetHeap()->empty_fixed_array() ||
properties == properties->GetHeap()->empty_property_dictionary()) {
- return Smi::FromInt(masked_hash);
+ return Smi::FromInt(hash);
}
if (properties->IsPropertyArray()) {
- PropertyArray::cast(properties)->SetHash(masked_hash);
+ PropertyArray::cast(properties)->SetHash(hash);
return properties;
}
DCHECK(properties->IsDictionary());
- NameDictionary::cast(properties)->SetHash(masked_hash);
+ NameDictionary::cast(properties)->SetHash(hash);
return properties;
}
int GetIdentityHashHelper(Isolate* isolate, JSReceiver* object) {
+ DisallowHeapAllocation no_gc;
Object* properties = object->raw_properties_or_hash();
if (properties->IsSmi()) {
return Smi::ToInt(properties);
@@ -6311,17 +6315,19 @@ int GetIdentityHashHelper(Isolate* isolate, JSReceiver* object) {
}
} // namespace
-void JSReceiver::SetIdentityHash(int masked_hash) {
- DCHECK_NE(PropertyArray::kNoHashSentinel, masked_hash);
- DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash);
+void JSReceiver::SetIdentityHash(int hash) {
+ DisallowHeapAllocation no_gc;
+ DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
+ DCHECK(PropertyArray::HashField::is_valid(hash));
HeapObject* existing_properties = HeapObject::cast(raw_properties_or_hash());
Object* new_properties =
- SetHashAndUpdateProperties(existing_properties, masked_hash);
+ SetHashAndUpdateProperties(existing_properties, hash);
set_raw_properties_or_hash(new_properties);
}
void JSReceiver::SetProperties(HeapObject* properties) {
+ DisallowHeapAllocation no_gc;
Isolate* isolate = properties->GetIsolate();
int hash = GetIdentityHashHelper(isolate, this);
Object* new_properties = properties;
@@ -6337,6 +6343,7 @@ void JSReceiver::SetProperties(HeapObject* properties) {
template
Smi* GetOrCreateIdentityHashHelper(Isolate* isolate, ProxyType* proxy) {
+ DisallowHeapAllocation no_gc;
Object* maybe_hash = proxy->hash();
if (maybe_hash->IsSmi()) return Smi::cast(maybe_hash);
@@ -6346,6 +6353,7 @@ Smi* GetOrCreateIdentityHashHelper(Isolate* isolate, ProxyType* proxy) {
}
Object* JSObject::GetIdentityHash(Isolate* isolate) {
+ DisallowHeapAllocation no_gc;
if (IsJSGlobalProxy()) {
return JSGlobalProxy::cast(this)->hash();
}
@@ -6359,6 +6367,7 @@ Object* JSObject::GetIdentityHash(Isolate* isolate) {
}
Smi* JSObject::GetOrCreateIdentityHash(Isolate* isolate) {
+ DisallowHeapAllocation no_gc;
if (IsJSGlobalProxy()) {
return GetOrCreateIdentityHashHelper(isolate, JSGlobalProxy::cast(this));
}
@@ -6368,16 +6377,11 @@ Smi* JSObject::GetOrCreateIdentityHash(Isolate* isolate) {
return Smi::cast(hash_obj);
}
- int masked_hash;
- // TODO(gsathya): Remove the loop and pass kHashMask directly to
- // GenerateIdentityHash.
- do {
- int hash = isolate->GenerateIdentityHash(Smi::kMaxValue);
- masked_hash = hash & JSReceiver::kHashMask;
- } while (masked_hash == PropertyArray::kNoHashSentinel);
+ int hash = isolate->GenerateIdentityHash(PropertyArray::HashField::kMax);
+ DCHECK_NE(PropertyArray::kNoHashSentinel, hash);
- SetIdentityHash(masked_hash);
- return Smi::FromInt(masked_hash);
+ SetIdentityHash(hash);
+ return Smi::FromInt(hash);
}
Object* JSProxy::GetIdentityHash() { return hash(); }
diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h
index f9987c2837c466..00a8d0da02220e 100644
--- a/deps/v8/src/objects.h
+++ b/deps/v8/src/objects.h
@@ -1953,12 +1953,10 @@ class PropertyArray : public HeapObject {
// No weak fields.
typedef BodyDescriptor BodyDescriptorWeak;
- static const int kLengthMask = 0x3ff;
- static const int kHashMask = 0x7ffffc00;
- STATIC_ASSERT(kLengthMask + kHashMask == 0x7fffffff);
-
- static const int kMaxLength = kLengthMask;
- STATIC_ASSERT(kMaxLength > kMaxNumberOfDescriptors);
+ static const int kLengthFieldSize = 10;
+ class LengthField : public BitField {};
+ class HashField : public BitField {};
static const int kNoHashSentinel = 0;
@@ -2185,7 +2183,7 @@ class JSReceiver: public HeapObject {
MUST_USE_RESULT static MaybeHandle GetOwnEntries(
Handle object, PropertyFilter filter);
- static const int kHashMask = PropertyArray::kHashMask;
+ static const int kHashMask = PropertyArray::HashField::kMask;
// Layout description.
static const int kPropertiesOrHashOffset = HeapObject::kHeaderSize;
diff --git a/deps/v8/src/objects/dictionary.h b/deps/v8/src/objects/dictionary.h
index a989c8fc8a2e53..09fdb9eb0764a2 100644
--- a/deps/v8/src/objects/dictionary.h
+++ b/deps/v8/src/objects/dictionary.h
@@ -138,14 +138,16 @@ class BaseNameDictionary : public Dictionary {
return Smi::ToInt(this->get(kNextEnumerationIndexIndex));
}
- void SetHash(int masked_hash) {
- DCHECK_EQ(masked_hash & JSReceiver::kHashMask, masked_hash);
- this->set(kObjectHashIndex, Smi::FromInt(masked_hash));
+ void SetHash(int hash) {
+ DCHECK(PropertyArray::HashField::is_valid(hash));
+ this->set(kObjectHashIndex, Smi::FromInt(hash));
}
int Hash() const {
Object* hash_obj = this->get(kObjectHashIndex);
- return Smi::ToInt(hash_obj);
+ int hash = Smi::ToInt(hash_obj);
+ DCHECK(PropertyArray::HashField::is_valid(hash));
+ return hash;
}
// Creates a new dictionary.
diff --git a/deps/v8/src/profiler/profiler-listener.cc b/deps/v8/src/profiler/profiler-listener.cc
index 540d93002459fb..b90f5a4894fc76 100644
--- a/deps/v8/src/profiler/profiler-listener.cc
+++ b/deps/v8/src/profiler/profiler-listener.cc
@@ -226,11 +226,18 @@ void ProfilerListener::RecordInliningInfo(CodeEntry* entry,
SharedFunctionInfo* shared_info = SharedFunctionInfo::cast(
deopt_input_data->LiteralArray()->get(shared_info_id));
if (!depth++) continue; // Skip the current function itself.
- CodeEntry* inline_entry = new CodeEntry(
- entry->tag(), GetFunctionName(shared_info->DebugName()),
- CodeEntry::kEmptyNamePrefix, entry->resource_name(),
- CpuProfileNode::kNoLineNumberInfo,
- CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
+ const char* resource_name =
+ (shared_info->script()->IsScript() &&
+ Script::cast(shared_info->script())->name()->IsName())
+ ? GetName(Name::cast(Script::cast(shared_info->script())->name()))
+ : CodeEntry::kEmptyResourceName;
+
+ CodeEntry* inline_entry =
+ new CodeEntry(entry->tag(), GetFunctionName(shared_info->DebugName()),
+ CodeEntry::kEmptyNamePrefix, resource_name,
+ CpuProfileNode::kNoLineNumberInfo,
+ CpuProfileNode::kNoColumnNumberInfo, nullptr,
+ code->instruction_start());
inline_entry->FillFunctionInfo(shared_info);
inline_stack.push_back(inline_entry);
}
diff --git a/deps/v8/src/v8.gyp b/deps/v8/src/v8.gyp
index bf7635ee339e2a..1c56842f81a548 100644
--- a/deps/v8/src/v8.gyp
+++ b/deps/v8/src/v8.gyp
@@ -2044,9 +2044,10 @@
'-L/usr/local/lib -lexecinfo',
]},
'sources': [
+ 'base/debug/stack_trace_posix.cc',
'base/platform/platform-openbsd.cc',
'base/platform/platform-posix.h',
- 'base/platform/platform-posix.cc'
+ 'base/platform/platform-posix.cc',
'base/platform/platform-posix-time.h',
'base/platform/platform-posix-time.cc',
],
diff --git a/deps/v8/test/cctest/test-cpu-profiler.cc b/deps/v8/test/cctest/test-cpu-profiler.cc
index f22a42a977d5be..b441d04fdd31db 100644
--- a/deps/v8/test/cctest/test-cpu-profiler.cc
+++ b/deps/v8/test/cctest/test-cpu-profiler.cc
@@ -1745,6 +1745,85 @@ TEST(FunctionDetails) {
script_a->GetUnboundScript()->GetId(), 5, 14);
}
+TEST(FunctionDetailsInlining) {
+ if (!CcTest::i_isolate()->use_optimizer() || i::FLAG_always_opt) return;
+ i::FLAG_allow_natives_syntax = true;
+ v8::HandleScope scope(CcTest::isolate());
+ v8::Local env = CcTest::NewContext(PROFILER_EXTENSION);
+ v8::Context::Scope context_scope(env);
+ ProfilerHelper helper(env);
+
+ // alpha is in a_script, beta in b_script. beta is
+ // inlined in alpha, but it should be attributed to b_script.
+
+ v8::Local script_b = CompileWithOrigin(
+ "function beta(k) {\n"
+ " let sum = 2;\n"
+ " for(let i = 0; i < k; i ++) {\n"
+ " sum += i;\n"
+ " sum = sum + 'a';\n"
+ " }\n"
+ " return sum;\n"
+ "}\n"
+ "\n",
+ "script_b");
+
+ v8::Local script_a = CompileWithOrigin(
+ "function alpha(p) {\n"
+ " let res = beta(p);\n"
+ " res = res + res;\n"
+ " return res;\n"
+ "}\n"
+ "let p = 2;\n"
+ "\n"
+ "\n"
+ "// Warm up before profiling or the inlining doesn't happen.\n"
+ "p = alpha(p);\n"
+ "p = alpha(p);\n"
+ "%OptimizeFunctionOnNextCall(alpha);\n"
+ "p = alpha(p);\n"
+ "\n"
+ "\n"
+ "startProfiling();\n"
+ "for(let i = 0; i < 10000; i++) {\n"
+ " p = alpha(p);\n"
+ "}\n"
+ "stopProfiling();\n"
+ "\n"
+ "\n",
+ "script_a");
+
+ script_b->Run(env).ToLocalChecked();
+ script_a->Run(env).ToLocalChecked();
+
+ const v8::CpuProfile* profile = i::ProfilerExtension::last_profile;
+ const v8::CpuProfileNode* current = profile->GetTopDownRoot();
+ reinterpret_cast(const_cast(current))
+ ->Print(0);
+ // The tree should look like this:
+ // 0 (root) 0 #1
+ // 5 (program) 0 #6
+ // 2 14 #2 script_a:1
+ // ;;; deopted at script_id: 14 position: 299 with reason 'Insufficient
+ // type feedback for call'.
+ // 1 alpha 14 #4 script_a:1
+ // 9 beta 13 #5 script_b:0
+ // 0 startProfiling 0 #3
+
+ const v8::CpuProfileNode* root = profile->GetTopDownRoot();
+ const v8::CpuProfileNode* script = GetChild(env, root, "");
+ CheckFunctionDetails(env->GetIsolate(), script, "", "script_a",
+ script_a->GetUnboundScript()->GetId(), 1, 1);
+ const v8::CpuProfileNode* alpha = FindChild(env, script, "alpha");
+ // Return early if profiling didn't sample alpha.
+ if (!alpha) return;
+ CheckFunctionDetails(env->GetIsolate(), alpha, "alpha", "script_a",
+ script_a->GetUnboundScript()->GetId(), 1, 15);
+ const v8::CpuProfileNode* beta = FindChild(env, alpha, "beta");
+ if (!beta) return;
+ CheckFunctionDetails(env->GetIsolate(), beta, "beta", "script_b",
+ script_b->GetUnboundScript()->GetId(), 0, 0);
+}
TEST(DontStopOnFinishedProfileDelete) {
v8::HandleScope scope(CcTest::isolate());
diff --git a/deps/v8/test/cctest/test-lockers.cc b/deps/v8/test/cctest/test-lockers.cc
index a310bfd68456c4..36a9f11ee27773 100644
--- a/deps/v8/test/cctest/test-lockers.cc
+++ b/deps/v8/test/cctest/test-lockers.cc
@@ -55,6 +55,244 @@ using ::v8::Value;
using ::v8::V8;
+namespace {
+
+class DeoptimizeCodeThread : public v8::base::Thread {
+ public:
+ DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local context,
+ const char* trigger)
+ : Thread(Options("DeoptimizeCodeThread")),
+ isolate_(isolate),
+ context_(isolate, context),
+ source_(trigger) {}
+
+ void Run() {
+ v8::Locker locker(isolate_);
+ isolate_->Enter();
+ v8::HandleScope handle_scope(isolate_);
+ v8::Local context =
+ v8::Local::New(isolate_, context_);
+ v8::Context::Scope context_scope(context);
+ CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
+ // This code triggers deoptimization of some function that will be
+ // used in a different thread.
+ CompileRun(source_);
+ isolate_->Exit();
+ }
+
+ private:
+ v8::Isolate* isolate_;
+ Persistent context_;
+ // The code that triggers the deoptimization.
+ const char* source_;
+};
+
+void UnlockForDeoptimization(const v8::FunctionCallbackInfo& args) {
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ // Gets the pointer to the thread that will trigger the deoptimization of the
+ // code.
+ DeoptimizeCodeThread* deoptimizer =
+ reinterpret_cast(isolate->GetData(0));
+ {
+ // Exits and unlocks the isolate.
+ isolate->Exit();
+ v8::Unlocker unlocker(isolate);
+ // Starts the deoptimizing thread.
+ deoptimizer->Start();
+ // Waits for deoptimization to finish.
+ deoptimizer->Join();
+ }
+ // The deoptimizing thread has finished its work, and the isolate
+ // will now be used by the current thread.
+ isolate->Enter();
+}
+
+void UnlockForDeoptimizationIfReady(
+ const v8::FunctionCallbackInfo& args) {
+ v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ bool* ready_to_deoptimize = reinterpret_cast(isolate->GetData(1));
+ if (*ready_to_deoptimize) {
+ // The test should enter here only once, so put the flag back to false.
+ *ready_to_deoptimize = false;
+ // Gets the pointer to the thread that will trigger the deoptimization of
+ // the code.
+ DeoptimizeCodeThread* deoptimizer =
+ reinterpret_cast(isolate->GetData(0));
+ {
+ // Exits and unlocks the thread.
+ isolate->Exit();
+ v8::Unlocker unlocker(isolate);
+ // Starts the thread that deoptimizes the function.
+ deoptimizer->Start();
+ // Waits for the deoptimizing thread to finish.
+ deoptimizer->Join();
+ }
+ // The deoptimizing thread has finished its work, and the isolate
+ // will now be used by the current thread.
+ isolate->Enter();
+ }
+}
+} // namespace
+
+TEST(LazyDeoptimizationMultithread) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
+ v8::Isolate* isolate = v8::Isolate::New(create_params);
+ {
+ v8::Locker locker(isolate);
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope scope(isolate);
+ v8::Local context = v8::Context::New(isolate);
+ const char* trigger_deopt = "obj = { y: 0, x: 1 };";
+
+ // We use the isolate to pass arguments to the UnlockForDeoptimization
+ // function. Namely, we pass a pointer to the deoptimizing thread.
+ DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
+ isolate->SetData(0, &deoptimize_thread);
+ v8::Context::Scope context_scope(context);
+
+ // Create the function templace for C++ code that is invoked from
+ // JavaScript code.
+ Local fun_templ =
+ v8::FunctionTemplate::New(isolate, UnlockForDeoptimization);
+ Local fun = fun_templ->GetFunction(context).ToLocalChecked();
+ CHECK(context->Global()
+ ->Set(context, v8_str("unlock_for_deoptimization"), fun)
+ .FromJust());
+
+ // Optimizes a function f, which will be deoptimized in another
+ // thread.
+ CompileRun(
+ "var b = false; var obj = { x: 1 };"
+ "function f() { g(); return obj.x; }"
+ "function g() { if (b) { unlock_for_deoptimization(); } }"
+ "%NeverOptimizeFunction(g);"
+ "f(); f(); %OptimizeFunctionOnNextCall(f);"
+ "f();");
+
+ // Trigger the unlocking.
+ Local v = CompileRun("b = true; f();");
+
+ // Once the isolate has been unlocked, the thread will wait for the
+ // other thread to finish its task. Once this happens, this thread
+ // continues with its execution, that is, with the execution of the
+ // function g, which then returns to f. The function f should have
+ // also been deoptimized. If the replacement did not happen on this
+ // thread's stack, then the test will fail here.
+ CHECK(v->IsNumber());
+ CHECK_EQ(1, static_cast(v->NumberValue(context).FromJust()));
+ }
+ isolate->Dispose();
+}
+
+TEST(LazyDeoptimizationMultithreadWithNatives) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
+ v8::Isolate* isolate = v8::Isolate::New(create_params);
+ {
+ v8::Locker locker(isolate);
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope scope(isolate);
+ v8::Local context = v8::Context::New(isolate);
+ const char* trigger_deopt = "%DeoptimizeFunction(f);";
+
+ // We use the isolate to pass arguments to the UnlockForDeoptimization
+ // function. Namely, we pass a pointer to the deoptimizing thread.
+ DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
+ isolate->SetData(0, &deoptimize_thread);
+ bool ready_to_deopt = false;
+ isolate->SetData(1, &ready_to_deopt);
+ v8::Context::Scope context_scope(context);
+
+ // Create the function templace for C++ code that is invoked from
+ // JavaScript code.
+ Local fun_templ =
+ v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
+ Local fun = fun_templ->GetFunction(context).ToLocalChecked();
+ CHECK(context->Global()
+ ->Set(context, v8_str("unlock_for_deoptimization"), fun)
+ .FromJust());
+
+ // Optimizes a function f, which will be deoptimized in another
+ // thread.
+ CompileRun(
+ "var obj = { x: 1 };"
+ "function f() { g(); return obj.x;}"
+ "function g() { "
+ " unlock_for_deoptimization(); }"
+ "%NeverOptimizeFunction(g);"
+ "f(); f(); %OptimizeFunctionOnNextCall(f);");
+
+ // Trigger the unlocking.
+ ready_to_deopt = true;
+ isolate->SetData(1, &ready_to_deopt);
+ Local v = CompileRun("f();");
+
+ // Once the isolate has been unlocked, the thread will wait for the
+ // other thread to finish its task. Once this happens, this thread
+ // continues with its execution, that is, with the execution of the
+ // function g, which then returns to f. The function f should have
+ // also been deoptimized. Otherwise, the test will fail here.
+ CHECK(v->IsNumber());
+ CHECK_EQ(1, static_cast(v->NumberValue(context).FromJust()));
+ }
+ isolate->Dispose();
+}
+
+TEST(EagerDeoptimizationMultithread) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
+ v8::Isolate* isolate = v8::Isolate::New(create_params);
+ {
+ v8::Locker locker(isolate);
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope scope(isolate);
+ v8::Local context = v8::Context::New(isolate);
+ const char* trigger_deopt = "f({y: 0, x: 1});";
+
+ // We use the isolate to pass arguments to the UnlockForDeoptimization
+ // function. Namely, we pass a pointer to the deoptimizing thread.
+ DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
+ isolate->SetData(0, &deoptimize_thread);
+ bool ready_to_deopt = false;
+ isolate->SetData(1, &ready_to_deopt);
+ v8::Context::Scope context_scope(context);
+
+ // Create the function templace for C++ code that is invoked from
+ // JavaScript code.
+ Local fun_templ =
+ v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
+ Local fun = fun_templ->GetFunction(context).ToLocalChecked();
+ CHECK(context->Global()
+ ->Set(context, v8_str("unlock_for_deoptimization"), fun)
+ .FromJust());
+
+ // Optimizes a function f, which will be deoptimized by another thread.
+ CompileRun(
+ "function f(obj) { unlock_for_deoptimization(); return obj.x; }"
+ "f({x: 1}); f({x: 1});"
+ "%OptimizeFunctionOnNextCall(f);"
+ "f({x: 1});");
+
+ // Trigger the unlocking.
+ ready_to_deopt = true;
+ isolate->SetData(1, &ready_to_deopt);
+ Local v = CompileRun("f({x: 1});");
+
+ // Once the isolate has been unlocked, the thread will wait for the
+ // other thread to finish its task. Once this happens, this thread
+ // continues with its execution, that is, with the execution of the
+ // function g, which then returns to f. The function f should have
+ // also been deoptimized. Otherwise, the test will fail here.
+ CHECK(v->IsNumber());
+ CHECK_EQ(1, static_cast(v->NumberValue(context).FromJust()));
+ }
+ isolate->Dispose();
+}
+
// Migrating an isolate
class KangarooThread : public v8::base::Thread {
public:
diff --git a/deps/v8/test/cctest/test-weakmaps.cc b/deps/v8/test/cctest/test-weakmaps.cc
index 13a0c538d9b82f..f645234144c092 100644
--- a/deps/v8/test/cctest/test-weakmaps.cc
+++ b/deps/v8/test/cctest/test-weakmaps.cc
@@ -191,7 +191,7 @@ TEST(Regress2060a) {
Handle object = factory->NewJSObject(function, TENURED);
CHECK(!heap->InNewSpace(*object));
CHECK(!first_page->Contains(object->address()));
- int32_t hash = object->GetOrCreateHash(isolate)->value();
+ int32_t hash = key->GetOrCreateHash(isolate)->value();
JSWeakCollection::Set(weakmap, key, object, hash);
}
}
diff --git a/doc/api/addons.md b/doc/api/addons.md
index c6802530f6dc67..03feb8ec619ed5 100644
--- a/doc/api/addons.md
+++ b/doc/api/addons.md
@@ -101,7 +101,7 @@ Addon module name is `addon`.
Once the source code has been written, it must be compiled into the binary
`addon.node` file. To do so, create a file called `binding.gyp` in the
top-level of the project describing the build configuration of the module
-using a JSON-like format. This file is used by [node-gyp][] -- a tool written
+using a JSON-like format. This file is used by [node-gyp][] — a tool written
specifically to compile Node.js Addons.
```json
diff --git a/doc/api/assert.md b/doc/api/assert.md
index 561d59bdc2932f..a859408a8be99e 100644
--- a/doc/api/assert.md
+++ b/doc/api/assert.md
@@ -276,7 +276,7 @@ added: v0.1.21
* `expected` {any}
* `message` {any}
* `operator` {string} **Default:** '!='
-* `stackStartFunction` {function} **Default:** `assert.fail`
+* `stackStartFunction` {Function} **Default:** `assert.fail`
Throws an `AssertionError`. If `message` is falsy, the error message is set as
the values of `actual` and `expected` separated by the provided `operator`.
@@ -631,10 +631,10 @@ For more information, see
[MDN's guide on equality comparisons and sameness][mdn-equality-guide].
[`Error.captureStackTrace`]: errors.html#errors_error_capturestacktrace_targetobject_constructoropt
-[`Map`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map
+[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
[`Object.is()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
[`RegExp`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
-[`Set`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set
+[`Set`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
[`TypeError`]: errors.html#errors_class_typeerror
[`assert.deepEqual()`]: #assert_assert_deepequal_actual_expected_message
[`assert.deepStrictEqual()`]: #assert_assert_deepstrictequal_actual_expected_message
diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md
index 4fa23f28d116b0..56640060bf7616 100644
--- a/doc/api/async_hooks.md
+++ b/doc/api/async_hooks.md
@@ -86,7 +86,7 @@ added: v8.1.0
* `before` {Function} The [`before` callback][].
* `after` {Function} The [`after` callback][].
* `destroy` {Function} The [`destroy` callback][].
-* Returns: `{AsyncHook}` Instance used for disabling and enabling hooks
+* Returns: {AsyncHook} Instance used for disabling and enabling hooks
Registers functions to be called for different lifetime events of each async
operation.
@@ -509,6 +509,9 @@ const server = net.createServer(function onConnection(conn) {
});
```
+Note that promise contexts may not get precise executionAsyncIds by default.
+See the section on [promise execution tracking][].
+
#### `async_hooks.triggerAsyncId()`
* Returns: {number} The ID of the resource responsible for calling the callback
@@ -531,6 +534,57 @@ const server = net.createServer((conn) => {
});
```
+Note that promise contexts may not get valid triggerAsyncIds by default. See
+the section on [promise execution tracking][].
+
+## Promise execution tracking
+
+By default, promise executions are not assigned asyncIds due to the relatively
+expensive nature of the [promise introspection API][PromiseHooks] provided by
+V8. This means that programs using promises or `async`/`await` will not get
+correct execution and trigger ids for promise callback contexts by default.
+
+Here's an example:
+
+```js
+const ah = require('async_hooks');
+Promise.resolve(1729).then(() => {
+ console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
+});
+// produces:
+// eid 1 tid 0
+```
+
+Observe that the `then` callback claims to have executed in the context of the
+outer scope even though there was an asynchronous hop involved. Also note that
+the triggerAsyncId value is 0, which means that we are missing context about the
+resource that caused (triggered) the `then` callback to be executed.
+
+Installing async hooks via `async_hooks.createHook` enables promise execution
+tracking. Example:
+
+```js
+const ah = require('async_hooks');
+ah.createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled.
+Promise.resolve(1729).then(() => {
+ console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
+});
+// produces:
+// eid 7 tid 6
+```
+
+In this example, adding any actual hook function enabled the tracking of
+promises. There are two promises in the example above; the promise created by
+`Promise.resolve()` and the promise returned by the call to `then`. In the
+example above, the first promise got the asyncId 6 and the latter got asyncId 7.
+During the execution of the `then` callback, we are executing in the context of
+promise with asyncId 7. This promise was triggered by async resource 6.
+
+Another subtlety with promises is that `before` and `after` callbacks are run
+only on chained promises. That means promises not created by `then`/`catch` will
+not have the `before` and `after` callbacks fired on them. For more details see
+the details of the V8 [PromiseHooks][] API.
+
## JavaScript Embedder API
Library developers that handle their own asynchronous resources performing tasks
@@ -582,7 +636,7 @@ asyncResource.triggerAsyncId();
* `type` {string} The type of async event.
* `options` {Object}
* `triggerAsyncId` {number} The ID of the execution context that created this
- async event. **Default:** `executionAsyncId()`
+ async event. **Default:** `executionAsyncId()`
* `requireManualDestroy` {boolean} Disables automatic `emitDestroy` when the
object is garbage collected. This usually does not need to be set (even if
`emitDestroy` is called manually), unless the resource's asyncId is retrieved
@@ -655,3 +709,5 @@ constructor.
[`destroy` callback]: #async_hooks_destroy_asyncid
[`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource
[Hook Callbacks]: #async_hooks_hook_callbacks
+[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk
+[promise execution tracking]: #async_hooks_promise_execution_tracking
diff --git a/doc/api/buffer.md b/doc/api/buffer.md
index 3f827088ce5676..8ab7b472605ba6 100644
--- a/doc/api/buffer.md
+++ b/doc/api/buffer.md
@@ -449,7 +449,7 @@ changes:
* `size` {integer} The desired length of the new `Buffer`.
-Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
+Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be
thrown. A zero-length `Buffer` will be created if `size` is 0.
@@ -535,7 +535,7 @@ const buf = Buffer.alloc(5);
console.log(buf);
```
-Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
+Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be
thrown. A zero-length `Buffer` will be created if `size` is 0.
@@ -580,7 +580,7 @@ changes:
* `size` {integer} The desired length of the new `Buffer`.
-Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
+Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be
thrown. A zero-length `Buffer` will be created if `size` is 0.
@@ -626,7 +626,7 @@ added: v5.12.0
* `size` {integer} The desired length of the new `Buffer`.
-Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
+Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be
thrown. A zero-length `Buffer` will be created if `size` is 0.
@@ -2660,7 +2660,7 @@ deprecated: v6.0.0
* `size` {integer} The desired length of the new `SlowBuffer`.
-Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
+Allocates a new `Buffer` of `size` bytes. If the `size` is larger than
[`buffer.constants.MAX_LENGTH`] or smaller than 0, a [`RangeError`] will be
thrown. A zero-length `Buffer` will be created if `size` is 0.
diff --git a/doc/api/child_process.md b/doc/api/child_process.md
index 2aeae73c728228..620719111c48c8 100755
--- a/doc/api/child_process.md
+++ b/doc/api/child_process.md
@@ -38,13 +38,13 @@ the event loop until the spawned process either exits or is terminated.
For convenience, the `child_process` module provides a handful of synchronous
and asynchronous alternatives to [`child_process.spawn()`][] and
-[`child_process.spawnSync()`][]. *Note that each of these alternatives are
+[`child_process.spawnSync()`][]. *Note that each of these alternatives are
implemented on top of [`child_process.spawn()`][] or [`child_process.spawnSync()`][].*
* [`child_process.exec()`][]: spawns a shell and runs a command within that shell,
passing the `stdout` and `stderr` to a callback function when complete.
* [`child_process.execFile()`][]: similar to [`child_process.exec()`][] except that
- it spawns the command directly without first spawning a shell.
+ it spawns the command directly without first spawning a shell by default.
* [`child_process.fork()`][]: spawns a new Node.js process and invokes a
specified module with an IPC communication channel established that allows
sending messages between parent and child.
@@ -78,7 +78,7 @@ when the child process terminates.
The importance of the distinction between [`child_process.exec()`][] and
[`child_process.execFile()`][] can vary based on platform. On Unix-type operating
systems (Unix, Linux, macOS) [`child_process.execFile()`][] can be more efficient
-because it does not spawn a shell. On Windows, however, `.bat` and `.cmd`
+because it does not spawn a shell by default. On Windows, however, `.bat` and `.cmd`
files are not executable on their own without a terminal, and therefore cannot
be launched using [`child_process.execFile()`][]. When running on Windows, `.bat`
and `.cmd` files can be invoked using [`child_process.spawn()`][] with the `shell`
@@ -143,8 +143,8 @@ changes:
[Shell Requirements][] and [Default Windows Shell][].
* `timeout` {number} **Default:** `0`
* `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or
- stderr. **Default:** `200*1024`. If exceeded, the child process is terminated.
- See caveat at [`maxBuffer` and Unicode][].
+ stderr. **Default:** `200 * 1024`. If exceeded, the child process is
+ terminated. See caveat at [`maxBuffer` and Unicode][].
* `killSignal` {string|integer} **Default:** `'SIGTERM'`
* `uid` {number} Sets the user identity of the process (see setuid(2)).
* `gid` {number} Sets the group identity of the process (see setgid(2)).
@@ -187,7 +187,7 @@ exec('cat *.js bad_file | wc -l', (error, stdout, stderr) => {
```
If a `callback` function is provided, it is called with the arguments
-`(error, stdout, stderr)`. On success, `error` will be `null`. On error,
+`(error, stdout, stderr)`. On success, `error` will be `null`. On error,
`error` will be an instance of [`Error`][]. The `error.code` property will be
the exit code of the child process while `error.signal` will be set to the
signal that terminated the process. Any exit code other than `0` is considered
@@ -257,8 +257,8 @@ changes:
* `encoding` {string} **Default:** `'utf8'`
* `timeout` {number} **Default:** `0`
* `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or
- stderr. **Default:** `200*1024` If exceeded, the child process is terminated.
- See caveat at [`maxBuffer` and Unicode][].
+ stderr. **Default:** `200 * 1024` If exceeded, the child process is
+ terminated. See caveat at [`maxBuffer` and Unicode][].
* `killSignal` {string|integer} **Default:** `'SIGTERM'`
* `uid` {number} Sets the user identity of the process (see setuid(2)).
* `gid` {number} Sets the group identity of the process (see setgid(2)).
@@ -266,6 +266,10 @@ changes:
normally be created on Windows systems. **Default:** `false`.
* `windowsVerbatimArguments` {boolean} No quoting or escaping of arguments is
done on Windows. Ignored on Unix. **Default:** `false`.
+ * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses
+ `'/bin/sh'` on UNIX, and `process.env.ComSpec` on Windows. A different
+ shell can be specified as a string. See [Shell Requirements][] and
+ [Default Windows Shell][]. **Default:** `false` (no shell).
* `callback` {Function} Called with the output when process terminates.
* `error` {Error}
* `stdout` {string|Buffer}
@@ -273,7 +277,7 @@ changes:
* Returns: {ChildProcess}
The `child_process.execFile()` function is similar to [`child_process.exec()`][]
-except that it does not spawn a shell. Rather, the specified executable `file`
+except that it does not spawn a shell by default. Rather, the specified executable `file`
is spawned directly as a new process making it slightly more efficient than
[`child_process.exec()`][].
@@ -312,6 +316,10 @@ async function getVersion() {
getVersion();
```
+*Note*: If the `shell` option is enabled, do not pass unsanitized user input
+to this function. Any input containing shell metacharacters may be used to
+trigger arbitrary command execution.
+
### child_process.fork(modulePath[, args][, options])
-* Returns: {Worker} A reference to `worker`.
+* Returns: {cluster.Worker} A reference to `worker`.
In a worker, this function will close all servers, wait for the `'close'` event on
those servers, and then disconnect the IPC channel.
@@ -499,7 +499,7 @@ Emitted after the worker IPC channel has disconnected. This can occur when a
worker exits gracefully, is killed, or is disconnected manually (such as with
worker.disconnect()).
-There may be a delay between the `'disconnect'` and `'exit'` events. These events
+There may be a delay between the `'disconnect'` and `'exit'` events. These events
can be used to detect if the process is stuck in a cleanup or if there are
long-living connections.
@@ -590,7 +590,7 @@ The `addressType` is one of:
* `4` (TCPv4)
* `6` (TCPv6)
* `-1` (unix domain socket)
-* `"udp4"` or `"udp6"` (UDP v4 or v6)
+* `'udp4'` or `'udp6'` (UDP v4 or v6)
## Event: 'message'
- `algorithm` {string}
- `key` {string | Buffer | TypedArray | DataView}
- `iv` {string | Buffer | TypedArray | DataView}
@@ -1281,8 +1292,8 @@ recent OpenSSL releases, `openssl list-cipher-algorithms` will display the
available cipher algorithms.
The `key` is the raw key used by the `algorithm` and `iv` is an
-[initialization vector][]. Both arguments must be `'utf8'` encoded strings or
-[buffers][`Buffer`].
+[initialization vector][]. Both arguments must be `'utf8'` encoded strings,
+[Buffers][`Buffer`], `TypedArray`, or `DataView`s.
### crypto.createDiffieHellman(prime[, primeEncoding][, generator][, generatorEncoding])
-* Returns {number} the `SO_RCVBUF` socket receive buffer size in bytes.
+* Returns: {number} the `SO_RCVBUF` socket receive buffer size in bytes.
### socket.getSendBufferSize()
-* Returns {number} the `SO_SNDBUF` socket send buffer size in bytes.
+* Returns: {number} the `SO_SNDBUF` socket send buffer size in bytes.
### socket.ref()
Sometimes, the domain in use is not the one that ought to be used for a
-specific event emitter. Or, the event emitter could have been created
+specific event emitter. Or, the event emitter could have been created
in the context of one domain, but ought to instead be bound to some
other domain.
@@ -280,7 +280,7 @@ Returns a new Domain object.
The Domain class encapsulates the functionality of routing errors and
uncaught exceptions to the active Domain object.
-Domain is a child class of [`EventEmitter`][]. To handle the errors that it
+Domain is a child class of [`EventEmitter`][]. To handle the errors that it
catches, listen to its `'error'` event.
### domain.members
@@ -294,13 +294,13 @@ to the domain.
* `emitter` {EventEmitter|Timer} emitter or timer to be added to the domain
-Explicitly adds an emitter to the domain. If any event handlers called by
+Explicitly adds an emitter to the domain. If any event handlers called by
the emitter throw an error, or if the emitter emits an `'error'` event, it
will be routed to the domain's `'error'` event, just like with implicit
binding.
This also works with timers that are returned from [`setInterval()`][] and
-[`setTimeout()`][]. If their callback function throws, it will be caught by
+[`setTimeout()`][]. If their callback function throws, it will be caught by
the domain 'error' handler.
If the Timer or EventEmitter was already bound to a domain, it is removed
@@ -312,7 +312,7 @@ from that one, and bound to this one instead.
* Returns: {Function} The bound function
The returned function will be a wrapper around the supplied callback
-function. When the returned function is called, any errors that are
+function. When the returned function is called, any errors that are
thrown will be routed to the domain's `'error'` event.
#### Example
@@ -336,7 +336,7 @@ d.on('error', (er) => {
### domain.dispose()
-> Stability: 0 - Deprecated. Please recover from failed IO actions
+> Stability: 0 - Deprecated. Please recover from failed IO actions
> explicitly via error event handlers set on the domain.
Once `dispose` has been called, the domain will no longer be used by callbacks
@@ -382,7 +382,7 @@ without exiting the domain.
* `callback` {Function} The callback function
* Returns: {Function} The intercepted function
-This method is almost identical to [`domain.bind(callback)`][]. However, in
+This method is almost identical to [`domain.bind(callback)`][]. However, in
addition to catching thrown errors, it will also intercept [`Error`][]
objects sent as the first argument to the function.
@@ -419,7 +419,7 @@ d.on('error', (er) => {
* `emitter` {EventEmitter|Timer} emitter or timer to be removed from the domain
-The opposite of [`domain.add(emitter)`][]. Removes domain handling from the
+The opposite of [`domain.add(emitter)`][]. Removes domain handling from the
specified emitter.
### domain.run(fn[, ...args])
diff --git a/doc/api/errors.md b/doc/api/errors.md
index b43a3395bff615..1c5836e553df1a 100755
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -107,7 +107,7 @@ pass or fail).
For *all* `EventEmitter` objects, if an `'error'` event handler is not
provided, the error will be thrown, causing the Node.js process to report an
-unhandled exception and crash unless either: The [`domain`][domains] module is
+unhandled exception and crash unless either: The [`domain`][domains] module is
used appropriately or a handler has been registered for the
[`process.on('uncaughtException')`][] event.
@@ -133,7 +133,7 @@ exactly how errors raised by those methods are propagated.
Most asynchronous methods exposed by the Node.js core API follow an idiomatic
-pattern referred to as an _error-first callback_ (sometimes referred to as
+pattern referred to as an _error-first callback_ (sometimes referred to as
a _Node.js style callback_). With this pattern, a callback function is passed
to the method as an argument. When the operation either completes or an error
is raised, the callback function is called with
@@ -156,7 +156,7 @@ fs.readFile('/some/file/that/does-exist', errorFirstCallback);
```
The JavaScript `try / catch` mechanism **cannot** be used to intercept errors
-generated by asynchronous APIs. A common mistake for beginners is to try to
+generated by asynchronous APIs. A common mistake for beginners is to try to
use `throw` inside an error-first callback:
```js
@@ -209,7 +209,7 @@ provided text message. If an object is passed as `message`, the text message
is generated by calling `message.toString()`. The `error.stack` property will
represent the point in the code at which `new Error()` was called. Stack traces
are dependent on [V8's stack trace API][]. Stack traces extend only to either
-(a) the beginning of *synchronous code execution*, or (b) the number of frames
+(a) the beginning of *synchronous code execution*, or (b) the number of frames
given by the property `Error.stackTraceLimit`, whichever is smaller.
### Error.captureStackTrace(targetObject[, constructorOpt])
@@ -526,7 +526,7 @@ found [here][online].
- `EACCES` (Permission denied): An attempt was made to access a file in a way
forbidden by its file access permissions.
-- `EADDRINUSE` (Address already in use): An attempt to bind a server
+- `EADDRINUSE` (Address already in use): An attempt to bind a server
([`net`][], [`http`][], or [`https`][]) to a local address failed due to
another server on the local system already occupying that address.
@@ -554,14 +554,14 @@ found [here][online].
`ulimit -n 2048` in the same shell that will run the Node.js process.
- `ENOENT` (No such file or directory): Commonly raised by [`fs`][] operations
- to indicate that a component of the specified pathname does not exist -- no
+ to indicate that a component of the specified pathname does not exist — no
entity (file or directory) could be found by the given path.
- `ENOTDIR` (Not a directory): A component of the given pathname existed, but
was not a directory as expected. Commonly raised by [`fs.readdir`][].
- `ENOTEMPTY` (Directory not empty): A directory with entries was the target
- of an operation that requires an empty directory -- usually [`fs.unlink`][].
+ of an operation that requires an empty directory — usually [`fs.unlink`][].
- `EPERM` (Operation not permitted): An attempt was made to perform an
operation that requires elevated privileges.
@@ -573,7 +573,7 @@ found [here][online].
- `ETIMEDOUT` (Operation timed out): A connect or send request failed because
the connected party did not properly respond after a period of time. Usually
- encountered by [`http`][] or [`net`][] -- often a sign that a `socket.end()`
+ encountered by [`http`][] or [`net`][] — often a sign that a `socket.end()`
was not properly called.
@@ -638,6 +638,21 @@ Status code was outside the regular status code range (100-999).
The `Trailer` header was set even though the transfer encoding does not support
that.
+
+### ERR_HTTP2_ALREADY_SHUTDOWN
+
+Occurs with multiple attempts to shutdown an HTTP/2 session.
+
+
+### ERR_HTTP2_ALTSVC_INVALID_ORIGIN
+
+HTTP/2 ALTSVC frames require a valid origin.
+
+
+### ERR_HTTP2_ALTSVC_LENGTH
+
+HTTP/2 ALTSVC frames are limited to a maximum of 16,382 payload bytes.
+
### ERR_HTTP2_CONNECT_AUTHORITY
@@ -661,6 +676,12 @@ forbidden.
A failure occurred sending an individual frame on the HTTP/2 session.
+
+### ERR_HTTP2_GOAWAY_SESSION
+
+New HTTP/2 Streams may not be opened after the `Http2Session` has received a
+`GOAWAY` frame from the connected peer.
+
### ERR_HTTP2_HEADER_REQUIRED
@@ -749,7 +770,7 @@ An operation was performed on a stream that had already been destroyed.
### ERR_HTTP2_MAX_PENDING_SETTINGS_ACK
Whenever an HTTP/2 `SETTINGS` frame is sent to a connected peer, the peer is
-required to send an acknowledgement that it has received and applied the new
+required to send an acknowledgment that it has received and applied the new
`SETTINGS`. By default, a maximum number of unacknowledged `SETTINGS` frames may
be sent at any given time. This error code is used when that limit has been
reached.
@@ -775,7 +796,7 @@ forbidden.
### ERR_HTTP2_PING_CANCEL
-An HTTP/2 ping was cancelled.
+An HTTP/2 ping was canceled.
### ERR_HTTP2_PING_LENGTH
@@ -800,6 +821,11 @@ client.
An attempt was made to use the `Http2Stream.prototype.responseWithFile()` API to
send something other than a regular file.
+
+### ERR_HTTP2_SESSION_ERROR
+
+The `Http2Session` closed with a non-zero error code.
+
### ERR_HTTP2_SOCKET_BOUND
@@ -817,10 +843,11 @@ Use of the `101` Informational status code is forbidden in HTTP/2.
An invalid HTTP status code has been specified. Status codes must be an integer
between `100` and `599` (inclusive).
-
-### ERR_HTTP2_STREAM_CLOSED
+
+### ERR_HTTP2_STREAM_CANCEL
-An action was performed on an HTTP/2 Stream that had already been closed.
+An `Http2Stream` was destroyed before any data was transmitted to the connected
+peer.
### ERR_HTTP2_STREAM_ERROR
@@ -981,6 +1008,37 @@ strict compliance with the API specification (which in some cases may accept
`func(undefined)` and `func()` are treated identically, and the
[`ERR_INVALID_ARG_TYPE`][] error code may be used instead.
+
+### ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK
+
+> Stability: 1 - Experimental
+
+Used when an [ES6 module][] loader hook specifies `format: 'dynamic` but does
+not provide a `dynamicInstantiate` hook.
+
+
+### ERR_MISSING_MODULE
+
+> Stability: 1 - Experimental
+
+Used when an [ES6 module][] cannot be resolved.
+
+
+### ERR_MODULE_RESOLUTION_LEGACY
+
+> Stability: 1 - Experimental
+
+Used when a failure occurred resolving imports in an [ES6 module][].
+
+
+### ERR_MULTIPLE_CALLBACK
+
+A callback was called more than once.
+
+*Note*: A callback is almost always meant to only be called once as the query
+can either be fulfilled or rejected but not both at the same time. The latter
+would be possible by calling a callback more than once.
+
### ERR_NAPI_CONS_FUNCTION
@@ -991,17 +1049,30 @@ While using `N-API`, a constructor passed was not a function.
While using `N-API`, `Constructor.prototype` was not an object.
+
+### ERR_NAPI_INVALID_DATAVIEW_ARGS
+
+While calling `napi_create_dataview()`, a given `offset` was outside the bounds
+of the dataview or `offset + length` was larger than a length of given `buffer`.
+
+
+### ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT
+
+While calling `napi_create_typedarray()`, the provided `offset` was not a
+multiple of the element size.
+
+
+### ERR_NAPI_INVALID_TYPEDARRAY_LENGTH
+
+While calling `napi_create_typedarray()`, `(length * size_of_element) +
+byte_offset` was larger than the length of given `buffer`.
+
### ERR_NO_ICU
An attempt was made to use features that require [ICU][], but Node.js was not
compiled with ICU support.
-
-### ERR_OUTOFMEMORY
-
-An operation caused an out-of-memory condition.
-
### ERR_SOCKET_ALREADY_BOUND
diff --git a/doc/api/esm.md b/doc/api/esm.md
index b90927c0d57cec..926555fc771484 100644
--- a/doc/api/esm.md
+++ b/doc/api/esm.md
@@ -132,12 +132,12 @@ module. This can be one of the following:
| `format` | Description |
| --- | --- |
-| `"esm"` | Load a standard JavaScript module |
-| `"commonjs"` | Load a node-style CommonJS module |
-| `"builtin"` | Load a node builtin CommonJS module |
-| `"json"` | Load a JSON file |
-| `"addon"` | Load a [C++ Addon][addons] |
-| `"dynamic"` | Use a [dynamic instantiate hook][] |
+| `'esm'` | Load a standard JavaScript module |
+| `'commonjs'` | Load a node-style CommonJS module |
+| `'builtin'` | Load a node builtin CommonJS module |
+| `'json'` | Load a JSON file |
+| `'addon'` | Load a [C++ Addon][addons] |
+| `'dynamic'` | Use a [dynamic instantiate hook][] |
For example, a dummy loader to load JavaScript restricted to browser resolution
rules with only JS file extension and Node builtin modules support could
@@ -191,7 +191,7 @@ would load the module `x.js` as an ES module with relative resolution support
To create a custom dynamic module that doesn't correspond to one of the
existing `format` interpretations, the `dynamicInstantiate` hook can be used.
-This hook is called only for modules that return `format: "dynamic"` from
+This hook is called only for modules that return `format: 'dynamic'` from
the `resolve` hook.
```js
diff --git a/doc/api/fs.md b/doc/api/fs.md
index 1eaae690190c70..93744100057c57 100644
--- a/doc/api/fs.md
+++ b/doc/api/fs.md
@@ -6,7 +6,7 @@
-File I/O is provided by simple wrappers around standard POSIX functions. To
+File I/O is provided by simple wrappers around standard POSIX functions. To
use this module do `require('fs')`. All the methods have asynchronous and
synchronous forms.
@@ -68,7 +68,7 @@ fs.rename('/tmp/hello', '/tmp/world', (err) => {
In busy processes, the programmer is _strongly encouraged_ to use the
asynchronous versions of these calls. The synchronous versions will block
-the entire process until they complete--halting all connections.
+the entire process until they complete — halting all connections.
The relative path to a filename can be used. Remember, however, that this path
will be relative to `process.cwd()`.
@@ -369,16 +369,16 @@ value, will not be reflected in the corresponding alternate representation.
The times in the stat object have the following semantics:
-* `atime` "Access Time" - Time when file data last accessed. Changed
+* `atime` "Access Time" - Time when file data last accessed. Changed
by the mknod(2), utimes(2), and read(2) system calls.
* `mtime` "Modified Time" - Time when file data last modified.
Changed by the mknod(2), utimes(2), and write(2) system calls.
* `ctime` "Change Time" - Time when file status was last changed
- (inode data modification). Changed by the chmod(2), chown(2),
+ (inode data modification). Changed by the chmod(2), chown(2),
link(2), mknod(2), rename(2), unlink(2), utimes(2),
read(2), and write(2) system calls.
-* `birthtime` "Birth Time" - Time of file creation. Set once when the
- file is created. On filesystems where birthtime is not available,
+* `birthtime` "Birth Time" - Time of file creation. Set once when the
+ file is created. On filesystems where birthtime is not available,
this field may instead hold either the `ctime` or
`1970-01-01T00:00Z` (ie, unix epoch timestamp `0`). Note that this
value may be greater than `atime` or `mtime` in this case. On Darwin
@@ -387,7 +387,7 @@ The times in the stat object have the following semantics:
utimes(2) system call.
Prior to Node v0.12, the `ctime` held the `birthtime` on Windows
-systems. Note that as of v0.12, `ctime` is not "creation time", and
+systems. Note that as of v0.12, `ctime` is not "creation time", and
on Unix systems, it never was.
## Class: fs.WriteStream
@@ -577,7 +577,7 @@ changes:
* `path` {string|Buffer|URL}
* `mode` {integer} **Default:** `fs.constants.F_OK`
-* Returns: `undefined`
+* Returns: {undefined}
Synchronously tests a user's permissions for the file or directory specified by
`path`. The `mode` argument is an optional integer that specifies the
@@ -862,7 +862,7 @@ changes:
* `callback` {Function}
* `err` {Error}
-Asynchronous close(2). No arguments other than a possible exception are given
+Asynchronous close(2). No arguments other than a possible exception are given
to the completion callback.
## fs.closeSync(fd)
@@ -1009,7 +1009,7 @@ const defaults = {
```
`options` can include `start` and `end` values to read a range of bytes from
-the file instead of the entire file. Both `start` and `end` are inclusive and
+the file instead of the entire file. Both `start` and `end` are inclusive and
start counting at 0. If `fd` is specified and `start` is omitted or `undefined`,
`fs.createReadStream()` reads sequentially from the current file position.
The `encoding` can be any one of those accepted by [`Buffer`][].
@@ -1079,7 +1079,7 @@ const defaults = {
```
`options` may also include a `start` option to allow writing data at
-some position past the beginning of the file. Modifying a file rather
+some position past the beginning of the file. Modifying a file rather
than replacing it may require a `flags` mode of `r+` rather than the
default mode `w`. The `encoding` can be any one of those accepted by
[`Buffer`][].
@@ -1115,7 +1115,7 @@ deprecated: v1.0.0
* `exists` {boolean}
Test whether or not the given path exists by checking with the file system.
-Then call the `callback` argument with either true or false. Example:
+Then call the `callback` argument with either true or false. Example:
```js
fs.exists('/etc/passwd', (exists) => {
@@ -1809,7 +1809,7 @@ to a non-existent file. The exclusive flag may or may not work with network file
systems.
`flags` can also be a number as documented by open(2); commonly used constants
-are available from `fs.constants`. On Windows, flags are translated to
+are available from `fs.constants`. On Windows, flags are translated to
their equivalent ones where applicable, e.g. `O_WRONLY` to `FILE_GENERIC_WRITE`,
or `O_EXCL|O_CREAT` to `CREATE_NEW`, as accepted by CreateFileW.
@@ -1923,7 +1923,7 @@ changes:
* `err` {Error}
* `files` {string[]|Buffer[]}
-Asynchronous readdir(3). Reads the contents of a directory.
+Asynchronous readdir(3). Reads the contents of a directory.
The callback gets two arguments `(err, files)` where `files` is an array of
the names of the files in the directory excluding `'.'` and `'..'`.
@@ -2468,7 +2468,7 @@ Calling `fs.unwatchFile()` with a filename that is not being watched is a
no-op, not an error.
*Note*: [`fs.watch()`][] is more efficient than `fs.watchFile()` and
-`fs.unwatchFile()`. `fs.watch()` should be used instead of `fs.watchFile()`
+`fs.unwatchFile()`. `fs.watch()` should be used instead of `fs.watchFile()`
and `fs.unwatchFile()` when possible.
## fs.utimes(path, atime, mtime, callback)
@@ -2559,12 +2559,12 @@ changes:
* `filename` {string|Buffer}
Watch for changes on `filename`, where `filename` is either a file or a
-directory. The returned object is a [`fs.FSWatcher`][].
+directory. The returned object is a [`fs.FSWatcher`][].
The second argument is optional. If `options` is provided as a string, it
specifies the `encoding`. Otherwise `options` should be passed as an object.
-The listener callback gets two arguments `(eventType, filename)`. `eventType` is either
+The listener callback gets two arguments `(eventType, filename)`. `eventType` is either
`'rename'` or `'change'`, and `filename` is the name of the file which triggered
the event.
@@ -2626,7 +2626,7 @@ content, and one for truncation).
Providing `filename` argument in the callback is only supported on Linux,
-macOS, Windows, and AIX. Even on supported platforms, `filename` is not always
+macOS, Windows, and AIX. Even on supported platforms, `filename` is not always
guaranteed to be provided. Therefore, don't assume that `filename` argument is
always provided in the callback, and have some fallback logic if it is null.
@@ -2775,7 +2775,7 @@ changes:
* `written` {integer}
* `string` {string}
-Write `string` to the file specified by `fd`. If `string` is not a string, then
+Write `string` to the file specified by `fd`. If `string` is not a string, then
the value will be coerced to one.
`position` refers to the offset from the beginning of the file where this data
@@ -3177,7 +3177,7 @@ The following constants are meant for use with the [`fs.Stats`][] object's
[Caveats]: #fs_caveats
[Common System Errors]: errors.html#errors_common_system_errors
[FS Constants]: #fs_fs_constants_1
-[MDN-Date]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date
+[MDN-Date]: https://developer.mozilla.org/en-US/JavaScript/Reference/Global_Objects/Date
[MDN-Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type
[MSDN-Rel-Path]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#fully_qualified_vs._relative_paths
[Readable Stream]: stream.html#stream_class_stream_readable
diff --git a/doc/api/http.md b/doc/api/http.md
index dea6c08ef35d6f..7331d8bc5d969d 100644
--- a/doc/api/http.md
+++ b/doc/api/http.md
@@ -9,7 +9,7 @@ To use the HTTP server and client one must `require('http')`.
The HTTP interfaces in Node.js are designed to support many features
of the protocol which have been traditionally difficult to use.
In particular, large, possibly chunk-encoded, messages. The interface is
-careful to never buffer entire requests or responses--the
+careful to never buffer entire requests or responses — the
user is able to stream data.
HTTP message headers are represented by an object like this:
@@ -33,7 +33,7 @@ parse the actual headers or the body.
See [`message.headers`][] for details on how duplicate headers are handled.
The raw headers as they were received are retained in the `rawHeaders`
-property, which is an array of `[key, value, key2, value2, ...]`. For
+property, which is an array of `[key, value, key2, value2, ...]`. For
example, the previous message header object might have a `rawHeaders`
list like the following:
@@ -122,9 +122,9 @@ added: v0.3.4
for TCP Keep-Alive packets. Ignored when the
`keepAlive` option is `false` or `undefined`. Defaults to `1000`.
* `maxSockets` {number} Maximum number of sockets to allow per
- host. Defaults to `Infinity`.
+ host. Defaults to `Infinity`.
* `maxFreeSockets` {number} Maximum number of sockets to leave open
- in a free state. Only relevant if `keepAlive` is set to `true`.
+ in a free state. Only relevant if `keepAlive` is set to `true`.
Defaults to `256`.
The default [`http.globalAgent`][] that is used by [`http.request()`][] has all
@@ -203,9 +203,9 @@ added: v0.11.4
Destroy any sockets that are currently in use by the agent.
-It is usually not necessary to do this. However, if using an
+It is usually not necessary to do this. However, if using an
agent with `keepAlive` enabled, then it is best to explicitly shut down
-the agent when it will no longer be used. Otherwise,
+the agent when it will no longer be used. Otherwise,
sockets may hang open for quite a long time before the server
terminates them.
@@ -217,7 +217,7 @@ added: v0.11.4
* {Object}
An object which contains arrays of sockets currently awaiting use by
-the agent when `keepAlive` is enabled. Do not modify.
+the agent when `keepAlive` is enabled. Do not modify.
### agent.getName(options)
-This object is created internally and returned from [`http.request()`][]. It
-represents an _in-progress_ request whose header has already been queued. The
+This object is created internally and returned from [`http.request()`][]. It
+represents an _in-progress_ request whose header has already been queued. The
header is still mutable using the [`setHeader(name, value)`][],
- [`getHeader(name)`][], [`removeHeader(name)`][] API. The actual header will
+ [`getHeader(name)`][], [`removeHeader(name)`][] API. The actual header will
be sent along with the first data chunk or when calling [`request.end()`][].
To get the response, add a listener for [`'response'`][] to the request object.
[`'response'`][] will be emitted from the request object when the response
-headers have been received. The [`'response'`][] event is executed with one
+headers have been received. The [`'response'`][] event is executed with one
argument which is an instance of [`http.IncomingMessage`][].
During the [`'response'`][] event, one can add listeners to the
response object; particularly to listen for the `'data'` event.
If no [`'response'`][] handler is added, then the response will be
-entirely discarded. However, if a [`'response'`][] event handler is added,
+entirely discarded. However, if a [`'response'`][] event handler is added,
then the data from the response object **must** be consumed, either by
calling `response.read()` whenever there is a `'readable'` event, or
by adding a `'data'` handler, or by calling the `.resume()` method.
-Until the data is consumed, the `'end'` event will not fire. Also, until
+Until the data is consumed, the `'end'` event will not fire. Also, until
the data is read it will consume memory that can eventually lead to a
'process out of memory' error.
@@ -541,7 +541,7 @@ For efficiency reasons, Node.js normally buffers the request headers until
then tries to pack the request headers and data into a single TCP packet.
That's usually desired (it saves a TCP round-trip), but not when the first
-data is not sent until possibly much later. `request.flushHeaders()` bypasses
+data is not sent until possibly much later. `request.flushHeaders()` bypasses
the optimization and kickstarts the request.
### request.getHeader(name)
@@ -669,9 +669,9 @@ added: v0.1.29
* `encoding` {string}
* `callback` {Function}
-Sends a chunk of the body. By calling this method
+Sends a chunk of the body. By calling this method
many times, a request body can be sent to a
-server--in that case it is suggested to use the
+server — in that case it is suggested to use the
`['Transfer-Encoding', 'chunked']` header line when
creating the request.
@@ -681,7 +681,9 @@ Defaults to `'utf8'`.
The `callback` argument is optional and will be called when this chunk of data
is flushed.
-Returns `request`.
+Returns `true` if the entire data was flushed successfully to the kernel
+buffer. Returns `false` if all or part of the data was queued in user memory.
+`'drain'` will be emitted when the buffer is free again.
## Class: http.Server
-This object is created internally by an HTTP server--not by the user. It is
+This object is created internally by an HTTP server — not by the user. It is
passed as the second parameter to the [`'request'`][] event.
The response implements, but does not inherit from, the [Writable Stream][]
@@ -1161,8 +1163,8 @@ added: v0.4.0
* `name` {string}
* `value` {string | string[]}
-Sets a single header value for implicit headers. If this header already exists
-in the to-be-sent headers, its value will be replaced. Use an array of strings
+Sets a single header value for implicit headers. If this header already exists
+in the to-be-sent headers, its value will be replaced. Use an array of strings
here to send multiple headers with the same name.
Example:
@@ -1202,12 +1204,12 @@ added: v0.9.12
* `msecs` {number}
* `callback` {Function}
-Sets the Socket's timeout value to `msecs`. If a callback is
+Sets the Socket's timeout value to `msecs`. If a callback is
provided, then it is added as a listener on the `'timeout'` event on
the response object.
If no `'timeout'` listener is added to the request, the response, or
-the server, then sockets are destroyed when they time out. If a handler is
+the server, then sockets are destroyed when they time out. If a handler is
assigned to the request, the response, or the server's `'timeout'` events,
timed out sockets must be handled explicitly.
@@ -1487,8 +1489,8 @@ added: v0.11.6
The raw request/response headers list exactly as they were received.
-Note that the keys and values are in the same list. It is *not* a
-list of tuples. So, the even-numbered offsets are key values, and the
+Note that the keys and values are in the same list. It is *not* a
+list of tuples. So, the even-numbered offsets are key values, and the
odd-numbered offsets are the associated values.
Header names are not lowercased, and duplicates are not merged.
@@ -1515,7 +1517,7 @@ added: v0.11.6
* {Array}
The raw request/response trailer keys and values exactly as they were
-received. Only populated at the `'end'` event.
+received. Only populated at the `'end'` event.
### message.setTimeout(msecs, callback)
@@ -19,13 +19,16 @@ compatibility with the existing [HTTP/1][] module API. However,
the [Compatibility API][] is.
The `http2` Core API is much more symmetric between client and server than the
-`http` API. For instance, most events, like `error` and `socketError`, can be
-emitted either by client-side code or server-side code.
+`http` API. For instance, most events, like `error`, `connect` and `stream`, can
+be emitted either by client-side code or server-side code.
### Server-side example
-The following illustrates a simple, plain-text HTTP/2 server using the
-Core API:
+The following illustrates a simple HTTP/2 server using the Core API.
+Since there are no browsers known that support
+[unencrypted HTTP/2][HTTP/2 Unencrypted], the use of
+[`http2.createSecureServer()`][] is necessary when communicating
+with browser clients.
```js
const http2 = require('http2');
@@ -36,7 +39,6 @@ const server = http2.createSecureServer({
cert: fs.readFileSync('localhost-cert.pem')
});
server.on('error', (err) => console.error(err));
-server.on('socketError', (err) => console.error(err));
server.on('stream', (stream, headers) => {
// stream is a Duplex
@@ -67,7 +69,6 @@ const fs = require('fs');
const client = http2.connect('https://localhost:8443', {
ca: fs.readFileSync('localhost-cert.pem')
});
-client.on('socketError', (err) => console.error(err));
client.on('error', (err) => console.error(err));
const req = client.request({ ':path': '/' });
@@ -83,7 +84,7 @@ let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
console.log(`\n${data}`);
- client.destroy();
+ client.close();
});
req.end();
```
@@ -127,7 +128,7 @@ solely on the API of the `Http2Session`.
added: v8.4.0
-->
-The `'close'` event is emitted once the `Http2Session` has been terminated.
+The `'close'` event is emitted once the `Http2Session` has been destroyed.
#### Event: 'connect'
-The `'localSettings'` event is emitted when an acknowledgement SETTINGS frame
+The `'localSettings'` event is emitted when an acknowledgment SETTINGS frame
has been received. When invoked, the handler function will receive a copy of
the local settings.
@@ -229,24 +230,18 @@ added: v8.4.0
The `'stream'` event is emitted when a new `Http2Stream` is created. When
invoked, the handler function will receive a reference to the `Http2Stream`
-object, a [Headers Object][], and numeric flags associated with the creation
-of the stream.
+object, a [HTTP/2 Headers Object][], and numeric flags associated with the
+creation of the stream.
```js
const http2 = require('http2');
-const {
- HTTP2_HEADER_METHOD,
- HTTP2_HEADER_PATH,
- HTTP2_HEADER_STATUS,
- HTTP2_HEADER_CONTENT_TYPE
-} = http2.constants;
session.on('stream', (stream, headers, flags) => {
- const method = headers[HTTP2_HEADER_METHOD];
- const path = headers[HTTP2_HEADER_PATH];
+ const method = headers[':method'];
+ const path = headers[':path'];
// ...
stream.respond({
- [HTTP2_HEADER_STATUS]: 200,
- [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ ':status': 200,
+ 'content-type': 'text/plain'
});
stream.write('hello ');
stream.end('world');
@@ -261,7 +256,7 @@ and would instead register a handler for the `'stream'` event emitted by the
```js
const http2 = require('http2');
-// Create a plain-text HTTP/2 server
+// Create an unencrypted HTTP/2 server
const server = http2.createServer();
server.on('stream', (stream, headers) => {
@@ -275,19 +270,6 @@ server.on('stream', (stream, headers) => {
server.listen(80);
```
-#### Event: 'socketError'
-
-
-The `'socketError'` event is emitted when an `'error'` is emitted on the
-`Socket` instance bound to the `Http2Session`. If this event is not handled,
-the `'error'` event will be re-emitted on the `Socket`.
-
-For `ServerHttp2Session` instances, a `'socketError'` event listener is always
-registered that will, by default, forward the event on to the owning
-`Http2Server` instance if no additional handlers are registered.
-
#### Event: 'timeout'
+
+* Value: {string|undefined}
+
+Value will be `undefined` if the `Http2Session` is not yet connected to a
+socket, `h2c` if the `Http2Session` is not connected to a `TLSSocket`, or
+will return the value of the connected `TLSSocket`'s own `alpnProtocol`
+property.
+
+#### http2session.close([callback])
+
+
+* `callback` {Function}
+
+Gracefully closes the `Http2Session`, allowing any existing streams to
+complete on their own and preventing new `Http2Stream` instances from being
+created. Once closed, `http2session.destroy()` *might* be called if there
+are no open `Http2Stream` instances.
+
+If specified, the `callback` function is registered as a handler for the
+`'close'` event.
+
+#### http2session.closed
+
+
+* Value: {boolean}
+
+Will be `true` if this `Http2Session` instance has been closed, otherwise
+`false`.
+
+#### http2session.connecting
+
+
+* {boolean}
+
+Will be `true` if this `Http2Session` instance is still connecting, will be set
+to `false` before emitting `connect` event and/or calling the `http2.connect`
+callback.
+
+#### http2session.destroy([error,][code])
+* `error` {Error} An `Error` object if the `Http2Session` is being destroyed
+ due to an error.
+* `code` {number} The HTTP/2 error code to send in the final `GOAWAY` frame.
+ If unspecified, and `error` is not undefined, the default is `INTERNAL_ERROR`,
+ otherwise defaults to `NO_ERROR`.
* Returns: {undefined}
Immediately terminates the `Http2Session` and the associated `net.Socket` or
`tls.TLSSocket`.
+Once destroyed, the `Http2Session` will emit the `'close'` event. If `error`
+is not undefined, an `'error'` event will be emitted immediately after the
+`'close'` event.
+
+If there are any remaining open `Http2Streams` associated with the
+`Http2Session`, those will also be destroyed.
+
#### http2session.destroyed
+
+* Value: {boolean|undefined}
+
+Value is `undefined` if the `Http2Session` session socket has not yet been
+connected, `true` if the `Http2Session` is connected with a `TLSSocket`,
+and `false` if the `Http2Session` is connected to any other kind of socket
+or stream.
+
+#### http2session.goaway([code, [lastStreamID, [opaqueData]]])
+
+
+* `code` {number} An HTTP/2 error code
+* `lastStreamID` {number} The numeric ID of the last processed `Http2Stream`
+* `opaqueData` {Buffer|TypedArray|DataView} A `TypedArray` or `DataView`
+ instance containing additional data to be carried within the GOAWAY frame.
+
+Transmits a `GOAWAY` frame to the connected peer *without* shutting down the
+`Http2Session`.
+
#### http2session.localSettings
-* Value: {[Settings Object][]}
+* Value: {HTTP/2 Settings Object}
A prototype-less object describing the current local settings of this
`Http2Session`. The local settings are local to *this* `Http2Session` instance.
+#### http2session.originSet
+
+
+* Value: {string[]|undefined}
+
+If the `Http2Session` is connected to a `TLSSocket`, the `originSet` property
+will return an Array of origins for which the `Http2Session` may be
+considered authoritative.
+
#### http2session.pendingSettingsAck
-* Value: {[Settings Object][]}
-
-A prototype-less object describing the current remote settings of this
-`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer.
+Calls [`ref()`][`net.Socket.prototype.ref`] on this `Http2Session`
+instance's underlying [`net.Socket`].
-#### http2session.request(headers[, options])
+#### http2session.remoteSettings
-* `headers` {[Headers Object][]}
-* `options` {Object}
- * `endStream` {boolean} `true` if the `Http2Stream` *writable* side should
- be closed initially, such as when sending a `GET` request that should not
- expect a payload body.
- * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream,
- the created stream is made the sole direct dependency of the parent, with
- all other existing dependents made a dependent of the newly created stream.
- **Default:** `false`
- * `parent` {number} Specifies the numeric identifier of a stream the newly
- created stream is dependent on.
- * `weight` {number} Specifies the relative dependency of a stream in relation
- to other streams with the same `parent`. The value is a number between `1`
- and `256` (inclusive).
- * `getTrailers` {Function} Callback function invoked to collect trailer
- headers.
-
-* Returns: {ClientHttp2Stream}
-
-For HTTP/2 Client `Http2Session` instances only, the `http2session.request()`
-creates and returns an `Http2Stream` instance that can be used to send an
-HTTP/2 request to the connected server.
-
-This method is only available if `http2session.type` is equal to
-`http2.constants.NGHTTP2_SESSION_CLIENT`.
-
-```js
-const http2 = require('http2');
-const clientSession = http2.connect('https://localhost:1234');
-const {
- HTTP2_HEADER_PATH,
- HTTP2_HEADER_STATUS
-} = http2.constants;
-
-const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' });
-req.on('response', (headers) => {
- console.log(headers[HTTP2_HEADER_STATUS]);
- req.on('data', (chunk) => { /** .. **/ });
- req.on('end', () => { /** .. **/ });
-});
-```
+* Value: {HTTP/2 Settings Object}
-When set, the `options.getTrailers()` function is called immediately after
-queuing the last chunk of payload data to be sent. The callback is passed a
-single object (with a `null` prototype) that the listener may used to specify
-the trailing header fields to send to the peer.
-
-*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
-"pseudo-header" fields (e.g. `':method'`, `':path'`, etc). An `'error'` event
-will be emitted if the `getTrailers` callback attempts to set such header
-fields.
+A prototype-less object describing the current remote settings of this
+`Http2Session`. The remote settings are set by the *connected* HTTP/2 peer.
#### http2session.setTimeout(msecs, callback)
-
-* `options` {Object}
- * `graceful` {boolean} `true` to attempt a polite shutdown of the
- `Http2Session`.
- * `errorCode` {number} The HTTP/2 [error code][] to return. Note that this is
- *not* the same thing as an HTTP Response Status Code. **Default:** `0x00`
- (No Error).
- * `lastStreamID` {number} The Stream ID of the last successfully processed
- `Http2Stream` on this `Http2Session`.
- * `opaqueData` {Buffer|Uint8Array} A `Buffer` or `Uint8Array` instance
- containing arbitrary additional data to send to the peer upon disconnection.
- This is used, typically, to provide additional data for debugging failures,
- if necessary.
-* `callback` {Function} A callback that is invoked after the session shutdown
- has been completed.
-* Returns: {undefined}
-
-Attempts to shutdown this `Http2Session` using HTTP/2 defined procedures.
-If specified, the given `callback` function will be invoked once the shutdown
-process has completed.
-
-Note that calling `http2session.shutdown()` does *not* destroy the session or
-tear down the `Socket` connection. It merely prompts both sessions to begin
-preparing to cease activity.
-
-During a "graceful" shutdown, the session will first send a `GOAWAY` frame to
-the connected peer identifying the last processed stream as 232-1.
-Then, on the next tick of the event loop, a second `GOAWAY` frame identifying
-the most recently processed stream identifier is sent. This process allows the
-remote peer to begin preparing for the connection to be terminated.
-
-```js
-session.shutdown({
- graceful: true,
- opaqueData: Buffer.from('add some debugging data here')
-}, () => session.destroy());
-```
-
#### http2session.socket
-* `settings` {[Settings Object][]}
-* Returns {undefined}
+* `settings` {HTTP/2 Settings Object}
Updates the current local settings for this `Http2Session` and sends a new
`SETTINGS` frame to the connected HTTP/2 peer.
@@ -568,8 +554,8 @@ while the session is waiting for the remote peer to acknowledge the new
settings.
*Note*: The new settings will not become effective until the SETTINGS
-acknowledgement is received and the `'localSettings'` event is emitted. It
-is possible to send multiple SETTINGS frames while acknowledgement is still
+acknowledgment is received and the `'localSettings'` event is emitted. It
+is possible to send multiple SETTINGS frames while acknowledgment is still
pending.
#### http2session.type
@@ -584,12 +570,184 @@ The `http2session.type` will be equal to
server, and `http2.constants.NGHTTP2_SESSION_CLIENT` if the instance is a
client.
+#### http2session.unref()
+
+
+Calls [`unref()`][`net.Socket.prototype.unref`] on this `Http2Session`
+instance's underlying [`net.Socket`].
+
+### Class: ServerHttp2Session
+
+
+#### serverhttp2session.altsvc(alt, originOrStream)
+
+
+* `alt` {string} A description of the alternative service configuration as
+ defined by [RFC 7838][].
+* `originOrStream` {number|string|URL|Object} Either a URL string specifying
+ the origin (or an Object with an `origin` property) or the numeric identifier
+ of an active `Http2Stream` as given by the `http2stream.id` property.
+
+Submits an `ALTSVC` frame (as defined by [RFC 7838][]) to the connected client.
+
+```js
+const http2 = require('http2');
+
+const server = http2.createServer();
+server.on('session', (session) => {
+ // Set altsvc for origin https://example.org:80
+ session.altsvc('h2=":8000"', 'https://example.org:80');
+});
+
+server.on('stream', (stream) => {
+ // Set altsvc for a specific stream
+ stream.session.altsvc('h2=":8000"', stream.id);
+});
+```
+
+Sending an `ALTSVC` frame with a specific stream ID indicates that the alternate
+service is associated with the origin of the given `Http2Stream`.
+
+The `alt` and origin string *must* contain only ASCII bytes and are
+strictly interpreted as a sequence of ASCII bytes. The special value `'clear'`
+may be passed to clear any previously set alternative service for a given
+domain.
+
+When a string is passed for the `originOrStream` argument, it will be parsed as
+a URL and the origin will be derived. For instance, the origin for the
+HTTP URL `'https://example.org/foo/bar'` is the ASCII string
+`'https://example.org'`. An error will be thrown if either the given string
+cannot be parsed as a URL or if a valid origin cannot be derived.
+
+A `URL` object, or any object with an `origin` property, may be passed as
+`originOrStream`, in which case the value of the `origin` property will be
+used. The value of the `origin` property *must* be a properly serialized
+ASCII origin.
+
+#### Specifying alternative services
+
+The format of the `alt` parameter is strictly defined by [RFC 7838][] as an
+ASCII string containing a comma-delimited list of "alternative" protocols
+associated with a specific host and port.
+
+For example, the value `'h2="example.org:81"'` indicates that the HTTP/2
+protocol is available on the host `'example.org'` on TCP/IP port 81. The
+host and port *must* be contained within the quote (`"`) characters.
+
+Multiple alternatives may be specified, for instance: `'h2="example.org:81",
+h2=":82"'`
+
+The protocol identifier (`'h2'` in the examples) may be any valid
+[ALPN Protocol ID][].
+
+The syntax of these values is not validated by the Node.js implementation and
+are passed through as provided by the user or received from the peer.
+
+### Class: ClientHttp2Session
+
+
+#### Event: 'altsvc'
+
+
+* `alt`: {string}
+* `origin`: {string}
+* `streamId`: {number}
+
+The `'altsvc'` event is emitted whenever an `ALTSVC` frame is received by
+the client. The event is emitted with the `ALTSVC` value, origin, and stream
+ID. If no `origin` is provided in the `ALTSVC` frame, `origin` will
+be an empty string.
+
+```js
+const http2 = require('http2');
+const client = http2.connect('https://example.org');
+
+client.on('altsvc', (alt, origin, streamId) => {
+ console.log(alt);
+ console.log(origin);
+ console.log(streamId);
+});
+```
+
+#### clienthttp2session.request(headers[, options])
+
+
+* `headers` {HTTP/2 Headers Object}
+* `options` {Object}
+ * `endStream` {boolean} `true` if the `Http2Stream` *writable* side should
+ be closed initially, such as when sending a `GET` request that should not
+ expect a payload body.
+ * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream,
+ the created stream is made the sole direct dependency of the parent, with
+ all other existing dependents made a dependent of the newly created stream.
+ **Default:** `false`
+ * `parent` {number} Specifies the numeric identifier of a stream the newly
+ created stream is dependent on.
+ * `weight` {number} Specifies the relative dependency of a stream in relation
+ to other streams with the same `parent`. The value is a number between `1`
+ and `256` (inclusive).
+ * `getTrailers` {Function} Callback function invoked to collect trailer
+ headers.
+
+* Returns: {ClientHttp2Stream}
+
+For HTTP/2 Client `Http2Session` instances only, the `http2session.request()`
+creates and returns an `Http2Stream` instance that can be used to send an
+HTTP/2 request to the connected server.
+
+This method is only available if `http2session.type` is equal to
+`http2.constants.NGHTTP2_SESSION_CLIENT`.
+
+```js
+const http2 = require('http2');
+const clientSession = http2.connect('https://localhost:1234');
+const {
+ HTTP2_HEADER_PATH,
+ HTTP2_HEADER_STATUS
+} = http2.constants;
+
+const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' });
+req.on('response', (headers) => {
+ console.log(headers[HTTP2_HEADER_STATUS]);
+ req.on('data', (chunk) => { /** .. **/ });
+ req.on('end', () => { /** .. **/ });
+});
+```
+
+When set, the `options.getTrailers()` function is called immediately after
+queuing the last chunk of payload data to be sent. The callback is passed a
+single object (with a `null` prototype) that the listener may use to specify
+the trailing header fields to send to the peer.
+
+*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
+pseudo-header fields (e.g. `':method'`, `':path'`, etc). An `'error'` event
+will be emitted if the `getTrailers` callback attempts to set such header
+fields.
+
+The `:method` and `:path` pseudo-headers are not specified within `headers`,
+they respectively default to:
+
+* `:method` = `'GET'`
+* `:path` = `/`
+
### Class: Http2Stream
-* Extends: {Duplex}
+* Extends: {stream.Duplex}
Each instance of the `Http2Stream` class represents a bidirectional HTTP/2
communications stream over an `Http2Session` instance. Any single `Http2Session`
@@ -605,7 +763,7 @@ On the client, `Http2Stream` instances are created and returned when either the
`'push'` event.
*Note*: The `Http2Stream` class is a base for the [`ServerHttp2Stream`][] and
-[`ClientHttp2Stream`][] classes, each of which are used specifically by either
+[`ClientHttp2Stream`][] classes, each of which is used specifically by either
the Server or Client side, respectively.
All `Http2Stream` instances are [`Duplex`][] streams. The `Writable` side of the
@@ -629,7 +787,7 @@ On the client side, instances of [`ClientHttp2Stream`][] are created when the
`http2session.request()` may not be immediately ready for use if the parent
`Http2Session` has not yet been fully established. In such cases, operations
called on the `Http2Stream` will be buffered until the `'ready'` event is
-emitted. User code should rarely, if ever, have need to handle the `'ready'`
+emitted. User code should rarely, if ever, need to handle the `'ready'`
event directly. The ready status of an `Http2Stream` can be determined by
checking the value of `http2stream.id`. If the value is `undefined`, the stream
is not yet ready for use.
@@ -639,7 +797,7 @@ is not yet ready for use.
All [`Http2Stream`][] instances are destroyed either when:
* An `RST_STREAM` frame for the stream is received by the connected peer.
-* The `http2stream.rstStream()` methods is called.
+* The `http2stream.close()` method is called.
* The `http2stream.destroy()` or `http2session.destroy()` methods are called.
When an `Http2Stream` instance is destroyed, an attempt will be made to send an
@@ -704,7 +862,7 @@ added: v8.4.0
-->
The `'timeout'` event is emitted after no activity is received for this
-`'Http2Stream'` within the number of millseconds set using
+`'Http2Stream'` within the number of milliseconds set using
`http2stream.setTimeout()`.
#### Event: 'trailers'
@@ -714,7 +872,7 @@ added: v8.4.0
The `'trailers'` event is emitted when a block of headers associated with
trailing header fields is received. The listener callback is passed the
-[Headers Object][] and flags associated with the headers.
+[HTTP/2 Headers Object][] and flags associated with the headers.
```js
stream.on('trailers', (headers, flags) => {
@@ -732,6 +890,29 @@ added: v8.4.0
Set to `true` if the `Http2Stream` instance was aborted abnormally. When set,
the `'aborted'` event will have been emitted.
+#### http2stream.close(code[, callback])
+
+
+* code {number} Unsigned 32-bit integer identifying the error code. **Default:**
+ `http2.constants.NGHTTP2_NO_ERROR` (`0x00`)
+* `callback` {Function} An optional function registered to listen for the
+ `'close'` event.
+* Returns: {undefined}
+
+Closes the `Http2Stream` instance by sending an `RST_STREAM` frame to the
+connected HTTP/2 peer.
+
+#### http2stream.closed
+
+
+* Value: {boolean}
+
+Set to `true` if the `Http2Stream` instance has been closed.
+
#### http2stream.destroyed
+
+* Value: {boolean}
+
+Set to `true` if the `Http2Stream` instance has not yet been assigned a
+numeric stream identifier.
+
#### http2stream.priority(options)
-
-* code {number} Unsigned 32-bit integer identifying the error code. **Default:**
- `http2.constant.NGHTTP2_NO_ERROR` (`0x00`)
-* Returns: {undefined}
-
-Sends an `RST_STREAM` frame to the connected HTTP/2 peer, causing this
-`Http2Stream` to be closed on both sides using [error code][] `code`.
-
-#### http2stream.rstWithNoError()
-
-
-* Returns: {undefined}
-
-Shortcut for `http2stream.rstStream()` using error code `0x00` (No Error).
-
-#### http2stream.rstWithProtocolError()
+#### http2stream.sentHeaders
-* Returns: {undefined}
-
-Shortcut for `http2stream.rstStream()` using error code `0x01` (Protocol Error).
-
-#### http2stream.rstWithCancel()
-
-
-* Returns: {undefined}
+* Value: {HTTP/2 Headers Object}
-Shortcut for `http2stream.rstStream()` using error code `0x08` (Cancel).
+An object containing the outbound headers sent for this `Http2Stream`.
-#### http2stream.rstWithRefuse()
+#### http2stream.sentInfoHeaders
-* Returns: {undefined}
+* Value: {HTTP/2 Headers Object[]}
-Shortcut for `http2stream.rstStream()` using error code `0x07` (Refused Stream).
+An array of objects containing the outbound informational (additional) headers
+sent for this `Http2Stream`.
-#### http2stream.rstWithInternalError()
+#### http2stream.sentTrailers
-* Returns: {undefined}
+* Value: {HTTP/2 Headers Object}
-Shortcut for `http2stream.rstStream()` using error code `0x02` (Internal Error).
+An object containing the outbound trailers sent for this this `HttpStream`.
#### http2stream.session
The `'headers'` event is emitted when an additional block of headers is received
-for a stream, such as when a block of `1xx` informational headers are received.
-The listener callback is passed the [Headers Object][] and flags associated with
-the headers.
+for a stream, such as when a block of `1xx` informational headers is received.
+The listener callback is passed the [HTTP/2 Headers Object][] and flags
+associated with the headers.
```js
stream.on('headers', (headers, flags) => {
@@ -926,8 +1088,8 @@ added: v8.4.0
-->
The `'push'` event is emitted when response headers for a Server Push stream
-are received. The listener callback is passed the [Headers Object][] and flags
-associated with the headers.
+are received. The listener callback is passed the [HTTP/2 Headers Object][] and
+flags associated with the headers.
```js
stream.on('push', (headers, flags) => {
@@ -943,7 +1105,7 @@ added: v8.4.0
The `'response'` event is emitted when a response `HEADERS` frame has been
received for this stream from the connected HTTP/2 server. The listener is
invoked with two arguments: an Object containing the received
-[Headers Object][], and flags associated with the headers.
+[HTTP/2 Headers Object][], and flags associated with the headers.
For example:
@@ -973,8 +1135,7 @@ provide additional methods such as `http2stream.pushStream()` and
added: v8.4.0
-->
-* `headers` {[Headers Object][]}
-* Returns: {undefined}
+* `headers` {HTTP/2 Headers Object}
Sends an additional informational `HEADERS` frame to the connected HTTP/2 peer.
@@ -1004,7 +1165,7 @@ accepts push streams, `false` otherwise. Settings are the same for every
added: v8.4.0
-->
-* `headers` {[Headers Object][]}
+* `headers` {HTTP/2 Headers Object}
* `options` {Object}
* `exclusive` {boolean} When `true` and `parent` identifies a parent Stream,
the created stream is made the sole direct dependency of the parent, with
@@ -1014,17 +1175,23 @@ added: v8.4.0
created stream is dependent on.
* `callback` {Function} Callback that is called once the push stream has been
initiated.
+ * `err` {Error}
+ * `pushStream` {ServerHttp2Stream} The returned pushStream object.
+ * `headers` {HTTP/2 Headers Object} Headers object the pushStream was
+ initiated with.
* Returns: {undefined}
Initiates a push stream. The callback is invoked with the new `Http2Stream`
-instance created for the push stream.
+instance created for the push stream passed as the second argument, or an
+`Error` passed as the first argument.
```js
const http2 = require('http2');
const server = http2.createServer();
server.on('stream', (stream) => {
stream.respond({ ':status': 200 });
- stream.pushStream({ ':path': '/' }, (pushStream) => {
+ stream.pushStream({ ':path': '/' }, (err, pushStream, headers) => {
+ if (err) throw err;
pushStream.respond({ ':status': 200 });
pushStream.end('some pushed data');
});
@@ -1041,11 +1208,11 @@ a `weight` value to `http2stream.priority` with the `silent` option set to
added: v8.4.0
-->
-* `headers` {[Headers Object][]}
+* `headers` {HTTP/2 Headers Object}
* `options` {Object}
* `endStream` {boolean} Set to `true` to indicate that the response will not
include payload data.
- * `getTrailers` {function} Callback function invoked to collect trailer
+ * `getTrailers` {Function} Callback function invoked to collect trailer
headers.
* Returns: {undefined}
@@ -1060,7 +1227,7 @@ server.on('stream', (stream) => {
When set, the `options.getTrailers()` function is called immediately after
queuing the last chunk of payload data to be sent. The callback is passed a
-single object (with a `null` prototype) that the listener may used to specify
+single object (with a `null` prototype) that the listener may use to specify
the trailing header fields to send to the peer.
```js
@@ -1077,7 +1244,7 @@ server.on('stream', (stream) => {
```
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
-"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
+pseudo-header fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
will be emitted if the `getTrailers` callback attempts to set such header
fields.
@@ -1087,7 +1254,7 @@ added: v8.4.0
-->
* `fd` {number} A readable file descriptor.
-* `headers` {[Headers Object][]}
+* `headers` {HTTP/2 Headers Object}
* `options` {Object}
* `statCheck` {Function}
* `getTrailers` {Function} Callback function invoked to collect trailer
@@ -1107,10 +1274,10 @@ automatically.
const http2 = require('http2');
const fs = require('fs');
-const fd = fs.openSync('/some/file', 'r');
-
const server = http2.createServer();
server.on('stream', (stream) => {
+ const fd = fs.openSync('/some/file', 'r');
+
const stat = fs.fstatSync(fd);
const headers = {
'content-length': stat.size,
@@ -1118,8 +1285,8 @@ server.on('stream', (stream) => {
'content-type': 'text/plain'
};
stream.respondWithFD(fd, headers);
+ stream.on('close', () => fs.closeSync(fd));
});
-server.on('close', () => fs.closeSync(fd));
```
The optional `options.statCheck` function may be specified to give user code
@@ -1132,19 +1299,25 @@ The `offset` and `length` options may be used to limit the response to a
specific range subset. This can be used, for instance, to support HTTP Range
requests.
+The file descriptor is not closed when the stream is closed, so it will need
+to be closed manually once it is no longer needed.
+Note that using the same file descriptor concurrently for multiple streams
+is not supported and may result in data loss. Re-using a file descriptor
+after a stream has finished is supported.
+
When set, the `options.getTrailers()` function is called immediately after
queuing the last chunk of payload data to be sent. The callback is passed a
-single object (with a `null` prototype) that the listener may used to specify
+single object (with a `null` prototype) that the listener may use to specify
the trailing header fields to send to the peer.
```js
const http2 = require('http2');
const fs = require('fs');
-const fd = fs.openSync('/some/file', 'r');
-
const server = http2.createServer();
server.on('stream', (stream) => {
+ const fd = fs.openSync('/some/file', 'r');
+
const stat = fs.fstatSync(fd);
const headers = {
'content-length': stat.size,
@@ -1156,12 +1329,13 @@ server.on('stream', (stream) => {
trailers['ABC'] = 'some value to send';
}
});
+
+ stream.on('close', () => fs.closeSync(fd));
});
-server.on('close', () => fs.closeSync(fd));
```
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
-"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
+pseudo-header fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
will be emitted if the `getTrailers` callback attempts to set such header
fields.
@@ -1171,7 +1345,7 @@ added: v8.4.0
-->
* `path` {string|Buffer|URL}
-* `headers` {[Headers Object][]}
+* `headers` {HTTP/2 Headers Object}
* `options` {Object}
* `statCheck` {Function}
* `onError` {Function} Callback function invoked in the case of an
@@ -1193,7 +1367,7 @@ of the given file:
If an error occurs while attempting to read the file data, the `Http2Stream`
will be closed using an `RST_STREAM` frame using the standard `INTERNAL_ERROR`
-code. If the `onError` callback is defined it will be called, otherwise
+code. If the `onError` callback is defined, then it will be called. Otherwise
the stream will be destroyed.
Example using a file path:
@@ -1253,7 +1427,7 @@ default behavior is to destroy the stream.
When set, the `options.getTrailers()` function is called immediately after
queuing the last chunk of payload data to be sent. The callback is passed a
-single object (with a `null` prototype) that the listener may used to specify
+single object (with a `null` prototype) that the listener may use to specify
the trailing header fields to send to the peer.
```js
@@ -1270,7 +1444,7 @@ server.on('stream', (stream) => {
```
*Note*: The HTTP/1 specification forbids trailers from containing HTTP/2
-"pseudo-header" fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
+pseudo-header fields (e.g. `':status'`, `':path'`, etc). An `'error'` event
will be emitted if the `getTrailers` callback attempts to set such header
fields.
@@ -1281,18 +1455,50 @@ added: v8.4.0
* Extends: {net.Server}
-In `Http2Server`, there is no `'clientError'` event as there is in
-HTTP1. However, there are `'socketError'`, `'sessionError'`, and
-`'streamError'`, for error happened on the socket, session, or stream
-respectively.
+In `Http2Server`, there are no `'clientError'` events as there are in
+HTTP1. However, there are `'sessionError'`, and `'streamError'` events for
+errors emitted on the socket, or from `Http2Session` or `Http2Stream` instances.
+
+#### Event: 'checkContinue'
+
+
+* `request` {http2.Http2ServerRequest}
+* `response` {http2.Http2ServerResponse}
+
+If a [`'request'`][] listener is registered or [`http2.createServer()`][] is
+supplied a callback function, the `'checkContinue'` event is emitted each time
+a request with an HTTP `Expect: 100-continue` is received. If this event is
+not listened for, the server will automatically respond with a status
+`100 Continue` as appropriate.
+
+Handling this event involves calling [`response.writeContinue()`][] if the client
+should continue to send the request body, or generating an appropriate HTTP
+response (e.g. 400 Bad Request) if the client should not continue to send the
+request body.
+
+Note that when this event is emitted and handled, the [`'request'`][] event will
+not be emitted.
-#### Event: 'socketError'
+#### Event: 'request'
-The `'socketError'` event is emitted when a `'socketError'` event is emitted by
-an `Http2Session` associated with the server.
+* `request` {http2.Http2ServerRequest}
+* `response` {http2.Http2ServerResponse}
+
+Emitted each time there is a request. Note that there may be multiple requests
+per session. See the [Compatibility API][].
+
+#### Event: 'session'
+
+
+The `'session'` event is emitted when a new `Http2Session` is created by the
+`Http2Server`.
#### Event: 'sessionError'
The `'sessionError'` event is emitted when an `'error'` event is emitted by
-an `Http2Session` object. If no listener is registered for this event, an
-`'error'` event is emitted.
+an `Http2Session` object associated with the `Http2Server`.
#### Event: 'streamError'
-* `socket` {http2.ServerHttp2Stream}
-
-If an `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here.
+If a `ServerHttp2Stream` emits an `'error'` event, it will be forwarded here.
The stream will already be destroyed when this event is triggered.
#### Event: 'stream'
@@ -1344,24 +1547,32 @@ server.on('stream', (stream, headers, flags) => {
});
```
-#### Event: 'request'
+#### Event: 'timeout'
-* `request` {http2.Http2ServerRequest}
-* `response` {http2.Http2ServerResponse}
+The `'timeout'` event is emitted when there is no activity on the Server for
+a given number of milliseconds set using `http2server.setTimeout()`.
-Emitted each time there is a request. Note that there may be multiple requests
-per session. See the [Compatibility API][].
+#### server.close([callback])
+
+- `callback` {Function}
-#### Event: 'timeout'
+Stops the server from accepting new connections. See [`net.Server.close()`][].
+
+Note that this is not analogous to restricting new requests since HTTP/2
+connections are persistent. To achieve a similar graceful shutdown behavior,
+consider also using [`http2session.close()`] on active sessions.
+
+### Class: Http2SecureServer
-The `'timeout'` event is emitted when there is no activity on the Server for
-a given number of milliseconds set using `http2server.setTimeout()`.
+* Extends: {tls.Server}
#### Event: 'checkContinue'
-* Extends: {tls.Server}
-
-#### Event: 'sessionError'
-
+* `request` {http2.Http2ServerRequest}
+* `response` {http2.Http2ServerResponse}
-The `'sessionError'` event is emitted when an `'error'` event is emitted by
-an `Http2Session` object. If no listener is registered for this event, an
-`'error'` event is emitted on the `Http2Session` instance instead.
+Emitted each time there is a request. Note that there may be multiple requests
+per session. See the [Compatibility API][].
-#### Event: 'socketError'
+#### Event: 'session'
-The `'socketError'` event is emitted when a `'socketError'` event is emitted by
-an `Http2Session` associated with the server.
+The `'session'` event is emitted when a new `Http2Session` is created by the
+`Http2SecureServer`.
-#### Event: 'unknownProtocol'
+#### Event: 'sessionError'
-The `'unknownProtocol'` event is emitted when a connecting client fails to
-negotiate an allowed protocol (i.e. HTTP/2 or HTTP/1.1). The event handler
-receives the socket for handling. If no listener is registered for this event,
-the connection is terminated. See the [Compatibility API][].
+The `'sessionError'` event is emitted when an `'error'` event is emitted by
+an `Http2Session` object associated with the `Http2SecureServer`.
#### Event: 'stream'
-* `request` {http2.Http2ServerRequest}
-* `response` {http2.Http2ServerResponse}
-
-Emitted each time there is a request. Note that there may be multiple requests
-per session. See the [Compatibility API][].
+The `'timeout'` event is emitted when there is no activity on the Server for
+a given number of milliseconds set using `http2secureServer.setTimeout()`.
-#### Event: 'timeout'
+#### Event: 'unknownProtocol'
-#### Event: 'checkContinue'
+The `'unknownProtocol'` event is emitted when a connecting client fails to
+negotiate an allowed protocol (i.e. HTTP/2 or HTTP/1.1). The event handler
+receives the socket for handling. If no listener is registered for this event,
+the connection is terminated. See the [Compatibility API][].
+
+#### server.close([callback])
+- `callback` {Function}
-* `request` {http2.Http2ServerRequest}
-* `response` {http2.Http2ServerResponse}
-
-If a [`'request'`][] listener is registered or [`http2.createSecureServer()`][]
-is supplied a callback function, the `'checkContinue'` event is emitted each
-time a request with an HTTP `Expect: 100-continue` is received. If this event
-is not listened for, the server will automatically respond with a status
-`100 Continue` as appropriate.
+Stops the server from accepting new connections. See [`tls.Server.close()`][].
-Handling this event involves calling [`response.writeContinue()`][] if the client
-should continue to send the request body, or generating an appropriate HTTP
-response (e.g. 400 Bad Request) if the client should not continue to send the
-request body.
-
-Note that when this event is emitted and handled, the [`'request'`][] event will
-not be emitted.
+Note that this is not analogous to restricting new requests since HTTP/2
+connections are persistent. To achieve a similar graceful shutdown behavior,
+consider also using [`http2session.close()`] on active sessions.
### http2.createServer(options[, onRequestHandler])
-* Returns: {[Settings Object][]}
+* Returns: {HTTP/2 Settings Object}
Returns an object containing the default settings for an `Http2Session`
instance. This method returns a new object instance every time it is called
@@ -1748,7 +2000,7 @@ so instances returned may be safely modified for use.
added: v8.4.0
-->
-* `settings` {[Settings Object][]}
+* `settings` {HTTP/2 Settings Object}
* Returns: {Buffer}
Returns a `Buffer` instance containing serialized representation of the given
@@ -1770,10 +2022,10 @@ added: v8.4.0
-->
* `buf` {Buffer|Uint8Array} The packed settings.
-* Returns: {[Settings Object][]}
+* Returns: {HTTP/2 Settings Object}
-Returns a [Settings Object][] containing the deserialized settings from the
-given `Buffer` as generated by `http2.getPackedSettings()`.
+Returns a [HTTP/2 Settings Object][] containing the deserialized settings from
+the given `Buffer` as generated by `http2.getPackedSettings()`.
### Headers Object
@@ -1838,8 +2090,8 @@ properties.
* `maxConcurrentStreams` {number} Specifies the maximum number of concurrent
streams permitted on an `Http2Session`. There is no default value which
implies, at least theoretically, 231-1 streams may be open
- concurrently at any given time in an `Http2Session`. The minimum value is
- 0. The maximum allowed value is 231-1.
+ concurrently at any given time in an `Http2Session`. The minimum value
+ is 0. The maximum allowed value is 231-1.
* `maxHeaderListSize` {number} Specifies the maximum size (uncompressed octets)
of header list that will be accepted. The minimum allowed value is 0. The
maximum allowed value is 232-1. **Default:** 65535.
@@ -1957,6 +2209,7 @@ An HTTP/2 CONNECT proxy:
```js
const http2 = require('http2');
+const { NGHTTP2_REFUSED_STREAM } = http2.constants;
const net = require('net');
const { URL } = require('url');
@@ -1964,7 +2217,7 @@ const proxy = http2.createServer();
proxy.on('stream', (stream, headers) => {
if (headers[':method'] !== 'CONNECT') {
// Only accept CONNECT requests
- stream.rstWithRefused();
+ stream.close(NGHTTP2_REFUSED_STREAM);
return;
}
const auth = new URL(`tcp://${headers[':authority']}`);
@@ -1976,7 +2229,7 @@ proxy.on('stream', (stream, headers) => {
stream.pipe(socket);
});
socket.on('error', (error) => {
- stream.rstStream(http2.constants.NGHTTP2_CONNECT_ERROR);
+ stream.close(http2.constants.NGHTTP2_CONNECT_ERROR);
});
});
@@ -2005,7 +2258,7 @@ req.setEncoding('utf8');
req.on('data', (chunk) => data += chunk);
req.on('end', () => {
console.log(`The server says: ${data}`);
- client.destroy();
+ client.close();
});
req.end('Jane');
```
@@ -2014,8 +2267,8 @@ req.end('Jane');
The Compatibility API has the goal of providing a similar developer experience
of HTTP/1 when using HTTP/2, making it possible to develop applications
-that supports both [HTTP/1][] and HTTP/2. This API targets only the
-**public API** of the [HTTP/1][], however many modules uses internal
+that support both [HTTP/1][] and HTTP/2. This API targets only the
+**public API** of the [HTTP/1][]. However many modules use internal
methods or state, and those _are not supported_ as it is a completely
different implementation.
@@ -2036,14 +2289,14 @@ In order to create a mixed [HTTPS][] and HTTP/2 server, refer to the
[ALPN negotiation][] section.
Upgrading from non-tls HTTP/1 servers is not supported.
-The HTTP2 compatibility API is composed of [`Http2ServerRequest`]() and
+The HTTP/2 compatibility API is composed of [`Http2ServerRequest`]() and
[`Http2ServerResponse`](). They aim at API compatibility with HTTP/1, but
they do not hide the differences between the protocols. As an example,
the status message for HTTP codes is ignored.
### ALPN negotiation
-ALPN negotiation allows to support both [HTTPS][] and HTTP/2 over
+ALPN negotiation allows supporting both [HTTPS][] and HTTP/2 over
the same socket. The `req` and `res` objects can be either HTTP/1 or
HTTP/2, and an application **must** restrict itself to the public API of
[HTTP/1][], and detect if it is possible to use the more advanced
@@ -2085,7 +2338,7 @@ added: v8.4.0
A `Http2ServerRequest` object is created by [`http2.Server`][] or
[`http2.SecureServer`][] and passed as the first argument to the
-[`'request'`][] event. It may be used to access a request status, headers and
+[`'request'`][] event. It may be used to access a request status, headers, and
data.
It implements the [Readable Stream][] interface, as well as the
@@ -2144,9 +2397,9 @@ Example:
console.log(request.headers);
```
-See [Headers Object][].
+See [HTTP/2 Headers Object][].
-*Note*: In HTTP/2, the request path, host name, protocol, and method are
+*Note*: In HTTP/2, the request path, hostname, protocol, and method are
represented as special headers prefixed with the `:` character (e.g. `':path'`).
These special headers will be included in the `request.headers` object. Care
must be taken not to inadvertently modify these special headers or errors may
@@ -2179,7 +2432,7 @@ added: v8.4.0
* {string}
-The request method as a string. Read only. Example:
+The request method as a string. Read-only. Example:
`'GET'`, `'DELETE'`.
#### request.rawHeaders
@@ -2191,8 +2444,8 @@ added: v8.4.0
The raw request/response headers list exactly as they were received.
-Note that the keys and values are in the same list. It is *not* a
-list of tuples. So, the even-numbered offsets are key values, and the
+Note that the keys and values are in the same list. It is *not* a
+list of tuples. So, the even-numbered offsets are key values, and the
odd-numbered offsets are the associated values.
Header names are not lowercased, and duplicates are not merged.
@@ -2219,7 +2472,7 @@ added: v8.4.0
* {Array}
The raw request/response trailer keys and values exactly as they were
-received. Only populated at the `'end'` event.
+received. Only populated at the `'end'` event.
#### request.setTimeout(msecs, callback)
-This object is created internally by an HTTP server--not by the user. It is
+This object is created internally by an HTTP server — not by the user. It is
passed as the second parameter to the [`'request'`][] event.
The response implements, but does not inherit from, the [Writable Stream][]
@@ -2560,8 +2813,8 @@ added: v8.4.0
* `name` {string}
* `value` {string|string[]}
-Sets a single header value for implicit headers. If this header already exists
-in the to-be-sent headers, its value will be replaced. Use an array of strings
+Sets a single header value for implicit headers. If this header already exists
+in the to-be-sent headers, its value will be replaced. Use an array of strings
here to send multiple headers with the same name.
Example:
@@ -2601,7 +2854,7 @@ added: v8.4.0
* `msecs` {number}
* `callback` {Function}
-Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is
+Sets the [`Http2Stream`]()'s timeout value to `msecs`. If a callback is
provided, then it is added as a listener on the `'timeout'` event on
the response object.
@@ -2760,7 +3013,7 @@ response.writeHead(200, {
```
Note that Content-Length is given in bytes not characters. The
-`Buffer.byteLength()` API may be used to determine the number of bytes in a
+`Buffer.byteLength()` API may be used to determine the number of bytes in a
given encoding. On outbound messages, Node.js does not check if Content-Length
and the length of the body being transmitted are equal or not. However, when
receiving messages, Node.js will automatically reject messages when the
@@ -2800,15 +3053,80 @@ given newly created [`Http2Stream`] on `Http2ServerRespose`.
The callback will be called with an error with code `ERR_HTTP2_STREAM_CLOSED`
if the stream is closed.
+## Collecting HTTP/2 Performance Metrics
+
+The [Performance Observer][] API can be used to collect basic performance
+metrics for each `Http2Session` and `Http2Stream` instance.
+
+```js
+const { PerformanceObserver } = require('perf_hooks');
+
+const obs = new PerformanceObserver((items) => {
+ const entry = items.getEntries()[0];
+ console.log(entry.entryType); // prints 'http2'
+ if (entry.name === 'Http2Session') {
+ // entry contains statistics about the Http2Session
+ } else if (entry.name === 'Http2Stream') {
+ // entry contains statistics about the Http2Stream
+ }
+});
+obs.observe({ entryTypes: ['http2'] });
+```
+
+The `entryType` property of the `PerformanceEntry` will be equal to `'http2'`.
+
+The `name` property of the `PerformanceEntry` will be equal to either
+`'Http2Stream'` or `'Http2Session'`.
+
+If `name` is equal to `Http2Stream`, the `PerformanceEntry` will contain the
+following additional properties:
+
+* `bytesRead` {number} The number of DATA frame bytes received for this
+ `Http2Stream`.
+* `bytesWritten` {number} The number of DATA frame bytes sent for this
+ `Http2Stream`.
+* `id` {number} The identifier of the associated `Http2Stream`
+* `timeToFirstByte` {number} The number of milliseconds elapsed between the
+ `PerformanceEntry` `startTime` and the reception of the first `DATA` frame.
+* `timeToFirstByteSent` {number} The number of milliseconds elapsed between
+ the `PerformanceEntry` `startTime` and sending of the first `DATA` frame.
+* `timeToFirstHeader` {number} The number of milliseconds elapsed between the
+ `PerformanceEntry` `startTime` and the reception of the first header.
+
+If `name` is equal to `Http2Session`, the `PerformanceEntry` will contain the
+following additional properties:
+
+* `bytesRead` {number} The number of bytes received for this `Http2Session`.
+* `bytesWritten` {number} The number of bytes sent for this `Http2Session`.
+* `framesReceived` {number} The number of HTTP/2 frames received by the
+ `Http2Session`.
+* `framesSent` {number} The number of HTTP/2 frames sent by the `Http2Session`.
+* `maxConcurrentStreams` {number} The maximum number of streams concurrently
+ open during the lifetime of the `Http2Session`.
+* `pingRTT` {number} The number of milliseconds elapsed since the transmission
+ of a `PING` frame and the reception of its acknowledgment. Only present if
+ a `PING` frame has been sent on the `Http2Session`.
+* `streamAverageDuration` {number} The average duration (in milliseconds) for
+ all `Http2Stream` instances.
+* `streamCount` {number} The number of `Http2Stream` instances processed by
+ the `Http2Session`.
+* `type` {string} Either `'server'` or `'client'` to identify the type of
+ `Http2Session`.
+
+
[ALPN negotiation]: #http2_alpn_negotiation
+[ALPN Protocol ID]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
[Compatibility API]: #http2_compatibility_api
[HTTP/1]: http.html
[HTTP/2]: https://tools.ietf.org/html/rfc7540
+[HTTP/2 Unencrypted]: https://http2.github.io/faq/#does-http2-require-encryption
+[HTTP/2 Headers Object]: #http2_headers_object
+[HTTP/2 Settings Object]: #http2_settings_object
[HTTPS]: https.html
-[Headers Object]: #http2_headers_object
[Http2Session and Sockets]: #http2_http2session_and_sockets
+[Performance Observer]: perf_hooks.html
[Readable Stream]: stream.html#stream_class_stream_readable
-[Settings Object]: #http2_settings_object
+[RFC 7838]: https://tools.ietf.org/html/rfc7838
[Using options.selectPadding]: #http2_using_options_selectpadding
[Writable Stream]: stream.html#stream_writable_streams
[`'checkContinue'`]: #http2_event_checkcontinue
@@ -2825,8 +3143,12 @@ if the stream is closed.
[`http2.createSecureServer()`]: #http2_http2_createsecureserver_options_onrequesthandler
[`http2.Server`]: #http2_class_http2server
[`http2.createServer()`]: #http2_http2_createserver_options_onrequesthandler
+[`http2session.close()`]: #http2_http2session_close_callback
[`http2stream.pushStream()`]: #http2_http2stream_pushstream_headers_options_callback
+[`net.Server.close()`]: net.html#net_server_close_callback
[`net.Socket`]: net.html#net_class_net_socket
+[`net.Socket.prototype.ref`]: net.html#net_socket_ref
+[`net.Socket.prototype.unref`]: net.html#net_socket_unref
[`net.connect()`]: net.html#net_net_connect
[`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed
[`response.end()`]: #http2_response_end_data_encoding_callback
@@ -2836,6 +3158,7 @@ if the stream is closed.
[`response.write(data, encoding)`]: http.html#http_response_write_chunk_encoding_callback
[`response.writeContinue()`]: #http2_response_writecontinue
[`response.writeHead()`]: #http2_response_writehead_statuscode_statusmessage_headers
+[`tls.Server.close()`]: tls.html#tls_server_close_callback
[`tls.TLSSocket`]: tls.html#tls_class_tls_tlssocket
[`tls.connect()`]: tls.html#tls_tls_connect_options_callback
[`tls.createServer()`]: tls.html#tls_tls_createserver_options_secureconnectionlistener
diff --git a/doc/api/https.md b/doc/api/https.md
index 4740986170b39a..daf10ac4a2bb94 100644
--- a/doc/api/https.md
+++ b/doc/api/https.md
@@ -12,7 +12,7 @@ separate module.
added: v0.4.5
-->
-An Agent object for HTTPS similar to [`http.Agent`][]. See [`https.request()`][]
+An Agent object for HTTPS similar to [`http.Agent`][]. See [`https.request()`][]
for more information.
## Class: https.Server
diff --git a/doc/api/intl.md b/doc/api/intl.md
index 224c00a62fe888..83a9947dc21238 100644
--- a/doc/api/intl.md
+++ b/doc/api/intl.md
@@ -190,17 +190,17 @@ to be helpful:
["ICU Data"]: http://userguide.icu-project.org/icudata
[`--icu-data-dir`]: cli.html#cli_icu_data_dir_file
-[`Date.prototype.toLocaleString()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
-[`Intl`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Intl
+[`Date.prototype.toLocaleString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
+[`Intl`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl
[`Intl.DateTimeFormat`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
[`NODE_ICU_DATA`]: cli.html#cli_node_icu_data_file
-[`Number.prototype.toLocaleString()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
+[`Number.prototype.toLocaleString()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
[`require('buffer').transcode()`]: buffer.html#buffer_buffer_transcode_source_fromenc_toenc
[`require('util').TextDecoder`]: util.html#util_class_util_textdecoder
-[`String.prototype.localeCompare()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
-[`String.prototype.normalize()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
-[`String.prototype.toLowerCase()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
-[`String.prototype.toUpperCase()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
+[`String.prototype.localeCompare()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
+[`String.prototype.normalize()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
+[`String.prototype.toLowerCase()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
+[`String.prototype.toUpperCase()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
[BUILDING.md]: https://github.com/nodejs/node/blob/master/BUILDING.md
[BUILDING.md#full-icu]: https://github.com/nodejs/node/blob/master/BUILDING.md#build-with-full-icu-support-all-locales-supported-by-icu
[ECMA-262]: https://tc39.github.io/ecma262/
diff --git a/doc/api/modules.md b/doc/api/modules.md
index 07c469c11408ad..6bac6a8b23144b 100644
--- a/doc/api/modules.md
+++ b/doc/api/modules.md
@@ -123,12 +123,12 @@ the version that is symlinked into
Furthermore, to make the module lookup process even more optimal, rather
than putting packages directly in `/usr/lib/node`, we could put them in
-`/usr/lib/node_modules//`. Then Node.js will not bother
+`/usr/lib/node_modules//`. Then Node.js will not bother
looking for missing dependencies in `/usr/node_modules` or `/node_modules`.
In order to make modules available to the Node.js REPL, it might be useful to
also add the `/usr/lib/node_modules` folder to the `$NODE_PATH` environment
-variable. Since the module lookups using `node_modules` folders are all
+variable. Since the module lookups using `node_modules` folders are all
relative, and based on the real path of the files making the calls to
`require()`, the packages themselves can be anywhere.
@@ -196,12 +196,12 @@ NODE_MODULES_PATHS(START)
-Modules are cached after the first time they are loaded. This means
+Modules are cached after the first time they are loaded. This means
(among other things) that every call to `require('foo')` will get
exactly the same object returned, if it would resolve to the same file.
Multiple calls to `require('foo')` may not cause the module code to be
-executed multiple times. This is an important feature. With it,
+executed multiple times. This is an important feature. With it,
"partially done" objects can be returned, thus allowing transitive
dependencies to be loaded even when they would cause cycles.
@@ -212,7 +212,7 @@ that function.
-Modules are cached based on their resolved filename. Since modules may
+Modules are cached based on their resolved filename. Since modules may
resolve to a different filename based on the location of the calling
module (loading from `node_modules` folders), it is not a *guarantee*
that `require('foo')` will always return the exact same object, if it
@@ -228,14 +228,14 @@ irrespective of whether or not `./foo` and `./FOO` are the same file.
-Node.js has several modules compiled into the binary. These modules are
+Node.js has several modules compiled into the binary. These modules are
described in greater detail elsewhere in this documentation.
The core modules are defined within Node.js's source and are located in the
`lib/` folder.
Core modules are always preferentially loaded if their identifier is
-passed to `require()`. For instance, `require('http')` will always
+passed to `require()`. For instance, `require('http')` will always
return the built in HTTP module, even if there is a file by that name.
## Cycles
@@ -275,13 +275,13 @@ console.log('b done');
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
-console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
+console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
```
-When `main.js` loads `a.js`, then `a.js` in turn loads `b.js`. At that
-point, `b.js` tries to load `a.js`. In order to prevent an infinite
+When `main.js` loads `a.js`, then `a.js` in turn loads `b.js`. At that
+point, `b.js` tries to load `a.js`. In order to prevent an infinite
loop, an **unfinished copy** of the `a.js` exports object is returned to the
-`b.js` module. `b.js` then finishes loading, and its `exports` object is
+`b.js` module. `b.js` then finishes loading, and its `exports` object is
provided to the `a.js` module.
By the time `main.js` has loaded both modules, they're both finished.
@@ -296,7 +296,7 @@ in b, a.done = false
b done
in a, b.done = true
a done
-in main, a.done=true, b.done=true
+in main, a.done = true, b.done = true
```
Careful planning is required to allow cyclic module dependencies to work
@@ -314,7 +314,7 @@ required filename with the added extensions: `.js`, `.json`, and finally
parsed as JSON text files. `.node` files are interpreted as compiled addon
modules loaded with `dlopen`.
-A required module prefixed with `'/'` is an absolute path to the file. For
+A required module prefixed with `'/'` is an absolute path to the file. For
example, `require('/home/marco/foo.js')` will load the file at
`/home/marco/foo.js`.
@@ -338,7 +338,7 @@ There are three ways in which a folder may be passed to `require()` as
an argument.
The first is to create a `package.json` file in the root of the folder,
-which specifies a `main` module. An example package.json file might
+which specifies a `main` module. An example package.json file might
look like this:
```json
@@ -352,7 +352,7 @@ If this was in a folder at `./some-library`, then
This is the extent of Node.js's awareness of package.json files.
-*Note*: If the file specified by the `"main"` entry of `package.json` is
+*Note*: If the file specified by the `'main'` entry of `package.json` is
missing and can not be resolved, Node.js will report the entire module as
missing with the default error:
@@ -362,7 +362,7 @@ Error: Cannot find module 'some-library'
If there is no package.json file present in the directory, then Node.js
will attempt to load an `index.js` or `index.node` file out of that
-directory. For example, if there was no package.json file in the above
+directory. For example, if there was no package.json file in the above
example, then `require('./some-library')` would attempt to load:
* `./some-library/index.js`
@@ -415,7 +415,7 @@ varying paths before the current [module resolution][] algorithm was frozen.
`NODE_PATH` is still supported, but is less necessary now that the Node.js
ecosystem has settled on a convention for locating dependent modules.
Sometimes deployments that rely on `NODE_PATH` show surprising behavior
-when people are unaware that `NODE_PATH` must be set. Sometimes a
+when people are unaware that `NODE_PATH` must be set. Sometimes a
module's dependencies change, causing a different version (or even a
different module) to be loaded as the `NODE_PATH` is searched.
@@ -583,14 +583,14 @@ Process files with the extension `.sjs` as `.js`:
require.extensions['.sjs'] = require.extensions['.js'];
```
-**Deprecated** In the past, this list has been used to load
+**Deprecated** In the past, this list has been used to load
non-JavaScript modules into Node.js by compiling them on-demand.
However, in practice, there are much better ways to do this, such as
loading modules via some other Node.js program, or compiling them to
JavaScript ahead of time.
Since the module system is locked, this feature will probably never go
-away. However, it may have subtle bugs and complexities that are best
+away. However, it may have subtle bugs and complexities that are best
left untouched.
Note that the number of file system operations that the module system
@@ -643,7 +643,7 @@ added: v0.1.16
* {Object}
In each module, the `module` free variable is a reference to the object
-representing the current module. For convenience, `module.exports` is
+representing the current module. For convenience, `module.exports` is
also accessible via the `exports` module-global. `module` is not actually
a global but rather local to each module.
@@ -694,7 +694,7 @@ a.on('ready', () => {
Note that assignment to `module.exports` must be done immediately. It cannot be
-done in any callbacks. This does not work:
+done in any callbacks. This does not work:
x.js:
@@ -774,7 +774,7 @@ added: v0.1.16
* {string}
-The identifier for the module. Typically this is the fully resolved
+The identifier for the module. Typically this is the fully resolved
filename.
### module.loaded
@@ -817,7 +817,7 @@ The `module.require` method provides a way to load a module as if
`require()` was called from the original module.
*Note*: In order to do this, it is necessary to get a reference to the
-`module` object. Since `require()` returns the `module.exports`, and the
+`module` object. Since `require()` returns the `module.exports`, and the
`module` is typically *only* available within a specific module's code, it must
be explicitly exported in order to be used.
@@ -830,7 +830,7 @@ added: v0.3.7
* {Object}
Provides general utility methods when interacting with instances of
-`Module` -- the `module` variable often seen in file modules. Accessed
+`Module` — the `module` variable often seen in file modules. Accessed
via `require('module')`.
### module.builtinModules
@@ -840,7 +840,7 @@ added: v8.10.0
* {string[]}
-A list of the names of all modules provided by Node.js. Can be used to verify
+A list of the names of all modules provided by Node.js. Can be used to verify
if a module is maintained by a third-party module or not.
[`__dirname`]: #modules_dirname
diff --git a/doc/api/n-api.md b/doc/api/n-api.md
index 689536c227aafd..e3e3242ef321bb 100644
--- a/doc/api/n-api.md
+++ b/doc/api/n-api.md
@@ -14,7 +14,7 @@ compiled for one version to run on later versions of Node.js without
recompilation.
Addons are built/packaged with the same approach/tools
-outlined in the section titled [C++ Addons](addons.html).
+outlined in the section titled [C++ Addons](addons.html).
The only difference is the set of APIs that are used by the native code.
Instead of using the V8 or [Native Abstractions for Node.js][] APIs,
the functions available in the N-API are used.
@@ -71,7 +71,7 @@ N-API exposes the following fundamental datatypes as abstractions that are
consumed by the various APIs. These APIs should be treated as opaque,
introspectable only with other N-API calls.
-### *napi_status*
+### napi_status
Integral status code indicating the success or failure of a N-API call.
Currently, the following status codes are supported.
```C
@@ -94,7 +94,7 @@ typedef enum {
If additional information is required upon an API returning a failed status,
it can be obtained by calling `napi_get_last_error_info`.
-### *napi_extended_error_info*
+### napi_extended_error_info
```C
typedef struct {
const char* error_message;
@@ -114,7 +114,7 @@ typedef struct {
See the [Error Handling][] section for additional information.
-### *napi_env*
+### napi_env
`napi_env` is used to represent a context that the underlying N-API
implementation can use to persist VM-specific state. This structure is passed
to native functions when they're invoked, and it must be passed back when
@@ -123,11 +123,11 @@ the initial native function was called must be passed to any subsequent
nested N-API calls. Caching the `napi_env` for the purpose of general reuse is
not allowed.
-### *napi_value*
+### napi_value
This is an opaque pointer that is used to represent a JavaScript value.
### N-API Memory Management types
-#### *napi_handle_scope*
+#### napi_handle_scope
This is an abstraction used to control and modify the lifetime of objects
created within a particular scope. In general, N-API values are created within
the context of a handle scope. When a native method is called from
@@ -145,11 +145,11 @@ referenced from the current stack frame.
For more details, review the [Object Lifetime Management][].
-#### *napi_escapable_handle_scope*
+#### napi_escapable_handle_scope
Escapable handle scopes are a special type of handle scope to return values
created within a particular handle scope to a parent scope.
-#### *napi_ref*
+#### napi_ref
This is the abstraction to use to reference a `napi_value`. This allows for
users to manage the lifetimes of JavaScript values, including defining their
minimum lifetimes explicitly.
@@ -157,12 +157,12 @@ minimum lifetimes explicitly.
For more details, review the [Object Lifetime Management][].
### N-API Callback types
-#### *napi_callback_info*
+#### napi_callback_info
Opaque datatype that is passed to a callback function. It can be used for
getting additional information about the context in which the callback was
invoked.
-#### *napi_callback*
+#### napi_callback
Function pointer type for user-provided native functions which are to be
exposed to JavaScript via N-API. Callback functions should satisfy the
following signature:
@@ -170,7 +170,7 @@ following signature:
typedef napi_value (*napi_callback)(napi_env, napi_callback_info);
```
-#### *napi_finalize*
+#### napi_finalize
Function pointer type for add-on provided functions that allow the user to be
notified when externally-owned data is ready to be cleaned up because the
object with which it was associated with, has been garbage-collected. The user
@@ -279,6 +279,8 @@ valid up until an n-api function is called on the same `env`.
information as it is not subject to SemVer and may change at any time.
It is intended only for logging purposes.
+This API can be called even if there is a pending JavaScript exception.
+
### Exceptions
Any N-API function call may result in a pending JavaScript exception. This is
@@ -308,7 +310,7 @@ where the native code can catch the exception, take the appropriate action,
and then continue. This is only recommended in specific cases
where it is known that the exception can be safely handled. In these
cases [`napi_get_and_clear_last_exception`][] can be used to get and
-clear the exception. On success, result will contain the handle to
+clear the exception. On success, result will contain the handle to
the last JavaScript Object thrown. If it is determined, after
retrieving the exception, the exception cannot be handled after all
it can be re-thrown it with [`napi_throw`][] where error is the
@@ -316,7 +318,7 @@ JavaScript Error object to be thrown.
The following utility functions are also available in case native code
needs to throw an exception or determine if a `napi_value` is an instance
-of a JavaScript `Error` object: [`napi_throw_error`][],
+of a JavaScript `Error` object: [`napi_throw_error`][],
[`napi_throw_type_error`][], [`napi_throw_range_error`][] and
[`napi_is_error`][].
@@ -327,7 +329,7 @@ where result is the napi_value that refers to the newly created
JavaScript Error object.
The Node.js project is adding error codes to all of the errors
-generated internally. The goal is for applications to use these
+generated internally. The goal is for applications to use these
error codes for all error checking. The associated error messages
will remain, but will only be meant to be used for logging and
display with the expectation that the message can change without
@@ -335,7 +337,7 @@ SemVer applying. In order to support this model with N-API, both
in internal functionality and for module specific functionality
(as its good practice), the `throw_` and `create_` functions
take an optional code parameter which is the string for the code
-to be added to the error object. If the optional parameter is NULL
+to be added to the error object. If the optional parameter is NULL
then no code will be associated with the error. If a code is provided,
the name associated with the error is also updated to be:
@@ -344,7 +346,7 @@ originalName [code]
```
where originalName is the original name associated with the error
-and code is the code that was provided. For example if the code
+and code is the code that was provided. For example if the code
is 'ERR_ERROR_1' and a TypeError is being created the name will be:
```text
@@ -504,7 +506,6 @@ Returns `napi_ok` if the API succeeded.
This API returns a JavaScript RangeError with the text provided.
-
#### napi_get_and_clear_last_exception
+```C
+napi_status napi_fatal_exception(napi_env env, napi_value err);
+```
+
+- `[in] env`: The environment that the API is invoked under.
+- `[in] err`: The error you want to pass to `uncaughtException`.
+
+Trigger an `uncaughtException` in JavaScript. Useful if an async
+callback throws an exception with no way to recover.
+
### Fatal Errors
In the event of an unrecoverable error in a native module, a fatal error can be
@@ -554,14 +573,16 @@ NAPI_NO_RETURN void napi_fatal_error(const char* location,
- `[in] location`: Optional location at which the error occurred.
- `[in] location_len`: The length of the location in bytes, or
-NAPI_AUTO_LENGTH if it is null-terminated.
+`NAPI_AUTO_LENGTH` if it is null-terminated.
- `[in] message`: The message associated with the error.
- `[in] message_len`: The length of the message in bytes, or
-NAPI_AUTO_LENGTH if it is
+`NAPI_AUTO_LENGTH` if it is
null-terminated.
The function call does not return, the process will be terminated.
+This API can be called even if there is a pending JavaScript exception.
+
## Object Lifetime management
As N-API calls are made, handles to objects in the heap for the underlying
@@ -589,7 +610,7 @@ that has a loop which iterates through the elements in a large array:
```C
for (int i = 0; i < 1000000; i++) {
napi_value result;
- napi_status status = napi_get_element(e object, i, &result);
+ napi_status status = napi_get_element(e, object, i, &result);
if (status != napi_ok) {
break;
}
@@ -608,7 +629,7 @@ are no longer required, the scope can be 'closed' and any handles associated
with the scope are invalidated. The methods available to open/close scopes are
[`napi_open_handle_scope`][] and [`napi_close_handle_scope`][].
-N-API only supports a single nested hiearchy of scopes. There is only one
+N-API only supports a single nested hierarchy of scopes. There is only one
active scope at any time, and all new handles will be associated with that
scope while it is active. Scopes must be closed in the reverse order from
which they are opened. In addition, all scopes created within a native method
@@ -626,7 +647,7 @@ for (int i = 0; i < 1000000; i++) {
break;
}
napi_value result;
- status = napi_get_element(e object, i, &result);
+ status = napi_get_element(e, object, i, &result);
if (status != napi_ok) {
break;
}
@@ -682,6 +703,8 @@ Returns `napi_ok` if the API succeeded.
This API closes the scope passed in. Scopes must be closed in the
reverse order from which they were created.
+This API can be called even if there is a pending JavaScript exception.
+
#### napi_open_escapable_handle_scope
@@ -1038,7 +1066,7 @@ JavaScript arrays are described in
[Section 22.1](https://tc39.github.io/ecma262/#sec-array-objects) of the
ECMAScript Language Specification.
-#### *napi_create_array_with_length*
+#### napi_create_array_with_length
@@ -1067,7 +1095,7 @@ JavaScript arrays are described in
[Section 22.1](https://tc39.github.io/ecma262/#sec-array-objects) of the
ECMAScript Language Specification.
-#### *napi_create_arraybuffer*
+#### napi_create_arraybuffer
@@ -1099,7 +1127,7 @@ JavaScript ArrayBuffer objects are described in
[Section 24.1](https://tc39.github.io/ecma262/#sec-arraybuffer-objects)
of the ECMAScript Language Specification.
-#### *napi_create_buffer*
+#### napi_create_buffer
@@ -1120,7 +1148,7 @@ Returns `napi_ok` if the API succeeded.
This API allocates a `node::Buffer` object. While this is still a
fully-supported data structure, in most cases using a TypedArray will suffice.
-#### *napi_create_buffer_copy*
+#### napi_create_buffer_copy
@@ -1145,7 +1173,7 @@ This API allocates a `node::Buffer` object and initializes it with data copied
from the passed-in buffer. While this is still a fully-supported data
structure, in most cases using a TypedArray will suffice.
-#### *napi_create_external*
+#### napi_create_external
@@ -1212,7 +1240,7 @@ JavaScript ArrayBuffers are described in
[Section 24.1](https://tc39.github.io/ecma262/#sec-arraybuffer-objects)
of the ECMAScript Language Specification.
-#### *napi_create_external_buffer*
+#### napi_create_external_buffer
@@ -1243,7 +1271,7 @@ structure, in most cases using a TypedArray will suffice.
*Note*: For Node.js >=4 `Buffers` are Uint8Arrays.
-#### *napi_create_function*
+#### napi_create_function
@@ -1260,7 +1288,7 @@ napi_status napi_create_function(napi_env env,
- `[in] utf8name`: A string representing the name of the function encoded as
UTF8.
- `[in] length`: The length of the utf8name in bytes, or
-NAPI_AUTO_LENGTH if it is null-terminated.
+`NAPI_AUTO_LENGTH` if it is null-terminated.
- `[in] cb`: A function pointer to the native function to be invoked when the
created function is invoked from JavaScript.
- `[in] data`: Optional arbitrary context data to be passed into the native
@@ -1276,7 +1304,7 @@ JavaScript Functions are described in
[Section 19.2](https://tc39.github.io/ecma262/#sec-function-objects)
of the ECMAScript Language Specification.
-#### *napi_create_object*
+#### napi_create_object
@@ -1296,7 +1324,7 @@ The JavaScript Object type is described in
[Section 6.1.7](https://tc39.github.io/ecma262/#sec-object-type) of the
ECMAScript Language Specification.
-#### *napi_create_symbol*
+#### napi_create_symbol
@@ -1319,7 +1347,7 @@ The JavaScript Symbol type is described in
[Section 19.4](https://tc39.github.io/ecma262/#sec-symbol-objects)
of the ECMAScript Language Specification.
-#### *napi_create_typedarray*
+#### napi_create_typedarray
@@ -1355,7 +1383,7 @@ JavaScript TypedArray Objects are described in
of the ECMAScript Language Specification.
-#### *napi_create_dataview*
+#### napi_create_dataview
@@ -1389,7 +1417,7 @@ JavaScript DataView Objects are described in
[Section 24.3][] of the ECMAScript Language Specification.
### Functions to convert from C types to N-API
-#### *napi_create_int32*
+#### napi_create_int32
@@ -1410,7 +1438,7 @@ The JavaScript Number type is described in
[Section 6.1.6](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type)
of the ECMAScript Language Specification.
-#### *napi_create_uint32*
+#### napi_create_uint32
@@ -1431,7 +1459,7 @@ The JavaScript Number type is described in
[Section 6.1.6](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type)
of the ECMAScript Language Specification.
-#### *napi_create_int64*
+#### napi_create_int64
@@ -1458,7 +1486,7 @@ outside the range of
[`Number.MAX_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.max_safe_integer)
(2^53 - 1) will lose precision.
-#### *napi_create_double*
+#### napi_create_double
@@ -1479,7 +1507,7 @@ The JavaScript Number type is described in
[Section 6.1.6](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type)
of the ECMAScript Language Specification.
-#### *napi_create_string_latin1*
+#### napi_create_string_latin1
@@ -1493,7 +1521,7 @@ napi_status napi_create_string_latin1(napi_env env,
- `[in] env`: The environment that the API is invoked under.
- `[in] str`: Character buffer representing a ISO-8859-1-encoded string.
- `[in] length`: The length of the string in bytes, or
-NAPI_AUTO_LENGTH if it is null-terminated.
+`NAPI_AUTO_LENGTH` if it is null-terminated.
- `[out] result`: A `napi_value` representing a JavaScript String.
Returns `napi_ok` if the API succeeded.
@@ -1504,7 +1532,7 @@ The JavaScript String type is described in
[Section 6.1.4](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-string-type)
of the ECMAScript Language Specification.
-#### *napi_create_string_utf16*
+#### napi_create_string_utf16
@@ -1518,7 +1546,7 @@ napi_status napi_create_string_utf16(napi_env env,
- `[in] env`: The environment that the API is invoked under.
- `[in] str`: Character buffer representing a UTF16-LE-encoded string.
- `[in] length`: The length of the string in two-byte code units, or
-NAPI_AUTO_LENGTH if it is null-terminated.
+`NAPI_AUTO_LENGTH` if it is null-terminated.
- `[out] result`: A `napi_value` representing a JavaScript String.
Returns `napi_ok` if the API succeeded.
@@ -1529,7 +1557,7 @@ The JavaScript String type is described in
[Section 6.1.4](https://tc39.github.io/ecma262/#sec-ecmascript-language-types-string-type)
of the ECMAScript Language Specification.
-#### *napi_create_string_utf8*
+#### napi_create_string_utf8
@@ -1542,7 +1570,7 @@ napi_status napi_create_string_utf8(napi_env env,
- `[in] env`: The environment that the API is invoked under.
- `[in] str`: Character buffer representing a UTF8-encoded string.
-- `[in] length`: The length of the string in bytes, or NAPI_AUTO_LENGTH
+- `[in] length`: The length of the string in bytes, or `NAPI_AUTO_LENGTH`
if it is null-terminated.
- `[out] result`: A `napi_value` representing a JavaScript String.
@@ -1555,7 +1583,7 @@ The JavaScript String type is described in
of the ECMAScript Language Specification.
### Functions to convert from N-API to C types
-#### *napi_get_array_length*
+#### napi_get_array_length
@@ -1578,7 +1606,7 @@ Array length is described in
[Section 22.1.4.1](https://tc39.github.io/ecma262/#sec-properties-of-array-instances-length)
of the ECMAScript Language Specification.
-#### *napi_get_arraybuffer_info*
+#### napi_get_arraybuffer_info
@@ -1606,7 +1634,7 @@ which can be used to guarantee control over the lifetime of the
ArrayBuffer. It's also safe to use the returned data buffer within the same
callback as long as there are no calls to other APIs that might trigger a GC.
-#### *napi_get_buffer_info*
+#### napi_get_buffer_info
@@ -1630,7 +1658,7 @@ and it's length.
*Warning*: Use caution while using this API since the underlying data buffer's
lifetime is not guaranteed if it's managed by the VM.
-#### *napi_get_prototype*
+#### napi_get_prototype
@@ -1648,7 +1676,7 @@ not the same as the function's `prototype` property).
Returns `napi_ok` if the API succeeded.
-#### *napi_get_typedarray_info*
+#### napi_get_typedarray_info
@@ -1678,9 +1706,7 @@ This API returns various properties of a typed array.
*Warning*: Use caution while using this API since the underlying data buffer
is managed by the VM
-
-
-#### *napi_get_dataview_info*
+#### napi_get_dataview_info
@@ -1707,8 +1733,7 @@ Returns `napi_ok` if the API succeeded.
This API returns various properties of a DataView.
-
-#### *napi_get_value_bool*
+#### napi_get_value_bool
@@ -1727,7 +1752,7 @@ passed in it returns `napi_boolean_expected`.
This API returns the C boolean primitive equivalent of the given JavaScript
Boolean.
-#### *napi_get_value_double*
+#### napi_get_value_double
@@ -1748,8 +1773,7 @@ in it returns `napi_number_expected`.
This API returns the C double primitive equivalent of the given JavaScript
Number.
-
-#### *napi_get_value_external*
+#### napi_get_value_external
@@ -1769,7 +1793,7 @@ passed in it returns `napi_invalid_arg`.
This API retrieves the external data pointer that was previously passed to
`napi_create_external()`.
-#### *napi_get_value_int32*
+#### napi_get_value_int32
@@ -1784,15 +1808,19 @@ napi_status napi_get_value_int32(napi_env env,
- `[out] result`: C int32 primitive equivalent of the given JavaScript Number.
Returns `napi_ok` if the API succeeded. If a non-number `napi_value`
-is passed in `napi_number_expected .
+is passed in `napi_number_expected`.
This API returns the C int32 primitive equivalent
-of the given JavaScript Number. If the number exceeds the range of the
-32 bit integer, then the result is truncated to the equivalent of the
-bottom 32 bits. This can result in a large positive number becoming
-a negative number if the value is > 2^31 -1.
+of the given JavaScript Number.
-#### *napi_get_value_int64*
+If the number exceeds the range of the 32 bit integer, then the result is
+truncated to the equivalent of the bottom 32 bits. This can result in a large
+positive number becoming a negative number if the value is > 2^31 -1.
+
+Non-finite number values (NaN, positive infinity, or negative infinity) set the
+result to zero.
+
+#### napi_get_value_int64
@@ -1809,10 +1837,19 @@ napi_status napi_get_value_int64(napi_env env,
Returns `napi_ok` if the API succeeded. If a non-number `napi_value`
is passed in it returns `napi_number_expected`.
-This API returns the C int64 primitive equivalent of the given
-JavaScript Number
+This API returns the C int64 primitive equivalent of the given JavaScript
+Number.
-#### *napi_get_value_string_latin1*
+Number values outside the range of
+[`Number.MIN_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.min_safe_integer)
+-(2^53 - 1) -
+[`Number.MAX_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.max_safe_integer)
+(2^53 - 1) will lose precision.
+
+Non-finite number values (NaN, positive infinity, or negative infinity) set the
+result to zero.
+
+#### napi_get_value_string_latin1
@@ -1839,7 +1876,7 @@ is passed in it returns `napi_string_expected`.
This API returns the ISO-8859-1-encoded string corresponding the value passed
in.
-#### *napi_get_value_string_utf8*
+#### napi_get_value_string_utf8
@@ -1865,7 +1902,7 @@ is passed in it returns `napi_string_expected`.
This API returns the UTF8-encoded string corresponding the value passed in.
-#### *napi_get_value_string_utf16*
+#### napi_get_value_string_utf16
@@ -1891,7 +1928,7 @@ is passed in it returns `napi_string_expected`.
This API returns the UTF16-encoded string corresponding the value passed in.
-#### *napi_get_value_uint32*
+#### napi_get_value_uint32
@@ -1913,7 +1950,7 @@ This API returns the C primitive equivalent of the given `napi_value` as a
`uint32_t`.
### Functions to get global instances
-#### *napi_get_boolean*
+#### napi_get_boolean
@@ -1931,7 +1968,7 @@ Returns `napi_ok` if the API succeeded.
This API is used to return the JavaScript singleton object that is used to
represent the given boolean value
-#### *napi_get_global*
+#### napi_get_global
@@ -1946,7 +1983,7 @@ Returns `napi_ok` if the API succeeded.
This API returns the global Object.
-#### *napi_get_null*
+#### napi_get_null
@@ -1961,7 +1998,7 @@ Returns `napi_ok` if the API succeeded.
This API returns the null Object.
-#### *napi_get_undefined*
+#### napi_get_undefined
@@ -1989,7 +2026,7 @@ These APIs support doing one of the following:
2. Check the type of a JavaScript value
3. Check for equality between two JavaScript values
-### *napi_coerce_to_bool*
+### napi_coerce_to_bool
@@ -2010,7 +2047,7 @@ This API implements the abstract operation ToBoolean as defined in
of the ECMAScript Language Specification.
This API can be re-entrant if getters are defined on the passed-in Object.
-### *napi_coerce_to_number*
+### napi_coerce_to_number
@@ -2031,7 +2068,7 @@ This API implements the abstract operation ToNumber as defined in
of the ECMAScript Language Specification.
This API can be re-entrant if getters are defined on the passed-in Object.
-### *napi_coerce_to_object*
+### napi_coerce_to_object
@@ -2052,7 +2089,7 @@ This API implements the abstract operation ToObject as defined in
of the ECMAScript Language Specification.
This API can be re-entrant if getters are defined on the passed-in Object.
-### *napi_coerce_to_string*
+### napi_coerce_to_string
@@ -2073,7 +2110,7 @@ This API implements the abstract operation ToString as defined in
of the ECMAScript Language Specification.
This API can be re-entrant if getters are defined on the passed-in Object.
-### *napi_typeof*
+### napi_typeof
@@ -2094,7 +2131,7 @@ the object as defined in [Section 12.5.5][] of the ECMAScript Language
Specification. However, it has support for detecting an External value.
If `value` has a type that is invalid, an error is returned.
-### *napi_instanceof*
+### napi_instanceof
@@ -2119,7 +2156,7 @@ defined in
[Section 12.10.4](https://tc39.github.io/ecma262/#sec-instanceofoperator)
of the ECMAScript Language Specification.
-### *napi_is_array*
+### napi_is_array
@@ -2137,7 +2174,7 @@ This API represents invoking the `IsArray` operation on the object
as defined in [Section 7.2.2](https://tc39.github.io/ecma262/#sec-isarray)
of the ECMAScript Language Specification.
-### *napi_is_arraybuffer*
+### napi_is_arraybuffer
@@ -2151,9 +2188,9 @@ napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result)
Returns `napi_ok` if the API succeeded.
-This API checks if the Object passsed in is an array buffer.
+This API checks if the Object passed in is an array buffer.
-### *napi_is_buffer*
+### napi_is_buffer
@@ -2168,9 +2205,9 @@ object.
Returns `napi_ok` if the API succeeded.
-This API checks if the Object passsed in is a buffer.
+This API checks if the Object passed in is a buffer.
-### *napi_is_error*
+### napi_is_error
@@ -2184,9 +2221,9 @@ napi_status napi_is_error(napi_env env, napi_value value, bool* result)
Returns `napi_ok` if the API succeeded.
-This API checks if the Object passsed in is an Error.
+This API checks if the Object passed in is an Error.
-### *napi_is_typedarray*
+### napi_is_typedarray
@@ -2200,11 +2237,9 @@ napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result)
Returns `napi_ok` if the API succeeded.
-This API checks if the Object passsed in is a typed array.
-
+This API checks if the Object passed in is a typed array.
-
-### *napi_is_dataview*
+### napi_is_dataview
@@ -2221,7 +2256,7 @@ Returns `napi_ok` if the API succeeded.
This API checks if the Object passed in is a DataView.
-### *napi_strict_equals*
+### napi_strict_equals
@@ -2364,8 +2399,8 @@ if (status != napi_ok) return status;
// Set the properties
napi_property_descriptor descriptors[] = {
- { "foo", nullptr, 0, 0, 0, fooValue, napi_default, 0 },
- { "bar", nullptr, 0, 0, 0, barValue, napi_default, 0 }
+ { "foo", NULL, 0, 0, 0, fooValue, napi_default, 0 },
+ { "bar", NULL, 0, 0, 0, barValue, napi_default, 0 }
}
status = napi_define_properties(env,
obj,
@@ -2375,7 +2410,7 @@ if (status != napi_ok) return status;
```
### Structures
-#### *napi_property_attributes*
+#### napi_property_attributes
```C
typedef enum {
napi_default = 0,
@@ -2398,7 +2433,7 @@ They can be one or more of the following bitflags:
- `napi_default` - Used to indicate that no explicit attributes are set on the
given property. By default, a property is read only, not enumerable and not
configurable.
-- `napi_writable` - Used to indicate that a given property is writable.
+- `napi_writable` - Used to indicate that a given property is writable.
- `napi_enumerable` - Used to indicate that a given property is enumerable.
- `napi_configurable` - Used to indicate that a given property is
configurable, as defined in
@@ -2409,7 +2444,7 @@ a static property on a class as opposed to an instance property, which is the
default. This is used only by [`napi_define_class`][]. It is ignored by
`napi_define_properties`.
-#### *napi_property_descriptor*
+#### napi_property_descriptor
```C
typedef struct {
// One of utf8name or name should be NULL.
@@ -2430,7 +2465,7 @@ typedef struct {
encoded as UTF8. One of `utf8name` or `name` must be provided for the
property.
- `name`: Optional napi_value that points to a JavaScript string or symbol
-to be used as the key for the property. One of `utf8name` or `name` must
+to be used as the key for the property. One of `utf8name` or `name` must
be provided for the property.
- `value`: The value that's retrieved by a get access of the property if the
property is a data property. If this is passed in, set `getter`, `setter`,
@@ -2455,7 +2490,7 @@ this function is invoked.
See [`napi_property_attributes`](#n_api_napi_property_attributes).
### Functions
-#### *napi_get_property_names*
+#### napi_get_property_names
@@ -2474,9 +2509,9 @@ and [`napi_get_element`][].
Returns `napi_ok` if the API succeeded.
-This API returns the array of propertys for the Object passed in
+This API returns the array of properties for the Object passed in
-#### *napi_set_property*
+#### napi_set_property
@@ -2496,7 +2531,7 @@ Returns `napi_ok` if the API succeeded.
This API set a property on the Object passed in.
-#### *napi_get_property*
+#### napi_get_property
@@ -2517,7 +2552,7 @@ Returns `napi_ok` if the API succeeded.
This API gets the requested property from the Object passed in.
-#### *napi_has_property*
+#### napi_has_property
@@ -2538,7 +2573,7 @@ Returns `napi_ok` if the API succeeded.
This API checks if the Object passed in has the named property.
-#### *napi_delete_property*
+#### napi_delete_property
@@ -2560,7 +2595,7 @@ Returns `napi_ok` if the API succeeded.
This API attempts to delete the `key` own property from `object`.
-#### *napi_has_own_property*
+#### napi_has_own_property
@@ -2583,7 +2618,7 @@ be a string or a Symbol, or an error will be thrown. N-API will not perform any
conversion between data types.
-#### *napi_set_named_property*
+#### napi_set_named_property
@@ -2604,7 +2639,7 @@ Returns `napi_ok` if the API succeeded.
This method is equivalent to calling [`napi_set_property`][] with a `napi_value`
created from the string passed in as `utf8Name`
-#### *napi_get_named_property*
+#### napi_get_named_property
@@ -2625,7 +2660,7 @@ Returns `napi_ok` if the API succeeded.
This method is equivalent to calling [`napi_get_property`][] with a `napi_value`
created from the string passed in as `utf8Name`
-#### *napi_has_named_property*
+#### napi_has_named_property
@@ -2646,7 +2681,7 @@ Returns `napi_ok` if the API succeeded.
This method is equivalent to calling [`napi_has_property`][] with a `napi_value`
created from the string passed in as `utf8Name`
-#### *napi_set_element*
+#### napi_set_element
@@ -2666,7 +2701,7 @@ Returns `napi_ok` if the API succeeded.
This API sets and element on the Object passed in.
-#### *napi_get_element*
+#### napi_get_element
@@ -2686,7 +2721,7 @@ Returns `napi_ok` if the API succeeded.
This API gets the element at the requested index.
-#### *napi_has_element*
+#### napi_has_element
@@ -2707,7 +2742,7 @@ Returns `napi_ok` if the API succeeded.
This API returns if the Object passed in has an element at the
requested index.
-#### *napi_delete_element*
+#### napi_delete_element
@@ -2728,7 +2763,7 @@ Returns `napi_ok` if the API succeeded.
This API attempts to delete the specified `index` from `object`.
-#### *napi_define_properties*
+#### napi_define_properties
@@ -2771,7 +2806,7 @@ like a regular JavaScript function call, or as a constructor
function.
-### *napi_call_function*
+### napi_call_function
@@ -2837,7 +2872,7 @@ status = napi_get_value_int32(env, return_val, &result);
if (status != napi_ok) return;
```
-### *napi_create_function*
+### napi_create_function
@@ -2876,18 +2911,18 @@ object. A sample module might look as follows:
```C
napi_value SayHello(napi_env env, napi_callback_info info) {
printf("Hello\n");
- return nullptr;
+ return NULL;
}
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_value fn;
- status = napi_create_function(env, nullptr, 0, SayHello, nullptr, &fn);
- if (status != napi_ok) return nullptr;
+ status = napi_create_function(env, NULL, 0, SayHello, nullptr, &fn);
+ if (status != napi_ok) return NULL;
status = napi_set_named_property(env, exports, "sayHello", fn);
- if (status != napi_ok) return nullptr;
+ if (status != napi_ok) return NULL;
return exports;
}
@@ -2905,7 +2940,7 @@ myaddon.sayHello();
`NAPI_MODULE` in the earlier snippet but the name of the target in `binding.gyp`
responsible for creating the `.node` file.
-### *napi_get_cb_info*
+### napi_get_cb_info
@@ -2935,7 +2970,7 @@ Returns `napi_ok` if the API succeeded.
This method is used within a callback function to retrieve details about the
call like the arguments and the `this` pointer from a given callback info.
-### *napi_get_new_target*
+### napi_get_new_target
@@ -2952,9 +2987,9 @@ napi_status napi_get_new_target(napi_env env,
Returns `napi_ok` if the API succeeded.
This API returns the `new.target` of the constructor call. If the current
-callback is not a constructor call, the result is `nullptr`.
+callback is not a constructor call, the result is `NULL`.
-### *napi_new_instance*
+### napi_new_instance
@@ -3034,7 +3069,7 @@ reference to the class constructor for later `instanceof` checks.
As an example:
```C
-napi_value MyClass_constructor = nullptr;
+napi_value MyClass_constructor = NULL;
status = napi_get_reference_value(env, MyClass::es_constructor, &MyClass_constructor);
assert(napi_ok == status);
bool is_instance = false;
@@ -3049,7 +3084,7 @@ if (is_instance) {
The reference must be freed once it is no longer needed.
-### *napi_define_class*
+### napi_define_class
@@ -3068,7 +3103,7 @@ napi_status napi_define_class(napi_env env,
- `[in] utf8name`: Name of the JavaScript constructor function; this is
not required to be the same as the C++ class name, though it is recommended
for clarity.
- - `[in] length`: The length of the utf8name in bytes, or NAPI_AUTO_LENGTH
+ - `[in] length`: The length of the utf8name in bytes, or `NAPI_AUTO_LENGTH`
if it is null-terminated.
- `[in] constructor`: Callback function that handles constructing instances
of the class. (This should be a static method on the class, not an actual
@@ -3105,7 +3140,7 @@ case, to prevent the function value from being garbage-collected, create a
persistent reference to it using [`napi_create_reference`][] and ensure the
reference count is kept >= 1.
-### *napi_wrap*
+### napi_wrap
@@ -3161,13 +3196,10 @@ required in order to enable correct proper of the reference.
Afterward, additional manipulation of the wrapper's prototype chain may cause
`napi_unwrap()` to fail.
-*Note*: Calling `napi_wrap()` a second time on an object that already has a
-native instance associated with it by virtue of a previous call to
-`napi_wrap()` will cause an error to be returned. If you wish to associate
-another native instance with the given object, call `napi_remove_wrap()` on it
-first.
+Calling napi_wrap() a second time on an object will return an error. To associate
+another native instance with the object, use napi_remove_wrap() first.
-### *napi_unwrap*
+### napi_unwrap
@@ -3192,7 +3224,7 @@ method or accessor, then the `this` argument to the callback is the wrapper
object; the wrapped C++ instance that is the target of the call can be obtained
then by calling `napi_unwrap()` on the wrapper object.
-### *napi_remove_wrap*
+### napi_remove_wrap
@@ -3254,7 +3286,7 @@ napi_status napi_queue_async_work(napi_env env,
napi_async_work work);
```
-[`napi_cancel_async_work`][] can be used if the work needs
+[`napi_cancel_async_work`][] can be used if the work needs
to be cancelled before the work has started execution.
After calling [`napi_cancel_async_work`][], the `complete` callback
@@ -3283,12 +3315,14 @@ napi_status napi_create_async_work(napi_env env,
- `[in] env`: The environment that the API is invoked under.
- `[in] async_resource`: An optional object associated with the async work
that will be passed to possible async_hooks [`init` hooks][].
-- `[in] async_resource_name`: An identifier for the kind of resource that is
+- `[in] async_resource_name`: Identifier for the kind of resource that is
being provided for diagnostic information exposed by the `async_hooks` API.
-- `[in] execute`: The native function which should be called to excute
-the logic asynchronously.
+- `[in] execute`: The native function which should be called to execute
+the logic asynchronously. The given function is called from a worker pool
+thread and can execute in parallel with the main event loop thread.
- `[in] complete`: The native function which will be called when the
-asynchronous logic is comple or is cancelled.
+asynchronous logic is completed or is cancelled. The given function is called
+from the main event loop thread.
- `[in] data`: User-provided data context. This will be passed back into the
execute and complete functions.
- `[out] result`: `napi_async_work*` which is the handle to the newly created
@@ -3324,6 +3358,8 @@ Returns `napi_ok` if the API succeeded.
This API frees a previously allocated work object.
+This API can be called even if there is a pending JavaScript exception.
+
### napi_queue_async_work
@@ -3382,14 +3420,14 @@ napi_status napi_async_init(napi_env env,
- `[in] env`: The environment that the API is invoked under.
- `[in] async_resource`: An optional object associated with the async work
that will be passed to possible `async_hooks` [`init` hooks][].
-- `[in] async_resource_name`: Required identifier for the kind of resource
+- `[in] async_resource_name`: Identifier for the kind of resource
that is being provided for diagnostic information exposed by the
`async_hooks` API.
- `[out] result`: The initialized async context.
Returns `napi_ok` if the API succeeded.
-### *napi_async_destroy**
+### napi_async_destroy
@@ -3403,7 +3441,9 @@ napi_status napi_async_destroy(napi_env env,
Returns `napi_ok` if the API succeeded.
-### *napi_make_callback*
+This API can be called even if there is a pending JavaScript exception.
+
+### napi_make_callback
+```C
+NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env,
+ napi_value resource_object,
+ napi_async_context context,
+ napi_callback_scope* result)
+```
+- `[in] env`: The environment that the API is invoked under.
+- `[in] resource_object`: An optional object associated with the async work
+ that will be passed to possible async_hooks [`init` hooks][].
+- `[in] context`: Context for the async operation that is
+invoking the callback. This should be a value previously obtained
+from [`napi_async_init`][].
+- `[out] result`: The newly created scope.
+
+There are cases (for example resolving promises) where it is
+necessary to have the equivalent of the scope associated with a callback
+in place when making certain N-API calls. If there is no other script on
+the stack the [`napi_open_callback_scope`][] and
+[`napi_close_callback_scope`][] functions can be used to open/close
+the required scope.
+
+### *napi_close_callback_scope*
+
+```C
+NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env,
+ napi_callback_scope scope)
+```
+- `[in] env`: The environment that the API is invoked under.
+- `[in] scope`: The scope to be closed.
+
+This API can be called even if there is a pending JavaScript exception.
+
## Version Management
### napi_get_node_version
@@ -3494,7 +3572,7 @@ napi_status napi_get_version(napi_env env,
Returns `napi_ok` if the API succeeded.
This API returns the highest N-API version supported by the
-Node.js runtime. N-API is planned to be additive such that
+Node.js runtime. N-API is planned to be additive such that
newer releases of Node.js may support additional API functions.
In order to allow an addon to use a newer function when running with
versions of Node.js that support it, while providing
@@ -3728,6 +3806,7 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
[`napi_async_init`]: #n_api_napi_async_init
[`napi_cancel_async_work`]: #n_api_napi_cancel_async_work
[`napi_close_escapable_handle_scope`]: #n_api_napi_close_escapable_handle_scope
+[`napi_close_callback_scope`]: #n_api_napi_close_callback_scope
[`napi_close_handle_scope`]: #n_api_napi_close_handle_scope
[`napi_create_async_work`]: #n_api_napi_create_async_work
[`napi_create_error`]: #n_api_napi_create_error
@@ -3753,6 +3832,7 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env,
[`napi_get_last_error_info`]: #n_api_napi_get_last_error_info
[`napi_get_and_clear_last_exception`]: #n_api_napi_get_and_clear_last_exception
[`napi_make_callback`]: #n_api_napi_make_callback
+[`napi_open_callback_scope`]: #n_api_napi_open_callback_scope
[`napi_open_escapable_handle_scope`]: #n_api_napi_open_escapable_handle_scope
[`napi_open_handle_scope`]: #n_api_napi_open_handle_scope
[`napi_property_descriptor`]: #n_api_napi_property_descriptor
diff --git a/doc/api/net.md b/doc/api/net.md
index 31e0ca7a596fa7..f281d480304862 100644
--- a/doc/api/net.md
+++ b/doc/api/net.md
@@ -185,8 +185,8 @@ Possible signatures:
* [`server.listen([port][, host][, backlog][, callback])`][`server.listen(port, host)`]
for TCP servers
-This function is asynchronous. When the server starts listening, the
-[`'listening'`][] event will be emitted. The last parameter `callback`
+This function is asynchronous. When the server starts listening, the
+[`'listening'`][] event will be emitted. The last parameter `callback`
will be added as a listener for the [`'listening'`][] event.
All `listen()` methods can take a `backlog` parameter to specify the maximum
@@ -420,8 +420,8 @@ added: v0.1.90
* {Buffer}
-Emitted when data is received. The argument `data` will be a `Buffer` or
-`String`. Encoding of data is set by `socket.setEncoding()`.
+Emitted when data is received. The argument `data` will be a `Buffer` or
+`String`. Encoding of data is set by `socket.setEncoding()`.
(See the [Readable Stream][] section for more information.)
Note that the **data will be lost** if there is no listener when a `Socket`
@@ -459,7 +459,7 @@ added: v0.1.90
* {Error}
-Emitted when an error occurs. The `'close'` event will be called directly
+Emitted when an error occurs. The `'close'` event will be called directly
following this event.
### Event: 'lookup'
@@ -474,9 +474,9 @@ changes:
Emitted after resolving the hostname but before connecting.
Not applicable to UNIX sockets.
-* `err` {Error|null} The error object. See [`dns.lookup()`][].
+* `err` {Error|null} The error object. See [`dns.lookup()`][].
* `address` {string} The IP address.
-* `family` {string|null} The address type. See [`dns.lookup()`][].
+* `family` {string|null} The address type. See [`dns.lookup()`][].
* `host` {string} The hostname.
### Event: 'timeout'
@@ -822,7 +822,7 @@ added: v0.1.90
-->
Sends data on the socket. The second parameter specifies the encoding in the
-case of a string--it defaults to UTF8 encoding.
+case of a string — it defaults to UTF8 encoding.
Returns `true` if the entire data was flushed successfully to the kernel
buffer. Returns `false` if all or part of the data was queued in user memory.
diff --git a/doc/api/os.md b/doc/api/os.md
index 306a11a3b8dd82..f46292b81386ec 100644
--- a/doc/api/os.md
+++ b/doc/api/os.md
@@ -225,7 +225,7 @@ The `os.loadavg()` method returns an array containing the 1, 5, and 15 minute
load averages.
The load average is a measure of system activity, calculated by the operating
-system and expressed as a fractional number. As a rule of thumb, the load
+system and expressed as a fractional number. As a rule of thumb, the load
average should ideally be less than the number of logical CPUs in the system.
The load average is a UNIX-specific concept with no real equivalent on
@@ -404,7 +404,7 @@ added: v6.0.0
* Returns: {Object}
The `os.userInfo()` method returns information about the currently effective
-user -- on POSIX platforms, this is typically a subset of the password file. The
+user — on POSIX platforms, this is typically a subset of the password file. The
returned object includes the `username`, `uid`, `gid`, `shell`, and `homedir`.
On Windows, the `uid` and `gid` fields are `-1`, and `shell` is `null`.
diff --git a/doc/api/path.md b/doc/api/path.md
index f2015db47048d7..ab6039206fbd91 100644
--- a/doc/api/path.md
+++ b/doc/api/path.md
@@ -163,7 +163,7 @@ changes:
The `path.extname()` method returns the extension of the `path`, from the last
occurrence of the `.` (period) character to end of string in the last portion of
-the `path`. If there is no `.` in the last portion of the `path`, or if the
+the `path`. If there is no `.` in the last portion of the `path`, or if the
first character of the basename of `path` (see `path.basename()`) is `.`, then
an empty string is returned.
@@ -396,7 +396,7 @@ path.parse('/home/user/dir/file.txt');
│ root │ │ name │ ext │
" / home/user/dir / file .txt "
└──────┴──────────────┴──────┴─────┘
-(all spaces in the "" line should be ignored -- they are purely for formatting)
+(all spaces in the "" line should be ignored — they are purely for formatting)
```
On Windows:
@@ -418,7 +418,7 @@ path.parse('C:\\path\\dir\\file.txt');
│ root │ │ name │ ext │
" C:\ path\dir \ file .txt "
└──────┴──────────────┴──────┴─────┘
-(all spaces in the "" line should be ignored -- they are purely for formatting)
+(all spaces in the "" line should be ignored — they are purely for formatting)
```
A [`TypeError`][] is thrown if `path` is not a string.
diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md
index 9b4ffb7ccb63c6..ace0c48bacdda3 100644
--- a/doc/api/perf_hooks.md
+++ b/doc/api/perf_hooks.md
@@ -29,6 +29,14 @@ added: v8.5.0
The `Performance` provides access to performance metric data. A single
instance of this class is provided via the `performance` property.
+### performance.clearEntries(name)
+
+
+Remove all performance entry objects with `entryType` equal to `name` from the
+Performance Timeline.
+
### performance.clearFunctions([name])
The `process.setuid(id)` method sets the user identity of the process. (See
-setuid(2).) The `id` can be passed as either a numeric ID or a username string.
+setuid(2).) The `id` can be passed as either a numeric ID or a username string.
If a username is specified, the method blocks while resolving the associated
numeric ID.
@@ -1747,7 +1747,7 @@ different maximum length restrictions on the title. Usually such restrictions
are quite limited. For instance, on Linux and macOS, `process.title` is limited
to the size of the binary name plus the length of the command line arguments
because setting the `process.title` overwrites the `argv` memory of the
-process. Node.js v0.8 allowed for longer process title strings by also
+process. Node.js v0.8 allowed for longer process title strings by also
overwriting the `environ` memory but that was potentially insecure and
confusing in some (rather obscure) cases.
@@ -1854,7 +1854,7 @@ Will generate an object similar to:
## Exit Codes
Node.js will normally exit with a `0` status code when no more async
-operations are pending. The following status codes are used in other
+operations are pending. The following status codes are used in other
cases:
* `1` **Uncaught Fatal Exception** - There was an uncaught exception,
@@ -1862,12 +1862,12 @@ cases:
handler.
* `2` - Unused (reserved by Bash for builtin misuse)
* `3` **Internal JavaScript Parse Error** - The JavaScript source code
- internal in Node.js's bootstrapping process caused a parse error. This
+ internal in Node.js's bootstrapping process caused a parse error. This
is extremely rare, and generally can only happen during development
of Node.js itself.
* `4` **Internal JavaScript Evaluation Failure** - The JavaScript
source code internal in Node.js's bootstrapping process failed to
- return a function value when evaluated. This is extremely rare, and
+ return a function value when evaluated. This is extremely rare, and
generally can only happen during development of Node.js itself.
* `5` **Fatal Error** - There was a fatal unrecoverable error in V8.
Typically a message will be printed to stderr with the prefix `FATAL
@@ -1877,22 +1877,22 @@ cases:
function was somehow set to a non-function, and could not be called.
* `7` **Internal Exception Handler Run-Time Failure** - There was an
uncaught exception, and the internal fatal exception handler
- function itself threw an error while attempting to handle it. This
+ function itself threw an error while attempting to handle it. This
can happen, for example, if a [`'uncaughtException'`][] or
`domain.on('error')` handler throws an error.
-* `8` - Unused. In previous versions of Node.js, exit code 8 sometimes
+* `8` - Unused. In previous versions of Node.js, exit code 8 sometimes
indicated an uncaught exception.
* `9` - **Invalid Argument** - Either an unknown option was specified,
or an option requiring a value was provided without a value.
* `10` **Internal JavaScript Run-Time Failure** - The JavaScript
source code internal in Node.js's bootstrapping process threw an error
- when the bootstrapping function was called. This is extremely rare,
+ when the bootstrapping function was called. This is extremely rare,
and generally can only happen during development of Node.js itself.
* `12` **Invalid Debug Argument** - The `--inspect` and/or `--inspect-brk`
options were set, but the port number chosen was invalid or unavailable.
* `>128` **Signal Exits** - If Node.js receives a fatal signal such as
`SIGKILL` or `SIGHUP`, then its exit code will be `128` plus the
- value of the signal code. This is a standard POSIX practice, since
+ value of the signal code. This is a standard POSIX practice, since
exit codes are defined to be 7-bit integers, and signal exits set
the high-order bit, and then contain the value of the signal code.
diff --git a/doc/api/readline.md b/doc/api/readline.md
index 7ba1277dce2d9c..78571d7e993ef8 100644
--- a/doc/api/readline.md
+++ b/doc/api/readline.md
@@ -295,7 +295,7 @@ added: v0.1.98
* `shift` {boolean} `true` to indicate the `` key.
* `name` {string} The name of the a key.
-The `rl.write()` method will write either `data` or a key sequence identified
+The `rl.write()` method will write either `data` or a key sequence identified
by `key` to the `output`. The `key` argument is supported only if `output` is
a [TTY][] text terminal.
@@ -323,7 +323,7 @@ Interface's `input` *as if it were provided by the user*.
added: v0.7.7
-->
-* `stream` {Writable}
+* `stream` {stream.Writable}
* `dir` {number}
* `-1` - to the left from cursor
* `1` - to the right from cursor
@@ -338,7 +338,7 @@ in a specified direction identified by `dir`.
added: v0.7.7
-->
-* `stream` {Writable}
+* `stream` {stream.Writable}
The `readline.clearScreenDown()` method clears the given [TTY][] stream from
the current position of the cursor down.
@@ -362,9 +362,9 @@ changes:
-->
* `options` {Object}
- * `input` {Readable} The [Readable][] stream to listen to. This option is
+ * `input` {stream.Readable} The [Readable][] stream to listen to. This option is
*required*.
- * `output` {Writable} The [Writable][] stream to write readline data to.
+ * `output` {stream.Writable} The [Writable][] stream to write readline data to.
* `completer` {Function} An optional function used for Tab autocompletion.
* `terminal` {boolean} `true` if the `input` and `output` streams should be
treated like a TTY, and have ANSI/VT100 escape codes written to it.
@@ -444,7 +444,7 @@ function completer(linePartial, callback) {
added: v0.7.7
-->
-* `stream` {Writable}
+* `stream` {stream.Writable}
* `x` {number}
* `y` {number}
@@ -456,7 +456,7 @@ given [TTY][] `stream`.
added: v0.7.7
-->
-* `stream` {Readable}
+* `stream` {stream.Readable}
* `interface` {readline.Interface}
The `readline.emitKeypressEvents()` method causes the given [Readable][]
@@ -482,7 +482,7 @@ if (process.stdin.isTTY)
added: v0.7.7
-->
-* `stream` {Writable}
+* `stream` {stream.Writable}
* `dx` {number}
* `dy` {number}
diff --git a/doc/api/repl.md b/doc/api/repl.md
index 0ff4ee099cb428..1b6013cf4ca4ed 100644
--- a/doc/api/repl.md
+++ b/doc/api/repl.md
@@ -96,7 +96,7 @@ are declared at the global scope.
The default evaluator provides access to any variables that exist in the global
scope. It is possible to expose a variable to the REPL explicitly by assigning
-it to the `context` object associated with each `REPLServer`. For example:
+it to the `context` object associated with each `REPLServer`. For example:
```js
const repl = require('repl');
@@ -388,9 +388,9 @@ changes:
* `options` {Object|string}
* `prompt` {string} The input prompt to display. Defaults to `> `
(with a trailing space).
- * `input` {Readable} The Readable stream from which REPL input will be read.
+ * `input` {stream.Readable} The Readable stream from which REPL input will be read.
Defaults to `process.stdin`.
- * `output` {Writable} The Writable stream to which REPL output will be
+ * `output` {stream.Writable} The Writable stream to which REPL output will be
written. Defaults to `process.stdout`.
* `terminal` {boolean} If `true`, specifies that the `output` should be
treated as a TTY terminal, and have ANSI/VT100 escape codes written to it.
@@ -398,7 +398,7 @@ changes:
stream upon instantiation.
* `eval` {Function} The function to be used when evaluating each given line
of input. Defaults to an async wrapper for the JavaScript `eval()`
- function. An `eval` function can error with `repl.Recoverable` to indicate
+ function. An `eval` function can error with `repl.Recoverable` to indicate
the input was incomplete and prompt for additional lines.
* `useColors` {boolean} If `true`, specifies that the default `writer`
function should include ANSI color styling to REPL output. If a custom
@@ -467,7 +467,7 @@ environment variables:
- `NODE_REPL_HISTORY` - When a valid path is given, persistent REPL history
will be saved to the specified file rather than `.node_repl_history` in the
- user's home directory. Setting this value to `""` will disable persistent
+ user's home directory. Setting this value to `''` will disable persistent
REPL history. Whitespace will be trimmed from the value.
- `NODE_REPL_HISTORY_SIZE` - Defaults to `1000`. Controls how many lines of
history will be persisted if history is available. Must be a positive number.
diff --git a/doc/api/stream.md b/doc/api/stream.md
index d92d5fde23a38b..f0d393543d6516 100644
--- a/doc/api/stream.md
+++ b/doc/api/stream.md
@@ -394,7 +394,7 @@ changes:
-->
* `encoding` {string} The new default encoding
-* Returns: `this`
+* Returns: {this}
The `writable.setDefaultEncoding()` method sets the default `encoding` for a
[Writable][] stream.
@@ -525,7 +525,7 @@ A Writable stream in object mode will always ignore the `encoding` argument.
added: v8.0.0
-->
-* Returns: `this`
+* Returns: {this}
Destroy the stream, and emit the passed error. After this call, the
writable stream has ended. Implementors should not override this method,
@@ -572,8 +572,8 @@ The Readable can switch back to paused mode using one of the following:
* If there are no pipe destinations, by calling the
[`stream.pause()`][stream-pause] method.
-* If there are pipe destinations, by removing any [`'data'`][] event
- handlers, and removing all pipe destinations by calling the
+* If there are pipe destinations, by removing all pipe destinations.
+ Multiple pipe destinations may be removed by calling the
[`stream.unpipe()`][] method.
The important concept to remember is that a Readable will not generate data
@@ -810,7 +810,7 @@ readable.isPaused(); // === false
added: v0.9.4
-->
-* Returns: `this`
+* Returns: {this}
The `readable.pause()` method will cause a stream in flowing mode to stop
emitting [`'data'`][] events, switching out of flowing mode. Any data that
@@ -901,7 +901,7 @@ added: v0.9.4
-->
* `size` {number} Optional argument to specify how much data to read.
-* Return {string|Buffer|null}
+* Returns: {string|Buffer|null}
The `readable.read()` method pulls some data out of the internal buffer and
returns it. If no data available to be read, `null` is returned. By default,
@@ -950,7 +950,7 @@ event has been emitted will return `null`. No runtime error will be raised.
added: v0.9.4
-->
-* Returns: `this`
+* Returns: {this}
The `readable.resume()` method causes an explicitly paused Readable stream to
resume emitting [`'data'`][] events, switching the stream into flowing mode.
@@ -973,7 +973,7 @@ added: v0.9.4
-->
* `encoding` {string} The encoding to use.
-* Returns: `this`
+* Returns: {this}
The `readable.setEncoding()` method sets the character encoding for
data read from the Readable stream.
@@ -1411,7 +1411,7 @@ write succeeded.
All calls to `writable.write()` that occur between the time `writable._write()`
is called and the `callback` is called will cause the written data to be
-buffered. Once the `callback` is invoked, the stream will emit a [`'drain'`][]
+buffered. When the `callback` is invoked, the stream might emit a [`'drain'`][]
event. If a stream implementation is capable of processing multiple chunks of
data at once, the `writable._writev()` method should be implemented.
@@ -1457,7 +1457,7 @@ added: v8.0.0
argument.
The `_destroy()` method is called by [`writable.destroy()`][writable-destroy].
-It can be overriden by child classes but it **must not** be called directly.
+It can be overridden by child classes but it **must not** be called directly.
#### writable.\_final(callback)
Returns the bound address, the address family name, and port of the
-server as reported by the operating system. See [`net.Server.address()`][] for
+server as reported by the operating system. See [`net.Server.address()`][] for
more information.
### server.close([callback])
@@ -462,7 +462,7 @@ changes:
* `options` {Object}
* `isServer`: The SSL/TLS protocol is asymmetrical, TLSSockets must know if
they are to behave as a server or a client. If `true` the TLS socket will be
- instantiated as a server. Defaults to `false`.
+ instantiated as a server. Defaults to `false`.
* `server` {net.Server} An optional [`net.Server`][] instance.
* `requestCert`: Whether to authenticate the remote peer by requesting a
certificate. Clients always request a server certificate. Servers
@@ -620,7 +620,7 @@ For example:
{ ... another certificate, possibly with a .issuerCertificate ... },
raw: < RAW DER buffer >,
valid_from: 'Nov 11 09:52:22 2009 GMT',
- valid_to: 'Nov 6 09:52:22 2029 GMT',
+ valid_to: 'Nov 6 09:52:22 2029 GMT',
fingerprint: '2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF',
serialNumber: 'B9B0D332A1AA5635' }
```
@@ -822,7 +822,7 @@ changes:
rather than creating a new socket. Typically, this is an instance of
[`net.Socket`][], but any `Duplex` stream is allowed.
If this option is specified, `path`, `host` and `port` are ignored,
- except for certificate validation. Usually, a socket is already connected
+ except for certificate validation. Usually, a socket is already connected
when passed to `tls.connect()`, but it can be connected later. Note that
connection/disconnection/destruction of `socket` is the user's
responsibility, calling `tls.connect()` will not cause `net.connect()` to be
@@ -983,7 +983,7 @@ changes:
decrypted with `object.passphrase` if provided, or `options.passphrase` if it is not.
* `key` {string|string[]|Buffer|Buffer[]|Object[]} Optional private keys in
PEM format. PEM allows the option of private keys being encrypted. Encrypted
- keys will be decrypted with `options.passphrase`. Multiple keys using
+ keys will be decrypted with `options.passphrase`. Multiple keys using
different algorithms can be provided either as an array of unencrypted key
strings or buffers, or an array of objects in the form `{pem:
[, passphrase: ]}`. The object form can only occur in
@@ -996,7 +996,7 @@ changes:
consist of the PEM formatted certificate for a provided private `key`,
followed by the PEM formatted intermediate certificates (if any), in order,
and not including the root CA (the root CA must be pre-known to the peer,
- see `ca`). When providing multiple cert chains, they do not have to be in
+ see `ca`). When providing multiple cert chains, they do not have to be in
the same order as their private keys in `key`. If the intermediate
certificates are not provided, the peer will not be able to validate the
certificate, and the handshake will fail.
@@ -1006,7 +1006,7 @@ changes:
using this option. The value can be a string or Buffer, or an Array of
strings and/or Buffers. Any string or Buffer can contain multiple PEM CAs
concatenated together. The peer's certificate must be chainable to a CA
- trusted by the server for the connection to be authenticated. When using
+ trusted by the server for the connection to be authenticated. When using
certificates that are not chainable to a well-known CA, the certificate's CA
must be explicitly specified as a trusted or the connection will fail to
authenticate.
@@ -1018,7 +1018,7 @@ changes:
* `crl` {string|string[]|Buffer|Buffer[]} Optional PEM formatted
CRLs (Certificate Revocation Lists).
* `ciphers` {string} Optional cipher suite specification, replacing the
- default. For more information, see [modifying the default cipher suite][].
+ default. For more information, see [modifying the default cipher suite][].
* `honorCipherOrder` {boolean} Attempt to use the server's cipher suite
preferences instead of the client's. When `true`, causes
`SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see
@@ -1037,8 +1037,8 @@ changes:
for stronger security. If omitted or invalid, the parameters are silently
discarded and DHE ciphers will not be available.
* `secureProtocol` {string} Optional SSL method to use, default is
- `"SSLv23_method"`. The possible values are listed as [SSL_METHODS][], use
- the function names as strings. For example, `"SSLv3_method"` to force SSL
+ `'SSLv23_method'`. The possible values are listed as [SSL_METHODS][], use
+ the function names as strings. For example, `'SSLv3_method'` to force SSL
version 3.
* `secureOptions` {number} Optionally affect the OpenSSL protocol behavior,
which is not usually necessary. This should be used carefully if at all!
@@ -1124,7 +1124,7 @@ changes:
servers, the identity options (`pfx` or `key`/`cert`) are usually required.
* `secureConnectionListener` {Function}
-Creates a new [tls.Server][]. The `secureConnectionListener`, if provided, is
+Creates a new [tls.Server][]. The `secureConnectionListener`, if provided, is
automatically set as a listener for the [`'secureConnection'`][] event.
*Note*: The `ticketKeys` options is automatically shared between `cluster`
diff --git a/doc/api/url.md b/doc/api/url.md
index 00211095627a46..40cc002f048e81 100644
--- a/doc/api/url.md
+++ b/doc/api/url.md
@@ -51,7 +51,7 @@ properties of a WHATWG `URL` object.
├─────────────┴─────────────────────┴─────────────────────┴──────────┴────────────────┴───────┤
│ href │
└─────────────────────────────────────────────────────────────────────────────────────────────┘
-(all spaces in the "" line should be ignored -- they are purely for formatting)
+(all spaces in the "" line should be ignored — they are purely for formatting)
```
Parsing the URL string using the WHATWG API:
@@ -556,7 +556,7 @@ Instantiate a new `URLSearchParams` object with an iterable map in a way that
is similar to [`Map`][]'s constructor. `iterable` can be an Array or any
iterable object. That means `iterable` can be another `URLSearchParams`, in
which case the constructor will simply create a clone of the provided
-`URLSearchParams`. Elements of `iterable` are key-value pairs, and can
+`URLSearchParams`. Elements of `iterable` are key-value pairs, and can
themselves be any iterable object.
Duplicate keys are allowed.
@@ -1030,6 +1030,11 @@ The formatting process operates as follows:
### url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
* `urlString` {string} The URL string to parse.
@@ -1141,7 +1146,7 @@ console.log(myURL.origin);
```
[`Error`]: errors.html#errors_class_error
-[`JSON.stringify()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
+[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
[`TypeError`]: errors.html#errors_class_typeerror
[`URLSearchParams`]: #url_class_urlsearchparams
diff --git a/doc/api/util.md b/doc/api/util.md
index 52e9e41639e0ca..7932b815e4a5e9 100644
--- a/doc/api/util.md
+++ b/doc/api/util.md
@@ -82,9 +82,9 @@ added: v0.11.3
The `util.debuglog()` method is used to create a function that conditionally
writes debug messages to `stderr` based on the existence of the `NODE_DEBUG`
-environment variable. If the `section` name appears within the value of that
+environment variable. If the `section` name appears within the value of that
environment variable, then the returned function operates similar to
-[`console.error()`][]. If not, then the returned function is a no-op.
+[`console.error()`][]. If not, then the returned function is a no-op.
For example:
@@ -102,7 +102,7 @@ it will output something like:
FOO 3245: hello from foo [123]
```
-where `3245` is the process id. If it is not run with that
+where `3245` is the process id. If it is not run with that
environment variable set, then it will not print anything.
Multiple comma-separated `section` names may be specified in the `NODE_DEBUG`
@@ -172,7 +172,7 @@ corresponding argument. Supported placeholders are:
* `%d` - Number (integer or floating point value).
* `%i` - Integer.
* `%f` - Floating point value.
-* `%j` - JSON. Replaced with the string `'[Circular]'` if the argument
+* `%j` - JSON. Replaced with the string `'[Circular]'` if the argument
contains circular references.
* `%o` - Object. A string representation of an object
with generic JavaScript object formatting.
@@ -233,7 +233,7 @@ that the two styles are [semantically incompatible][].
* `constructor` {Function}
* `superConstructor` {Function}
-Inherit the prototype methods from one [constructor][] into another. The
+Inherit the prototype methods from one [constructor][] into another. The
prototype of `constructor` will be set to a new object created from
`superConstructor`.
@@ -1191,7 +1191,7 @@ Deprecated predecessor of `console.log`.
[`Array.isArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
[`Buffer.isBuffer()`]: buffer.html#buffer_class_method_buffer_isbuffer_obj
[`Error`]: errors.html#errors_class_error
-[`Object.assign()`]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
+[`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
[`console.error()`]: console.html#console_console_error_data_args
[`console.log()`]: console.html#console_console_log_data_args
[`util.inspect()`]: #util_util_inspect_object_options
@@ -1201,5 +1201,5 @@ Deprecated predecessor of `console.log`.
[Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors
[Internationalization]: intl.html
[WHATWG Encoding Standard]: https://encoding.spec.whatwg.org/
-[constructor]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor
+[constructor]: https://developer.mozilla.org/en-US/JavaScript/Reference/Global_Objects/Object/constructor
[semantically incompatible]: https://github.com/nodejs/node/issues/4179
diff --git a/doc/api/v8.md b/doc/api/v8.md
index 3684a61583cb37..63a4ded9fe96ea 100644
--- a/doc/api/v8.md
+++ b/doc/api/v8.md
@@ -144,7 +144,7 @@ after the VM has started may result in unpredictable behavior, including
crashes and data loss; or it may simply do nothing.
The V8 options available for a version of Node.js may be determined by running
-`node --v8-options`. An unofficial, community-maintained list of options
+`node --v8-options`. An unofficial, community-maintained list of options
and their effects is available [here][].
Usage:
diff --git a/doc/api/vm.md b/doc/api/vm.md
index 41101a5e5cf76f..842b0c8ac20cdd 100644
--- a/doc/api/vm.md
+++ b/doc/api/vm.md
@@ -116,7 +116,7 @@ changes:
will be thrown.
* `breakOnSigint`: if `true`, the execution will be terminated when
`SIGINT` (Ctrl+C) is received. Existing handlers for the
- event that have been attached via `process.on("SIGINT")` will be disabled
+ event that have been attached via `process.on('SIGINT')` will be disabled
during script execution, but will continue to work after that.
If execution is terminated, an [`Error`][] will be thrown.
diff --git a/doc/api/zlib.md b/doc/api/zlib.md
index 7c55b94b26706b..00d3a8c821d2eb 100644
--- a/doc/api/zlib.md
+++ b/doc/api/zlib.md
@@ -64,8 +64,8 @@ header is used to identify the compression encodings actually applied to a
message.
*Note*: the examples given below are drastically simplified to show
-the basic concept. Using `zlib` encoding can be expensive, and the results
-ought to be cached. See [Memory Usage Tuning][] for more information
+the basic concept. Using `zlib` encoding can be expensive, and the results
+ought to be cached. See [Memory Usage Tuning][] for more information
on the speed/memory/compression tradeoffs involved in `zlib` usage.
```js
@@ -165,7 +165,7 @@ The memory requirements for deflate are (in bytes):
(1 << (windowBits + 2)) + (1 << (memLevel + 9))
```
-That is: 128K for windowBits=15 + 128K for memLevel = 8
+That is: 128K for windowBits = 15 + 128K for memLevel = 8
(default values) plus a few kilobytes for small objects.
For example, to reduce the default memory requirements from 256K to 128K, the
@@ -178,20 +178,20 @@ const options = { windowBits: 14, memLevel: 7 };
This will, however, generally degrade compression.
The memory requirements for inflate are (in bytes) `1 << windowBits`.
-That is, 32K for windowBits=15 (default value) plus a few kilobytes
+That is, 32K for windowBits = 15 (default value) plus a few kilobytes
for small objects.
This is in addition to a single internal output slab buffer of size
`chunkSize`, which defaults to 16K.
The speed of `zlib` compression is affected most dramatically by the
-`level` setting. A higher level will result in better compression, but
-will take longer to complete. A lower level will result in less
+`level` setting. A higher level will result in better compression, but
+will take longer to complete. A lower level will result in less
compression, but will be much faster.
In general, greater memory usage options will mean that Node.js has to make
fewer calls to `zlib` because it will be able to process more data on
-each `write` operation. So, this is another factor that affects the
+each `write` operation. So, this is another factor that affects the
speed, at the cost of memory usage.
## Flushing
@@ -233,9 +233,9 @@ added: v0.5.8
All of the constants defined in `zlib.h` are also defined on
`require('zlib').constants`. In the normal course of operations, it will not be
-necessary to use these constants. They are documented so that their presence is
+necessary to use these constants. They are documented so that their presence is
not surprising. This section is taken almost directly from the
-[zlib documentation][]. See for more
+[zlib documentation][]. See for more
details.
*Note*: Previously, the constants were available directly from
@@ -296,7 +296,7 @@ changes:
-Each class takes an `options` object. All options are optional.
+Each class takes an `options` object. All options are optional.
Note that some options are only relevant when compressing, and are
ignored by the decompression classes.
diff --git a/doc/changelogs/CHANGELOG_V8.md b/doc/changelogs/CHANGELOG_V8.md
index 960f97ebc6e289..11b17bb89afd9b 100644
--- a/doc/changelogs/CHANGELOG_V8.md
+++ b/doc/changelogs/CHANGELOG_V8.md
@@ -10,6 +10,7 @@
+8.11.2 8.11.1 8.11.0 8.10.0
@@ -53,6 +54,239 @@
[Node.js Long Term Support Plan](https://github.com/nodejs/LTS) and
will be supported actively until April 2019 and maintained until December 2019.
+
+## 2018-05-15, Version 8.11.2 'Carbon' (LTS), @MylesBorins
+
+### Notable Changes
+
+* **deps**:
+ - update node-inspect to 1.11.3 (Jan Krems) [#18354](https://github.com/nodejs/node/pull/18354)
+ - update nghttp2 to 1.29.0 (James M Snell) [#17908](https://github.com/nodejs/node/pull/17908)
+* **http2**:
+ - Sync with current release stream
+* **n-api**:
+ - Sync with current release stream
+
+### Commits
+
+* [[`ce3866bdcc`](https://github.com/nodejs/node/commit/ce3866bdcc)] - **async_hooks**: clean up comments (Ali Ijaz Sheikh) [#18467](https://github.com/nodejs/node/pull/18467)
+* [[`86e3c89ea4`](https://github.com/nodejs/node/commit/86e3c89ea4)] - **benchmark**: improve compare output (Ruben Bridgewater) [#18597](https://github.com/nodejs/node/pull/18597)
+* [[`18be476116`](https://github.com/nodejs/node/commit/18be476116)] - **benchmark**: fix punycode test for --without-intl (Timothy Gu) [#16251](https://github.com/nodejs/node/pull/16251)
+* [[`88d3028e22`](https://github.com/nodejs/node/commit/88d3028e22)] - **build**: no longer have v8-debug.h as dependency. (Yang Guo) [#18677](https://github.com/nodejs/node/pull/18677)
+* [[`7b6d93c145`](https://github.com/nodejs/node/commit/7b6d93c145)] - **build**: add doc linting when runnning `make lint` (Camilo Gonzalez) [#18472](https://github.com/nodejs/node/pull/18472)
+* [[`9bce14172a`](https://github.com/nodejs/node/commit/9bce14172a)] - **build**: do not suppress output in make doc-only (Joyee Cheung) [#18507](https://github.com/nodejs/node/pull/18507)
+* [[`333d7dda84`](https://github.com/nodejs/node/commit/333d7dda84)] - **build**: make lint-js independent of local node (Joyee Cheung) [#18272](https://github.com/nodejs/node/pull/18272)
+* [[`d537f45aaa`](https://github.com/nodejs/node/commit/d537f45aaa)] - **build**: make lint-md independent of local node (Joyee Cheung) [#18272](https://github.com/nodejs/node/pull/18272)
+* [[`658dd409fd`](https://github.com/nodejs/node/commit/658dd409fd)] - **build**: refine static and shared lib build (Yihong Wang) [#17604](https://github.com/nodejs/node/pull/17604)
+* [[`659b2a1821`](https://github.com/nodejs/node/commit/659b2a1821)] - **build**: allow x86\_64 as a dest\_cpu alias for x64 (Rod Vagg) [#18052](https://github.com/nodejs/node/pull/18052)
+* [[`424703a556`](https://github.com/nodejs/node/commit/424703a556)] - **build**: add cflags for OpenBSD, remove stray comma. (Aaron Bieber) [#18448](https://github.com/nodejs/node/pull/18448)
+* [[`ab4809f195`](https://github.com/nodejs/node/commit/ab4809f195)] - **build,win**: restore vcbuild TAG functionality (Rod Vagg) [#18031](https://github.com/nodejs/node/pull/18031)
+* [[`bf4d0743be`](https://github.com/nodejs/node/commit/bf4d0743be)] - **cluster**: fix inspector port assignment (Santiago Gimeno) [#18696](https://github.com/nodejs/node/pull/18696)
+* [[`16bf5fed69`](https://github.com/nodejs/node/commit/16bf5fed69)] - **crypto**: reuse variable instead of reevaluation (Tobias Nießen) [#17735](https://github.com/nodejs/node/pull/17735)
+* [[`9acc7f3fbb`](https://github.com/nodejs/node/commit/9acc7f3fbb)] - **deps**: update nghttp2 to 1.29.0 (James M Snell) [#17908](https://github.com/nodejs/node/pull/17908)
+* [[`ab005592be`](https://github.com/nodejs/node/commit/ab005592be)] - **deps**: V8: backport 76c3ac5 from upstream (Ali Ijaz Sheikh) [#18298](https://github.com/nodejs/node/pull/18298)
+* [[`f12db24947`](https://github.com/nodejs/node/commit/f12db24947)] - **deps**: cherry-pick a803fad from upstream V8 (Michaël Zasso) [#19824](https://github.com/nodejs/node/pull/19824)
+* [[`09f5e252bf`](https://github.com/nodejs/node/commit/09f5e252bf)] - **deps**: cherry-pick 7abdadc from upstream V8 (Michaël Zasso) [#19824](https://github.com/nodejs/node/pull/19824)
+* [[`c97237bc10`](https://github.com/nodejs/node/commit/c97237bc10)] - **deps**: cherry-pick a4bddba from upstream V8 (Michaël Zasso) [#19824](https://github.com/nodejs/node/pull/19824)
+* [[`d02b72e799`](https://github.com/nodejs/node/commit/d02b72e799)] - **deps**: V8: backport 596d55a from upstream (Myles Borins) [#19477](https://github.com/nodejs/node/pull/19477)
+* [[`79a7a17312`](https://github.com/nodejs/node/commit/79a7a17312)] - **deps**: update node-inspect to 1.11.3 (Jan Krems) [#18354](https://github.com/nodejs/node/pull/18354)
+* [[`5394bc5c42`](https://github.com/nodejs/node/commit/5394bc5c42)] - **deps,src**: align ssize\_t ABI between Node & nghttp2 (Anna Henningsen) [#18565](https://github.com/nodejs/node/pull/18565)
+* [[`165d214a54`](https://github.com/nodejs/node/commit/165d214a54)] - **doc**: add Http2Session.connecting property (Pieter Mees) [#19842](https://github.com/nodejs/node/pull/19842)
+* [[`1ff3544a4b`](https://github.com/nodejs/node/commit/1ff3544a4b)] - **doc**: guard against md list parsing edge case (Vse Mozhet Byt) [#19647](https://github.com/nodejs/node/pull/19647)
+* [[`f59eab0165`](https://github.com/nodejs/node/commit/f59eab0165)] - **doc**: rename HTTP2 to HTTP/2 (Timothy Gu) [#19603](https://github.com/nodejs/node/pull/19603)
+* [[`da185cec8f`](https://github.com/nodejs/node/commit/da185cec8f)] - **doc**: add note about browsers and HTTP/2 (Steven) [#19476](https://github.com/nodejs/node/pull/19476)
+* [[`30070c7568`](https://github.com/nodejs/node/commit/30070c7568)] - **doc**: warn against concurrent http2stream.respondWithFD (Anna Henningsen) [#18762](https://github.com/nodejs/node/pull/18762)
+* [[`39267e8bb0`](https://github.com/nodejs/node/commit/39267e8bb0)] - **doc**: fix typo in http2.md (Vse Mozhet Byt) [#18602](https://github.com/nodejs/node/pull/18602)
+* [[`2da965c5b8`](https://github.com/nodejs/node/commit/2da965c5b8)] - **doc**: remove removed apis from http2 docs (Kelvin Jin) [#18439](https://github.com/nodejs/node/pull/18439)
+* [[`9a4a8c127e`](https://github.com/nodejs/node/commit/9a4a8c127e)] - **doc**: unify type linkification (Vse Mozhet Byt) [#18407](https://github.com/nodejs/node/pull/18407)
+* [[`15023c7d28`](https://github.com/nodejs/node/commit/15023c7d28)] - **doc**: fix documentation of http2Stream.pushstream() (Peter Dalgaard-Jensen) [#18258](https://github.com/nodejs/node/pull/18258)
+* [[`fac76f9a6b`](https://github.com/nodejs/node/commit/fac76f9a6b)] - **doc**: fix typo in http2stream.close param default (Moritz Peters) [#18166](https://github.com/nodejs/node/pull/18166)
+* [[`88babd5a23`](https://github.com/nodejs/node/commit/88babd5a23)] - **doc**: fix s/rstStream/close in example (James M Snell) [#18088](https://github.com/nodejs/node/pull/18088)
+* [[`d9d0d0e98e`](https://github.com/nodejs/node/commit/d9d0d0e98e)] - **doc**: update pushStream docs to use err first (James M Snell) [#18088](https://github.com/nodejs/node/pull/18088)
+* [[`940457394a`](https://github.com/nodejs/node/commit/940457394a)] - **doc**: compact eslint directives in common/README (Vse Mozhet Byt) [#17971](https://github.com/nodejs/node/pull/17971)
+* [[`e9de5a976b`](https://github.com/nodejs/node/commit/e9de5a976b)] - **doc**: re-alphabetise sections in common/README.md (Vse Mozhet Byt) [#17971](https://github.com/nodejs/node/pull/17971)
+* [[`c924adf33d`](https://github.com/nodejs/node/commit/c924adf33d)] - **doc**: fix code nits in common/README (Vse Mozhet Byt) [#17971](https://github.com/nodejs/node/pull/17971)
+* [[`0205b3f0c1`](https://github.com/nodejs/node/commit/0205b3f0c1)] - **doc**: correct spelling (sreepurnajasti) [#17911](https://github.com/nodejs/node/pull/17911)
+* [[`591f78bb0a`](https://github.com/nodejs/node/commit/591f78bb0a)] - **doc**: grammar fixes in http2.md (Rich Trott) [#17972](https://github.com/nodejs/node/pull/17972)
+* [[`35ee8943da`](https://github.com/nodejs/node/commit/35ee8943da)] - **doc**: add docs for common/http2.js utility (James M Snell) [#17942](https://github.com/nodejs/node/pull/17942)
+* [[`f0ba2c6ceb`](https://github.com/nodejs/node/commit/f0ba2c6ceb)] - **doc**: Add a missing comma (jiangq) [#19555](https://github.com/nodejs/node/pull/19555)
+* [[`7c6fa183cb`](https://github.com/nodejs/node/commit/7c6fa183cb)] - **doc**: fix typos on n-api (Kyle Robinson Young) [#19385](https://github.com/nodejs/node/pull/19385)
+* [[`1abb168838`](https://github.com/nodejs/node/commit/1abb168838)] - **doc**: fix n-api asynchronous threading docs (Eric Bickle) [#19073](https://github.com/nodejs/node/pull/19073)
+* [[`87d0fd8212`](https://github.com/nodejs/node/commit/87d0fd8212)] - **doc**: mark NAPI\_AUTO\_LENGTH as code (Tobias Nießen) [#18697](https://github.com/nodejs/node/pull/18697)
+* [[`58688d97dc`](https://github.com/nodejs/node/commit/58688d97dc)] - **doc**: fix exporting a function example (Aonghus O Nia) [#18661](https://github.com/nodejs/node/pull/18661)
+* [[`4d43607474`](https://github.com/nodejs/node/commit/4d43607474)] - **doc**: fix typo in n-api.md (Vse Mozhet Byt) [#18590](https://github.com/nodejs/node/pull/18590)
+* [[`9729278007`](https://github.com/nodejs/node/commit/9729278007)] - **doc**: small typo in n-api.md (iskore) [#18555](https://github.com/nodejs/node/pull/18555)
+* [[`7ed1dfef28`](https://github.com/nodejs/node/commit/7ed1dfef28)] - **doc**: remove usage of you in n-api doc (Michael Dawson) [#18528](https://github.com/nodejs/node/pull/18528)
+* [[`84e0a03727`](https://github.com/nodejs/node/commit/84e0a03727)] - **doc**: remove uannecessary Require (Michael Dawson) [#18184](https://github.com/nodejs/node/pull/18184)
+* [[`51513fdf9e`](https://github.com/nodejs/node/commit/51513fdf9e)] - **doc**: napi: make header style consistent (Ali Ijaz Sheikh) [#18122](https://github.com/nodejs/node/pull/18122)
+* [[`02ae3295d5`](https://github.com/nodejs/node/commit/02ae3295d5)] - **doc**: napi: fix unbalanced emphasis (Ali Ijaz Sheikh) [#18122](https://github.com/nodejs/node/pull/18122)
+* [[`79ecc2c586`](https://github.com/nodejs/node/commit/79ecc2c586)] - **doc**: updates examples to use NULL (Michael Dawson) [#18008](https://github.com/nodejs/node/pull/18008)
+* [[`b2213798a3`](https://github.com/nodejs/node/commit/b2213798a3)] - **doc**: fix MDN links to avoid redirections (Vse Mozhet Byt) [#18631](https://github.com/nodejs/node/pull/18631)
+* [[`f4ddaaec0e`](https://github.com/nodejs/node/commit/f4ddaaec0e)] - **doc**: move Fedor to TSC Emeritus (Myles Borins) [#18752](https://github.com/nodejs/node/pull/18752)
+* [[`b8f2acd2e8`](https://github.com/nodejs/node/commit/b8f2acd2e8)] - **doc**: add mmarchini to collaborators (Matheus Marchini) [#18740](https://github.com/nodejs/node/pull/18740)
+* [[`16f9631475`](https://github.com/nodejs/node/commit/16f9631475)] - **doc**: add history for url.parse (Steven) [#18685](https://github.com/nodejs/node/pull/18685)
+* [[`d30c3533ff`](https://github.com/nodejs/node/commit/d30c3533ff)] - **doc**: fix links to Style Guide and CPP Style Guide (Justin Lee) [#18683](https://github.com/nodejs/node/pull/18683)
+* [[`176ed1e9b1`](https://github.com/nodejs/node/commit/176ed1e9b1)] - **doc**: add devsnek to collaborators (Gus Caplan) [#18679](https://github.com/nodejs/node/pull/18679)
+* [[`25db460f03`](https://github.com/nodejs/node/commit/25db460f03)] - **doc**: expand on promises and async\_hooks (Ali Ijaz Sheikh) [#18540](https://github.com/nodejs/node/pull/18540)
+* [[`73adadd56a`](https://github.com/nodejs/node/commit/73adadd56a)] - **doc**: add section for strategic initiatives (Michael Dawson) [#17104](https://github.com/nodejs/node/pull/17104)
+* [[`8ca6d34801`](https://github.com/nodejs/node/commit/8ca6d34801)] - **doc**: add introduce about cli options (Weijia Wang) [#18475](https://github.com/nodejs/node/pull/18475)
+* [[`1ea1970c37`](https://github.com/nodejs/node/commit/1ea1970c37)] - **doc**: modify the return value of request.write() (陈刚) [#18526](https://github.com/nodejs/node/pull/18526)
+* [[`50bdf0ed78`](https://github.com/nodejs/node/commit/50bdf0ed78)] - **doc**: be more explicit in the sypnosis (Tim O. Peters) [#17977](https://github.com/nodejs/node/pull/17977)
+* [[`f8ad381e61`](https://github.com/nodejs/node/commit/f8ad381e61)] - **doc**: add missing meta for createCipheriv (Tobias Nießen) [#18651](https://github.com/nodejs/node/pull/18651)
+* [[`0071560eb4`](https://github.com/nodejs/node/commit/0071560eb4)] - **doc**: fix description of createDecipheriv (Tobias Nießen) [#18651](https://github.com/nodejs/node/pull/18651)
+* [[`c89781583b`](https://github.com/nodejs/node/commit/c89781583b)] - **doc**: fix various nits (Vse Mozhet Byt) [#19743](https://github.com/nodejs/node/pull/19743)
+* [[`1091dfc801`](https://github.com/nodejs/node/commit/1091dfc801)] - **doc**: linkify missing types (Vse Mozhet Byt) [#18444](https://github.com/nodejs/node/pull/18444)
+* [[`1107a494e4`](https://github.com/nodejs/node/commit/1107a494e4)] - **doc**: shell option for the execFile and execFileSync functions (jvelezpo) [#18237](https://github.com/nodejs/node/pull/18237)
+* [[`36ea472393`](https://github.com/nodejs/node/commit/36ea472393)] - **doc**: improve http.request documentation (Guangcong Luo) [#18289](https://github.com/nodejs/node/pull/18289)
+* [[`e5d5137963`](https://github.com/nodejs/node/commit/e5d5137963)] - **doc**: streamline README intro (Rich Trott) [#18483](https://github.com/nodejs/node/pull/18483)
+* [[`eec9334a2e`](https://github.com/nodejs/node/commit/eec9334a2e)] - **doc**: move Brian White to TSC Emeriti list (Rich Trott) [#18482](https://github.com/nodejs/node/pull/18482)
+* [[`ac41aacb05`](https://github.com/nodejs/node/commit/ac41aacb05)] - **doc**: improve stream documentation (陈刚) [#18375](https://github.com/nodejs/node/pull/18375)
+* [[`7feeb1574e`](https://github.com/nodejs/node/commit/7feeb1574e)] - **doc**: add Gibson Fahnestock to TSC (Rich Trott) [#18481](https://github.com/nodejs/node/pull/18481)
+* [[`142ad8d450`](https://github.com/nodejs/node/commit/142ad8d450)] - **doc**: reorder section on updating PR branch (Ali Ijaz Sheikh) [#18355](https://github.com/nodejs/node/pull/18355)
+* [[`39ea4f12c5`](https://github.com/nodejs/node/commit/39ea4f12c5)] - **doc**: fix manpage warnings (Roman Reiss)
+* [[`5209f9e1e2`](https://github.com/nodejs/node/commit/5209f9e1e2)] - **doc**: warn about GCM authenticity (Tobias Nießen) [#18376](https://github.com/nodejs/node/pull/18376)
+* [[`e84e9db6fe`](https://github.com/nodejs/node/commit/e84e9db6fe)] - **doc**: capitalize non-primitive types (Vse Mozhet Byt) [#18111](https://github.com/nodejs/node/pull/18111)
+* [[`84fa6eb173`](https://github.com/nodejs/node/commit/84fa6eb173)] - **doc, http2**: add sections for server.close() (Chris Miller) [#19802](https://github.com/nodejs/node/pull/19802)
+* [[`cbc8561949`](https://github.com/nodejs/node/commit/cbc8561949)] - **errors**: remove ERR\_OUTOFMEMORY (Tobias Nießen) [#17877](https://github.com/nodejs/node/pull/17877)
+* [[`2995506bbf`](https://github.com/nodejs/node/commit/2995506bbf)] - **fs**: fix stack overflow in fs.readdirSync (Joyee Cheung) [#18647](https://github.com/nodejs/node/pull/18647)
+* [[`a653f23dfc`](https://github.com/nodejs/node/commit/a653f23dfc)] - **fs**: fix `createReadStream(…, {end: n})` for non-seekable fds (Anna Henningsen) [#19329](https://github.com/nodejs/node/pull/19329)
+* [[`6bfdba125f`](https://github.com/nodejs/node/commit/6bfdba125f)] - **http**: remove default 'drain' listener on upgrade (Luigi Pinca) [#18866](https://github.com/nodejs/node/pull/18866)
+* [[`29c395d975`](https://github.com/nodejs/node/commit/29c395d975)] - **http**: allow \_httpMessage to be GC'ed (Luigi Pinca) [#18865](https://github.com/nodejs/node/pull/18865)
+* [[`d2a884edf9`](https://github.com/nodejs/node/commit/d2a884edf9)] - **http**: fix parsing of binary upgrade response body (Ben Noordhuis) [#17806](https://github.com/nodejs/node/pull/17806)
+* [[`1d88266543`](https://github.com/nodejs/node/commit/1d88266543)] - **http**: free the parser before emitting 'upgrade' (Luigi Pinca) [#18209](https://github.com/nodejs/node/pull/18209)
+* [[`1455b1dec2`](https://github.com/nodejs/node/commit/1455b1dec2)] - **http2**: emit session connect on next tick (Pieter Mees) [#19842](https://github.com/nodejs/node/pull/19842)
+* [[`6d6e2e2454`](https://github.com/nodejs/node/commit/6d6e2e2454)] - **http2**: callback valid check before closing request (Trivikram) [#19061](https://github.com/nodejs/node/pull/19061)
+* [[`eddf3a6c70`](https://github.com/nodejs/node/commit/eddf3a6c70)] - **http2**: destroy() stream, upon errnoException (Sarat Addepalli) [#19389](https://github.com/nodejs/node/pull/19389)
+* [[`e4c10e1201`](https://github.com/nodejs/node/commit/e4c10e1201)] - **http2**: remove some unnecessary next ticks (James M Snell) [#19451](https://github.com/nodejs/node/pull/19451)
+* [[`c976cb5be5`](https://github.com/nodejs/node/commit/c976cb5be5)] - **http2**: no stream destroy while its data is on the wire (Anna Henningsen) [#19002](https://github.com/nodejs/node/pull/19002)
+* [[`bfd7d6d0de`](https://github.com/nodejs/node/commit/bfd7d6d0de)] - **http2**: fix flaky test-http2-https-fallback (Matteo Collina) [#19093](https://github.com/nodejs/node/pull/19093)
+* [[`b75897f982`](https://github.com/nodejs/node/commit/b75897f982)] - **http2**: fix endless loop when writing empty string (Anna Henningsen) [#18924](https://github.com/nodejs/node/pull/18924)
+* [[`7e4a9c9fe2`](https://github.com/nodejs/node/commit/7e4a9c9fe2)] - **http2**: use original error for cancelling pending streams (Anna Henningsen) [#18988](https://github.com/nodejs/node/pull/18988)
+* [[`2a04f57444`](https://github.com/nodejs/node/commit/2a04f57444)] - **http2**: send error text in case of ALPN mismatch (Anna Henningsen) [#18986](https://github.com/nodejs/node/pull/18986)
+* [[`f366373ad8`](https://github.com/nodejs/node/commit/f366373ad8)] - **http2**: fix condition where data is lost (Matteo Collina) [#18895](https://github.com/nodejs/node/pull/18895)
+* [[`20fb59fdc4`](https://github.com/nodejs/node/commit/20fb59fdc4)] - **http2**: use `\_final` instead of `on('finish')` (Anna Henningsen) [#18609](https://github.com/nodejs/node/pull/18609)
+* [[`ac64b4f6a7`](https://github.com/nodejs/node/commit/ac64b4f6a7)] - **http2**: add checks for server close callback (James M Snell) [#18182](https://github.com/nodejs/node/pull/18182)
+* [[`8b0a1b32de`](https://github.com/nodejs/node/commit/8b0a1b32de)] - **http2**: refactor read mechanism (Anna Henningsen) [#18030](https://github.com/nodejs/node/pull/18030)
+* [[`a4d910c644`](https://github.com/nodejs/node/commit/a4d910c644)] - **http2**: remember sent headers (James M Snell) [#18045](https://github.com/nodejs/node/pull/18045)
+* [[`3cd205431b`](https://github.com/nodejs/node/commit/3cd205431b)] - **http2**: use aliased buffer for perf stats, add stats (James M Snell) [#18020](https://github.com/nodejs/node/pull/18020)
+* [[`46d1b331e0`](https://github.com/nodejs/node/commit/46d1b331e0)] - **http2**: verify flood error and unsolicited frames (James M Snell) [#17969](https://github.com/nodejs/node/pull/17969)
+* [[`a85518ed22`](https://github.com/nodejs/node/commit/a85518ed22)] - **http2**: verify that a dependency cycle may exist (James M Snell) [#17968](https://github.com/nodejs/node/pull/17968)
+* [[`9c85ada4e3`](https://github.com/nodejs/node/commit/9c85ada4e3)] - **http2**: implement maxSessionMemory (James M Snell) [#17967](https://github.com/nodejs/node/pull/17967)
+* [[`9a6ea7eb02`](https://github.com/nodejs/node/commit/9a6ea7eb02)] - **http2**: properly handle already closed stream error (James M Snell) [#17942](https://github.com/nodejs/node/pull/17942)
+* [[`0078a97793`](https://github.com/nodejs/node/commit/0078a97793)] - **http2**: add aligned padding strategy (James M Snell) [#17938](https://github.com/nodejs/node/pull/17938)
+* [[`1c313e09d6`](https://github.com/nodejs/node/commit/1c313e09d6)] - **http2**: add initial support for originSet (James M Snell) [#17935](https://github.com/nodejs/node/pull/17935)
+* [[`1a24feccb5`](https://github.com/nodejs/node/commit/1a24feccb5)] - **http2**: add altsvc support (James M Snell) [#17917](https://github.com/nodejs/node/pull/17917)
+* [[`c915bc54d4`](https://github.com/nodejs/node/commit/c915bc54d4)] - **http2**: strictly limit number on concurrent streams (James M Snell) [#16766](https://github.com/nodejs/node/pull/16766)
+* [[`dcc7f4d84c`](https://github.com/nodejs/node/commit/dcc7f4d84c)] - **http2**: perf\_hooks integration (James M Snell) [#17906](https://github.com/nodejs/node/pull/17906)
+* [[`72b42de33a`](https://github.com/nodejs/node/commit/72b42de33a)] - **http2**: implement ref() and unref() on client sessions (Kelvin Jin) [#17620](https://github.com/nodejs/node/pull/17620)
+* [[`55f6bdb698`](https://github.com/nodejs/node/commit/55f6bdb698)] - **http2**: keep session objects alive during Http2Scope (Anna Henningsen) [#17863](https://github.com/nodejs/node/pull/17863)
+* [[`c61a54ec3d`](https://github.com/nodejs/node/commit/c61a54ec3d)] - **http2**: fix compiling with `--debug-http2` (Anna Henningsen) [#17863](https://github.com/nodejs/node/pull/17863)
+* [[`04632214c1`](https://github.com/nodejs/node/commit/04632214c1)] - **http2**: convert Http2Settings to an AsyncWrap (James M Snell) [#17763](https://github.com/nodejs/node/pull/17763)
+* [[`ea98fd573e`](https://github.com/nodejs/node/commit/ea98fd573e)] - **http2**: refactor outgoing write mechanism (Anna Henningsen) [#17718](https://github.com/nodejs/node/pull/17718)
+* [[`05b823d4ad`](https://github.com/nodejs/node/commit/05b823d4ad)] - **http2**: remove redundant write indirection (Anna Henningsen) [#17718](https://github.com/nodejs/node/pull/17718)
+* [[`fc40b7de46`](https://github.com/nodejs/node/commit/fc40b7de46)] - **http2**: cleanup Http2Stream/Http2Session destroy (James M Snell) [#17406](https://github.com/nodejs/node/pull/17406)
+* [[`1d65f2b879`](https://github.com/nodejs/node/commit/1d65f2b879)] - **http2**: be sure to destroy the Http2Stream (James M Snell) [#17406](https://github.com/nodejs/node/pull/17406)
+* [[`8431b4297c`](https://github.com/nodejs/node/commit/8431b4297c)] - **http2**: only schedule write when necessary (Anna Henningsen) [#17183](https://github.com/nodejs/node/pull/17183)
+* [[`38cfb707bd`](https://github.com/nodejs/node/commit/38cfb707bd)] - **http2**: don't call into JS from GC (Anna Henningsen) [#17183](https://github.com/nodejs/node/pull/17183)
+* [[`a1539e5731`](https://github.com/nodejs/node/commit/a1539e5731)] - **http2**: simplify onSelectPadding (Anna Henningsen) [#17717](https://github.com/nodejs/node/pull/17717)
+* [[`9a4bac2081`](https://github.com/nodejs/node/commit/9a4bac2081)] - **http2,perf_hooks**: perf state using AliasedBuffer (Kyle Farnung) [#18300](https://github.com/nodejs/node/pull/18300)
+* [[`9129bc4fde`](https://github.com/nodejs/node/commit/9129bc4fde)] - **lib**: set process.execPath on OpenBSD (Aaron Bieber) [#18543](https://github.com/nodejs/node/pull/18543)
+* [[`2019b023a7`](https://github.com/nodejs/node/commit/2019b023a7)] - **lib**: refactor ES module loader for readability (Anna Henningsen) [#16579](https://github.com/nodejs/node/pull/16579)
+* [[`3df0570c90`](https://github.com/nodejs/node/commit/3df0570c90)] - **lib**: fix spelling in comments (Tobias Nießen) [#18018](https://github.com/nodejs/node/pull/18018)
+* [[`20844d1716`](https://github.com/nodejs/node/commit/20844d1716)] - **lib**: remove debugger dead code (Qingyan Li) [#18426](https://github.com/nodejs/node/pull/18426)
+* [[`07a6770614`](https://github.com/nodejs/node/commit/07a6770614)] - **n-api**: add more `int64\_t` tests (Kyle Farnung) [#19402](https://github.com/nodejs/node/pull/19402)
+* [[`8b3ef4660a`](https://github.com/nodejs/node/commit/8b3ef4660a)] - **n-api**: back up env before finalize (Gabriel Schulhof) [#19718](https://github.com/nodejs/node/pull/19718)
+* [[`92f699e021`](https://github.com/nodejs/node/commit/92f699e021)] - **n-api**: ensure in-module exceptions are propagated (Gabriel Schulhof) [#19537](https://github.com/nodejs/node/pull/19537)
+* [[`367113f5d7`](https://github.com/nodejs/node/commit/367113f5d7)] - **n-api**: bump version of n-api supported (Michael Dawson) [#19497](https://github.com/nodejs/node/pull/19497)
+* [[`24b8bb6708`](https://github.com/nodejs/node/commit/24b8bb6708)] - **n-api**: re-write test\_make\_callback (Gabriel Schulhof) [#19448](https://github.com/nodejs/node/pull/19448)
+* [[`3a6b7e610d`](https://github.com/nodejs/node/commit/3a6b7e610d)] - **n-api**: add napi\_fatal\_exception (Mathias Buus) [#19337](https://github.com/nodejs/node/pull/19337)
+* [[`9949d55ae9`](https://github.com/nodejs/node/commit/9949d55ae9)] - **n-api**: separate out async\_hooks test (Gabriel Schulhof) [#19392](https://github.com/nodejs/node/pull/19392)
+* [[`f29d8e0e8d`](https://github.com/nodejs/node/commit/f29d8e0e8d)] - **n-api**: add missing exception checking (Michael Dawson) [#19362](https://github.com/nodejs/node/pull/19362)
+* [[`faf94b1c49`](https://github.com/nodejs/node/commit/faf94b1c49)] - **n-api**: resolve promise in test (Gabriel Schulhof) [#19245](https://github.com/nodejs/node/pull/19245)
+* [[`df63adf7aa`](https://github.com/nodejs/node/commit/df63adf7aa)] - **n-api**: update documentation (Gabriel Schulhof) [#19078](https://github.com/nodejs/node/pull/19078)
+* [[`b26410e86f`](https://github.com/nodejs/node/commit/b26410e86f)] - **n-api**: update reference test (Gabriel Schulhof) [#19086](https://github.com/nodejs/node/pull/19086)
+* [[`cb3f90a1a9`](https://github.com/nodejs/node/commit/cb3f90a1a9)] - **n-api**: fix object test (Gabriel Schulhof) [#19039](https://github.com/nodejs/node/pull/19039)
+* [[`9244e1d234`](https://github.com/nodejs/node/commit/9244e1d234)] - **n-api**: remove extra reference from test (Gabriel Schulhof) [#18542](https://github.com/nodejs/node/pull/18542)
+* [[`927fc0b19f`](https://github.com/nodejs/node/commit/927fc0b19f)] - **n-api**: add methods to open/close callback scope (Michael Dawson) [#18089](https://github.com/nodejs/node/pull/18089)
+* [[`969a520990`](https://github.com/nodejs/node/commit/969a520990)] - **n-api**: wrap control flow macro in do/while (Ben Noordhuis) [#18532](https://github.com/nodejs/node/pull/18532)
+* [[`d89f5937eb`](https://github.com/nodejs/node/commit/d89f5937eb)] - **n-api**: implement wrapping using private properties (Gabriel Schulhof) [#18311](https://github.com/nodejs/node/pull/18311)
+* [[`af655f586c`](https://github.com/nodejs/node/commit/af655f586c)] - **n-api**: change assert ok check to notStrictEqual. (Aaron Kau) [#18414](https://github.com/nodejs/node/pull/18414)
+* [[`ca10fda064`](https://github.com/nodejs/node/commit/ca10fda064)] - **n-api**: throw RangeError napi\_create\_typedarray() (Jinho Bang) [#18037](https://github.com/nodejs/node/pull/18037)
+* [[`853b4d593c`](https://github.com/nodejs/node/commit/853b4d593c)] - **n-api**: expose n-api version in process.versions (Michael Dawson) [#18067](https://github.com/nodejs/node/pull/18067)
+* [[`48be8a4793`](https://github.com/nodejs/node/commit/48be8a4793)] - **n-api**: throw RangeError in napi\_create\_dataview() with invalid range (Jinho Bang) [#17869](https://github.com/nodejs/node/pull/17869)
+* [[`a744535f99`](https://github.com/nodejs/node/commit/a744535f99)] - **n-api**: fix memory leak in napi\_async\_destroy() (alnyan) [#17714](https://github.com/nodejs/node/pull/17714)
+* [[`584fadc605`](https://github.com/nodejs/node/commit/584fadc605)] - **n-api,test**: add int64 bounds tests (Kyle Farnung) [#19309](https://github.com/nodejs/node/pull/19309)
+* [[`4c1181dc02`](https://github.com/nodejs/node/commit/4c1181dc02)] - **n-api,test**: add a new.target test to addons-napi (Taylor Woll) [#19236](https://github.com/nodejs/node/pull/19236)
+* [[`3225601ffc`](https://github.com/nodejs/node/commit/3225601ffc)] - **net**: remove Socket.prototoype.read (Anna Henningsen) [#18568](https://github.com/nodejs/node/pull/18568)
+* [[`35aaee1059`](https://github.com/nodejs/node/commit/35aaee1059)] - **net**: remove redundant code from \_writeGeneric() (Luigi Pinca) [#18429](https://github.com/nodejs/node/pull/18429)
+* [[`54442efcd2`](https://github.com/nodejs/node/commit/54442efcd2)] - **perf_hooks**: refactor internals (James M Snell) [#17822](https://github.com/nodejs/node/pull/17822)
+* [[`6bdfb1f8f0`](https://github.com/nodejs/node/commit/6bdfb1f8f0)] - **perf_hooks,http2**: add performance.clear() (James M Snell) [#18046](https://github.com/nodejs/node/pull/18046)
+* [[`1faae90b74`](https://github.com/nodejs/node/commit/1faae90b74)] - **readline**: use Date.now() and move test to parallel (Anatoli Papirovski) [#18563](https://github.com/nodejs/node/pull/18563)
+* [[`965b56a34e`](https://github.com/nodejs/node/commit/965b56a34e)] - **readline**: update references to archived repository (Tobias Nießen) [#17924](https://github.com/nodejs/node/pull/17924)
+* [[`801a49935b`](https://github.com/nodejs/node/commit/801a49935b)] - **src**: add nullptr check for session in DEBUG macro (Daniel Bevenius) [#18815](https://github.com/nodejs/node/pull/18815)
+* [[`4e807d648e`](https://github.com/nodejs/node/commit/4e807d648e)] - **src**: introduce internal buffer slice constructor (Anna Henningsen) [#18030](https://github.com/nodejs/node/pull/18030)
+* [[`0b828e5125`](https://github.com/nodejs/node/commit/0b828e5125)] - **src**: remove declarations for missing functions (Anna Henningsen) [#18134](https://github.com/nodejs/node/pull/18134)
+* [[`3766e04f31`](https://github.com/nodejs/node/commit/3766e04f31)] - **src**: silence http2 -Wunused-result warnings (cjihrig) [#17954](https://github.com/nodejs/node/pull/17954)
+* [[`2b7732788a`](https://github.com/nodejs/node/commit/2b7732788a)] - **src**: add optional keep-alive object to SetImmediate (Anna Henningsen) [#17183](https://github.com/nodejs/node/pull/17183)
+* [[`f3e082c4ea`](https://github.com/nodejs/node/commit/f3e082c4ea)] - **src**: replace SetAccessor w/ SetAccessorProperty (Jure Triglav) [#17665](https://github.com/nodejs/node/pull/17665)
+* [[`45e28a8628`](https://github.com/nodejs/node/commit/45e28a8628)] - **src**: minor refactoring to StreamBase writes (Anna Henningsen) [#17564](https://github.com/nodejs/node/pull/17564)
+* [[`42b4f3ce0b`](https://github.com/nodejs/node/commit/42b4f3ce0b)] - **src**: fix abort when taking a heap snapshot (Ben Noordhuis) [#18898](https://github.com/nodejs/node/pull/18898)
+* [[`b48ca0a140`](https://github.com/nodejs/node/commit/b48ca0a140)] - **src**: fix crypto.pbkdf2 callback error argument (BufoViridis) [#18458](https://github.com/nodejs/node/pull/18458)
+* [[`973488b77b`](https://github.com/nodejs/node/commit/973488b77b)] - **src**: replace var for let / const. (alejandro estrada) [#18649](https://github.com/nodejs/node/pull/18649)
+* [[`9ac91b14de`](https://github.com/nodejs/node/commit/9ac91b14de)] - **src**: fix util abort (Ruben Bridgewater) [#19224](https://github.com/nodejs/node/pull/19224)
+* [[`c0f40be23b`](https://github.com/nodejs/node/commit/c0f40be23b)] - **src**: free memory before re-setting URLHost value (Ivan Filenko) [#18357](https://github.com/nodejs/node/pull/18357)
+* [[`5e4f9b37ba`](https://github.com/nodejs/node/commit/5e4f9b37ba)] - **src,doc,test**: Fix common misspellings (Roman Reiss) [#18151](https://github.com/nodejs/node/pull/18151)
+* [[`10231a9e44`](https://github.com/nodejs/node/commit/10231a9e44)] - **stream**: cleanup() when unpiping all streams. (陈刚) [#18266](https://github.com/nodejs/node/pull/18266)
+* [[`bf523822ba`](https://github.com/nodejs/node/commit/bf523822ba)] - **stream**: simplify `src.\_readableState` to `state` (陈刚) [#18264](https://github.com/nodejs/node/pull/18264)
+* [[`37e594ed4a`](https://github.com/nodejs/node/commit/37e594ed4a)] - **stream**: remove unreachable code (Luigi Pinca) [#18239](https://github.com/nodejs/node/pull/18239)
+* [[`f96b0bf494`](https://github.com/nodejs/node/commit/f96b0bf494)] - **string_decoder**: reset decoder on end (Justin Ridgewell) [#18494](https://github.com/nodejs/node/pull/18494)
+* [[`4dbdb8ae4e`](https://github.com/nodejs/node/commit/4dbdb8ae4e)] - **test**: http2 errors on req.close() (Trivikram) [#18854](https://github.com/nodejs/node/pull/18854)
+* [[`83d8ad351c`](https://github.com/nodejs/node/commit/83d8ad351c)] - **test**: http2 stream.respond() error checks (Trivikram) [#18861](https://github.com/nodejs/node/pull/18861)
+* [[`b0664426f5`](https://github.com/nodejs/node/commit/b0664426f5)] - **test**: check endless loop while writing empty string (XadillaX) [#18924](https://github.com/nodejs/node/pull/18924)
+* [[`7eba62e028`](https://github.com/nodejs/node/commit/7eba62e028)] - **test**: make test-tls-external-accessor agnostic (Rich Trott) [#16272](https://github.com/nodejs/node/pull/16272)
+* [[`9e68947ff4`](https://github.com/nodejs/node/commit/9e68947ff4)] - **test**: add hasCrypto when using binding('crypto') (Daniel Bevenius) [#17867](https://github.com/nodejs/node/pull/17867)
+* [[`6129ff4b99`](https://github.com/nodejs/node/commit/6129ff4b99)] - **test**: remove unnecessary timer (cjihrig) [#18719](https://github.com/nodejs/node/pull/18719)
+* [[`2838f9b150`](https://github.com/nodejs/node/commit/2838f9b150)] - **test**: convert new tests to use error types (Jack Horton) [#18581](https://github.com/nodejs/node/pull/18581)
+* [[`5c0983e5a2`](https://github.com/nodejs/node/commit/5c0983e5a2)] - **test**: improve error message output (Bhavani Shankar) [#18498](https://github.com/nodejs/node/pull/18498)
+* [[`bebcdfe382`](https://github.com/nodejs/node/commit/bebcdfe382)] - **test**: show pending exception error in napi tests (Ben Wilcox) [#18413](https://github.com/nodejs/node/pull/18413)
+* [[`5b1b74c5a5`](https://github.com/nodejs/node/commit/5b1b74c5a5)] - **test**: refactor addons-napi/test\_exception/test.js (Rich Trott) [#18340](https://github.com/nodejs/node/pull/18340)
+* [[`8cfa87832d`](https://github.com/nodejs/node/commit/8cfa87832d)] - **test**: fixed typos in napi test (furstenheim) [#18148](https://github.com/nodejs/node/pull/18148)
+* [[`ad8c079af7`](https://github.com/nodejs/node/commit/ad8c079af7)] - **test**: remove ambiguous error messages from test\_error (Nicholas Drane) [#17812](https://github.com/nodejs/node/pull/17812)
+* [[`2e100c82be`](https://github.com/nodejs/node/commit/2e100c82be)] - **test**: remove literals that obscure assert messages (Rich Trott) [#17642](https://github.com/nodejs/node/pull/17642)
+* [[`077e1870ae`](https://github.com/nodejs/node/commit/077e1870ae)] - **test**: add unhandled rejection guard (babygoat) [#17275](https://github.com/nodejs/node/pull/17275)
+* [[`9236332cc3`](https://github.com/nodejs/node/commit/9236332cc3)] - **test**: update a few tests to work on OpenBSD (Aaron Bieber) [#18543](https://github.com/nodejs/node/pull/18543)
+* [[`cbd698a521`](https://github.com/nodejs/node/commit/cbd698a521)] - **test**: refactor test-http-abort-before-end (cjihrig) [#18508](https://github.com/nodejs/node/pull/18508)
+* [[`ab8edc9d48`](https://github.com/nodejs/node/commit/ab8edc9d48)] - **test**: fix flaky timers-block-eventloop test (Anatoli Papirovski) [#18567](https://github.com/nodejs/node/pull/18567)
+* [[`53b702fdba`](https://github.com/nodejs/node/commit/53b702fdba)] - **test**: remove common.PORT from parallel tests (Rich Trott) [#17410](https://github.com/nodejs/node/pull/17410)
+* [[`da162278de`](https://github.com/nodejs/node/commit/da162278de)] - **test**: mock the lookup function in parallel tests (Joyee Cheung) [#17296](https://github.com/nodejs/node/pull/17296)
+* [[`34af49401b`](https://github.com/nodejs/node/commit/34af49401b)] - **test**: add common.dns.errorLookupMock (Joyee Cheung) [#17296](https://github.com/nodejs/node/pull/17296)
+* [[`bff7258535`](https://github.com/nodejs/node/commit/bff7258535)] - **test**: do not check TXT content in test-dns-any (Joyee Cheung) [#18547](https://github.com/nodejs/node/pull/18547)
+* [[`daeb6de8ec`](https://github.com/nodejs/node/commit/daeb6de8ec)] - **test**: use internet.addresses in internet tests (Joyee Cheung) [#16390](https://github.com/nodejs/node/pull/16390)
+* [[`7813a0de0a`](https://github.com/nodejs/node/commit/7813a0de0a)] - **test**: introduce test/common/internet.addresses (Joyee Cheung) [#16390](https://github.com/nodejs/node/pull/16390)
+* [[`745600a0b3`](https://github.com/nodejs/node/commit/745600a0b3)] - **test**: remove orphaned entries from status (Kyle Farnung) [#18092](https://github.com/nodejs/node/pull/18092)
+* [[`e42ab10957`](https://github.com/nodejs/node/commit/e42ab10957)] - **test**: add assertions for TextEncoder/Decoder (Sho Miyamoto) [#18132](https://github.com/nodejs/node/pull/18132)
+* [[`a1b0d5da07`](https://github.com/nodejs/node/commit/a1b0d5da07)] - **test**: move tmpdir to submodule of common (Rich Trott) [#17856](https://github.com/nodejs/node/pull/17856)
+* [[`5155d8e7c3`](https://github.com/nodejs/node/commit/5155d8e7c3)] - **test**: update references to archived repository (Tobias Nießen) [#17924](https://github.com/nodejs/node/pull/17924)
+* [[`1043b6fd7c`](https://github.com/nodejs/node/commit/1043b6fd7c)] - **test**: fix spelling in test case comments (Tobias Nießen) [#18018](https://github.com/nodejs/node/pull/18018)
+* [[`fdb4dbc04b`](https://github.com/nodejs/node/commit/fdb4dbc04b)] - **test**: remove destructor from node\_test\_fixture (Daniel Bevenius) [#18524](https://github.com/nodejs/node/pull/18524)
+* [[`e55e08c80e`](https://github.com/nodejs/node/commit/e55e08c80e)] - **test**: verify the shell option works properly on execFile (jvelezpo) [#18384](https://github.com/nodejs/node/pull/18384)
+* [[`0cf9d0483f`](https://github.com/nodejs/node/commit/0cf9d0483f)] - **test**: add test for tls benchmarks (Anatoli Papirovski) [#18489](https://github.com/nodejs/node/pull/18489)
+* [[`d630874250`](https://github.com/nodejs/node/commit/d630874250)] - **test**: speed up parallel/test-tls-session-cache (Anna Henningsen) [#18424](https://github.com/nodejs/node/pull/18424)
+* [[`a1fb263880`](https://github.com/nodejs/node/commit/a1fb263880)] - **test**: fix flaky test-http-dns-error (Bryan English) [#16534](https://github.com/nodejs/node/pull/16534)
+* [[`6f3fb46541`](https://github.com/nodejs/node/commit/6f3fb46541)] - **test**: use correct size in test-stream-buffer-list (Luigi Pinca) [#18239](https://github.com/nodejs/node/pull/18239)
+* [[`a52f15efae`](https://github.com/nodejs/node/commit/a52f15efae)] - **timers**: fix a bug in error handling (Anatoli Papirovski) [#20497](https://github.com/nodejs/node/pull/20497)
+* [[`e15f57745d`](https://github.com/nodejs/node/commit/e15f57745d)] - **timers**: allow Immediates to be unrefed (Anatoli Papirovski) [#18139](https://github.com/nodejs/node/pull/18139)
+* [[`95c1e2d606`](https://github.com/nodejs/node/commit/95c1e2d606)] - **tls**: set servername on client side too (James M Snell) [#17935](https://github.com/nodejs/node/pull/17935)
+* [[`d4bccccf23`](https://github.com/nodejs/node/commit/d4bccccf23)] - **tools**: add fixer for prefer-assert-iferror.js (Shobhit Chittora) [#16648](https://github.com/nodejs/node/pull/16648)
+* [[`016a28ac08`](https://github.com/nodejs/node/commit/016a28ac08)] - **tools**: non-Ascii linter for /lib only (Sarat Addepalli) [#18043](https://github.com/nodejs/node/pull/18043)
+* [[`a0a45fc3b6`](https://github.com/nodejs/node/commit/a0a45fc3b6)] - **tools**: add .mjs linting for Windows (Vse Mozhet Byt) [#18569](https://github.com/nodejs/node/pull/18569)
+* [[`e0d2842b29`](https://github.com/nodejs/node/commit/e0d2842b29)] - **tools**: add check for using process.binding crypto (Daniel Bevenius) [#17867](https://github.com/nodejs/node/pull/17867)
+* [[`a8b5a96d15`](https://github.com/nodejs/node/commit/a8b5a96d15)] - **tools**: auto fix custom eslint rule (Shobhit Chittora) [#16652](https://github.com/nodejs/node/pull/16652)
+* [[`5d03c8219a`](https://github.com/nodejs/node/commit/5d03c8219a)] - **url**: simplify loop in parser (Tobias Nießen) [#18468](https://github.com/nodejs/node/pull/18468)
+* [[`e0e0ef7bab`](https://github.com/nodejs/node/commit/e0e0ef7bab)] - **util**: escaping object keys in util.inspect() (buji) [#16986](https://github.com/nodejs/node/pull/16986)
+* [[`8ac69c457b`](https://github.com/nodejs/node/commit/8ac69c457b)] - **v8**: add missing ',' in OpenBSD's 'sources' section. (Aaron Bieber) [#18448](https://github.com/nodejs/node/pull/18448)
+* [[`c61754fad9`](https://github.com/nodejs/node/commit/c61754fad9)] - **win, build**: fix intl-none option (Birunthan Mohanathas) [#18292](https://github.com/nodejs/node/pull/18292)
+
## 2018-03-29, Version 8.11.1 'Carbon' (LTS), @MylesBorins
diff --git a/doc/guides/contributing/pull-requests.md b/doc/guides/contributing/pull-requests.md
index 5812c8c54645e2..3d7c548bbd3022 100644
--- a/doc/guides/contributing/pull-requests.md
+++ b/doc/guides/contributing/pull-requests.md
@@ -109,12 +109,12 @@ If you are modifying code, please be sure to run `make lint` from time to
time to ensure that the changes follow the Node.js code style guide.
Any documentation you write (including code comments and API documentation)
-should follow the [Style Guide](doc/STYLE_GUIDE.md). Code samples included
+should follow the [Style Guide](../../STYLE_GUIDE.md). Code samples included
in the API docs will also be checked when running `make lint` (or
`vcbuild.bat lint` on Windows).
For contributing C++ code, you may want to look at the
-[C++ Style Guide](CPP_STYLE_GUIDE.md).
+[C++ Style Guide](../../../CPP_STYLE_GUIDE.md).
### Step 4: Commit
diff --git a/doc/guides/writing-and-running-benchmarks.md b/doc/guides/writing-and-running-benchmarks.md
index 75435daf394898..740829fe1a51af 100644
--- a/doc/guides/writing-and-running-benchmarks.md
+++ b/doc/guides/writing-and-running-benchmarks.md
@@ -183,6 +183,17 @@ The `compare.js` tool will then produce a csv file with the benchmark results.
$ node benchmark/compare.js --old ./node-master --new ./node-pr-5134 string_decoder > compare-pr-5134.csv
```
+*Tips: there are some useful options of `benchmark/compare.js`. For example, if you want to compare the benchmark of a single script instead of a whole module, you can use the `--filter` option:*
+
+```console
+ --new ./new-node-binary new node binary (required)
+ --old ./old-node-binary old node binary (required)
+ --runs 30 number of samples
+ --filter pattern string to filter benchmark scripts
+ --set variable=value set benchmark variable (can be repeated)
+ --no-progress don't show benchmark progress indicator
+```
+
For analysing the benchmark results use the `compare.R` tool.
```console
diff --git a/doc/node.1 b/doc/node.1
index 3dfadc699c8749..50b8668007fdae 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -37,9 +37,9 @@ node \- Server-side JavaScript runtime
.RI [ script.js \ |
.B -e
.RI \&" script \&"
-.R |
+.RI |
.B -
-.R ]
+.RI ]
.B [--]
.RI [ arguments ]
.br
diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml
index 437aa575645ad6..0b00638e2a638c 100644
--- a/lib/.eslintrc.yaml
+++ b/lib/.eslintrc.yaml
@@ -6,3 +6,4 @@ rules:
buffer-constructor: error
no-let-in-for-declaration: error
lowercase-name-for-primitive: error
+ non-ascii-character: error
diff --git a/lib/_http_agent.js b/lib/_http_agent.js
index 5f1e56caeab981..7586a48680bb6a 100644
--- a/lib/_http_agent.js
+++ b/lib/_http_agent.js
@@ -277,6 +277,7 @@ function installListeners(agent, s, options) {
s.removeListener('close', onClose);
s.removeListener('free', onFree);
s.removeListener('agentRemove', onRemove);
+ s._httpMessage = null;
}
s.on('agentRemove', onRemove);
}
diff --git a/lib/_http_client.js b/lib/_http_client.js
index 0170b2c9b0e6fb..dfab2fce131122 100644
--- a/lib/_http_client.js
+++ b/lib/_http_client.js
@@ -37,7 +37,7 @@ const { OutgoingMessage } = require('_http_outgoing');
const Agent = require('_http_agent');
const { Buffer } = require('buffer');
const { urlToOptions, searchParamsSymbol } = require('internal/url');
-const { outHeadersKey } = require('internal/http');
+const { outHeadersKey, ondrain } = require('internal/http');
const { nextTick } = require('internal/process/next_tick');
// The actual list of disallowed characters in regexp form is more like:
@@ -452,7 +452,9 @@ function socketOnData(d) {
socket.removeListener('data', socketOnData);
socket.removeListener('end', socketOnEnd);
+ socket.removeListener('drain', ondrain);
parser.finish();
+ freeParser(parser, req, socket);
var bodyHead = d.slice(bytesParsed, d.length);
@@ -475,7 +477,6 @@ function socketOnData(d) {
// Got Upgrade header or CONNECT method, but have no handler.
socket.destroy();
}
- freeParser(parser, req, socket);
} else if (parser.incoming && parser.incoming.complete &&
// When the status code is 100 (Continue), the server will
// send a final response after this client sends a request
@@ -493,7 +494,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
var socket = this.socket;
var req = socket._httpMessage;
-
// propagate "domain" setting...
if (req.domain && !res.domain) {
debug('setting "res.domain"');
@@ -506,29 +506,22 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
// We already have a response object, this means the server
// sent a double response.
socket.destroy();
- return;
+ return 0; // No special treatment.
}
req.res = res;
// Responses to CONNECT request is handled as Upgrade.
- if (req.method === 'CONNECT') {
+ const method = req.method;
+ if (method === 'CONNECT') {
res.upgrade = true;
- return 2; // skip body, and the rest
+ return 2; // Skip body and treat as Upgrade.
}
- // Responses to HEAD requests are crazy.
- // HEAD responses aren't allowed to have an entity-body
- // but *can* have a content-length which actually corresponds
- // to the content-length of the entity-body had the request
- // been a GET.
- var isHeadResponse = req.method === 'HEAD';
- debug('AGENT isHeadResponse', isHeadResponse);
-
if (res.statusCode === 100) {
// restart the parser, as this is a continue message.
req.res = null; // Clear res so that we don't hit double-responses.
req.emit('continue');
- return true;
+ return 1; // Skip body but don't treat as Upgrade.
}
if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
@@ -538,7 +531,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
req.shouldKeepAlive = false;
}
-
DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
LTTNG_HTTP_CLIENT_RESPONSE(socket, req);
COUNTER_HTTP_CLIENT_RESPONSE();
@@ -556,7 +548,10 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
if (!handled)
res._dump();
- return isHeadResponse;
+ if (method === 'HEAD')
+ return 1; // Skip body but don't treat as Upgrade.
+
+ return 0; // No special treatment.
}
// client
diff --git a/lib/_http_common.js b/lib/_http_common.js
index ad0dec520d1210..381ffeb807a84e 100644
--- a/lib/_http_common.js
+++ b/lib/_http_common.js
@@ -106,19 +106,10 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
parser.incoming.upgrade = upgrade;
- var skipBody = 0; // response to HEAD or CONNECT
+ if (upgrade)
+ return 2; // Skip body and treat as Upgrade.
- if (!upgrade) {
- // For upgraded connections and CONNECT method request, we'll emit this
- // after parser.execute so that we can capture the first part of the new
- // protocol.
- skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);
- }
-
- if (typeof skipBody !== 'number')
- return skipBody ? 1 : 0;
- else
- return skipBody;
+ return parser.onIncoming(parser.incoming, shouldKeepAlive);
}
// XXX This is a mess.
diff --git a/lib/_http_server.js b/lib/_http_server.js
index 32c39e6160e5a9..be591c437ca083 100644
--- a/lib/_http_server.js
+++ b/lib/_http_server.js
@@ -618,7 +618,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
} else {
server.emit('request', req, res);
}
- return false; // Not a HEAD response. (Not even a response!)
+ return 0; // No special treatment.
}
function resetSocketTimeout(server, socket, state) {
diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js
index a75f3fa74cba22..cd8669830a5368 100644
--- a/lib/_stream_readable.js
+++ b/lib/_stream_readable.js
@@ -645,8 +645,8 @@ Readable.prototype.pipe = function(dest, pipeOpts) {
if (((state.pipesCount === 1 && state.pipes === dest) ||
(state.pipesCount > 1 && state.pipes.indexOf(dest) !== -1)) &&
!cleanedUp) {
- debug('false write response, pause', src._readableState.awaitDrain);
- src._readableState.awaitDrain++;
+ debug('false write response, pause', state.awaitDrain);
+ state.awaitDrain++;
increasedAwaitDrain = true;
}
src.pause();
@@ -747,7 +747,7 @@ Readable.prototype.unpipe = function(dest) {
state.flowing = false;
for (var i = 0; i < len; i++)
- dests[i].emit('unpipe', this, unpipeInfo);
+ dests[i].emit('unpipe', this, { hasUnpiped: false });
return this;
}
diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
index 598bfaa5ae8c3e..4bd8ecbf48437d 100644
--- a/lib/_tls_wrap.js
+++ b/lib/_tls_wrap.js
@@ -628,7 +628,7 @@ TLSSocket.prototype._finishInit = function() {
this.alpnProtocol = this.ssl.getALPNNegotiatedProtocol();
}
- if (process.features.tls_sni && this._tlsOptions.isServer) {
+ if (process.features.tls_sni) {
this.servername = this._handle.getServername();
}
diff --git a/lib/console.js b/lib/console.js
index d0f7e61fd5a709..4495074231a2eb 100644
--- a/lib/console.js
+++ b/lib/console.js
@@ -81,7 +81,7 @@ function createWriteErrorHandler(stream) {
// If there was an error, it will be emitted on `stream` as
// an `error` event. Adding a `once` listener will keep that error
// from becoming an uncaught exception, but since the handler is
- // removed after the event, non-console.* writes won’t be affected.
+ // removed after the event, non-console.* writes won't be affected.
// we are only adding noop if there is no one else listening for 'error'
if (stream.listenerCount('error') === 0) {
stream.on('error', noop);
@@ -114,7 +114,7 @@ function write(ignoreErrors, stream, string, errorhandler, groupIndent) {
// even in edge cases such as low stack space.
if (e.message === 'Maximum call stack size exceeded')
throw e;
- // Sorry, there’s no proper way to pass along the error here.
+ // Sorry, there's no proper way to pass along the error here.
} finally {
stream.removeListener('error', noop);
}
diff --git a/lib/fs.js b/lib/fs.js
index bfd085b4213881..b5565201e2abdd 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -1950,8 +1950,7 @@ function ReadStream(path, options) {
this.flags = options.flags === undefined ? 'r' : options.flags;
this.mode = options.mode === undefined ? 0o666 : options.mode;
- this.start = typeof this.fd !== 'number' && options.start === undefined ?
- 0 : options.start;
+ this.start = options.start;
this.end = options.end;
this.autoClose = options.autoClose === undefined ? true : options.autoClose;
this.pos = undefined;
@@ -1974,6 +1973,12 @@ function ReadStream(path, options) {
this.pos = this.start;
}
+ // Backwards compatibility: Make sure `end` is a number regardless of `start`.
+ // TODO(addaleax): Make the above typecheck not depend on `start` instead.
+ // (That is a semver-major change).
+ if (typeof this.end !== 'number')
+ this.end = Infinity;
+
if (typeof this.fd !== 'number')
this.open();
@@ -2028,6 +2033,8 @@ ReadStream.prototype._read = function(n) {
if (this.pos !== undefined)
toRead = Math.min(this.end - this.pos + 1, toRead);
+ else
+ toRead = Math.min(this.end - this.bytesRead + 1, toRead);
// already read everything we were supposed to read!
// treat as EOF.
diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js
index 06f6b06f093804..e14a0000640e7d 100644
--- a/lib/internal/async_hooks.js
+++ b/lib/internal/async_hooks.js
@@ -11,16 +11,17 @@ const async_wrap = process.binding('async_wrap');
* the various asynchronous states of the application. These are:
* kExecutionAsyncId: The async_id assigned to the resource responsible for the
* current execution stack.
- * kTriggerAsyncId: The trigger_async_id of the resource responsible for
- * the current execution stack.
+ * kTriggerAsyncId: The async_id of the resource that caused (or 'triggered')
+ * the resource corresponding to the current execution stack.
* kAsyncIdCounter: Incremental counter tracking the next assigned async_id.
* kDefaultTriggerAsyncId: Written immediately before a resource's constructor
- * that sets the value of the init()'s triggerAsyncId. The order of
- * retrieving the triggerAsyncId value is passing directly to the
- * constructor -> value set in kDefaultTriggerAsyncId -> executionAsyncId of
- * the current resource.
+ * that sets the value of the init()'s triggerAsyncId. The precedence order
+ * of retrieving the triggerAsyncId value is:
+ * 1. the value passed directly to the constructor
+ * 2. value set in kDefaultTriggerAsyncId
+ * 3. executionAsyncId of the current resource.
*
- * async_ids_fast_stack is a Float64Array that contains part of the async ID
+ * async_ids_stack is a Float64Array that contains part of the async ID
* stack. Each pushAsyncIds() call adds two doubles to it, and each
* popAsyncIds() call removes two doubles from it.
* It has a fixed size, so if that is exceeded, calls to the native
@@ -28,12 +29,12 @@ const async_wrap = process.binding('async_wrap');
*/
const { async_id_symbol, async_hook_fields, async_id_fields } = async_wrap;
// Store the pair executionAsyncId and triggerAsyncId in a std::stack on
-// Environment::AsyncHooks::ids_stack_ tracks the resource responsible for the
-// current execution stack. This is unwound as each resource exits. In the case
-// of a fatal exception this stack is emptied after calling each hook's after()
-// callback.
+// Environment::AsyncHooks::async_ids_stack_ tracks the resource responsible for
+// the current execution stack. This is unwound as each resource exits. In the
+// case of a fatal exception this stack is emptied after calling each hook's
+// after() callback.
const { pushAsyncIds: pushAsyncIds_, popAsyncIds: popAsyncIds_ } = async_wrap;
-// For performance reasons, only track Proimses when a hook is enabled.
+// For performance reasons, only track Promises when a hook is enabled.
const { enablePromiseHook, disablePromiseHook } = async_wrap;
// Properties in active_hooks are used to keep track of the set of hooks being
// executed in case another hook is enabled/disabled. The new set of hooks is
@@ -264,7 +265,7 @@ function getOrSetAsyncId(object) {
// the user to safeguard this call and make sure it's zero'd out when the
// constructor is complete.
function getDefaultTriggerAsyncId() {
- var defaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId];
+ let defaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId];
// If defaultTriggerAsyncId isn't set, use the executionAsyncId
if (defaultTriggerAsyncId < 0)
defaultTriggerAsyncId = async_id_fields[kExecutionAsyncId];
@@ -278,7 +279,7 @@ function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) {
const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId];
async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId;
- var ret;
+ let ret;
try {
ret = Reflect.apply(block, null, args);
} finally {
diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js
index 9af547a923022b..6b7ac044f638af 100644
--- a/lib/internal/bootstrap_node.js
+++ b/lib/internal/bootstrap_node.js
@@ -58,11 +58,7 @@
NativeModule.require('internal/trace_events_async_hooks').setup();
NativeModule.require('internal/inspector_async_hook').setup();
- // Do not initialize channel in debugger agent, it deletes env variable
- // and the main thread won't see it.
- if (process.argv[1] !== '--debug-agent')
- _process.setupChannel();
-
+ _process.setupChannel();
_process.setupRawDebug();
const browserGlobals = !process._noBrowserGlobals;
@@ -75,6 +71,13 @@
// URL::ToObject() method is used.
NativeModule.require('internal/url');
+ // On OpenBSD process.execPath will be relative unless we
+ // get the full path before process.execPath is used.
+ if (process.platform === 'openbsd') {
+ const { realpathSync } = NativeModule.require('fs');
+ process.execPath = realpathSync.native(process.execPath);
+ }
+
Object.defineProperty(process, 'argv0', {
enumerable: true,
configurable: false,
@@ -175,7 +178,7 @@
const fs = NativeModule.require('fs');
// read the source
const filename = Module._resolveFilename(process.argv[1]);
- var source = fs.readFileSync(filename, 'utf-8');
+ const source = fs.readFileSync(filename, 'utf-8');
checkScriptSyntax(source, filename);
process.exit(0);
}
@@ -221,7 +224,7 @@
// Read all of stdin - execute it.
process.stdin.setEncoding('utf8');
- var code = '';
+ let code = '';
process.stdin.on('data', function(d) {
code += d;
});
@@ -415,7 +418,7 @@
const versionTypes = icu.getVersion().split(',');
for (var n = 0; n < versionTypes.length; n++) {
- var name = versionTypes[n];
+ const name = versionTypes[n];
const version = icu.getVersion(name);
Object.defineProperty(process.versions, name, {
writable: false,
@@ -583,7 +586,7 @@
];
NativeModule.prototype.compile = function() {
- var source = NativeModule.getSource(this.id);
+ let source = NativeModule.getSource(this.id);
source = NativeModule.wrap(source);
this.loading = true;
diff --git a/lib/internal/cluster/master.js b/lib/internal/cluster/master.js
index 27a591a6f8d6d5..4836ede540a9cf 100644
--- a/lib/internal/cluster/master.js
+++ b/lib/internal/cluster/master.js
@@ -14,6 +14,7 @@ const intercom = new EventEmitter();
const SCHED_NONE = 1;
const SCHED_RR = 2;
const { isLegalPort } = require('internal/net');
+const [ minPort, maxPort ] = [ 1024, 65535 ];
module.exports = cluster;
@@ -119,6 +120,8 @@ function createWorkerProcess(id, env) {
}
} else {
inspectPort = process.debugPort + debugPortOffset;
+ if (inspectPort > maxPort)
+ inspectPort = inspectPort - maxPort + minPort - 1;
debugPortOffset++;
}
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 76ca6098f83868..551817b343bf63 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -150,6 +150,12 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA',
E('ERR_ENCODING_NOT_SUPPORTED',
(enc) => `The "${enc}" encoding is not supported`);
E('ERR_FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value');
+E('ERR_HTTP2_ALREADY_SHUTDOWN',
+ 'Http2Session is already shutdown or destroyed');
+E('ERR_HTTP2_ALTSVC_INVALID_ORIGIN',
+ 'HTTP/2 ALTSVC frames require a valid origin');
+E('ERR_HTTP2_ALTSVC_LENGTH',
+ 'HTTP/2 ALTSVC frames are limited to 16382 bytes');
E('ERR_HTTP2_CONNECT_AUTHORITY',
':authority header is required for CONNECT requests');
E('ERR_HTTP2_CONNECT_PATH',
@@ -164,6 +170,8 @@ E('ERR_HTTP2_FRAME_ERROR',
msg += ` with code ${code}`;
return msg;
});
+E('ERR_HTTP2_GOAWAY_SESSION',
+ 'New streams cannot be created after receiving a GOAWAY');
E('ERR_HTTP2_HEADERS_AFTER_RESPOND',
'Cannot specify additional headers after response initiated');
E('ERR_HTTP2_HEADERS_OBJECT', 'Headers must be an object');
@@ -202,15 +210,14 @@ E('ERR_HTTP2_PING_LENGTH', 'HTTP2 ping payload must be 8 bytes');
E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', 'Cannot set HTTP/2 pseudo-headers');
E('ERR_HTTP2_PUSH_DISABLED', 'HTTP/2 client has disabled push streams');
E('ERR_HTTP2_SEND_FILE', 'Only regular files can be sent');
+E('ERR_HTTP2_SESSION_ERROR', 'Session closed with error code %s');
E('ERR_HTTP2_SOCKET_BOUND',
'The socket is already bound to an Http2Session');
E('ERR_HTTP2_STATUS_101',
'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2');
-E('ERR_HTTP2_STATUS_INVALID',
- (code) => `Invalid status code: ${code}`);
-E('ERR_HTTP2_STREAM_CLOSED', 'The stream is already closed');
-E('ERR_HTTP2_STREAM_ERROR',
- (code) => `Stream closed with error code ${code}`);
+E('ERR_HTTP2_STATUS_INVALID', 'Invalid status code: %s');
+E('ERR_HTTP2_STREAM_CANCEL', 'The pending stream has been canceled');
+E('ERR_HTTP2_STREAM_ERROR', 'Stream closed with error code %s');
E('ERR_HTTP2_STREAM_SELF_DEPENDENCY', 'A stream cannot depend on itself');
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL',
(protocol) => `protocol "${protocol}" is unsupported.`);
@@ -230,6 +237,7 @@ E('ERR_INVALID_ARRAY_LENGTH',
});
E('ERR_INVALID_ASYNC_ID', (type, id) => `Invalid ${type} value: ${id}`);
E('ERR_INVALID_CALLBACK', 'callback must be a function');
+E('ERR_INVALID_CHAR', 'Invalid character in %s');
E('ERR_INVALID_FD', (fd) => `"fd" must be a positive integer: ${fd}`);
E('ERR_INVALID_FILE_URL_HOST', 'File URL host %s');
E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s');
@@ -257,14 +265,23 @@ E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected');
E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe');
E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks');
E('ERR_MISSING_ARGS', missingArgs);
+E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK',
+ 'The ES Module loader may not return a format of \'dynamic\' when no ' +
+ 'dynamicInstantiate function was provided');
E('ERR_MISSING_MODULE', 'Cannot find module %s');
E('ERR_MODULE_RESOLUTION_LEGACY', '%s not found by import in %s.' +
' Legacy behavior in require() would have found it at %s');
E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function');
E('ERR_NAPI_CONS_PROTOTYPE_OBJECT', 'Constructor.prototype must be an object');
+E('ERR_NAPI_INVALID_DATAVIEW_ARGS',
+ 'byte_offset + byte_length should be less than or eqaul to the size in ' +
+ 'bytes of the array passed in');
+E('ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT', 'start offset of %s should be a ' +
+ 'multiple of %s');
+E('ERR_NAPI_INVALID_TYPEDARRAY_LENGTH', 'Invalid typed array length');
E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support');
E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU');
-E('ERR_OUTOFMEMORY', 'Out of memory');
+E('ERR_OUT_OF_RANGE', 'The "%s" argument is out of range');
E('ERR_PARSE_HISTORY_DATA', 'Could not parse history data in %s');
E('ERR_REQUIRE_ESM', 'Must use import to load ES Module: %s');
E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound');
diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js
index ec1f0ba64eff0a..b5dd81c80f4038 100644
--- a/lib/internal/http2/compat.js
+++ b/lib/internal/http2/compat.js
@@ -126,14 +126,11 @@ function onStreamAbortedRequest() {
const request = this[kRequest];
if (request !== undefined && request[kState].closed === false) {
request.emit('aborted');
- request.emit('close');
}
}
function onStreamAbortedResponse() {
- const response = this[kResponse];
- if (response !== undefined && response[kState].closed === false)
- response.emit('close');
+ // non-op for now
}
function resumeStream(stream) {
@@ -234,9 +231,7 @@ class Http2ServerRequest extends Readable {
stream.on('end', onStreamEnd);
stream.on('error', onStreamError);
stream.on('aborted', onStreamAbortedRequest);
- const onfinish = this[kFinish].bind(this);
- stream.on('close', onfinish);
- stream.on('finish', onfinish);
+ stream.on('close', this[kFinish].bind(this));
this.on('pause', onRequestPause);
this.on('resume', onRequestResume);
}
@@ -297,7 +292,7 @@ class Http2ServerRequest extends Readable {
state.didRead = true;
process.nextTick(resumeStream, this[kStream]);
} else {
- this.emit('error', new errors.Error('ERR_HTTP2_STREAM_CLOSED'));
+ this.emit('error', new errors.Error('ERR_HTTP2_INVALID_STREAM'));
}
}
@@ -345,6 +340,7 @@ class Http2ServerRequest extends Readable {
// dump it for compatibility with http1
if (!state.didRead && !this._readableState.resumeScheduled)
this.resume();
+ this.emit('close');
}
}
@@ -366,9 +362,7 @@ class Http2ServerResponse extends Stream {
this.writable = true;
stream.on('drain', onStreamDrain);
stream.on('aborted', onStreamAbortedResponse);
- const onfinish = this[kFinish].bind(this);
- stream.on('close', onfinish);
- stream.on('finish', onfinish);
+ stream.on('close', this[kFinish].bind(this));
}
// User land modules such as finalhandler just check truthiness of this
@@ -520,7 +514,7 @@ class Http2ServerResponse extends Stream {
const state = this[kState];
if (state.closed)
- throw new errors.Error('ERR_HTTP2_STREAM_CLOSED');
+ throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
if (this[kStream].headersSent)
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
@@ -550,7 +544,7 @@ class Http2ServerResponse extends Stream {
}
if (this[kState].closed) {
- const err = new errors.Error('ERR_HTTP2_STREAM_CLOSED');
+ const err = new errors.Error('ERR_HTTP2_INVALID_STREAM');
if (typeof cb === 'function')
process.nextTick(cb, err);
else
@@ -620,12 +614,15 @@ class Http2ServerResponse extends Stream {
if (typeof callback !== 'function')
throw new errors.TypeError('ERR_INVALID_CALLBACK');
if (this[kState].closed) {
- process.nextTick(callback, new errors.Error('ERR_HTTP2_STREAM_CLOSED'));
+ process.nextTick(callback, new errors.Error('ERR_HTTP2_INVALID_STREAM'));
return;
}
- this[kStream].pushStream(headers, {}, function(stream, headers, options) {
- const response = new Http2ServerResponse(stream);
- callback(null, response);
+ this[kStream].pushStream(headers, {}, (err, stream, headers, options) => {
+ if (err) {
+ callback(err);
+ return;
+ }
+ callback(null, new Http2ServerResponse(stream));
});
}
@@ -649,6 +646,7 @@ class Http2ServerResponse extends Stream {
this[kProxySocket] = null;
stream[kResponse] = undefined;
this.emit('finish');
+ this.emit('close');
}
// TODO doesn't support callbacks
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index 2e0910dbee2963..cbaf908246bd6d 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -32,6 +32,9 @@ const kMaxFrameSize = (2 ** 24) - 1;
const kMaxInt = (2 ** 32) - 1;
const kMaxStreams = (2 ** 31) - 1;
+// eslint-disable-next-line no-control-regex
+const kQuotedString = /^[\x09\x20-\x5b\x5d-\x7e\x80-\xff]*$/;
+
const {
assertIsObject,
assertValidPseudoHeaderResponse,
@@ -65,11 +68,14 @@ const TLSServer = tls.Server;
const kInspect = require('internal/util').customInspectSymbol;
+const kAlpnProtocol = Symbol('alpnProtocol');
const kAuthority = Symbol('authority');
-const kDestroySocket = Symbol('destroy-socket');
+const kEncrypted = Symbol('encrypted');
const kHandle = Symbol('handle');
const kID = Symbol('id');
const kInit = Symbol('init');
+const kInfoHeaders = Symbol('sent-info-headers');
+const kMaybeDestroy = Symbol('maybe-destroy');
const kLocalSettings = Symbol('local-settings');
const kOptions = Symbol('options');
const kOwner = Symbol('owner');
@@ -77,6 +83,8 @@ const kProceed = Symbol('proceed');
const kProtocol = Symbol('protocol');
const kProxySocket = Symbol('proxy-socket');
const kRemoteSettings = Symbol('remote-settings');
+const kSentHeaders = Symbol('sent-headers');
+const kSentTrailers = Symbol('sent-trailers');
const kServer = Symbol('server');
const kSession = Symbol('session');
const kState = Symbol('state');
@@ -84,7 +92,6 @@ const kType = Symbol('type');
const kUpdateTimer = Symbol('update-timer');
const kDefaultSocketTimeout = 2 * 60 * 1000;
-const kRenegTest = /TLS session renegotiation disabled for this socket/;
const {
paddingBuffer,
@@ -95,14 +102,13 @@ const {
const {
NGHTTP2_CANCEL,
+ NGHTTP2_REFUSED_STREAM,
NGHTTP2_DEFAULT_WEIGHT,
NGHTTP2_FLAG_END_STREAM,
NGHTTP2_HCAT_PUSH_RESPONSE,
NGHTTP2_HCAT_RESPONSE,
NGHTTP2_INTERNAL_ERROR,
NGHTTP2_NO_ERROR,
- NGHTTP2_PROTOCOL_ERROR,
- NGHTTP2_REFUSED_STREAM,
NGHTTP2_SESSION_CLIENT,
NGHTTP2_SESSION_SERVER,
NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE,
@@ -139,6 +145,18 @@ const {
STREAM_OPTION_GET_TRAILERS
} = constants;
+const STREAM_FLAGS_PENDING = 0x0;
+const STREAM_FLAGS_READY = 0x1;
+const STREAM_FLAGS_CLOSED = 0x2;
+const STREAM_FLAGS_HEADERS_SENT = 0x4;
+const STREAM_FLAGS_HEAD_REQUEST = 0x8;
+const STREAM_FLAGS_ABORTED = 0x10;
+
+const SESSION_FLAGS_PENDING = 0x0;
+const SESSION_FLAGS_READY = 0x1;
+const SESSION_FLAGS_CLOSED = 0x2;
+const SESSION_FLAGS_DESTROYED = 0x4;
+
// Top level to avoid creating a closure
function emit(self, ...args) {
self.emit(...args);
@@ -150,12 +168,15 @@ function emit(self, ...args) {
// event. If the stream is not new, emit the 'headers' event to pass
// the block of headers on.
function onSessionHeaders(handle, id, cat, flags, headers) {
- const owner = this[kOwner];
- const type = owner[kType];
- owner[kUpdateTimer]();
+ const session = this[kOwner];
+ if (session.destroyed)
+ return;
+
+ const type = session[kType];
+ session[kUpdateTimer]();
debug(`Http2Stream ${id} [Http2Session ` +
`${sessionName(type)}]: headers received`);
- const streams = owner[kState].streams;
+ const streams = session[kState].streams;
const endOfStream = !!(flags & NGHTTP2_FLAG_END_STREAM);
let stream = streams.get(id);
@@ -164,21 +185,28 @@ function onSessionHeaders(handle, id, cat, flags, headers) {
const obj = toHeaderObject(headers);
if (stream === undefined) {
+ if (session.closed) {
+ // we are not accepting any new streams at this point. This callback
+ // should not be invoked at this point in time, but just in case it is,
+ // refuse the stream using an RST_STREAM and destroy the handle.
+ handle.rstStream(NGHTTP2_REFUSED_STREAM);
+ handle.destroy();
+ return;
+ }
const opts = { readable: !endOfStream };
- // owner[kType] can be only one of two possible values
+ // session[kType] can be only one of two possible values
if (type === NGHTTP2_SESSION_SERVER) {
- stream = new ServerHttp2Stream(owner, handle, id, opts, obj);
+ stream = new ServerHttp2Stream(session, handle, id, opts, obj);
if (obj[HTTP2_HEADER_METHOD] === HTTP2_METHOD_HEAD) {
// For head requests, there must not be a body...
// end the writable side immediately.
stream.end();
- stream[kState].headRequest = true;
+ stream[kState].flags |= STREAM_FLAGS_HEAD_REQUEST;
}
} else {
- stream = new ClientHttp2Stream(owner, handle, id, opts);
+ stream = new ClientHttp2Stream(session, handle, id, opts);
}
- streams.set(id, stream);
- process.nextTick(emit, owner, 'stream', stream, obj, flags, headers);
+ process.nextTick(emit, session, 'stream', stream, obj, flags, headers);
} else {
let event;
const status = obj[HTTP2_HEADER_STATUS];
@@ -208,6 +236,12 @@ function onSessionHeaders(handle, id, cat, flags, headers) {
}
}
+function tryClose(fd) {
+ // Try to close the file descriptor. If closing fails, assert because
+ // an error really should not happen at this point.
+ fs.close(fd, (err) => assert.ifError(err));
+}
+
// Called to determine if there are trailers to be sent at the end of a
// Stream. The 'getTrailers' callback is invoked and passed a holder object.
// The trailers to return are set on that object by the handler. Once the
@@ -216,133 +250,192 @@ function onSessionHeaders(handle, id, cat, flags, headers) {
// there are trailing headers to send.
function onStreamTrailers() {
const stream = this[kOwner];
+ if (stream.destroyed)
+ return [];
const trailers = Object.create(null);
stream[kState].getTrailers.call(stream, trailers);
const headersList = mapToHeaders(trailers, assertValidPseudoHeaderTrailer);
if (!Array.isArray(headersList)) {
- process.nextTick(emit, stream, 'error', headersList);
- return;
+ stream.destroy(headersList);
+ return [];
}
+ stream[kSentTrailers] = trailers;
return headersList;
}
-// Called when the stream is closed. The close event is emitted on the
-// Http2Stream instance
+// Submit an RST-STREAM frame to be sent to the remote peer.
+// This will cause the Http2Stream to be closed.
+function submitRstStream(code) {
+ if (this[kHandle] !== undefined) {
+ this[kHandle].rstStream(code);
+ }
+}
+
+// Called when the stream is closed either by sending or receiving an
+// RST_STREAM frame, or through a natural end-of-stream.
+// If the writable and readable sides of the stream are still open at this
+// point, close them. If there is an open fd for file send, close that also.
+// At this point the underlying node::http2:Http2Stream handle is no
+// longer usable so destroy it also.
function onStreamClose(code) {
const stream = this[kOwner];
- stream[kUpdateTimer]();
- abort(stream);
+ if (stream.destroyed)
+ return;
+
const state = stream[kState];
- state.rst = true;
- state.rstCode = code;
- if (state.fd !== undefined)
- fs.close(state.fd, afterFDClose.bind(stream));
- setImmediate(stream.destroy.bind(stream));
-}
+ debug(`Http2Stream ${stream[kID]} [Http2Session ` +
+ `${sessionName(stream[kSession][kType])}]: closed with code ${code}`);
-function afterFDClose(err) {
- if (err)
- process.nextTick(emit, this, 'error', err);
-}
+ if (!stream.closed) {
+ // Unenroll from timeouts
+ unenroll(stream);
+ stream.removeAllListeners('timeout');
-// Called when an error event needs to be triggered
-function onSessionError(error) {
- const owner = this[kOwner];
- owner[kUpdateTimer]();
- process.nextTick(emit, owner, 'error', error);
+ // Set the state flags
+ state.flags |= STREAM_FLAGS_CLOSED;
+ state.rstCode = code;
+
+ // Close the writable side of the stream
+ abort(stream);
+ stream.end();
+ }
+
+ if (state.fd !== undefined)
+ tryClose(state.fd);
+
+ // Defer destroy we actually emit end.
+ if (stream._readableState.endEmitted || code !== NGHTTP2_NO_ERROR) {
+ // If errored or ended, we can destroy immediately.
+ stream[kMaybeDestroy](null, code);
+ } else {
+ // Wait for end to destroy.
+ stream.on('end', stream[kMaybeDestroy]);
+ // Push a null so the stream can end whenever the client consumes
+ // it completely.
+ stream.push(null);
+
+ // Same as net.
+ if (stream._readableState.length === 0) {
+ stream.read(0);
+ }
+ }
}
// Receives a chunk of data for a given stream and forwards it on
// to the Http2Stream Duplex for processing.
-function onStreamRead(nread, buf, handle) {
- const stream = handle[kOwner];
- stream[kUpdateTimer]();
+function onStreamRead(nread, buf) {
+ const stream = this[kOwner];
if (nread >= 0 && !stream.destroyed) {
+ debug(`Http2Stream ${stream[kID]} [Http2Session ` +
+ `${sessionName(stream[kSession][kType])}]: receiving data chunk ` +
+ `of size ${nread}`);
+ stream[kUpdateTimer]();
if (!stream.push(buf)) {
- handle.readStop();
+ if (!stream.destroyed) // we have to check a second time
+ this.readStop();
}
return;
}
+
// Last chunk was received. End the readable side.
- stream.push(null);
+ debug(`Http2Stream ${stream[kID]} [Http2Session ` +
+ `${sessionName(stream[kSession][kType])}]: ending readable.`);
+
+ // defer this until we actually emit end
+ if (stream._readableState.endEmitted) {
+ stream[kMaybeDestroy]();
+ } else {
+ stream.on('end', stream[kMaybeDestroy]);
+ stream.push(null);
+ stream.read(0);
+ }
}
// Called when the remote peer settings have been updated.
// Resets the cached settings.
-function onSettings(ack) {
- const owner = this[kOwner];
- debug(`Http2Session ${sessionName(owner[kType])}: new settings received`);
- owner[kUpdateTimer]();
- let event = 'remoteSettings';
- if (ack) {
- if (owner[kState].pendingAck > 0)
- owner[kState].pendingAck--;
- owner[kLocalSettings] = undefined;
- event = 'localSettings';
- } else {
- owner[kRemoteSettings] = undefined;
- }
- // Only emit the event if there are listeners registered
- if (owner.listenerCount(event) > 0)
- process.nextTick(emit, owner, event, owner[event]);
+function onSettings() {
+ const session = this[kOwner];
+ if (session.destroyed)
+ return;
+ session[kUpdateTimer]();
+ debug(`Http2Session ${sessionName(session[kType])}: new settings received`);
+ session[kRemoteSettings] = undefined;
+ session.emit('remoteSettings', session.remoteSettings);
}
// If the stream exists, an attempt will be made to emit an event
// on the stream object itself. Otherwise, forward it on to the
// session (which may, in turn, forward it on to the server)
function onPriority(id, parent, weight, exclusive) {
- const owner = this[kOwner];
+ const session = this[kOwner];
+ if (session.destroyed)
+ return;
debug(`Http2Stream ${id} [Http2Session ` +
- `${sessionName(owner[kType])}]: priority [parent: ${parent}, ` +
+ `${sessionName(session[kType])}]: priority [parent: ${parent}, ` +
`weight: ${weight}, exclusive: ${exclusive}]`);
- owner[kUpdateTimer]();
- const streams = owner[kState].streams;
- const stream = streams.get(id);
- const emitter = stream === undefined ? owner : stream;
- process.nextTick(emit, emitter, 'priority', id, parent, weight, exclusive);
-}
-
-function emitFrameError(self, id, type, code) {
- if (!self.emit('frameError', type, code, id)) {
- const err = new errors.Error('ERR_HTTP2_FRAME_ERROR', type, code, id);
- err.errno = code;
- self.emit('error', err);
+ const emitter = session[kState].streams.get(id) || session;
+ if (!emitter.destroyed) {
+ emitter[kUpdateTimer]();
+ emitter.emit('priority', id, parent, weight, exclusive);
}
}
// Called by the native layer when an error has occurred sending a
// frame. This should be exceedingly rare.
function onFrameError(id, type, code) {
- const owner = this[kOwner];
- debug(`Http2Session ${sessionName(owner[kType])}: error sending frame type ` +
- `${type} on stream ${id}, code: ${code}`);
- owner[kUpdateTimer]();
- const streams = owner[kState].streams;
- const stream = streams.get(id);
- const emitter = stream !== undefined ? stream : owner;
- process.nextTick(emitFrameError, emitter, id, type, code);
+ const session = this[kOwner];
+ if (session.destroyed)
+ return;
+ debug(`Http2Session ${sessionName(session[kType])}: error sending frame ` +
+ `type ${type} on stream ${id}, code: ${code}`);
+ const emitter = session[kState].streams.get(id) || session;
+ emitter[kUpdateTimer]();
+ emitter.emit('frameError', type, code, id);
}
-function emitGoaway(self, code, lastStreamID, buf) {
- self.emit('goaway', code, lastStreamID, buf);
- // Tear down the session or destroy
- const state = self[kState];
- if (state.destroying || state.destroyed)
+function onAltSvc(stream, origin, alt) {
+ const session = this[kOwner];
+ if (session.destroyed)
return;
- if (!state.shuttingDown && !state.shutdown) {
- self.shutdown({}, self.destroy.bind(self));
+ debug(`Http2Session ${sessionName(session[kType])}: altsvc received: ` +
+ `stream: ${stream}, origin: ${origin}, alt: ${alt}`);
+ session[kUpdateTimer]();
+ session.emit('altsvc', alt, origin, stream);
+}
+
+// Receiving a GOAWAY frame from the connected peer is a signal that no
+// new streams should be created. If the code === NGHTTP2_NO_ERROR, we
+// are going to send our our close, but allow existing frames to close
+// normally. If code !== NGHTTP2_NO_ERROR, we are going to send our own
+// close using the same code then destroy the session with an error.
+// The goaway event will be emitted on next tick.
+function onGoawayData(code, lastStreamID, buf) {
+ const session = this[kOwner];
+ if (session.destroyed)
return;
+ debug(`Http2Session ${sessionName(session[kType])}: goaway ${code} ` +
+ `received [last stream id: ${lastStreamID}]`);
+
+ const state = session[kState];
+ state.goawayCode = code;
+ state.goawayLastStreamID = lastStreamID;
+
+ session.emit('goaway', code, lastStreamID, buf);
+ if (code === NGHTTP2_NO_ERROR) {
+ // If this is a no error goaway, begin shutting down.
+ // No new streams permitted, but existing streams may
+ // close naturally on their own.
+ session.close();
+ } else {
+ // However, if the code is not NGHTTP_NO_ERROR, destroy the
+ // session immediately. We destroy with an error but send a
+ // goaway using NGHTTP2_NO_ERROR because there was no error
+ // condition on this side of the session that caused the
+ // shutdown.
+ session.destroy(new errors.Error('ERR_HTTP2_SESSION_ERROR', code),
+ { errorCode: NGHTTP2_NO_ERROR });
}
- self.destroy();
-}
-
-// Called by the native layer when a goaway frame has been received
-function onGoawayData(code, lastStreamID, buf) {
- const owner = this[kOwner];
- debug(`Http2Session ${sessionName(owner[kType])}: goaway ${code} received ` +
- `[last stream id: ${lastStreamID}]`);
- process.nextTick(emitGoaway, owner, code, lastStreamID, buf);
}
// Returns the padding to use per frame. The selectPadding callback is set
@@ -353,11 +446,7 @@ function onSelectPadding(fn) {
return function getPadding() {
const frameLen = paddingBuffer[PADDING_BUF_FRAME_LENGTH];
const maxFramePayloadLen = paddingBuffer[PADDING_BUF_MAX_PAYLOAD_LENGTH];
- paddingBuffer[PADDING_BUF_RETURN_VALUE] =
- Math.min(maxFramePayloadLen,
- Math.max(frameLen,
- fn(frameLen,
- maxFramePayloadLen) | 0));
+ paddingBuffer[PADDING_BUF_RETURN_VALUE] = fn(frameLen, maxFramePayloadLen);
};
}
@@ -366,18 +455,23 @@ function onSelectPadding(fn) {
// will be deferred until the socket is ready to go.
function requestOnConnect(headers, options) {
const session = this[kSession];
- debug(`Http2Session ${sessionName(session[kType])}: connected, ` +
- 'initializing request');
- const streams = session[kState].streams;
- validatePriorityOptions(options);
+ // At this point, the stream should have already been destroyed during
+ // the session.destroy() method. Do nothing else.
+ if (session.destroyed)
+ return;
- const headersList = mapToHeaders(headers);
- if (!Array.isArray(headersList)) {
- process.nextTick(emit, this, 'error', headersList);
+ // If the session was closed while waiting for for the connect, destroy
+ // the stream and do not continue with the request.
+ if (session.closed) {
+ const err = new errors.Error('ERR_HTTP2_GOAWAY_SESSION');
+ this.destroy(err);
return;
}
+ debug(`Http2Session ${sessionName(session[kType])}: connected, ` +
+ 'initializing request');
+
let streamOptions = 0;
if (options.endStream)
streamOptions |= STREAM_OPTION_EMPTY_PAYLOAD;
@@ -389,7 +483,7 @@ function requestOnConnect(headers, options) {
// ret will be either the reserved stream ID (if positive)
// or an error code (if negative)
- const ret = session[kHandle].request(headersList,
+ const ret = session[kHandle].request(headers,
streamOptions,
options.parent | 0,
options.weight | 0,
@@ -406,127 +500,111 @@ function requestOnConnect(headers, options) {
// session if not handled.
if (typeof ret === 'number') {
let err;
- let target = session;
switch (ret) {
case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE:
err = new errors.Error('ERR_HTTP2_OUT_OF_STREAMS');
- target = this;
+ this.destroy(err);
break;
case NGHTTP2_ERR_INVALID_ARGUMENT:
err = new errors.Error('ERR_HTTP2_STREAM_SELF_DEPENDENCY');
- target = this;
+ this.destroy(err);
break;
default:
- err = new NghttpError(ret);
+ session.destroy(new NghttpError(ret));
}
- process.nextTick(emit, target, 'error', err);
return;
}
- const id = ret.id();
- streams.set(id, this);
- this[kInit](id, ret);
+ this[kInit](ret.id(), ret);
}
+// Validates that priority options are correct, specifically:
+// 1. options.weight must be a number
+// 2. options.parent must be a positive number
+// 3. options.exclusive must be a boolean
+// 4. if specified, options.silent must be a boolean
+//
+// Also sets the default priority options if they are not set.
function validatePriorityOptions(options) {
+ let err;
if (options.weight === undefined) {
options.weight = NGHTTP2_DEFAULT_WEIGHT;
} else if (typeof options.weight !== 'number') {
- const err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'weight',
- options.weight);
- Error.captureStackTrace(err, validatePriorityOptions);
- throw err;
+ err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'weight',
+ options.weight);
}
if (options.parent === undefined) {
options.parent = 0;
} else if (typeof options.parent !== 'number' || options.parent < 0) {
- const err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'parent',
- options.parent);
- Error.captureStackTrace(err, validatePriorityOptions);
- throw err;
+ err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'parent',
+ options.parent);
}
if (options.exclusive === undefined) {
options.exclusive = false;
} else if (typeof options.exclusive !== 'boolean') {
- const err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'exclusive',
- options.exclusive);
- Error.captureStackTrace(err, validatePriorityOptions);
- throw err;
+ err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'exclusive',
+ options.exclusive);
}
if (options.silent === undefined) {
options.silent = false;
} else if (typeof options.silent !== 'boolean') {
- const err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'silent',
- options.silent);
+ err = new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'silent',
+ options.silent);
+ }
+
+ if (err) {
Error.captureStackTrace(err, validatePriorityOptions);
throw err;
}
}
+// When an error occurs internally at the binding level, immediately
+// destroy the session.
function onSessionInternalError(code) {
- const owner = this[kOwner];
- const err = new NghttpError(code);
- process.nextTick(emit, owner, 'error', err);
+ if (this[kOwner] !== undefined)
+ this[kOwner].destroy(new NghttpError(code));
}
-// Creates the internal binding.Http2Session handle for an Http2Session
-// instance. This occurs only after the socket connection has been
-// established. Note: the binding.Http2Session will take over ownership
-// of the socket. No other code should read from or write to the socket.
-function setupHandle(session, socket, type, options) {
- return function() {
- debug(`Http2Session ${sessionName(type)}: setting up session handle`);
- session[kState].connecting = false;
-
- updateOptionsBuffer(options);
- const handle = new binding.Http2Session(type);
- handle[kOwner] = session;
- handle.error = onSessionInternalError;
- handle.onpriority = onPriority;
- handle.onsettings = onSettings;
- handle.onheaders = onSessionHeaders;
- handle.onerror = onSessionError;
- handle.onframeerror = onFrameError;
- handle.ongoawaydata = onGoawayData;
-
- if (typeof options.selectPadding === 'function')
- handle.ongetpadding = onSelectPadding(options.selectPadding);
-
- assert(socket._handle !== undefined,
- 'Internal HTTP/2 Failure. The socket is not connected. Please ' +
- 'report this as a bug in Node.js');
- handle.consume(socket._handle._externalStream);
-
- session[kHandle] = handle;
-
- const settings = typeof options.settings === 'object' ?
- options.settings : Object.create(null);
-
- session.settings(settings);
- process.nextTick(emit, session, 'connect', session, socket);
- };
+function settingsCallback(cb, ack, duration) {
+ this[kState].pendingAck--;
+ this[kLocalSettings] = undefined;
+ if (ack) {
+ debug(`Http2Session ${sessionName(this[kType])}: settings received`);
+ const settings = this.localSettings;
+ if (typeof cb === 'function')
+ cb(null, settings, duration);
+ this.emit('localSettings', settings);
+ } else {
+ debug(`Http2Session ${sessionName(this[kType])}: settings canceled`);
+ if (typeof cb === 'function')
+ cb(new errors.Error('ERR_HTTP2_SETTINGS_CANCEL'));
+ }
}
// Submits a SETTINGS frame to be sent to the remote peer.
-function submitSettings(settings) {
- const type = this[kType];
- debug(`Http2Session ${sessionName(type)}: submitting settings`);
+function submitSettings(settings, callback) {
+ if (this.destroyed)
+ return;
+ debug(`Http2Session ${sessionName(this[kType])}: submitting settings`);
this[kUpdateTimer]();
- this[kLocalSettings] = undefined;
updateSettingsBuffer(settings);
- this[kHandle].settings();
+ if (!this[kHandle].settings(settingsCallback.bind(this, callback))) {
+ this.destroy(new errors.Error('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK'));
+ }
}
// Submits a PRIORITY frame to be sent to the remote peer
// Note: If the silent option is true, the change will be made
// locally with no PRIORITY frame sent.
function submitPriority(options) {
+ if (this.destroyed)
+ return;
this[kUpdateTimer]();
// If the parent is the id, do nothing because a
@@ -540,74 +618,16 @@ function submitPriority(options) {
!!options.silent);
}
-// Submit an RST-STREAM frame to be sent to the remote peer.
-// This will cause the Http2Stream to be closed.
-function submitRstStream(code) {
- this[kUpdateTimer]();
-
- const state = this[kState];
- if (state.rst) return;
- state.rst = true;
- state.rstCode = code;
-
- const ret = this[kHandle].rstStream(code);
- if (ret < 0) {
- const err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- return;
- }
- this.destroy();
-}
-
-function doShutdown(options) {
- const handle = this[kHandle];
- const state = this[kState];
- if (handle === undefined || state.shutdown)
- return; // Nothing to do, possibly because the session shutdown already.
- const ret = handle.goaway(options.errorCode | 0,
- options.lastStreamID | 0,
- options.opaqueData);
- state.shuttingDown = false;
- state.shutdown = true;
- if (ret < 0) {
- debug(`Http2Session ${sessionName(this[kType])}: shutdown failed`);
- const err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- return;
- }
- process.nextTick(emit, this, 'shutdown', options);
-}
-
-// Submit a graceful or immediate shutdown request for the Http2Session.
-function submitShutdown(options) {
- const type = this[kType];
- debug(`Http2Session ${sessionName(type)}: submitting shutdown request`);
- const shutdownFn = doShutdown.bind(this, options);
- if (type === NGHTTP2_SESSION_SERVER && options.graceful === true) {
- // first send a shutdown notice
- this[kHandle].shutdownNotice();
- // then, on flip of the event loop, do the actual shutdown
- setImmediate(shutdownFn);
+// Submit a GOAWAY frame to be sent to the remote peer.
+// If the lastStreamID is set to <= 0, then the lastProcStreamID will
+// be used. The opaqueData must either be a typed array or undefined
+// (which will be checked elsewhere).
+function submitGoaway(code, lastStreamID, opaqueData) {
+ if (this.destroyed)
return;
- }
- shutdownFn();
-}
-
-function finishSessionDestroy(socket) {
- if (!socket.destroyed)
- socket.destroy();
-
- const state = this[kState];
- state.destroying = false;
- state.destroyed = true;
-
- // Destroy the handle
- if (this[kHandle] !== undefined) {
- this[kHandle].destroy(state.skipUnconsume);
- this[kHandle] = undefined;
- }
-
- process.nextTick(emit, this, 'close');
+ debug(`Http2Session ${sessionName(this[kType])}: submitting goaway`);
+ this[kUpdateTimer]();
+ this[kHandle].goaway(code, lastStreamID, opaqueData);
}
const proxySocketHandler = {
@@ -652,13 +672,27 @@ const proxySocketHandler = {
}
};
+// pingCallback() returns a function that is invoked when an HTTP2 PING
+// frame acknowledgement is received. The ack is either true or false to
+// indicate if the ping was successful or not. The duration indicates the
+// number of milliseconds elapsed since the ping was sent and the ack
+// received. The payload is a Buffer containing the 8 bytes of payload
+// data received on the PING acknowlegement.
function pingCallback(cb) {
- return function(ack, duration, payload) {
+ return function pingCallback(ack, duration, payload) {
const err = ack ? null : new errors.Error('ERR_HTTP2_PING_CANCEL');
cb(err, duration, payload);
};
}
+// Validates the values in a settings object. Specifically:
+// 1. headerTableSize must be a number in the range 0 <= n <= kMaxInt
+// 2. initialWindowSize must be a number in the range 0 <= n <= kMaxInt
+// 3. maxFrameSize must be a number in the range 16384 <= n <= kMaxFrameSize
+// 4. maxConcurrentStreams must be a number in the range 0 <= n <= kMaxStreams
+// 5. maxHeaderListSize must be a number in the range 0 <= n <= kMaxInt
+// 6. enablePush must be a boolean
+// All settings are optional and may be left undefined
function validateSettings(settings) {
settings = Object.assign({}, settings);
assertWithinRange('headerTableSize',
@@ -681,13 +715,110 @@ function validateSettings(settings) {
const err = new errors.TypeError('ERR_HTTP2_INVALID_SETTING_VALUE',
'enablePush', settings.enablePush);
err.actual = settings.enablePush;
+ Error.captureStackTrace(err, 'validateSettings');
throw err;
}
return settings;
}
+// Creates the internal binding.Http2Session handle for an Http2Session
+// instance. This occurs only after the socket connection has been
+// established. Note: the binding.Http2Session will take over ownership
+// of the socket. No other code should read from or write to the socket.
+function setupHandle(socket, type, options) {
+ // If the session has been destroyed, go ahead and emit 'connect',
+ // but do nothing else. The various on('connect') handlers set by
+ // core will check for session.destroyed before progressing, this
+ // ensures that those at l`east get cleared out.
+ if (this.destroyed) {
+ process.nextTick(emit, this, 'connect', this, socket);
+ return;
+ }
+ debug(`Http2Session ${sessionName(type)}: setting up session handle`);
+ this[kState].flags |= SESSION_FLAGS_READY;
+
+ updateOptionsBuffer(options);
+ const handle = new binding.Http2Session(type);
+ handle[kOwner] = this;
+ handle.error = onSessionInternalError;
+ handle.onpriority = onPriority;
+ handle.onsettings = onSettings;
+ handle.onheaders = onSessionHeaders;
+ handle.onframeerror = onFrameError;
+ handle.ongoawaydata = onGoawayData;
+ handle.onaltsvc = onAltSvc;
+
+ if (typeof options.selectPadding === 'function')
+ handle.ongetpadding = onSelectPadding(options.selectPadding);
+
+ assert(socket._handle !== undefined,
+ 'Internal HTTP/2 Failure. The socket is not connected. Please ' +
+ 'report this as a bug in Node.js');
+ handle.consume(socket._handle._externalStream);
+
+ this[kHandle] = handle;
+
+ if (socket.encrypted) {
+ this[kAlpnProtocol] = socket.alpnProtocol;
+ this[kEncrypted] = true;
+ } else {
+ // 'h2c' is the protocol identifier for HTTP/2 over plain-text. We use
+ // it here to identify any session that is not explicitly using an
+ // encrypted socket.
+ this[kAlpnProtocol] = 'h2c';
+ this[kEncrypted] = false;
+ }
+
+ const settings = typeof options.settings === 'object' ?
+ options.settings : {};
+
+ this.settings(settings);
+ process.nextTick(emit, this, 'connect', this, socket);
+}
+
+// Emits a close event followed by an error event if err is truthy. Used
+// by Http2Session.prototype.destroy()
+function emitClose(self, error) {
+ if (error)
+ self.emit('error', error);
+ self.emit('close');
+}
+
// Upon creation, the Http2Session takes ownership of the socket. The session
// may not be ready to use immediately if the socket is not yet fully connected.
+// In that case, the Http2Session will wait for the socket to connect. Once
+// the Http2Session is ready, it will emit its own 'connect' event.
+//
+// The Http2Session.goaway() method will send a GOAWAY frame, signalling
+// to the connected peer that a shutdown is in progress. Sending a goaway
+// frame has no other effect, however.
+//
+// Receiving a GOAWAY frame will cause the Http2Session to first emit a 'goaway'
+// event notifying the user that a shutdown is in progress. If the goaway
+// error code equals 0 (NGHTTP2_NO_ERROR), session.close() will be called,
+// causing the Http2Session to send its own GOAWAY frame and switch itself
+// into a graceful closing state. In this state, new inbound or outbound
+// Http2Streams will be rejected. Existing *pending* streams (those created
+// but without an assigned stream ID or handle) will be destroyed with a
+// cancel error. Existing open streams will be permitted to complete on their
+// own. Once all existing streams close, session.destroy() will be called
+// automatically.
+//
+// Calling session.destroy() will tear down the Http2Session immediately,
+// making it no longer usable. Pending and existing streams will be destroyed.
+// The bound socket will be destroyed. Once all resources have been freed up,
+// the 'close' event will be emitted. Note that pending streams will be
+// destroyed using a specific "ERR_HTTP2_STREAM_CANCEL" error. Existing open
+// streams will be destroyed using the same error passed to session.destroy()
+//
+// If destroy is called with an error, an 'error' event will be emitted
+// immediately following the 'close' event.
+//
+// The socket and Http2Session lifecycles are tightly bound. Once one is
+// destroyed, the other should also be destroyed. When the socket is destroyed
+// with an error, session.destroy() will be called with that same error.
+// Likewise, when session.destroy() is called with an error, the same error
+// will be sent to the socket.
class Http2Session extends EventEmitter {
constructor(type, options, socket) {
super();
@@ -708,15 +839,16 @@ class Http2Session extends EventEmitter {
socket[kSession] = this;
this[kState] = {
+ flags: SESSION_FLAGS_PENDING,
streams: new Map(),
- destroyed: false,
- shutdown: false,
- shuttingDown: false,
+ pendingStreams: new Set(),
pendingAck: 0,
- maxPendingAck: Math.max(1, (options.maxPendingAck | 0) || 10),
- writeQueueSize: 0
+ writeQueueSize: 0,
+ originSet: undefined
};
+ this[kEncrypted] = undefined;
+ this[kAlpnProtocol] = undefined;
this[kType] = type;
this[kProxySocket] = null;
this[kSocket] = socket;
@@ -729,12 +861,8 @@ class Http2Session extends EventEmitter {
if (typeof socket.disableRenegotiation === 'function')
socket.disableRenegotiation();
- socket[kDestroySocket] = socket.destroy;
- socket.destroy = socketDestroy;
-
- const setupFn = setupHandle(this, socket, type, options);
+ const setupFn = setupHandle.bind(this, socket, type, options);
if (socket.connecting) {
- this[kState].connecting = true;
const connectEvent =
socket instanceof tls.TLSSocket ? 'secureConnect' : 'connect';
socket.once(connectEvent, setupFn);
@@ -742,22 +870,78 @@ class Http2Session extends EventEmitter {
setupFn();
}
- // Any individual session can have any number of active open
- // streams, these may all need to be made aware of changes
- // in state that occur -- such as when the associated socket
- // is closed. To do so, we need to set the max listener count
- // to something more reasonable because we may have any number
- // of concurrent streams (2^31-1 is the upper limit on the number
- // of streams)
- this.setMaxListeners(kMaxStreams);
debug(`Http2Session ${sessionName(type)}: created`);
}
+ // Returns undefined if the socket is not yet connected, true if the
+ // socket is a TLSSocket, and false if it is not.
+ get encrypted() {
+ return this[kEncrypted];
+ }
+
+ // Returns undefined if the socket is not yet connected, `h2` if the
+ // socket is a TLSSocket and the alpnProtocol is `h2`, or `h2c` if the
+ // socket is not a TLSSocket.
+ get alpnProtocol() {
+ return this[kAlpnProtocol];
+ }
+
+ // TODO(jasnell): originSet is being added in preparation for ORIGIN frame
+ // support. At the current time, the ORIGIN frame specification is awaiting
+ // publication as an RFC and is awaiting implementation in nghttp2. Once
+ // added, an ORIGIN frame will add to the origins included in the origin
+ // set. 421 responses will remove origins from the set.
+ get originSet() {
+ if (!this.encrypted || this.destroyed)
+ return undefined;
+
+ let originSet = this[kState].originSet;
+ if (originSet === undefined) {
+ const socket = this[kSocket];
+ this[kState].originSet = originSet = new Set();
+ if (socket.servername != null) {
+ let originString = `https://${socket.servername}`;
+ if (socket.remotePort != null)
+ originString += `:${socket.remotePort}`;
+ // We have to ensure that it is a properly serialized
+ // ASCII origin string. The socket.servername might not
+ // be properly ASCII encoded.
+ originSet.add((new URL(originString)).origin);
+ }
+ }
+
+ return Array.from(originSet);
+ }
+
+ // True if the Http2Session is still waiting for the socket to connect
+ get connecting() {
+ return (this[kState].flags & SESSION_FLAGS_READY) === 0;
+ }
+
+ // True if Http2Session.prototype.close() has been called
+ get closed() {
+ return !!(this[kState].flags & SESSION_FLAGS_CLOSED);
+ }
+
+ // True if Http2Session.prototype.destroy() has been called
+ get destroyed() {
+ return !!(this[kState].flags & SESSION_FLAGS_DESTROYED);
+ }
+
+ // Resets the timeout counter
[kUpdateTimer]() {
+ if (this.destroyed)
+ return;
_unrefActive(this);
}
+ // Sets the id of the next stream to be created by this Http2Session.
+ // The value must be a number in the range 0 <= n <= kMaxStreams. The
+ // value also needs to be larger than the current next stream ID.
setNextStreamID(id) {
+ if (this.destroyed)
+ throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
+
if (typeof id !== 'number')
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'id', 'number');
if (id <= 0 || id > kMaxStreams)
@@ -765,10 +949,13 @@ class Http2Session extends EventEmitter {
this[kHandle].setNextStreamID(id);
}
+ // If ping is called while we are still connecting, or after close() has
+ // been called, the ping callback will be invoked immediately will a ping
+ // cancelled error and a duration of 0.0.
ping(payload, callback) {
- const state = this[kState];
- if (state.destroyed || state.destroying)
+ if (this.destroyed)
throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
+
if (typeof payload === 'function') {
callback = payload;
payload = undefined;
@@ -783,17 +970,21 @@ class Http2Session extends EventEmitter {
}
if (typeof callback !== 'function')
throw new errors.TypeError('ERR_INVALID_CALLBACK');
- return this[kHandle].ping(payload, pingCallback(callback));
+
+ const cb = pingCallback(callback);
+ if (this.connecting || this.closed) {
+ process.nextTick(cb, false, 0.0, payload);
+ return;
+ }
+
+ return this[kHandle].ping(payload, cb);
}
[kInspect](depth, opts) {
- const state = this[kState];
const obj = {
type: this[kType],
- destroyed: state.destroyed,
- destroying: state.destroying,
- shutdown: state.shutdown,
- shuttingDown: state.shuttingDown,
+ closed: this.closed,
+ destroyed: this.destroyed,
state: this.state,
localSettings: this.localSettings,
remoteSettings: this.remoteSettings
@@ -814,171 +1005,209 @@ class Http2Session extends EventEmitter {
return this[kType];
}
+ // If a GOAWAY frame has been received, gives the error code specified
+ get goawayCode() {
+ return this[kState].goawayCode || NGHTTP2_NO_ERROR;
+ }
+
+ // If a GOAWAY frame has been received, gives the last stream ID reported
+ get goawayLastStreamID() {
+ return this[kState].goawayLastStreamID || 0;
+ }
+
// true if the Http2Session is waiting for a settings acknowledgement
get pendingSettingsAck() {
return this[kState].pendingAck > 0;
}
- // true if the Http2Session has been destroyed
- get destroyed() {
- return this[kState].destroyed;
- }
-
// Retrieves state information for the Http2Session
get state() {
- const handle = this[kHandle];
- return handle === undefined ? {} : getSessionState(handle);
+ return this.connecting || this.destroyed ?
+ {} : getSessionState(this[kHandle]);
}
// The settings currently in effect for the local peer. These will
// be updated only when a settings acknowledgement has been received.
get localSettings() {
- let settings = this[kLocalSettings];
+ const settings = this[kLocalSettings];
if (settings !== undefined)
return settings;
- const handle = this[kHandle];
- if (handle === undefined)
+ if (this.destroyed || this.connecting)
return {};
- settings = getSettings(handle, false); // Local
- this[kLocalSettings] = settings;
- return settings;
+ return this[kLocalSettings] = getSettings(this[kHandle], false); // Local
}
// The settings currently in effect for the remote peer.
get remoteSettings() {
- let settings = this[kRemoteSettings];
+ const settings = this[kRemoteSettings];
if (settings !== undefined)
return settings;
- const handle = this[kHandle];
- if (handle === undefined)
+ if (this.destroyed || this.connecting)
return {};
- settings = getSettings(handle, true); // Remote
- this[kRemoteSettings] = settings;
- return settings;
+ return this[kRemoteSettings] = getSettings(this[kHandle], true); // Remote
}
// Submits a SETTINGS frame to be sent to the remote peer.
- settings(settings) {
- const state = this[kState];
- if (state.destroyed || state.destroying)
+ settings(settings, callback) {
+ if (this.destroyed)
throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
-
- // Validate the input first
assertIsObject(settings, 'settings');
settings = validateSettings(settings);
- if (state.pendingAck === state.maxPendingAck) {
- throw new errors.Error('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK',
- this[kState].pendingAck);
- }
+
+ if (callback && typeof callback !== 'function')
+ throw new errors.TypeError('ERR_INVALID_CALLBACK');
debug(`Http2Session ${sessionName(this[kType])}: sending settings`);
- state.pendingAck++;
- const settingsFn = submitSettings.bind(this, settings);
- if (state.connecting) {
+ this[kState].pendingAck++;
+
+ const settingsFn = submitSettings.bind(this, settings, callback);
+ if (this.connecting) {
this.once('connect', settingsFn);
return;
}
settingsFn();
}
- // Destroy the Http2Session
- destroy() {
- const state = this[kState];
- if (state.destroyed || state.destroying)
- return;
- debug(`Http2Session ${sessionName(this[kType])}: destroying`);
- state.destroying = true;
- state.destroyed = false;
-
- // Unenroll the timer
- this.setTimeout(0, sessionOnTimeout);
-
- // Shut down any still open streams
- const streams = state.streams;
- streams.forEach((stream) => stream.destroy());
-
- // Disassociate from the socket and server
- const socket = this[kSocket];
- // socket.pause();
- delete this[kProxySocket];
- delete this[kSocket];
- delete this[kServer];
+ // Sumits a GOAWAY frame to be sent to the remote peer. Note that this
+ // is only a notification, and does not affect the usable state of the
+ // session with the notable exception that new incoming streams will
+ // be rejected automatically.
+ goaway(code = NGHTTP2_NO_ERROR, lastStreamID = 0, opaqueData) {
+ if (this.destroyed)
+ throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
- if (this[kHandle] !== undefined)
- this[kHandle].destroying();
+ if (opaqueData !== undefined && !isArrayBufferView(opaqueData)) {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
+ 'opaqueData',
+ ['Buffer', 'TypedArray', 'DataView']);
+ }
+ if (typeof code !== 'number') {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'code', 'number');
+ }
+ if (typeof lastStreamID !== 'number') {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
+ 'lastStreamID', 'number');
+ }
- setImmediate(finishSessionDestroy.bind(this), socket);
+ const goawayFn = submitGoaway.bind(this, code, lastStreamID, opaqueData);
+ if (this.connecting) {
+ this.once('connect', goawayFn);
+ return;
+ }
+ goawayFn();
}
- // Graceful or immediate shutdown of the Http2Session. Graceful shutdown
- // is only supported on the server-side
- shutdown(options, callback) {
- const state = this[kState];
- if (state.destroyed || state.destroying)
- throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
-
- if (state.shutdown || state.shuttingDown)
+ // Destroy the Http2Session, making it no longer usable and cancelling
+ // any pending activity.
+ destroy(error = NGHTTP2_NO_ERROR, code) {
+ if (this.destroyed)
return;
+ debug(`Http2Session ${sessionName(this[kType])}: destroying`);
- const type = this[kType];
-
- if (typeof options === 'function') {
- callback = options;
- options = undefined;
+ if (typeof error === 'number') {
+ code = error;
+ error =
+ code !== NGHTTP2_NO_ERROR ?
+ new errors.Error('ERR_HTTP2_SESSION_ERROR', code) : undefined;
}
+ if (code === undefined && error != null)
+ code = NGHTTP2_INTERNAL_ERROR;
- assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
-
- if (options.opaqueData !== undefined &&
- !isArrayBufferView(options.opaqueData)) {
- throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'opaqueData',
- options.opaqueData);
- }
- if (type === NGHTTP2_SESSION_SERVER &&
- options.graceful !== undefined &&
- typeof options.graceful !== 'boolean') {
- throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'graceful',
- options.graceful);
- }
- if (options.errorCode !== undefined &&
- typeof options.errorCode !== 'number') {
- throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'errorCode',
- options.errorCode);
- }
- if (options.lastStreamID !== undefined &&
- (typeof options.lastStreamID !== 'number' ||
- options.lastStreamID < 0)) {
- throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
- 'lastStreamID',
- options.lastStreamID);
+ const state = this[kState];
+ state.flags |= SESSION_FLAGS_DESTROYED;
+
+ // Clear timeout and remove timeout listeners
+ unenroll(this);
+ this.removeAllListeners('timeout');
+
+ // Destroy any pending and open streams
+ const cancel = new errors.Error('ERR_HTTP2_STREAM_CANCEL');
+ if (error) {
+ cancel.cause = error;
+ if (typeof error.message === 'string')
+ cancel.message += ` (caused by: ${error.message})`;
}
+ state.pendingStreams.forEach((stream) => stream.destroy(cancel));
+ state.streams.forEach((stream) => stream.destroy(error));
+
+ // Disassociate from the socket and server
+ const socket = this[kSocket];
+ const handle = this[kHandle];
- debug(`Http2Session ${sessionName(type)}: initiating shutdown`);
- state.shuttingDown = true;
+ // Destroy the handle if it exists at this point
+ if (handle !== undefined)
+ handle.destroy(code, socket.destroyed);
- if (callback) {
- this.on('shutdown', callback);
+ // If there is no error, use setImmediate to destroy the socket on the
+ // next iteration of the event loop in order to give data time to transmit.
+ // Otherwise, destroy immediately.
+ if (!socket.destroyed) {
+ if (!error) {
+ setImmediate(socket.end.bind(socket));
+ } else {
+ socket.destroy(error);
+ }
}
- const shutdownFn = submitShutdown.bind(this, options);
- if (state.connecting) {
- this.once('connect', shutdownFn);
+ this[kProxySocket] = undefined;
+ this[kSocket] = undefined;
+ this[kHandle] = undefined;
+ socket[kSession] = undefined;
+ socket[kServer] = undefined;
+
+ // Finally, emit the close and error events (if necessary) on next tick.
+ process.nextTick(emitClose, this, error);
+ }
+
+ // Closing the session will:
+ // 1. Send a goaway frame
+ // 2. Mark the session as closed
+ // 3. Prevent new inbound or outbound streams from being opened
+ // 4. Optionally register a 'close' event handler
+ // 5. Will cause the session to automatically destroy after the
+ // last currently open Http2Stream closes.
+ //
+ // Close always assumes a good, non-error shutdown (NGHTTP_NO_ERROR)
+ //
+ // If the session has not connected yet, the closed flag will still be
+ // set but the goaway will not be sent until after the connect event
+ // is emitted.
+ close(callback) {
+ if (this.closed || this.destroyed)
return;
+ debug(`Http2Session ${sessionName(this[kType])}: marking session closed`);
+ this[kState].flags |= SESSION_FLAGS_CLOSED;
+ if (typeof callback === 'function')
+ this.once('close', callback);
+ this.goaway();
+ this[kMaybeDestroy]();
+ }
+
+ // Destroy the session if:
+ // * error is not undefined/null
+ // * session is closed and there are no more pending or open streams
+ [kMaybeDestroy](error) {
+ if (error == null) {
+ const state = this[kState];
+ // Do not destroy if we're not closed and there are pending/open streams
+ if (!this.closed ||
+ state.streams.size > 0 ||
+ state.pendingStreams.size > 0) {
+ return;
+ }
}
-
- debug(`Http2Session ${sessionName(type)}: sending shutdown`);
- shutdownFn();
+ this.destroy(error);
}
_onTimeout() {
+ // If the session is destroyed, this should never actually be invoked,
+ // but just in case...
+ if (this.destroyed)
+ return;
// This checks whether a write is currently in progress and also whether
// that write is actually sending data across the write. The kHandle
// stored `chunksSentSinceLastWrite` is only updated when a timeout event
@@ -995,10 +1224,25 @@ class Http2Session extends EventEmitter {
}
}
- process.nextTick(emit, this, 'timeout');
+ this.emit('timeout');
+ }
+
+ ref() {
+ if (this[kSocket]) {
+ this[kSocket].ref();
+ }
+ }
+
+ unref() {
+ if (this[kSocket]) {
+ this[kSocket].unref();
+ }
}
}
+// ServerHttp2Session instances should never have to wait for the socket
+// to connect as they are always created after the socket has already been
+// established.
class ServerHttp2Session extends Http2Session {
constructor(options, socket, server) {
super(NGHTTP2_SESSION_SERVER, options, socket);
@@ -1008,8 +1252,60 @@ class ServerHttp2Session extends Http2Session {
get server() {
return this[kServer];
}
+
+ // Submits an altsvc frame to be sent to the client. `stream` is a
+ // numeric Stream ID. origin is a URL string that will be used to get
+ // the origin. alt is a string containing the altsvc details. No fancy
+ // API is provided for that.
+ altsvc(alt, originOrStream) {
+ if (this.destroyed)
+ throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
+
+ let stream = 0;
+ let origin;
+
+ if (typeof originOrStream === 'string') {
+ origin = (new URL(originOrStream)).origin;
+ if (origin === 'null')
+ throw new errors.TypeError('ERR_HTTP2_ALTSVC_INVALID_ORIGIN');
+ } else if (typeof originOrStream === 'number') {
+ if (originOrStream >>> 0 !== originOrStream || originOrStream === 0)
+ throw new errors.RangeError('ERR_OUT_OF_RANGE', 'originOrStream');
+ stream = originOrStream;
+ } else if (originOrStream !== undefined) {
+ // Allow origin to be passed a URL or object with origin property
+ if (originOrStream !== null && typeof originOrStream === 'object')
+ origin = originOrStream.origin;
+ // Note: if originOrStream is an object with an origin property other
+ // than a URL, then it is possible that origin will be malformed.
+ // We do not verify that here. Users who go that route need to
+ // ensure they are doing the right thing or the payload data will
+ // be invalid.
+ if (typeof origin !== 'string') {
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'originOrStream',
+ ['string', 'number', 'URL', 'object']);
+ } else if (origin === 'null' || origin.length === 0) {
+ throw new errors.TypeError('ERR_HTTP2_ALTSVC_INVALID_ORIGIN');
+ }
+ }
+
+ if (typeof alt !== 'string')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'alt', 'string');
+ if (!kQuotedString.test(alt))
+ throw new errors.TypeError('ERR_INVALID_CHAR', 'alt');
+
+ // Max length permitted for ALTSVC
+ if ((alt.length + (origin !== undefined ? origin.length : 0)) > 16382)
+ throw new errors.TypeError('ERR_HTTP2_ALTSVC_LENGTH');
+
+ this[kHandle].altsvc(stream, origin || '', alt);
+ }
}
+// ClientHttp2Session instances have to wait for the socket to connect after
+// they have been created. Various operations such as request() may be used,
+// but the actual protocol communication will only occur after the socket
+// has been connected.
class ClientHttp2Session extends Http2Session {
constructor(options, socket) {
super(NGHTTP2_SESSION_CLIENT, options, socket);
@@ -1018,11 +1314,14 @@ class ClientHttp2Session extends Http2Session {
// Submits a new HTTP2 request to the connected peer. Returns the
// associated Http2Stream instance.
request(headers, options) {
- const state = this[kState];
- if (state.destroyed || state.destroying)
- throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
debug(`Http2Session ${sessionName(this[kType])}: initiating request`);
+ if (this.destroyed)
+ throw new errors.Error('ERR_HTTP2_INVALID_SESSION');
+
+ if (this.closed)
+ throw new errors.Error('ERR_HTTP2_GOAWAY_SESSION');
+
this[kUpdateTimer]();
assertIsObject(headers, 'headers');
@@ -1072,15 +1371,20 @@ class ClientHttp2Session extends Http2Session {
options.getTrailers);
}
+ const headersList = mapToHeaders(headers);
+ if (!Array.isArray(headersList))
+ throw headersList;
+
const stream = new ClientHttp2Stream(this, undefined, undefined, {});
+ stream[kSentHeaders] = headers;
// Close the writable side of the stream if options.endStream is set.
if (options.endStream)
stream.end();
- const onConnect = requestOnConnect.bind(stream, headers, options);
- if (state.connecting) {
- stream.on('connect', onConnect);
+ const onConnect = requestOnConnect.bind(stream, headersList, options);
+ if (this.connecting) {
+ this.on('connect', onConnect);
} else {
onConnect();
}
@@ -1128,63 +1432,38 @@ function afterDoStreamWrite(status, handle, req) {
if (session !== undefined)
session[kState].writeQueueSize -= bytes;
-
if (typeof req.callback === 'function')
- req.callback();
+ req.callback(null);
req.handle = undefined;
}
-function onHandleFinish() {
- if (this[kID] === undefined) {
- this.once('ready', onHandleFinish);
- } else {
- const handle = this[kHandle];
- if (handle !== undefined) {
- const req = new ShutdownWrap();
- req.oncomplete = () => {};
- req.handle = handle;
- handle.shutdown(req);
- }
- }
-}
-
-function onSessionClose(hadError, code) {
- abort(this);
- this.push(null); // Close the readable side
- this.end(); // Close the writable side
-}
-
function streamOnResume() {
- if (this[kID] === undefined) {
- this.once('ready', streamOnResume);
- return;
- }
- this[kHandle].readStart();
+ if (!this.destroyed && !this.pending)
+ this[kHandle].readStart();
}
function streamOnPause() {
- this[kHandle].readStop();
-}
-
-function handleFlushData(handle) {
- handle.flushData();
-}
-
-function streamOnSessionConnect() {
- const session = this[kSession];
- debug(`Http2Session ${sessionName(session[kType])}: session connected`);
- this[kState].connecting = false;
- process.nextTick(emit, this, 'connect');
+ if (!this.destroyed && !this.pending)
+ 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[kState].aborted &&
+ if (!stream.aborted &&
!(stream._writableState.ended || stream._writableState.ending)) {
+ stream[kState].flags |= STREAM_FLAGS_ABORTED;
stream.emit('aborted');
- stream[kState].aborted = true;
}
}
+function afterShutdown() {
+ this.callback();
+ const stream = this.handle[kOwner];
+ if (stream)
+ stream[kMaybeDestroy]();
+}
+
// An Http2Stream is a Duplex stream that is backed by a
// node::http2::Http2Stream handle implementing StreamBase.
class Http2Stream extends Duplex {
@@ -1193,37 +1472,40 @@ class Http2Stream extends Duplex {
options.decodeStrings = false;
super(options);
this[async_id_symbol] = -1;
+
+ // Corking the stream automatically allows writes to happen
+ // but ensures that those are buffered until the handle has
+ // been assigned.
this.cork();
this[kSession] = session;
+ session[kState].pendingStreams.add(this);
- const state = this[kState] = {
- rst: false,
+ this[kState] = {
+ flags: STREAM_FLAGS_PENDING,
rstCode: NGHTTP2_NO_ERROR,
- headersSent: false,
- headRequest: false,
- aborted: false,
- closeHandler: onSessionClose.bind(this),
writeQueueSize: 0
};
- this.once('finish', onHandleFinish);
this.on('resume', streamOnResume);
this.on('pause', streamOnPause);
- session.once('close', state.closeHandler);
-
- if (session[kState].connecting) {
- state.connecting = true;
- session.once('connect', streamOnSessionConnect.bind(this));
- }
}
[kUpdateTimer]() {
+ if (this.destroyed)
+ return;
_unrefActive(this);
if (this[kSession])
_unrefActive(this[kSession]);
}
[kInit](id, handle) {
+ const state = this[kState];
+ state.flags |= STREAM_FLAGS_READY;
+
+ const session = this[kSession];
+ session[kState].pendingStreams.delete(this);
+ session[kState].streams.set(id, this);
+
this[kID] = id;
this[async_id_symbol] = handle.getAsyncId();
handle[kOwner] = this;
@@ -1237,7 +1519,9 @@ class Http2Stream extends Duplex {
[kInspect](depth, opts) {
const obj = {
- id: this[kID],
+ id: this[kID] || '',
+ closed: this.closed,
+ destroyed: this.destroyed,
state: this.state,
readableState: this._readableState,
writableState: this._writableState
@@ -1245,6 +1529,22 @@ class Http2Stream extends Duplex {
return `Http2Stream ${util.format(obj)}`;
}
+ get sentHeaders() {
+ return this[kSentHeaders];
+ }
+
+ get sentTrailers() {
+ return this[kSentTrailers];
+ }
+
+ get sentInfoHeaders() {
+ return this[kInfoHeaders];
+ }
+
+ get pending() {
+ return this[kID] === undefined;
+ }
+
// The id of the Http2Stream, will be undefined if the socket is not
// yet connected.
get id() {
@@ -1257,6 +1557,8 @@ class Http2Stream extends Duplex {
}
_onTimeout() {
+ if (this.destroyed)
+ return;
// This checks whether a write is currently in progress and also whether
// that write is actually sending data across the write. The kHandle
// stored `chunksSentSinceLastWrite` is only updated when a timeout event
@@ -1273,22 +1575,27 @@ class Http2Stream extends Duplex {
}
}
- process.nextTick(emit, this, 'timeout');
+ this.emit('timeout');
+ }
+
+ // true if the HEADERS frame has been sent
+ get headersSent() {
+ return !!(this[kState].flags & STREAM_FLAGS_HEADERS_SENT);
}
- // true if the Http2Stream was aborted abornomally.
+ // true if the Http2Stream was aborted abnormally.
get aborted() {
- return this[kState].aborted;
+ return !!(this[kState].flags & STREAM_FLAGS_ABORTED);
}
// true if dealing with a HEAD request
get headRequest() {
- return this[kState].headRequest;
+ return !!(this[kState].flags & STREAM_FLAGS_HEAD_REQUEST);
}
// The error code reported when this Http2Stream was closed.
get rstCode() {
- return this[kState].rst ? this[kState].rstCode : undefined;
+ return this[kState].rstCode;
}
// State information for the Http2Stream
@@ -1306,14 +1613,26 @@ class Http2Stream extends Duplex {
}
_write(data, encoding, cb) {
- if (this[kID] === undefined) {
+ // When the Http2Stream is first created, it is corked until the
+ // handle and the stream ID is assigned. However, if the user calls
+ // uncork() before that happens, the Duplex will attempt to pass
+ // writes through. Those need to be queued up here.
+ if (this.pending) {
this.once('ready', this._write.bind(this, data, encoding, cb));
return;
}
- this[kUpdateTimer]();
+ // If the stream has been destroyed, there's nothing else we can do
+ // because the handle has been destroyed. This should only be an
+ // issue if a write occurs before the 'ready' event in the case where
+ // the duplex is uncorked before the stream is ready to go. In that
+ // case, drop the data on the floor. An error should have already been
+ // emitted.
+ if (this.destroyed)
+ return;
- if (!this[kState].headersSent)
+ this[kUpdateTimer]();
+ if (!this.headersSent)
this[kProceed]();
const handle = this[kHandle];
@@ -1325,19 +1644,32 @@ class Http2Stream extends Duplex {
req.async = false;
const err = createWriteReq(req, handle, data, encoding);
if (err)
- throw util._errnoException(err, 'write', req.error);
+ return this.destroy(util._errnoException(err, 'write', req.error), cb);
trackWriteState(this, req.bytes);
}
_writev(data, cb) {
- if (this[kID] === undefined) {
+ // When the Http2Stream is first created, it is corked until the
+ // handle and the stream ID is assigned. However, if the user calls
+ // uncork() before that happens, the Duplex will attempt to pass
+ // writes through. Those need to be queued up here.
+ if (this.pending) {
this.once('ready', this._writev.bind(this, data, cb));
return;
}
+ // If the stream has been destroyed, there's nothing else we can do
+ // because the handle has been destroyed. This should only be an
+ // issue if a write occurs before the 'ready' event in the case where
+ // the duplex is uncorked before the stream is ready to go. In that
+ // case, drop the data on the floor. An error should have already been
+ // emitted.
+ if (this.destroyed)
+ return;
+
this[kUpdateTimer]();
- if (!this[kState].headersSent)
+ if (!this.headersSent)
this[kProceed]();
const handle = this[kHandle];
@@ -1355,56 +1687,37 @@ class Http2Stream extends Duplex {
}
const err = handle.writev(req, chunks);
if (err)
- throw util._errnoException(err, 'write', req.error);
+ return this.destroy(util._errnoException(err, 'write', req.error), cb);
trackWriteState(this, req.bytes);
}
+ _final(cb) {
+ const handle = this[kHandle];
+ if (this[kID] === undefined) {
+ this.once('ready', () => this._final(cb));
+ } else if (handle !== undefined) {
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(this[kSession][kType])}]: _final shutting down`);
+ const req = new ShutdownWrap();
+ req.oncomplete = afterShutdown;
+ req.callback = cb;
+ req.handle = handle;
+ handle.shutdown(req);
+ } else {
+ cb();
+ }
+ }
+
_read(nread) {
if (this.destroyed) {
this.push(null);
return;
}
- if (this[kHandle] !== undefined)
- process.nextTick(handleFlushData, this[kHandle]);
- }
-
- // Submits an RST-STREAM frame to shutdown this stream.
- // If the stream ID has not yet been allocated, the action will
- // defer until the ready event is emitted.
- // After sending the rstStream, this.destroy() will be called making
- // the stream object no longer usable.
- rstStream(code = NGHTTP2_NO_ERROR) {
- if (typeof code !== 'number')
- throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'code', 'number');
- if (code < 0 || code > kMaxInt)
- throw new errors.RangeError('ERR_OUT_OF_RANGE', 'code');
-
- const rstStreamFn = submitRstStream.bind(this, code);
- if (this[kID] === undefined) {
- this.once('ready', rstStreamFn);
- return;
+ if (!this.pending) {
+ streamOnResume.call(this);
+ } else {
+ this.once('ready', streamOnResume);
}
- rstStreamFn();
- }
-
- rstWithNoError() {
- this.rstStream(NGHTTP2_NO_ERROR);
- }
-
- rstWithProtocolError() {
- this.rstStream(NGHTTP2_PROTOCOL_ERROR);
- }
-
- rstWithCancel() {
- this.rstStream(NGHTTP2_CANCEL);
- }
-
- rstWithRefuse() {
- this.rstStream(NGHTTP2_REFUSED_STREAM);
- }
-
- rstWithInternalError() {
- this.rstStream(NGHTTP2_INTERNAL_ERROR);
}
priority(options) {
@@ -1416,86 +1729,148 @@ class Http2Stream extends Duplex {
validatePriorityOptions(options);
const priorityFn = submitPriority.bind(this, options);
- if (this[kID] === undefined) {
+
+ // If the handle has not yet been assigned, queue up the priority
+ // frame to be sent as soon as the ready event is emitted.
+ if (this.pending) {
this.once('ready', priorityFn);
return;
}
priorityFn();
}
+ get closed() {
+ return !!(this[kState].flags & STREAM_FLAGS_CLOSED);
+ }
+
+ // Close initiates closing the Http2Stream instance by sending an RST_STREAM
+ // frame to the connected peer. The readable and writable sides of the
+ // Http2Stream duplex are closed and the timeout timer is unenrolled. If
+ // a callback is passed, it is registered to listen for the 'close' event.
+ //
+ // If the handle and stream ID have not been assigned yet, the close
+ // will be queued up to wait for the ready event. As soon as the stream ID
+ // is determined, the close will proceed.
+ //
+ // Submitting the RST_STREAM frame to the underlying handle will cause
+ // the Http2Stream to be closed and ultimately destroyed. After calling
+ // close, it is still possible to queue up PRIORITY and RST_STREAM frames,
+ // but no DATA and HEADERS frames may be sent.
+ close(code = NGHTTP2_NO_ERROR, callback) {
+ if (typeof code !== 'number')
+ throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'code', 'number');
+ if (code < 0 || code > kMaxInt)
+ throw new errors.RangeError('ERR_OUT_OF_RANGE', 'code');
+ if (callback !== undefined && typeof callback !== 'function')
+ throw new errors.TypeError('ERR_INVALID_CALLBACK');
+
+ // Unenroll the timeout.
+ unenroll(this);
+ 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) {
+ 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();
+ }
+
// Called by this.destroy().
- // * If called before the stream is allocated, will defer until the
- // ready event is emitted.
// * Will submit an RST stream to shutdown the stream if necessary.
// This will cause the internal resources to be released.
// * Then cleans up the resources on the js side
_destroy(err, callback) {
const session = this[kSession];
- if (this[kID] === undefined) {
- this.once('ready', this._destroy.bind(this, err, callback));
- return;
- }
+ const handle = this[kHandle];
+ const id = this[kID];
- debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ debug(`Http2Stream ${this[kID] || ''} [Http2Session ` +
`${sessionName(session[kType])}]: destroying stream`);
-
const state = this[kState];
- session[kState].writeQueueSize -= state.writeQueueSize;
- state.writeQueueSize = 0;
-
- const server = session[kServer];
- if (server !== undefined && err) {
- server.emit('streamError', err, this);
+ 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);
+ handle.destroy();
+ session[kState].streams.delete(id);
+ } else {
+ unenroll(this);
+ this.removeAllListeners('timeout');
+ state.flags |= STREAM_FLAGS_CLOSED;
+ abort(this);
+ this.end();
+ this.push(null);
+ session[kState].pendingStreams.delete(this);
}
- process.nextTick(continueStreamDestroy.bind(this), err, callback);
- }
-}
-
-function continueStreamDestroy(err, callback) {
- const session = this[kSession];
- const state = this[kState];
-
- // Submit RST-STREAM frame if one hasn't been sent already and the
- // stream hasn't closed normally...
- const rst = state.rst;
- let code = state.rstCode;
- if (!rst && !session.destroyed) {
- code = err instanceof Error ? NGHTTP2_INTERNAL_ERROR : NGHTTP2_NO_ERROR;
- this.rstStream(code);
- }
+ // Adjust the write queue size for accounting
+ session[kState].writeQueueSize -= state.writeQueueSize;
+ state.writeQueueSize = 0;
- // Remove the close handler on the session
- session.removeListener('close', state.closeHandler);
+ // RST code 8 not emitted as an error as its used by clients to signify
+ // abort and is already covered by aborted event, also allows more
+ // seamless compatibility with http1
+ if (err == null && code !== NGHTTP2_NO_ERROR && code !== NGHTTP2_CANCEL)
+ err = new errors.Error('ERR_HTTP2_STREAM_ERROR', code);
- // Unenroll the timer
- this.setTimeout(0);
+ this[kSession] = undefined;
+ this[kHandle] = undefined;
- setImmediate(finishStreamDestroy.bind(this));
+ // This notifies the session that this stream has been destroyed and
+ // gives the session the opportunity to clean itself up. The session
+ // will destroy if it has been closed and there are no other open or
+ // pending streams.
+ session[kMaybeDestroy]();
+ process.nextTick(emit, this, 'close', code);
+ callback(err);
- // RST code 8 not emitted as an error as its used by clients to signify
- // abort and is already covered by aborted event, also allows more
- // seamless compatibility with http1
- if (code !== NGHTTP2_NO_ERROR && code !== NGHTTP2_CANCEL && !err) {
- err = new errors.Error('ERR_HTTP2_STREAM_ERROR', code);
}
- callback(err);
- abort(this);
- this.push(null); // Close the readable side
- this.end(); // Close the writable side
- process.nextTick(emit, this, 'close', code);
-}
+ // The Http2Stream can be destroyed if it has closed and if the readable
+ // side has received the final chunk.
+ [kMaybeDestroy](error, code = NGHTTP2_NO_ERROR) {
+ if (error || code !== NGHTTP2_NO_ERROR) {
+ this.destroy(error);
+ return;
+ }
-function finishStreamDestroy() {
- const id = this[kID];
- this[kSession][kState].streams.delete(id);
- this[kSession] = undefined;
- const handle = this[kHandle];
- if (handle !== undefined) {
- this[kHandle] = undefined;
- handle.destroy();
+ // 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
+ }
}
- this.emit('destroy');
}
function processHeaders(headers) {
@@ -1509,7 +1884,7 @@ function processHeaders(headers) {
// This is intentionally stricter than the HTTP/1 implementation, which
// allows values between 100 and 999 (inclusive) in order to allow for
// backwards compatibility with non-spec compliant code. With HTTP/2,
- // we have the opportunity to start fresh with stricter spec copmliance.
+ // we have the opportunity to start fresh with stricter spec compliance.
// This will have an impact on the compatibility layer for anyone using
// non-standard, non-compliant status codes.
if (statusCode < 200 || statusCode > 599)
@@ -1519,36 +1894,48 @@ function processHeaders(headers) {
return headers;
}
-function processRespondWithFD(fd, headers, offset = 0, length = -1,
+function processRespondWithFD(self, fd, headers, offset = 0, length = -1,
streamOptions = 0) {
- const state = this[kState];
- state.headersSent = true;
+ const state = self[kState];
+ state.flags |= STREAM_FLAGS_HEADERS_SENT;
+
+ const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse);
+ self[kSentHeaders] = headers;
+ if (!Array.isArray(headersList)) {
+ self.destroy(headersList);
+ return;
+ }
+
// Close the writable side of the stream
- this.end();
+ self.end();
- const ret = this[kHandle].respondFD(fd, headers,
+ const ret = self[kHandle].respondFD(fd, headersList,
offset, length,
streamOptions);
if (ret < 0) {
- const err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
+ self.destroy(new NghttpError(ret));
return;
}
// exact length of the file doesn't matter here, since the
- // stream is closing anyway — just use 1 to signify that
+ // stream is closing anyway - just use 1 to signify that
// a write does exist
- trackWriteState(this, 1);
+ trackWriteState(self, 1);
}
function doSendFD(session, options, fd, headers, streamOptions, err, stat) {
- if (this.destroyed || session.destroyed) {
- abort(this);
+ if (err) {
+ this.destroy(err);
return;
}
- if (err) {
- process.nextTick(emit, this, 'error', err);
+
+ // This can happen if the stream is destroyed or closed while we are waiting
+ // for the file descriptor to be opened or the stat call to be completed.
+ // In either case, we do not want to continue because the we are shutting
+ // down and should not attempt to send any data.
+ if (this.destroyed || this.closed) {
+ this.destroy(new errors.Error('ERR_HTTP2_INVALID_STREAM'));
return;
}
@@ -1557,47 +1944,47 @@ function doSendFD(session, options, fd, headers, streamOptions, err, stat) {
length: options.length !== undefined ? options.length : -1
};
- if (typeof options.statCheck === 'function' &&
- options.statCheck.call(this, stat, headers, statOptions) === false) {
- return;
- }
-
- const headersList = mapToHeaders(headers,
- assertValidPseudoHeaderResponse);
- if (!Array.isArray(headersList)) {
- process.nextTick(emit, this, 'error', headersList);
+ // options.statCheck is a user-provided function that can be used to
+ // verify stat values, override or set headers, or even cancel the
+ // response operation. If statCheck explicitly returns false, the
+ // response is canceled. The user code may also send a separate type
+ // of response so check again for the HEADERS_SENT flag
+ if ((typeof options.statCheck === 'function' &&
+ options.statCheck.call(this, stat, headers, statOptions) === false) ||
+ (this[kState].flags & STREAM_FLAGS_HEADERS_SENT)) {
return;
}
- processRespondWithFD.call(this, fd, headersList,
- statOptions.offset,
- statOptions.length,
- streamOptions);
+ processRespondWithFD(this, fd, headers,
+ statOptions.offset | 0,
+ statOptions.length | 0,
+ streamOptions);
}
function doSendFileFD(session, options, fd, headers, streamOptions, err, stat) {
- if (this.destroyed || session.destroyed) {
- abort(this);
- return;
- }
const onError = options.onError;
if (err) {
- if (onError) {
+ tryClose(fd);
+ if (onError)
onError(err);
- } else {
+ else
this.destroy(err);
- }
return;
}
if (!stat.isFile()) {
- err = new errors.Error('ERR_HTTP2_SEND_FILE');
- if (onError) {
+ const err = new errors.Error('ERR_HTTP2_SEND_FILE');
+ if (onError)
onError(err);
- } else {
+ else
this.destroy(err);
- }
+ return;
+ }
+
+ if (this.destroyed || this.closed) {
+ tryClose(fd);
+ this.destroy(new errors.Error('ERR_HTTP2_INVALID_STREAM'));
return;
}
@@ -1606,9 +1993,14 @@ function doSendFileFD(session, options, fd, headers, streamOptions, err, stat) {
length: options.length !== undefined ? options.length : -1
};
- // Set the content-length by default
- if (typeof options.statCheck === 'function' &&
- options.statCheck.call(this, stat, headers) === false) {
+ // options.statCheck is a user-provided function that can be used to
+ // verify stat values, override or set headers, or even cancel the
+ // response operation. If statCheck explicitly returns false, the
+ // response is canceled. The user code may also send a separate type
+ // of response so check again for the HEADERS_SENT flag
+ if ((typeof options.statCheck === 'function' &&
+ options.statCheck.call(this, stat, headers) === false) ||
+ (this[kState].flags & STREAM_FLAGS_HEADERS_SENT)) {
return;
}
@@ -1617,35 +2009,27 @@ function doSendFileFD(session, options, fd, headers, streamOptions, err, stat) {
Math.min(stat.size - (+statOptions.offset),
statOptions.length);
- if (headers[HTTP2_HEADER_CONTENT_LENGTH] === undefined)
- headers[HTTP2_HEADER_CONTENT_LENGTH] = statOptions.length;
-
- const headersList = mapToHeaders(headers,
- assertValidPseudoHeaderResponse);
- if (!Array.isArray(headersList)) {
- process.nextTick(emit, this, 'error', headersList);
- return;
- }
+ headers[HTTP2_HEADER_CONTENT_LENGTH] = statOptions.length;
- processRespondWithFD.call(this, fd, headersList,
- options.offset,
- options.length,
- streamOptions);
+ processRespondWithFD(this, fd, headers,
+ options.offset | 0,
+ statOptions.length | 0,
+ streamOptions);
}
function afterOpen(session, options, headers, streamOptions, err, fd) {
const state = this[kState];
const onError = options.onError;
- if (this.destroyed || session.destroyed) {
- abort(this);
- return;
- }
if (err) {
- if (onError) {
+ if (onError)
onError(err);
- } else {
+ else
this.destroy(err);
- }
+ return;
+ }
+ if (this.destroyed || this.closed) {
+ tryClose(fd);
+ abort(this);
return;
}
state.fd = fd;
@@ -1658,8 +2042,6 @@ function afterOpen(session, options, headers, streamOptions, err, fd) {
function streamOnError(err) {
// we swallow the error for parity with HTTP1
// all the errors that ends here are not critical for the project
- debug(`Http2Stream ${this[kID]} [Http2Session ` +
- `${this[kSession][kType]}: error`, err);
}
@@ -1672,25 +2054,22 @@ class ServerHttp2Stream extends Http2Stream {
this.on('error', streamOnError);
}
- // true if the HEADERS frame has been sent
- get headersSent() {
- return this[kState].headersSent;
- }
-
// true if the remote peer accepts push streams
get pushAllowed() {
- return this[kSession].remoteSettings.enablePush;
+ return !this.destroyed &&
+ !this.closed &&
+ !this.session.closed &&
+ !this.session.destroyed &&
+ this[kSession].remoteSettings.enablePush;
}
// create a push stream, call the given callback with the created
// Http2Stream for the push stream.
pushStream(headers, options, callback) {
- if (this.destroyed)
- throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
+ if (!this.pushAllowed)
+ throw new errors.Error('ERR_HTTP2_PUSH_DISABLED');
const session = this[kSession];
- if (!session.remoteSettings.enablePush)
- throw new errors.Error('ERR_HTTP2_PUSH_DISABLED');
debug(`Http2Stream ${this[kID]} [Http2Session ` +
`${sessionName(session[kType])}]: initiating push stream`);
@@ -1740,44 +2119,46 @@ class ServerHttp2Stream extends Http2Stream {
err = new errors.Error('ERR_HTTP2_OUT_OF_STREAMS');
break;
case NGHTTP2_ERR_STREAM_CLOSED:
- err = new errors.Error('ERR_HTTP2_STREAM_CLOSED');
+ err = new errors.Error('ERR_HTTP2_INVALID_STREAM');
break;
default:
err = new NghttpError(ret);
break;
}
- process.nextTick(emit, this, 'error', err);
+ process.nextTick(callback, err);
return;
}
const id = ret.id();
const stream = new ServerHttp2Stream(session, ret, id, options, headers);
- session[kState].streams.set(id, stream);
+ stream[kSentHeaders] = headers;
if (options.endStream)
stream.end();
if (headRequest)
- stream[kState].headRequest = true;
+ stream[kState].flags |= STREAM_FLAGS_HEAD_REQUEST;
- process.nextTick(callback, stream, headers, 0);
+ process.nextTick(callback, null, stream, headers, 0);
}
// Initiate a response on this Http2Stream
respond(headers, options) {
- const session = this[kSession];
- if (this.destroyed)
+ if (this.destroyed || this.closed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- debug(`Http2Stream ${this[kID]} [Http2Session ` +
- `${sessionName(session[kType])}]: initiating response`);
- this[kUpdateTimer]();
- const state = this[kState];
-
- if (state.headersSent)
+ if (this.headersSent)
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
+ const state = this[kState];
+
assertIsObject(options, 'options');
options = Object.assign({}, options);
+
+ const session = this[kSession];
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: initiating response`);
+ this[kUpdateTimer]();
+
options.endStream = !!options.endStream;
let streamOptions = 0;
@@ -1803,25 +2184,24 @@ class ServerHttp2Stream extends Http2Stream {
if (statusCode === HTTP_STATUS_NO_CONTENT ||
statusCode === HTTP_STATUS_RESET_CONTENT ||
statusCode === HTTP_STATUS_NOT_MODIFIED ||
- state.headRequest === true) {
+ this.headRequest === true) {
options.endStream = true;
}
const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse);
if (!Array.isArray(headersList))
throw headersList;
+ this[kSentHeaders] = headers;
- state.headersSent = true;
+ state.flags |= STREAM_FLAGS_HEADERS_SENT;
// Close the writable side if the endStream option is set
if (options.endStream)
this.end();
const ret = this[kHandle].respond(headersList, streamOptions);
- if (ret < 0) {
- const err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- }
+ if (ret < 0)
+ this.destroy(new NghttpError(ret));
}
// Initiate a response using an open FD. Note that there are fewer
@@ -1831,19 +2211,15 @@ class ServerHttp2Stream extends Http2Stream {
// mechanism is not able to read from the fd, then the stream will be
// reset with an error code.
respondWithFD(fd, headers, options) {
- const session = this[kSession];
- if (this.destroyed)
+ if (this.destroyed || this.closed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- debug(`Http2Stream ${this[kID]} [Http2Session ` +
- `${sessionName(session[kType])}]: initiating response`);
- this[kUpdateTimer]();
- const state = this[kState];
-
- if (state.headersSent)
+ if (this.headersSent)
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
+ const session = this[kSession];
+
assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
+ options = Object.assign({}, options);
if (options.offset !== undefined && typeof options.offset !== 'number')
throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
@@ -1870,13 +2246,17 @@ class ServerHttp2Stream extends Http2Stream {
options.getTrailers);
}
streamOptions |= STREAM_OPTION_GET_TRAILERS;
- state.getTrailers = options.getTrailers;
+ this[kState].getTrailers = options.getTrailers;
}
if (typeof fd !== 'number')
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
'fd', 'number');
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: initiating response`);
+ this[kUpdateTimer]();
+
headers = processHeaders(headers);
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
// Payload/DATA frames are not permitted in these cases
@@ -1893,17 +2273,10 @@ class ServerHttp2Stream extends Http2Stream {
return;
}
- const headersList = mapToHeaders(headers,
- assertValidPseudoHeaderResponse);
- if (!Array.isArray(headersList)) {
- process.nextTick(emit, this, 'error', headersList);
- return;
- }
-
- processRespondWithFD.call(this, fd, headersList,
- options.offset,
- options.length,
- streamOptions);
+ processRespondWithFD(this, fd, headers,
+ options.offset,
+ options.length,
+ streamOptions);
}
// Initiate a file response on this Http2Stream. The path is passed to
@@ -1914,19 +2287,13 @@ class ServerHttp2Stream extends Http2Stream {
// headers. If statCheck returns false, the operation is aborted and no
// file details are sent.
respondWithFile(path, headers, options) {
- const session = this[kSession];
- if (this.destroyed)
+ if (this.destroyed || this.closed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
- debug(`Http2Stream ${this[kID]} [Http2Session ` +
- `${sessionName(session[kType])}]: initiating response`);
- this[kUpdateTimer]();
- const state = this[kState];
-
- if (state.headersSent)
+ if (this.headersSent)
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
+ options = Object.assign({}, options);
if (options.offset !== undefined && typeof options.offset !== 'number')
throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
@@ -1953,9 +2320,15 @@ class ServerHttp2Stream extends Http2Stream {
options.getTrailers);
}
streamOptions |= STREAM_OPTION_GET_TRAILERS;
- state.getTrailers = options.getTrailers;
+ this[kState].getTrailers = options.getTrailers;
}
+ const session = this[kSession];
+ debug(`Http2Stream ${this[kID]} [Http2Session ` +
+ `${sessionName(session[kType])}]: initiating response`);
+ this[kUpdateTimer]();
+
+
headers = processHeaders(headers);
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
// Payload/DATA frames are not permitted in these cases
@@ -1977,18 +2350,18 @@ class ServerHttp2Stream extends Http2Stream {
// a 1xx informational code and it MUST be sent before the request/response
// headers are sent, or an error will be thrown.
additionalHeaders(headers) {
- if (this.destroyed)
+ if (this.destroyed || this.closed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
-
- if (this[kState].headersSent)
+ if (this.headersSent)
throw new errors.Error('ERR_HTTP2_HEADERS_AFTER_RESPOND');
+ assertIsObject(headers, 'headers');
+ headers = Object.assign(Object.create(null), headers);
+
const session = this[kSession];
debug(`Http2Stream ${this[kID]} [Http2Session ` +
- `${sessionName(session[kType])}]: sending additional headers`);
+ `${sessionName(session[kType])}]: sending additional headers`);
- assertIsObject(headers, 'headers');
- headers = Object.assign(Object.create(null), headers);
if (headers[HTTP2_HEADER_STATUS] != null) {
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
if (statusCode === HTTP_STATUS_SWITCHING_PROTOCOLS)
@@ -2001,17 +2374,17 @@ class ServerHttp2Stream extends Http2Stream {
this[kUpdateTimer]();
- const headersList = mapToHeaders(headers,
- assertValidPseudoHeaderResponse);
- if (!Array.isArray(headersList)) {
+ const headersList = mapToHeaders(headers, assertValidPseudoHeaderResponse);
+ if (!Array.isArray(headersList))
throw headersList;
- }
+ if (!this[kInfoHeaders])
+ this[kInfoHeaders] = [headers];
+ else
+ this[kInfoHeaders].push(headers);
const ret = this[kHandle].info(headersList);
- if (ret < 0) {
- const err = new NghttpError(ret);
- process.nextTick(emit, this, 'error', err);
- }
+ if (ret < 0)
+ this.destroy(new NghttpError(ret));
}
}
@@ -2020,7 +2393,7 @@ ServerHttp2Stream.prototype[kProceed] = ServerHttp2Stream.prototype.respond;
class ClientHttp2Stream extends Http2Stream {
constructor(session, handle, id, options) {
super(session, options);
- this[kState].headersSent = true;
+ this[kState].flags |= STREAM_FLAGS_HEADERS_SENT;
if (id !== undefined)
this[kInit](id, handle);
this.on('headers', handleHeaderContinue);
@@ -2028,9 +2401,8 @@ class ClientHttp2Stream extends Http2Stream {
}
function handleHeaderContinue(headers) {
- if (headers[HTTP2_HEADER_STATUS] === HTTP_STATUS_CONTINUE) {
+ if (headers[HTTP2_HEADER_STATUS] === HTTP_STATUS_CONTINUE)
this.emit('continue');
- }
}
const setTimeout = {
@@ -2038,6 +2410,8 @@ const setTimeout = {
enumerable: true,
writable: true,
value: function(msecs, callback) {
+ if (this.destroyed)
+ return;
if (typeof msecs !== 'number') {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
'msecs',
@@ -2065,91 +2439,44 @@ const setTimeout = {
Object.defineProperty(Http2Stream.prototype, 'setTimeout', setTimeout);
Object.defineProperty(Http2Session.prototype, 'setTimeout', setTimeout);
-// --------------------------------------------------------------------
-
-// Set as a replacement for socket.prototype.destroy upon the
-// establishment of a new connection.
-function socketDestroy(error) {
- const session = this[kSession];
- const type = session[kType];
- debug(`Http2Session ${sessionName(type)}: socket destroy called`);
- delete this[kServer];
- // destroy the session first so that it will stop trying to
- // send data while we close the socket.
- session.destroy();
- this.destroy = this[kDestroySocket];
- this.destroy(error);
-}
-
-// When an Http2Session emits an error, first try to forward it to the
-// server as a sessionError; failing that, forward it to the socket as
-// a sessionError; failing that, destroy, remove the error listener, and
-// re-emit the error event
-function sessionOnError(error) {
- debug(`Http2Session ${sessionName(this[kType])}: session error: ` +
- `${error.message}`);
- if (this[kServer] !== undefined && this[kServer].emit('sessionError', error))
- return;
- if (this[kSocket] !== undefined && this[kSocket].emit('sessionError', error))
- return;
- this.destroy();
- this.removeListener('error', sessionOnError);
- this.emit('error', error);
-}
-// When a Socket emits an error, forward it to the session as a
-// socketError; failing that, remove the listener and call destroy
+// When the socket emits an error, destroy the associated Http2Session and
+// foward it the same error.
function socketOnError(error) {
const session = this[kSession];
- const type = session && session[kType];
- debug(`Http2Session ${sessionName(type)}: socket error: ${error.message}`);
- if (kRenegTest.test(error.message))
- return this.destroy();
- if (session !== undefined &&
- session.emit('socketError', error, this))
- return;
- this.removeListener('error', socketOnError);
- this.destroy(error);
+ if (session !== undefined) {
+ debug(`Http2Session ${sessionName(session[kType])}: socket error [` +
+ `${error.message}]`);
+ session.destroy(error);
+ }
}
// Handles the on('stream') event for a session and forwards
// it on to the server object.
function sessionOnStream(stream, headers, flags, rawHeaders) {
- this[kServer].emit('stream', stream, headers, flags, rawHeaders);
+ if (this[kServer] !== undefined)
+ this[kServer].emit('stream', stream, headers, flags, rawHeaders);
}
function sessionOnPriority(stream, parent, weight, exclusive) {
- debug(`Http2Session ${sessionName(this[kType])}: priority change received`);
- this[kServer].emit('priority', stream, parent, weight, exclusive);
+ if (this[kServer] !== undefined)
+ this[kServer].emit('priority', stream, parent, weight, exclusive);
}
-function sessionOnSocketError(error, socket) {
- if (this.listenerCount('socketError') <= 1 && this[kServer] !== undefined)
- this[kServer].emit('socketError', error, socket, this);
+function sessionOnError(error) {
+ if (this[kServer])
+ this[kServer].emit('sessionError', error, this);
}
-// When the session times out on the server, attempt a graceful shutdown
+// When the session times out on the server, try emitting a timeout event.
+// If no handler is registered, destroy the session.
function sessionOnTimeout() {
- process.nextTick(() => {
- const state = this[kState];
- // if destroyed or destryoing, do nothing
- if (state.destroyed || state.destroying)
- return;
- const server = this[kServer];
- const socket = this[kSocket];
- // If server or socket are undefined, then we're already in the process of
- // shutting down, do nothing.
- if (server === undefined || socket === undefined)
- return;
- if (!server.emit('timeout', this)) {
- this.shutdown(
- {
- graceful: true,
- errorCode: NGHTTP2_NO_ERROR
- },
- socket.destroy.bind(socket));
- }
- });
+ // if destroyed or closed already, do nothing
+ if (this.destroyed || this.closed)
+ return;
+ const server = this[kServer];
+ if (!server.emit('timeout', this))
+ this.destroy(); // No error code, just things down.
}
function connectionListener(socket) {
@@ -2161,10 +2488,18 @@ function connectionListener(socket) {
if (options.allowHTTP1 === true)
return httpConnectionListener.call(this, socket);
// Let event handler deal with the socket
- if (this.emit('unknownProtocol', socket))
- return;
- // Reject non-HTTP/2 client
- return socket.destroy();
+ debug(`Unknown protocol from ${socket.remoteAddress}:${socket.remotePort}`);
+ if (!this.emit('unknownProtocol', socket)) {
+ // We don't know what to do, so let's just tell the other side what's
+ // going on in a format that they *might* understand.
+ socket.end('HTTP/1.0 403 Forbidden\r\n' +
+ 'Content-Type: text/plain\r\n\r\n' +
+ 'Unknown ALPN Protocol, expected `h2` to be available.\n' +
+ 'If this is a HTTP request: The server was not ' +
+ 'configured with the `allowHTTP1` option or a ' +
+ 'listener for the `unknownProtocol` event.\n');
+ }
+ return;
}
socket.on('error', socketOnError);
@@ -2173,27 +2508,24 @@ function connectionListener(socket) {
// Set up the Session
const session = new ServerHttp2Session(options, socket, this);
- session.on('error', sessionOnError);
session.on('stream', sessionOnStream);
session.on('priority', sessionOnPriority);
- session.on('socketError', sessionOnSocketError);
+ session.on('error', sessionOnError);
- if (this.timeout) {
- session.setTimeout(this.timeout);
- session.on('timeout', sessionOnTimeout);
- }
+ if (this.timeout)
+ session.setTimeout(this.timeout, sessionOnTimeout);
socket[kServer] = this;
- process.nextTick(emit, this, 'session', session);
+ this.emit('session', session);
}
function initializeOptions(options) {
assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
+ options = Object.assign({}, options);
options.allowHalfOpen = true;
assertIsObject(options.settings, 'options.settings');
- options.settings = Object.assign(Object.create(null), options.settings);
+ options.settings = Object.assign({}, options.settings);
return options;
}
@@ -2207,9 +2539,9 @@ function initializeTLSOptions(options, servername) {
return options;
}
-function onErrorSecureServerSession(err, conn) {
- if (!this.emit('clientError', err, conn))
- conn.destroy(err);
+function onErrorSecureServerSession(err, socket) {
+ if (!this.emit('clientError', err, socket))
+ socket.destroy(err);
}
class Http2SecureServer extends TLSServer {
@@ -2265,25 +2597,18 @@ function setupCompat(ev) {
function socketOnClose() {
const session = this[kSession];
- if (session !== undefined && !session.destroyed) {
- // Skip unconsume because the socket is destroyed.
- session[kState].skipUnconsume = true;
- session.destroy();
+ if (session !== undefined) {
+ debug(`Http2Session ${sessionName(session[kType])}: socket closed`);
+ const err = session.connecting ?
+ new errors.Error('ERR_SOCKET_CLOSED') : null;
+ const state = session[kState];
+ state.streams.forEach((stream) => stream.close(NGHTTP2_CANCEL));
+ state.pendingStreams.forEach((stream) => stream.close(NGHTTP2_CANCEL));
+ session.close();
+ session[kMaybeDestroy](err);
}
}
-// If the session emits an error, forward it to the socket as a sessionError;
-// failing that, destroy the session, remove the listener and re-emit the error
-function clientSessionOnError(error) {
- debug(`Http2Session ${sessionName(this[kType])}]: session error: ` +
- `${error.message}`);
- if (this[kSocket] !== undefined && this[kSocket].emit('sessionError', error))
- return;
- this.destroy();
- this.removeListener('error', socketOnError);
- this.removeListener('error', clientSessionOnError);
-}
-
function connect(authority, options, listener) {
if (typeof options === 'function') {
listener = options;
@@ -2291,7 +2616,7 @@ function connect(authority, options, listener) {
}
assertIsObject(options, 'options');
- options = Object.assign(Object.create(null), options);
+ options = Object.assign({}, options);
if (typeof authority === 'string')
authority = new URL(authority);
@@ -2324,8 +2649,6 @@ function connect(authority, options, listener) {
const session = new ClientHttp2Session(options, socket);
- session.on('error', clientSessionOnError);
-
session[kAuthority] = `${options.servername || host}:${port}`;
session[kProtocol] = protocol;
@@ -2346,19 +2669,16 @@ Object.defineProperty(connect, promisify.custom, {
});
function createSecureServer(options, handler) {
- if (options == null || typeof options !== 'object') {
- throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
- 'options',
- 'object');
- }
+ assertIsObject(options, 'options');
return new Http2SecureServer(options, handler);
}
function createServer(options, handler) {
if (typeof options === 'function') {
handler = options;
- options = Object.create(null);
+ options = {};
}
+ assertIsObject(options, 'options');
return new Http2Server(options, handler);
}
diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js
index 1800dc5cff33ff..ef48b83d783af0 100644
--- a/lib/internal/http2/util.js
+++ b/lib/internal/http2/util.js
@@ -174,7 +174,9 @@ const IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS = 3;
const IDX_OPTIONS_PADDING_STRATEGY = 4;
const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5;
const IDX_OPTIONS_MAX_OUTSTANDING_PINGS = 6;
-const IDX_OPTIONS_FLAGS = 7;
+const IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS = 7;
+const IDX_OPTIONS_MAX_SESSION_MEMORY = 8;
+const IDX_OPTIONS_FLAGS = 9;
function updateOptionsBuffer(options) {
var flags = 0;
@@ -213,6 +215,16 @@ function updateOptionsBuffer(options) {
optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS] =
options.maxOutstandingPings;
}
+ if (typeof options.maxOutstandingSettings === 'number') {
+ flags |= (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS);
+ optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS] =
+ Math.max(1, options.maxOutstandingSettings);
+ }
+ if (typeof options.maxSessionMemory === 'number') {
+ flags |= (1 << IDX_OPTIONS_MAX_SESSION_MEMORY);
+ optionsBuffer[IDX_OPTIONS_MAX_SESSION_MEMORY] =
+ Math.max(1, options.maxSessionMemory);
+ }
optionsBuffer[IDX_OPTIONS_FLAGS] = flags;
}
diff --git a/lib/internal/loader/Loader.js b/lib/internal/loader/Loader.js
index f2c7fa0cfffc47..49c8699771e819 100644
--- a/lib/internal/loader/Loader.js
+++ b/lib/internal/loader/Loader.js
@@ -10,7 +10,8 @@ const ModuleRequest = require('internal/loader/ModuleRequest');
const errors = require('internal/errors');
const debug = require('util').debuglog('esm');
-function getBase() {
+// Returns a file URL for the current working directory.
+function getURLStringForCwd() {
try {
return getURLFromFilePath(`${process.cwd()}/`).href;
} catch (e) {
@@ -23,22 +24,44 @@ function getBase() {
}
}
+/* A Loader instance is used as the main entry point for loading ES modules.
+ * Currently, this is a singleton -- there is only one used for loading
+ * the main module and everything in its dependency graph. */
class Loader {
- constructor(base = getBase()) {
- this.moduleMap = new ModuleMap();
+ constructor(base = getURLStringForCwd()) {
if (typeof base !== 'string') {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'base', 'string');
}
+
+ this.moduleMap = new ModuleMap();
this.base = base;
- this.resolver = ModuleRequest.resolve.bind(null);
+ // The resolver has the signature
+ // (specifier : string, parentURL : string, defaultResolve)
+ // -> Promise<{ url : string,
+ // format: anything in Loader.validFormats }>
+ // where defaultResolve is ModuleRequest.resolve (having the same
+ // signature itself).
+ // If `.format` on the returned value is 'dynamic', .dynamicInstantiate
+ // will be used as described below.
+ this.resolver = ModuleRequest.resolve;
+ // This hook is only called when resolve(...).format is 'dynamic' and has
+ // the signature
+ // (url : string) -> Promise<{ exports: { ... }, execute: function }>
+ // Where `exports` is an object whose property names define the exported
+ // names of the generated module. `execute` is a function that receives
+ // an object with the same keys as `exports`, whose values are get/set
+ // functions for the actual exported values.
this.dynamicInstantiate = undefined;
}
hook({ resolve = ModuleRequest.resolve, dynamicInstantiate }) {
+ // Use .bind() to avoid giving access to the Loader instance when it is
+ // called as this.resolver(...);
this.resolver = resolve.bind(null);
this.dynamicInstantiate = dynamicInstantiate;
}
+ // Typechecking wrapper around .resolver().
async resolve(specifier, parentURL = this.base) {
if (typeof parentURL !== 'string') {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
@@ -48,10 +71,11 @@ class Loader {
const { url, format } = await this.resolver(specifier, parentURL,
ModuleRequest.resolve);
- if (typeof format !== 'string') {
+ if (!Loader.validFormats.includes(format)) {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'format',
- ['esm', 'cjs', 'builtin', 'addon', 'json']);
+ Loader.validFormats);
}
+
if (typeof url !== 'string') {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'url', 'string');
}
@@ -72,14 +96,20 @@ class Loader {
return { url, format };
}
+ // May create a new ModuleJob instance if one did not already exist.
async getModuleJob(specifier, parentURL = this.base) {
const { url, format } = await this.resolve(specifier, parentURL);
let job = this.moduleMap.get(url);
if (job === undefined) {
let loaderInstance;
if (format === 'dynamic') {
+ const { dynamicInstantiate } = this;
+ if (typeof dynamicInstantiate !== 'function') {
+ throw new errors.Error('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK');
+ }
+
loaderInstance = async (url) => {
- const { exports, execute } = await this.dynamicInstantiate(url);
+ const { exports, execute } = await dynamicInstantiate(url);
return createDynamicModule(exports, url, (reflect) => {
debug(`Loading custom loader ${url}`);
execute(reflect.exports);
@@ -100,5 +130,6 @@ class Loader {
return module.namespace();
}
}
+Loader.validFormats = ['esm', 'cjs', 'builtin', 'addon', 'json', 'dynamic'];
Object.setPrototypeOf(Loader.prototype, null);
module.exports = Loader;
diff --git a/lib/internal/loader/ModuleJob.js b/lib/internal/loader/ModuleJob.js
index 04d6111b87f1f6..77c89f6230e5de 100644
--- a/lib/internal/loader/ModuleJob.js
+++ b/lib/internal/loader/ModuleJob.js
@@ -1,27 +1,36 @@
'use strict';
+const { ModuleWrap } =
+ require('internal/process').internalBinding('module_wrap');
const { SafeSet, SafePromise } = require('internal/safe_globals');
+const assert = require('assert');
const resolvedPromise = SafePromise.resolve();
+const enableDebug = (process.env.NODE_DEBUG || '').match(/\besm\b/) ||
+ process.features.debug;
+
+/* A ModuleJob tracks the loading of a single Module, and the ModuleJobs of
+ * its dependencies, over time. */
class ModuleJob {
- /**
- * @param {module: ModuleWrap?, compiled: Promise} moduleProvider
- */
+ // `loader` is the Loader instance used for loading dependencies.
+ // `moduleProvider` is a function
constructor(loader, url, moduleProvider) {
this.loader = loader;
this.error = null;
this.hadError = false;
- // linked == promise for dependency jobs, with module populated,
- // module wrapper linked
- this.moduleProvider = moduleProvider;
- this.modulePromise = this.moduleProvider(url);
+ // This is a Promise<{ module, reflect }>, whose fields will be copied
+ // onto `this` by `link()` below once it has been resolved.
+ this.modulePromise = moduleProvider(url);
this.module = undefined;
this.reflect = undefined;
- const linked = async () => {
+
+ // Wait for the ModuleWrap instance being linked with all dependencies.
+ const link = async () => {
const dependencyJobs = [];
({ module: this.module,
reflect: this.reflect } = await this.modulePromise);
+ assert(this.module instanceof ModuleWrap);
this.module.link(async (dependencySpecifier) => {
const dependencyJobPromise =
this.loader.getModuleJob(dependencySpecifier, url);
@@ -29,63 +38,57 @@ class ModuleJob {
const dependencyJob = await dependencyJobPromise;
return (await dependencyJob.modulePromise).module;
});
+ if (enableDebug) {
+ // Make sure all dependencies are entered into the list synchronously.
+ Object.freeze(dependencyJobs);
+ }
return SafePromise.all(dependencyJobs);
};
- this.linked = linked();
+ // Promise for the list of all dependencyJobs.
+ this.linked = link();
// instantiated == deep dependency jobs wrappers instantiated,
// module wrapper instantiated
this.instantiated = undefined;
}
- instantiate() {
+ async instantiate() {
if (this.instantiated) {
return this.instantiated;
}
- return this.instantiated = new Promise(async (resolve, reject) => {
- const jobsInGraph = new SafeSet();
- let jobsReadyToInstantiate = 0;
- // (this must be sync for counter to work)
- const queueJob = (moduleJob) => {
- if (jobsInGraph.has(moduleJob)) {
- return;
- }
- jobsInGraph.add(moduleJob);
- moduleJob.linked.then((dependencyJobs) => {
- for (const dependencyJob of dependencyJobs) {
- queueJob(dependencyJob);
- }
- checkComplete();
- }, (e) => {
- if (!this.hadError) {
- this.error = e;
- this.hadError = true;
- }
- checkComplete();
- });
- };
- const checkComplete = () => {
- if (++jobsReadyToInstantiate === jobsInGraph.size) {
- // I believe we only throw once the whole tree is finished loading?
- // or should the error bail early, leaving entire tree to still load?
- if (this.hadError) {
- reject(this.error);
- } else {
- try {
- this.module.instantiate();
- for (const dependencyJob of jobsInGraph) {
- dependencyJob.instantiated = resolvedPromise;
- }
- resolve(this.module);
- } catch (e) {
- e.stack;
- reject(e);
- }
- }
- }
- };
- queueJob(this);
- });
+ return this.instantiated = this._instantiate();
+ }
+
+ // This method instantiates the module associated with this job and its
+ // entire dependency graph, i.e. creates all the module namespaces and the
+ // exported/imported variables.
+ async _instantiate() {
+ const jobsInGraph = new SafeSet();
+
+ const addJobsToDependencyGraph = async (moduleJob) => {
+ if (jobsInGraph.has(moduleJob)) {
+ return;
+ }
+ jobsInGraph.add(moduleJob);
+ const dependencyJobs = await moduleJob.linked;
+ return Promise.all(dependencyJobs.map(addJobsToDependencyGraph));
+ };
+ try {
+ await addJobsToDependencyGraph(this);
+ } catch (e) {
+ if (!this.hadError) {
+ this.error = e;
+ this.hadError = true;
+ }
+ throw e;
+ }
+ this.module.instantiate();
+ for (const dependencyJob of jobsInGraph) {
+ // Calling `this.module.instantiate()` instantiates not only the
+ // ModuleWrap in this module, but all modules in the graph.
+ dependencyJob.instantiated = resolvedPromise;
+ }
+ return this.module;
}
async run() {
diff --git a/lib/internal/loader/ModuleWrap.js b/lib/internal/loader/ModuleWrap.js
index c97b4888ea22ce..0ee05ca81ffbb9 100644
--- a/lib/internal/loader/ModuleWrap.js
+++ b/lib/internal/loader/ModuleWrap.js
@@ -11,39 +11,49 @@ const createDynamicModule = (exports, url = '', evaluate) => {
`creating ESM facade for ${url} with exports: ${ArrayJoin(exports, ', ')}`
);
const names = ArrayMap(exports, (name) => `${name}`);
- // sanitized ESM for reflection purposes
- const src = `export let executor;
- ${ArrayJoin(ArrayMap(names, (name) => `export let $${name}`), ';\n')}
- ;(() => [
- fn => executor = fn,
- { exports: { ${
- ArrayJoin(ArrayMap(names, (name) => `${name}: {
- get: () => $${name},
- set: v => $${name} = v
- }`), ',\n')
-} } }
- ]);
- `;
+ // Create two modules: One whose exports are get- and set-able ('reflective'),
+ // and one which re-exports all of these but additionally may
+ // run an executor function once everything is set up.
+ const src = `
+ export let executor;
+ ${ArrayJoin(ArrayMap(names, (name) => `export let $${name};`), '\n')}
+ /* This function is implicitly returned as the module's completion value */
+ (() => ({
+ setExecutor: fn => executor = fn,
+ reflect: {
+ exports: { ${
+ ArrayJoin(ArrayMap(names, (name) => `
+ ${name}: {
+ get: () => $${name},
+ set: v => $${name} = v
+ }`), ', \n')}
+ }
+ }
+ }));`;
const reflectiveModule = new ModuleWrap(src, `cjs-facade:${url}`);
reflectiveModule.instantiate();
- const [setExecutor, reflect] = reflectiveModule.evaluate()();
+ const { setExecutor, reflect } = reflectiveModule.evaluate()();
// public exposed ESM
- const reexports = `import { executor,
+ const reexports = `
+ import {
+ executor,
${ArrayMap(names, (name) => `$${name}`)}
} from "";
export {
${ArrayJoin(ArrayMap(names, (name) => `$${name} as ${name}`), ', ')}
}
- // add await to this later if top level await comes along
- typeof executor === "function" ? executor() : void 0;`;
+ if (typeof executor === "function") {
+ // add await to this later if top level await comes along
+ executor()
+ }`;
if (typeof evaluate === 'function') {
setExecutor(() => evaluate(reflect));
}
- const runner = new ModuleWrap(reexports, `${url}`);
- runner.link(async () => reflectiveModule);
- runner.instantiate();
+ const module = new ModuleWrap(reexports, `${url}`);
+ module.link(async () => reflectiveModule);
+ module.instantiate();
return {
- module: runner,
+ module,
reflect
};
};
diff --git a/lib/internal/readline.js b/lib/internal/readline.js
index b15ed4972ef7f2..e3d3007a75c645 100644
--- a/lib/internal/readline.js
+++ b/lib/internal/readline.js
@@ -9,8 +9,8 @@ const ansi =
const kEscape = '\x1b';
-var getStringWidth;
-var isFullWidthCodePoint;
+let getStringWidth;
+let isFullWidthCodePoint;
function CSI(strings, ...args) {
let ret = `${kEscape}[`;
diff --git a/lib/internal/streams/BufferList.js b/lib/internal/streams/BufferList.js
index 23d5a8a2db0eb7..b2daf82e74190b 100644
--- a/lib/internal/streams/BufferList.js
+++ b/lib/internal/streams/BufferList.js
@@ -61,8 +61,6 @@ module.exports = class BufferList {
concat(n) {
if (this.length === 0)
return Buffer.alloc(0);
- if (this.length === 1)
- return this.head.data;
const ret = Buffer.allocUnsafe(n >>> 0);
var p = this.head;
var i = 0;
diff --git a/lib/internal/test/unicode.js b/lib/internal/test/unicode.js
index 1445276d9ae891..7172a43ec20a8a 100644
--- a/lib/internal/test/unicode.js
+++ b/lib/internal/test/unicode.js
@@ -3,4 +3,6 @@
// This module exists entirely for regression testing purposes.
// See `test/parallel/test-internal-unicode.js`.
+/* eslint-disable non-ascii-character */
module.exports = '✓';
+/* eslint-enable non-ascii-character */
diff --git a/lib/net.js b/lib/net.js
index 1781350f540dc8..53c91a640f5c1d 100644
--- a/lib/net.js
+++ b/lib/net.js
@@ -370,16 +370,6 @@ function writeAfterFIN(chunk, encoding, cb) {
}
}
-Socket.prototype.read = function(n) {
- if (n === 0)
- return stream.Readable.prototype.read.call(this, n);
-
- this.read = stream.Readable.prototype.read;
- this._consuming = true;
- return this.read(n);
-};
-
-
// FIXME(joyeecheung): this method is neither documented nor tested
Socket.prototype.listen = function() {
debug('socket.listen');
@@ -767,13 +757,7 @@ Socket.prototype._writeGeneric = function(writev, data, encoding, cb) {
// Retain chunks
if (err === 0) req._chunks = chunks;
} else {
- var enc;
- if (data instanceof Buffer) {
- enc = 'buffer';
- } else {
- enc = encoding;
- }
- err = createWriteReq(req, this._handle, data, enc);
+ err = createWriteReq(req, this._handle, data, encoding);
}
if (err)
@@ -1141,7 +1125,9 @@ Socket.prototype.ref = function() {
return this;
}
- this._handle.ref();
+ if (typeof this._handle.ref === 'function') {
+ this._handle.ref();
+ }
return this;
};
@@ -1153,7 +1139,9 @@ Socket.prototype.unref = function() {
return this;
}
- this._handle.unref();
+ if (typeof this._handle.unref === 'function') {
+ this._handle.unref();
+ }
return this;
};
diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js
index 4e7a0de7eb37be..15256a63c0b97c 100644
--- a/lib/perf_hooks.js
+++ b/lib/perf_hooks.js
@@ -18,6 +18,7 @@ const {
NODE_PERFORMANCE_ENTRY_TYPE_MEASURE,
NODE_PERFORMANCE_ENTRY_TYPE_GC,
NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION,
+ NODE_PERFORMANCE_ENTRY_TYPE_HTTP2,
NODE_PERFORMANCE_MILESTONE_NODE_START,
NODE_PERFORMANCE_MILESTONE_V8_START,
@@ -61,9 +62,74 @@ const observerableTypes = [
'mark',
'measure',
'gc',
- 'function'
+ 'function',
+ 'http2'
];
+const IDX_STREAM_STATS_ID = 0;
+const IDX_STREAM_STATS_TIMETOFIRSTBYTE = 1;
+const IDX_STREAM_STATS_TIMETOFIRSTHEADER = 2;
+const IDX_STREAM_STATS_TIMETOFIRSTBYTESENT = 3;
+const IDX_STREAM_STATS_SENTBYTES = 4;
+const IDX_STREAM_STATS_RECEIVEDBYTES = 5;
+
+const IDX_SESSION_STATS_TYPE = 0;
+const IDX_SESSION_STATS_PINGRTT = 1;
+const IDX_SESSION_STATS_FRAMESRECEIVED = 2;
+const IDX_SESSION_STATS_FRAMESSENT = 3;
+const IDX_SESSION_STATS_STREAMCOUNT = 4;
+const IDX_SESSION_STATS_STREAMAVERAGEDURATION = 5;
+const IDX_SESSION_STATS_DATA_SENT = 6;
+const IDX_SESSION_STATS_DATA_RECEIVED = 7;
+const IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS = 8;
+
+let sessionStats;
+let streamStats;
+
+function collectHttp2Stats(entry) {
+ switch (entry.name) {
+ case 'Http2Stream':
+ if (streamStats === undefined)
+ streamStats = process.binding('http2').streamStats;
+ entry.id =
+ streamStats[IDX_STREAM_STATS_ID] >>> 0;
+ entry.timeToFirstByte =
+ streamStats[IDX_STREAM_STATS_TIMETOFIRSTBYTE];
+ entry.timeToFirstHeader =
+ streamStats[IDX_STREAM_STATS_TIMETOFIRSTHEADER];
+ entry.timeToFirstByteSent =
+ streamStats[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT];
+ entry.bytesWritten =
+ streamStats[IDX_STREAM_STATS_SENTBYTES];
+ entry.bytesRead =
+ streamStats[IDX_STREAM_STATS_RECEIVEDBYTES];
+ break;
+ case 'Http2Session':
+ if (sessionStats === undefined)
+ sessionStats = process.binding('http2').sessionStats;
+ entry.type =
+ sessionStats[IDX_SESSION_STATS_TYPE] >>> 0 === 0 ? 'server' : 'client';
+ entry.pingRTT =
+ sessionStats[IDX_SESSION_STATS_PINGRTT];
+ entry.framesReceived =
+ sessionStats[IDX_SESSION_STATS_FRAMESRECEIVED];
+ entry.framesSent =
+ sessionStats[IDX_SESSION_STATS_FRAMESSENT];
+ entry.streamCount =
+ sessionStats[IDX_SESSION_STATS_STREAMCOUNT];
+ entry.streamAverageDuration =
+ sessionStats[IDX_SESSION_STATS_STREAMAVERAGEDURATION];
+ entry.bytesWritten =
+ sessionStats[IDX_SESSION_STATS_DATA_SENT];
+ entry.bytesRead =
+ sessionStats[IDX_SESSION_STATS_DATA_RECEIVED];
+ entry.maxConcurrentStreams =
+ sessionStats[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS];
+ break;
+ }
+}
+
+
let errors;
function lazyErrors() {
if (errors === undefined)
@@ -405,6 +471,10 @@ class Performance extends PerformanceObserverEntryList {
this[kClearEntry]('function', name);
}
+ clearEntries(name) {
+ this[kClearEntry](name);
+ }
+
timerify(fn) {
if (typeof fn !== 'function') {
const errors = lazyErrors();
@@ -465,6 +535,10 @@ function doNotify() {
// Set up the callback used to receive PerformanceObserver notifications
function observersCallback(entry) {
const type = mapTypes(entry.entryType);
+
+ if (type === NODE_PERFORMANCE_ENTRY_TYPE_HTTP2)
+ collectHttp2Stats(entry);
+
performance[kInsertEntry](entry);
const list = getObserversList(type);
@@ -504,6 +578,7 @@ function mapTypes(i) {
case 'measure': return NODE_PERFORMANCE_ENTRY_TYPE_MEASURE;
case 'gc': return NODE_PERFORMANCE_ENTRY_TYPE_GC;
case 'function': return NODE_PERFORMANCE_ENTRY_TYPE_FUNCTION;
+ case 'http2': return NODE_PERFORMANCE_ENTRY_TYPE_HTTP2;
}
}
diff --git a/lib/readline.js b/lib/readline.js
index fa9cc188e1483c..d749e2c8f23f0f 100644
--- a/lib/readline.js
+++ b/lib/readline.js
@@ -47,8 +47,6 @@ const {
kClearScreenDown
} = CSI;
-const { now } = process.binding('timer_wrap').Timer;
-
const kHistorySize = 30;
const kMincrlfDelay = 100;
// \r\n, \n, or \r followed by something other than \n
@@ -400,7 +398,7 @@ Interface.prototype._normalWrite = function(b) {
}
var string = this._decoder.write(b);
if (this._sawReturnAt &&
- now() - this._sawReturnAt <= this.crlfDelay) {
+ Date.now() - this._sawReturnAt <= this.crlfDelay) {
string = string.replace(/^\n/, '');
this._sawReturnAt = 0;
}
@@ -413,7 +411,7 @@ Interface.prototype._normalWrite = function(b) {
this._line_buffer = null;
}
if (newPartContainsEnding) {
- this._sawReturnAt = string.endsWith('\r') ? now() : 0;
+ this._sawReturnAt = string.endsWith('\r') ? Date.now() : 0;
// got one or more newlines; process into "line" events
var lines = string.split(lineEnding);
@@ -751,7 +749,8 @@ Interface.prototype._ttyWrite = function(s, key) {
key = key || {};
this._previousKey = key;
- // Ignore escape key - Fixes #2876
+ // Ignore escape key, fixes
+ // https://github.com/nodejs/node-v0.x-archive/issues/2876.
if (key.name === 'escape') return;
if (key.ctrl && key.shift) {
@@ -907,14 +906,14 @@ Interface.prototype._ttyWrite = function(s, key) {
switch (key.name) {
case 'return': // carriage return, i.e. \r
- this._sawReturnAt = now();
+ this._sawReturnAt = Date.now();
this._line();
break;
case 'enter':
// When key interval > crlfDelay
if (this._sawReturnAt === 0 ||
- now() - this._sawReturnAt > this.crlfDelay) {
+ Date.now() - this._sawReturnAt > this.crlfDelay) {
this._line();
}
this._sawReturnAt = 0;
diff --git a/lib/stream.js b/lib/stream.js
index edc5f231b83411..9a816600a05e5a 100644
--- a/lib/stream.js
+++ b/lib/stream.js
@@ -45,7 +45,7 @@ try {
try {
Stream._isUint8Array = process.binding('util').isUint8Array;
} catch (e) {
- // This throws for Node < 4.2.0 because there’s no util binding and
+ // This throws for Node < 4.2.0 because there's no util binding and
// returns undefined for Node < 7.4.0.
}
}
diff --git a/lib/string_decoder.js b/lib/string_decoder.js
index 5e60bf1ad1c3d7..d22a85b050d8ab 100644
--- a/lib/string_decoder.js
+++ b/lib/string_decoder.js
@@ -209,8 +209,11 @@ function utf8Text(buf, i) {
// character.
function utf8End(buf) {
const r = (buf && buf.length ? this.write(buf) : '');
- if (this.lastNeed)
+ if (this.lastNeed) {
+ this.lastNeed = 0;
+ this.lastTotal = 0;
return r + '\ufffd';
+ }
return r;
}
@@ -245,6 +248,8 @@ function utf16End(buf) {
const r = (buf && buf.length ? this.write(buf) : '');
if (this.lastNeed) {
const end = this.lastTotal - this.lastNeed;
+ this.lastNeed = 0;
+ this.lastTotal = 0;
return r + this.lastChar.toString('utf16le', 0, end);
}
return r;
@@ -268,8 +273,12 @@ function base64Text(buf, i) {
function base64End(buf) {
const r = (buf && buf.length ? this.write(buf) : '');
- if (this.lastNeed)
- return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed);
+ if (this.lastNeed) {
+ const end = 3 - this.lastNeed;
+ this.lastNeed = 0;
+ this.lastTotal = 0;
+ return r + this.lastChar.toString('base64', 0, end);
+ }
return r;
}
diff --git a/lib/timers.js b/lib/timers.js
index 0e6ae45950c5c1..e6655c2f527349 100644
--- a/lib/timers.js
+++ b/lib/timers.js
@@ -89,6 +89,7 @@ const TIMEOUT_MAX = 2147483647; // 2^31-1
// TimerWrap C++ handle, which makes the call after the duration to process the
// list it is attached to.
//
+/* eslint-disable non-ascii-character */
//
// ╔════ > Object Map
// ║
@@ -110,6 +111,7 @@ const TIMEOUT_MAX = 2147483647; // 2^31-1
// ║
// ╚════ > Linked List
//
+/* eslint-enable non-ascii-character */
//
// With this, virtually constant-time insertion (append), removal, and timeout
// is possible in the JavaScript layer. Any one list of timers is able to be
@@ -215,6 +217,17 @@ function TimersList(msecs, unrefed) {
this.nextTick = false;
}
+function deleteTimersList(list, msecs) {
+ // Either refedLists[msecs] or unrefedLists[msecs] may have been removed and
+ // recreated since the reference to `list` was created. Make sure they're
+ // the same instance of the list before destroying.
+ if (list._unrefed === true && list === unrefedLists[msecs]) {
+ delete unrefedLists[msecs];
+ } else if (list === refedLists[msecs]) {
+ delete refedLists[msecs];
+ }
+}
+
function listOnTimeout() {
var list = this._list;
var msecs = list.msecs;
@@ -286,14 +299,7 @@ function listOnTimeout() {
debug('%d list empty', msecs);
assert(L.isEmpty(list));
- // Either refedLists[msecs] or unrefedLists[msecs] may have been removed and
- // recreated since the reference to `list` was created. Make sure they're
- // the same instance of the list before destroying.
- if (list._unrefed === true && list === unrefedLists[msecs]) {
- delete unrefedLists[msecs];
- } else if (list === refedLists[msecs]) {
- delete refedLists[msecs];
- }
+ deleteTimersList(list, msecs);
// Do not close the underlying handle if its ownership has changed
// (e.g it was unrefed in its callback).
@@ -327,24 +333,34 @@ function tryOnTimeout(timer, list) {
}
}
- if (!threw) return;
+ if (threw) {
+ const { msecs } = list;
+
+ if (L.isEmpty(list)) {
+ deleteTimersList(list, msecs);
- // Postpone all later list events to next tick. We need to do this
- // so that the events are called in the order they were created.
- const lists = list._unrefed === true ? unrefedLists : refedLists;
- for (var key in lists) {
- if (key > list.msecs) {
- lists[key].nextTick = true;
+ if (!list._timer.owner)
+ list._timer.close();
+ } else {
+ // Postpone all later list events to next tick. We need to do this
+ // so that the events are called in the order they were created.
+ const lists = list._unrefed === true ? unrefedLists : refedLists;
+ for (var key in lists) {
+ if (key > msecs) {
+ lists[key].nextTick = true;
+ }
+ }
+
+ // We need to continue processing after domain error handling
+ // is complete, but not by using whatever domain was left over
+ // when the timeout threw its exception.
+ const domain = process.domain;
+ process.domain = null;
+ // If we threw, we need to process the rest of the list in nextTick.
+ process.nextTick(listOnTimeoutNT, list);
+ process.domain = domain;
}
}
- // We need to continue processing after domain error handling
- // is complete, but not by using whatever domain was left over
- // when the timeout threw its exception.
- const domain = process.domain;
- process.domain = null;
- // If we threw, we need to process the rest of the list in nextTick.
- process.nextTick(listOnTimeoutNT, list);
- process.domain = domain;
}
}
diff --git a/lib/url.js b/lib/url.js
index 2cc4488a3edfd9..6562be5d8302e6 100644
--- a/lib/url.js
+++ b/lib/url.js
@@ -880,7 +880,7 @@ Url.prototype.resolveObject = function resolveObject(relative) {
// if the path is allowed to go above the root, restore leading ..s
if (!mustEndAbs && !removeAllDots) {
- for (; up--; up) {
+ while (up--) {
srcPath.unshift('..');
}
}
diff --git a/lib/util.js b/lib/util.js
index 0b1d25ba22a520..4c6db1a479e7b3 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -77,9 +77,7 @@ var Debug;
/* eslint-disable */
const strEscapeSequencesRegExp = /[\x00-\x1f\x27\x5c]/;
-const keyEscapeSequencesRegExp = /[\x00-\x1f\x27]/;
const strEscapeSequencesReplacer = /[\x00-\x1f\x27\x5c]/g;
-const keyEscapeSequencesReplacer = /[\x00-\x1f\x27]/g;
/* eslint-enable */
const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/;
const colorRegExp = /\u001b\[\d\d?m/g;
@@ -133,34 +131,6 @@ function strEscape(str) {
return `'${result}'`;
}
-// Escape control characters and single quotes.
-// Note: for performance reasons this is not combined with strEscape
-function keyEscape(str) {
- if (str.length < 5000 && !keyEscapeSequencesRegExp.test(str))
- return `'${str}'`;
- if (str.length > 100)
- return `'${str.replace(keyEscapeSequencesReplacer, escapeFn)}'`;
- var result = '';
- var last = 0;
- for (var i = 0; i < str.length; i++) {
- const point = str.charCodeAt(i);
- if (point === 39 || point < 32) {
- if (last === i) {
- result += meta[point];
- } else {
- result += `${str.slice(last, i)}${meta[point]}`;
- }
- last = i + 1;
- }
- }
- if (last === 0) {
- result = str;
- } else if (last !== i) {
- result += str.slice(last);
- }
- return `'${result}'`;
-}
-
function tryStringify(arg) {
try {
return JSON.stringify(arg);
@@ -851,7 +821,7 @@ function formatProperty(ctx, value, recurseTimes, key, array) {
} else if (keyStrRegExp.test(key)) {
name = ctx.stylize(key, 'name');
} else {
- name = ctx.stylize(keyEscape(key), 'string');
+ name = ctx.stylize(strEscape(key), 'string');
}
return `${name}: ${str}`;
diff --git a/lib/zlib.js b/lib/zlib.js
index 7f41200f86be19..bbe89043248459 100644
--- a/lib/zlib.js
+++ b/lib/zlib.js
@@ -339,7 +339,7 @@ Zlib.prototype.flush = function flush(kind, callback) {
this._scheduledFlushFlag = maxFlush(kind, this._scheduledFlushFlag);
// If a callback was passed, always register a new `drain` + flush handler,
- // mostly because that’s simpler and flush callbacks piling up is a rare
+ // mostly because that's simpler and flush callbacks piling up is a rare
// thing anyway.
if (!alreadyHadFlushScheduled || callback) {
const drainHandler = () => this.flush(this._scheduledFlushFlag, callback);
diff --git a/node.gyp b/node.gyp
index 38dcdac089ffff..ee4157567f5a31 100644
--- a/node.gyp
+++ b/node.gyp
@@ -22,6 +22,8 @@
'node_v8_options%': '',
'node_enable_v8_vtunejit%': 'false',
'node_core_target_name%': 'node',
+ 'node_lib_target_name%': 'node_lib',
+ 'node_intermediate_lib_type%': 'static_library',
'library_files': [
'lib/internal/bootstrap_node.js',
'lib/async_hooks.js',
@@ -145,6 +147,17 @@
'conditions': [
[ 'node_shared=="true"', {
'node_target_type%': 'shared_library',
+ 'conditions': [
+ ['OS=="aix"', {
+ # For AIX, always generate static library first,
+ # It needs an extra step to generate exp and
+ # then use both static lib and exp to create
+ # shared lib.
+ 'node_intermediate_lib_type': 'static_library',
+ }, {
+ 'node_intermediate_lib_type': 'shared_library',
+ }],
+ ],
}, {
'node_target_type%': 'executable',
}],
@@ -161,7 +174,81 @@
'targets': [
{
'target_name': '<(node_core_target_name)',
- 'type': '<(node_target_type)',
+ 'type': 'executable',
+ 'sources': [
+ 'src/node_main.cc'
+ ],
+ 'include_dirs': [
+ 'src',
+ 'deps/v8/include',
+ ],
+ 'conditions': [
+ [ 'node_intermediate_lib_type=="static_library" and '
+ 'node_shared=="true" and OS=="aix"', {
+ # For AIX, shared lib is linked by static lib and .exp. In the
+ # case here, the executable needs to link to shared lib.
+ # Therefore, use 'node_aix_shared' target to generate the
+ # shared lib and then executable.
+ 'dependencies': [ 'node_aix_shared' ],
+ }, {
+ 'dependencies': [ '<(node_lib_target_name)' ],
+ }],
+ [ 'node_intermediate_lib_type=="static_library" and '
+ 'node_shared=="false"', {
+ 'includes': [
+ 'node.gypi'
+ ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-Wl,-force_load,<(PRODUCT_DIR)/<(STATIC_LIB_PREFIX)'
+ '<(node_core_target_name)<(STATIC_LIB_SUFFIX)',
+ ],
+ },
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'AdditionalOptions': [
+ '/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\'
+ '<(node_core_target_name)<(STATIC_LIB_SUFFIX)',
+ ],
+ },
+ },
+ 'conditions': [
+ ['OS in "linux freebsd openbsd solaris android"', {
+ 'ldflags': [
+ '-Wl,--whole-archive,<(OBJ_DIR)/<(STATIC_LIB_PREFIX)'
+ '<(node_core_target_name)<(STATIC_LIB_SUFFIX)',
+ '-Wl,--no-whole-archive',
+ ],
+ }],
+ [ 'OS=="win"', {
+ 'sources': [ 'src/res/node.rc' ],
+ 'conditions': [
+ [ 'node_use_etw=="true"', {
+ 'sources': [
+ 'tools/msvs/genfiles/node_etw_provider.rc'
+ ],
+ }],
+ [ 'node_use_perfctr=="true"', {
+ 'sources': [
+ 'tools/msvs/genfiles/node_perfctr_provider.rc',
+ ],
+ }]
+ ],
+ }],
+ ],
+ }],
+ [ 'node_intermediate_lib_type=="shared_library" and OS=="win"', {
+ # On Windows, having the same name for both executable and shared
+ # lib causes filename collision. Need a different PRODUCT_NAME for
+ # the executable and rename it back to node.exe later
+ 'product_name': '<(node_core_target_name)-win',
+ }],
+ ],
+ },
+ {
+ 'target_name': '<(node_lib_target_name)',
+ 'type': '<(node_intermediate_lib_type)',
+ 'product_name': '<(node_core_target_name)',
'dependencies': [
'node_js2c#host',
@@ -173,7 +260,6 @@
'include_dirs': [
'src',
- 'tools/msvs/genfiles',
'<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h
],
@@ -199,7 +285,6 @@
'src/node_file.cc',
'src/node_http2.cc',
'src/node_http_parser.cc',
- 'src/node_main.cc',
'src/node_os.cc',
'src/node_platform.cc',
'src/node_perf.cc',
@@ -279,7 +364,6 @@
'src/util-inl.h',
'deps/http_parser/http_parser.h',
'deps/v8/include/v8.h',
- 'deps/v8/include/v8-debug.h',
# javascript files to make for an even more pleasant IDE experience
'<@(library_files)',
# node.gyp is added to the project by default.
@@ -303,6 +387,9 @@
[ 'node_shared=="true" and node_module_version!="" and OS!="win"', {
'product_extension': '<(shlib_suffix)',
}],
+ ['node_shared=="true" and OS=="aix"', {
+ 'product_name': 'node_base',
+ }],
[ 'v8_enable_inspector==1', {
'defines': [
'HAVE_INSPECTOR=1',
@@ -333,7 +420,7 @@
'src/backtrace_win32.cc',
],
'conditions': [
- [ 'node_target_type!="static_library"', {
+ [ 'node_intermediate_lib_type!="static_library"', {
'sources': [
'src/res/node.rc',
],
@@ -353,6 +440,64 @@
'defines': [ '__POSIX__' ],
'sources': [ 'src/backtrace_posix.cc' ],
}],
+ [ 'node_use_etw=="true"', {
+ 'defines': [ 'HAVE_ETW=1' ],
+ 'dependencies': [ 'node_etw' ],
+ 'include_dirs': [
+ 'src',
+ 'tools/msvs/genfiles',
+ '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h
+ ],
+ 'sources': [
+ 'src/node_win32_etw_provider.h',
+ 'src/node_win32_etw_provider-inl.h',
+ 'src/node_win32_etw_provider.cc',
+ 'src/node_dtrace.cc',
+ 'tools/msvs/genfiles/node_etw_provider.h',
+ ],
+ 'conditions': [
+ ['node_intermediate_lib_type != "static_library"', {
+ 'sources': [
+ 'tools/msvs/genfiles/node_etw_provider.rc',
+ ],
+ }],
+ ],
+ }],
+ [ 'node_use_perfctr=="true"', {
+ 'defines': [ 'HAVE_PERFCTR=1' ],
+ 'dependencies': [ 'node_perfctr' ],
+ 'include_dirs': [
+ 'src',
+ 'tools/msvs/genfiles',
+ '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h
+ ],
+ 'sources': [
+ 'src/node_win32_perfctr_provider.h',
+ 'src/node_win32_perfctr_provider.cc',
+ 'src/node_counters.cc',
+ 'src/node_counters.h',
+ ],
+ 'conditions': [
+ ['node_intermediate_lib_type != "static_library"', {
+ 'sources': [
+ 'tools/msvs/genfiles/node_perfctr_provider.rc',
+ ],
+ }],
+ ],
+ }],
+ [ 'node_use_lttng=="true"', {
+ 'defines': [ 'HAVE_LTTNG=1' ],
+ 'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)' ],
+ 'libraries': [ '-llttng-ust' ],
+ 'include_dirs': [
+ 'src',
+ 'tools/msvs/genfiles',
+ '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h
+ ],
+ 'sources': [
+ 'src/node_lttng.cc'
+ ],
+ }],
[ 'node_use_dtrace=="true"', {
'defines': [ 'HAVE_DTRACE=1' ],
'dependencies': [
@@ -393,7 +538,6 @@
] ]
} ],
[ 'node_use_openssl=="true"', {
- 'defines': [ 'HAVE_OPENSSL=1' ],
'sources': [
'src/node_crypto.cc',
'src/node_crypto_bio.cc',
@@ -404,49 +548,6 @@
'src/tls_wrap.cc',
'src/tls_wrap.h'
],
- 'conditions': [
- ['openssl_fips != ""', {
- 'defines': [ 'NODE_FIPS_MODE' ],
- }],
- [ 'node_shared_openssl=="false"', {
- 'dependencies': [
- './deps/openssl/openssl.gyp:openssl',
-
- # For tests
- './deps/openssl/openssl.gyp:openssl-cli',
- ],
- 'conditions': [
- # -force_load or --whole-archive are not applicable for
- # the static library
- [ 'node_target_type!="static_library"', {
- 'xcode_settings': {
- 'OTHER_LDFLAGS': [
- '-Wl,-force_load,<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)',
- ],
- },
- 'conditions': [
- ['OS in "linux freebsd" and node_shared=="false"', {
- 'ldflags': [
- '-Wl,--whole-archive,'
- '<(OBJ_DIR)/deps/openssl/'
- '<(OPENSSL_PRODUCT)',
- '-Wl,--no-whole-archive',
- ],
- }],
- # openssl.def is based on zlib.def, zlib symbols
- # are always exported.
- ['use_openssl_def==1', {
- 'sources': ['<(SHARED_INTERMEDIATE_DIR)/openssl.def'],
- }],
- ['OS=="win" and use_openssl_def==0', {
- 'sources': ['deps/zlib/win32/zlib.def'],
- }],
- ],
- }],
- ],
- }]]
- }, {
- 'defines': [ 'HAVE_OPENSSL=0' ]
}],
],
'direct_dependent_settings': {
@@ -509,7 +610,7 @@
'target_name': 'node_etw',
'type': 'none',
'conditions': [
- [ 'node_use_etw=="true" and node_target_type!="static_library"', {
+ [ 'node_use_etw=="true"', {
'actions': [
{
'action_name': 'node_etw',
@@ -530,7 +631,7 @@
'target_name': 'node_perfctr',
'type': 'none',
'conditions': [
- [ 'node_use_perfctr=="true" and node_target_type!="static_library"', {
+ [ 'node_use_perfctr=="true"', {
'actions': [
{
'action_name': 'node_perfctr_man',
@@ -592,15 +693,13 @@
'<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc',
],
'conditions': [
- [ 'node_use_dtrace=="false" and node_use_etw=="false" or '
- 'node_target_type=="static_library"', {
+ [ 'node_use_dtrace=="false" and node_use_etw=="false"', {
'inputs': [ 'src/notrace_macros.py' ]
}],
- ['node_use_lttng=="false" or node_target_type=="static_library"', {
+ [ 'node_use_lttng=="false"', {
'inputs': [ 'src/nolttng_macros.py' ]
}],
- [ 'node_use_perfctr=="false" or '
- 'node_target_type=="static_library"', {
+ [ 'node_use_perfctr=="false"', {
'inputs': [ 'src/noperfctr_macros.py' ]
}]
],
@@ -650,10 +749,10 @@
{
'action_name': 'node_dtrace_provider_o',
'inputs': [
- '<(OBJ_DIR)/node/src/node_dtrace.o',
+ '<(OBJ_DIR)/<(node_lib_target_name)/src/node_dtrace.o',
],
'outputs': [
- '<(OBJ_DIR)/node/src/node_dtrace_provider.o'
+ '<(OBJ_DIR)/<(node_lib_target_name)/src/node_dtrace_provider.o'
],
'action': [ 'dtrace', '-G', '-xnolibs', '-s', 'src/node_provider.d',
'<@(_inputs)', '-o', '<@(_outputs)' ]
@@ -703,7 +802,7 @@
'<(SHARED_INTERMEDIATE_DIR)/v8constants.h'
],
'outputs': [
- '<(OBJ_DIR)/node/src/node_dtrace_ustack.o'
+ '<(OBJ_DIR)/<(node_lib_target_name)/src/node_dtrace_ustack.o'
],
'conditions': [
[ 'target_arch=="ia32" or target_arch=="arm"', {
@@ -750,12 +849,41 @@
} ],
]
},
+ {
+ # When using shared lib to build executable in Windows, in order to avoid
+ # filename collision, the executable name is node-win.exe. Need to rename
+ # it back to node.exe
+ 'target_name': 'rename_node_bin_win',
+ 'type': 'none',
+ 'dependencies': [
+ '<(node_core_target_name)',
+ ],
+ 'conditions': [
+ [ 'OS=="win" and node_intermediate_lib_type=="shared_library"', {
+ 'actions': [
+ {
+ 'action_name': 'rename_node_bin_win',
+ 'inputs': [
+ '<(PRODUCT_DIR)/<(node_core_target_name)-win.exe'
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/<(node_core_target_name).exe',
+ ],
+ 'action': [
+ 'mv', '<@(_inputs)', '<@(_outputs)',
+ ],
+ },
+ ],
+ } ],
+ ]
+ },
{
'target_name': 'cctest',
'type': 'executable',
'dependencies': [
'<(node_core_target_name)',
+ 'rename_node_bin_win',
'deps/gtest/gtest.gyp:gtest',
'node_js2c#host',
'node_dtrace_header',
@@ -764,9 +892,9 @@
],
'variables': {
- 'OBJ_PATH': '<(OBJ_DIR)/node/src',
- 'OBJ_GEN_PATH': '<(OBJ_DIR)/node/gen',
- 'OBJ_TRACING_PATH': '<(OBJ_DIR)/node/src/tracing',
+ 'OBJ_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/src',
+ 'OBJ_GEN_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/gen',
+ 'OBJ_TRACING_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/src/tracing',
'OBJ_SUFFIX': 'o',
'OBJ_SEPARATOR': '/',
'conditions': [
@@ -777,18 +905,19 @@
'OBJ_PATH': '<(OBJ_DIR)/src',
'OBJ_GEN_PATH': '<(OBJ_DIR)/gen',
'OBJ_TRACING_PATH': '<(OBJ_DIR)/src/tracing',
- 'OBJ_SEPARATOR': '/node.',
+ 'OBJ_SEPARATOR': '/<(node_lib_target_name).',
}, {
'conditions': [
['OS=="win"', {
- 'OBJ_PATH': '<(OBJ_DIR)/node',
- 'OBJ_GEN_PATH': '<(OBJ_DIR)/node',
- 'OBJ_TRACING_PATH': '<(OBJ_DIR)/node',
+ 'OBJ_PATH': '<(OBJ_DIR)/<(node_lib_target_name)',
+ 'OBJ_GEN_PATH': '<(OBJ_DIR)/<(node_lib_target_name)',
+ 'OBJ_TRACING_PATH': '<(OBJ_DIR)/<(node_lib_target_name)',
}],
['OS=="aix"', {
- 'OBJ_PATH': '<(OBJ_DIR)/node_base/src',
- 'OBJ_GEN_PATH': '<(OBJ_DIR)/node_base/gen',
- 'OBJ_TRACING_PATH': '<(OBJ_DIR)/node_base/src/tracing',
+ 'OBJ_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/src',
+ 'OBJ_GEN_PATH': '<(OBJ_DIR)/<(node_lib_target_name)/gen',
+ 'OBJ_TRACING_PATH':
+ '<(OBJ_DIR)/<(node_lib_target_name)/src/tracing',
}],
]}
]
@@ -820,34 +949,29 @@
'test/cctest/test_url.cc'
],
- 'sources!': [
- 'src/node_main.cc'
+ 'libraries': [
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)async_wrap.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_debug_options.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_perf.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_platform.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)string_search.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)stream_base.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_constants.<(OBJ_SUFFIX)',
+ '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)agent.<(OBJ_SUFFIX)',
+ '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_buffer.<(OBJ_SUFFIX)',
+ '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_writer.<(OBJ_SUFFIX)',
+ '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)trace_event.<(OBJ_SUFFIX)',
+ '<(OBJ_GEN_PATH)<(OBJ_SEPARATOR)node_javascript.<(OBJ_SUFFIX)',
],
'conditions': [
- ['node_target_type!="static_library"', {
- 'libraries': [
- '<(OBJ_PATH)<(OBJ_SEPARATOR)async_wrap.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)env.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node_buffer.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node_debug_options.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node_i18n.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node_perf.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node_platform.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node_url.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)util.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)string_bytes.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)string_search.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)stream_base.<(OBJ_SUFFIX)',
- '<(OBJ_PATH)<(OBJ_SEPARATOR)node_constants.<(OBJ_SUFFIX)',
- '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)agent.<(OBJ_SUFFIX)',
- '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_buffer.<(OBJ_SUFFIX)',
- '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)node_trace_writer.<(OBJ_SUFFIX)',
- '<(OBJ_TRACING_PATH)<(OBJ_SEPARATOR)trace_event.<(OBJ_SUFFIX)',
- '<(OBJ_GEN_PATH)<(OBJ_SEPARATOR)node_javascript.<(OBJ_SUFFIX)',
- ],
- }],
[ 'node_use_openssl=="true"', {
'conditions': [
['node_target_type!="static_library"', {
@@ -863,6 +987,14 @@
'HAVE_OPENSSL=1',
],
}],
+ [ 'node_use_perfctr=="true"', {
+ 'defines': [ 'HAVE_PERFCTR=1' ],
+ 'libraries': [
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_counters.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)'
+ 'node_win32_perfctr_provider.<(OBJ_SUFFIX)',
+ ],
+ }],
['v8_enable_inspector==1', {
'sources': [
'test/cctest/test_inspector_socket.cc',
@@ -896,10 +1028,21 @@
}],
['OS=="linux"', {
'libraries': [
- '<(SHARED_INTERMEDIATE_DIR)/node_dtrace_provider.o',
+ '<(SHARED_INTERMEDIATE_DIR)<(OBJ_SEPARATOR)'
+ 'node_dtrace_provider.<(OBJ_SUFFIX)',
]
}],
],
+ }, {
+ 'conditions': [
+ [ 'node_use_etw=="true" and OS=="win"', {
+ 'libraries': [
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)node_dtrace.<(OBJ_SUFFIX)',
+ '<(OBJ_PATH)<(OBJ_SEPARATOR)'
+ 'node_win32_etw_provider.<(OBJ_SUFFIX)',
+ ],
+ }]
+ ]
}],
[ 'OS=="win" and node_target_type!="static_library"', {
'libraries': [
@@ -914,129 +1057,27 @@
}],
],
}],
- [ 'node_shared_zlib=="false"', {
- 'dependencies': [
- 'deps/zlib/zlib.gyp:zlib',
- ]
- }],
- [ 'node_shared_openssl=="false" and node_shared=="false"', {
- 'dependencies': [
- 'deps/openssl/openssl.gyp:openssl'
- ]
- }],
- [ 'node_shared_http_parser=="false"', {
- 'dependencies': [
- 'deps/http_parser/http_parser.gyp:http_parser'
- ]
- }],
- [ 'node_shared_libuv=="false"', {
- 'dependencies': [
- 'deps/uv/uv.gyp:libuv'
- ]
- }],
- [ 'node_shared_nghttp2=="false"', {
- 'dependencies': [
- 'deps/nghttp2/nghttp2.gyp:nghttp2'
- ],
- 'include_dirs': [
- 'deps/nghttp2/lib/includes'
- ]
- }],
- [ 'node_use_v8_platform=="true"', {
- 'dependencies': [
- 'deps/v8/src/v8.gyp:v8_libplatform',
- ],
- }],
['OS=="solaris"', {
'ldflags': [ '-I<(SHARED_INTERMEDIATE_DIR)' ]
}],
- [ 'node_use_openssl=="true"', {
- 'conditions': [
- [ 'node_shared_openssl=="false"', {
- 'conditions': [
- # -force_load or --whole-archive are not applicable for
- # the static library
- [ 'node_target_type!="static_library"', {
- 'xcode_settings': {
- 'OTHER_LDFLAGS': [
- '-Wl,-force_load,<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)',
- ],
- },
- 'conditions': [
- ['OS in "linux freebsd" and node_shared=="false"', {
- 'ldflags': [
- '-Wl,--whole-archive,'
- '<(OBJ_DIR)/deps/openssl/'
- '<(OPENSSL_PRODUCT)',
- '-Wl,--no-whole-archive',
- ],
- }],
- ],
- }],
- ],
- }]]
- }],
]
}
], # end targets
'conditions': [
- [ 'node_target_type=="static_library"', {
+ [ 'OS=="aix" and node_shared=="true"', {
'targets': [
{
- 'target_name': 'static_node',
- 'type': 'executable',
+ 'target_name': 'node_aix_shared',
+ 'type': 'shared_library',
'product_name': '<(node_core_target_name)',
- 'dependencies': [
- '<(node_core_target_name)',
- ],
- 'sources+': [
- 'src/node_main.cc',
- ],
- 'include_dirs': [
- 'deps/v8/include',
- ],
- 'xcode_settings': {
- 'OTHER_LDFLAGS': [
- '-Wl,-force_load,<(PRODUCT_DIR)/<(STATIC_LIB_PREFIX)'
- '<(node_core_target_name)<(STATIC_LIB_SUFFIX)',
- ],
- },
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'AdditionalOptions': [
- '/WHOLEARCHIVE:<(PRODUCT_DIR)/lib/'
- '<(node_core_target_name)<(STATIC_LIB_SUFFIX)',
- ],
- },
- },
- 'conditions': [
- ['OS in "linux freebsd openbsd solaris android"', {
- 'ldflags': [
- '-Wl,--whole-archive,<(OBJ_DIR)/<(STATIC_LIB_PREFIX)'
- '<(node_core_target_name)<(STATIC_LIB_SUFFIX)',
- '-Wl,--no-whole-archive',
- ],
- }],
- ],
- },
- ],
- }],
- ['OS=="aix"', {
- 'targets': [
- {
- 'target_name': 'node',
+ 'ldflags': [ '--shared' ],
+ 'product_extension': '<(shlib_suffix)',
'conditions': [
- ['node_shared=="true"', {
- 'type': 'shared_library',
- 'ldflags': ['--shared'],
- 'product_extension': '<(shlib_suffix)',
- }, {
- 'type': 'executable',
- }],
['target_arch=="ppc64"', {
'ldflags': [
- '-Wl,-blibpath:/usr/lib:/lib:/opt/freeware/lib/pthread/ppc64'
+ '-Wl,-blibpath:/usr/lib:/lib:'
+ '/opt/freeware/lib/pthread/ppc64'
],
}],
['target_arch=="ppc"', {
@@ -1045,45 +1086,20 @@
],
}]
],
- 'dependencies': ['<(node_core_target_name)', 'node_exp'],
-
+ 'includes': [
+ 'node.gypi'
+ ],
+ 'dependencies': [ '<(node_lib_target_name)' ],
'include_dirs': [
'src',
'deps/v8/include',
],
-
'sources': [
- 'src/node_main.cc',
'<@(library_files)',
- # node.gyp is added to the project by default.
'common.gypi',
],
-
- 'ldflags': ['-Wl,-bE:<(PRODUCT_DIR)/node.exp'],
},
- {
- 'target_name': 'node_exp',
- 'type': 'none',
- 'dependencies': [
- '<(node_core_target_name)',
- ],
- 'actions': [
- {
- 'action_name': 'expfile',
- 'inputs': [
- '<(OBJ_DIR)'
- ],
- 'outputs': [
- '<(PRODUCT_DIR)/node.exp'
- ],
- 'action': [
- 'sh', 'tools/create_expfile.sh',
- '<@(_inputs)', '<@(_outputs)'
- ],
- }
- ]
- }
- ], # end targets
+ ]
}], # end aix section
], # end conditions block
}
diff --git a/node.gypi b/node.gypi
index 3990c59ef98851..386601906fbe4a 100644
--- a/node.gypi
+++ b/node.gypi
@@ -1,4 +1,29 @@
{
+ # 'force_load' means to include the static libs into the shared lib or
+ # executable. Therefore, it is enabled when building:
+ # 1. The executable and it uses static lib (cctest and node)
+ # 2. The shared lib
+ # Linker optimizes out functions that are not used. When force_load=true,
+ # --whole-archive,force_load and /WHOLEARCHIVE are used to include
+ # all obj files in static libs into the executable or shared lib.
+ 'variables': {
+ 'variables': {
+ 'variables': {
+ 'force_load%': 'true',
+ 'current_type%': '<(_type)',
+ },
+ 'force_load%': '<(force_load)',
+ 'conditions': [
+ ['current_type=="static_library"', {
+ 'force_load': 'false',
+ }],
+ [ 'current_type=="executable" and node_target_type=="shared_library"', {
+ 'force_load': 'false',
+ }]
+ ],
+ },
+ 'force_load%': '<(force_load)',
+ },
'conditions': [
[ 'node_shared=="false"', {
'msvs_settings': {
@@ -36,12 +61,6 @@
[ 'node_v8_options!=""', {
'defines': [ 'NODE_V8_OPTIONS="<(node_v8_options)"'],
}],
- # No node_main.cc for anything except executable
- [ 'node_target_type!="executable"', {
- 'sources!': [
- 'src/node_main.cc',
- ],
- }],
[ 'node_release_urlbase!=""', {
'defines': [
'NODE_RELEASE_URLBASE="<(node_release_urlbase)"',
@@ -70,37 +89,6 @@
'deps/v8/src/third_party/vtune/v8vtune.gyp:v8_vtune'
],
}],
- [ 'node_use_lttng=="true"', {
- 'defines': [ 'HAVE_LTTNG=1' ],
- 'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)' ],
- 'libraries': [ '-llttng-ust' ],
- 'sources': [
- 'src/node_lttng.cc'
- ],
- } ],
- [ 'node_use_etw=="true" and node_target_type!="static_library"', {
- 'defines': [ 'HAVE_ETW=1' ],
- 'dependencies': [ 'node_etw' ],
- 'sources': [
- 'src/node_win32_etw_provider.h',
- 'src/node_win32_etw_provider-inl.h',
- 'src/node_win32_etw_provider.cc',
- 'src/node_dtrace.cc',
- 'tools/msvs/genfiles/node_etw_provider.h',
- 'tools/msvs/genfiles/node_etw_provider.rc',
- ]
- } ],
- [ 'node_use_perfctr=="true" and node_target_type!="static_library"', {
- 'defines': [ 'HAVE_PERFCTR=1' ],
- 'dependencies': [ 'node_perfctr' ],
- 'sources': [
- 'src/node_win32_perfctr_provider.h',
- 'src/node_win32_perfctr_provider.cc',
- 'src/node_counters.cc',
- 'src/node_counters.h',
- 'tools/msvs/genfiles/node_perfctr_provider.rc',
- ]
- } ],
[ 'node_no_browser_globals=="true"', {
'defines': [ 'NODE_NO_BROWSER_GLOBALS' ],
} ],
@@ -108,7 +96,7 @@
'dependencies': [ 'deps/v8/src/v8.gyp:postmortem-metadata' ],
'conditions': [
# -force_load is not applicable for the static library
- [ 'node_target_type!="static_library"', {
+ [ 'force_load=="true"', {
'xcode_settings': {
'OTHER_LDFLAGS': [
'-Wl,-force_load,<(V8_BASE)',
@@ -159,6 +147,27 @@
'defines': [
'_LINUX_SOURCE_COMPAT',
],
+ 'conditions': [
+ [ 'force_load=="true"', {
+
+ 'actions': [
+ {
+ 'action_name': 'expfile',
+ 'inputs': [
+ '<(OBJ_DIR)'
+ ],
+ 'outputs': [
+ '<(PRODUCT_DIR)/node.exp'
+ ],
+ 'action': [
+ 'sh', 'tools/create_expfile.sh',
+ '<@(_inputs)', '<@(_outputs)'
+ ],
+ }
+ ],
+ 'ldflags': ['-Wl,-bE:<(PRODUCT_DIR)/node.exp', '-Wl,-brtl'],
+ }],
+ ],
}],
[ 'OS=="solaris"', {
'libraries': [
@@ -174,12 +183,14 @@
'NODE_PLATFORM="sunos"',
],
}],
- [ '(OS=="freebsd" or OS=="linux") and node_shared=="false" and coverage=="false"', {
+ [ '(OS=="freebsd" or OS=="linux") and node_shared=="false"'
+ ' and coverage=="false" and force_load=="true"', {
'ldflags': [ '-Wl,-z,noexecstack',
'-Wl,--whole-archive <(V8_BASE)',
'-Wl,--no-whole-archive' ]
}],
- [ '(OS=="freebsd" or OS=="linux") and node_shared=="false" and coverage=="true"', {
+ [ '(OS=="freebsd" or OS=="linux") and node_shared=="false"'
+ ' and coverage=="true" and force_load=="true"', {
'ldflags': [ '-Wl,-z,noexecstack',
'-Wl,--whole-archive <(V8_BASE)',
'-Wl,--no-whole-archive',
@@ -206,5 +217,54 @@
[ 'OS=="sunos"', {
'ldflags': [ '-Wl,-M,/usr/lib/ld/map.noexstk' ],
}],
+
+ [ 'node_use_openssl=="true"', {
+ 'defines': [ 'HAVE_OPENSSL=1' ],
+ 'conditions': [
+ ['openssl_fips != ""', {
+ 'defines': [ 'NODE_FIPS_MODE' ],
+ }],
+ [ 'node_shared_openssl=="false"', {
+ 'dependencies': [
+ './deps/openssl/openssl.gyp:openssl',
+
+ # For tests
+ './deps/openssl/openssl.gyp:openssl-cli',
+ ],
+ 'conditions': [
+ # -force_load or --whole-archive are not applicable for
+ # the static library
+ [ 'force_load=="true"', {
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '-Wl,-force_load,<(PRODUCT_DIR)/<(OPENSSL_PRODUCT)',
+ ],
+ },
+ 'conditions': [
+ ['OS in "linux freebsd" and node_shared=="false"', {
+ 'ldflags': [
+ '-Wl,--whole-archive,'
+ '<(OBJ_DIR)/deps/openssl/'
+ '<(OPENSSL_PRODUCT)',
+ '-Wl,--no-whole-archive',
+ ],
+ }],
+ # openssl.def is based on zlib.def, zlib symbols
+ # are always exported.
+ ['use_openssl_def==1', {
+ 'sources': ['<(SHARED_INTERMEDIATE_DIR)/openssl.def'],
+ }],
+ ['OS=="win" and use_openssl_def==0', {
+ 'sources': ['deps/zlib/win32/zlib.def'],
+ }],
+ ],
+ }],
+ ],
+ }]]
+
+ }, {
+ 'defines': [ 'HAVE_OPENSSL=0' ]
+ }],
+
],
}
diff --git a/src/async_wrap.cc b/src/async_wrap.cc
index 7e3c0c257ab22d..b1212e97704ef0 100644
--- a/src/async_wrap.cc
+++ b/src/async_wrap.cc
@@ -129,7 +129,7 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) {
CHECK_GT(object->InternalFieldCount(), 0);
AsyncWrap* wrap = Unwrap(object);
- CHECK_NE(nullptr, wrap);
+ if (wrap == nullptr) return nullptr; // ClearWrap() already called.
return new RetainedAsyncInfo(class_id, wrap);
}
@@ -554,12 +554,12 @@ void AsyncWrap::Initialize(Local