Skip to content

Commit 921ef9b

Browse files
authoredJun 1, 2022
deps: update undici to 5.4.0
PR-URL: nodejs#43262 Reviewed-By: Filip Skokan <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Darshan Sen <[email protected]>
1 parent 0ef81d1 commit 921ef9b

14 files changed

+79
-202
lines changed
 

‎deps/undici/src/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ const headers = await fetch(url)
288288
.then(res => res.headers)
289289
```
290290

291+
##### Forbidden and Safelisted Header Names
292+
293+
* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
294+
* https://fetch.spec.whatwg.org/#forbidden-header-name
295+
* https://fetch.spec.whatwg.org/#forbidden-response-header-name
296+
* https://github.com/wintercg/fetch/issues/6
297+
298+
The [Fetch Standard](https://fetch.spec.whatwg.org) requires implementations to exclude certain headers from requests and responses. In browser environments, some headers are forbidden so the user agent remains in full control over them. In Undici, these constraints are removed to give more control to the user.
299+
291300
### `undici.upgrade([url, options]): Promise`
292301

293302
Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.

‎deps/undici/src/docs/api/MockPool.md

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Returns: `MockInterceptor` corresponding to the input options.
5757
* **method** `string | RegExp | (method: string) => boolean` - a matcher for the HTTP request method.
5858
* **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body.
5959
* **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way.
60+
* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params.
6061

6162
### Return: `MockInterceptor`
6263

‎deps/undici/src/lib/client.js

+5
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,11 @@ class Parser {
873873
// have been queued since then.
874874
util.destroy(socket, new InformationalError('reset'))
875875
return constants.ERROR.PAUSED
876+
} else if (client[kPipelining] === 1) {
877+
// We must wait a full event loop cycle to reuse this socket to make sure
878+
// that non-spec compliant servers are not closing the connection even if they
879+
// said they won't.
880+
setImmediate(resume, client)
876881
} else {
877882
resume(client)
878883
}

‎deps/undici/src/lib/fetch/constants.js

-31
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,5 @@
11
'use strict'
22

3-
const forbiddenHeaderNames = [
4-
'accept-charset',
5-
'accept-encoding',
6-
'access-control-request-headers',
7-
'access-control-request-method',
8-
'connection',
9-
'content-length',
10-
'cookie',
11-
'cookie2',
12-
'date',
13-
'dnt',
14-
'expect',
15-
'host',
16-
'keep-alive',
17-
'origin',
18-
'referer',
19-
'te',
20-
'trailer',
21-
'transfer-encoding',
22-
'upgrade',
23-
'via'
24-
]
25-
263
const corsSafeListedMethods = ['GET', 'HEAD', 'POST']
274

285
const nullBodyStatus = [101, 204, 205, 304]
@@ -58,9 +35,6 @@ const requestCache = [
5835
'only-if-cached'
5936
]
6037

61-
// https://fetch.spec.whatwg.org/#forbidden-response-header-name
62-
const forbiddenResponseHeaderNames = ['set-cookie', 'set-cookie2']
63-
6438
const requestBodyHeader = [
6539
'content-encoding',
6640
'content-language',
@@ -86,20 +60,15 @@ const subresource = [
8660
''
8761
]
8862

89-
const corsSafeListedResponseHeaderNames = [] // TODO
90-
9163
module.exports = {
9264
subresource,
93-
forbiddenResponseHeaderNames,
94-
corsSafeListedResponseHeaderNames,
9565
forbiddenMethods,
9666
requestBodyHeader,
9767
referrerPolicy,
9868
requestRedirect,
9969
requestMode,
10070
requestCredentials,
10171
requestCache,
102-
forbiddenHeaderNames,
10372
redirectStatus,
10473
corsSafeListedMethods,
10574
nullBodyStatus,

‎deps/undici/src/lib/fetch/formdata.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@ function makeEntry (name, value, filename) {
243243
// object, representing the same bytes, whose name attribute value is "blob".
244244
if (isBlobLike(value) && !isFileLike(value)) {
245245
value = value instanceof Blob
246-
? new File([value], 'blob')
247-
: new FileLike(value, 'blob')
246+
? new File([value], 'blob', value)
247+
: new FileLike(value, 'blob', value)
248248
}
249249

250250
// 4. If value is (now) a File object and filename is given, then set value to a
@@ -256,8 +256,8 @@ function makeEntry (name, value, filename) {
256256
// creating one more File instance doesn't make much sense....
257257
if (isFileLike(value) && filename != null) {
258258
value = value instanceof File
259-
? new File([value], filename)
260-
: new FileLike(value, filename)
259+
? new File([value], filename, value)
260+
: new FileLike(value, filename, value)
261261
}
262262

263263
// 5. Set entry’s value to value.

‎deps/undici/src/lib/fetch/headers.js

+8-38
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ const { validateHeaderName, validateHeaderValue } = require('http')
66
const { kHeadersList } = require('../core/symbols')
77
const { kGuard } = require('./symbols')
88
const { kEnumerableProperty } = require('../core/util')
9-
const {
10-
forbiddenHeaderNames,
11-
forbiddenResponseHeaderNames
12-
} = require('./constants')
139

1410
const kHeadersMap = Symbol('headers map')
1511
const kHeadersSortedMap = Symbol('headers map sorted')
@@ -115,6 +111,11 @@ class HeadersList {
115111
}
116112
}
117113

114+
clear () {
115+
this[kHeadersMap].clear()
116+
this[kHeadersSortedMap] = null
117+
}
118+
118119
append (name, value) {
119120
this[kHeadersSortedMap] = null
120121

@@ -211,22 +212,11 @@ class Headers {
211212
)
212213
}
213214

214-
const normalizedName = normalizeAndValidateHeaderName(String(name))
215-
215+
// Note: undici does not implement forbidden header names
216216
if (this[kGuard] === 'immutable') {
217217
throw new TypeError('immutable')
218-
} else if (
219-
this[kGuard] === 'request' &&
220-
forbiddenHeaderNames.includes(normalizedName)
221-
) {
222-
return
223218
} else if (this[kGuard] === 'request-no-cors') {
224219
// TODO
225-
} else if (
226-
this[kGuard] === 'response' &&
227-
forbiddenResponseHeaderNames.includes(normalizedName)
228-
) {
229-
return
230220
}
231221

232222
return this[kHeadersList].append(String(name), String(value))
@@ -244,22 +234,11 @@ class Headers {
244234
)
245235
}
246236

247-
const normalizedName = normalizeAndValidateHeaderName(String(name))
248-
237+
// Note: undici does not implement forbidden header names
249238
if (this[kGuard] === 'immutable') {
250239
throw new TypeError('immutable')
251-
} else if (
252-
this[kGuard] === 'request' &&
253-
forbiddenHeaderNames.includes(normalizedName)
254-
) {
255-
return
256240
} else if (this[kGuard] === 'request-no-cors') {
257241
// TODO
258-
} else if (
259-
this[kGuard] === 'response' &&
260-
forbiddenResponseHeaderNames.includes(normalizedName)
261-
) {
262-
return
263242
}
264243

265244
return this[kHeadersList].delete(String(name))
@@ -307,20 +286,11 @@ class Headers {
307286
)
308287
}
309288

289+
// Note: undici does not implement forbidden header names
310290
if (this[kGuard] === 'immutable') {
311291
throw new TypeError('immutable')
312-
} else if (
313-
this[kGuard] === 'request' &&
314-
forbiddenHeaderNames.includes(String(name).toLocaleLowerCase())
315-
) {
316-
return
317292
} else if (this[kGuard] === 'request-no-cors') {
318293
// TODO
319-
} else if (
320-
this[kGuard] === 'response' &&
321-
forbiddenResponseHeaderNames.includes(String(name).toLocaleLowerCase())
322-
) {
323-
return
324294
}
325295

326296
return this[kHeadersList].set(String(name), String(value))

‎deps/undici/src/lib/fetch/request.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,8 @@ class Request {
384384
// Realm, whose header list is request’s header list and guard is
385385
// "request".
386386
this[kHeaders] = new Headers()
387-
this[kHeaders][kGuard] = 'request'
388387
this[kHeaders][kHeadersList] = request.headersList
388+
this[kHeaders][kGuard] = 'request'
389389
this[kHeaders][kRealm] = this[kRealm]
390390

391391
// 31. If this’s request’s mode is "no-cors", then:
@@ -406,26 +406,25 @@ class Request {
406406
if (Object.keys(init).length !== 0) {
407407
// 1. Let headers be a copy of this’s headers and its associated header
408408
// list.
409-
let headers = new Headers(this.headers)
409+
let headers = new Headers(this[kHeaders])
410410

411411
// 2. If init["headers"] exists, then set headers to init["headers"].
412412
if (init.headers !== undefined) {
413413
headers = init.headers
414414
}
415415

416416
// 3. Empty this’s headers’s header list.
417-
this[kState].headersList = new HeadersList()
418-
this[kHeaders][kHeadersList] = this[kState].headersList
417+
this[kHeaders][kHeadersList].clear()
419418

420419
// 4. If headers is a Headers object, then for each header in its header
421420
// list, append header’s name/header’s value to this’s headers.
422421
if (headers.constructor.name === 'Headers') {
423-
for (const [key, val] of headers[kHeadersList] || headers) {
422+
for (const [key, val] of headers) {
424423
this[kHeaders].append(key, val)
425424
}
426425
} else {
427426
// 5. Otherwise, fill this’s headers with headers.
428-
fillHeaders(this[kState].headersList, headers)
427+
fillHeaders(this[kHeaders], headers)
429428
}
430429
}
431430

‎deps/undici/src/lib/fetch/response.js

+6-31
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ const { kEnumerableProperty } = util
88
const { responseURL, isValidReasonPhrase, toUSVString, isCancelled, isAborted, serializeJavascriptValueToJSONString } = require('./util')
99
const {
1010
redirectStatus,
11-
nullBodyStatus,
12-
forbiddenResponseHeaderNames,
13-
corsSafeListedResponseHeaderNames
11+
nullBodyStatus
1412
} = require('./constants')
1513
const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
1614
const { kHeadersList } = require('../core/symbols')
@@ -380,28 +378,6 @@ function makeFilteredResponse (response, state) {
380378
})
381379
}
382380

383-
function makeFilteredHeadersList (headersList, filter) {
384-
return new Proxy(headersList, {
385-
get (target, prop) {
386-
// Override methods used by Headers class.
387-
if (prop === 'get' || prop === 'has') {
388-
const defaultReturn = prop === 'has' ? false : null
389-
return (name) => filter(name) ? target[prop](name) : defaultReturn
390-
} else if (prop === Symbol.iterator) {
391-
return function * () {
392-
for (const entry of target) {
393-
if (filter(entry[0])) {
394-
yield entry
395-
}
396-
}
397-
}
398-
} else {
399-
return target[prop]
400-
}
401-
}
402-
})
403-
}
404-
405381
// https://fetch.spec.whatwg.org/#concept-filtered-response
406382
function filterResponse (response, type) {
407383
// Set response to the following filtered response with response as its
@@ -411,22 +387,21 @@ function filterResponse (response, type) {
411387
// and header list excludes any headers in internal response’s header list
412388
// whose name is a forbidden response-header name.
413389

390+
// Note: undici does not implement forbidden response-header names
414391
return makeFilteredResponse(response, {
415392
type: 'basic',
416-
headersList: makeFilteredHeadersList(
417-
response.headersList,
418-
(name) => !forbiddenResponseHeaderNames.includes(name.toLowerCase())
419-
)
393+
headersList: response.headersList
420394
})
421395
} else if (type === 'cors') {
422396
// A CORS filtered response is a filtered response whose type is "cors"
423397
// and header list excludes any headers in internal response’s header
424398
// list whose name is not a CORS-safelisted response-header name, given
425399
// internal response’s CORS-exposed header-name list.
426400

401+
// Note: undici does not implement CORS-safelisted response-header names
427402
return makeFilteredResponse(response, {
428403
type: 'cors',
429-
headersList: makeFilteredHeadersList(response.headersList, (name) => !corsSafeListedResponseHeaderNames.includes(name))
404+
headersList: response.headersList
430405
})
431406
} else if (type === 'opaque') {
432407
// An opaque filtered response is a filtered response whose type is
@@ -449,7 +424,7 @@ function filterResponse (response, type) {
449424
type: 'opaqueredirect',
450425
status: 0,
451426
statusText: '',
452-
headersList: makeFilteredHeadersList(response.headersList, () => false),
427+
headersList: [],
453428
body: null
454429
})
455430
} else {

‎deps/undici/src/lib/mock/mock-interceptor.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
kMockDispatch
1111
} = require('./mock-symbols')
1212
const { InvalidArgumentError } = require('../core/errors')
13+
const { buildURL } = require('../core/util')
1314

1415
/**
1516
* Defines the scope API for an interceptor reply
@@ -70,9 +71,13 @@ class MockInterceptor {
7071
// As per RFC 3986, clients are not supposed to send URI
7172
// fragments to servers when they retrieve a document,
7273
if (typeof opts.path === 'string') {
73-
// Matches https://github.com/nodejs/undici/blob/main/lib/fetch/index.js#L1811
74-
const parsedURL = new URL(opts.path, 'data://')
75-
opts.path = parsedURL.pathname + parsedURL.search
74+
if (opts.query) {
75+
opts.path = buildURL(opts.path, opts.query)
76+
} else {
77+
// Matches https://github.com/nodejs/undici/blob/main/lib/fetch/index.js#L1811
78+
const parsedURL = new URL(opts.path, 'data://')
79+
opts.path = parsedURL.pathname + parsedURL.search
80+
}
7681
}
7782
if (typeof opts.method === 'string') {
7883
opts.method = opts.method.toUpperCase()

‎deps/undici/src/lib/mock/mock-utils.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
kOrigin,
99
kGetNetConnect
1010
} = require('./mock-symbols')
11+
const { buildURL } = require('../core/util')
1112

1213
function matchValue (match, value) {
1314
if (typeof match === 'string') {
@@ -98,10 +99,12 @@ function getResponseData (data) {
9899
}
99100

100101
function getMockDispatch (mockDispatches, key) {
102+
const resolvedPath = key.query ? buildURL(key.path, key.query) : key.path
103+
101104
// Match path
102-
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(path, key.path))
105+
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(path, resolvedPath))
103106
if (matchedMockDispatches.length === 0) {
104-
throw new MockNotMatchedError(`Mock dispatch not matched for path '${key.path}'`)
107+
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`)
105108
}
106109

107110
// Match method
@@ -146,12 +149,13 @@ function deleteMockDispatch (mockDispatches, key) {
146149
}
147150

148151
function buildKey (opts) {
149-
const { path, method, body, headers } = opts
152+
const { path, method, body, headers, query } = opts
150153
return {
151154
path,
152155
method,
153156
body,
154-
headers
157+
headers,
158+
query
155159
}
156160
}
157161

0 commit comments

Comments
 (0)
Please sign in to comment.