Skip to content

Commit

Permalink
net: support abortSignal in server.listen
Browse files Browse the repository at this point in the history
PR-URL: nodejs#36623
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
Nitzan Uziely authored and jasnell committed Dec 31, 2020
1 parent 51dfb86 commit 51b4367
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 1 deletion.
18 changes: 18 additions & 0 deletions doc/api/net.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ Listening on a file descriptor is not supported on Windows.
<!-- YAML
added: v0.11.14
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/36623
description: AbortSignal support was added.
- version: v11.4.0
pr-url: https://github.com/nodejs/node/pull/23798
description: The `ipv6Only` option is supported.
Expand All @@ -344,6 +347,7 @@ changes:
* `ipv6Only` {boolean} For TCP servers, setting `ipv6Only` to `true` will
disable dual-stack support, i.e., binding to host `::` won't make
`0.0.0.0` be bound. **Default:** `false`.
* `signal` {AbortSignal} An AbortSignal that may be used to close a listening server.
* `callback` {Function}
functions.
* Returns: {net.Server}
Expand Down Expand Up @@ -375,6 +379,20 @@ Starting an IPC server as root may cause the server path to be inaccessible for
unprivileged users. Using `readableAll` and `writableAll` will make the server
accessible for all users.

If the `signal` option is enabled, calling `.abort()` on the corresponding
`AbortController` is similar to calling `.close()` on the server:

```js
const controller = new AbortController();
server.listen({
host: 'localhost',
port: 80,
signal: controller.signal
});
// Later, when you want to close the server.
controller.abort();
```

#### `server.listen(path[, backlog][, callback])`
<!-- YAML
added: v0.1.90
Expand Down
2 changes: 1 addition & 1 deletion lib/internal/streams/add-abort-signal.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const eos = require('internal/streams/end-of-stream');
const { ERR_INVALID_ARG_TYPE } = codes;

// This method is inlined here for readable-stream
// It also does not allow for signal to not exist on the steam
// It also does not allow for signal to not exist on the stream
// https://github.com/nodejs/node/pull/36061#discussion_r533718029
const validateAbortSignal = (signal, name) => {
if (typeof signal !== 'object' ||
Expand Down
18 changes: 18 additions & 0 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const {
} = require('internal/errors');
const { isUint8Array } = require('internal/util/types');
const {
validateAbortSignal,
validateInt32,
validatePort,
validateString
Expand Down Expand Up @@ -1148,6 +1149,22 @@ function afterConnect(status, handle, req, readable, writable) {
}
}

function addAbortSignalOption(self, options) {
if (options?.signal === undefined) {
return;
}
validateAbortSignal(options.signal, 'options.signal');
const { signal } = options;
const onAborted = () => {
self.close();
};
if (signal.aborted) {
process.nextTick(onAborted);
} else {
signal.addEventListener('abort', onAborted);
self.once('close', () => signal.removeEventListener('abort', onAborted));
}
}

function Server(options, connectionListener) {
if (!(this instanceof Server))
Expand Down Expand Up @@ -1398,6 +1415,7 @@ Server.prototype.listen = function(...args) {
listenInCluster(this, null, -1, -1, backlogFromArgs);
return this;
}
addAbortSignalOption(this, options);
// (handle[, backlog][, cb]) where handle is an object with a fd
if (typeof options.fd === 'number' && options.fd >= 0) {
listenInCluster(this, null, null, null, backlogFromArgs, options.fd);
Expand Down
33 changes: 33 additions & 0 deletions test/parallel/test-net-server-listen-options-signal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');

{
// Test bad signal.
const server = net.createServer();
assert.throws(
() => server.listen({ port: 0, signal: 'INVALID_SIGNAL' }),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError'
});
}

{
// Test close.
const server = net.createServer();
const controller = new AbortController();
server.on('close', common.mustCall());
server.listen({ port: 0, signal: controller.signal });
controller.abort();
}

{
// Test close with pre-aborted signal.
const server = net.createServer();
const controller = new AbortController();
controller.abort();
server.on('close', common.mustCall());
server.listen({ port: 0, signal: controller.signal });
}

0 comments on commit 51b4367

Please sign in to comment.