Skip to content

Commit 02dbcc4

Browse files
Nitzan Uzielydanielleadams
Nitzan Uziely
authored andcommitted
net: support abortSignal in server.listen
PR-URL: #36623 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 833e614 commit 02dbcc4

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

doc/api/net.md

+18
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ Listening on a file descriptor is not supported on Windows.
324324
<!-- YAML
325325
added: v0.11.14
326326
changes:
327+
- version: REPLACEME
328+
pr-url: https://github.com/nodejs/node/pull/36623
329+
description: AbortSignal support was added.
327330
- version: v11.4.0
328331
pr-url: https://github.com/nodejs/node/pull/23798
329332
description: The `ipv6Only` option is supported.
@@ -344,6 +347,7 @@ changes:
344347
* `ipv6Only` {boolean} For TCP servers, setting `ipv6Only` to `true` will
345348
disable dual-stack support, i.e., binding to host `::` won't make
346349
`0.0.0.0` be bound. **Default:** `false`.
350+
* `signal` {AbortSignal} An AbortSignal that may be used to close a listening server.
347351
* `callback` {Function}
348352
functions.
349353
* Returns: {net.Server}
@@ -375,6 +379,20 @@ Starting an IPC server as root may cause the server path to be inaccessible for
375379
unprivileged users. Using `readableAll` and `writableAll` will make the server
376380
accessible for all users.
377381

382+
If the `signal` option is enabled, calling `.abort()` on the corresponding
383+
`AbortController` is similar to calling `.close()` on the server:
384+
385+
```js
386+
const controller = new AbortController();
387+
server.listen({
388+
host: 'localhost',
389+
port: 80,
390+
signal: controller.signal
391+
});
392+
// Later, when you want to close the server.
393+
controller.abort();
394+
```
395+
378396
#### `server.listen(path[, backlog][, callback])`
379397
<!-- YAML
380398
added: v0.1.90

lib/internal/streams/add-abort-signal.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const eos = require('internal/streams/end-of-stream');
99
const { ERR_INVALID_ARG_TYPE } = codes;
1010

1111
// This method is inlined here for readable-stream
12-
// It also does not allow for signal to not exist on the steam
12+
// It also does not allow for signal to not exist on the stream
1313
// https://github.com/nodejs/node/pull/36061#discussion_r533718029
1414
const validateAbortSignal = (signal, name) => {
1515
if (typeof signal !== 'object' ||

lib/net.js

+18
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ const {
109109
} = require('internal/errors');
110110
const { isUint8Array } = require('internal/util/types');
111111
const {
112+
validateAbortSignal,
112113
validateInt32,
113114
validatePort,
114115
validateString
@@ -1148,6 +1149,22 @@ function afterConnect(status, handle, req, readable, writable) {
11481149
}
11491150
}
11501151

1152+
function addAbortSignalOption(self, options) {
1153+
if (options?.signal === undefined) {
1154+
return;
1155+
}
1156+
validateAbortSignal(options.signal, 'options.signal');
1157+
const { signal } = options;
1158+
const onAborted = () => {
1159+
self.close();
1160+
};
1161+
if (signal.aborted) {
1162+
process.nextTick(onAborted);
1163+
} else {
1164+
signal.addEventListener('abort', onAborted);
1165+
self.once('close', () => signal.removeEventListener('abort', onAborted));
1166+
}
1167+
}
11511168

11521169
function Server(options, connectionListener) {
11531170
if (!(this instanceof Server))
@@ -1398,6 +1415,7 @@ Server.prototype.listen = function(...args) {
13981415
listenInCluster(this, null, -1, -1, backlogFromArgs);
13991416
return this;
14001417
}
1418+
addAbortSignalOption(this, options);
14011419
// (handle[, backlog][, cb]) where handle is an object with a fd
14021420
if (typeof options.fd === 'number' && options.fd >= 0) {
14031421
listenInCluster(this, null, null, null, backlogFromArgs, options.fd);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const net = require('net');
5+
6+
{
7+
// Test bad signal.
8+
const server = net.createServer();
9+
assert.throws(
10+
() => server.listen({ port: 0, signal: 'INVALID_SIGNAL' }),
11+
{
12+
code: 'ERR_INVALID_ARG_TYPE',
13+
name: 'TypeError'
14+
});
15+
}
16+
17+
{
18+
// Test close.
19+
const server = net.createServer();
20+
const controller = new AbortController();
21+
server.on('close', common.mustCall());
22+
server.listen({ port: 0, signal: controller.signal });
23+
controller.abort();
24+
}
25+
26+
{
27+
// Test close with pre-aborted signal.
28+
const server = net.createServer();
29+
const controller = new AbortController();
30+
controller.abort();
31+
server.on('close', common.mustCall());
32+
server.listen({ port: 0, signal: controller.signal });
33+
}

0 commit comments

Comments
 (0)