undici 5.18.0 → 5.19.1
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/docs/api/MockPool.md +2 -1
- package/lib/cookies/index.js +2 -1
- package/lib/core/request.js +3 -0
- package/lib/core/util.js +20 -3
- package/lib/fetch/headers.js +71 -12
- package/lib/fetch/request.js +6 -0
- package/lib/mock/mock-utils.js +5 -1
- package/package.json +1 -1
- package/types/connector.d.ts +2 -0
- package/types/fetch.d.ts +1 -0
package/docs/api/MockPool.md
CHANGED
|
@@ -63,7 +63,8 @@ Returns: `MockInterceptor` corresponding to the input options.
|
|
|
63
63
|
|
|
64
64
|
We can define the behaviour of an intercepted request with the following options.
|
|
65
65
|
|
|
66
|
-
* **reply** `(statusCode: number, replyData: string | Buffer | object | MockInterceptor.MockResponseDataHandler, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. You can define
|
|
66
|
+
* **reply** `(statusCode: number, replyData: string | Buffer | object | MockInterceptor.MockResponseDataHandler, responseOptions?: MockResponseOptions) => MockScope` - define a reply for a matching request. You can define the replyData as a callback to read incoming request data. Default for `responseOptions` is `{}`.
|
|
67
|
+
* **reply** `(callback: MockInterceptor.MockReplyOptionsCallback) => MockScope` - define a reply for a matching request, allowing dynamic mocking of all reply options rather than just the data.
|
|
67
68
|
* **replyWithError** `(error: Error) => MockScope` - define an error for a matching request to throw.
|
|
68
69
|
* **defaultReplyHeaders** `(headers: Record<string, string>) => MockInterceptor` - define default headers to be included in subsequent replies. These are in addition to headers on a specific reply.
|
|
69
70
|
* **defaultReplyTrailers** `(trailers: Record<string, string>) => MockInterceptor` - define default trailers to be included in subsequent replies. These are in addition to trailers on a specific reply.
|
package/lib/cookies/index.js
CHANGED
|
@@ -83,7 +83,8 @@ function getSetCookies (headers) {
|
|
|
83
83
|
return []
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
// In older versions of undici, cookies is a list of name:value.
|
|
87
|
+
return cookies.map((pair) => parseSetCookie(Array.isArray(pair) ? pair[1] : pair))
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
/**
|
package/lib/core/request.js
CHANGED
|
@@ -304,6 +304,9 @@ function processHeader (request, key, val) {
|
|
|
304
304
|
key.length === 4 &&
|
|
305
305
|
key.toLowerCase() === 'host'
|
|
306
306
|
) {
|
|
307
|
+
if (headerCharRegex.exec(val) !== null) {
|
|
308
|
+
throw new InvalidArgumentError(`invalid ${key} header`)
|
|
309
|
+
}
|
|
307
310
|
// Consumed by Client
|
|
308
311
|
request.host = val
|
|
309
312
|
} else if (
|
package/lib/core/util.js
CHANGED
|
@@ -213,25 +213,42 @@ function parseHeaders (headers, obj = {}) {
|
|
|
213
213
|
for (let i = 0; i < headers.length; i += 2) {
|
|
214
214
|
const key = headers[i].toString().toLowerCase()
|
|
215
215
|
let val = obj[key]
|
|
216
|
+
|
|
217
|
+
const encoding = key.length === 19 && key === 'content-disposition'
|
|
218
|
+
? 'latin1'
|
|
219
|
+
: 'utf8'
|
|
220
|
+
|
|
216
221
|
if (!val) {
|
|
217
222
|
if (Array.isArray(headers[i + 1])) {
|
|
218
223
|
obj[key] = headers[i + 1]
|
|
219
224
|
} else {
|
|
220
|
-
obj[key] = headers[i + 1].toString()
|
|
225
|
+
obj[key] = headers[i + 1].toString(encoding)
|
|
221
226
|
}
|
|
222
227
|
} else {
|
|
223
228
|
if (!Array.isArray(val)) {
|
|
224
229
|
val = [val]
|
|
225
230
|
obj[key] = val
|
|
226
231
|
}
|
|
227
|
-
val.push(headers[i + 1].toString())
|
|
232
|
+
val.push(headers[i + 1].toString(encoding))
|
|
228
233
|
}
|
|
229
234
|
}
|
|
230
235
|
return obj
|
|
231
236
|
}
|
|
232
237
|
|
|
233
238
|
function parseRawHeaders (headers) {
|
|
234
|
-
|
|
239
|
+
const ret = []
|
|
240
|
+
for (let n = 0; n < headers.length; n += 2) {
|
|
241
|
+
const key = headers[n + 0].toString()
|
|
242
|
+
|
|
243
|
+
const encoding = key.length === 19 && key.toLowerCase() === 'content-disposition'
|
|
244
|
+
? 'latin1'
|
|
245
|
+
: 'utf8'
|
|
246
|
+
|
|
247
|
+
const val = headers[n + 1].toString(encoding)
|
|
248
|
+
|
|
249
|
+
ret.push(key, val)
|
|
250
|
+
}
|
|
251
|
+
return ret
|
|
235
252
|
}
|
|
236
253
|
|
|
237
254
|
function isBuffer (buffer) {
|
package/lib/fetch/headers.js
CHANGED
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
isValidHeaderValue
|
|
12
12
|
} = require('./util')
|
|
13
13
|
const { webidl } = require('./webidl')
|
|
14
|
+
const assert = require('assert')
|
|
14
15
|
|
|
15
16
|
const kHeadersMap = Symbol('headers map')
|
|
16
17
|
const kHeadersSortedMap = Symbol('headers map sorted')
|
|
@@ -23,10 +24,12 @@ function headerValueNormalize (potentialValue) {
|
|
|
23
24
|
// To normalize a byte sequence potentialValue, remove
|
|
24
25
|
// any leading and trailing HTTP whitespace bytes from
|
|
25
26
|
// potentialValue.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
|
|
28
|
+
// Trimming the end with `.replace()` and a RegExp is typically subject to
|
|
29
|
+
// ReDoS. This is safer and faster.
|
|
30
|
+
let i = potentialValue.length
|
|
31
|
+
while (/[\r\n\t ]/.test(potentialValue.charAt(--i)));
|
|
32
|
+
return potentialValue.slice(0, i + 1).replace(/^[\r\n\t ]+/, '')
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
function fill (headers, object) {
|
|
@@ -115,7 +118,7 @@ class HeadersList {
|
|
|
115
118
|
|
|
116
119
|
if (lowercaseName === 'set-cookie') {
|
|
117
120
|
this.cookies ??= []
|
|
118
|
-
this.cookies.push(
|
|
121
|
+
this.cookies.push(value)
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
|
|
@@ -125,7 +128,7 @@ class HeadersList {
|
|
|
125
128
|
const lowercaseName = name.toLowerCase()
|
|
126
129
|
|
|
127
130
|
if (lowercaseName === 'set-cookie') {
|
|
128
|
-
this.cookies = [
|
|
131
|
+
this.cookies = [value]
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
// 1. If list contains name, then set the value of
|
|
@@ -383,18 +386,74 @@ class Headers {
|
|
|
383
386
|
return this[kHeadersList].set(name, value)
|
|
384
387
|
}
|
|
385
388
|
|
|
389
|
+
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
|
|
390
|
+
getSetCookie () {
|
|
391
|
+
webidl.brandCheck(this, Headers)
|
|
392
|
+
|
|
393
|
+
// 1. If this’s header list does not contain `Set-Cookie`, then return « ».
|
|
394
|
+
// 2. Return the values of all headers in this’s header list whose name is
|
|
395
|
+
// a byte-case-insensitive match for `Set-Cookie`, in order.
|
|
396
|
+
|
|
397
|
+
const list = this[kHeadersList].cookies
|
|
398
|
+
|
|
399
|
+
if (list) {
|
|
400
|
+
return [...list]
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return []
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
|
|
386
407
|
get [kHeadersSortedMap] () {
|
|
387
|
-
if (
|
|
388
|
-
this[kHeadersList][kHeadersSortedMap]
|
|
408
|
+
if (this[kHeadersList][kHeadersSortedMap]) {
|
|
409
|
+
return this[kHeadersList][kHeadersSortedMap]
|
|
389
410
|
}
|
|
390
|
-
|
|
411
|
+
|
|
412
|
+
// 1. Let headers be an empty list of headers with the key being the name
|
|
413
|
+
// and value the value.
|
|
414
|
+
const headers = []
|
|
415
|
+
|
|
416
|
+
// 2. Let names be the result of convert header names to a sorted-lowercase
|
|
417
|
+
// set with all the names of the headers in list.
|
|
418
|
+
const names = [...this[kHeadersList]].sort((a, b) => a[0] < b[0] ? -1 : 1)
|
|
419
|
+
const cookies = this[kHeadersList].cookies
|
|
420
|
+
|
|
421
|
+
// 3. For each name of names:
|
|
422
|
+
for (const [name, value] of names) {
|
|
423
|
+
// 1. If name is `set-cookie`, then:
|
|
424
|
+
if (name === 'set-cookie') {
|
|
425
|
+
// 1. Let values be a list of all values of headers in list whose name
|
|
426
|
+
// is a byte-case-insensitive match for name, in order.
|
|
427
|
+
|
|
428
|
+
// 2. For each value of values:
|
|
429
|
+
// 1. Append (name, value) to headers.
|
|
430
|
+
for (const value of cookies) {
|
|
431
|
+
headers.push([name, value])
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
// 2. Otherwise:
|
|
435
|
+
|
|
436
|
+
// 1. Let value be the result of getting name from list.
|
|
437
|
+
|
|
438
|
+
// 2. Assert: value is non-null.
|
|
439
|
+
assert(value !== null)
|
|
440
|
+
|
|
441
|
+
// 3. Append (name, value) to headers.
|
|
442
|
+
headers.push([name, value])
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
this[kHeadersList][kHeadersSortedMap] = headers
|
|
447
|
+
|
|
448
|
+
// 4. Return headers.
|
|
449
|
+
return headers
|
|
391
450
|
}
|
|
392
451
|
|
|
393
452
|
keys () {
|
|
394
453
|
webidl.brandCheck(this, Headers)
|
|
395
454
|
|
|
396
455
|
return makeIterator(
|
|
397
|
-
() => [...this[kHeadersSortedMap].
|
|
456
|
+
() => [...this[kHeadersSortedMap].values()],
|
|
398
457
|
'Headers',
|
|
399
458
|
'key'
|
|
400
459
|
)
|
|
@@ -404,7 +463,7 @@ class Headers {
|
|
|
404
463
|
webidl.brandCheck(this, Headers)
|
|
405
464
|
|
|
406
465
|
return makeIterator(
|
|
407
|
-
() => [...this[kHeadersSortedMap].
|
|
466
|
+
() => [...this[kHeadersSortedMap].values()],
|
|
408
467
|
'Headers',
|
|
409
468
|
'value'
|
|
410
469
|
)
|
|
@@ -414,7 +473,7 @@ class Headers {
|
|
|
414
473
|
webidl.brandCheck(this, Headers)
|
|
415
474
|
|
|
416
475
|
return makeIterator(
|
|
417
|
-
() => [...this[kHeadersSortedMap].
|
|
476
|
+
() => [...this[kHeadersSortedMap].values()],
|
|
418
477
|
'Headers',
|
|
419
478
|
'key+value'
|
|
420
479
|
)
|
package/lib/fetch/request.js
CHANGED
|
@@ -28,6 +28,7 @@ const { getGlobalOrigin } = require('./global')
|
|
|
28
28
|
const { URLSerializer } = require('./dataURL')
|
|
29
29
|
const { kHeadersList } = require('../core/symbols')
|
|
30
30
|
const assert = require('assert')
|
|
31
|
+
const { setMaxListeners, getEventListeners, defaultMaxListeners } = require('events')
|
|
31
32
|
|
|
32
33
|
let TransformStream = globalThis.TransformStream
|
|
33
34
|
|
|
@@ -352,6 +353,11 @@ class Request {
|
|
|
352
353
|
const abort = function () {
|
|
353
354
|
acRef.deref()?.abort(this.reason)
|
|
354
355
|
}
|
|
356
|
+
|
|
357
|
+
if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) {
|
|
358
|
+
setMaxListeners(100, signal)
|
|
359
|
+
}
|
|
360
|
+
|
|
355
361
|
signal.addEventListener('abort', abort, { once: true })
|
|
356
362
|
requestFinalizer.register(this, { signal, abort })
|
|
357
363
|
}
|
package/lib/mock/mock-utils.js
CHANGED
|
@@ -188,7 +188,11 @@ function buildKey (opts) {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
function generateKeyValues (data) {
|
|
191
|
-
return Object.entries(data).reduce((keyValuePairs, [key, value]) => [
|
|
191
|
+
return Object.entries(data).reduce((keyValuePairs, [key, value]) => [
|
|
192
|
+
...keyValuePairs,
|
|
193
|
+
Buffer.from(`${key}`),
|
|
194
|
+
Array.isArray(value) ? value.map(x => Buffer.from(`${x}`)) : Buffer.from(`${value}`)
|
|
195
|
+
], [])
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
/**
|
package/package.json
CHANGED
package/types/connector.d.ts
CHANGED
package/types/fetch.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ export declare class Headers implements SpecIterable<[string, string]> {
|
|
|
59
59
|
readonly get: (name: string) => string | null
|
|
60
60
|
readonly has: (name: string) => boolean
|
|
61
61
|
readonly set: (name: string, value: string) => void
|
|
62
|
+
readonly getSetCookie: () => string[]
|
|
62
63
|
readonly forEach: (
|
|
63
64
|
callbackfn: (value: string, key: string, iterable: Headers) => void,
|
|
64
65
|
thisArg?: unknown
|