22
22
'use strict' ;
23
23
24
24
const {
25
+ NumberIsNaN,
25
26
ObjectKeys,
26
27
ObjectSetPrototypeOf,
27
28
ObjectValues,
@@ -34,7 +35,15 @@ let debug = require('internal/util/debuglog').debuglog('http', (fn) => {
34
35
debug = fn ;
35
36
} ) ;
36
37
const { async_id_symbol } = require ( 'internal/async_hooks' ) . symbols ;
38
+ const {
39
+ codes : {
40
+ ERR_OUT_OF_RANGE ,
41
+ } ,
42
+ } = require ( 'internal/errors' ) ;
43
+ const { validateNumber } = require ( 'internal/validators' ) ;
44
+
37
45
const kOnKeylog = Symbol ( 'onkeylog' ) ;
46
+ const kRequestOptions = Symbol ( 'requestOptions' ) ;
38
47
// New Agent code.
39
48
40
49
// The largest departure from the previous implementation is that
@@ -81,6 +90,17 @@ function Agent(options) {
81
90
this . keepAlive = this . options . keepAlive || false ;
82
91
this . maxSockets = this . options . maxSockets || Agent . defaultMaxSockets ;
83
92
this . maxFreeSockets = this . options . maxFreeSockets || 256 ;
93
+ this . maxTotalSockets = this . options . maxTotalSockets ;
94
+ this . totalSocketCount = 0 ;
95
+
96
+ if ( this . maxTotalSockets !== undefined ) {
97
+ validateNumber ( this . maxTotalSockets , 'maxTotalSockets' ) ;
98
+ if ( this . maxTotalSockets <= 0 || NumberIsNaN ( this . maxTotalSockets ) )
99
+ throw new ERR_OUT_OF_RANGE ( 'maxTotalSockets' , '> 0' ,
100
+ this . maxTotalSockets ) ;
101
+ } else {
102
+ this . maxTotalSockets = Infinity ;
103
+ }
84
104
85
105
this . on ( 'free' , ( socket , options ) => {
86
106
const name = this . getName ( options ) ;
@@ -113,7 +133,9 @@ function Agent(options) {
113
133
if ( this . sockets [ name ] )
114
134
count += this . sockets [ name ] . length ;
115
135
116
- if ( count > this . maxSockets || freeLen >= this . maxFreeSockets ) {
136
+ if ( this . totalSocketCount > this . maxTotalSockets ||
137
+ count > this . maxSockets ||
138
+ freeLen >= this . maxFreeSockets ) {
117
139
socket . destroy ( ) ;
118
140
} else if ( this . keepSocketAlive ( socket ) ) {
119
141
freeSockets = freeSockets || [ ] ;
@@ -236,7 +258,9 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
236
258
this . reuseSocket ( socket , req ) ;
237
259
setRequestSocket ( this , req , socket ) ;
238
260
this . sockets [ name ] . push ( socket ) ;
239
- } else if ( sockLen < this . maxSockets ) {
261
+ this . totalSocketCount ++ ;
262
+ } else if ( sockLen < this . maxSockets &&
263
+ this . totalSocketCount < this . maxTotalSockets ) {
240
264
debug ( 'call onSocket' , sockLen , freeLen ) ;
241
265
// If we are under maxSockets create a new one.
242
266
this . createSocket ( req , options , handleSocketCreation ( this , req , true ) ) ;
@@ -246,6 +270,10 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
246
270
if ( ! this . requests [ name ] ) {
247
271
this . requests [ name ] = [ ] ;
248
272
}
273
+
274
+ // Used to create sockets for pending requests from different origin
275
+ req [ kRequestOptions ] = options ;
276
+
249
277
this . requests [ name ] . push ( req ) ;
250
278
}
251
279
} ;
@@ -275,7 +303,8 @@ Agent.prototype.createSocket = function createSocket(req, options, cb) {
275
303
this . sockets [ name ] = [ ] ;
276
304
}
277
305
this . sockets [ name ] . push ( s ) ;
278
- debug ( 'sockets' , name , this . sockets [ name ] . length ) ;
306
+ this . totalSocketCount ++ ;
307
+ debug ( 'sockets' , name , this . sockets [ name ] . length , this . totalSocketCount ) ;
279
308
installListeners ( this , s , options ) ;
280
309
cb ( null , s ) ;
281
310
} ;
@@ -376,17 +405,38 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
376
405
// Don't leak
377
406
if ( sockets [ name ] . length === 0 )
378
407
delete sockets [ name ] ;
408
+ this . totalSocketCount -- ;
379
409
}
380
410
}
381
411
}
382
412
413
+ let req ;
383
414
if ( this . requests [ name ] && this . requests [ name ] . length ) {
384
415
debug ( 'removeSocket, have a request, make a socket' ) ;
385
- const req = this . requests [ name ] [ 0 ] ;
416
+ req = this . requests [ name ] [ 0 ] ;
417
+ } else {
418
+ // TODO(rickyes): this logic will not be FIFO across origins.
419
+ // There might be older requests in a different origin, but
420
+ // if the origin which releases the socket has pending requests
421
+ // that will be prioritized.
422
+ for ( const prop in this . requests ) {
423
+ // Check whether this specific origin is already at maxSockets
424
+ if ( this . sockets [ prop ] && this . sockets [ prop ] . length ) break ;
425
+ debug ( 'removeSocket, have a request with different origin,' +
426
+ ' make a socket' ) ;
427
+ req = this . requests [ prop ] [ 0 ] ;
428
+ options = req [ kRequestOptions ] ;
429
+ break ;
430
+ }
431
+ }
432
+
433
+ if ( req && options ) {
434
+ req [ kRequestOptions ] = undefined ;
386
435
// If we have pending requests and a socket gets closed make a new one
387
436
const socketCreationHandler = handleSocketCreation ( this , req , false ) ;
388
437
this . createSocket ( req , options , socketCreationHandler ) ;
389
438
}
439
+
390
440
} ;
391
441
392
442
Agent . prototype . keepSocketAlive = function keepSocketAlive ( socket ) {
0 commit comments