22
22
'use strict' ;
23
23
24
24
const {
25
+ NumberIsNaN,
25
26
ObjectKeys,
26
27
ObjectSetPrototypeOf,
27
28
ObjectValues,
@@ -36,11 +37,14 @@ const {
36
37
codes : {
37
38
ERR_INVALID_ARG_TYPE ,
38
39
ERR_INVALID_OPT_VALUE ,
40
+ ERR_OUT_OF_RANGE ,
39
41
} ,
40
42
} = require ( 'internal/errors' ) ;
41
43
const { once } = require ( 'internal/util' ) ;
44
+ const { validateNumber } = require ( 'internal/validators' ) ;
42
45
43
46
const kOnKeylog = Symbol ( 'onkeylog' ) ;
47
+ const kRequestOptions = Symbol ( 'requestOptions' ) ;
44
48
// New Agent code.
45
49
46
50
// The largest departure from the previous implementation is that
@@ -88,11 +92,22 @@ function Agent(options) {
88
92
this . maxSockets = this . options . maxSockets || Agent . defaultMaxSockets ;
89
93
this . maxFreeSockets = this . options . maxFreeSockets || 256 ;
90
94
this . scheduling = this . options . scheduling || 'fifo' ;
95
+ this . maxTotalSockets = this . options . maxTotalSockets ;
96
+ this . totalSocketCount = 0 ;
91
97
92
98
if ( this . scheduling !== 'fifo' && this . scheduling !== 'lifo' ) {
93
99
throw new ERR_INVALID_OPT_VALUE ( 'scheduling' , this . scheduling ) ;
94
100
}
95
101
102
+ if ( this . maxTotalSockets !== undefined ) {
103
+ validateNumber ( this . maxTotalSockets , 'maxTotalSockets' ) ;
104
+ if ( this . maxTotalSockets <= 0 || NumberIsNaN ( this . maxTotalSockets ) )
105
+ throw new ERR_OUT_OF_RANGE ( 'maxTotalSockets' , '> 0' ,
106
+ this . maxTotalSockets ) ;
107
+ } else {
108
+ this . maxTotalSockets = Infinity ;
109
+ }
110
+
96
111
this . on ( 'free' , ( socket , options ) => {
97
112
const name = this . getName ( options ) ;
98
113
debug ( 'agent.on(free)' , name ) ;
@@ -131,7 +146,8 @@ function Agent(options) {
131
146
if ( this . sockets [ name ] )
132
147
count += this . sockets [ name ] . length ;
133
148
134
- if ( count > this . maxSockets ||
149
+ if ( this . totalSocketCount > this . maxTotalSockets ||
150
+ count > this . maxSockets ||
135
151
freeLen >= this . maxFreeSockets ||
136
152
! this . keepSocketAlive ( socket ) ) {
137
153
socket . destroy ( ) ;
@@ -246,7 +262,9 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
246
262
this . reuseSocket ( socket , req ) ;
247
263
setRequestSocket ( this , req , socket ) ;
248
264
this . sockets [ name ] . push ( socket ) ;
249
- } else if ( sockLen < this . maxSockets ) {
265
+ this . totalSocketCount ++ ;
266
+ } else if ( sockLen < this . maxSockets &&
267
+ this . totalSocketCount < this . maxTotalSockets ) {
250
268
debug ( 'call onSocket' , sockLen , freeLen ) ;
251
269
// If we are under maxSockets create a new one.
252
270
this . createSocket ( req , options , ( err , socket ) => {
@@ -261,6 +279,10 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
261
279
if ( ! this . requests [ name ] ) {
262
280
this . requests [ name ] = [ ] ;
263
281
}
282
+
283
+ // Used to create sockets for pending requests from different origin
284
+ req [ kRequestOptions ] = options ;
285
+
264
286
this . requests [ name ] . push ( req ) ;
265
287
}
266
288
} ;
@@ -286,7 +308,8 @@ Agent.prototype.createSocket = function createSocket(req, options, cb) {
286
308
this . sockets [ name ] = [ ] ;
287
309
}
288
310
this . sockets [ name ] . push ( s ) ;
289
- debug ( 'sockets' , name , this . sockets [ name ] . length ) ;
311
+ this . totalSocketCount ++ ;
312
+ debug ( 'sockets' , name , this . sockets [ name ] . length , this . totalSocketCount ) ;
290
313
installListeners ( this , s , options ) ;
291
314
cb ( null , s ) ;
292
315
} ) ;
@@ -392,13 +415,33 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
392
415
// Don't leak
393
416
if ( sockets [ name ] . length === 0 )
394
417
delete sockets [ name ] ;
418
+ this . totalSocketCount -- ;
395
419
}
396
420
}
397
421
}
398
422
423
+ let req ;
399
424
if ( this . requests [ name ] && this . requests [ name ] . length ) {
400
425
debug ( 'removeSocket, have a request, make a socket' ) ;
401
- const req = this . requests [ name ] [ 0 ] ;
426
+ req = this . requests [ name ] [ 0 ] ;
427
+ } else {
428
+ // TODO(rickyes): this logic will not be FIFO across origins.
429
+ // There might be older requests in a different origin, but
430
+ // if the origin which releases the socket has pending requests
431
+ // that will be prioritized.
432
+ for ( const prop in this . requests ) {
433
+ // Check whether this specific origin is already at maxSockets
434
+ if ( this . sockets [ prop ] && this . sockets [ prop ] . length ) break ;
435
+ debug ( 'removeSocket, have a request with different origin,' +
436
+ ' make a socket' ) ;
437
+ req = this . requests [ prop ] [ 0 ] ;
438
+ options = req [ kRequestOptions ] ;
439
+ break ;
440
+ }
441
+ }
442
+
443
+ if ( req && options ) {
444
+ req [ kRequestOptions ] = undefined ;
402
445
// If we have pending requests and a socket gets closed make a new one
403
446
this . createSocket ( req , options , ( err , socket ) => {
404
447
if ( err )
@@ -407,6 +450,7 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
407
450
socket . emit ( 'free' ) ;
408
451
} ) ;
409
452
}
453
+
410
454
} ;
411
455
412
456
Agent . prototype . keepSocketAlive = function keepSocketAlive ( socket ) {
0 commit comments