Skip to content

Commit 517f17b

Browse files
nodejs-github-botdanielleadams
authored andcommittedJun 16, 2022
deps: update undici to 5.5.1
PR-URL: #43412 Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: LiviaMedeiros <livia@cirno.name> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent d2a98dc commit 517f17b

16 files changed

+297
-154
lines changed
 

‎deps/undici/src/README.md

+11-16
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,12 @@ Help us improve the test coverage by following instructions at [nodejs/undici/#9
185185
Basic usage example:
186186

187187
```js
188-
import {fetch} from 'undici';
188+
import { fetch } from 'undici';
189189

190-
async function fetchJson() {
191-
const res = await fetch('https://example.com')
192-
const json = await res.json()
193-
console.log(json);
194-
}
190+
191+
const res = await fetch('https://example.com')
192+
const json = await res.json()
193+
console.log(json);
195194
```
196195

197196
You can pass an optional dispatcher to `fetch` as:
@@ -235,24 +234,20 @@ const data = {
235234
},
236235
};
237236

238-
(async () => {
239-
await fetch("https://example.com", { body: data, method: 'POST' });
240-
})();
237+
await fetch("https://example.com", { body: data, method: 'POST' });
241238
```
242239

243240
#### `response.body`
244241

245242
Nodejs has two kinds of streams: [web streams](https://nodejs.org/dist/latest-v16.x/docs/api/webstreams.html), which follow the API of the WHATWG web standard found in browsers, and an older Node-specific [streams API](https://nodejs.org/api/stream.html). `response.body` returns a readable web stream. If you would prefer to work with a Node stream you can convert a web stream using `.fromWeb()`.
246243

247244
```js
248-
import {fetch} from 'undici';
249-
import {Readable} from 'node:stream';
245+
import { fetch } from 'undici';
246+
import { Readable } from 'node:stream';
250247

251-
async function fetchStream() {
252-
const response = await fetch('https://example.com')
253-
const readableWebStream = response.body;
254-
const readableNodeStream = Readable.fromWeb(readableWebStream);
255-
}
248+
const response = await fetch('https://example.com')
249+
const readableWebStream = response.body;
250+
const readableNodeStream = Readable.fromWeb(readableWebStream);
256251
```
257252

258253
#### Specification Compliance

‎deps/undici/src/lib/api/api-connect.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ConnectHandler extends AsyncResource {
1515
throw new InvalidArgumentError('invalid callback')
1616
}
1717

18-
const { signal, opaque, responseHeaders } = opts
18+
const { signal, opaque, responseHeaders, httpTunnel } = opts
1919

2020
if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') {
2121
throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget')
@@ -27,6 +27,7 @@ class ConnectHandler extends AsyncResource {
2727
this.responseHeaders = responseHeaders || null
2828
this.callback = callback
2929
this.abort = null
30+
this.httpTunnel = httpTunnel
3031

3132
addSignal(this, signal)
3233
}
@@ -40,8 +41,23 @@ class ConnectHandler extends AsyncResource {
4041
this.context = context
4142
}
4243

43-
onHeaders () {
44-
throw new SocketError('bad connect', null)
44+
onHeaders (statusCode) {
45+
// when httpTunnel headers are allowed
46+
if (this.httpTunnel) {
47+
const { callback, opaque } = this
48+
if (statusCode !== 200) {
49+
if (callback) {
50+
this.callback = null
51+
const err = new RequestAbortedError('Proxy response !== 200 when HTTP Tunneling')
52+
queueMicrotask(() => {
53+
this.runInAsyncScope(callback, null, err, { opaque })
54+
})
55+
}
56+
return 1
57+
}
58+
} else {
59+
throw new SocketError('bad connect', null)
60+
}
4561
}
4662

4763
onUpgrade (statusCode, rawHeaders, socket) {

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
2121
timeout = timeout == null ? 10e3 : timeout
2222
maxCachedSessions = maxCachedSessions == null ? 100 : maxCachedSessions
2323

24-
return function connect ({ hostname, host, protocol, port, servername }, callback) {
24+
return function connect ({ hostname, host, protocol, port, servername, httpSocket }, callback) {
2525
let socket
2626
if (protocol === 'https:') {
2727
if (!tls) {
@@ -39,6 +39,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
3939
...options,
4040
servername,
4141
session,
42+
socket: httpSocket, // upgrade socket connection
4243
port: port || 443,
4344
host: hostname
4445
})
@@ -65,6 +66,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
6566
}
6667
})
6768
} else {
69+
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
6870
socket = net.connect({
6971
highWaterMark: 64 * 1024, // Same as nodejs fs streams.
7072
...options,

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

+9-6
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ class Request {
4848
}, handler) {
4949
if (typeof path !== 'string') {
5050
throw new InvalidArgumentError('path must be a string')
51-
} else if (path[0] !== '/' && !(path.startsWith('http://') || path.startsWith('https://'))) {
51+
} else if (
52+
path[0] !== '/' &&
53+
!(path.startsWith('http://') || path.startsWith('https://')) &&
54+
method !== 'CONNECT'
55+
) {
5256
throw new InvalidArgumentError('path must be an absolute URL or start with a slash')
5357
}
5458

@@ -80,13 +84,12 @@ class Request {
8084
this.body = null
8185
} else if (util.isStream(body)) {
8286
this.body = body
83-
} else if (body instanceof DataView) {
84-
// TODO: Why is DataView special?
85-
this.body = body.buffer.byteLength ? Buffer.from(body.buffer) : null
86-
} else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
87-
this.body = body.byteLength ? Buffer.from(body) : null
8887
} else if (util.isBuffer(body)) {
8988
this.body = body.byteLength ? body : null
89+
} else if (ArrayBuffer.isView(body)) {
90+
this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null
91+
} else if (body instanceof ArrayBuffer) {
92+
this.body = body.byteLength ? Buffer.from(body) : null
9093
} else if (typeof body === 'string') {
9194
this.body = body.length ? Buffer.from(body) : null
9295
} else if (util.isFormDataLike(body) || util.isIterable(body) || util.isBlobLike(body)) {

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const { kBodyUsed } = require('../core/symbols')
99
const assert = require('assert')
1010
const { NotSupportedError } = require('../core/errors')
1111
const { isErrored } = require('../core/util')
12-
const { isUint8Array } = require('util/types')
12+
const { isUint8Array, isArrayBuffer } = require('util/types')
1313

1414
let ReadableStream
1515

@@ -61,7 +61,7 @@ function extractBody (object, keepalive = false) {
6161

6262
// Set Content-Type to `application/x-www-form-urlencoded;charset=UTF-8`.
6363
contentType = 'application/x-www-form-urlencoded;charset=UTF-8'
64-
} else if (object instanceof ArrayBuffer || ArrayBuffer.isView(object)) {
64+
} else if (isArrayBuffer(object) || ArrayBuffer.isView(object)) {
6565
// BufferSource
6666

6767
if (object instanceof DataView) {

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

+54-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const { isBlobLike, isFileLike, toUSVString } = require('./util')
3+
const { isBlobLike, isFileLike, toUSVString, makeIterator } = require('./util')
44
const { kState } = require('./symbols')
55
const { File, FileLike } = require('./file')
66
const { Blob } = require('buffer')
@@ -187,45 +187,68 @@ class FormData {
187187
return this.constructor.name
188188
}
189189

190-
* entries () {
190+
entries () {
191191
if (!(this instanceof FormData)) {
192192
throw new TypeError('Illegal invocation')
193193
}
194194

195-
for (const pair of this) {
196-
yield pair
197-
}
195+
return makeIterator(
196+
makeIterable(this[kState], 'entries'),
197+
'FormData'
198+
)
198199
}
199200

200-
* keys () {
201+
keys () {
201202
if (!(this instanceof FormData)) {
202203
throw new TypeError('Illegal invocation')
203204
}
204205

205-
for (const [key] of this) {
206-
yield key
206+
return makeIterator(
207+
makeIterable(this[kState], 'keys'),
208+
'FormData'
209+
)
210+
}
211+
212+
values () {
213+
if (!(this instanceof FormData)) {
214+
throw new TypeError('Illegal invocation')
207215
}
216+
217+
return makeIterator(
218+
makeIterable(this[kState], 'values'),
219+
'FormData'
220+
)
208221
}
209222

210-
* values () {
223+
/**
224+
* @param {(value: string, key: string, self: FormData) => void} callbackFn
225+
* @param {unknown} thisArg
226+
*/
227+
forEach (callbackFn, thisArg = globalThis) {
211228
if (!(this instanceof FormData)) {
212229
throw new TypeError('Illegal invocation')
213230
}
214231

215-
for (const [, value] of this) {
216-
yield value
232+
if (arguments.length < 1) {
233+
throw new TypeError(
234+
`Failed to execute 'forEach' on 'FormData': 1 argument required, but only ${arguments.length} present.`
235+
)
217236
}
218-
}
219237

220-
* [Symbol.iterator] () {
221-
// The value pairs to iterate over are this’s entry list’s entries with
222-
// the key being the name and the value being the value.
223-
for (const { name, value } of this[kState]) {
224-
yield [name, value]
238+
if (typeof callbackFn !== 'function') {
239+
throw new TypeError(
240+
"Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'."
241+
)
242+
}
243+
244+
for (const [key, value] of this) {
245+
callbackFn.apply(thisArg, [value, key, this])
225246
}
226247
}
227248
}
228249

250+
FormData.prototype[Symbol.iterator] = FormData.prototype.entries
251+
229252
function makeEntry (name, value, filename) {
230253
// To create an entry for name, value, and optionally a filename, run these
231254
// steps:
@@ -267,4 +290,18 @@ function makeEntry (name, value, filename) {
267290
return entry
268291
}
269292

293+
function * makeIterable (entries, type) {
294+
// The value pairs to iterate over are this’s entry list’s entries
295+
// with the key being the name and the value being the value.
296+
for (const { name, value } of entries) {
297+
if (type === 'entries') {
298+
yield [name, value]
299+
} else if (type === 'values') {
300+
yield value
301+
} else {
302+
yield name
303+
}
304+
}
305+
}
306+
270307
module.exports = { FormData }

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

+4-30
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const { validateHeaderName, validateHeaderValue } = require('http')
66
const { kHeadersList } = require('../core/symbols')
77
const { kGuard } = require('./symbols')
88
const { kEnumerableProperty } = require('../core/util')
9+
const { makeIterator } = require('./util')
910

1011
const kHeadersMap = Symbol('headers map')
1112
const kHeadersSortedMap = Symbol('headers map sorted')
@@ -73,33 +74,6 @@ function fill (headers, object) {
7374
}
7475
}
7576

76-
// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object
77-
const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))
78-
79-
// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
80-
function makeHeadersIterator (iterator) {
81-
const i = {
82-
next () {
83-
if (Object.getPrototypeOf(this) !== i) {
84-
throw new TypeError(
85-
'\'next\' called on an object that does not implement interface Headers Iterator.'
86-
)
87-
}
88-
89-
return iterator.next()
90-
},
91-
// The class string of an iterator prototype object for a given interface is the
92-
// result of concatenating the identifier of the interface and the string " Iterator".
93-
[Symbol.toStringTag]: 'Headers Iterator'
94-
}
95-
96-
// The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.
97-
Object.setPrototypeOf(i, esIteratorPrototype)
98-
// esIteratorPrototype needs to be the prototype of i
99-
// which is the prototype of an empty object. Yes, it's confusing.
100-
return Object.setPrototypeOf({}, i)
101-
}
102-
10377
class HeadersList {
10478
constructor (init) {
10579
if (init instanceof HeadersList) {
@@ -306,23 +280,23 @@ class Headers {
306280
throw new TypeError('Illegal invocation')
307281
}
308282

309-
return makeHeadersIterator(this[kHeadersSortedMap].keys())
283+
return makeIterator(this[kHeadersSortedMap].keys(), 'Headers')
310284
}
311285

312286
values () {
313287
if (!(this instanceof Headers)) {
314288
throw new TypeError('Illegal invocation')
315289
}
316290

317-
return makeHeadersIterator(this[kHeadersSortedMap].values())
291+
return makeIterator(this[kHeadersSortedMap].values(), 'Headers')
318292
}
319293

320294
entries () {
321295
if (!(this instanceof Headers)) {
322296
throw new TypeError('Illegal invocation')
323297
}
324298

325-
return makeHeadersIterator(this[kHeadersSortedMap].entries())
299+
return makeIterator(this[kHeadersSortedMap].entries(), 'Headers')
326300
}
327301

328302
/**

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ async function httpRedirectFetch (fetchParams, response) {
11641164
if (
11651165
([301, 302].includes(actualResponse.status) && request.method === 'POST') ||
11661166
(actualResponse.status === 303 &&
1167-
!['GET', 'HEADER'].includes(request.method))
1167+
!['GET', 'HEAD'].includes(request.method))
11681168
) {
11691169
// then:
11701170
// 1. Set request’s method to `GET` and request’s body to null.

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

+29-1
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,33 @@ function serializeJavascriptValueToJSONString (value) {
361361
return result
362362
}
363363

364+
// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object
365+
const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))
366+
367+
// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
368+
function makeIterator (iterator, name) {
369+
const i = {
370+
next () {
371+
if (Object.getPrototypeOf(this) !== i) {
372+
throw new TypeError(
373+
`'next' called on an object that does not implement interface ${name} Iterator.`
374+
)
375+
}
376+
377+
return iterator.next()
378+
},
379+
// The class string of an iterator prototype object for a given interface is the
380+
// result of concatenating the identifier of the interface and the string " Iterator".
381+
[Symbol.toStringTag]: `${name} Iterator`
382+
}
383+
384+
// The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.
385+
Object.setPrototypeOf(i, esIteratorPrototype)
386+
// esIteratorPrototype needs to be the prototype of i
387+
// which is the prototype of an empty object. Yes, it's confusing.
388+
return Object.setPrototypeOf({}, i)
389+
}
390+
364391
module.exports = {
365392
isAborted,
366393
isCancelled,
@@ -390,5 +417,6 @@ module.exports = {
390417
isValidReasonPhrase,
391418
sameOrigin,
392419
normalizeMethod,
393-
serializeJavascriptValueToJSONString
420+
serializeJavascriptValueToJSONString,
421+
makeIterator
394422
}

0 commit comments

Comments
 (0)