2222'use strict' ;
2323
2424const {
25+ NumberIsNaN,
2526 ObjectKeys,
2627 ObjectSetPrototypeOf,
2728 ObjectValues,
@@ -34,7 +35,15 @@ let debug = require('internal/util/debuglog').debuglog('http', (fn) => {
3435 debug = fn ;
3536} ) ;
3637const { 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+
3745const kOnKeylog = Symbol ( 'onkeylog' ) ;
46+ const kRequestOptions = Symbol ( 'requestOptions' ) ;
3847// New Agent code.
3948
4049// The largest departure from the previous implementation is that
@@ -81,6 +90,17 @@ function Agent(options) {
8190 this . keepAlive = this . options . keepAlive || false ;
8291 this . maxSockets = this . options . maxSockets || Agent . defaultMaxSockets ;
8392 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+ }
84104
85105 this . on ( 'free' , ( socket , options ) => {
86106 const name = this . getName ( options ) ;
@@ -113,7 +133,9 @@ function Agent(options) {
113133 if ( this . sockets [ name ] )
114134 count += this . sockets [ name ] . length ;
115135
116- if ( count > this . maxSockets || freeLen >= this . maxFreeSockets ) {
136+ if ( this . totalSocketCount > this . maxTotalSockets ||
137+ count > this . maxSockets ||
138+ freeLen >= this . maxFreeSockets ) {
117139 socket . destroy ( ) ;
118140 } else if ( this . keepSocketAlive ( socket ) ) {
119141 freeSockets = freeSockets || [ ] ;
@@ -236,7 +258,9 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
236258 this . reuseSocket ( socket , req ) ;
237259 setRequestSocket ( this , req , socket ) ;
238260 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 ) {
240264 debug ( 'call onSocket' , sockLen , freeLen ) ;
241265 // If we are under maxSockets create a new one.
242266 this . createSocket ( req , options , handleSocketCreation ( this , req , true ) ) ;
@@ -246,6 +270,10 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
246270 if ( ! this . requests [ name ] ) {
247271 this . requests [ name ] = [ ] ;
248272 }
273+
274+ // Used to create sockets for pending requests from different origin
275+ req [ kRequestOptions ] = options ;
276+
249277 this . requests [ name ] . push ( req ) ;
250278 }
251279} ;
@@ -275,7 +303,8 @@ Agent.prototype.createSocket = function createSocket(req, options, cb) {
275303 this . sockets [ name ] = [ ] ;
276304 }
277305 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 ) ;
279308 installListeners ( this , s , options ) ;
280309 cb ( null , s ) ;
281310 } ;
@@ -376,17 +405,38 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
376405 // Don't leak
377406 if ( sockets [ name ] . length === 0 )
378407 delete sockets [ name ] ;
408+ this . totalSocketCount -- ;
379409 }
380410 }
381411 }
382412
413+ let req ;
383414 if ( this . requests [ name ] && this . requests [ name ] . length ) {
384415 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 ;
386435 // If we have pending requests and a socket gets closed make a new one
387436 const socketCreationHandler = handleSocketCreation ( this , req , false ) ;
388437 this . createSocket ( req , options , socketCreationHandler ) ;
389438 }
439+
390440} ;
391441
392442Agent . prototype . keepSocketAlive = function keepSocketAlive ( socket ) {
0 commit comments