Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
913ec56
deps: cjs-module-lexer: cherry-pick 22093e765f
peZhmanParsaee Mar 24, 2021
5e15ae0
test: add DataView test case for v8 serdes
Trott Mar 28, 2021
570fbce
url: forbid pipe in URL host
RaisinTen Mar 23, 2021
13c3924
doc: add Windows-specific info to subprocess.kill()
joaolucasl Aug 21, 2020
bbdcdad
deps: upgrade openssl sources to 1.1.1k+quic
hassaanp Mar 25, 2021
f0e7714
deps: update archs files for OpenSSL-1.1.1k
hassaanp Mar 25, 2021
f3fabb5
doc: add missing cleanup step in OpenSSL upgrade
tniessen Mar 26, 2021
a57dc06
doc: improve Buffer's encoding documentation
targos Mar 27, 2021
d86aca9
http: optimize debug function correctly
targos Mar 29, 2021
a94cc27
path: refactor to use more primordials
marsonya Mar 24, 2021
4ef102d
deps: update to cjs-module-lexer@1.1.1
guybedford Mar 30, 2021
51e7a33
tools,doc: add "legacy" badge in the TOC
aduh95 Mar 27, 2021
062541a
http2: add specific error code for custom frames
addaleax Mar 26, 2021
8792c7c
doc: add missing events.on metadata
addaleax Mar 29, 2021
669b81c
net,tls: add abort signal support to connect
Linkgoron Mar 12, 2021
a1123f0
readline: add AbortSignal support to interface
Linkgoron Mar 26, 2021
6cc1e15
readline: fix pre-aborted signal question handling
Linkgoron Mar 26, 2021
21e399b
lib: change wording in lib/internal/child_process comment
marsonya Mar 25, 2021
8525231
lib: change wording in lib/domain.js comment
marsonya Mar 26, 2021
ae70aa3
doc: add distinctive color for code elements inside links
aduh95 Mar 27, 2021
a4169ce
net: make net.BlockList cloneable
jasnell Mar 25, 2021
daa8a7b
net: add SocketAddress class
jasnell Mar 25, 2021
0709cbb
net: allow net.BlockList to use net.SocketAddress objects
jasnell Mar 25, 2021
4d50975
test: improve clarity of ALS-enable-disable.js
PhakornKiong Mar 31, 2021
ce14080
doc: move psmarshall to collaborators emeriti
psmarshall Mar 31, 2021
ad7e344
fs: fix chown abort
RaisinTen Mar 31, 2021
6ad0b6f
src: fix error handling for CryptoJob::ToResult
tniessen Apr 1, 2021
6d28a24
tools: update ESLint to 7.23.0
lpinca Mar 30, 2021
0db1a1e
test: deflake test-fs-read-optional-params
lpinca Mar 30, 2021
960c6be
crypto: add buffering to randomInt
tniessen Sep 8, 2020
b02c352
test: fix test-tls-no-sslv3 for OpenSSL 3
richardlau Apr 1, 2021
b40d35d
doc: document how to unref stdin when using readline.Interface
AnupamaP Apr 1, 2021
0243376
test: use faster variant for rss
PoojaDurgad Jan 7, 2021
3ab9619
module: improve error message for invalid data URL
aduh95 Mar 10, 2021
3175559
test: add extra space in test failure output
Ayase-252 Mar 28, 2021
afc6ab2
doc: fix asyncLocalStorage.run() description
PhakornKiong Apr 1, 2021
ac69b95
crypto: use correct webcrypto RSASSA-PKCS1-v1_5 algorithm name
panva Apr 1, 2021
e61cc0b
src: fix typos in crypto comments
tniessen Apr 1, 2021
b6f4901
fs: add support for async iterators to `fsPromises.writeFile`
Feb 21, 2021
629e72e
src: fix typo in node_mutex
tniessen Mar 31, 2021
1a34e9c
2021-04-06, Version 15.14.0 (Current)
MylesBorins Apr 5, 2021
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
Prev Previous commit
Next Next commit
net,tls: add abort signal support to connect
Add documentation for net.connect AbortSignal,
and add the support to tls.connect as well

PR-URL: #37735
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
Linkgoron authored and MylesBorins committed Apr 4, 2021
commit 669b81c68bd7b9befc9c55f364b802bce59abf74
6 changes: 6 additions & 0 deletions doc/api/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@ it to interact with the client.
### `new net.Socket([options])`
<!-- YAML
added: v0.3.4
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/37735
description: AbortSignal support was added.
-->

* `options` {Object} Available options are:
Expand All @@ -508,6 +512,8 @@ added: v0.3.4
otherwise ignored. **Default:** `false`.
* `writable` {boolean} Allow writes on the socket when an `fd` is passed,
otherwise ignored. **Default:** `false`.
* `signal` {AbortSignal} An Abort signal that may be used to destroy the
socket.
* Returns: {net.Socket}

Creates a new socket object.
Expand Down
2 changes: 2 additions & 0 deletions lib/_tls_wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ function TLSSocket(socket, opts) {
manualStart: true,
highWaterMark: tlsOptions.highWaterMark,
onread: !socket ? tlsOptions.onread : null,
signal: tlsOptions.signal,
}]);

// Proxy for API compatibility
Expand Down Expand Up @@ -1627,6 +1628,7 @@ exports.connect = function connect(...args) {
pskCallback: options.pskCallback,
highWaterMark: options.highWaterMark,
onread: options.onread,
signal: options.signal,
});

tlssock[kConnectOptions] = options;
Expand Down
45 changes: 45 additions & 0 deletions test/parallel/test-http2-client-destroy.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,48 @@ const { getEventListeners } = require('events');
req.on('close', common.mustCall(() => server.close()));
}));
}


// Destroy ClientHttpSession with AbortSignal
{
function testH2ConnectAbort(secure) {
const server = secure ? h2.createSecureServer() : h2.createServer();
const controller = new AbortController();

server.on('stream', common.mustNotCall());
server.listen(0, common.mustCall(() => {
const { signal } = controller;
const protocol = secure ? 'https' : 'http';
const client = h2.connect(`${protocol}://localhost:${server.address().port}`, {
signal,
});
client.on('close', common.mustCall());
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);

client.on('error', common.mustCall(common.mustCall((err) => {
assert.strictEqual(err.code, 'ABORT_ERR');
assert.strictEqual(err.name, 'AbortError');
})));

const req = client.request({}, {});
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);

req.on('error', common.mustCall((err) => {
assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_CANCEL');
assert.strictEqual(err.name, 'Error');
assert.strictEqual(req.aborted, false);
assert.strictEqual(req.destroyed, true);
}));
req.on('close', common.mustCall(() => server.close()));

assert.strictEqual(req.aborted, false);
assert.strictEqual(req.destroyed, false);
// Signal listener attached
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);

controller.abort();
}));
}
testH2ConnectAbort(false);
testH2ConnectAbort(true);
}
55 changes: 55 additions & 0 deletions test/parallel/test-https-abortcontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const options = {
cert: fixtures.readKey('agent1-cert.pem')
};

// Check async post-aborted
(async () => {
const { port, server } = await new Promise((resolve) => {
const server = https.createServer(options, () => {});
Expand All @@ -38,3 +39,57 @@ const options = {
server.close();
}
})().then(common.mustCall());

// Check sync post-aborted signal
(async () => {
const { port, server } = await new Promise((resolve) => {
const server = https.createServer(options, () => {});
server.listen(0, () => resolve({ port: server.address().port, server }));
});
try {
const ac = new AbortController();
const { signal } = ac;
const req = https.request({
host: 'localhost',
port,
path: '/',
method: 'GET',
rejectUnauthorized: false,
signal,
});
assert.strictEqual(getEventListeners(ac.signal, 'abort').length, 1);
ac.abort();
const [ err ] = await once(req, 'error');
assert.strictEqual(err.name, 'AbortError');
assert.strictEqual(err.code, 'ABORT_ERR');
} finally {
server.close();
}
})().then(common.mustCall());

// Check pre-aborted signal
(async () => {
const { port, server } = await new Promise((resolve) => {
const server = https.createServer(options, () => {});
server.listen(0, () => resolve({ port: server.address().port, server }));
});
try {
const ac = new AbortController();
const { signal } = ac;
ac.abort();
const req = https.request({
host: 'localhost',
port,
path: '/',
method: 'GET',
rejectUnauthorized: false,
signal,
});
assert.strictEqual(getEventListeners(ac.signal, 'abort').length, 0);
const [ err ] = await once(req, 'error');
assert.strictEqual(err.name, 'AbortError');
assert.strictEqual(err.code, 'ABORT_ERR');
} finally {
server.close();
}
})().then(common.mustCall());
87 changes: 87 additions & 0 deletions test/parallel/test-https-agent-abort-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const https = require('https');
const { once } = require('events');
const Agent = https.Agent;
const fixtures = require('../common/fixtures');

const { getEventListeners } = require('events');
const agent = new Agent();

const options = {
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem')
};

const server = https.createServer(options);

server.listen(0, common.mustCall(async () => {
const port = server.address().port;
const host = 'localhost';
const options = {
port: port,
host: host,
rejectUnauthorized: false,
_agentKey: agent.getName({ port, host })
};

async function postCreateConnection() {
const ac = new AbortController();
const { signal } = ac;
const connection = agent.createConnection({ ...options, signal });
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
ac.abort();
const [err] = await once(connection, 'error');
assert.strictEqual(err.name, 'AbortError');
}

async function preCreateConnection() {
const ac = new AbortController();
const { signal } = ac;
ac.abort();
const connection = agent.createConnection({ ...options, signal });
const [err] = await once(connection, 'error');
assert.strictEqual(err.name, 'AbortError');
}


async function agentAsParam() {
const ac = new AbortController();
const { signal } = ac;
const request = https.get({
port: server.address().port,
path: '/hello',
agent: agent,
signal,
});
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
ac.abort();
const [err] = await once(request, 'error');
assert.strictEqual(err.name, 'AbortError');
}

async function agentAsParamPreAbort() {
const ac = new AbortController();
const { signal } = ac;
ac.abort();
const request = https.get({
port: server.address().port,
path: '/hello',
agent: agent,
signal,
});
assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
const [err] = await once(request, 'error');
assert.strictEqual(err.name, 'AbortError');
}

await postCreateConnection();
await preCreateConnection();
await agentAsParam();
await agentAsParamPreAbort();
server.close();
}));
96 changes: 96 additions & 0 deletions test/parallel/test-net-connect-abort-controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict';
const common = require('../common');
const net = require('net');
const assert = require('assert');
const server = net.createServer();
const { getEventListeners, once } = require('events');

const liveConnections = new Set();

server.listen(0, common.mustCall(async () => {
const port = server.address().port;
const host = 'localhost';
const socketOptions = (signal) => ({ port, host, signal });
server.on('connection', (connection) => {
liveConnections.add(connection);
connection.on('close', () => {
liveConnections.delete(connection);
});
});

const assertAbort = async (socket, testName) => {
try {
await once(socket, 'close');
assert.fail(`close ${testName} should have thrown`);
} catch (err) {
assert.strictEqual(err.name, 'AbortError');
}
};

async function postAbort() {
const ac = new AbortController();
const { signal } = ac;
const socket = net.connect(socketOptions(signal));
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
ac.abort();
await assertAbort(socket, 'postAbort');
}

async function preAbort() {
const ac = new AbortController();
const { signal } = ac;
ac.abort();
const socket = net.connect(socketOptions(signal));
assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
await assertAbort(socket, 'preAbort');
}

async function tickAbort() {
const ac = new AbortController();
const { signal } = ac;
setImmediate(() => ac.abort());
const socket = net.connect(socketOptions(signal));
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
await assertAbort(socket, 'tickAbort');
}

async function testConstructor() {
const ac = new AbortController();
const { signal } = ac;
ac.abort();
const socket = new net.Socket(socketOptions(signal));
assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
await assertAbort(socket, 'testConstructor');
}

async function testConstructorPost() {
const ac = new AbortController();
const { signal } = ac;
const socket = new net.Socket(socketOptions(signal));
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
ac.abort();
await assertAbort(socket, 'testConstructorPost');
}

async function testConstructorPostTick() {
const ac = new AbortController();
const { signal } = ac;
const socket = new net.Socket(socketOptions(signal));
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
setImmediate(() => ac.abort());
await assertAbort(socket, 'testConstructorPostTick');
}

await postAbort();
await preAbort();
await tickAbort();
await testConstructor();
await testConstructorPost();
await testConstructorPostTick();

// Killing the net.socket without connecting hangs the server.
for (const connection of liveConnections) {
connection.destroy();
}
server.close(common.mustCall());
}));
Loading