Skip to content

v4.8.3 proposal #12499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 2, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 65 additions & 5 deletions BUILDING.md
Original file line number Diff line number Diff line change
@@ -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).
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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

2 changes: 1 addition & 1 deletion deps/v8/include/v8-version.h
Original file line number Diff line number Diff line change
@@ -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.)
14 changes: 11 additions & 3 deletions deps/v8/src/api.cc
Original file line number Diff line number Diff line change
@@ -6580,7 +6580,11 @@ Local<ArrayBuffer> v8::ArrayBuffer::New(Isolate* isolate, size_t byte_length) {
ENTER_V8(i_isolate);
i::Handle<i::JSArrayBuffer> 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<SharedArrayBuffer> v8::SharedArrayBuffer::New(Isolate* isolate,
ENTER_V8(i_isolate);
i::Handle<i::JSArrayBuffer> 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);
}

4 changes: 3 additions & 1 deletion lib/_tls_wrap.js
Original file line number Diff line number Diff line change
@@ -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)) {
11 changes: 10 additions & 1 deletion lib/module.js
Original file line number Diff line number Diff line change
@@ -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'));
14 changes: 14 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
#endif

#include <errno.h>
#include <fcntl.h> // _O_RDWR
#include <limits.h> // PATH_MAX
#include <locale.h>
#include <signal.h>
@@ -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<HANDLE>(_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
}


2 changes: 1 addition & 1 deletion src/node_version.h
Original file line number Diff line number Diff line change
@@ -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)
9 changes: 8 additions & 1 deletion src/stream_base.h
Original file line number Diff line number Diff line change
@@ -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<AllocCb> c) { alloc_cb_ = c; }
inline void set_read_cb(Callback<ReadCb> c) { read_cb_ = c; }
inline void set_destruct_cb(Callback<DestructCb> c) { destruct_cb_ = c; }

inline Callback<AfterWriteCb> after_write_cb() { return after_write_cb_; }
inline Callback<AllocCb> alloc_cb() { return alloc_cb_; }
inline Callback<ReadCb> read_cb() { return read_cb_; }
inline Callback<DestructCb> destruct_cb() { return destruct_cb_; }

private:
Callback<AfterWriteCb> after_write_cb_;
Callback<AllocCb> alloc_cb_;
Callback<ReadCb> read_cb_;
Callback<DestructCb> destruct_cb_;
uint64_t bytes_read_;

friend class StreamBase;
4 changes: 2 additions & 2 deletions src/string_bytes.cc
Original file line number Diff line number Diff line change
@@ -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; \
15 changes: 14 additions & 1 deletion src/tls_wrap.cc
Original file line number Diff line number Diff line change
@@ -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<TLSWrap*>(ctx);
wrap->clear_stream();
}


void TLSWrap::OnAllocSelf(size_t suggested_size, uv_buf_t* buf, void* ctx) {
buf->base = static_cast<char*>(node::Malloc(suggested_size));
CHECK_NE(buf->base, nullptr);
3 changes: 3 additions & 0 deletions src/tls_wrap.h
Original file line number Diff line number Diff line change
@@ -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);

8 changes: 8 additions & 0 deletions test/fixtures/spawn_closed_stdio.py
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.string = '$HOME/.node_libraries';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.string = '$HOME/.node_libraries';

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
'use strict';
console.log(require('foo').string);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.string = '$NODE_PATH';
4 changes: 4 additions & 0 deletions test/parallel/test-buffer-alloc.js
Original file line number Diff line number Diff line change
@@ -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;
101 changes: 101 additions & 0 deletions test/parallel/test-module-loading-globalpaths.js
Original file line number Diff line number Diff line change
@@ -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');
}
14 changes: 13 additions & 1 deletion test/parallel/test-stdio-closed.js
Original file line number Diff line number Diff line change
@@ -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;
}

42 changes: 42 additions & 0 deletions test/parallel/test-tls-retain-handle-no-abort.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
Original file line number Diff line number Diff line change
@@ -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));
47 changes: 47 additions & 0 deletions test/parallel/test-tls-socket-close.js
Original file line number Diff line number Diff line change
@@ -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');
}));
36 changes: 36 additions & 0 deletions test/parallel/test-tls-socket-destroy.js
Original file line number Diff line number Diff line change
@@ -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.
});
});
38 changes: 38 additions & 0 deletions test/parallel/test-tls-socket-failed-handshake-emits-error.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
50 changes: 31 additions & 19 deletions test/parallel/test-tls-wrap-timeout.js
Original file line number Diff line number Diff line change
@@ -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);
});