Skip to content

Commit df8024e

Browse files
ShogunPandaaduh95
authored andcommitted
net: add new options to net.Socket and net.Server
PR-URL: #41310 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 1e47815 commit df8024e

File tree

6 files changed

+202
-13
lines changed

6 files changed

+202
-13
lines changed

doc/api/http.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2851,6 +2851,16 @@ changes:
28512851
[`--max-http-header-size`][] for requests received by this server, i.e.
28522852
the maximum length of request headers in bytes.
28532853
**Default:** 16384 (16 KB).
2854+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's
2855+
algorithm immediately after a new incoming connection is received.
2856+
**Default:** `false`.
2857+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality
2858+
on the socket immediately after a new incoming connection is received,
2859+
similarly on what is done in \[`socket.setKeepAlive([enable][, initialDelay])`]\[`socket.setKeepAlive(enable, initialDelay)`].
2860+
**Default:** `false`.
2861+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the
2862+
initial delay before the first keepalive probe is sent on an idle socket.
2863+
**Default:** `0`.
28542864

28552865
* `requestListener` {Function}
28562866

@@ -3092,6 +3102,8 @@ changes:
30923102
* `callback` {Function}
30933103
* Returns: {http.ClientRequest}
30943104

3105+
`options` in [`socket.connect()`][] are also supported.
3106+
30953107
Node.js maintains several connections per server to make HTTP requests.
30963108
This function allows one to transparently issue requests.
30973109

doc/api/net.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,14 @@ For TCP connections, available `options` are:
826826
`0` indicates that both IPv4 and IPv6 addresses are allowed. **Default:** `0`.
827827
* `hints` {number} Optional [`dns.lookup()` hints][].
828828
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
829+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's algorithm immediately
830+
after the socket is established. **Default:** `false`.
831+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality on the socket
832+
immediately after the connection is established, similarly on what is done in
833+
[`socket.setKeepAlive([enable][, initialDelay])`][`socket.setKeepAlive(enable, initialDelay)`].
834+
**Default:** `false`.
835+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the initial delay before
836+
the first keepalive probe is sent on an idle socket.**Default:** `0`.
829837

830838
For [IPC][] connections, available `options` are:
831839

@@ -1381,8 +1389,18 @@ added: v0.5.0
13811389
**Default:** `false`.
13821390
* `pauseOnConnect` {boolean} Indicates whether the socket should be
13831391
paused on incoming connections. **Default:** `false`.
1392+
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's algorithm immediately
1393+
after a new incoming connection is received. **Default:** `false`.
1394+
* `keepAlive` {boolean} If set to `true`, it enables keep-alive functionality on the socket
1395+
immediately after a new incoming connection is received, similarly on what is done in
1396+
[`socket.setKeepAlive([enable][, initialDelay])`][`socket.setKeepAlive(enable, initialDelay)`].
1397+
**Default:** `false`.
1398+
* `keepAliveInitialDelay` {number} If set to a positive number, it sets the initial delay before
1399+
the first keepalive probe is sent on an idle socket.**Default:** `0`.
1400+
13841401
* `connectionListener` {Function} Automatically set as a listener for the
13851402
[`'connection'`][] event.
1403+
13861404
* Returns: {net.Server}
13871405

13881406
Creates a new TCP or [IPC][] server.
@@ -1548,6 +1566,7 @@ net.isIPv6('fhqwhgads'); // returns false
15481566
[`socket.pause()`]: #socketpause
15491567
[`socket.resume()`]: #socketresume
15501568
[`socket.setEncoding()`]: #socketsetencodingencoding
1569+
[`socket.setKeepAlive(enable, initialDelay)`]: #socketsetkeepaliveenable-initialdelay
15511570
[`socket.setTimeout()`]: #socketsettimeouttimeout-callback
15521571
[`socket.setTimeout(timeout)`]: #socketsettimeouttimeout-callback
15531572
[`writable.destroy()`]: stream.md#writabledestroyerror

lib/_http_server.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,11 @@ function Server(options, requestListener) {
380380
}
381381

382382
storeHTTPOptions.call(this, options);
383-
net.Server.call(this, { allowHalfOpen: true });
383+
net.Server.call(
384+
this,
385+
{ allowHalfOpen: true, noDelay: options.noDelay,
386+
keepAlive: options.keepAlive,
387+
keepAliveInitialDelay: options.keepAliveInitialDelay });
384388

385389
if (requestListener) {
386390
this.on('request', requestListener);

lib/net.js

Lines changed: 61 additions & 12 deletions
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

@@ -503,31 +517,38 @@ Socket.prototype._onTimeout = function() {
503517

504518

505519
Socket.prototype.setNoDelay = function(enable) {
520+
// Backwards compatibility: assume true when `enable` is omitted
521+
enable = Boolean(enable === undefined ? true : enable);
522+
506523
if (!this._handle) {
507-
this.once('connect',
508-
enable ? this.setNoDelay : () => this.setNoDelay(enable));
524+
this[kSetNoDelay] = enable;
509525
return this;
510526
}
511527

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

519533
return this;
520534
};
521535

522536

523-
Socket.prototype.setKeepAlive = function(setting, msecs) {
537+
Socket.prototype.setKeepAlive = function(enable, initialDelayMsecs) {
538+
enable = Boolean(enable);
539+
const initialDelay = ~~(initialDelayMsecs / 1000);
540+
524541
if (!this._handle) {
525-
this.once('connect', () => this.setKeepAlive(setting, msecs));
542+
this[kSetKeepAlive] = enable;
543+
this[kSetKeepAliveInitialDelay] = initialDelay;
526544
return this;
527545
}
528546

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

532553
return this;
533554
};
@@ -1140,6 +1161,14 @@ function afterConnect(status, handle, req, readable, writable) {
11401161
}
11411162
self._unrefTimer();
11421163

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

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

12071245
this._connections = 0;
12081246

@@ -1214,6 +1252,9 @@ function Server(options, connectionListener) {
12141252

12151253
this.allowHalfOpen = options.allowHalfOpen || false;
12161254
this.pauseOnConnect = !!options.pauseOnConnect;
1255+
this.noDelay = Boolean(options.noDelay);
1256+
this.keepAlive = Boolean(options.keepAlive);
1257+
this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000);
12171258
}
12181259
ObjectSetPrototypeOf(Server.prototype, EventEmitter.prototype);
12191260
ObjectSetPrototypeOf(Server, EventEmitter);
@@ -1565,6 +1606,14 @@ function onconnection(err, clientHandle) {
15651606
writable: true
15661607
});
15671608

1609+
if (self.noDelay && handle.setNoDelay) {
1610+
handle.setNoDelay(true);
1611+
}
1612+
1613+
if (self.keepAlive && self.setKeepAlive) {
1614+
handle.setKeepAlive(true, handle.keepAliveInitialDelay);
1615+
}
1616+
15681617
self._connections++;
15691618
socket.server = self;
15701619
socket._server = self;
Lines changed: 56 additions & 0 deletions
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+
}
Lines changed: 49 additions & 0 deletions
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)