undici 7.0.0-alpha.3 → 7.0.0-alpha.4
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 -1
- package/docs/docs/api/BalancedPool.md +1 -1
- package/docs/docs/api/CacheStore.md +16 -32
- package/docs/docs/api/Dispatcher.md +22 -2
- package/docs/docs/api/MockClient.md +1 -1
- package/docs/docs/api/Pool.md +1 -1
- package/docs/docs/api/api-lifecycle.md +2 -2
- package/docs/docs/best-practices/mocking-request.md +2 -2
- package/docs/docs/best-practices/proxy.md +1 -1
- package/index.d.ts +1 -1
- package/index.js +2 -1
- package/lib/api/api-request.js +1 -1
- package/lib/cache/memory-cache-store.js +108 -200
- package/lib/core/connect.js +5 -0
- package/lib/core/request.js +2 -2
- package/lib/core/util.js +13 -40
- package/lib/dispatcher/client-h2.js +53 -33
- package/lib/handler/cache-handler.js +112 -82
- package/lib/handler/cache-revalidation-handler.js +45 -13
- package/lib/handler/redirect-handler.js +5 -3
- package/lib/handler/retry-handler.js +3 -3
- package/lib/interceptor/cache.js +115 -94
- package/lib/interceptor/dns.js +71 -48
- package/lib/util/cache.js +38 -13
- package/lib/web/cookies/index.js +12 -1
- package/lib/web/cookies/parse.js +6 -1
- package/lib/web/fetch/body.js +1 -5
- package/lib/web/fetch/formdata-parser.js +70 -43
- package/lib/web/fetch/headers.js +1 -1
- package/lib/web/fetch/index.js +4 -6
- package/lib/web/fetch/webidl.js +12 -4
- package/package.json +2 -3
- package/types/cache-interceptor.d.ts +36 -32
- package/types/cookies.d.ts +2 -0
- package/types/dispatcher.d.ts +1 -1
- package/types/index.d.ts +0 -1
- package/types/interceptors.d.ts +0 -1
|
@@ -1,33 +1,26 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { Writable
|
|
3
|
+
const { Writable } = require('node:stream')
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
|
|
7
7
|
* @implements {CacheStore}
|
|
8
8
|
*
|
|
9
9
|
* @typedef {{
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* opts: import('../../types/cache-interceptor.d.ts').default.CacheStoreValue
|
|
14
|
-
* body: Buffer[]
|
|
10
|
+
* locked: boolean
|
|
11
|
+
* opts: import('../../types/cache-interceptor.d.ts').default.CachedResponse
|
|
12
|
+
* body?: Buffer[]
|
|
15
13
|
* }} MemoryStoreValue
|
|
16
14
|
*/
|
|
17
15
|
class MemoryCacheStore {
|
|
18
|
-
#
|
|
16
|
+
#maxCount = Infinity
|
|
19
17
|
|
|
20
18
|
#maxEntrySize = Infinity
|
|
21
19
|
|
|
22
|
-
/**
|
|
23
|
-
* @type {((err) => void) | undefined}
|
|
24
|
-
*/
|
|
25
|
-
#errorCallback = undefined
|
|
26
|
-
|
|
27
20
|
#entryCount = 0
|
|
28
21
|
|
|
29
22
|
/**
|
|
30
|
-
* @type {Map<string, Map<string, MemoryStoreValue>>}
|
|
23
|
+
* @type {Map<string, Map<string, MemoryStoreValue[]>>}
|
|
31
24
|
*/
|
|
32
25
|
#data = new Map()
|
|
33
26
|
|
|
@@ -40,15 +33,15 @@ class MemoryCacheStore {
|
|
|
40
33
|
throw new TypeError('MemoryCacheStore options must be an object')
|
|
41
34
|
}
|
|
42
35
|
|
|
43
|
-
if (opts.
|
|
36
|
+
if (opts.maxCount !== undefined) {
|
|
44
37
|
if (
|
|
45
|
-
typeof opts.
|
|
46
|
-
!Number.isInteger(opts.
|
|
47
|
-
opts.
|
|
38
|
+
typeof opts.maxCount !== 'number' ||
|
|
39
|
+
!Number.isInteger(opts.maxCount) ||
|
|
40
|
+
opts.maxCount < 0
|
|
48
41
|
) {
|
|
49
|
-
throw new TypeError('MemoryCacheStore options.
|
|
42
|
+
throw new TypeError('MemoryCacheStore options.maxCount must be a non-negative integer')
|
|
50
43
|
}
|
|
51
|
-
this.#
|
|
44
|
+
this.#maxCount = opts.maxCount
|
|
52
45
|
}
|
|
53
46
|
|
|
54
47
|
if (opts.maxEntrySize !== undefined) {
|
|
@@ -61,51 +54,44 @@ class MemoryCacheStore {
|
|
|
61
54
|
}
|
|
62
55
|
this.#maxEntrySize = opts.maxEntrySize
|
|
63
56
|
}
|
|
64
|
-
|
|
65
|
-
if (opts.errorCallback !== undefined) {
|
|
66
|
-
if (typeof opts.errorCallback !== 'function') {
|
|
67
|
-
throw new TypeError('MemoryCacheStore options.errorCallback must be a function')
|
|
68
|
-
}
|
|
69
|
-
this.#errorCallback = opts.errorCallback
|
|
70
|
-
}
|
|
71
57
|
}
|
|
72
58
|
}
|
|
73
59
|
|
|
74
60
|
get isFull () {
|
|
75
|
-
return this.#entryCount >= this.#
|
|
61
|
+
return this.#entryCount >= this.#maxCount
|
|
76
62
|
}
|
|
77
63
|
|
|
78
64
|
/**
|
|
79
|
-
* @param {import('../../types/
|
|
80
|
-
* @returns {import('../../types/cache-interceptor.d.ts').default.
|
|
65
|
+
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
|
|
66
|
+
* @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}
|
|
81
67
|
*/
|
|
82
|
-
|
|
83
|
-
if (typeof
|
|
84
|
-
throw new TypeError(`expected
|
|
68
|
+
get (key) {
|
|
69
|
+
if (typeof key !== 'object') {
|
|
70
|
+
throw new TypeError(`expected key to be object, got ${typeof key}`)
|
|
85
71
|
}
|
|
86
72
|
|
|
87
|
-
const values = this.#getValuesForRequest(
|
|
73
|
+
const values = this.#getValuesForRequest(key, false)
|
|
88
74
|
if (!values) {
|
|
89
75
|
return undefined
|
|
90
76
|
}
|
|
91
77
|
|
|
92
|
-
const value = this.#findValue(
|
|
78
|
+
const value = this.#findValue(key, values)
|
|
93
79
|
|
|
94
|
-
if (!value || value.
|
|
80
|
+
if (!value || value.locked) {
|
|
95
81
|
return undefined
|
|
96
82
|
}
|
|
97
83
|
|
|
98
|
-
return
|
|
84
|
+
return { ...value.opts, body: value.body }
|
|
99
85
|
}
|
|
100
86
|
|
|
101
87
|
/**
|
|
102
|
-
* @param {import('../../types/
|
|
103
|
-
* @param {import('../../types/cache-interceptor.d.ts').default.
|
|
104
|
-
* @returns {
|
|
88
|
+
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
|
|
89
|
+
* @param {import('../../types/cache-interceptor.d.ts').default.CachedResponse} opts
|
|
90
|
+
* @returns {Writable | undefined}
|
|
105
91
|
*/
|
|
106
|
-
createWriteStream (
|
|
107
|
-
if (typeof
|
|
108
|
-
throw new TypeError(`expected
|
|
92
|
+
createWriteStream (key, opts) {
|
|
93
|
+
if (typeof key !== 'object') {
|
|
94
|
+
throw new TypeError(`expected key to be object, got ${typeof key}`)
|
|
109
95
|
}
|
|
110
96
|
if (typeof opts !== 'object') {
|
|
111
97
|
throw new TypeError(`expected value to be object, got ${typeof opts}`)
|
|
@@ -115,9 +101,13 @@ class MemoryCacheStore {
|
|
|
115
101
|
return undefined
|
|
116
102
|
}
|
|
117
103
|
|
|
118
|
-
const values = this.#getValuesForRequest(
|
|
104
|
+
const values = this.#getValuesForRequest(key, true)
|
|
119
105
|
|
|
120
|
-
|
|
106
|
+
/**
|
|
107
|
+
* @type {(MemoryStoreValue & { index: number }) | undefined}
|
|
108
|
+
*/
|
|
109
|
+
let value = this.#findValue(key, values)
|
|
110
|
+
let valueIndex = value?.index
|
|
121
111
|
if (!value) {
|
|
122
112
|
// The value doesn't already exist, meaning we haven't cached this
|
|
123
113
|
// response before. Let's assign it a value and insert it into our data
|
|
@@ -131,11 +121,8 @@ class MemoryCacheStore {
|
|
|
131
121
|
this.#entryCount++
|
|
132
122
|
|
|
133
123
|
value = {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
writeLock: false,
|
|
137
|
-
opts,
|
|
138
|
-
body: []
|
|
124
|
+
locked: true,
|
|
125
|
+
opts
|
|
139
126
|
}
|
|
140
127
|
|
|
141
128
|
// We want to sort our responses in decending order by their deleteAt
|
|
@@ -147,9 +134,11 @@ class MemoryCacheStore {
|
|
|
147
134
|
// Our value is either the only response for this path or our deleteAt
|
|
148
135
|
// time is sooner than all the other responses
|
|
149
136
|
values.push(value)
|
|
137
|
+
valueIndex = values.length - 1
|
|
150
138
|
} else if (opts.deleteAt >= values[0].deleteAt) {
|
|
151
139
|
// Our deleteAt is later than everyone elses
|
|
152
140
|
values.unshift(value)
|
|
141
|
+
valueIndex = 0
|
|
153
142
|
} else {
|
|
154
143
|
// We're neither in the front or the end, let's just binary search to
|
|
155
144
|
// find our stop we need to be in
|
|
@@ -165,6 +154,7 @@ class MemoryCacheStore {
|
|
|
165
154
|
const middleValue = values[middleIndex]
|
|
166
155
|
if (opts.deleteAt === middleIndex) {
|
|
167
156
|
values.splice(middleIndex, 0, value)
|
|
157
|
+
valueIndex = middleIndex
|
|
168
158
|
break
|
|
169
159
|
} else if (opts.deleteAt > middleValue.opts.deleteAt) {
|
|
170
160
|
endIndex = middleIndex
|
|
@@ -178,7 +168,7 @@ class MemoryCacheStore {
|
|
|
178
168
|
} else {
|
|
179
169
|
// Check if there's already another request writing to the value or
|
|
180
170
|
// a request reading from it
|
|
181
|
-
if (value.
|
|
171
|
+
if (value.locked) {
|
|
182
172
|
return undefined
|
|
183
173
|
}
|
|
184
174
|
|
|
@@ -186,70 +176,98 @@ class MemoryCacheStore {
|
|
|
186
176
|
value.body = []
|
|
187
177
|
}
|
|
188
178
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
179
|
+
let currentSize = 0
|
|
180
|
+
/**
|
|
181
|
+
* @type {Buffer[] | null}
|
|
182
|
+
*/
|
|
183
|
+
let body = key.method !== 'HEAD' ? [] : null
|
|
184
|
+
const maxEntrySize = this.#maxEntrySize
|
|
193
185
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
186
|
+
const writable = new Writable({
|
|
187
|
+
write (chunk, encoding, callback) {
|
|
188
|
+
if (key.method === 'HEAD') {
|
|
189
|
+
throw new Error('HEAD request shouldn\'t have a body')
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!body) {
|
|
193
|
+
return callback()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (typeof chunk === 'string') {
|
|
197
|
+
chunk = Buffer.from(chunk, encoding)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
currentSize += chunk.byteLength
|
|
201
|
+
|
|
202
|
+
if (currentSize >= maxEntrySize) {
|
|
203
|
+
body = null
|
|
204
|
+
this.end()
|
|
205
|
+
shiftAtIndex(values, valueIndex)
|
|
206
|
+
return callback()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
body.push(chunk)
|
|
210
|
+
callback()
|
|
211
|
+
},
|
|
212
|
+
final (callback) {
|
|
213
|
+
value.locked = false
|
|
214
|
+
if (body !== null) {
|
|
215
|
+
value.body = body
|
|
216
|
+
}
|
|
201
217
|
|
|
202
|
-
|
|
203
|
-
|
|
218
|
+
callback()
|
|
219
|
+
}
|
|
204
220
|
})
|
|
205
221
|
|
|
206
222
|
return writable
|
|
207
223
|
}
|
|
208
224
|
|
|
209
225
|
/**
|
|
210
|
-
* @param {
|
|
226
|
+
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
|
|
211
227
|
*/
|
|
212
|
-
|
|
213
|
-
this.#data.delete(origin)
|
|
228
|
+
delete (key) {
|
|
229
|
+
this.#data.delete(`${key.origin}:${key.path}`)
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
/**
|
|
217
233
|
* Gets all of the requests of the same origin, path, and method. Does not
|
|
218
234
|
* take the `vary` property into account.
|
|
219
|
-
* @param {import('../../types/
|
|
235
|
+
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
|
|
220
236
|
* @param {boolean} [makeIfDoesntExist=false]
|
|
237
|
+
* @returns {MemoryStoreValue[] | undefined}
|
|
221
238
|
*/
|
|
222
|
-
#getValuesForRequest (
|
|
239
|
+
#getValuesForRequest (key, makeIfDoesntExist) {
|
|
223
240
|
// https://www.rfc-editor.org/rfc/rfc9111.html#section-2-3
|
|
224
|
-
|
|
241
|
+
const topLevelKey = `${key.origin}:${key.path}`
|
|
242
|
+
let cachedPaths = this.#data.get(topLevelKey)
|
|
225
243
|
if (!cachedPaths) {
|
|
226
244
|
if (!makeIfDoesntExist) {
|
|
227
245
|
return undefined
|
|
228
246
|
}
|
|
229
247
|
|
|
230
248
|
cachedPaths = new Map()
|
|
231
|
-
this.#data.set(
|
|
249
|
+
this.#data.set(topLevelKey, cachedPaths)
|
|
232
250
|
}
|
|
233
251
|
|
|
234
|
-
let
|
|
235
|
-
if (!
|
|
236
|
-
|
|
237
|
-
cachedPaths.set(
|
|
252
|
+
let value = cachedPaths.get(key.method)
|
|
253
|
+
if (!value && makeIfDoesntExist) {
|
|
254
|
+
value = []
|
|
255
|
+
cachedPaths.set(key.method, value)
|
|
238
256
|
}
|
|
239
257
|
|
|
240
|
-
return
|
|
258
|
+
return value
|
|
241
259
|
}
|
|
242
260
|
|
|
243
261
|
/**
|
|
244
262
|
* Given a list of values of a certain request, this decides the best value
|
|
245
263
|
* to respond with.
|
|
246
|
-
* @param {import('../../types/
|
|
264
|
+
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} req
|
|
247
265
|
* @param {MemoryStoreValue[]} values
|
|
248
|
-
* @returns {MemoryStoreValue | undefined}
|
|
266
|
+
* @returns {(MemoryStoreValue & { index: number }) | undefined}
|
|
249
267
|
*/
|
|
250
268
|
#findValue (req, values) {
|
|
251
269
|
/**
|
|
252
|
-
* @type {MemoryStoreValue}
|
|
270
|
+
* @type {MemoryStoreValue | undefined}
|
|
253
271
|
*/
|
|
254
272
|
let value
|
|
255
273
|
const now = Date.now()
|
|
@@ -280,7 +298,10 @@ class MemoryCacheStore {
|
|
|
280
298
|
}
|
|
281
299
|
|
|
282
300
|
if (matches) {
|
|
283
|
-
value =
|
|
301
|
+
value = {
|
|
302
|
+
...current,
|
|
303
|
+
index: i
|
|
304
|
+
}
|
|
284
305
|
break
|
|
285
306
|
}
|
|
286
307
|
}
|
|
@@ -289,129 +310,16 @@ class MemoryCacheStore {
|
|
|
289
310
|
}
|
|
290
311
|
}
|
|
291
312
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
*/
|
|
300
|
-
#chunksToSend = []
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* @param {MemoryStoreValue} value
|
|
304
|
-
*/
|
|
305
|
-
constructor (value) {
|
|
306
|
-
super()
|
|
307
|
-
|
|
308
|
-
if (value.readLock) {
|
|
309
|
-
throw new Error('can\'t read a locked value')
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
this.#value = value
|
|
313
|
-
this.#chunksToSend = value?.body ? [...value.body, null] : [null]
|
|
314
|
-
|
|
315
|
-
this.#value.readers++
|
|
316
|
-
this.#value.writeLock = true
|
|
317
|
-
|
|
318
|
-
this.on('close', () => {
|
|
319
|
-
this.#value.readers--
|
|
320
|
-
|
|
321
|
-
if (this.#value.readers === 0) {
|
|
322
|
-
this.#value.writeLock = false
|
|
323
|
-
}
|
|
324
|
-
})
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
get value () {
|
|
328
|
-
return this.#value.opts
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* @param {number} size
|
|
333
|
-
*/
|
|
334
|
-
_read (size) {
|
|
335
|
-
if (this.#chunksToSend.length === 0) {
|
|
336
|
-
throw new Error('no chunks left to read, stream should have closed')
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (size > this.#chunksToSend.length) {
|
|
340
|
-
size = this.#chunksToSend.length
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
for (let i = 0; i < size; i++) {
|
|
344
|
-
this.push(this.#chunksToSend.shift())
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
class MemoryStoreWritableStream extends Writable {
|
|
350
|
-
/**
|
|
351
|
-
* @type {MemoryStoreValue}
|
|
352
|
-
*/
|
|
353
|
-
#value
|
|
354
|
-
#currentSize = 0
|
|
355
|
-
#maxEntrySize = 0
|
|
356
|
-
/**
|
|
357
|
-
* @type {Buffer[]|null}
|
|
358
|
-
*/
|
|
359
|
-
#body = []
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* @param {MemoryStoreValue} value
|
|
363
|
-
* @param {number} maxEntrySize
|
|
364
|
-
*/
|
|
365
|
-
constructor (value, maxEntrySize) {
|
|
366
|
-
super()
|
|
367
|
-
this.#value = value
|
|
368
|
-
this.#value.readLock = true
|
|
369
|
-
this.#maxEntrySize = maxEntrySize ?? Infinity
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
get rawTrailers () {
|
|
373
|
-
return this.#value.opts.rawTrailers
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* @param {string[] | undefined} trailers
|
|
378
|
-
*/
|
|
379
|
-
set rawTrailers (trailers) {
|
|
380
|
-
this.#value.opts.rawTrailers = trailers
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* @param {Buffer} chunk
|
|
385
|
-
* @param {string} encoding
|
|
386
|
-
* @param {BufferEncoding} encoding
|
|
387
|
-
*/
|
|
388
|
-
_write (chunk, encoding, callback) {
|
|
389
|
-
if (typeof chunk === 'string') {
|
|
390
|
-
chunk = Buffer.from(chunk, encoding)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
this.#currentSize += chunk.byteLength
|
|
394
|
-
if (this.#currentSize < this.#maxEntrySize) {
|
|
395
|
-
this.#body.push(chunk)
|
|
396
|
-
} else {
|
|
397
|
-
this.#body = null // release memory as early as possible
|
|
398
|
-
this.emit('bodyOversized')
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
callback()
|
|
313
|
+
/**
|
|
314
|
+
* @param {any[]} array Array to modify
|
|
315
|
+
* @param {number} idx Index to delete
|
|
316
|
+
*/
|
|
317
|
+
function shiftAtIndex (array, idx) {
|
|
318
|
+
for (let i = idx + 1; idx < array.length; i++) {
|
|
319
|
+
array[i - 1] = array[i]
|
|
402
320
|
}
|
|
403
321
|
|
|
404
|
-
|
|
405
|
-
* @param {() => void} callback
|
|
406
|
-
*/
|
|
407
|
-
_final (callback) {
|
|
408
|
-
if (this.#currentSize < this.#maxEntrySize) {
|
|
409
|
-
this.#value.readLock = false
|
|
410
|
-
this.#value.body = this.#body
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
callback()
|
|
414
|
-
}
|
|
322
|
+
array.length--
|
|
415
323
|
}
|
|
416
324
|
|
|
417
325
|
module.exports = MemoryCacheStore
|
package/lib/core/connect.js
CHANGED
|
@@ -220,6 +220,11 @@ const setupConnectTimeout = process.platform === 'win32'
|
|
|
220
220
|
* @param {number} opts.port
|
|
221
221
|
*/
|
|
222
222
|
function onConnectTimeout (socket, opts) {
|
|
223
|
+
// The socket could be already garbage collected
|
|
224
|
+
if (socket == null) {
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
|
|
223
228
|
let message = 'Connect Timeout Error'
|
|
224
229
|
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
|
|
225
230
|
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`
|
package/lib/core/request.js
CHANGED
|
@@ -130,7 +130,6 @@ class Request {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
this.completed = false
|
|
133
|
-
|
|
134
133
|
this.aborted = false
|
|
135
134
|
|
|
136
135
|
this.upgrade = upgrade || null
|
|
@@ -143,7 +142,7 @@ class Request {
|
|
|
143
142
|
? method === 'HEAD' || method === 'GET'
|
|
144
143
|
: idempotent
|
|
145
144
|
|
|
146
|
-
this.blocking = blocking
|
|
145
|
+
this.blocking = blocking ?? this.method !== 'HEAD'
|
|
147
146
|
|
|
148
147
|
this.reset = reset == null ? null : reset
|
|
149
148
|
|
|
@@ -272,6 +271,7 @@ class Request {
|
|
|
272
271
|
this.onFinally()
|
|
273
272
|
|
|
274
273
|
assert(!this.aborted)
|
|
274
|
+
assert(!this.completed)
|
|
275
275
|
|
|
276
276
|
this.completed = true
|
|
277
277
|
if (channels.trailers.hasSubscribers) {
|
package/lib/core/util.js
CHANGED
|
@@ -10,7 +10,7 @@ const nodeUtil = require('node:util')
|
|
|
10
10
|
const { stringify } = require('node:querystring')
|
|
11
11
|
const { EventEmitter: EE } = require('node:events')
|
|
12
12
|
const { InvalidArgumentError } = require('./errors')
|
|
13
|
-
const { headerNameLowerCasedRecord
|
|
13
|
+
const { headerNameLowerCasedRecord } = require('./constants')
|
|
14
14
|
const { tree } = require('./tree')
|
|
15
15
|
|
|
16
16
|
const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v))
|
|
@@ -436,44 +436,6 @@ function parseHeaders (headers, obj) {
|
|
|
436
436
|
return obj
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
/**
|
|
440
|
-
* @param {Record<string, string | string[]>} headers
|
|
441
|
-
* @returns {(Buffer | Buffer[])[]}
|
|
442
|
-
*/
|
|
443
|
-
function encodeHeaders (headers) {
|
|
444
|
-
const headerNames = Object.keys(headers)
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* @type {Buffer[]|Buffer[][]}
|
|
448
|
-
*/
|
|
449
|
-
const rawHeaders = new Array(headerNames.length * 2)
|
|
450
|
-
|
|
451
|
-
let rawHeadersIndex = 0
|
|
452
|
-
for (const header of headerNames) {
|
|
453
|
-
let rawValue
|
|
454
|
-
const value = headers[header]
|
|
455
|
-
if (Array.isArray(value)) {
|
|
456
|
-
rawValue = new Array(value.length)
|
|
457
|
-
|
|
458
|
-
for (let i = 0; i < value.length; i++) {
|
|
459
|
-
rawValue[i] = Buffer.from(value[i])
|
|
460
|
-
}
|
|
461
|
-
} else {
|
|
462
|
-
rawValue = Buffer.from(value)
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const headerBuffer = getHeaderNameAsBuffer(header)
|
|
466
|
-
|
|
467
|
-
rawHeaders[rawHeadersIndex] = headerBuffer
|
|
468
|
-
rawHeadersIndex++
|
|
469
|
-
|
|
470
|
-
rawHeaders[rawHeadersIndex] = rawValue
|
|
471
|
-
rawHeadersIndex++
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return rawHeaders
|
|
475
|
-
}
|
|
476
|
-
|
|
477
439
|
/**
|
|
478
440
|
* @param {Buffer[]} headers
|
|
479
441
|
* @returns {string[]}
|
|
@@ -516,6 +478,17 @@ function parseRawHeaders (headers) {
|
|
|
516
478
|
return ret
|
|
517
479
|
}
|
|
518
480
|
|
|
481
|
+
/**
|
|
482
|
+
* @param {string[]} headers
|
|
483
|
+
* @param {Buffer[]} headers
|
|
484
|
+
*/
|
|
485
|
+
function encodeRawHeaders (headers) {
|
|
486
|
+
if (!Array.isArray(headers)) {
|
|
487
|
+
throw new TypeError('expected headers to be an array')
|
|
488
|
+
}
|
|
489
|
+
return headers.map(x => Buffer.from(x))
|
|
490
|
+
}
|
|
491
|
+
|
|
519
492
|
/**
|
|
520
493
|
* @param {*} buffer
|
|
521
494
|
* @returns {buffer is Buffer}
|
|
@@ -901,8 +874,8 @@ module.exports = {
|
|
|
901
874
|
removeAllListeners,
|
|
902
875
|
errorRequest,
|
|
903
876
|
parseRawHeaders,
|
|
877
|
+
encodeRawHeaders,
|
|
904
878
|
parseHeaders,
|
|
905
|
-
encodeHeaders,
|
|
906
879
|
parseKeepAliveTimeout,
|
|
907
880
|
destroy,
|
|
908
881
|
bodyLength,
|