Skip to content

Commit d09bc54

Browse files
nodejs-github-botruyadorno
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 873941a commit d09bc54

15 files changed

Lines changed: 195 additions & 71 deletions

File tree

‎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-
exportasyncfunctionbankTransfer(recepient, amount) {
11+
exportasyncfunctionbankTransfer(recipient, amount) {
1212
const { body } =awaitrequest('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
constmockAgent=newMockAgent();
@@ -89,7 +89,7 @@ mockAgent.disableNetConnect()
8989
constmockPool=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-
consttimeoutId=timeout
79-
? setTimeout(onConnectTimeout,timeout,socket)
80-
: null
78+
constcancelTimeout=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
constcb=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
constcb=callback
@@ -104,6 +102,31 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
104102
}
105103
}
106104

105+
functionsetupTimeout(onConnectTimeout,timeout){
106+
if(!timeout){
107+
return()=>{}
108+
}
109+
110+
lets1=null
111+
lets2=null
112+
consttimeoutId=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
functiononConnectTimeout(socket){
108131
util.destroy(socket,newConnectTimeoutError())
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
letentries
406406
try{
407-
entries=newURLSearchParams(awaitthis.text())
407+
lettext=''
408+
// application/x-www-form-urlencoded parser will keep the BOM.
409+
// https://url.spec.whatwg.org/#concept-urlencoded-parser
410+
consttextDecoder=newTextDecoder('utf-8',{ignoreBOM: true})
411+
forawait(constchunkofconsumeBody(this[kState].body)){
412+
if(!isUint8Array(chunk)){
413+
thrownewTypeError('Expected Uint8Array chunk')
414+
}
415+
text+=textDecoder.decode(chunk,{stream: true})
416+
}
417+
text+=textDecoder.decode()
418+
entries=newURLSearchParams(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-
returnUint8Array.of(...output)
258+
returnUint8Array.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
constassert=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(bytesinstanceofError){
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(newTypeError('terminated',{
1897-
cause: reasoninstanceofError ? reason : undefined
1898+
cause: isErrorLike(reason) ? reason : undefined
18981899
}))
18991900
}
19001901
}
@@ -1942,14 +1943,17 @@ async function httpNetworkFetch (
19421943
}
19431944

19441945
letcodings=[]
1946+
letlocation=''
19451947

19461948
constheaders=newHeaders()
19471949
for(letn=0;n<headersList.length;n+=2){
1948-
constkey=headersList[n+0].toString()
1949-
constval=headersList[n+1].toString()
1950+
constkey=headersList[n+0].toString('latin1')
1951+
constval=headersList[n+1].toString('latin1')
19501952

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

19551959
headers.append(key,val)
@@ -1960,7 +1964,7 @@ async function httpNetworkFetch (
19601964
constdecoders=[]
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(constcodingofcodings){
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-
constabort=()=>ac.abort()
372+
constabort=()=>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
constac=newAbortController()
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
functionmakeNetworkError(reason){
351+
constisError=isErrorLike(reason)
350352
returnmakeResponse({
351353
type: 'error',
352354
status: 0,
353-
error:
354-
reasoninstanceofError
355-
? reason
356-
: newError(reason ? String(reason) : reason,{
357-
cause: reasoninstanceofError ? reason : undefined
358-
}),
355+
error: isError
356+
? reason
357+
: newError(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+
functionisErrorLike(object){
86+
returnobjectinstanceofError||(
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
returnString(V)
389389
}
390390

391+
// Check for 0 or more characters outside of the latin1 range.
391392
// eslint-disable-next-line no-control-regex
392-
constisNotLatin1=/[^\u0000-\u00ff]/
393+
constisLatin1=/^[\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
thrownewTypeError('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
functiongetHeaderByName(headers,key){
3939
if(Array.isArray(headers)){
4040
for(leti=0;i<headers.length;i+=2){
41-
if(headers[i]===key){
41+
if(headers[i].toLocaleLowerCase()===key.toLocaleLowerCase()){
4242
returnheaders[i+1]
4343
}
4444
}
@@ -47,19 +47,24 @@ function getHeaderByName (headers, key) {
4747
}elseif(typeofheaders.get==='function'){
4848
returnheaders.get(key)
4949
}else{
50-
returnheaders[key]
50+
returnlowerCaseEntries(headers)[key.toLocaleLowerCase()]
5151
}
5252
}
5353

54+
/** @param {string[]} headers */
55+
functionbuildHeadersFromArray(headers){// fetch HeadersList
56+
constclone=headers.slice()
57+
constentries=[]
58+
for(letindex=0;index<clone.length;index+=2){
59+
entries.push([clone[index],clone[index+1]])
60+
}
61+
returnObject.fromEntries(entries)
62+
}
63+
5464
functionmatchHeaders(mockDispatch,headers){
5565
if(typeofmockDispatch.headers==='function'){
5666
if(Array.isArray(headers)){// fetch HeadersList
57-
constclone=headers.slice()
58-
constentries=[]
59-
for(letindex=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
returnmockDispatch.headers(headers ? lowerCaseEntries(headers) : {})
6570
}
@@ -284,7 +289,13 @@ function mockDispatch (opts, handler) {
284289
}
285290

286291
functionhandleReply(mockDispatches){
287-
constresponseData=getResponseData(typeofdata==='function' ? data(opts) : data)
292+
// fetch's HeadersList is a 1D string array
293+
constoptsHeaders=Array.isArray(opts.headers)
294+
? buildHeadersFromArray(opts.headers)
295+
: opts.headers
296+
constresponseData=getResponseData(
297+
typeofdata==='function' ? data({ ...opts,headers: optsHeaders}) : data
298+
)
288299
constresponseHeaders=generateKeyValues(headers)
289300
constresponseTrailers=generateKeyValues(trailers)
290301

0 commit comments

Comments
 (0)