Skip to content

Commit

Permalink
deps: update undici to 5.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
nodejs-github-bot committed Jul 3, 2022
1 parent 8c53794 commit 23ff898
Show file tree
Hide file tree
Showing 21 changed files with 2,478 additions and 629 deletions.
10 changes: 6 additions & 4 deletions deps/undici/src/docs/api/Dispatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,22 +461,22 @@ Arguments:
* **options** `RequestOptions`
* **callback** `(error: Error | null, data: ResponseData) => void` (optional)

Returns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callback` argument was passed
Returns: `void | Promise<ResponseData>` - Only returns a `Promise` if no `callback` argument was passed.

#### Parameter: `RequestOptions`

Extends: [`DispatchOptions`](#parameter-dispatchoptions)

* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`
* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`
* **opaque** `unknown` (optional) - Default: `null` - Used for passing through context to `ResponseData`.
* **signal** `AbortSignal | events.EventEmitter | null` (optional) - Default: `null`.
* **onInfo** `({statusCode: number, headers: Record<string, string | string[]>}) => void | null` (optional) - Default: `null` - Callback collecting all the info headers (HTTP 100-199) received.

The `RequestOptions.method` property should not be value `'CONNECT'`.

#### Parameter: `ResponseData`

* **statusCode** `number`
* **headers** `http.IncomingHttpHeaders`
* **headers** `http.IncomingHttpHeaders` - Note that all header keys are lower-cased, e. g. `content-type`.
* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
* **trailers** `Record<string, string>` - This object starts out
as empty and will be mutated to contain trailers after `body` has emitted `'end'`.
Expand All @@ -497,6 +497,8 @@ The `RequestOptions.method` property should not be value `'CONNECT'`.

- `dump({ limit: Integer })`, dump the response by reading up to `limit` bytes without killing the socket (optional) - Default: 262144.

Note that body will still be a `Readable` even if it is empty, but attempting to deserialize it with `json()` will result in an exception. Recommended way to ensure there is a body to deserialize is to check if status code is not 204, and `content-type` header starts with `application/json`.

#### Example 1 - Basic GET Request

```js
Expand Down
22 changes: 3 additions & 19 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, httpTunnel } = opts
const { signal, opaque, responseHeaders } = opts

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

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

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)
}
onHeaders () {
throw new SocketError('bad connect', null)
}

onUpgrade (statusCode, rawHeaders, socket) {
Expand Down
34 changes: 31 additions & 3 deletions deps/undici/src/lib/api/api-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,17 @@ class RequestHandler extends AsyncResource {
}

const parsedHeaders = util.parseHeaders(rawHeaders)
const body = new Readable(resume, abort, parsedHeaders['content-type'])
const contentType = parsedHeaders['content-type']
const body = new Readable(resume, abort, contentType)

this.callback = null
this.res = body
const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders)

if (callback !== null) {
if (this.throwOnError && statusCode >= 400) {
this.runInAsyncScope(callback, null,
new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers)
this.runInAsyncScope(getResolveErrorBodyCallback, null,
{ callback, body, contentType, statusCode, statusMessage, headers }
)
return
}
Expand Down Expand Up @@ -152,6 +153,33 @@ class RequestHandler extends AsyncResource {
}
}

async function getResolveErrorBodyCallback ({ callback, body, contentType, statusCode, statusMessage, headers }) {
if (statusCode === 204 || !contentType) {
body.dump()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
return
}

try {
if (contentType.startsWith('application/json')) {
const payload = await body.json()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
return
}

if (contentType.startsWith('text/')) {
const payload = await body.text()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload))
return
}
} catch (err) {
// Process in a fallback if error
}

body.dump()
process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers))
}

function request (opts, callback) {
if (callback === undefined) {
return new Promise((resolve, reject) => {
Expand Down
8 changes: 3 additions & 5 deletions deps/undici/src/lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ class Parser {
}
}

if (request.method === 'CONNECT' && statusCode >= 200 && statusCode < 300) {
if (request.method === 'CONNECT') {
assert(client[kRunning] === 1)
this.upgrade = true
return 2
Expand Down Expand Up @@ -889,10 +889,8 @@ function onParserTimeout (parser) {

/* istanbul ignore else */
if (timeoutType === TIMEOUT_HEADERS) {
if (!socket[kWriting]) {
assert(!parser.paused, 'cannot be paused while waiting for headers')
util.destroy(socket, new HeadersTimeoutError())
}
assert(!parser.paused, 'cannot be paused while waiting for headers')
util.destroy(socket, new HeadersTimeoutError())
} else if (timeoutType === TIMEOUT_BODY) {
if (!parser.paused) {
util.destroy(socket, new BodyTimeoutError())
Expand Down
12 changes: 2 additions & 10 deletions deps/undici/src/lib/core/errors.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
'use strict'

class AbortError extends Error {
constructor () {
super('The operation was aborted')
this.code = 'ABORT_ERR'
this.name = 'AbortError'
}
}

class UndiciError extends Error {
constructor (message) {
super(message)
Expand Down Expand Up @@ -57,12 +49,13 @@ class BodyTimeoutError extends UndiciError {
}

class ResponseStatusCodeError extends UndiciError {
constructor (message, statusCode, headers) {
constructor (message, statusCode, headers, body) {
super(message)
Error.captureStackTrace(this, ResponseStatusCodeError)
this.name = 'ResponseStatusCodeError'
this.message = message || 'Response Status Code Error'
this.code = 'UND_ERR_RESPONSE_STATUS_CODE'
this.body = body
this.status = statusCode
this.statusCode = statusCode
this.headers = headers
Expand Down Expand Up @@ -191,7 +184,6 @@ class HTTPParserError extends Error {
}

module.exports = {
AbortError,
HTTPParserError,
UndiciError,
HeadersTimeoutError,
Expand Down
154 changes: 95 additions & 59 deletions deps/undici/src/lib/fetch/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const util = require('../core/util')
const { ReadableStreamFrom, toUSVString, isBlobLike } = require('./util')
const { FormData } = require('./formdata')
const { kState } = require('./symbols')
const { webidl } = require('./webidl')
const { Blob } = require('buffer')
const { kBodyUsed } = require('../core/symbols')
const assert = require('assert')
Expand Down Expand Up @@ -262,100 +263,135 @@ function cloneBody (body) {
}
}

const methods = {
async blob () {
const chunks = []
function bodyMixinMethods (instance) {
const methods = {
async blob () {
if (!(this instanceof instance)) {
throw new TypeError('Illegal invocation')
}

if (this[kState].body) {
if (isUint8Array(this[kState].body)) {
chunks.push(this[kState].body)
} else {
const stream = this[kState].body.stream
const chunks = []

if (util.isDisturbed(stream)) {
throw new TypeError('disturbed')
}
if (this[kState].body) {
if (isUint8Array(this[kState].body)) {
chunks.push(this[kState].body)
} else {
const stream = this[kState].body.stream

if (stream.locked) {
throw new TypeError('locked')
}
if (util.isDisturbed(stream)) {
throw new TypeError('disturbed')
}

if (stream.locked) {
throw new TypeError('locked')
}

// Compat.
stream[kBodyUsed] = true
// Compat.
stream[kBodyUsed] = true

for await (const chunk of stream) {
chunks.push(chunk)
for await (const chunk of stream) {
chunks.push(chunk)
}
}
}
}

return new Blob(chunks, { type: this.headers.get('Content-Type') || '' })
},
return new Blob(chunks, { type: this.headers.get('Content-Type') || '' })
},

async arrayBuffer () {
const blob = await this.blob()
return await blob.arrayBuffer()
},
async arrayBuffer () {
if (!(this instanceof instance)) {
throw new TypeError('Illegal invocation')
}

async text () {
const blob = await this.blob()
return toUSVString(await blob.text())
},
const blob = await this.blob()
return await blob.arrayBuffer()
},

async json () {
return JSON.parse(await this.text())
},
async text () {
if (!(this instanceof instance)) {
throw new TypeError('Illegal invocation')
}

const blob = await this.blob()
return toUSVString(await blob.text())
},

async formData () {
const contentType = this.headers.get('Content-Type')

// If mimeType’s essence is "multipart/form-data", then:
if (/multipart\/form-data/.test(contentType)) {
throw new NotSupportedError('multipart/form-data not supported')
} else if (/application\/x-www-form-urlencoded/.test(contentType)) {
// Otherwise, if mimeType’s essence is "application/x-www-form-urlencoded", then:

// 1. Let entries be the result of parsing bytes.
let entries
try {
entries = new URLSearchParams(await this.text())
} catch (err) {
// istanbul ignore next: Unclear when new URLSearchParams can fail on a string.
// 2. If entries is failure, then throw a TypeError.
throw Object.assign(new TypeError(), { cause: err })
async json () {
if (!(this instanceof instance)) {
throw new TypeError('Illegal invocation')
}

// 3. Return a new FormData object whose entries are entries.
const formData = new FormData()
for (const [name, value] of entries) {
formData.append(name, value)
return JSON.parse(await this.text())
},

async formData () {
if (!(this instanceof instance)) {
throw new TypeError('Illegal invocation')
}

const contentType = this.headers.get('Content-Type')

// If mimeType’s essence is "multipart/form-data", then:
if (/multipart\/form-data/.test(contentType)) {
throw new NotSupportedError('multipart/form-data not supported')
} else if (/application\/x-www-form-urlencoded/.test(contentType)) {
// Otherwise, if mimeType’s essence is "application/x-www-form-urlencoded", then:

// 1. Let entries be the result of parsing bytes.
let entries
try {
entries = new URLSearchParams(await this.text())
} catch (err) {
// istanbul ignore next: Unclear when new URLSearchParams can fail on a string.
// 2. If entries is failure, then throw a TypeError.
throw Object.assign(new TypeError(), { cause: err })
}

// 3. Return a new FormData object whose entries are entries.
const formData = new FormData()
for (const [name, value] of entries) {
formData.append(name, value)
}
return formData
} else {
// Otherwise, throw a TypeError.
webidl.errors.exception({
header: `${instance.name}.formData`,
value: 'Could not parse content as FormData.'
})
}
return formData
} else {
// Otherwise, throw a TypeError.
throw new TypeError()
}
}

return methods
}

const properties = {
body: {
enumerable: true,
get () {
if (!this || !this[kState]) {
throw new TypeError('Illegal invocation')
}

return this[kState].body ? this[kState].body.stream : null
}
},
bodyUsed: {
enumerable: true,
get () {
if (!this || !this[kState]) {
throw new TypeError('Illegal invocation')
}

return !!this[kState].body && util.isDisturbed(this[kState].body.stream)
}
}
}

function mixinBody (prototype) {
Object.assign(prototype, methods)
Object.defineProperties(prototype, properties)
Object.assign(prototype.prototype, bodyMixinMethods(prototype))
Object.defineProperties(prototype.prototype, properties)
}

module.exports = {
Expand Down
Loading

0 comments on commit 23ff898

Please sign in to comment.