undici 6.6.2 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/{README.md → docs/README.md} +19 -15
  2. package/docs/{api → docs/api}/Dispatcher.md +39 -3
  3. package/docs/docs/api/Fetch.md +57 -0
  4. package/docs/{api → docs/api}/ProxyAgent.md +3 -1
  5. package/docs/docs/api/RetryAgent.md +45 -0
  6. package/docs/{api → docs/api}/RetryHandler.md +1 -1
  7. package/docs/{api → docs/api}/api-lifecycle.md +33 -4
  8. package/docs/{best-practices → docs/best-practices}/proxy.md +6 -6
  9. package/index-fetch.js +9 -8
  10. package/index.js +31 -25
  11. package/lib/core/request.js +72 -135
  12. package/lib/core/symbols.js +6 -5
  13. package/lib/core/tree.js +46 -26
  14. package/lib/core/util.js +41 -20
  15. package/lib/{agent.js → dispatcher/agent.js} +4 -4
  16. package/lib/{balanced-pool.js → dispatcher/balanced-pool.js} +3 -3
  17. package/lib/dispatcher/client-h1.js +1339 -0
  18. package/lib/dispatcher/client-h2.js +639 -0
  19. package/lib/dispatcher/client.js +611 -0
  20. package/lib/{dispatcher-base.js → dispatcher/dispatcher-base.js} +2 -2
  21. package/lib/{pool-base.js → dispatcher/pool-base.js} +3 -3
  22. package/lib/{pool-stats.js → dispatcher/pool-stats.js} +1 -1
  23. package/lib/{pool.js → dispatcher/pool.js} +4 -4
  24. package/lib/{proxy-agent.js → dispatcher/proxy-agent.js} +29 -35
  25. package/lib/dispatcher/retry-agent.js +35 -0
  26. package/lib/global.js +1 -1
  27. package/lib/handler/{RetryHandler.js → retry-handler.js} +2 -2
  28. package/lib/interceptor/{redirectInterceptor.js → redirect-interceptor.js} +1 -1
  29. package/lib/mock/mock-agent.js +2 -2
  30. package/lib/mock/mock-client.js +1 -1
  31. package/lib/mock/mock-interceptor.js +2 -2
  32. package/lib/mock/mock-pool.js +1 -1
  33. package/lib/mock/mock-utils.js +6 -4
  34. package/lib/{cache → web/cache}/cache.js +2 -4
  35. package/lib/{cache → web/cache}/cachestorage.js +1 -1
  36. package/lib/web/cache/symbols.js +5 -0
  37. package/lib/{cache → web/cache}/util.js +5 -9
  38. package/lib/{cookies → web/cookies}/parse.js +1 -1
  39. package/lib/{cookies → web/cookies}/util.js +76 -60
  40. package/lib/{eventsource → web/eventsource}/eventsource.js +2 -6
  41. package/lib/{fetch → web/fetch}/body.js +23 -52
  42. package/lib/{fetch/dataURL.js → web/fetch/data-url.js} +2 -0
  43. package/lib/{compat → web/fetch}/dispatcher-weakref.js +1 -1
  44. package/lib/{fetch → web/fetch}/file.js +2 -2
  45. package/lib/{fetch → web/fetch}/formdata.js +6 -67
  46. package/lib/{fetch → web/fetch}/headers.js +99 -71
  47. package/lib/{fetch → web/fetch}/index.js +33 -25
  48. package/lib/{fetch → web/fetch}/request.js +14 -6
  49. package/lib/{fetch → web/fetch}/response.js +3 -3
  50. package/lib/{fetch → web/fetch}/symbols.js +2 -1
  51. package/lib/{fetch → web/fetch}/util.js +142 -48
  52. package/lib/{fetch → web/fetch}/webidl.js +46 -16
  53. package/lib/{fileapi → web/fileapi}/filereader.js +1 -1
  54. package/lib/{fileapi → web/fileapi}/util.js +1 -1
  55. package/lib/{websocket → web/websocket}/connection.js +20 -10
  56. package/lib/{websocket → web/websocket}/constants.js +7 -0
  57. package/lib/{websocket → web/websocket}/events.js +1 -1
  58. package/lib/{websocket → web/websocket}/frame.js +1 -0
  59. package/lib/{websocket → web/websocket}/receiver.js +9 -16
  60. package/lib/{websocket → web/websocket}/util.js +37 -23
  61. package/lib/{websocket → web/websocket}/websocket.js +21 -9
  62. package/package.json +26 -51
  63. package/types/dispatcher.d.ts +1 -1
  64. package/types/fetch.d.ts +20 -21
  65. package/types/index.d.ts +2 -1
  66. package/types/retry-agent.d.ts +11 -0
  67. package/types/webidl.d.ts +6 -1
  68. package/docs/api/Fetch.md +0 -27
  69. package/docs/assets/lifecycle-diagram.png +0 -0
  70. package/lib/cache/symbols.js +0 -5
  71. package/lib/client.js +0 -2295
  72. package/lib/llhttp/llhttp-wasm.js +0 -3
  73. package/lib/llhttp/llhttp.wasm +0 -0
  74. package/lib/llhttp/llhttp_simd.wasm +0 -0
  75. /package/docs/{api → docs/api}/Agent.md +0 -0
  76. /package/docs/{api → docs/api}/BalancedPool.md +0 -0
  77. /package/docs/{api → docs/api}/CacheStorage.md +0 -0
  78. /package/docs/{api → docs/api}/Client.md +0 -0
  79. /package/docs/{api → docs/api}/Connector.md +0 -0
  80. /package/docs/{api → docs/api}/ContentType.md +0 -0
  81. /package/docs/{api → docs/api}/Cookies.md +0 -0
  82. /package/docs/{api → docs/api}/Debug.md +0 -0
  83. /package/docs/{api → docs/api}/DiagnosticsChannel.md +0 -0
  84. /package/docs/{api → docs/api}/DispatchInterceptor.md +0 -0
  85. /package/docs/{api → docs/api}/Errors.md +0 -0
  86. /package/docs/{api → docs/api}/EventSource.md +0 -0
  87. /package/docs/{api → docs/api}/MockAgent.md +0 -0
  88. /package/docs/{api → docs/api}/MockClient.md +0 -0
  89. /package/docs/{api → docs/api}/MockErrors.md +0 -0
  90. /package/docs/{api → docs/api}/MockPool.md +0 -0
  91. /package/docs/{api → docs/api}/Pool.md +0 -0
  92. /package/docs/{api → docs/api}/PoolStats.md +0 -0
  93. /package/docs/{api → docs/api}/RedirectHandler.md +0 -0
  94. /package/docs/{api → docs/api}/Util.md +0 -0
  95. /package/docs/{api → docs/api}/WebSocket.md +0 -0
  96. /package/docs/{best-practices → docs/best-practices}/client-certificate.md +0 -0
  97. /package/docs/{best-practices → docs/best-practices}/mocking-request.md +0 -0
  98. /package/docs/{best-practices → docs/best-practices}/writing-tests.md +0 -0
  99. /package/lib/{dispatcher.js → dispatcher/dispatcher.js} +0 -0
  100. /package/lib/{node → dispatcher}/fixed-queue.js +0 -0
  101. /package/lib/handler/{DecoratorHandler.js → decorator-handler.js} +0 -0
  102. /package/lib/handler/{RedirectHandler.js → redirect-handler.js} +0 -0
  103. /package/lib/{timers.js → util/timers.js} +0 -0
  104. /package/lib/{cookies → web/cookies}/constants.js +0 -0
  105. /package/lib/{cookies → web/cookies}/index.js +0 -0
  106. /package/lib/{eventsource → web/eventsource}/eventsource-stream.js +0 -0
  107. /package/lib/{eventsource → web/eventsource}/util.js +0 -0
  108. /package/lib/{fetch → web/fetch}/LICENSE +0 -0
  109. /package/lib/{fetch → web/fetch}/constants.js +0 -0
  110. /package/lib/{fetch → web/fetch}/global.js +0 -0
  111. /package/lib/{fileapi → web/fileapi}/encoding.js +0 -0
  112. /package/lib/{fileapi → web/fileapi}/progressevent.js +0 -0
  113. /package/lib/{fileapi → web/fileapi}/symbols.js +0 -0
  114. /package/lib/{websocket → web/websocket}/symbols.js +0 -0
@@ -0,0 +1,611 @@
1
+ // @ts-check
2
+
3
+ 'use strict'
4
+
5
+ const assert = require('node:assert')
6
+ const net = require('node:net')
7
+ const http = require('node:http')
8
+ const util = require('../core/util.js')
9
+ const { channels } = require('../core/diagnostics.js')
10
+ const Request = require('../core/request.js')
11
+ const DispatcherBase = require('./dispatcher-base')
12
+ const {
13
+ InvalidArgumentError,
14
+ InformationalError,
15
+ ClientDestroyedError
16
+ } = require('../core/errors.js')
17
+ const buildConnector = require('../core/connect.js')
18
+ const {
19
+ kUrl,
20
+ kServerName,
21
+ kClient,
22
+ kBusy,
23
+ kConnect,
24
+ kResuming,
25
+ kRunning,
26
+ kPending,
27
+ kSize,
28
+ kQueue,
29
+ kConnected,
30
+ kConnecting,
31
+ kNeedDrain,
32
+ kKeepAliveDefaultTimeout,
33
+ kHostHeader,
34
+ kPendingIdx,
35
+ kRunningIdx,
36
+ kError,
37
+ kPipelining,
38
+ kKeepAliveTimeoutValue,
39
+ kMaxHeadersSize,
40
+ kKeepAliveMaxTimeout,
41
+ kKeepAliveTimeoutThreshold,
42
+ kHeadersTimeout,
43
+ kBodyTimeout,
44
+ kStrictContentLength,
45
+ kConnector,
46
+ kMaxRedirections,
47
+ kMaxRequests,
48
+ kCounter,
49
+ kClose,
50
+ kDestroy,
51
+ kDispatch,
52
+ kInterceptors,
53
+ kLocalAddress,
54
+ kMaxResponseSize,
55
+ kOnError,
56
+ kHTTPContext,
57
+ kMaxConcurrentStreams,
58
+ kResume
59
+ } = require('../core/symbols.js')
60
+ const connectH1 = require('./client-h1.js')
61
+ const connectH2 = require('./client-h2.js')
62
+
63
+ const kClosedResolve = Symbol('kClosedResolve')
64
+
65
+ function getPipelining (client) {
66
+ return client[kPipelining] ?? client[kHTTPContext]?.defaultPipelining ?? 1
67
+ }
68
+
69
+ /**
70
+ * @type {import('../../types/client.js').default}
71
+ */
72
+ class Client extends DispatcherBase {
73
+ /**
74
+ *
75
+ * @param {string|URL} url
76
+ * @param {import('../../types/client.js').Client.Options} options
77
+ */
78
+ constructor (url, {
79
+ interceptors,
80
+ maxHeaderSize,
81
+ headersTimeout,
82
+ socketTimeout,
83
+ requestTimeout,
84
+ connectTimeout,
85
+ bodyTimeout,
86
+ idleTimeout,
87
+ keepAlive,
88
+ keepAliveTimeout,
89
+ maxKeepAliveTimeout,
90
+ keepAliveMaxTimeout,
91
+ keepAliveTimeoutThreshold,
92
+ socketPath,
93
+ pipelining,
94
+ tls,
95
+ strictContentLength,
96
+ maxCachedSessions,
97
+ maxRedirections,
98
+ connect,
99
+ maxRequestsPerClient,
100
+ localAddress,
101
+ maxResponseSize,
102
+ autoSelectFamily,
103
+ autoSelectFamilyAttemptTimeout,
104
+ // h2
105
+ maxConcurrentStreams,
106
+ allowH2
107
+ } = {}) {
108
+ super()
109
+
110
+ if (keepAlive !== undefined) {
111
+ throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')
112
+ }
113
+
114
+ if (socketTimeout !== undefined) {
115
+ throw new InvalidArgumentError('unsupported socketTimeout, use headersTimeout & bodyTimeout instead')
116
+ }
117
+
118
+ if (requestTimeout !== undefined) {
119
+ throw new InvalidArgumentError('unsupported requestTimeout, use headersTimeout & bodyTimeout instead')
120
+ }
121
+
122
+ if (idleTimeout !== undefined) {
123
+ throw new InvalidArgumentError('unsupported idleTimeout, use keepAliveTimeout instead')
124
+ }
125
+
126
+ if (maxKeepAliveTimeout !== undefined) {
127
+ throw new InvalidArgumentError('unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead')
128
+ }
129
+
130
+ if (maxHeaderSize != null && !Number.isFinite(maxHeaderSize)) {
131
+ throw new InvalidArgumentError('invalid maxHeaderSize')
132
+ }
133
+
134
+ if (socketPath != null && typeof socketPath !== 'string') {
135
+ throw new InvalidArgumentError('invalid socketPath')
136
+ }
137
+
138
+ if (connectTimeout != null && (!Number.isFinite(connectTimeout) || connectTimeout < 0)) {
139
+ throw new InvalidArgumentError('invalid connectTimeout')
140
+ }
141
+
142
+ if (keepAliveTimeout != null && (!Number.isFinite(keepAliveTimeout) || keepAliveTimeout <= 0)) {
143
+ throw new InvalidArgumentError('invalid keepAliveTimeout')
144
+ }
145
+
146
+ if (keepAliveMaxTimeout != null && (!Number.isFinite(keepAliveMaxTimeout) || keepAliveMaxTimeout <= 0)) {
147
+ throw new InvalidArgumentError('invalid keepAliveMaxTimeout')
148
+ }
149
+
150
+ if (keepAliveTimeoutThreshold != null && !Number.isFinite(keepAliveTimeoutThreshold)) {
151
+ throw new InvalidArgumentError('invalid keepAliveTimeoutThreshold')
152
+ }
153
+
154
+ if (headersTimeout != null && (!Number.isInteger(headersTimeout) || headersTimeout < 0)) {
155
+ throw new InvalidArgumentError('headersTimeout must be a positive integer or zero')
156
+ }
157
+
158
+ if (bodyTimeout != null && (!Number.isInteger(bodyTimeout) || bodyTimeout < 0)) {
159
+ throw new InvalidArgumentError('bodyTimeout must be a positive integer or zero')
160
+ }
161
+
162
+ if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
163
+ throw new InvalidArgumentError('connect must be a function or an object')
164
+ }
165
+
166
+ if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
167
+ throw new InvalidArgumentError('maxRedirections must be a positive number')
168
+ }
169
+
170
+ if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) {
171
+ throw new InvalidArgumentError('maxRequestsPerClient must be a positive number')
172
+ }
173
+
174
+ if (localAddress != null && (typeof localAddress !== 'string' || net.isIP(localAddress) === 0)) {
175
+ throw new InvalidArgumentError('localAddress must be valid string IP address')
176
+ }
177
+
178
+ if (maxResponseSize != null && (!Number.isInteger(maxResponseSize) || maxResponseSize < -1)) {
179
+ throw new InvalidArgumentError('maxResponseSize must be a positive number')
180
+ }
181
+
182
+ if (
183
+ autoSelectFamilyAttemptTimeout != null &&
184
+ (!Number.isInteger(autoSelectFamilyAttemptTimeout) || autoSelectFamilyAttemptTimeout < -1)
185
+ ) {
186
+ throw new InvalidArgumentError('autoSelectFamilyAttemptTimeout must be a positive number')
187
+ }
188
+
189
+ // h2
190
+ if (allowH2 != null && typeof allowH2 !== 'boolean') {
191
+ throw new InvalidArgumentError('allowH2 must be a valid boolean value')
192
+ }
193
+
194
+ if (maxConcurrentStreams != null && (typeof maxConcurrentStreams !== 'number' || maxConcurrentStreams < 1)) {
195
+ throw new InvalidArgumentError('maxConcurrentStreams must be a positive integer, greater than 0')
196
+ }
197
+
198
+ if (typeof connect !== 'function') {
199
+ connect = buildConnector({
200
+ ...tls,
201
+ maxCachedSessions,
202
+ allowH2,
203
+ socketPath,
204
+ timeout: connectTimeout,
205
+ ...(util.nodeHasAutoSelectFamily && autoSelectFamily ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined),
206
+ ...connect
207
+ })
208
+ }
209
+
210
+ this[kInterceptors] = interceptors?.Client && Array.isArray(interceptors.Client)
211
+ ? interceptors.Client
212
+ : [createRedirectInterceptor({ maxRedirections })]
213
+ this[kUrl] = util.parseOrigin(url)
214
+ this[kConnector] = connect
215
+ this[kPipelining] = pipelining != null ? pipelining : 1
216
+ this[kMaxHeadersSize] = maxHeaderSize || http.maxHeaderSize
217
+ this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout
218
+ this[kKeepAliveMaxTimeout] = keepAliveMaxTimeout == null ? 600e3 : keepAliveMaxTimeout
219
+ this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 1e3 : keepAliveTimeoutThreshold
220
+ this[kKeepAliveTimeoutValue] = this[kKeepAliveDefaultTimeout]
221
+ this[kServerName] = null
222
+ this[kLocalAddress] = localAddress != null ? localAddress : null
223
+ this[kResuming] = 0 // 0, idle, 1, scheduled, 2 resuming
224
+ this[kNeedDrain] = 0 // 0, idle, 1, scheduled, 2 resuming
225
+ this[kHostHeader] = `host: ${this[kUrl].hostname}${this[kUrl].port ? `:${this[kUrl].port}` : ''}\r\n`
226
+ this[kBodyTimeout] = bodyTimeout != null ? bodyTimeout : 300e3
227
+ this[kHeadersTimeout] = headersTimeout != null ? headersTimeout : 300e3
228
+ this[kStrictContentLength] = strictContentLength == null ? true : strictContentLength
229
+ this[kMaxRedirections] = maxRedirections
230
+ this[kMaxRequests] = maxRequestsPerClient
231
+ this[kClosedResolve] = null
232
+ this[kMaxResponseSize] = maxResponseSize > -1 ? maxResponseSize : -1
233
+ this[kMaxConcurrentStreams] = maxConcurrentStreams != null ? maxConcurrentStreams : 100 // Max peerConcurrentStreams for a Node h2 server
234
+ this[kHTTPContext] = null
235
+
236
+ // kQueue is built up of 3 sections separated by
237
+ // the kRunningIdx and kPendingIdx indices.
238
+ // | complete | running | pending |
239
+ // ^ kRunningIdx ^ kPendingIdx ^ kQueue.length
240
+ // kRunningIdx points to the first running element.
241
+ // kPendingIdx points to the first pending element.
242
+ // This implements a fast queue with an amortized
243
+ // time of O(1).
244
+
245
+ this[kQueue] = []
246
+ this[kRunningIdx] = 0
247
+ this[kPendingIdx] = 0
248
+
249
+ this[kResume] = (sync) => resume(this, sync)
250
+ this[kOnError] = (err) => onError(this, err)
251
+ }
252
+
253
+ get pipelining () {
254
+ return this[kPipelining]
255
+ }
256
+
257
+ set pipelining (value) {
258
+ this[kPipelining] = value
259
+ this[kResume](true)
260
+ }
261
+
262
+ get [kPending] () {
263
+ return this[kQueue].length - this[kPendingIdx]
264
+ }
265
+
266
+ get [kRunning] () {
267
+ return this[kPendingIdx] - this[kRunningIdx]
268
+ }
269
+
270
+ get [kSize] () {
271
+ return this[kQueue].length - this[kRunningIdx]
272
+ }
273
+
274
+ get [kConnected] () {
275
+ return !!this[kHTTPContext] && !this[kConnecting] && !this[kHTTPContext].destroyed
276
+ }
277
+
278
+ get [kBusy] () {
279
+ return Boolean(
280
+ this[kHTTPContext]?.busy(null) ||
281
+ (this[kSize] >= (getPipelining(this) || 1)) ||
282
+ this[kPending] > 0
283
+ )
284
+ }
285
+
286
+ /* istanbul ignore: only used for test */
287
+ [kConnect] (cb) {
288
+ connect(this)
289
+ this.once('connect', cb)
290
+ }
291
+
292
+ [kDispatch] (opts, handler) {
293
+ const origin = opts.origin || this[kUrl].origin
294
+ const request = new Request(origin, opts, handler)
295
+
296
+ this[kQueue].push(request)
297
+ if (this[kResuming]) {
298
+ // Do nothing.
299
+ } else if (util.bodyLength(request.body) == null && util.isIterable(request.body)) {
300
+ // Wait a tick in case stream/iterator is ended in the same tick.
301
+ this[kResuming] = 1
302
+ queueMicrotask(() => resume(this))
303
+ } else {
304
+ this[kResume](true)
305
+ }
306
+
307
+ if (this[kResuming] && this[kNeedDrain] !== 2 && this[kBusy]) {
308
+ this[kNeedDrain] = 2
309
+ }
310
+
311
+ return this[kNeedDrain] < 2
312
+ }
313
+
314
+ async [kClose] () {
315
+ // TODO: for H2 we need to gracefully flush the remaining enqueued
316
+ // request and close each stream.
317
+ return new Promise((resolve) => {
318
+ if (this[kSize]) {
319
+ this[kClosedResolve] = resolve
320
+ } else {
321
+ resolve(null)
322
+ }
323
+ })
324
+ }
325
+
326
+ async [kDestroy] (err) {
327
+ return new Promise((resolve) => {
328
+ const requests = this[kQueue].splice(this[kPendingIdx])
329
+ for (let i = 0; i < requests.length; i++) {
330
+ const request = requests[i]
331
+ errorRequest(this, request, err)
332
+ }
333
+
334
+ const callback = () => {
335
+ if (this[kClosedResolve]) {
336
+ // TODO (fix): Should we error here with ClientDestroyedError?
337
+ this[kClosedResolve]()
338
+ this[kClosedResolve] = null
339
+ }
340
+ resolve(null)
341
+ }
342
+
343
+ if (this[kHTTPContext]) {
344
+ this[kHTTPContext].destroy(err, callback)
345
+ this[kHTTPContext] = null
346
+ } else {
347
+ queueMicrotask(callback)
348
+ }
349
+
350
+ this[kResume]()
351
+ })
352
+ }
353
+ }
354
+
355
+ const createRedirectInterceptor = require('../interceptor/redirect-interceptor.js')
356
+
357
+ function onError (client, err) {
358
+ if (
359
+ client[kRunning] === 0 &&
360
+ err.code !== 'UND_ERR_INFO' &&
361
+ err.code !== 'UND_ERR_SOCKET'
362
+ ) {
363
+ // Error is not caused by running request and not a recoverable
364
+ // socket error.
365
+
366
+ assert(client[kPendingIdx] === client[kRunningIdx])
367
+
368
+ const requests = client[kQueue].splice(client[kRunningIdx])
369
+ for (let i = 0; i < requests.length; i++) {
370
+ const request = requests[i]
371
+ errorRequest(client, request, err)
372
+ }
373
+ assert(client[kSize] === 0)
374
+ }
375
+ }
376
+
377
+ async function connect (client) {
378
+ assert(!client[kConnecting])
379
+ assert(!client[kHTTPContext])
380
+
381
+ let { host, hostname, protocol, port } = client[kUrl]
382
+
383
+ // Resolve ipv6
384
+ if (hostname[0] === '[') {
385
+ const idx = hostname.indexOf(']')
386
+
387
+ assert(idx !== -1)
388
+ const ip = hostname.substring(1, idx)
389
+
390
+ assert(net.isIP(ip))
391
+ hostname = ip
392
+ }
393
+
394
+ client[kConnecting] = true
395
+
396
+ if (channels.beforeConnect.hasSubscribers) {
397
+ channels.beforeConnect.publish({
398
+ connectParams: {
399
+ host,
400
+ hostname,
401
+ protocol,
402
+ port,
403
+ version: client[kHTTPContext]?.version,
404
+ servername: client[kServerName],
405
+ localAddress: client[kLocalAddress]
406
+ },
407
+ connector: client[kConnector]
408
+ })
409
+ }
410
+
411
+ try {
412
+ const socket = await new Promise((resolve, reject) => {
413
+ client[kConnector]({
414
+ host,
415
+ hostname,
416
+ protocol,
417
+ port,
418
+ servername: client[kServerName],
419
+ localAddress: client[kLocalAddress]
420
+ }, (err, socket) => {
421
+ if (err) {
422
+ reject(err)
423
+ } else {
424
+ resolve(socket)
425
+ }
426
+ })
427
+ })
428
+
429
+ if (client.destroyed) {
430
+ util.destroy(socket.on('error', () => {}), new ClientDestroyedError())
431
+ return
432
+ }
433
+
434
+ assert(socket)
435
+
436
+ try {
437
+ client[kHTTPContext] = socket.alpnProtocol === 'h2'
438
+ ? await connectH2(client, socket)
439
+ : await connectH1(client, socket)
440
+ } catch (err) {
441
+ socket.destroy().on('error', () => {})
442
+ throw err
443
+ }
444
+
445
+ client[kConnecting] = false
446
+
447
+ socket[kCounter] = 0
448
+ socket[kMaxRequests] = client[kMaxRequests]
449
+ socket[kClient] = client
450
+ socket[kError] = null
451
+
452
+ if (channels.connected.hasSubscribers) {
453
+ channels.connected.publish({
454
+ connectParams: {
455
+ host,
456
+ hostname,
457
+ protocol,
458
+ port,
459
+ version: client[kHTTPContext]?.version,
460
+ servername: client[kServerName],
461
+ localAddress: client[kLocalAddress]
462
+ },
463
+ connector: client[kConnector],
464
+ socket
465
+ })
466
+ }
467
+ client.emit('connect', client[kUrl], [client])
468
+ } catch (err) {
469
+ if (client.destroyed) {
470
+ return
471
+ }
472
+
473
+ client[kConnecting] = false
474
+
475
+ if (channels.connectError.hasSubscribers) {
476
+ channels.connectError.publish({
477
+ connectParams: {
478
+ host,
479
+ hostname,
480
+ protocol,
481
+ port,
482
+ version: client[kHTTPContext]?.version,
483
+ servername: client[kServerName],
484
+ localAddress: client[kLocalAddress]
485
+ },
486
+ connector: client[kConnector],
487
+ error: err
488
+ })
489
+ }
490
+
491
+ if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
492
+ assert(client[kRunning] === 0)
493
+ while (client[kPending] > 0 && client[kQueue][client[kPendingIdx]].servername === client[kServerName]) {
494
+ const request = client[kQueue][client[kPendingIdx]++]
495
+ errorRequest(client, request, err)
496
+ }
497
+ } else {
498
+ onError(client, err)
499
+ }
500
+
501
+ client.emit('connectionError', client[kUrl], [client], err)
502
+ }
503
+
504
+ client[kResume]()
505
+ }
506
+
507
+ function emitDrain (client) {
508
+ client[kNeedDrain] = 0
509
+ client.emit('drain', client[kUrl], [client])
510
+ }
511
+
512
+ function resume (client, sync) {
513
+ if (client[kResuming] === 2) {
514
+ return
515
+ }
516
+
517
+ client[kResuming] = 2
518
+
519
+ _resume(client, sync)
520
+ client[kResuming] = 0
521
+
522
+ if (client[kRunningIdx] > 256) {
523
+ client[kQueue].splice(0, client[kRunningIdx])
524
+ client[kPendingIdx] -= client[kRunningIdx]
525
+ client[kRunningIdx] = 0
526
+ }
527
+ }
528
+
529
+ function _resume (client, sync) {
530
+ while (true) {
531
+ if (client.destroyed) {
532
+ assert(client[kPending] === 0)
533
+ return
534
+ }
535
+
536
+ if (client[kClosedResolve] && !client[kSize]) {
537
+ client[kClosedResolve]()
538
+ client[kClosedResolve] = null
539
+ return
540
+ }
541
+
542
+ if (client[kHTTPContext]) {
543
+ client[kHTTPContext].resume()
544
+ }
545
+
546
+ if (client[kBusy]) {
547
+ client[kNeedDrain] = 2
548
+ } else if (client[kNeedDrain] === 2) {
549
+ if (sync) {
550
+ client[kNeedDrain] = 1
551
+ queueMicrotask(() => emitDrain(client))
552
+ } else {
553
+ emitDrain(client)
554
+ }
555
+ continue
556
+ }
557
+
558
+ if (client[kPending] === 0) {
559
+ return
560
+ }
561
+
562
+ if (client[kRunning] >= (getPipelining(client) || 1)) {
563
+ return
564
+ }
565
+
566
+ const request = client[kQueue][client[kPendingIdx]]
567
+
568
+ if (client[kUrl].protocol === 'https:' && client[kServerName] !== request.servername) {
569
+ if (client[kRunning] > 0) {
570
+ return
571
+ }
572
+
573
+ client[kServerName] = request.servername
574
+ client[kHTTPContext]?.destroy(new InformationalError('servername changed'))
575
+ }
576
+
577
+ if (client[kConnecting]) {
578
+ return
579
+ }
580
+
581
+ if (!client[kHTTPContext]) {
582
+ connect(client)
583
+ return
584
+ }
585
+
586
+ if (client[kHTTPContext].destroyed) {
587
+ return
588
+ }
589
+
590
+ if (client[kHTTPContext].busy(request)) {
591
+ return
592
+ }
593
+
594
+ if (!request.aborted && client[kHTTPContext].write(request)) {
595
+ client[kPendingIdx]++
596
+ } else {
597
+ client[kQueue].splice(client[kPendingIdx], 1)
598
+ }
599
+ }
600
+ }
601
+
602
+ function errorRequest (client, request, err) {
603
+ try {
604
+ request.onError(err)
605
+ assert(request.aborted)
606
+ } catch (err) {
607
+ client.emit('error', err)
608
+ }
609
+ }
610
+
611
+ module.exports = Client
@@ -5,8 +5,8 @@ const {
5
5
  ClientDestroyedError,
6
6
  ClientClosedError,
7
7
  InvalidArgumentError
8
- } = require('./core/errors')
9
- const { kDestroy, kClose, kDispatch, kInterceptors } = require('./core/symbols')
8
+ } = require('../core/errors')
9
+ const { kDestroy, kClose, kDispatch, kInterceptors } = require('../core/symbols')
10
10
 
11
11
  const kDestroyed = Symbol('destroyed')
12
12
  const kClosed = Symbol('closed')
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const DispatcherBase = require('./dispatcher-base')
4
- const FixedQueue = require('./node/fixed-queue')
5
- const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require('./core/symbols')
4
+ const FixedQueue = require('./fixed-queue')
5
+ const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require('../core/symbols')
6
6
  const PoolStats = require('./pool-stats')
7
7
 
8
8
  const kClients = Symbol('clients')
@@ -158,7 +158,7 @@ class PoolBase extends DispatcherBase {
158
158
  this[kClients].push(client)
159
159
 
160
160
  if (this[kNeedDrain]) {
161
- process.nextTick(() => {
161
+ queueMicrotask(() => {
162
162
  if (this[kNeedDrain]) {
163
163
  this[kOnDrain](client[kUrl], [this, client])
164
164
  }
@@ -1,4 +1,4 @@
1
- const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = require('./core/symbols')
1
+ const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = require('../core/symbols')
2
2
  const kPool = Symbol('pool')
3
3
 
4
4
  class PoolStats {
@@ -10,10 +10,10 @@ const {
10
10
  const Client = require('./client')
11
11
  const {
12
12
  InvalidArgumentError
13
- } = require('./core/errors')
14
- const util = require('./core/util')
15
- const { kUrl, kInterceptors } = require('./core/symbols')
16
- const buildConnector = require('./core/connect')
13
+ } = require('../core/errors')
14
+ const util = require('../core/util')
15
+ const { kUrl, kInterceptors } = require('../core/symbols')
16
+ const buildConnector = require('../core/connect')
17
17
 
18
18
  const kOptions = Symbol('options')
19
19
  const kConnections = Symbol('connections')