Skip to content

Commit

Permalink
deps: update undici to 5.5.1
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
nodejs-github-bot authored and targos committed Jul 12, 2022
1 parent ffe79ac commit 7e77033
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 154 deletions.
27 changes: 11 additions & 16 deletions deps/undici/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,12 @@ Help us improve the test coverage by following instructions at [nodejs/undici/#9
Basic usage example:

```js
import {fetch} from 'undici';
import { fetch } from 'undici';

async function fetchJson() {
const res = await fetch('https://example.com')
const json = await res.json()
console.log(json);
}

const res = await fetch('https://example.com')
const json = await res.json()
console.log(json);
```

You can pass an optional dispatcher to `fetch` as:
Expand Down Expand Up @@ -235,24 +234,20 @@ const data = {
},
};

(async () => {
await fetch("https://example.com", { body: data, method: 'POST' });
})();
await fetch("https://example.com", { body: data, method: 'POST' });
```

#### `response.body`

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()`.

```js
import {fetch} from 'undici';
import {Readable} from 'node:stream';
import { fetch } from 'undici';
import { Readable } from 'node:stream';

async function fetchStream() {
const response = await fetch('https://example.com')
const readableWebStream = response.body;
const readableNodeStream = Readable.fromWeb(readableWebStream);
}
const response = await fetch('https://example.com')
const readableWebStream = response.body;
const readableNodeStream = Readable.fromWeb(readableWebStream);
```

#### Specification Compliance
Expand Down
22 changes: 19 additions & 3 deletions deps/undici/src/lib/api/api-connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ConnectHandler extends AsyncResource {
throw new InvalidArgumentError('invalid callback')
}

const { signal, opaque, responseHeaders } = opts
const { signal, opaque, responseHeaders, httpTunnel } = opts

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

addSignal(this, signal)
}
Expand All @@ -40,8 +41,23 @@ class ConnectHandler extends AsyncResource {
this.context = context
}

onHeaders () {
throw new SocketError('bad connect', null)
onHeaders (statusCode) {
// when httpTunnel headers are allowed
if (this.httpTunnel) {
const { callback, opaque } = this
if (statusCode !== 200) {
if (callback) {
this.callback = null
const err = new RequestAbortedError('Proxy response !== 200 when HTTP Tunneling')
queueMicrotask(() => {
this.runInAsyncScope(callback, null, err, { opaque })
})
}
return 1
}
} else {
throw new SocketError('bad connect', null)
}
}

onUpgrade (statusCode, rawHeaders, socket) {
Expand Down
4 changes: 3 additions & 1 deletion deps/undici/src/lib/core/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
timeout = timeout == null ? 10e3 : timeout
maxCachedSessions = maxCachedSessions == null ? 100 : maxCachedSessions

return function connect ({ hostname, host, protocol, port, servername }, callback) {
return function connect ({ hostname, host, protocol, port, servername, httpSocket }, callback) {
let socket
if (protocol === 'https:') {
if (!tls) {
Expand All @@ -39,6 +39,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
...options,
servername,
session,
socket: httpSocket, // upgrade socket connection
port: port || 443,
host: hostname
})
Expand All @@ -65,6 +66,7 @@ function buildConnector ({ maxCachedSessions, socketPath, timeout, ...opts }) {
}
})
} else {
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
socket = net.connect({
highWaterMark: 64 * 1024, // Same as nodejs fs streams.
...options,
Expand Down
15 changes: 9 additions & 6 deletions deps/undici/src/lib/core/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ class Request {
}, handler) {
if (typeof path !== 'string') {
throw new InvalidArgumentError('path must be a string')
} else if (path[0] !== '/' && !(path.startsWith('http://') || path.startsWith('https://'))) {
} else if (
path[0] !== '/' &&
!(path.startsWith('http://') || path.startsWith('https://')) &&
method !== 'CONNECT'
) {
throw new InvalidArgumentError('path must be an absolute URL or start with a slash')
}

Expand Down Expand Up @@ -80,13 +84,12 @@ class Request {
this.body = null
} else if (util.isStream(body)) {
this.body = body
} else if (body instanceof DataView) {
// TODO: Why is DataView special?
this.body = body.buffer.byteLength ? Buffer.from(body.buffer) : null
} else if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
this.body = body.byteLength ? Buffer.from(body) : null
} else if (util.isBuffer(body)) {
this.body = body.byteLength ? body : null
} else if (ArrayBuffer.isView(body)) {
this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null
} else if (body instanceof ArrayBuffer) {
this.body = body.byteLength ? Buffer.from(body) : null
} else if (typeof body === 'string') {
this.body = body.length ? Buffer.from(body) : null
} else if (util.isFormDataLike(body) || util.isIterable(body) || util.isBlobLike(body)) {
Expand Down
4 changes: 2 additions & 2 deletions deps/undici/src/lib/fetch/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { kBodyUsed } = require('../core/symbols')
const assert = require('assert')
const { NotSupportedError } = require('../core/errors')
const { isErrored } = require('../core/util')
const { isUint8Array } = require('util/types')
const { isUint8Array, isArrayBuffer } = require('util/types')

let ReadableStream

Expand Down Expand Up @@ -61,7 +61,7 @@ function extractBody (object, keepalive = false) {

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

if (object instanceof DataView) {
Expand Down
71 changes: 54 additions & 17 deletions deps/undici/src/lib/fetch/formdata.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { isBlobLike, isFileLike, toUSVString } = require('./util')
const { isBlobLike, isFileLike, toUSVString, makeIterator } = require('./util')
const { kState } = require('./symbols')
const { File, FileLike } = require('./file')
const { Blob } = require('buffer')
Expand Down Expand Up @@ -187,45 +187,68 @@ class FormData {
return this.constructor.name
}

* entries () {
entries () {
if (!(this instanceof FormData)) {
throw new TypeError('Illegal invocation')
}

for (const pair of this) {
yield pair
}
return makeIterator(
makeIterable(this[kState], 'entries'),
'FormData'
)
}

* keys () {
keys () {
if (!(this instanceof FormData)) {
throw new TypeError('Illegal invocation')
}

for (const [key] of this) {
yield key
return makeIterator(
makeIterable(this[kState], 'keys'),
'FormData'
)
}

values () {
if (!(this instanceof FormData)) {
throw new TypeError('Illegal invocation')
}

return makeIterator(
makeIterable(this[kState], 'values'),
'FormData'
)
}

* values () {
/**
* @param {(value: string, key: string, self: FormData) => void} callbackFn
* @param {unknown} thisArg
*/
forEach (callbackFn, thisArg = globalThis) {
if (!(this instanceof FormData)) {
throw new TypeError('Illegal invocation')
}

for (const [, value] of this) {
yield value
if (arguments.length < 1) {
throw new TypeError(
`Failed to execute 'forEach' on 'FormData': 1 argument required, but only ${arguments.length} present.`
)
}
}

* [Symbol.iterator] () {
// The value pairs to iterate over are this’s entry list’s entries with
// the key being the name and the value being the value.
for (const { name, value } of this[kState]) {
yield [name, value]
if (typeof callbackFn !== 'function') {
throw new TypeError(
"Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'."
)
}

for (const [key, value] of this) {
callbackFn.apply(thisArg, [value, key, this])
}
}
}

FormData.prototype[Symbol.iterator] = FormData.prototype.entries

function makeEntry (name, value, filename) {
// To create an entry for name, value, and optionally a filename, run these
// steps:
Expand Down Expand Up @@ -267,4 +290,18 @@ function makeEntry (name, value, filename) {
return entry
}

function * makeIterable (entries, type) {
// The value pairs to iterate over are this’s entry list’s entries
// with the key being the name and the value being the value.
for (const { name, value } of entries) {
if (type === 'entries') {
yield [name, value]
} else if (type === 'values') {
yield value
} else {
yield name
}
}
}

module.exports = { FormData }
34 changes: 4 additions & 30 deletions deps/undici/src/lib/fetch/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { validateHeaderName, validateHeaderValue } = require('http')
const { kHeadersList } = require('../core/symbols')
const { kGuard } = require('./symbols')
const { kEnumerableProperty } = require('../core/util')
const { makeIterator } = require('./util')

const kHeadersMap = Symbol('headers map')
const kHeadersSortedMap = Symbol('headers map sorted')
Expand Down Expand Up @@ -73,33 +74,6 @@ function fill (headers, object) {
}
}

// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object
const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))

// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
function makeHeadersIterator (iterator) {
const i = {
next () {
if (Object.getPrototypeOf(this) !== i) {
throw new TypeError(
'\'next\' called on an object that does not implement interface Headers Iterator.'
)
}

return iterator.next()
},
// The class string of an iterator prototype object for a given interface is the
// result of concatenating the identifier of the interface and the string " Iterator".
[Symbol.toStringTag]: 'Headers Iterator'
}

// The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.
Object.setPrototypeOf(i, esIteratorPrototype)
// esIteratorPrototype needs to be the prototype of i
// which is the prototype of an empty object. Yes, it's confusing.
return Object.setPrototypeOf({}, i)
}

class HeadersList {
constructor (init) {
if (init instanceof HeadersList) {
Expand Down Expand Up @@ -306,23 +280,23 @@ class Headers {
throw new TypeError('Illegal invocation')
}

return makeHeadersIterator(this[kHeadersSortedMap].keys())
return makeIterator(this[kHeadersSortedMap].keys(), 'Headers')
}

values () {
if (!(this instanceof Headers)) {
throw new TypeError('Illegal invocation')
}

return makeHeadersIterator(this[kHeadersSortedMap].values())
return makeIterator(this[kHeadersSortedMap].values(), 'Headers')
}

entries () {
if (!(this instanceof Headers)) {
throw new TypeError('Illegal invocation')
}

return makeHeadersIterator(this[kHeadersSortedMap].entries())
return makeIterator(this[kHeadersSortedMap].entries(), 'Headers')
}

/**
Expand Down
2 changes: 1 addition & 1 deletion deps/undici/src/lib/fetch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,7 @@ async function httpRedirectFetch (fetchParams, response) {
if (
([301, 302].includes(actualResponse.status) && request.method === 'POST') ||
(actualResponse.status === 303 &&
!['GET', 'HEADER'].includes(request.method))
!['GET', 'HEAD'].includes(request.method))
) {
// then:
// 1. Set request’s method to `GET` and request’s body to null.
Expand Down
30 changes: 29 additions & 1 deletion deps/undici/src/lib/fetch/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,33 @@ function serializeJavascriptValueToJSONString (value) {
return result
}

// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object
const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))

// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
function makeIterator (iterator, name) {
const i = {
next () {
if (Object.getPrototypeOf(this) !== i) {
throw new TypeError(
`'next' called on an object that does not implement interface ${name} Iterator.`
)
}

return iterator.next()
},
// The class string of an iterator prototype object for a given interface is the
// result of concatenating the identifier of the interface and the string " Iterator".
[Symbol.toStringTag]: `${name} Iterator`
}

// The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%.
Object.setPrototypeOf(i, esIteratorPrototype)
// esIteratorPrototype needs to be the prototype of i
// which is the prototype of an empty object. Yes, it's confusing.
return Object.setPrototypeOf({}, i)
}

module.exports = {
isAborted,
isCancelled,
Expand Down Expand Up @@ -390,5 +417,6 @@ module.exports = {
isValidReasonPhrase,
sameOrigin,
normalizeMethod,
serializeJavascriptValueToJSONString
serializeJavascriptValueToJSONString,
makeIterator
}
Loading

0 comments on commit 7e77033

Please sign in to comment.