Skip to content

Commit 45b5ca8

Browse files
authored
net: add new options to net.Socket and net.Server
PR-URL: #41310 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent c071bd5 commit 45b5ca8

File tree

6 files changed

+202
-13
lines changed

6 files changed

+202
-13
lines changed

doc/api/http.md

+12
Original file line numberDiff line numberDiff line change
@@ -2865,6 +2865,16 @@ changes:
28652865
[`--max-http-header-size`][] for requests received by this server, i.e.
28662866
the maximum length of request headers in bytes.
28672867
**Default:** 16384 (16 KB).
2868+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's
2869+
algorithm immediately after a new incoming connection is received.
2870+
**Default:** `false`.
2871+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality
2872+
on the socket immediately after a new incoming connection is received,
2873+
similarly on what is done in \[`socket.setKeepAlive([enable][, initialDelay])`]\[`socket.setKeepAlive(enable, initialDelay)`].
2874+
**Default:** `false`.
2875+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the
2876+
initial delay before the first keepalive probe is sent on an idle socket.
2877+
**Default:** `0`.
28682878

28692879
* `requestListener` {Function}
28702880

@@ -3108,6 +3118,8 @@ changes:
31083118
* `callback` {Function}
31093119
* Returns: {http.ClientRequest}
31103120

3121+
`options` in [`socket.connect()`][] are also supported.
3122+
31113123
Node.js maintains several connections per server to make HTTP requests.
31123124
This function allows one to transparently issue requests.
31133125

doc/api/net.md

+19
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,14 @@ For TCP connections, available `options` are:
854854
`0` indicates that both IPv4 and IPv6 addresses are allowed. **Default:** `0`.
855855
* `hints` {number} Optional [`dns.lookup()` hints][].
856856
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
857+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's algorithm immediately
858+
after the socket is established. **Default:** `false`.
859+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality on the socket
860+
immediately after the connection is established, similarly on what is done in
861+
[`socket.setKeepAlive([enable][, initialDelay])`][`socket.setKeepAlive(enable, initialDelay)`].
862+
**Default:** `false`.
863+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the initial delay before
864+
the first keepalive probe is sent on an idle socket.**Default:** `0`.
857865

858866
For [IPC][] connections, available `options` are:
859867

@@ -1415,8 +1423,18 @@ added: v0.5.0
14151423
**Default:** `false`.
14161424
* `pauseOnConnect` {boolean} Indicates whether the socket should be
14171425
paused on incoming connections. **Default:** `false`.
1426+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's algorithm immediately
1427+
after a new incoming connection is received. **Default:** `false`.
1428+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality on the socket
1429+
immediately after a new incoming connection is received, similarly on what is done in
1430+
[`socket.setKeepAlive([enable][, initialDelay])`][`socket.setKeepAlive(enable, initialDelay)`].
1431+
**Default:** `false`.
1432+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the initial delay before
1433+
the first keepalive probe is sent on an idle socket.**Default:** `0`.
1434+
14181435
* `connectionListener` {Function} Automatically set as a listener for the
14191436
[`'connection'`][] event.
1437+
14201438
* Returns: {net.Server}
14211439

14221440
Creates a new TCP or [IPC][] server.
@@ -1582,6 +1600,7 @@ net.isIPv6('fhqwhgads'); // returns false
15821600
[`socket.pause()`]: #socketpause
15831601
[`socket.resume()`]: #socketresume
15841602
[`socket.setEncoding()`]: #socketsetencodingencoding
1603+
[`socket.setKeepAlive(enable, initialDelay)`]: #socketsetkeepaliveenable-initialdelay
15851604
[`socket.setTimeout()`]: #socketsettimeouttimeout-callback
15861605
[`socket.setTimeout(timeout)`]: #socketsettimeouttimeout-callback
15871606
[`writable.destroy()`]: stream.md#writabledestroyerror

lib/_http_server.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,11 @@ function Server(options, requestListener) {
378378
}
379379

380380
storeHTTPOptions.call(this, options);
381-
net.Server.call(this, { allowHalfOpen: true });
381+
net.Server.call(
382+
this,
383+
{ allowHalfOpen: true, noDelay: options.noDelay,
384+
keepAlive: options.keepAlive,
385+
keepAliveInitialDelay: options.keepAliveInitialDelay });
382386

383387
if (requestListener) {
384388
this.on('request', requestListener);

lib/net.js

+61-12
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ function initSocketHandle(self) {
279279
const kBytesRead = Symbol('kBytesRead');
280280
const kBytesWritten = Symbol('kBytesWritten');
281281
const kSetNoDelay = Symbol('kSetNoDelay');
282+
const kSetKeepAlive = Symbol('kSetKeepAlive');
283+
const kSetKeepAliveInitialDelay = Symbol('kSetKeepAliveInitialDelay');
282284

283285
function Socket(options) {
284286
if (!(this instanceof Socket)) return new Socket(options);
@@ -297,6 +299,15 @@ function Socket(options) {
297299
'is not supported'
298300
);
299301
}
302+
if (typeof options?.keepAliveInitialDelay !== 'undefined') {
303+
validateNumber(
304+
options?.keepAliveInitialDelay, 'options.keepAliveInitialDelay'
305+
);
306+
307+
if (options.keepAliveInitialDelay < 0) {
308+
options.keepAliveInitialDelay = 0;
309+
}
310+
}
300311

301312
this.connecting = false;
302313
// Problem with this is that users can supply their own handle, that may not
@@ -307,7 +318,6 @@ function Socket(options) {
307318
this[kHandle] = null;
308319
this._parent = null;
309320
this._host = null;
310-
this[kSetNoDelay] = false;
311321
this[kLastWriteQueueSize] = 0;
312322
this[kTimeout] = null;
313323
this[kBuffer] = null;
@@ -381,6 +391,10 @@ function Socket(options) {
381391
this[kBufferCb] = onread.callback;
382392
}
383393

394+
this[kSetNoDelay] = Boolean(options.noDelay);
395+
this[kSetKeepAlive] = Boolean(options.keepAlive);
396+
this[kSetKeepAliveInitialDelay] = ~~(options.keepAliveInitialDelay / 1000);
397+
384398
// Shut down the socket when we're finished with it.
385399
this.on('end', onReadableStreamEnd);
386400

@@ -504,31 +518,38 @@ Socket.prototype._onTimeout = function() {
504518

505519

506520
Socket.prototype.setNoDelay = function(enable) {
521+
// Backwards compatibility: assume true when `enable` is omitted
522+
enable = Boolean(enable === undefined ? true : enable);
523+
507524
if (!this._handle) {
508-
this.once('connect',
509-
enable ? this.setNoDelay : () => this.setNoDelay(enable));
525+
this[kSetNoDelay] = enable;
510526
return this;
511527
}
512528

513-
// Backwards compatibility: assume true when `enable` is omitted
514-
const newValue = enable === undefined ? true : !!enable;
515-
if (this._handle.setNoDelay && newValue !== this[kSetNoDelay]) {
516-
this[kSetNoDelay] = newValue;
517-
this._handle.setNoDelay(newValue);
529+
if (this._handle.setNoDelay && enable !== this[kSetNoDelay]) {
530+
this[kSetNoDelay] = enable;
531+
this._handle.setNoDelay(enable);
518532
}
519533

520534
return this;
521535
};
522536

523537

524-
Socket.prototype.setKeepAlive = function(setting, msecs) {
538+
Socket.prototype.setKeepAlive = function(enable, initialDelayMsecs) {
539+
enable = Boolean(enable);
540+
const initialDelay = ~~(initialDelayMsecs / 1000);
541+
525542
if (!this._handle) {
526-
this.once('connect', () => this.setKeepAlive(setting, msecs));
543+
this[kSetKeepAlive] = enable;
544+
this[kSetKeepAliveInitialDelay] = initialDelay;
527545
return this;
528546
}
529547

530-
if (this._handle.setKeepAlive)
531-
this._handle.setKeepAlive(setting, ~~(msecs / 1000));
548+
if (this._handle.setKeepAlive && enable !== this[kSetKeepAlive]) {
549+
this[kSetKeepAlive] = enable;
550+
this[kSetKeepAliveInitialDelay] = initialDelay;
551+
this._handle.setKeepAlive(enable, initialDelay);
552+
}
532553

533554
return this;
534555
};
@@ -1141,6 +1162,14 @@ function afterConnect(status, handle, req, readable, writable) {
11411162
}
11421163
self._unrefTimer();
11431164

1165+
if (self[kSetNoDelay] && self._handle.setNoDelay) {
1166+
self._handle.setNoDelay(true);
1167+
}
1168+
1169+
if (self[kSetKeepAlive] && self._handle.setKeepAlive) {
1170+
self._handle.setKeepAlive(true, self[kSetKeepAliveInitialDelay]);
1171+
}
1172+
11441173
self.emit('connect');
11451174
self.emit('ready');
11461175

@@ -1204,6 +1233,15 @@ function Server(options, connectionListener) {
12041233
} else {
12051234
throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
12061235
}
1236+
if (typeof options.keepAliveInitialDelay !== 'undefined') {
1237+
validateNumber(
1238+
options.keepAliveInitialDelay, 'options.keepAliveInitialDelay'
1239+
);
1240+
1241+
if (options.keepAliveInitialDelay < 0) {
1242+
options.keepAliveInitialDelay = 0;
1243+
}
1244+
}
12071245

12081246
this._connections = 0;
12091247

@@ -1215,6 +1253,9 @@ function Server(options, connectionListener) {
12151253

12161254
this.allowHalfOpen = options.allowHalfOpen || false;
12171255
this.pauseOnConnect = !!options.pauseOnConnect;
1256+
this.noDelay = Boolean(options.noDelay);
1257+
this.keepAlive = Boolean(options.keepAlive);
1258+
this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000);
12181259
}
12191260
ObjectSetPrototypeOf(Server.prototype, EventEmitter.prototype);
12201261
ObjectSetPrototypeOf(Server, EventEmitter);
@@ -1567,6 +1608,14 @@ function onconnection(err, clientHandle) {
15671608
writable: true
15681609
});
15691610

1611+
if (self.noDelay && handle.setNoDelay) {
1612+
handle.setNoDelay(true);
1613+
}
1614+
1615+
if (self.keepAlive && self.setKeepAlive) {
1616+
handle.setKeepAlive(true, handle.keepAliveInitialDelay);
1617+
}
1618+
15701619
self._connections++;
15711620
socket.server = self;
15721621
socket._server = self;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const net = require('net');
6+
7+
const truthyValues = [true, 1, 'true', {}, []];
8+
const delays = [[123, 0], [456123, 456], [-123000, 0], [undefined, 0]];
9+
const falseyValues = [false, 0, ''];
10+
11+
const genSetKeepAlive = (desiredEnable, desiredDelay) => (enable, delay) => {
12+
assert.strictEqual(enable, desiredEnable);
13+
assert.strictEqual(delay, desiredDelay);
14+
};
15+
16+
for (const value of truthyValues) {
17+
for (const delay of delays) {
18+
const server = net.createServer();
19+
20+
server.listen(0, common.mustCall(function() {
21+
const port = server.address().port;
22+
23+
const client = net.connect(
24+
{ port, keepAlive: value, keepAliveInitialDelay: delay[0] },
25+
common.mustCall(() => client.end())
26+
);
27+
28+
client._handle.setKeepAlive = common.mustCall(
29+
genSetKeepAlive(true, delay[1])
30+
);
31+
32+
client.on('end', common.mustCall(function() {
33+
server.close();
34+
}));
35+
}));
36+
}
37+
}
38+
39+
for (const value of falseyValues) {
40+
const server = net.createServer();
41+
42+
server.listen(0, common.mustCall(function() {
43+
const port = server.address().port;
44+
45+
const client = net.connect(
46+
{ port, keepAlive: value },
47+
common.mustCall(() => client.end())
48+
);
49+
50+
client._handle.setKeepAlive = common.mustNotCall();
51+
52+
client.on('end', common.mustCall(function() {
53+
server.close();
54+
}));
55+
}));
56+
}
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const net = require('net');
6+
7+
const truthyValues = [true, 1, 'true', {}, []];
8+
const falseyValues = [false, 0, ''];
9+
const genSetNoDelay = (desiredArg) => (enable) => {
10+
assert.strictEqual(enable, desiredArg);
11+
};
12+
13+
for (const value of truthyValues) {
14+
const server = net.createServer();
15+
16+
server.listen(0, common.mustCall(function() {
17+
const port = server.address().port;
18+
19+
const client = net.connect(
20+
{ port, noDelay: value },
21+
common.mustCall(() => client.end())
22+
);
23+
24+
client._handle.setNoDelay = common.mustCall(genSetNoDelay(true));
25+
26+
client.on('end', common.mustCall(function() {
27+
server.close();
28+
}));
29+
}));
30+
}
31+
32+
for (const value of falseyValues) {
33+
const server = net.createServer();
34+
35+
server.listen(0, common.mustCall(function() {
36+
const port = server.address().port;
37+
38+
const client = net.connect(
39+
{ port, noDelay: value },
40+
common.mustCall(() => client.end())
41+
);
42+
43+
client._handle.setNoDelay = common.mustNotCall();
44+
45+
client.on('end', common.mustCall(function() {
46+
server.close();
47+
}));
48+
}));
49+
}

0 commit comments

Comments
 (0)