undici 7.0.0-alpha.1 → 7.0.0-alpha.2
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.
- package/README.md +2 -2
- package/docs/docs/api/Client.md +1 -1
- package/docs/docs/api/Debug.md +1 -1
- package/docs/docs/api/Dispatcher.md +53 -2
- package/docs/docs/api/MockAgent.md +2 -0
- package/docs/docs/api/MockPool.md +2 -1
- package/docs/docs/api/RetryAgent.md +1 -1
- package/docs/docs/api/RetryHandler.md +1 -1
- package/docs/docs/api/WebSocket.md +45 -3
- package/index.js +6 -2
- package/lib/api/abort-signal.js +2 -0
- package/lib/api/api-pipeline.js +4 -2
- package/lib/api/api-request.js +4 -2
- package/lib/api/api-stream.js +3 -1
- package/lib/api/api-upgrade.js +2 -2
- package/lib/api/readable.js +194 -41
- package/lib/api/util.js +2 -0
- package/lib/core/connect.js +49 -22
- package/lib/core/constants.js +11 -9
- package/lib/core/diagnostics.js +122 -128
- package/lib/core/request.js +4 -4
- package/lib/core/symbols.js +2 -0
- package/lib/core/tree.js +4 -2
- package/lib/core/util.js +220 -39
- package/lib/dispatcher/client-h1.js +299 -60
- package/lib/dispatcher/client-h2.js +1 -1
- package/lib/dispatcher/client.js +24 -7
- package/lib/dispatcher/fixed-queue.js +91 -49
- package/lib/dispatcher/pool-stats.js +2 -0
- package/lib/dispatcher/proxy-agent.js +3 -1
- package/lib/handler/redirect-handler.js +2 -2
- package/lib/handler/retry-handler.js +2 -2
- package/lib/interceptor/dns.js +346 -0
- package/lib/mock/mock-agent.js +5 -8
- package/lib/mock/mock-client.js +7 -2
- package/lib/mock/mock-errors.js +3 -1
- package/lib/mock/mock-interceptor.js +8 -6
- package/lib/mock/mock-pool.js +7 -2
- package/lib/mock/mock-symbols.js +2 -1
- package/lib/mock/mock-utils.js +33 -5
- package/lib/util/timers.js +50 -6
- package/lib/web/cache/cache.js +24 -21
- package/lib/web/cache/cachestorage.js +1 -1
- package/lib/web/cookies/index.js +6 -4
- package/lib/web/fetch/body.js +42 -34
- package/lib/web/fetch/constants.js +35 -26
- package/lib/web/fetch/formdata-parser.js +14 -3
- package/lib/web/fetch/formdata.js +40 -20
- package/lib/web/fetch/headers.js +116 -84
- package/lib/web/fetch/index.js +65 -59
- package/lib/web/fetch/request.js +130 -55
- package/lib/web/fetch/response.js +79 -36
- package/lib/web/fetch/util.js +104 -57
- package/lib/web/fetch/webidl.js +38 -14
- package/lib/web/websocket/connection.js +92 -15
- package/lib/web/websocket/constants.js +2 -3
- package/lib/web/websocket/events.js +4 -2
- package/lib/web/websocket/receiver.js +20 -26
- package/lib/web/websocket/stream/websocketerror.js +83 -0
- package/lib/web/websocket/stream/websocketstream.js +485 -0
- package/lib/web/websocket/util.js +115 -10
- package/lib/web/websocket/websocket.js +45 -170
- package/package.json +6 -6
- package/types/interceptors.d.ts +14 -0
- package/types/mock-agent.d.ts +3 -0
- package/types/readable.d.ts +10 -7
- package/types/webidl.d.ts +24 -4
- package/types/websocket.d.ts +33 -0
- package/lib/mock/pluralizer.js +0 -29
- package/lib/web/cache/symbols.js +0 -5
- package/lib/web/fetch/symbols.js +0 -8
package/lib/api/readable.js
CHANGED
|
@@ -19,7 +19,20 @@ const kBytesRead = Symbol('kBytesRead')
|
|
|
19
19
|
|
|
20
20
|
const noop = () => {}
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @class
|
|
24
|
+
* @extends {Readable}
|
|
25
|
+
* @see https://fetch.spec.whatwg.org/#body
|
|
26
|
+
*/
|
|
22
27
|
class BodyReadable extends Readable {
|
|
28
|
+
/**
|
|
29
|
+
* @param {object} opts
|
|
30
|
+
* @param {(this: Readable, size: number) => void} opts.resume
|
|
31
|
+
* @param {() => (void | null)} opts.abort
|
|
32
|
+
* @param {string} [opts.contentType = '']
|
|
33
|
+
* @param {number} [opts.contentLength]
|
|
34
|
+
* @param {number} [opts.highWaterMark = 64 * 1024]
|
|
35
|
+
*/
|
|
23
36
|
constructor ({
|
|
24
37
|
resume,
|
|
25
38
|
abort,
|
|
@@ -36,8 +49,15 @@ class BodyReadable extends Readable {
|
|
|
36
49
|
this._readableState.dataEmitted = false
|
|
37
50
|
|
|
38
51
|
this[kAbort] = abort
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @type {Consume | null}
|
|
55
|
+
*/
|
|
39
56
|
this[kConsume] = null
|
|
40
57
|
this[kBytesRead] = 0
|
|
58
|
+
/**
|
|
59
|
+
* @type {ReadableStream|null}
|
|
60
|
+
*/
|
|
41
61
|
this[kBody] = null
|
|
42
62
|
this[kUsed] = false
|
|
43
63
|
this[kContentType] = contentType
|
|
@@ -50,6 +70,11 @@ class BodyReadable extends Readable {
|
|
|
50
70
|
this[kReading] = false
|
|
51
71
|
}
|
|
52
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @param {Error|null} err
|
|
75
|
+
* @param {(error:(Error|null)) => void} callback
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
53
78
|
_destroy (err, callback) {
|
|
54
79
|
if (!err && !this._readableState.endEmitted) {
|
|
55
80
|
err = new RequestAbortedError()
|
|
@@ -61,7 +86,7 @@ class BodyReadable extends Readable {
|
|
|
61
86
|
|
|
62
87
|
// Workaround for Node "bug". If the stream is destroyed in same
|
|
63
88
|
// tick as it is created, then a user who is waiting for a
|
|
64
|
-
// promise (i.e micro tick) for installing
|
|
89
|
+
// promise (i.e micro tick) for installing an 'error' listener will
|
|
65
90
|
// never get a chance and will always encounter an unhandled exception.
|
|
66
91
|
if (!this[kUsed]) {
|
|
67
92
|
setImmediate(() => {
|
|
@@ -72,21 +97,36 @@ class BodyReadable extends Readable {
|
|
|
72
97
|
}
|
|
73
98
|
}
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
/**
|
|
101
|
+
* @param {string} event
|
|
102
|
+
* @param {(...args: any[]) => void} listener
|
|
103
|
+
* @returns {this}
|
|
104
|
+
*/
|
|
105
|
+
on (event, listener) {
|
|
106
|
+
if (event === 'data' || event === 'readable') {
|
|
77
107
|
this[kReading] = true
|
|
78
108
|
this[kUsed] = true
|
|
79
109
|
}
|
|
80
|
-
return super.on(
|
|
110
|
+
return super.on(event, listener)
|
|
81
111
|
}
|
|
82
112
|
|
|
83
|
-
|
|
84
|
-
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} event
|
|
115
|
+
* @param {(...args: any[]) => void} listener
|
|
116
|
+
* @returns {this}
|
|
117
|
+
*/
|
|
118
|
+
addListener (event, listener) {
|
|
119
|
+
return this.on(event, listener)
|
|
85
120
|
}
|
|
86
121
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
122
|
+
/**
|
|
123
|
+
* @param {string|symbol} event
|
|
124
|
+
* @param {(...args: any[]) => void} listener
|
|
125
|
+
* @returns {this}
|
|
126
|
+
*/
|
|
127
|
+
off (event, listener) {
|
|
128
|
+
const ret = super.off(event, listener)
|
|
129
|
+
if (event === 'data' || event === 'readable') {
|
|
90
130
|
this[kReading] = (
|
|
91
131
|
this.listenerCount('data') > 0 ||
|
|
92
132
|
this.listenerCount('readable') > 0
|
|
@@ -95,10 +135,19 @@ class BodyReadable extends Readable {
|
|
|
95
135
|
return ret
|
|
96
136
|
}
|
|
97
137
|
|
|
98
|
-
|
|
99
|
-
|
|
138
|
+
/**
|
|
139
|
+
* @param {string|symbol} event
|
|
140
|
+
* @param {(...args: any[]) => void} listener
|
|
141
|
+
* @returns {this}
|
|
142
|
+
*/
|
|
143
|
+
removeListener (event, listener) {
|
|
144
|
+
return this.off(event, listener)
|
|
100
145
|
}
|
|
101
146
|
|
|
147
|
+
/**
|
|
148
|
+
* @param {Buffer|null} chunk
|
|
149
|
+
* @returns {boolean}
|
|
150
|
+
*/
|
|
102
151
|
push (chunk) {
|
|
103
152
|
this[kBytesRead] += chunk ? chunk.length : 0
|
|
104
153
|
|
|
@@ -109,43 +158,84 @@ class BodyReadable extends Readable {
|
|
|
109
158
|
return super.push(chunk)
|
|
110
159
|
}
|
|
111
160
|
|
|
112
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Consumes and returns the body as a string.
|
|
163
|
+
*
|
|
164
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-text
|
|
165
|
+
* @returns {Promise<string>}
|
|
166
|
+
*/
|
|
113
167
|
async text () {
|
|
114
168
|
return consume(this, 'text')
|
|
115
169
|
}
|
|
116
170
|
|
|
117
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Consumes and returns the body as a JavaScript Object.
|
|
173
|
+
*
|
|
174
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-json
|
|
175
|
+
* @returns {Promise<unknown>}
|
|
176
|
+
*/
|
|
118
177
|
async json () {
|
|
119
178
|
return consume(this, 'json')
|
|
120
179
|
}
|
|
121
180
|
|
|
122
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Consumes and returns the body as a Blob
|
|
183
|
+
*
|
|
184
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-blob
|
|
185
|
+
* @returns {Promise<Blob>}
|
|
186
|
+
*/
|
|
123
187
|
async blob () {
|
|
124
188
|
return consume(this, 'blob')
|
|
125
189
|
}
|
|
126
190
|
|
|
127
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Consumes and returns the body as an Uint8Array.
|
|
193
|
+
*
|
|
194
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-bytes
|
|
195
|
+
* @returns {Promise<Uint8Array>}
|
|
196
|
+
*/
|
|
128
197
|
async bytes () {
|
|
129
198
|
return consume(this, 'bytes')
|
|
130
199
|
}
|
|
131
200
|
|
|
132
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Consumes and returns the body as an ArrayBuffer.
|
|
203
|
+
*
|
|
204
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-arraybuffer
|
|
205
|
+
* @returns {Promise<ArrayBuffer>}
|
|
206
|
+
*/
|
|
133
207
|
async arrayBuffer () {
|
|
134
208
|
return consume(this, 'arrayBuffer')
|
|
135
209
|
}
|
|
136
210
|
|
|
137
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Not implemented
|
|
213
|
+
*
|
|
214
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-formdata
|
|
215
|
+
* @throws {NotSupportedError}
|
|
216
|
+
*/
|
|
138
217
|
async formData () {
|
|
139
218
|
// TODO: Implement.
|
|
140
219
|
throw new NotSupportedError()
|
|
141
220
|
}
|
|
142
221
|
|
|
143
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Returns true if the body is not null and the body has been consumed.
|
|
224
|
+
* Otherwise, returns false.
|
|
225
|
+
*
|
|
226
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-bodyused
|
|
227
|
+
* @readonly
|
|
228
|
+
* @returns {boolean}
|
|
229
|
+
*/
|
|
144
230
|
get bodyUsed () {
|
|
145
231
|
return util.isDisturbed(this)
|
|
146
232
|
}
|
|
147
233
|
|
|
148
|
-
|
|
234
|
+
/**
|
|
235
|
+
* @see https://fetch.spec.whatwg.org/#dom-body-body
|
|
236
|
+
* @readonly
|
|
237
|
+
* @returns {ReadableStream}
|
|
238
|
+
*/
|
|
149
239
|
get body () {
|
|
150
240
|
if (!this[kBody]) {
|
|
151
241
|
this[kBody] = ReadableStreamFrom(this)
|
|
@@ -158,6 +248,13 @@ class BodyReadable extends Readable {
|
|
|
158
248
|
return this[kBody]
|
|
159
249
|
}
|
|
160
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Dumps the response body by reading `limit` number of bytes.
|
|
253
|
+
* @param {object} opts
|
|
254
|
+
* @param {number} [opts.limit = 131072] Number of bytes to read.
|
|
255
|
+
* @param {AbortSignal} [opts.signal] An AbortSignal to cancel the dump.
|
|
256
|
+
* @returns {Promise<null>}
|
|
257
|
+
*/
|
|
161
258
|
async dump (opts) {
|
|
162
259
|
const signal = opts?.signal
|
|
163
260
|
|
|
@@ -165,7 +262,9 @@ class BodyReadable extends Readable {
|
|
|
165
262
|
throw new InvalidArgumentError('signal must be an AbortSignal')
|
|
166
263
|
}
|
|
167
264
|
|
|
168
|
-
const limit =
|
|
265
|
+
const limit = opts?.limit && Number.isFinite(opts.limit)
|
|
266
|
+
? opts.limit
|
|
267
|
+
: 128 * 1024
|
|
169
268
|
|
|
170
269
|
signal?.throwIfAborted()
|
|
171
270
|
|
|
@@ -174,26 +273,34 @@ class BodyReadable extends Readable {
|
|
|
174
273
|
}
|
|
175
274
|
|
|
176
275
|
return await new Promise((resolve, reject) => {
|
|
177
|
-
if (
|
|
276
|
+
if (
|
|
277
|
+
(this[kContentLength] && (this[kContentLength] > limit)) ||
|
|
278
|
+
this[kBytesRead] > limit
|
|
279
|
+
) {
|
|
178
280
|
this.destroy(new AbortError())
|
|
179
281
|
}
|
|
180
282
|
|
|
181
|
-
|
|
182
|
-
|
|
283
|
+
if (signal) {
|
|
284
|
+
const onAbort = () => {
|
|
285
|
+
this.destroy(signal.reason ?? new AbortError())
|
|
286
|
+
}
|
|
287
|
+
signal.addEventListener('abort', onAbort)
|
|
288
|
+
this
|
|
289
|
+
.on('close', function () {
|
|
290
|
+
signal.removeEventListener('abort', onAbort)
|
|
291
|
+
if (signal.aborted) {
|
|
292
|
+
reject(signal.reason ?? new AbortError())
|
|
293
|
+
} else {
|
|
294
|
+
resolve(null)
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
} else {
|
|
298
|
+
this.on('close', resolve)
|
|
183
299
|
}
|
|
184
|
-
signal?.addEventListener('abort', onAbort)
|
|
185
300
|
|
|
186
301
|
this
|
|
187
|
-
.on('close', function () {
|
|
188
|
-
signal?.removeEventListener('abort', onAbort)
|
|
189
|
-
if (signal?.aborted) {
|
|
190
|
-
reject(signal.reason ?? new AbortError())
|
|
191
|
-
} else {
|
|
192
|
-
resolve(null)
|
|
193
|
-
}
|
|
194
|
-
})
|
|
195
302
|
.on('error', noop)
|
|
196
|
-
.on('data',
|
|
303
|
+
.on('data', () => {
|
|
197
304
|
if (this[kBytesRead] > limit) {
|
|
198
305
|
this.destroy()
|
|
199
306
|
}
|
|
@@ -204,7 +311,7 @@ class BodyReadable extends Readable {
|
|
|
204
311
|
|
|
205
312
|
/**
|
|
206
313
|
* @param {BufferEncoding} encoding
|
|
207
|
-
* @returns {
|
|
314
|
+
* @returns {this}
|
|
208
315
|
*/
|
|
209
316
|
setEncoding (encoding) {
|
|
210
317
|
if (Buffer.isEncoding(encoding)) {
|
|
@@ -214,17 +321,40 @@ class BodyReadable extends Readable {
|
|
|
214
321
|
}
|
|
215
322
|
}
|
|
216
323
|
|
|
217
|
-
|
|
218
|
-
|
|
324
|
+
/**
|
|
325
|
+
* @see https://streams.spec.whatwg.org/#readablestream-locked
|
|
326
|
+
* @param {BodyReadable} bodyReadable
|
|
327
|
+
* @returns {boolean}
|
|
328
|
+
*/
|
|
329
|
+
function isLocked (bodyReadable) {
|
|
219
330
|
// Consume is an implicit lock.
|
|
220
|
-
return
|
|
331
|
+
return bodyReadable[kBody]?.locked === true || bodyReadable[kConsume] !== null
|
|
221
332
|
}
|
|
222
333
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
334
|
+
/**
|
|
335
|
+
* @see https://fetch.spec.whatwg.org/#body-unusable
|
|
336
|
+
* @param {BodyReadable} bodyReadable
|
|
337
|
+
* @returns {boolean}
|
|
338
|
+
*/
|
|
339
|
+
function isUnusable (bodyReadable) {
|
|
340
|
+
return util.isDisturbed(bodyReadable) || isLocked(bodyReadable)
|
|
226
341
|
}
|
|
227
342
|
|
|
343
|
+
/**
|
|
344
|
+
* @typedef {object} Consume
|
|
345
|
+
* @property {string} type
|
|
346
|
+
* @property {BodyReadable} stream
|
|
347
|
+
* @property {((value?: any) => void)} resolve
|
|
348
|
+
* @property {((err: Error) => void)} reject
|
|
349
|
+
* @property {number} length
|
|
350
|
+
* @property {Buffer[]} body
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @param {BodyReadable} stream
|
|
355
|
+
* @param {string} type
|
|
356
|
+
* @returns {Promise<any>}
|
|
357
|
+
*/
|
|
228
358
|
async function consume (stream, type) {
|
|
229
359
|
assert(!stream[kConsume])
|
|
230
360
|
|
|
@@ -269,6 +399,10 @@ async function consume (stream, type) {
|
|
|
269
399
|
})
|
|
270
400
|
}
|
|
271
401
|
|
|
402
|
+
/**
|
|
403
|
+
* @param {Consume} consume
|
|
404
|
+
* @returns {void}
|
|
405
|
+
*/
|
|
272
406
|
function consumeStart (consume) {
|
|
273
407
|
if (consume.body === null) {
|
|
274
408
|
return
|
|
@@ -356,6 +490,11 @@ function chunksConcat (chunks, length) {
|
|
|
356
490
|
return buffer
|
|
357
491
|
}
|
|
358
492
|
|
|
493
|
+
/**
|
|
494
|
+
* @param {Consume} consume
|
|
495
|
+
* @param {BufferEncoding} encoding
|
|
496
|
+
* @returns {void}
|
|
497
|
+
*/
|
|
359
498
|
function consumeEnd (consume, encoding) {
|
|
360
499
|
const { type, body, resolve, stream, length } = consume
|
|
361
500
|
|
|
@@ -378,11 +517,21 @@ function consumeEnd (consume, encoding) {
|
|
|
378
517
|
}
|
|
379
518
|
}
|
|
380
519
|
|
|
520
|
+
/**
|
|
521
|
+
* @param {Consume} consume
|
|
522
|
+
* @param {Buffer} chunk
|
|
523
|
+
* @returns {void}
|
|
524
|
+
*/
|
|
381
525
|
function consumePush (consume, chunk) {
|
|
382
526
|
consume.length += chunk.length
|
|
383
527
|
consume.body.push(chunk)
|
|
384
528
|
}
|
|
385
529
|
|
|
530
|
+
/**
|
|
531
|
+
* @param {Consume} consume
|
|
532
|
+
* @param {Error} [err]
|
|
533
|
+
* @returns {void}
|
|
534
|
+
*/
|
|
386
535
|
function consumeFinish (consume, err) {
|
|
387
536
|
if (consume.body === null) {
|
|
388
537
|
return
|
|
@@ -394,6 +543,7 @@ function consumeFinish (consume, err) {
|
|
|
394
543
|
consume.resolve()
|
|
395
544
|
}
|
|
396
545
|
|
|
546
|
+
// Reset the consume object to allow for garbage collection.
|
|
397
547
|
consume.type = null
|
|
398
548
|
consume.stream = null
|
|
399
549
|
consume.resolve = null
|
|
@@ -402,4 +552,7 @@ function consumeFinish (consume, err) {
|
|
|
402
552
|
consume.body = null
|
|
403
553
|
}
|
|
404
554
|
|
|
405
|
-
module.exports = {
|
|
555
|
+
module.exports = {
|
|
556
|
+
Readable: BodyReadable,
|
|
557
|
+
chunksDecode
|
|
558
|
+
}
|
package/lib/api/util.js
CHANGED
package/lib/core/connect.js
CHANGED
|
@@ -6,6 +6,8 @@ const util = require('./util')
|
|
|
6
6
|
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
|
|
7
7
|
const timers = require('../util/timers')
|
|
8
8
|
|
|
9
|
+
function noop () {}
|
|
10
|
+
|
|
9
11
|
let tls // include tls conditionally since it is not always available
|
|
10
12
|
|
|
11
13
|
// TODO: session re-use does not wait for the first
|
|
@@ -92,9 +94,11 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
|
|
92
94
|
servername = servername || options.servername || util.getServerName(host) || null
|
|
93
95
|
|
|
94
96
|
const sessionKey = servername || hostname
|
|
97
|
+
assert(sessionKey)
|
|
98
|
+
|
|
95
99
|
const session = customSession || sessionCache.get(sessionKey) || null
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
port = port || 443
|
|
98
102
|
|
|
99
103
|
socket = tls.connect({
|
|
100
104
|
highWaterMark: 16384, // TLS in node can't have bigger HWM anyway...
|
|
@@ -105,7 +109,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
|
|
105
109
|
// TODO(HTTP/2): Add support for h2c
|
|
106
110
|
ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'],
|
|
107
111
|
socket: httpSocket, // upgrade socket connection
|
|
108
|
-
port
|
|
112
|
+
port,
|
|
109
113
|
host: hostname
|
|
110
114
|
})
|
|
111
115
|
|
|
@@ -116,11 +120,14 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
|
|
116
120
|
})
|
|
117
121
|
} else {
|
|
118
122
|
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
|
|
123
|
+
|
|
124
|
+
port = port || 80
|
|
125
|
+
|
|
119
126
|
socket = net.connect({
|
|
120
127
|
highWaterMark: 64 * 1024, // Same as nodejs fs streams.
|
|
121
128
|
...options,
|
|
122
129
|
localAddress,
|
|
123
|
-
port
|
|
130
|
+
port,
|
|
124
131
|
host: hostname
|
|
125
132
|
})
|
|
126
133
|
}
|
|
@@ -131,12 +138,12 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
|
|
131
138
|
socket.setKeepAlive(true, keepAliveInitialDelay)
|
|
132
139
|
}
|
|
133
140
|
|
|
134
|
-
const
|
|
141
|
+
const clearConnectTimeout = setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port })
|
|
135
142
|
|
|
136
143
|
socket
|
|
137
144
|
.setNoDelay(true)
|
|
138
145
|
.once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {
|
|
139
|
-
|
|
146
|
+
queueMicrotask(clearConnectTimeout)
|
|
140
147
|
|
|
141
148
|
if (callback) {
|
|
142
149
|
const cb = callback
|
|
@@ -145,7 +152,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
|
|
145
152
|
}
|
|
146
153
|
})
|
|
147
154
|
.on('error', function (err) {
|
|
148
|
-
|
|
155
|
+
queueMicrotask(clearConnectTimeout)
|
|
149
156
|
|
|
150
157
|
if (callback) {
|
|
151
158
|
const cb = callback
|
|
@@ -158,50 +165,70 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
|
|
158
165
|
}
|
|
159
166
|
}
|
|
160
167
|
|
|
168
|
+
/**
|
|
169
|
+
* @param {WeakRef<net.Socket>} socketWeakRef
|
|
170
|
+
* @param {object} opts
|
|
171
|
+
* @param {number} opts.timeout
|
|
172
|
+
* @param {string} opts.hostname
|
|
173
|
+
* @param {number} opts.port
|
|
174
|
+
* @returns {() => void}
|
|
175
|
+
*/
|
|
161
176
|
const setupConnectTimeout = process.platform === 'win32'
|
|
162
|
-
? (
|
|
163
|
-
if (!timeout) {
|
|
164
|
-
return
|
|
177
|
+
? (socketWeakRef, opts) => {
|
|
178
|
+
if (!opts.timeout) {
|
|
179
|
+
return noop
|
|
165
180
|
}
|
|
166
181
|
|
|
167
182
|
let s1 = null
|
|
168
183
|
let s2 = null
|
|
169
|
-
const
|
|
184
|
+
const fastTimer = timers.setFastTimeout(() => {
|
|
170
185
|
// setImmediate is added to make sure that we prioritize socket error events over timeouts
|
|
171
186
|
s1 = setImmediate(() => {
|
|
172
187
|
// Windows needs an extra setImmediate probably due to implementation differences in the socket logic
|
|
173
|
-
s2 = setImmediate(() => onConnectTimeout(
|
|
188
|
+
s2 = setImmediate(() => onConnectTimeout(socketWeakRef.deref(), opts))
|
|
174
189
|
})
|
|
175
|
-
}, timeout)
|
|
190
|
+
}, opts.timeout)
|
|
176
191
|
return () => {
|
|
177
|
-
timers.
|
|
192
|
+
timers.clearFastTimeout(fastTimer)
|
|
178
193
|
clearImmediate(s1)
|
|
179
194
|
clearImmediate(s2)
|
|
180
195
|
}
|
|
181
196
|
}
|
|
182
|
-
: (
|
|
183
|
-
if (!timeout) {
|
|
184
|
-
return
|
|
197
|
+
: (socketWeakRef, opts) => {
|
|
198
|
+
if (!opts.timeout) {
|
|
199
|
+
return noop
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
let s1 = null
|
|
188
|
-
const
|
|
203
|
+
const fastTimer = timers.setFastTimeout(() => {
|
|
189
204
|
// setImmediate is added to make sure that we prioritize socket error events over timeouts
|
|
190
205
|
s1 = setImmediate(() => {
|
|
191
|
-
onConnectTimeout(
|
|
206
|
+
onConnectTimeout(socketWeakRef.deref(), opts)
|
|
192
207
|
})
|
|
193
|
-
}, timeout)
|
|
208
|
+
}, opts.timeout)
|
|
194
209
|
return () => {
|
|
195
|
-
timers.
|
|
210
|
+
timers.clearFastTimeout(fastTimer)
|
|
196
211
|
clearImmediate(s1)
|
|
197
212
|
}
|
|
198
213
|
}
|
|
199
214
|
|
|
200
|
-
|
|
215
|
+
/**
|
|
216
|
+
* @param {net.Socket} socket
|
|
217
|
+
* @param {object} opts
|
|
218
|
+
* @param {number} opts.timeout
|
|
219
|
+
* @param {string} opts.hostname
|
|
220
|
+
* @param {number} opts.port
|
|
221
|
+
*/
|
|
222
|
+
function onConnectTimeout (socket, opts) {
|
|
201
223
|
let message = 'Connect Timeout Error'
|
|
202
224
|
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
|
|
203
|
-
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')}
|
|
225
|
+
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`
|
|
226
|
+
} else {
|
|
227
|
+
message += ` (attempted address: ${opts.hostname}:${opts.port},`
|
|
204
228
|
}
|
|
229
|
+
|
|
230
|
+
message += ` timeout: ${opts.timeout}ms)`
|
|
231
|
+
|
|
205
232
|
util.destroy(socket, new ConnectTimeoutError(message))
|
|
206
233
|
}
|
|
207
234
|
|
package/lib/core/constants.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const wellknownHeaderNames = [
|
|
3
|
+
/**
|
|
4
|
+
* @see https://developer.mozilla.org/docs/Web/HTTP/Headers
|
|
5
|
+
*/
|
|
6
|
+
const wellknownHeaderNames = /** @type {const} */ ([
|
|
8
7
|
'Accept',
|
|
9
8
|
'Accept-Encoding',
|
|
10
9
|
'Accept-Language',
|
|
@@ -100,7 +99,13 @@ const wellknownHeaderNames = [
|
|
|
100
99
|
'X-Powered-By',
|
|
101
100
|
'X-Requested-With',
|
|
102
101
|
'X-XSS-Protection'
|
|
103
|
-
]
|
|
102
|
+
])
|
|
103
|
+
|
|
104
|
+
/** @type {Record<typeof wellknownHeaderNames[number]|Lowercase<typeof wellknownHeaderNames[number]>, string>} */
|
|
105
|
+
const headerNameLowerCasedRecord = {}
|
|
106
|
+
|
|
107
|
+
// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
|
|
108
|
+
Object.setPrototypeOf(headerNameLowerCasedRecord, null)
|
|
104
109
|
|
|
105
110
|
for (let i = 0; i < wellknownHeaderNames.length; ++i) {
|
|
106
111
|
const key = wellknownHeaderNames[i]
|
|
@@ -109,9 +114,6 @@ for (let i = 0; i < wellknownHeaderNames.length; ++i) {
|
|
|
109
114
|
lowerCasedKey
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
|
|
113
|
-
Object.setPrototypeOf(headerNameLowerCasedRecord, null)
|
|
114
|
-
|
|
115
117
|
module.exports = {
|
|
116
118
|
wellknownHeaderNames,
|
|
117
119
|
headerNameLowerCasedRecord
|