undici 8.8.0 → 8.9.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.
- package/lib/cache/memory-cache-store.js +54 -9
- package/lib/cache/sqlite-cache-store.js +7 -1
- package/lib/core/request.js +12 -1
- package/lib/dispatcher/client-h1.js +11 -2
- package/lib/global.js +36 -16
- package/lib/handler/cache-handler.js +236 -116
- package/lib/handler/cache-revalidation-handler.js +3 -3
- package/lib/handler/retry-handler.js +24 -0
- package/lib/interceptor/cache.js +152 -62
- package/lib/interceptor/decompress.js +5 -2
- package/lib/util/cache.js +328 -66
- package/lib/util/date.js +23 -6
- package/lib/web/cookies/util.js +78 -8
- package/package.json +1 -1
|
@@ -218,17 +218,62 @@ class MemoryCacheStore extends EventEmitter {
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
function findEntry (key, entries, now) {
|
|
221
|
-
|
|
222
|
-
entry
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
221
|
+
for (let i = 0; i < entries.length; i++) {
|
|
222
|
+
const entry = entries[i]
|
|
223
|
+
if (
|
|
224
|
+
entry.deleteAt > now &&
|
|
225
|
+
entry.method === key.method &&
|
|
226
|
+
varyMatches(key, entry)
|
|
227
|
+
) {
|
|
228
|
+
return entry
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function varyMatches (key, entry) {
|
|
234
|
+
if (entry.vary == null) {
|
|
235
|
+
return true
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
for (const headerName in entry.vary) {
|
|
239
|
+
if (Object.hasOwn(entry.vary, headerName) && !headerValueEquals(key.headers?.[headerName], entry.vary[headerName])) {
|
|
240
|
+
return false
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return true
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {string|string[]|null|undefined} lhs
|
|
249
|
+
* @param {string|string[]|null|undefined} rhs
|
|
250
|
+
* @returns {boolean}
|
|
251
|
+
*/
|
|
252
|
+
function headerValueEquals (lhs, rhs) {
|
|
253
|
+
if (lhs == null && rhs == null) {
|
|
254
|
+
return true
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if ((lhs == null && rhs != null) ||
|
|
258
|
+
(lhs != null && rhs == null)) {
|
|
259
|
+
return false
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (Array.isArray(lhs) && Array.isArray(rhs)) {
|
|
263
|
+
if (lhs.length !== rhs.length) {
|
|
264
|
+
return false
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
268
|
+
if (lhs[i] !== rhs[i]) {
|
|
269
|
+
return false
|
|
227
270
|
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
228
275
|
|
|
229
|
-
|
|
230
|
-
}))
|
|
231
|
-
))
|
|
276
|
+
return lhs === rhs
|
|
232
277
|
}
|
|
233
278
|
|
|
234
279
|
module.exports = MemoryCacheStore
|
|
@@ -456,7 +456,13 @@ function headerValueEquals (lhs, rhs) {
|
|
|
456
456
|
return false
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
-
|
|
459
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
460
|
+
if (lhs[i] !== rhs[i]) {
|
|
461
|
+
return false
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return true
|
|
460
466
|
}
|
|
461
467
|
|
|
462
468
|
return lhs === rhs
|
package/lib/core/request.js
CHANGED
|
@@ -472,7 +472,13 @@ function processHeader (request, key, val) {
|
|
|
472
472
|
} else if (typeof val[i] === 'object') {
|
|
473
473
|
throw new InvalidArgumentError(`invalid ${key} header`)
|
|
474
474
|
} else {
|
|
475
|
-
|
|
475
|
+
// Coerce primitives (and reject unsafe coercions such as functions
|
|
476
|
+
// with a crafted toString/Symbol.toPrimitive).
|
|
477
|
+
const str = `${val[i]}`
|
|
478
|
+
if (!isValidHeaderValue(str)) {
|
|
479
|
+
throw new InvalidArgumentError(`invalid ${key} header`)
|
|
480
|
+
}
|
|
481
|
+
arr.push(str)
|
|
476
482
|
}
|
|
477
483
|
}
|
|
478
484
|
val = arr
|
|
@@ -483,7 +489,12 @@ function processHeader (request, key, val) {
|
|
|
483
489
|
} else if (val === null) {
|
|
484
490
|
val = ''
|
|
485
491
|
} else {
|
|
492
|
+
// Coerce primitives (and reject unsafe coercions such as functions
|
|
493
|
+
// with a crafted toString/Symbol.toPrimitive).
|
|
486
494
|
val = `${val}`
|
|
495
|
+
if (!isValidHeaderValue(val)) {
|
|
496
|
+
throw new InvalidArgumentError(`invalid ${key} header`)
|
|
497
|
+
}
|
|
487
498
|
}
|
|
488
499
|
|
|
489
500
|
if (headerName === 'host') {
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
RequestContentLengthMismatchError,
|
|
11
11
|
ResponseContentLengthMismatchError,
|
|
12
12
|
RequestAbortedError,
|
|
13
|
+
InvalidArgumentError,
|
|
13
14
|
HeadersTimeoutError,
|
|
14
15
|
HeadersOverflowError,
|
|
15
16
|
SocketError,
|
|
@@ -1200,8 +1201,16 @@ function writeH1 (client, request) {
|
|
|
1200
1201
|
}
|
|
1201
1202
|
body = bodyStream.stream
|
|
1202
1203
|
contentLength = bodyStream.length
|
|
1203
|
-
} else if (util.isBlobLike(body) && request.contentType == null
|
|
1204
|
-
|
|
1204
|
+
} else if (util.isBlobLike(body) && request.contentType == null) {
|
|
1205
|
+
const contentType = body.type
|
|
1206
|
+
if (contentType) {
|
|
1207
|
+
const contentTypeValue = `${contentType}`
|
|
1208
|
+
if (!util.isValidHeaderValue(contentTypeValue)) {
|
|
1209
|
+
util.errorRequest(client, request, new InvalidArgumentError('invalid content-type header'))
|
|
1210
|
+
return false
|
|
1211
|
+
}
|
|
1212
|
+
headers.push('content-type', contentTypeValue)
|
|
1213
|
+
}
|
|
1205
1214
|
}
|
|
1206
1215
|
|
|
1207
1216
|
if (body && typeof body.read === 'function') {
|
package/lib/global.js
CHANGED
|
@@ -8,6 +8,9 @@ const { InvalidArgumentError } = require('./core/errors')
|
|
|
8
8
|
const Agent = require('./dispatcher/agent')
|
|
9
9
|
const Dispatcher1Wrapper = require('./dispatcher/dispatcher1-wrapper')
|
|
10
10
|
|
|
11
|
+
// Fallback storage for when globalThis is not extensible (e.g. frozen)
|
|
12
|
+
let fallbackDispatcher
|
|
13
|
+
|
|
11
14
|
if (getGlobalDispatcher() === undefined) {
|
|
12
15
|
setGlobalDispatcher(new Agent())
|
|
13
16
|
}
|
|
@@ -17,25 +20,42 @@ function setGlobalDispatcher (agent) {
|
|
|
17
20
|
throw new InvalidArgumentError('Argument agent must implement Agent')
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
try {
|
|
24
|
+
Object.defineProperty(globalThis, globalDispatcher, {
|
|
25
|
+
value: agent,
|
|
26
|
+
writable: true,
|
|
27
|
+
enumerable: false,
|
|
28
|
+
configurable: false
|
|
29
|
+
})
|
|
30
|
+
} catch (err) {
|
|
31
|
+
// globalThis is not extensible (e.g. Object.freeze(globalThis))
|
|
32
|
+
// Use fallback storage instead
|
|
33
|
+
if (err instanceof TypeError) {
|
|
34
|
+
fallbackDispatcher = agent
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
throw err
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const legacyAgent = agent instanceof Dispatcher1Wrapper ? agent : new Dispatcher1Wrapper(agent)
|
|
42
|
+
|
|
43
|
+
Object.defineProperty(globalThis, legacyGlobalDispatcher, {
|
|
44
|
+
value: legacyAgent,
|
|
45
|
+
writable: true,
|
|
46
|
+
enumerable: false,
|
|
47
|
+
configurable: false
|
|
48
|
+
})
|
|
49
|
+
} catch (err) {
|
|
50
|
+
// globalThis is not extensible; fallback storage is already set
|
|
51
|
+
if (!(err instanceof TypeError)) {
|
|
52
|
+
throw err
|
|
53
|
+
}
|
|
54
|
+
}
|
|
35
55
|
}
|
|
36
56
|
|
|
37
57
|
function getGlobalDispatcher () {
|
|
38
|
-
return globalThis[globalDispatcher]
|
|
58
|
+
return globalThis[globalDispatcher] ?? fallbackDispatcher
|
|
39
59
|
}
|
|
40
60
|
|
|
41
61
|
// These are the globals that can be installed by undici.install().
|