Skip to content

Commit 7c9b51e

Browse files
committed
net: add new options to net.Socket and net.Server
1 parent e4aa575 commit 7c9b51e

File tree

6 files changed

+192
-6
lines changed

6 files changed

+192
-6
lines changed

doc/api/http.md

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

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

854862
For [IPC][] connections, available `options` are:
855863

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

14121430
Creates a new TCP or [IPC][] server.
@@ -1572,6 +1590,7 @@ net.isIPv6('fhqwhgads'); // returns false
15721590
[`socket.pause()`]: #socketpause
15731591
[`socket.resume()`]: #socketresume
15741592
[`socket.setEncoding()`]: #socketsetencodingencoding
1593+
[`socket.setKeepAlive(enable, initialDelay)`]: #socketsetkeepaliveenable-initialdelay
15751594
[`socket.setTimeout()`]: #socketsettimeouttimeout-callback
15761595
[`socket.setTimeout(timeout)`]: #socketsettimeouttimeout-callback
15771596
[`writable.destroy()`]: stream.md#writabledestroyerror

lib/_http_server.js

Lines changed: 5 additions & 1 deletion
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

Lines changed: 51 additions & 5 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

@@ -520,14 +534,18 @@ Socket.prototype.setNoDelay = function(enable) {
520534
};
521535

522536

523-
Socket.prototype.setKeepAlive = function(setting, msecs) {
537+
Socket.prototype.setKeepAlive = function(enable, initialDelayMsecs) {
524538
if (!this._handle) {
525-
this.once('connect', () => this.setKeepAlive(setting, msecs));
539+
this.once('connect', () => this.setKeepAlive(enable, initialDelayMsecs));
526540
return this;
527541
}
528542

529-
if (this._handle.setKeepAlive)
530-
this._handle.setKeepAlive(setting, ~~(msecs / 1000));
543+
if (this._handle.setKeepAlive && enable !== this[kSetKeepAlive]) {
544+
const initialDelay = ~~(initialDelayMsecs / 1000);
545+
this[kSetKeepAlive] = enable;
546+
this[kSetKeepAliveInitialDelay] = initialDelay;
547+
this._handle.setKeepAlive(enable, initialDelay);
548+
}
531549

532550
return this;
533551
};
@@ -1140,6 +1158,14 @@ function afterConnect(status, handle, req, readable, writable) {
11401158
}
11411159
self._unrefTimer();
11421160

1161+
if (self[kSetNoDelay] && self._handle.setNoDelay) {
1162+
self._handle.setNoDelay(true);
1163+
}
1164+
1165+
if (self[kSetKeepAlive] && self._handle.setKeepAlive) {
1166+
self._handle.setKeepAlive(true, self[kSetKeepAliveInitialDelay]);
1167+
}
1168+
11431169
self.emit('connect');
11441170
self.emit('ready');
11451171

@@ -1203,6 +1229,15 @@ function Server(options, connectionListener) {
12031229
} else {
12041230
throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
12051231
}
1232+
if (typeof options.keepAliveInitialDelay !== 'undefined') {
1233+
validateNumber(
1234+
options.keepAliveInitialDelay, 'options.keepAliveInitialDelay'
1235+
);
1236+
1237+
if (options.keepAliveInitialDelay < 0) {
1238+
options.keepAliveInitialDelay = 0;
1239+
}
1240+
}
12061241

12071242
this._connections = 0;
12081243

@@ -1214,6 +1249,9 @@ function Server(options, connectionListener) {
12141249

12151250
this.allowHalfOpen = options.allowHalfOpen || false;
12161251
this.pauseOnConnect = !!options.pauseOnConnect;
1252+
this.noDelay = Boolean(options.noDelay);
1253+
this.keepAlive = Boolean(options.keepAlive);
1254+
this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000);
12171255
}
12181256
ObjectSetPrototypeOf(Server.prototype, EventEmitter.prototype);
12191257
ObjectSetPrototypeOf(Server, EventEmitter);
@@ -1565,6 +1603,14 @@ function onconnection(err, clientHandle) {
15651603
writable: true
15661604
});
15671605

1606+
if (self.noDelay && handle.setNoDelay) {
1607+
handle.setNoDelay(true);
1608+
}
1609+
1610+
if (self.keepAlive && self.setKeepAlive) {
1611+
handle.setKeepAlive(true, handle.keepAliveInitialDelay);
1612+
}
1613+
15681614
self._connections++;
15691615
socket.server = self;
15701616
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)