Skip to content

Commit e404ac7

Browse files
nodejs-github-botjuanarbol
authored andcommitted
deps: update undici to 5.8.1
PR-URL: #44158 Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com> Reviewed-By: Feng Yu <F3n67u@outlook.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 1b6d294 commit e404ac7

File tree

15 files changed

+195
-71
lines changed

15 files changed

+195
-71
lines changed

deps/undici/src/docs/best-practices/mocking-request.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
# Mocking Request
22

3-
Undici have its own mocking [utility](../api/MockAgent.md). It allow us to intercept undici HTTP request and return mocked value instead. It can be useful for testing purposes.
3+
Undici has its own mocking [utility](../api/MockAgent.md). It allow us to intercept undici HTTP requests and return mocked values instead. It can be useful for testing purposes.
44

55
Example:
66

77
```js
88
// bank.mjs
99
import { request } from 'undici'
1010

11-
export async function bankTransfer(recepient, amount) {
11+
export async function bankTransfer(recipient, amount) {
1212
const { body } = await request('http://localhost:3000/bank-transfer',
1313
{
1414
method: 'POST',
1515
headers: {
1616
'X-TOKEN-SECRET': 'SuperSecretToken',
1717
},
1818
body: JSON.stringify({
19-
recepient,
19+
recipient,
2020
amount
2121
})
2222
}
@@ -48,7 +48,7 @@ mockPool.intercept({
4848
'X-TOKEN-SECRET': 'SuperSecretToken',
4949
},
5050
body: JSON.stringify({
51-
recepient: '1234567890',
51+
recipient: '1234567890',
5252
amount: '100'
5353
})
5454
}).reply(200, {
@@ -77,7 +77,7 @@ Explore other MockAgent functionality [here](../api/MockAgent.md)
7777

7878
## Debug Mock Value
7979

80-
When the interceptor we wrote are not the same undici will automatically call real HTTP request. To debug our mock value use `mockAgent.disableNetConnect()`
80+
When the interceptor and the request options are not the same, undici will automatically make a real HTTP request. To prevent real requests from being made, use `mockAgent.disableNetConnect()`:
8181

8282
```js
8383
const mockAgent = new MockAgent();
@@ -89,7 +89,7 @@ mockAgent.disableNetConnect()
8989
const mockPool = mockAgent.get('http://localhost:3000');
9090

9191
mockPool.intercept({
92-
path: '/bank-tanfer',
92+
path: '/bank-transfer',
9393
method: 'POST',
9494
}).reply(200, {
9595
message: 'transaction processed'
@@ -103,7 +103,7 @@ const badRequest = await bankTransfer('1234567890', '100')
103103

104104
## Reply with data based on request
105105

106-
If the mocked response needs to be dynamically derived from the request parameters, you can provide a function instead of an object to `reply`
106+
If the mocked response needs to be dynamically derived from the request parameters, you can provide a function instead of an object to `reply`:
107107

108108
```js
109109
mockPool.intercept({
@@ -113,7 +113,7 @@ mockPool.intercept({
113113
'X-TOKEN-SECRET': 'SuperSecretToken',
114114
},
115115
body: JSON.stringify({
116-
recepient: '1234567890',
116+
recipient: '1234567890',
117117
amount: '100'
118118
})
119119
}).reply(200, (opts) => {
@@ -129,7 +129,7 @@ in this case opts will be
129129
{
130130
method: 'POST',
131131
headers: { 'X-TOKEN-SECRET': 'SuperSecretToken' },
132-
body: '{"recepient":"1234567890","amount":"100"}',
132+
body: '{"recipient":"1234567890","amount":"100"}',
133133
origin: 'http://localhost:3000',
134134
path: '/bank-transfer'
135135
}

deps/undici/src/lib/core/connect.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,12 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
7575
})
7676
}
7777

78-
const timeoutId = timeout
79-
? setTimeout(onConnectTimeout, timeout, socket)
80-
: null
78+
const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout)
8179

8280
socket
8381
.setNoDelay(true)
8482
.once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {
85-
clearTimeout(timeoutId)
83+
cancelTimeout()
8684

8785
if (callback) {
8886
const cb = callback
@@ -91,7 +89,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
9189
}
9290
})
9391
.on('error', function (err) {
94-
clearTimeout(timeoutId)
92+
cancelTimeout()
9593

9694
if (callback) {
9795
const cb = callback
@@ -104,6 +102,31 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
104102
}
105103
}
106104

105+
function setupTimeout (onConnectTimeout, timeout) {
106+
if (!timeout) {
107+
return () => {}
108+
}
109+
110+
let s1 = null
111+
let s2 = null
112+
const timeoutId = setTimeout(() => {
113+
// setImmediate is added to make sure that we priotorise socket error events over timeouts
114+
s1 = setImmediate(() => {
115+
if (process.platform === 'win32') {
116+
// Windows needs an extra setImmediate probably due to implementation differences in the socket logic
117+
s2 = setImmediate(() => onConnectTimeout())
118+
} else {
119+
onConnectTimeout()
120+
}
121+
})
122+
}, timeout)
123+
return () => {
124+
clearTimeout(timeoutId)
125+
clearImmediate(s1)
126+
clearImmediate(s2)
127+
}
128+
}
129+
107130
function onConnectTimeout (socket) {
108131
util.destroy(socket, new ConnectTimeoutError())
109132
}

deps/undici/src/lib/fetch/body.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,18 @@ function bodyMixinMethods (instance) {
404404
// 1. Let entries be the result of parsing bytes.
405405
let entries
406406
try {
407-
entries = new URLSearchParams(await this.text())
407+
let text = ''
408+
// application/x-www-form-urlencoded parser will keep the BOM.
409+
// https://url.spec.whatwg.org/#concept-urlencoded-parser
410+
const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true })
411+
for await (const chunk of consumeBody(this[kState].body)) {
412+
if (!isUint8Array(chunk)) {
413+
throw new TypeError('Expected Uint8Array chunk')
414+
}
415+
text += textDecoder.decode(chunk, { stream: true })
416+
}
417+
text += textDecoder.decode()
418+
entries = new URLSearchParams(text)
408419
} catch (err) {
409420
// istanbul ignore next: Unclear when new URLSearchParams can fail on a string.
410421
// 2. If entries is failure, then throw a TypeError.

deps/undici/src/lib/fetch/dataURL.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ function percentDecode (input) {
255255
}
256256

257257
// 3. Return output.
258-
return Uint8Array.of(...output)
258+
return Uint8Array.from(output)
259259
}
260260

261261
// https://mimesniff.spec.whatwg.org/#parse-a-mime-type

deps/undici/src/lib/fetch/index.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ const {
3333
isBlobLike,
3434
sameOrigin,
3535
isCancelled,
36-
isAborted
36+
isAborted,
37+
isErrorLike
3738
} = require('./util')
3839
const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
3940
const assert = require('assert')
@@ -1854,7 +1855,7 @@ async function httpNetworkFetch (
18541855
timingInfo.decodedBodySize += bytes?.byteLength ?? 0
18551856

18561857
// 6. If bytes is failure, then terminate fetchParams’s controller.
1857-
if (bytes instanceof Error) {
1858+
if (isErrorLike(bytes)) {
18581859
fetchParams.controller.terminate(bytes)
18591860
return
18601861
}
@@ -1894,7 +1895,7 @@ async function httpNetworkFetch (
18941895
// 3. Otherwise, if stream is readable, error stream with a TypeError.
18951896
if (isReadable(stream)) {
18961897
fetchParams.controller.controller.error(new TypeError('terminated', {
1897-
cause: reason instanceof Error ? reason : undefined
1898+
cause: isErrorLike(reason) ? reason : undefined
18981899
}))
18991900
}
19001901
}
@@ -1942,14 +1943,17 @@ async function httpNetworkFetch (
19421943
}
19431944

19441945
let codings = []
1946+
let location = ''
19451947

19461948
const headers = new Headers()
19471949
for (let n = 0; n < headersList.length; n += 2) {
1948-
const key = headersList[n + 0].toString()
1949-
const val = headersList[n + 1].toString()
1950+
const key = headersList[n + 0].toString('latin1')
1951+
const val = headersList[n + 1].toString('latin1')
19501952

19511953
if (key.toLowerCase() === 'content-encoding') {
19521954
codings = val.split(',').map((x) => x.trim())
1955+
} else if (key.toLowerCase() === 'location') {
1956+
location = val
19531957
}
19541958

19551959
headers.append(key, val)
@@ -1960,7 +1964,7 @@ async function httpNetworkFetch (
19601964
const decoders = []
19611965

19621966
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
1963-
if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status)) {
1967+
if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !(request.redirect === 'follow' && location)) {
19641968
for (const coding of codings) {
19651969
if (/(x-)?gzip/.test(coding)) {
19661970
decoders.push(zlib.createGunzip())
@@ -1980,7 +1984,7 @@ async function httpNetworkFetch (
19801984
statusText,
19811985
headersList: headers[kHeadersList],
19821986
body: decoders.length
1983-
? pipeline(this.body, ...decoders, () => {})
1987+
? pipeline(this.body, ...decoders, () => { })
19841988
: this.body.on('error', () => {})
19851989
})
19861990

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,9 @@ class Request {
367367
}
368368

369369
if (signal.aborted) {
370-
ac.abort()
370+
ac.abort(signal.reason)
371371
} else {
372-
const abort = () => ac.abort()
372+
const abort = () => ac.abort(signal.reason)
373373
signal.addEventListener('abort', abort, { once: true })
374374
requestFinalizer.register(this, { signal, abort })
375375
}
@@ -726,12 +726,12 @@ class Request {
726726
// 4. Make clonedRequestObject’s signal follow this’s signal.
727727
const ac = new AbortController()
728728
if (this.signal.aborted) {
729-
ac.abort()
729+
ac.abort(this.signal.reason)
730730
} else {
731731
this.signal.addEventListener(
732732
'abort',
733-
function () {
734-
ac.abort()
733+
() => {
734+
ac.abort(this.signal.reason)
735735
},
736736
{ once: true }
737737
)

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const {
1010
isCancelled,
1111
isAborted,
1212
isBlobLike,
13-
serializeJavascriptValueToJSONString
13+
serializeJavascriptValueToJSONString,
14+
isErrorLike
1415
} = require('./util')
1516
const {
1617
redirectStatus,
@@ -347,15 +348,15 @@ function makeResponse (init) {
347348
}
348349

349350
function makeNetworkError (reason) {
351+
const isError = isErrorLike(reason)
350352
return makeResponse({
351353
type: 'error',
352354
status: 0,
353-
error:
354-
reason instanceof Error
355-
? reason
356-
: new Error(reason ? String(reason) : reason, {
357-
cause: reason instanceof Error ? reason : undefined
358-
}),
355+
error: isError
356+
? reason
357+
: new Error(reason ? String(reason) : reason, {
358+
cause: isError ? reason : undefined
359+
}),
359360
aborted: reason && reason.name === 'AbortError'
360361
})
361362
}

deps/undici/src/lib/fetch/util.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ function isFileLike (object) {
8282
)
8383
}
8484

85+
function isErrorLike (object) {
86+
return object instanceof Error || (
87+
object?.constructor?.name === 'Error' ||
88+
object?.constructor?.name === 'DOMException'
89+
)
90+
}
91+
8592
// Check whether |statusText| is a ByteString and
8693
// matches the Reason-Phrase token production.
8794
// RFC 2616: https://tools.ietf.org/html/rfc2616
@@ -469,5 +476,6 @@ module.exports = {
469476
makeIterator,
470477
isValidHeaderName,
471478
isValidHeaderValue,
472-
hasOwn
479+
hasOwn,
480+
isErrorLike
473481
}

deps/undici/src/lib/fetch/webidl.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,9 @@ webidl.converters.DOMString = function (V, opts = {}) {
388388
return String(V)
389389
}
390390

391+
// Check for 0 or more characters outside of the latin1 range.
391392
// eslint-disable-next-line no-control-regex
392-
const isNotLatin1 = /[^\u0000-\u00ff]/
393+
const isLatin1 = /^[\u0000-\u00ff]{0,}$/
393394

394395
// https://webidl.spec.whatwg.org/#es-ByteString
395396
webidl.converters.ByteString = function (V) {
@@ -399,7 +400,7 @@ webidl.converters.ByteString = function (V) {
399400

400401
// 2. If the value of any element of x is greater than
401402
// 255, then throw a TypeError.
402-
if (isNotLatin1.test(x)) {
403+
if (!isLatin1.test(x)) {
403404
throw new TypeError('Argument is not a ByteString')
404405
}
405406

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function lowerCaseEntries (headers) {
3838
function getHeaderByName (headers, key) {
3939
if (Array.isArray(headers)) {
4040
for (let i = 0; i < headers.length; i += 2) {
41-
if (headers[i] === key) {
41+
if (headers[i].toLocaleLowerCase() === key.toLocaleLowerCase()) {
4242
return headers[i + 1]
4343
}
4444
}
@@ -47,19 +47,24 @@ function getHeaderByName (headers, key) {
4747
} else if (typeof headers.get === 'function') {
4848
return headers.get(key)
4949
} else {
50-
return headers[key]
50+
return lowerCaseEntries(headers)[key.toLocaleLowerCase()]
5151
}
5252
}
5353

54+
/** @param {string[]} headers */
55+
function buildHeadersFromArray (headers) { // fetch HeadersList
56+
const clone = headers.slice()
57+
const entries = []
58+
for (let index = 0; index < clone.length; index += 2) {
59+
entries.push([clone[index], clone[index + 1]])
60+
}
61+
return Object.fromEntries(entries)
62+
}
63+
5464
function matchHeaders (mockDispatch, headers) {
5565
if (typeof mockDispatch.headers === 'function') {
5666
if (Array.isArray(headers)) { // fetch HeadersList
57-
const clone = headers.slice()
58-
const entries = []
59-
for (let index = 0; index < clone.length; index += 2) {
60-
entries.push([clone[index], clone[index + 1]])
61-
}
62-
headers = Object.fromEntries(entries)
67+
headers = buildHeadersFromArray(headers)
6368
}
6469
return mockDispatch.headers(headers ? lowerCaseEntries(headers) : {})
6570
}
@@ -284,7 +289,13 @@ function mockDispatch (opts, handler) {
284289
}
285290

286291
function handleReply (mockDispatches) {
287-
const responseData = getResponseData(typeof data === 'function' ? data(opts) : data)
292+
// fetch's HeadersList is a 1D string array
293+
const optsHeaders = Array.isArray(opts.headers)
294+
? buildHeadersFromArray(opts.headers)
295+
: opts.headers
296+
const responseData = getResponseData(
297+
typeof data === 'function' ? data({ ...opts, headers: optsHeaders }) : data
298+
)
288299
const responseHeaders = generateKeyValues(headers)
289300
const responseTrailers = generateKeyValues(trailers)
290301

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy