@@ -27,7 +27,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
27
27
} ;
28
28
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
29
29
exports . Agent = void 0 ;
30
+ const net = __importStar ( require ( "net" ) ) ;
30
31
const http = __importStar ( require ( "http" ) ) ;
32
+ const https_1 = require ( "https" ) ;
31
33
__exportStar ( require ( "./helpers" ) , exports ) ;
32
34
const INTERNAL = Symbol ( 'AgentBaseInternalState' ) ;
33
35
class Agent extends http . Agent {
@@ -64,22 +66,83 @@ class Agent extends http.Agent {
64
66
. some ( ( l ) => l . indexOf ( '(https.js:' ) !== - 1 ||
65
67
l . indexOf ( 'node:https:' ) !== - 1 ) ;
66
68
}
69
+ // In order to support async signatures in `connect()` and Node's native
70
+ // connection pooling in `http.Agent`, the array of sockets for each origin
71
+ // has to be updated synchronously. This is so the length of the array is
72
+ // accurate when `addRequest()` is next called. We achieve this by creating a
73
+ // fake socket and adding it to `sockets[origin]` and incrementing
74
+ // `totalSocketCount`.
75
+ incrementSockets ( name ) {
76
+ // If `maxSockets` and `maxTotalSockets` are both Infinity then there is no
77
+ // need to create a fake socket because Node.js native connection pooling
78
+ // will never be invoked.
79
+ if ( this . maxSockets === Infinity && this . maxTotalSockets === Infinity ) {
80
+ return null ;
81
+ }
82
+ // All instances of `sockets` are expected TypeScript errors. The
83
+ // alternative is to add it as a private property of this class but that
84
+ // will break TypeScript subclassing.
85
+ if ( ! this . sockets [ name ] ) {
86
+ // @ts -expect-error `sockets` is readonly in `@types/node`
87
+ this . sockets [ name ] = [ ] ;
88
+ }
89
+ const fakeSocket = new net . Socket ( { writable : false } ) ;
90
+ this . sockets [ name ] . push ( fakeSocket ) ;
91
+ // @ts -expect-error `totalSocketCount` isn't defined in `@types/node`
92
+ this . totalSocketCount ++ ;
93
+ return fakeSocket ;
94
+ }
95
+ decrementSockets ( name , socket ) {
96
+ if ( ! this . sockets [ name ] || socket === null ) {
97
+ return ;
98
+ }
99
+ const sockets = this . sockets [ name ] ;
100
+ const index = sockets . indexOf ( socket ) ;
101
+ if ( index !== - 1 ) {
102
+ sockets . splice ( index , 1 ) ;
103
+ // @ts -expect-error `totalSocketCount` isn't defined in `@types/node`
104
+ this . totalSocketCount -- ;
105
+ if ( sockets . length === 0 ) {
106
+ // @ts -expect-error `sockets` is readonly in `@types/node`
107
+ delete this . sockets [ name ] ;
108
+ }
109
+ }
110
+ }
111
+ // In order to properly update the socket pool, we need to call `getName()` on
112
+ // the core `https.Agent` if it is a secureEndpoint.
113
+ getName ( options ) {
114
+ const secureEndpoint = typeof options . secureEndpoint === 'boolean'
115
+ ? options . secureEndpoint
116
+ : this . isSecureEndpoint ( options ) ;
117
+ if ( secureEndpoint ) {
118
+ // @ts -expect-error `getName()` isn't defined in `@types/node`
119
+ return https_1 . Agent . prototype . getName . call ( this , options ) ;
120
+ }
121
+ // @ts -expect-error `getName()` isn't defined in `@types/node`
122
+ return super . getName ( options ) ;
123
+ }
67
124
createSocket ( req , options , cb ) {
68
125
const connectOpts = {
69
126
...options ,
70
127
secureEndpoint : this . isSecureEndpoint ( options ) ,
71
128
} ;
129
+ const name = this . getName ( connectOpts ) ;
130
+ const fakeSocket = this . incrementSockets ( name ) ;
72
131
Promise . resolve ( )
73
132
. then ( ( ) => this . connect ( req , connectOpts ) )
74
133
. then ( ( socket ) => {
134
+ this . decrementSockets ( name , fakeSocket ) ;
75
135
if ( socket instanceof http . Agent ) {
76
136
// @ts -expect-error `addRequest()` isn't defined in `@types/node`
77
137
return socket . addRequest ( req , connectOpts ) ;
78
138
}
79
139
this [ INTERNAL ] . currentSocket = socket ;
80
140
// @ts -expect-error `createSocket()` isn't defined in `@types/node`
81
141
super . createSocket ( req , options , cb ) ;
82
- } , cb ) ;
142
+ } , ( err ) => {
143
+ this . decrementSockets ( name , fakeSocket ) ;
144
+ cb ( err ) ;
145
+ } ) ;
83
146
}
84
147
createConnection ( ) {
85
148
const socket = this [ INTERNAL ] . currentSocket ;
0 commit comments