Skip to content

Commit a2926c4

Browse files
ShogunPandasxa
authored andcommitted
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 3789d66 commit a2926c4

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
@@ -2857,6 +2857,16 @@ changes:
28572857
[`--max-http-header-size`][] for requests received by this server, i.e.
28582858
the maximum length of request headers in bytes.
28592859
**Default:** 16384 (16 KB).
2860+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's
2861+
algorithm immediately after a new incoming connection is received.
2862+
**Default:** `false`.
2863+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality
2864+
on the socket immediately after a new incoming connection is received,
2865+
similarly on what is done in \[`socket.setKeepAlive([enable][, initialDelay])`]\[`socket.setKeepAlive(enable, initialDelay)`].
2866+
**Default:** `false`.
2867+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the
2868+
initial delay before the first keepalive probe is sent on an idle socket.
2869+
**Default:** `0`.
28602870

28612871
* `requestListener` {Function}
28622872

@@ -3100,6 +3110,8 @@ changes:
31003110
* `callback` {Function}
31013111
* Returns: {http.ClientRequest}
31023112

3113+
`options` in [`socket.connect()`][] are also supported.
3114+
31033115
Node.js maintains several connections per server to make HTTP requests.
31043116
This function allows one to transparently issue requests.
31053117

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

@@ -1409,8 +1417,18 @@ added: v0.5.0
14091417
**Default:** `false`.
14101418
* `pauseOnConnect` {boolean} Indicates whether the socket should be
14111419
paused on incoming connections. **Default:** `false`.
1420+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's algorithm immediately
1421+
after a new incoming connection is received. **Default:** `false`.
1422+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality on the socket
1423+
immediately after a new incoming connection is received, similarly on what is done in
1424+
[`socket.setKeepAlive([enable][, initialDelay])`][`socket.setKeepAlive(enable, initialDelay)`].
1425+
**Default:** `false`.
1426+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the initial delay before
1427+
the first keepalive probe is sent on an idle socket.**Default:** `0`.
1428+
14121429
* `connectionListener` {Function} Automatically set as a listener for the
14131430
[`'connection'`][] event.
1431+
14141432
* Returns: {net.Server}
14151433

14161434
Creates a new TCP or [IPC][] server.
@@ -1576,6 +1594,7 @@ net.isIPv6('fhqwhgads'); // returns false
15761594
[`socket.pause()`]: #socketpause
15771595
[`socket.resume()`]: #socketresume
15781596
[`socket.setEncoding()`]: #socketsetencodingencoding
1597+
[`socket.setKeepAlive(enable, initialDelay)`]: #socketsetkeepaliveenable-initialdelay
15791598
[`socket.setTimeout()`]: #socketsettimeouttimeout-callback
15801599
[`socket.setTimeout(timeout)`]: #socketsettimeouttimeout-callback
15811600
[`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)