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 <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 3789d66 commit a2926c4

6 files changed

Lines changed: 202 additions & 13 deletions

File tree

‎doc/api/http.md‎

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

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

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: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ function initSocketHandle(self) {
279279
constkBytesRead=Symbol('kBytesRead');
280280
constkBytesWritten=Symbol('kBytesWritten');
281281
constkSetNoDelay=Symbol('kSetNoDelay');
282+
constkSetKeepAlive=Symbol('kSetKeepAlive');
283+
constkSetKeepAliveInitialDelay=Symbol('kSetKeepAliveInitialDelay');
282284

283285
functionSocket(options){
284286
if(!(thisinstanceofSocket))returnnewSocket(options);
@@ -297,6 +299,15 @@ function Socket(options) {
297299
'is not supported'
298300
);
299301
}
302+
if(typeofoptions?.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
returnthis;
511527
}
512528

513-
// Backwards compatibility: assume true when `enable` is omitted
514-
constnewValue=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
returnthis;
521535
};
522536

523537

524-
Socket.prototype.setKeepAlive=function(setting,msecs){
538+
Socket.prototype.setKeepAlive=function(enable,initialDelayMsecs){
539+
enable=Boolean(enable);
540+
constinitialDelay=~~(initialDelayMsecs/1000);
541+
525542
if(!this._handle){
526-
this.once('connect',()=>this.setKeepAlive(setting,msecs));
543+
this[kSetKeepAlive]=enable;
544+
this[kSetKeepAliveInitialDelay]=initialDelay;
527545
returnthis;
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
returnthis;
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
thrownewERR_INVALID_ARG_TYPE('options','Object',options);
12061235
}
1236+
if(typeofoptions.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;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
3+
constcommon=require('../common');
4+
constassert=require('assert');
5+
constnet=require('net');
6+
7+
consttruthyValues=[true,1,'true',{},[]];
8+
constdelays=[[123,0],[456123,456],[-123000,0],[undefined,0]];
9+
constfalseyValues=[false,0,''];
10+
11+
constgenSetKeepAlive=(desiredEnable,desiredDelay)=>(enable,delay)=>{
12+
assert.strictEqual(enable,desiredEnable);
13+
assert.strictEqual(delay,desiredDelay);
14+
};
15+
16+
for(constvalueoftruthyValues){
17+
for(constdelayofdelays){
18+
constserver=net.createServer();
19+
20+
server.listen(0,common.mustCall(function(){
21+
constport=server.address().port;
22+
23+
constclient=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(constvalueoffalseyValues){
40+
constserver=net.createServer();
41+
42+
server.listen(0,common.mustCall(function(){
43+
constport=server.address().port;
44+
45+
constclient=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+
constcommon=require('../common');
4+
constassert=require('assert');
5+
constnet=require('net');
6+
7+
consttruthyValues=[true,1,'true',{},[]];
8+
constfalseyValues=[false,0,''];
9+
constgenSetNoDelay=(desiredArg)=>(enable)=>{
10+
assert.strictEqual(enable,desiredArg);
11+
};
12+
13+
for(constvalueoftruthyValues){
14+
constserver=net.createServer();
15+
16+
server.listen(0,common.mustCall(function(){
17+
constport=server.address().port;
18+
19+
constclient=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(constvalueoffalseyValues){
33+
constserver=net.createServer();
34+
35+
server.listen(0,common.mustCall(function(){
36+
constport=server.address().port;
37+
38+
constclient=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)