diff --git a/BUILDING.md b/BUILDING.md index 667714ba2b3de5..57f790be8ba042 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -8,21 +8,76 @@ If you consistently can reproduce a test failure, search for it in the [Node.js issue tracker](https://github.com/nodejs/node/issues) or file a new issue. +## Supported platforms + +This list of supported platforms is current as of the branch / release to +which it is attached. + +### Input + +Node.js relies on V8 and libuv. Therefore, we adopt a subset of their +supported platforms. + +### Strategy + +Support is divided into three tiers: + +* **Tier 1**: Full test coverage and maintenance by the Node.js core team and + the broader community. +* **Tier 2**: Full test coverage but more limited maintenance, + often provided by the vendor of the platform. +* **Experimental**: Known to compile but not necessarily reliably or with + a full passing test suite. These are often working to be promoted to Tier + 2 but are not quite ready. There is at least one individual actively + providing maintenance and the team is striving to broaden quality and + reliability of support. + +### Supported platforms + +| System | Support type | Version | Architectures | Notes | +|--------------|--------------|----------------------------------|----------------------|------------------| +| GNU/Linux | Tier 1 | kernel >= 2.6.18, glibc >= 2.5 | x86, x64, arm, arm64 | | +| macOS | Tier 1 | >= 10.10 | x64 | | +| Windows | Tier 1 | >= Windows 7 or >= Windows2008R2 | x86, x64 | | +| SmartOS | Tier 2 | >= 14 | x86, x64 | | +| FreeBSD | Tier 2 | >= 10 | x64 | | +| GNU/Linux | Tier 2 | kernel >= 4.2.0, glibc >= 2.19 | ppc64be | | +| GNU/Linux | Tier 2 | kernel >= 3.13.0, glibc >= 2.19 | ppc64le | | +| macOS | Experimental | >= 10.8 < 10.10 | x64 | no test coverage | +| Linux (musl) | Experimental | musl >= 1.0 | x64 | | + + +### Supported toolchains + +Depending on host platform, the selection of toolchains may vary. + +#### Unix + +* GCC 4.8 or newer +* Clang 3.4.1 or newer + +#### Windows + +* Building Node: Visual Studio 2013 or Visual C++ Build Tools 2013 or newer +* Building native add-ons: Visual Studio 2013 or Visual C++ Build Tools 2013 + or newer + +## Building Node.js on supported platforms ### Unix / OS X Prerequisites: * `gcc` and `g++` 4.8 or newer, or -* `clang` and `clang++` 3.4 or newer +* `clang` and `clang++` 3.4.1 or newer * Python 2.6 or 2.7 * GNU Make 3.81 or newer On OS X, you will also need: * [Xcode](https://developer.apple.com/xcode/download/) - * You also need to install the `Command Line Tools` via Xcode. You can find + - You also need to install the `Command Line Tools` via Xcode. You can find this under the menu `Xcode -> Preferences -> Downloads` - * This step will install `gcc` and the related toolchain containing `make` + - This step will install `gcc` and the related toolchain containing `make` * After building, you may want to setup [firewall rules](tools/macosx-firewall.sh) to avoid popups asking to accept incoming network connections when running tests: @@ -53,6 +108,9 @@ $ make $ [sudo] make install ``` +Note that the above requires that `python` resolve to Python 2.6 or 2.7 +and not a newer version. + To run the tests: ```text @@ -263,9 +321,11 @@ It is possible to build Node.js with **Note**: building in this way does **not** allow you to claim that the runtime is FIPS 140-2 validated. Instead you can indicate that the runtime -uses a validated module. See the [security policy](http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp1747.pdf) +uses a validated module. See the +[security policy](http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp1747.pdf) page 60 for more details. In addition, the validation for the underlying module -is only valid if it is deployed in accordance with its [security policy](http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp1747.pdf). +is only valid if it is deployed in accordance with its +[security policy](http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp1747.pdf). If you need FIPS validated cryptography it is recommended that you read both the [security policy](http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp1747.pdf) and [user guide](https://openssl.org/docs/fips/UserGuide-2.0.pdf). diff --git a/CHANGELOG.md b/CHANGELOG.md index 5544099f78f6e5..c54b2eee2b1605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # Node.js ChangeLog +## 2017-05-02, Version 4.8.3 'Argon' (Maintenance), @MylesBorins + +### Notable Changes + +* **module**: + - The [module loading global fallback](https://nodejs.org/dist/latest-v4.x/docs/api/modules.html#modules_loading_from_the_global_folders) to the Node executable's directory now works correctly on Windows. (Richard Lau) [#9283](https://github.com/nodejs/node/pull/9283) +* **src**: + - fix base64 decoding in rare edgecase (Nikolai Vavilov) [#11995](https://github.com/nodejs/node/pull/11995) +* **tls**: + - fix rare segmentation faults when using TLS + * (Trevor Norris) [#11947](https://github.com/nodejs/node/pull/11947) + * (Ben Noordhuis) [#11898](https://github.com/nodejs/node/pull/11898) + * (jBarz) [#11776](https://github.com/nodejs/node/pull/11776) + +### Commits + +* [[`44260806a6`](https://github.com/nodejs/node/commit/44260806a6)] - Partial revert "tls: keep track of stream that is closed" (Trevor Norris) [#11947](https://github.com/nodejs/node/pull/11947) +* [[`ab3fdf531f`](https://github.com/nodejs/node/commit/ab3fdf531f)] - **deps**: cherry-pick ca0f9573 from V8 upstream (Ali Ijaz Sheikh) [#11940](https://github.com/nodejs/node/pull/11940) +* [[`07b92a3c0b`](https://github.com/nodejs/node/commit/07b92a3c0b)] - **doc**: add supported platforms list for v4.x (Michael Dawson) [#12091](https://github.com/nodejs/node/pull/12091) +* [[`ba91c41478`](https://github.com/nodejs/node/commit/ba91c41478)] - **module**: fix loading from global folders on Windows (Richard Lau) [#9283](https://github.com/nodejs/node/pull/9283) +* [[`b5b78b12b8`](https://github.com/nodejs/node/commit/b5b78b12b8)] - **src**: add fcntl.h include to node.cc (Bartosz Sosnowski) [#12540](https://github.com/nodejs/node/pull/12540) +* [[`eb393f9ae1`](https://github.com/nodejs/node/commit/eb393f9ae1)] - **src**: fix base64 decoding (Nikolai Vavilov) [#11995](https://github.com/nodejs/node/pull/11995) +* [[`8ed18a1429`](https://github.com/nodejs/node/commit/8ed18a1429)] - **src**: ensure that fd 0-2 are valid on windows (Bartosz Sosnowski) [#11863](https://github.com/nodejs/node/pull/11863) +* [[`ff1d61c11b`](https://github.com/nodejs/node/commit/ff1d61c11b)] - **stream_base,tls_wrap**: notify on destruct (Trevor Norris) [#11947](https://github.com/nodejs/node/pull/11947) +* [[`6040efd7dc`](https://github.com/nodejs/node/commit/6040efd7dc)] - **test**: fix flaky test-tls-wrap-timeout (Rich Trott) [#7857](https://github.com/nodejs/node/pull/7857) +* [[`7a1920dc84`](https://github.com/nodejs/node/commit/7a1920dc84)] - **test**: add hasCrypto check to tls-socket-close (Daniel Bevenius) [#11911](https://github.com/nodejs/node/pull/11911) +* [[`1dc6b38dcf`](https://github.com/nodejs/node/commit/1dc6b38dcf)] - **test**: add test for loading from global folders (Richard Lau) [#9283](https://github.com/nodejs/node/pull/9283) +* [[`54f5258582`](https://github.com/nodejs/node/commit/54f5258582)] - **tls**: fix segfault on destroy after partial read (Ben Noordhuis) [#11898](https://github.com/nodejs/node/pull/11898) +* [[`99749dccfe`](https://github.com/nodejs/node/commit/99749dccfe)] - **tls**: keep track of stream that is closed (jBarz) [#11776](https://github.com/nodejs/node/pull/11776) +* [[`6d3aaa72a8`](https://github.com/nodejs/node/commit/6d3aaa72a8)] - **tls**: TLSSocket emits 'error' on handshake failure (Mariusz 'koder' Chwalba) [#8805](https://github.com/nodejs/node/pull/8805) + ## 2017-04-04, Version 4.8.2 'Argon' (Maintenance), @MylesBorins This is a maintenance release to fix a memory leak that was introduced in 4.8.1. diff --git a/README.md b/README.md index 43f19ade786830..50974cb29c7671 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ the binary verification command above. ## Building Node.js See [BUILDING.md](BUILDING.md) for instructions on how to build -Node.js from source. +Node.js from source along with a list of officially supported platforms. ## Security diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index fc3292b05ff939..2101944d14770b 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 4 #define V8_MINOR_VERSION 5 #define V8_BUILD_NUMBER 103 -#define V8_PATCH_LEVEL 46 +#define V8_PATCH_LEVEL 47 // 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 ec0f805876fd9c..594d2ebcf20baa 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -6580,7 +6580,11 @@ Local v8::ArrayBuffer::New(Isolate* isolate, size_t byte_length) { ENTER_V8(i_isolate); i::Handle obj = i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared); - i::Runtime::SetupArrayBufferAllocatingData(i_isolate, obj, byte_length); + // TODO(jbroman): It may be useful in the future to provide a MaybeLocal + // version that throws an exception or otherwise does not crash. + if (!i::Runtime::SetupArrayBufferAllocatingData(i_isolate, obj, byte_length)) { + i::FatalProcessOutOfMemory("v8::ArrayBuffer::New"); + } return Utils::ToLocal(obj); } @@ -6775,8 +6779,12 @@ Local v8::SharedArrayBuffer::New(Isolate* isolate, ENTER_V8(i_isolate); i::Handle obj = i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kShared); - i::Runtime::SetupArrayBufferAllocatingData(i_isolate, obj, byte_length, true, - i::SharedFlag::kShared); + // TODO(jborman): It may be useful in the future to provide a MaybeLocal + // version that throws an exception or otherwise does not crash. + if (!i::Runtime::SetupArrayBufferAllocatingData(i_isolate, obj, byte_length, true, + i::SharedFlag::kShared)) { + i::FatalProcessOutOfMemory("v8::SharedArrayBuffer::New"); + } return Utils::ToLocalShared(obj); } diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index cb435e45f26ef6..31cbe3e65edfa9 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -463,7 +463,9 @@ TLSSocket.prototype._init = function(socket, wrap) { // Destroy socket if error happened before handshake's finish if (!self._secureEstablished) { - self.destroy(self._tlsError(err)); + // When handshake fails control is not yet released, + // so self._tlsError will return null instead of actual error + self.destroy(err); } else if (options.isServer && rejectUnauthorized && /peer did not return a certificate/.test(err.message)) { diff --git a/lib/module.js b/lib/module.js index 36e0b160c99038..1a8dcfc4ad04a1 100644 --- a/lib/module.js +++ b/lib/module.js @@ -453,7 +453,16 @@ Module._initPaths = function() { homeDir = process.env.HOME; } - var paths = [path.resolve(process.execPath, '..', '..', 'lib', 'node')]; + // $PREFIX/lib/node, where $PREFIX is the root of the Node.js installation. + var prefixDir; + // process.execPath is $PREFIX/bin/node except on Windows where it is + // $PREFIX\node.exe. + if (isWindows) { + prefixDir = path.resolve(process.execPath, '..'); + } else { + prefixDir = path.resolve(process.execPath, '..', '..'); + } + var paths = [path.resolve(prefixDir, 'lib', 'node')]; if (homeDir) { paths.unshift(path.resolve(homeDir, '.node_libraries')); diff --git a/src/node.cc b/src/node.cc index 63c49c49b09a5c..a5390e12f3e2d2 100644 --- a/src/node.cc +++ b/src/node.cc @@ -51,6 +51,7 @@ #endif #include +#include // _O_RDWR #include // PATH_MAX #include #include @@ -3995,6 +3996,19 @@ inline void PlatformInit() { } while (min + 1 < max); } #endif // __POSIX__ +#ifdef _WIN32 + for (int fd = 0; fd <= 2; ++fd) { + auto handle = reinterpret_cast(_get_osfhandle(fd)); + if (handle == INVALID_HANDLE_VALUE || + GetFileType(handle) == FILE_TYPE_UNKNOWN) { + // Ignore _close result. If it fails or not depends on used Windows + // version. We will just check _open result. + _close(fd); + if (fd != _open("nul", _O_RDWR)) + ABORT(); + } + } +#endif // _WIN32 } diff --git a/src/node_version.h b/src/node_version.h index 5b519561cc8b37..8bfd98c57ea273 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -8,7 +8,7 @@ #define NODE_VERSION_IS_LTS 1 #define NODE_VERSION_LTS_CODENAME "Argon" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) diff --git a/src/stream_base.h b/src/stream_base.h index cdce46abc8dcd2..1ca21d96245471 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -135,10 +135,14 @@ class StreamResource { const uv_buf_t* buf, uv_handle_type pending, void* ctx); + typedef void (*DestructCb)(void* ctx); StreamResource() : bytes_read_(0) { } - virtual ~StreamResource() = default; + virtual ~StreamResource() { + if (!destruct_cb_.is_empty()) + destruct_cb_.fn(destruct_cb_.ctx); + } virtual int DoShutdown(ShutdownWrap* req_wrap) = 0; virtual int DoTryWrite(uv_buf_t** bufs, size_t* count); @@ -175,15 +179,18 @@ class StreamResource { inline void set_alloc_cb(Callback c) { alloc_cb_ = c; } inline void set_read_cb(Callback c) { read_cb_ = c; } + inline void set_destruct_cb(Callback c) { destruct_cb_ = c; } inline Callback after_write_cb() { return after_write_cb_; } inline Callback alloc_cb() { return alloc_cb_; } inline Callback read_cb() { return read_cb_; } + inline Callback destruct_cb() { return destruct_cb_; } private: Callback after_write_cb_; Callback alloc_cb_; Callback read_cb_; + Callback destruct_cb_; uint64_t bytes_read_; friend class StreamBase; diff --git a/src/string_bytes.cc b/src/string_bytes.cc index a650ac0b00452e..8cb669e223eeba 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -174,13 +174,13 @@ size_t base64_decode_slow(char* dst, size_t dstlen, size_t k = 0; for (;;) { #define V(expr) \ - while (i < srclen) { \ + for (;;) { \ const uint8_t c = src[i]; \ lo = unbase64(c); \ i += 1; \ if (lo < 64) \ break; /* Legal character. */ \ - if (c == '=') \ + if (c == '=' || i >= srclen) \ return k; \ } \ expr; \ diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index dd1b0e3b5f340f..cab6a75c8c2a3e 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -66,6 +66,7 @@ TLSWrap::TLSWrap(Environment* env, stream_->set_after_write_cb({ OnAfterWriteImpl, this }); stream_->set_alloc_cb({ OnAllocImpl, this }); stream_->set_read_cb({ OnReadImpl, this }); + stream_->set_destruct_cb({ OnDestructImpl, this }); set_alloc_cb({ OnAllocSelf, this }); set_read_cb({ OnReadSelf, this }); @@ -426,6 +427,12 @@ void TLSWrap::ClearOut() { memcpy(buf.base, current, avail); OnRead(avail, &buf); + // Caveat emptor: OnRead() calls into JS land which can result in + // the SSL context object being destroyed. We have to carefully + // check that ssl_ != nullptr afterwards. + if (ssl_ == nullptr) + return; + read -= avail; current += avail; } @@ -523,7 +530,7 @@ int TLSWrap::GetFD() { bool TLSWrap::IsAlive() { - return ssl_ != nullptr && stream_->IsAlive(); + return ssl_ != nullptr && stream_ != nullptr && stream_->IsAlive(); } @@ -661,6 +668,12 @@ void TLSWrap::OnReadImpl(ssize_t nread, } +void TLSWrap::OnDestructImpl(void* ctx) { + TLSWrap* wrap = static_cast(ctx); + wrap->clear_stream(); +} + + void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) { buf->base = static_cast(node::Malloc(suggested_size)); CHECK_NE(buf->base, nullptr); diff --git a/src/tls_wrap.h b/src/tls_wrap.h index ba5eecbf0030e5..7c70d364bdbd38 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -52,6 +52,8 @@ class TLSWrap : public AsyncWrap, size_t self_size() const override { return sizeof(*this); } + void clear_stream() { stream_ = nullptr; } + protected: static const int kClearOutChunkSize = 16384; @@ -119,6 +121,7 @@ class TLSWrap : public AsyncWrap, const uv_buf_t* buf, uv_handle_type pending, void* ctx); + static void OnDestructImpl(void* ctx); void DoRead(ssize_t nread, const uv_buf_t* buf, uv_handle_type pending); diff --git a/test/fixtures/spawn_closed_stdio.py b/test/fixtures/spawn_closed_stdio.py new file mode 100644 index 00000000000000..b5de2552c2b13e --- /dev/null +++ b/test/fixtures/spawn_closed_stdio.py @@ -0,0 +1,8 @@ +import os +import sys +import subprocess +os.close(0) +os.close(1) +os.close(2) +exit_code = subprocess.call(sys.argv[1:], shell=False) +sys.exit(exit_code) diff --git a/test/fixtures/test-module-loading-globalpaths/home-pkg-in-both/.node_libraries/foo.js b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-both/.node_libraries/foo.js new file mode 100644 index 00000000000000..eb278f95762215 --- /dev/null +++ b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-both/.node_libraries/foo.js @@ -0,0 +1 @@ +exports.string = '$HOME/.node_libraries'; diff --git a/test/fixtures/test-module-loading-globalpaths/home-pkg-in-both/.node_modules/foo.js b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-both/.node_modules/foo.js new file mode 100644 index 00000000000000..8a665b3e98cfe2 --- /dev/null +++ b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-both/.node_modules/foo.js @@ -0,0 +1 @@ +exports.string = '$HOME/.node_modules'; diff --git a/test/fixtures/test-module-loading-globalpaths/home-pkg-in-node_libraries/.node_libraries/foo.js b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-node_libraries/.node_libraries/foo.js new file mode 100644 index 00000000000000..eb278f95762215 --- /dev/null +++ b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-node_libraries/.node_libraries/foo.js @@ -0,0 +1 @@ +exports.string = '$HOME/.node_libraries'; diff --git a/test/fixtures/test-module-loading-globalpaths/home-pkg-in-node_modules/.node_modules/foo.js b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-node_modules/.node_modules/foo.js new file mode 100644 index 00000000000000..8a665b3e98cfe2 --- /dev/null +++ b/test/fixtures/test-module-loading-globalpaths/home-pkg-in-node_modules/.node_modules/foo.js @@ -0,0 +1 @@ +exports.string = '$HOME/.node_modules'; diff --git a/test/fixtures/test-module-loading-globalpaths/local-pkg/node_modules/foo.js b/test/fixtures/test-module-loading-globalpaths/local-pkg/node_modules/foo.js new file mode 100644 index 00000000000000..63e844e4d4060b --- /dev/null +++ b/test/fixtures/test-module-loading-globalpaths/local-pkg/node_modules/foo.js @@ -0,0 +1 @@ +exports.string = 'local'; diff --git a/test/fixtures/test-module-loading-globalpaths/local-pkg/test.js b/test/fixtures/test-module-loading-globalpaths/local-pkg/test.js new file mode 100644 index 00000000000000..8054983e992ce8 --- /dev/null +++ b/test/fixtures/test-module-loading-globalpaths/local-pkg/test.js @@ -0,0 +1,2 @@ +'use strict'; +console.log(require('foo').string); diff --git a/test/fixtures/test-module-loading-globalpaths/node_path/foo.js b/test/fixtures/test-module-loading-globalpaths/node_path/foo.js new file mode 100644 index 00000000000000..3ce43c49206f73 --- /dev/null +++ b/test/fixtures/test-module-loading-globalpaths/node_path/foo.js @@ -0,0 +1 @@ +exports.string = '$NODE_PATH'; diff --git a/test/parallel/test-buffer-alloc.js b/test/parallel/test-buffer-alloc.js index d3564ab81b8d92..49d628bfec58b0 100644 --- a/test/parallel/test-buffer-alloc.js +++ b/test/parallel/test-buffer-alloc.js @@ -696,6 +696,10 @@ assert.equal(dot.toString('base64'), '//4uAA=='); // Regression test for https://github.com/nodejs/node/issues/3496. assert.equal(Buffer.from('=bad'.repeat(1e4), 'base64').length, 0); +// Regression test for https://github.com/nodejs/node/issues/11987. +assert.deepStrictEqual(Buffer.from('w0 ', 'base64'), + Buffer.from('w0', 'base64')); + { // Creating buffers larger than pool size. const l = Buffer.poolSize + 5; diff --git a/test/parallel/test-module-loading-globalpaths.js b/test/parallel/test-module-loading-globalpaths.js new file mode 100644 index 00000000000000..d789f5409901ba --- /dev/null +++ b/test/parallel/test-module-loading-globalpaths.js @@ -0,0 +1,101 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); +const child_process = require('child_process'); +const pkgName = 'foo'; + +if (process.argv[2] === 'child') { + console.log(require(pkgName).string); +} else { + common.refreshTmpDir(); + + // Copy node binary into a test $PREFIX directory. + const prefixPath = path.join(common.tmpDir, 'install'); + fs.mkdirSync(prefixPath); + let testExecPath; + if (common.isWindows) { + testExecPath = path.join(prefixPath, path.basename(process.execPath)); + } else { + const prefixBinPath = path.join(prefixPath, 'bin'); + fs.mkdirSync(prefixBinPath); + testExecPath = path.join(prefixBinPath, path.basename(process.execPath)); + } + const mode = fs.statSync(process.execPath).mode; + fs.writeFileSync(testExecPath, fs.readFileSync(process.execPath)); + fs.chmodSync(testExecPath, mode); + + const runTest = (expectedString, env) => { + const child = child_process.execFileSync(testExecPath, + [ __filename, 'child' ], + { encoding: 'utf8', env: env }); + assert.strictEqual(child.trim(), expectedString); + }; + + const testFixturesDir = path.join(common.fixturesDir, + path.basename(__filename, '.js')); + + const env = Object.assign({}, process.env); + // Turn on module debug to aid diagnosing failures. + env['NODE_DEBUG'] = 'module'; + // Unset NODE_PATH. + delete env['NODE_PATH']; + + // Test empty global path. + const noPkgHomeDir = path.join(common.tmpDir, 'home-no-pkg'); + fs.mkdirSync(noPkgHomeDir); + env['HOME'] = env['USERPROFILE'] = noPkgHomeDir; + assert.throws( + () => { + child_process.execFileSync(testExecPath, [ __filename, 'child' ], + { encoding: 'utf8', env: env }); + }, + new RegExp('Cannot find module \'' + pkgName + '\'')); + + // Test module in $HOME/.node_modules. + const modHomeDir = path.join(testFixturesDir, 'home-pkg-in-node_modules'); + env['HOME'] = env['USERPROFILE'] = modHomeDir; + runTest('$HOME/.node_modules', env); + + // Test module in $HOME/.node_libraries. + const libHomeDir = path.join(testFixturesDir, 'home-pkg-in-node_libraries'); + env['HOME'] = env['USERPROFILE'] = libHomeDir; + runTest('$HOME/.node_libraries', env); + + // Test module both $HOME/.node_modules and $HOME/.node_libraries. + const bothHomeDir = path.join(testFixturesDir, 'home-pkg-in-both'); + env['HOME'] = env['USERPROFILE'] = bothHomeDir; + runTest('$HOME/.node_modules', env); + + // Test module in $PREFIX/lib/node. + // Write module into $PREFIX/lib/node. + const expectedString = '$PREFIX/lib/node'; + const prefixLibPath = path.join(prefixPath, 'lib'); + fs.mkdirSync(prefixLibPath); + const prefixLibNodePath = path.join(prefixLibPath, 'node'); + fs.mkdirSync(prefixLibNodePath); + const pkgPath = path.join(prefixLibNodePath, pkgName + '.js'); + fs.writeFileSync(pkgPath, 'exports.string = \'' + expectedString + '\';'); + + env['HOME'] = env['USERPROFILE'] = noPkgHomeDir; + runTest(expectedString, env); + + // Test module in all global folders. + env['HOME'] = env['USERPROFILE'] = bothHomeDir; + runTest('$HOME/.node_modules', env); + + // Test module in NODE_PATH is loaded ahead of global folders. + env['HOME'] = env['USERPROFILE'] = bothHomeDir; + env['NODE_PATH'] = path.join(testFixturesDir, 'node_path'); + runTest('$NODE_PATH', env); + + // Test module in local folder is loaded ahead of global folders. + const localDir = path.join(testFixturesDir, 'local-pkg'); + env['HOME'] = env['USERPROFILE'] = bothHomeDir; + env['NODE_PATH'] = path.join(testFixturesDir, 'node_path'); + const child = child_process.execFileSync(testExecPath, + [ path.join(localDir, 'test.js') ], + { encoding: 'utf8', env: env }); + assert.strictEqual(child.trim(), 'local'); +} diff --git a/test/parallel/test-stdio-closed.js b/test/parallel/test-stdio-closed.js index 98e4f980d50dd6..2313140a26aea7 100644 --- a/test/parallel/test-stdio-closed.js +++ b/test/parallel/test-stdio-closed.js @@ -3,9 +3,21 @@ const common = require('../common'); const assert = require('assert'); const spawn = require('child_process').spawn; const fs = require('fs'); +const path = require('path'); if (common.isWindows) { - common.skip('platform not supported.'); + if (process.argv[2] === 'child') { + process.stdin; + process.stdout; + process.stderr; + return; + } + const python = process.env.PYTHON || 'python'; + const script = path.join(common.fixturesDir, 'spawn_closed_stdio.py'); + const proc = spawn(python, [script, process.execPath, __filename, 'child']); + proc.on('exit', common.mustCall(function(exitCode) { + assert.strictEqual(exitCode, 0); + })); return; } diff --git a/test/parallel/test-tls-retain-handle-no-abort.js b/test/parallel/test-tls-retain-handle-no-abort.js new file mode 100644 index 00000000000000..43b3709fd5f85b --- /dev/null +++ b/test/parallel/test-tls-retain-handle-no-abort.js @@ -0,0 +1,42 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); + return; +} +const tls = require('tls'); +const fs = require('fs'); +const util = require('util'); + +const sent = 'hello world'; +const serverOptions = { + isServer: true, + key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem') +}; + +let ssl = null; + +process.on('exit', function() { + assert.ok(ssl !== null); + // If the internal pointer to stream_ isn't cleared properly then this + // will abort. + util.inspect(ssl); +}); + +const server = tls.createServer(serverOptions, function(s) { + s.on('data', function() { }); + s.on('end', function() { + server.close(); + s.destroy(); + }); +}).listen(0, function() { + const c = new tls.TLSSocket(); + ssl = c.ssl; + c.connect(this.address().port, function() { + c.end(sent); + }); +}); diff --git a/test/parallel/test-tls-server-failed-handshake-emits-clienterror.js b/test/parallel/test-tls-server-failed-handshake-emits-clienterror.js new file mode 100644 index 00000000000000..a02912542221fb --- /dev/null +++ b/test/parallel/test-tls-server-failed-handshake-emits-clienterror.js @@ -0,0 +1,36 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); + return; +} +const tls = require('tls'); +const net = require('net'); +const assert = require('assert'); + +const bonkers = Buffer.alloc(1024, 42); + +let clientErrorEmited = false; + +const server = tls.createServer({}) + .listen(0, function() { + const c = net.connect({ port: this.address().port }, function() { + c.write(bonkers); + }); + + }).on('clientError', function(e) { + clientErrorEmited = true; + assert.ok(e instanceof Error, + 'Instance of Error should be passed to error handler'); + assert.ok(e.message.match( + /SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol/), + 'Expecting SSL unknown protocol'); + }); + +setTimeout(function() { + server.close(); + + assert.ok(clientErrorEmited, 'clientError should be emited'); + +}, common.platformTimeout(200)); diff --git a/test/parallel/test-tls-socket-close.js b/test/parallel/test-tls-socket-close.js new file mode 100644 index 00000000000000..4e7382f3407a8a --- /dev/null +++ b/test/parallel/test-tls-socket-close.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { + common.skip('missing crypto'); + return; +} +const assert = require('assert'); + +const tls = require('tls'); +const fs = require('fs'); +const net = require('net'); + +const key = fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'); +const cert = fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem'); + +const T = 100; + +// tls server +const tlsServer = tls.createServer({ cert, key }, (socket) => { + setTimeout(() => { + socket.on('error', (error) => { + assert.strictEqual(error.code, 'EINVAL'); + tlsServer.close(); + netServer.close(); + }); + socket.write('bar'); + }, T * 2); +}); + +// plain tcp server +const netServer = net.createServer((socket) => { + // if client wants to use tls + tlsServer.emit('connection', socket); + + socket.setTimeout(T, () => { + // this breaks if TLSSocket is already managing the socket: + socket.destroy(); + }); +}).listen(0, common.mustCall(function() { + + // connect client + tls.connect({ + host: 'localhost', + port: this.address().port, + rejectUnauthorized: false + }).write('foo'); +})); diff --git a/test/parallel/test-tls-socket-destroy.js b/test/parallel/test-tls-socket-destroy.js new file mode 100644 index 00000000000000..27651f8ec7206a --- /dev/null +++ b/test/parallel/test-tls-socket-destroy.js @@ -0,0 +1,36 @@ +'use strict'; + +const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); + return; +} + +const fs = require('fs'); +const net = require('net'); +const tls = require('tls'); + +const key = fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'); +const cert = fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem'); +const secureContext = tls.createSecureContext({ key, cert }); + +const server = net.createServer(common.mustCall((conn) => { + const options = { isServer: true, secureContext, server }; + const socket = new tls.TLSSocket(conn, options); + socket.once('data', common.mustCall(() => { + socket._destroySSL(); // Should not crash. + server.close(); + })); +})); + +server.listen(0, function() { + const options = { + port: this.address().port, + rejectUnauthorized: false, + }; + tls.connect(options, function() { + this.write('*'.repeat(1 << 20)); // Write more data than fits in a frame. + this.on('error', this.destroy); // Server closes connection on us. + }); +}); diff --git a/test/parallel/test-tls-socket-failed-handshake-emits-error.js b/test/parallel/test-tls-socket-failed-handshake-emits-error.js new file mode 100644 index 00000000000000..f655dc97b5a99b --- /dev/null +++ b/test/parallel/test-tls-socket-failed-handshake-emits-error.js @@ -0,0 +1,38 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); + return; +} +const tls = require('tls'); +const net = require('net'); +const assert = require('assert'); + +const bonkers = Buffer.alloc(1024, 42); + +const server = net.createServer(function(c) { + setTimeout(function() { + const s = new tls.TLSSocket(c, { + isServer: true, + server: server + }); + + s.on('error', common.mustCall(function(e) { + assert.ok(e instanceof Error, + 'Instance of Error should be passed to error handler'); + assert.ok(e.message.match( + /SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol/), + 'Expecting SSL unknown protocol'); + })); + + s.on('close', function() { + server.close(); + s.destroy(); + }); + }, common.platformTimeout(200)); +}).listen(0, function() { + const c = net.connect({port: this.address().port}, function() { + c.write(bonkers); + }); +}); diff --git a/test/parallel/test-tls-wrap-timeout.js b/test/parallel/test-tls-wrap-timeout.js index ab0f307a970bb3..0454242d92ecb6 100644 --- a/test/parallel/test-tls-wrap-timeout.js +++ b/test/parallel/test-tls-wrap-timeout.js @@ -1,42 +1,54 @@ 'use strict'; -var common = require('../common'); -var assert = require('assert'); +const common = require('../common'); if (!common.hasCrypto) { common.skip('missing crypto'); return; } -var tls = require('tls'); +const assert = require('assert'); +const tls = require('tls'); -var net = require('net'); -var fs = require('fs'); +const net = require('net'); +const fs = require('fs'); -var options = { +const options = { key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem') }; -var server = tls.createServer(options, function(c) { - setTimeout(function() { - c.write('hello'); - setTimeout(function() { - c.destroy(); - server.close(); - }, 150); - }, 150); -}); +const server = tls.createServer(options, common.mustCall((c) => { + setImmediate(() => { + c.write('hello', () => { + setImmediate(() => { + c.destroy(); + server.close(); + }); + }); + }); +})); + +var socket; +var lastIdleStart; -server.listen(0, function() { - var socket = net.connect(this.address().port, function() { - var s = socket.setTimeout(common.platformTimeout(240), function() { +server.listen(0, () => { + socket = net.connect(server.address().port, function() { + const s = socket.setTimeout(Number.MAX_VALUE, function() { throw new Error('timeout'); }); assert.ok(s instanceof net.Socket); - var tsocket = tls.connect({ + assert.notStrictEqual(socket._idleTimeout, -1); + lastIdleStart = socket._idleStart; + + const tsocket = tls.connect({ socket: socket, rejectUnauthorized: false }); tsocket.resume(); }); }); + +process.on('exit', () => { + assert.strictEqual(socket._idleTimeout, -1); + assert(lastIdleStart < socket._idleStart); +});