diff options
author | Node.js GitHub Bot <github-bot@iojs.org> | 2022-05-25 01:23:21 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-25 01:23:21 +0300 |
commit | 810893f145dbba6bfc823afbc5958505378ba963 (patch) | |
tree | 2d58997a79002162bc69d829f0bfb0d84cadbf61 | |
parent | 4a3ba87eabd7bbceb95f0e902fa37a76c0aff223 (diff) |
deps: update undici to 5.3.0
PR-URL: https://github.com/nodejs/node/pull/43197
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
22 files changed, 487 insertions, 284 deletions
diff --git a/deps/undici/src/README.md b/deps/undici/src/README.md index 5acff95e270..b7a5ef1acd4 100644 --- a/deps/undici/src/README.md +++ b/deps/undici/src/README.md @@ -198,7 +198,7 @@ You can pass an optional dispatcher to `fetch` as: ```js import { fetch, Agent } from 'undici' - + const res = await fetch('https://example.com', { // Mocks are also supported dispatcher: new Agent({ @@ -375,6 +375,7 @@ Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling * [__Daniele Belardi__](https://github.com/dnlup), <https://www.npmjs.com/~dnlup> * [__Ethan Arrowood__](https://github.com/ethan-arrowood), <https://www.npmjs.com/~ethan_arrowood> * [__Matteo Collina__](https://github.com/mcollina), <https://www.npmjs.com/~matteo.collina> +* [__Matthew Aitken__](https://github.com/KhafraDev), <https://www.npmjs.com/~khaf> * [__Robert Nagy__](https://github.com/ronag), <https://www.npmjs.com/~ronag> * [__Szymon Marczak__](https://github.com/szmarczak), <https://www.npmjs.com/~szmarczak> * [__Tomas Della Vedova__](https://github.com/delvedor), <https://www.npmjs.com/~delvedor> diff --git a/deps/undici/src/docs/api/Dispatcher.md b/deps/undici/src/docs/api/Dispatcher.md index 56b34275209..ffe72cceb1b 100644 --- a/deps/undici/src/docs/api/Dispatcher.md +++ b/deps/undici/src/docs/api/Dispatcher.md @@ -194,18 +194,20 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo * **method** `string` * **body** `string | Buffer | Uint8Array | stream.Readable | Iterable | AsyncIterable | null` (optional) - Default: `null` * **headers** `UndiciHeaders | string[]` (optional) - Default: `null`. +* **query** `Record<string, any> | null` (optional) - Default: `null` - Query string params to be embedded in the request URL. Note that both keys and values of query are encoded using `encodeURIComponent`. If for some reason you need to send them unencoded, embed query params into path directly instead. * **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline has completed. * **blocking** `boolean` (optional) - Default: `false` - Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received. * **upgrade** `string | null` (optional) - Default: `null` - Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`. * **bodyTimeout** `number | null` (optional) - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 30 seconds. * **headersTimeout** `number | null` (optional) - The amount of time the parser will wait to receive the complete HTTP headers. Defaults to 30 seconds. +* **throwOnError** `boolean` (optional) - Default: `false` - Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server. #### Parameter: `DispatchHandler` * **onConnect** `(abort: () => void, context: object) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails. * **onError** `(error: Error) => void` - Invoked when an error has occurred. May not throw. * **onUpgrade** `(statusCode: number, headers: Buffer[], socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`. -* **onHeaders** `(statusCode: number, headers: Buffer[], resume: () => void) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests. +* **onHeaders** `(statusCode: number, headers: Buffer[], resume: () => void, statusText: string) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests. * **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests. * **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests. * **onBodySent** `(chunk: string | Buffer | Uint8Array) => void` - Invoked when a body chunk is sent to the server. Not required. For a stream or iterable body this will be invoked for every chunk. For other body types, it will be invoked once after the body is sent. diff --git a/deps/undici/src/docs/best-practices/mocking-request.md b/deps/undici/src/docs/best-practices/mocking-request.md index ebcc90d24b7..b98a450a32e 100644 --- a/deps/undici/src/docs/best-practices/mocking-request.md +++ b/deps/undici/src/docs/best-practices/mocking-request.md @@ -101,4 +101,36 @@ const badRequest = await bankTransfer('1234567890', '100') // subsequent request to origin http://localhost:3000 was not allowed (net.connect disabled) ``` +## Reply with data based on request +If the mocked response needs to be dynamically derived from the request parameters, you can provide a function instead of an object to `reply` + +```js +mockPool.intercept({ + path: '/bank-transfer', + method: 'POST', + headers: { + 'X-TOKEN-SECRET': 'SuperSecretToken', + }, + body: JSON.stringify({ + recepient: '1234567890', + amount: '100' + }) +}).reply(200, (opts) => { + // do something with opts + + return { message: 'transaction processed' } +}) +``` + +in this case opts will be + +``` +{ + method: 'POST', + headers: { 'X-TOKEN-SECRET': 'SuperSecretToken' }, + body: '{"recepient":"1234567890","amount":"100"}', + origin: 'http://localhost:3000', + path: '/bank-transfer' +} +``` diff --git a/deps/undici/src/index.d.ts b/deps/undici/src/index.d.ts index 44ab285041f..e4aa8f62cdb 100644 --- a/deps/undici/src/index.d.ts +++ b/deps/undici/src/index.d.ts @@ -16,6 +16,7 @@ import { request, pipeline, stream, connect, upgrade } from './types/api' export * from './types/fetch' export * from './types/file' export * from './types/formdata' +export * from './types/diagnostics-channel' export { Interceptable } from './types/mock-interceptor' export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, MockClient, MockPool, MockAgent, mockErrors, ProxyAgent } diff --git a/deps/undici/src/lib/api/api-request.js b/deps/undici/src/lib/api/api-request.js index bcfe483ebef..2fc5afa991c 100644 --- a/deps/undici/src/lib/api/api-request.js +++ b/deps/undici/src/lib/api/api-request.js @@ -3,7 +3,8 @@ const Readable = require('./readable') const { InvalidArgumentError, - RequestAbortedError + RequestAbortedError, + ResponseStatusCodeError } = require('../core/errors') const util = require('../core/util') const { AsyncResource } = require('async_hooks') @@ -15,7 +16,7 @@ class RequestHandler extends AsyncResource { throw new InvalidArgumentError('invalid opts') } - const { signal, method, opaque, body, onInfo, responseHeaders } = opts + const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts try { if (typeof callback !== 'function') { @@ -51,6 +52,7 @@ class RequestHandler extends AsyncResource { this.trailers = {} this.context = null this.onInfo = onInfo || null + this.throwOnError = throwOnError if (util.isStream(body)) { body.on('error', (err) => { @@ -70,7 +72,7 @@ class RequestHandler extends AsyncResource { this.context = context } - onHeaders (statusCode, rawHeaders, resume) { + onHeaders (statusCode, rawHeaders, resume, statusMessage) { const { callback, opaque, abort, context } = this if (statusCode < 200) { @@ -89,6 +91,13 @@ class RequestHandler extends AsyncResource { 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) + ) + return + } + this.runInAsyncScope(callback, null, null, { statusCode, headers, diff --git a/deps/undici/src/lib/core/errors.js b/deps/undici/src/lib/core/errors.js index f480f31a176..a36fd067c9f 100644 --- a/deps/undici/src/lib/core/errors.js +++ b/deps/undici/src/lib/core/errors.js @@ -56,6 +56,19 @@ class BodyTimeoutError extends UndiciError { } } +class ResponseStatusCodeError extends UndiciError { + constructor (message, statusCode, headers) { + super(message) + Error.captureStackTrace(this, ResponseStatusCodeError) + this.name = 'ResponseStatusCodeError' + this.message = message || 'Response Status Code Error' + this.code = 'UND_ERR_RESPONSE_STATUS_CODE' + this.status = statusCode + this.statusCode = statusCode + this.headers = headers + } +} + class InvalidArgumentError extends UndiciError { constructor (message) { super(message) @@ -186,6 +199,7 @@ module.exports = { BodyTimeoutError, RequestContentLengthMismatchError, ConnectTimeoutError, + ResponseStatusCodeError, InvalidArgumentError, InvalidReturnValueError, RequestAbortedError, diff --git a/deps/undici/src/lib/core/request.js b/deps/undici/src/lib/core/request.js index f04fe4fab21..2a13b0549a8 100644 --- a/deps/undici/src/lib/core/request.js +++ b/deps/undici/src/lib/core/request.js @@ -4,8 +4,8 @@ const { InvalidArgumentError, NotSupportedError } = require('./errors') -const util = require('./util') const assert = require('assert') +const util = require('./util') const kHandler = Symbol('handler') @@ -38,11 +38,13 @@ class Request { method, body, headers, + query, idempotent, blocking, upgrade, headersTimeout, - bodyTimeout + bodyTimeout, + throwOnError }, handler) { if (typeof path !== 'string') { throw new InvalidArgumentError('path must be a string') @@ -70,6 +72,8 @@ class Request { this.bodyTimeout = bodyTimeout + this.throwOnError = throwOnError === true + this.method = method if (body == null) { @@ -97,7 +101,7 @@ class Request { this.upgrade = upgrade || null - this.path = path + this.path = query ? util.buildURL(path, query) : path this.origin = origin diff --git a/deps/undici/src/lib/core/util.js b/deps/undici/src/lib/core/util.js index 0cb60b0d2a4..635ef2e15f2 100644 --- a/deps/undici/src/lib/core/util.js +++ b/deps/undici/src/lib/core/util.js @@ -26,6 +26,51 @@ function isBlobLike (object) { ) } +function isObject (val) { + return val !== null && typeof val === 'object' +} + +// this escapes all non-uri friendly characters +function encode (val) { + return encodeURIComponent(val) +} + +// based on https://github.com/axios/axios/blob/63e559fa609c40a0a460ae5d5a18c3470ffc6c9e/lib/helpers/buildURL.js (MIT license) +function buildURL (url, queryParams) { + if (url.includes('?') || url.includes('#')) { + throw new Error('Query params cannot be passed when url already contains "?" or "#".') + } + if (!isObject(queryParams)) { + throw new Error('Query params must be an object') + } + + const parts = [] + for (let [key, val] of Object.entries(queryParams)) { + if (val === null || typeof val === 'undefined') { + continue + } + + if (!Array.isArray(val)) { + val = [val] + } + + for (const v of val) { + if (isObject(v)) { + throw new Error('Passing object as a query param is not supported, please serialize to string up-front') + } + parts.push(encode(key) + '=' + encode(v)) + } + } + + const serializedParams = parts.join('&') + + if (serializedParams) { + url += '?' + serializedParams + } + + return url +} + function parseURL (url) { if (typeof url === 'string') { url = new URL(url) @@ -357,5 +402,6 @@ module.exports = { isBuffer, validateHandler, getSocketInfo, - isFormDataLike + isFormDataLike, + buildURL } diff --git a/deps/undici/src/lib/fetch/file.js b/deps/undici/src/lib/fetch/file.js index 2b98739d2bb..a9c2355fc89 100644 --- a/deps/undici/src/lib/fetch/file.js +++ b/deps/undici/src/lib/fetch/file.js @@ -69,10 +69,6 @@ class File extends Blob { } get [Symbol.toStringTag] () { - if (!(this instanceof File)) { - throw new TypeError('Illegal invocation') - } - return this.constructor.name } } @@ -190,10 +186,6 @@ class FileLike { } get [Symbol.toStringTag] () { - if (!(this instanceof FileLike)) { - throw new TypeError('Illegal invocation') - } - return 'File' } } diff --git a/deps/undici/src/lib/fetch/formdata.js b/deps/undici/src/lib/fetch/formdata.js index 550e1e46947..e12d2b42bd2 100644 --- a/deps/undici/src/lib/fetch/formdata.js +++ b/deps/undici/src/lib/fetch/formdata.js @@ -6,6 +6,8 @@ const { File, FileLike } = require('./file') const { Blob } = require('buffer') class FormData { + static name = 'FormData' + constructor (...args) { if (args.length > 0 && !(args[0]?.constructor?.name === 'HTMLFormElement')) { throw new TypeError( @@ -182,10 +184,6 @@ class FormData { } get [Symbol.toStringTag] () { - if (!(this instanceof FormData)) { - throw new TypeError('Illegal invocation') - } - return this.constructor.name } @@ -269,4 +267,4 @@ function makeEntry (name, value, filename) { return entry } -module.exports = { FormData: globalThis.FormData ?? FormData } +module.exports = { FormData } diff --git a/deps/undici/src/lib/fetch/index.js b/deps/undici/src/lib/fetch/index.js index 5dab8a0532f..c1f3391995e 100644 --- a/deps/undici/src/lib/fetch/index.js +++ b/deps/undici/src/lib/fetch/index.js @@ -31,7 +31,6 @@ const { coarsenedSharedCurrentTime, createDeferredPromise, isBlobLike, - CORBCheck, sameOrigin, isCancelled, isAborted @@ -52,7 +51,6 @@ const EE = require('events') const { Readable, pipeline } = require('stream') const { isErrored, isReadable } = require('../core/util') const { dataURLProcessor } = require('./dataURL') -const { kIsMockActive } = require('../mock/mock-symbols') const { TransformStream } = require('stream/web') /** @type {import('buffer').resolveObjectURL} */ @@ -588,18 +586,8 @@ async function mainFetch (fetchParams, recursive = false) { // 2. Set request’s response tainting to "opaque". request.responseTainting = 'opaque' - // 3. Let noCorsResponse be the result of running scheme fetch given - // fetchParams. - const noCorsResponse = await schemeFetch(fetchParams) - - // 4. If noCorsResponse is a filtered response or the CORB check with - // request and noCorsResponse returns allowed, then return noCorsResponse. - if (noCorsResponse.status === 0 || CORBCheck(request, noCorsResponse) === 'allowed') { - return noCorsResponse - } - - // 5. Return a new response whose status is noCorsResponse’s status. - return makeResponse({ status: noCorsResponse.status }) + // 3. Return the result of running scheme fetch given fetchParams. + return await schemeFetch(fetchParams) } // request’s current URL’s scheme is not an HTTP(S) scheme @@ -1923,7 +1911,7 @@ async function httpNetworkFetch ( path: url.pathname + url.search, origin: url.origin, method: request.method, - body: fetchParams.controller.dispatcher[kIsMockActive] ? request.body && request.body.source : body, + body: fetchParams.controller.dispatcher.isMockActive ? request.body && request.body.source : body, headers: [...request.headersList].flat(), maxRedirections: 0, bodyTimeout: 300_000, diff --git a/deps/undici/src/lib/fetch/request.js b/deps/undici/src/lib/fetch/request.js index c17927ee768..661f5814ba2 100644 --- a/deps/undici/src/lib/fetch/request.js +++ b/deps/undici/src/lib/fetch/request.js @@ -516,10 +516,6 @@ class Request { } get [Symbol.toStringTag] () { - if (!(this instanceof Request)) { - throw new TypeError('Illegal invocation') - } - return this.constructor.name } diff --git a/deps/undici/src/lib/fetch/response.js b/deps/undici/src/lib/fetch/response.js index b8aa4897c1b..03db7eb488e 100644 --- a/deps/undici/src/lib/fetch/response.js +++ b/deps/undici/src/lib/fetch/response.js @@ -5,7 +5,7 @@ const { AbortError } = require('../core/errors') const { extractBody, cloneBody, mixinBody } = require('./body') const util = require('../core/util') const { kEnumerableProperty } = util -const { responseURL, isValidReasonPhrase, toUSVString, isCancelled, isAborted } = require('./util') +const { responseURL, isValidReasonPhrase, toUSVString, isCancelled, isAborted, serializeJavascriptValueToJSONString } = require('./util') const { redirectStatus, nullBodyStatus, @@ -35,6 +35,50 @@ class Response { return responseObject } + // https://fetch.spec.whatwg.org/#dom-response-json + static json (data, init = {}) { + if (arguments.length === 0) { + throw new TypeError( + 'Failed to execute \'json\' on \'Response\': 1 argument required, but 0 present.' + ) + } + + if (init === null || typeof init !== 'object') { + throw new TypeError( + `Failed to execute 'json' on 'Response': init must be a RequestInit, found ${typeof init}.` + ) + } + + init = { + status: 200, + statusText: '', + headers: new HeadersList(), + ...init + } + + // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data. + const bytes = new TextEncoder('utf-8').encode( + serializeJavascriptValueToJSONString(data) + ) + + // 2. Let body be the result of extracting bytes. + const body = extractBody(bytes) + + // 3. Let responseObject be the result of creating a Response object, given a new response, + // "response", and this’s relevant Realm. + const relevantRealm = { settingsObject: {} } + const responseObject = new Response() + responseObject[kRealm] = relevantRealm + responseObject[kHeaders][kGuard] = 'response' + responseObject[kHeaders][kRealm] = relevantRealm + + // 4. Perform initialize a response given responseObject, init, and (body, "application/json"). + initializeResponse(responseObject, init, { body: body[0], type: 'application/json' }) + + // 5. Return responseObject. + return responseObject + } + // Creates a redirect Response that redirects to url with status status. static redirect (...args) { const relevantRealm = { settingsObject: {} } @@ -105,34 +149,10 @@ class Response { // TODO this[kRealm] = { settingsObject: {} } - // 1. If init["status"] is not in the range 200 to 599, inclusive, then - // throw a RangeError. - if ('status' in init && init.status !== undefined) { - if (!Number.isFinite(init.status)) { - throw new TypeError() - } - - if (init.status < 200 || init.status > 599) { - throw new RangeError( - `Failed to construct 'Response': The status provided (${init.status}) is outside the range [200, 599].` - ) - } - } - - if ('statusText' in init && init.statusText !== undefined) { - // 2. If init["statusText"] does not match the reason-phrase token - // production, then throw a TypeError. - // See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2: - // reason-phrase = *( HTAB / SP / VCHAR / obs-text ) - if (!isValidReasonPhrase(String(init.statusText))) { - throw new TypeError('Invalid statusText') - } - } - - // 3. Set this’s response to a new response. + // 1. Set this’s response to a new response. this[kState] = makeResponse({}) - // 4. Set this’s headers to a new Headers object with this’s relevant + // 2. Set this’s headers to a new Headers object with this’s relevant // Realm, whose header list is this’s response’s header list and guard // is "response". this[kHeaders] = new Headers() @@ -140,48 +160,20 @@ class Response { this[kHeaders][kHeadersList] = this[kState].headersList this[kHeaders][kRealm] = this[kRealm] - // 5. Set this’s response’s status to init["status"]. - if ('status' in init && init.status !== undefined) { - this[kState].status = init.status - } + // 3. Let bodyWithType be null. + let bodyWithType = null - // 6. Set this’s response’s status message to init["statusText"]. - if ('statusText' in init && init.statusText !== undefined) { - this[kState].statusText = String(init.statusText) - } - - // 7. If init["headers"] exists, then fill this’s headers with init["headers"]. - if ('headers' in init) { - fill(this[kState].headersList, init.headers) - } - - // 8. If body is non-null, then: + // 4. If body is non-null, then set bodyWithType to the result of extracting body. if (body != null) { - // 1. If init["status"] is a null body status, then throw a TypeError. - if (nullBodyStatus.includes(init.status)) { - throw new TypeError('Response with null body status cannot have body') - } - - // 2. Let Content-Type be null. - // 3. Set this’s response’s body and Content-Type to the result of - // extracting body. - const [extractedBody, contentType] = extractBody(body) - this[kState].body = extractedBody - - // 4. If Content-Type is non-null and this’s response’s header list does - // not contain `Content-Type`, then append `Content-Type`/Content-Type - // to this’s response’s header list. - if (contentType && !this.headers.has('content-type')) { - this.headers.append('content-type', contentType) - } + const [extractedBody, type] = extractBody(body) + bodyWithType = { body: extractedBody, type } } + + // 5. Perform initialize a response given this, init, and bodyWithType. + initializeResponse(this, init, bodyWithType) } get [Symbol.toStringTag] () { - if (!(this instanceof Response)) { - throw new TypeError('Illegal invocation') - } - return this.constructor.name } @@ -477,6 +469,57 @@ function makeAppropriateNetworkError (fetchParams) { : makeNetworkError(fetchParams.controller.terminated.reason) } +// https://whatpr.org/fetch/1392.html#initialize-a-response +function initializeResponse (response, init, body) { + // 1. If init["status"] is not in the range 200 to 599, inclusive, then + // throw a RangeError. + if (init.status != null && (init.status < 200 || init.status > 599)) { + throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.') + } + + // 2. If init["statusText"] does not match the reason-phrase token production, + // then throw a TypeError. + if ('statusText' in init && init.statusText != null) { + // See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2: + // reason-phrase = *( HTAB / SP / VCHAR / obs-text ) + if (!isValidReasonPhrase(String(init.statusText))) { + throw new TypeError('Invalid statusText') + } + } + + // 3. Set response’s response’s status to init["status"]. + if ('status' in init && init.status != null) { + response[kState].status = init.status + } + + // 4. Set response’s response’s status message to init["statusText"]. + if ('statusText' in init && init.statusText != null) { + response[kState].statusText = init.statusText + } + + // 5. If init["headers"] exists, then fill response’s headers with init["headers"]. + if ('headers' in init && init.headers != null) { + fill(response[kState].headersList, init.headers) + } + + // 6. If body was given, then: + if (body) { + // 1. If response's status is a null body status, then throw a TypeError. + if (nullBodyStatus.includes(response.status)) { + throw new TypeError() + } + + // 2. Set response's body to body's body. + response[kState].body = body.body + + // 3. If body's type is non-null and response's header list does not contain + // `Content-Type`, then append (`Content-Type`, body's type) to response's header list. + if (body.type != null && !response[kState].headersList.has('Content-Type')) { + response[kState].headersList.append('content-type', body.type) + } + } +} + module.exports = { makeNetworkError, makeResponse, diff --git a/deps/undici/src/lib/fetch/util.js b/deps/undici/src/lib/fetch/util.js index 0116f67a8b6..1c67f9c7c84 100644 --- a/deps/undici/src/lib/fetch/util.js +++ b/deps/undici/src/lib/fetch/util.js @@ -3,6 +3,7 @@ const { redirectStatus } = require('./constants') const { performance } = require('perf_hooks') const { isBlobLike, toUSVString, ReadableStreamFrom } = require('../core/util') +const assert = require('assert') let File @@ -316,47 +317,6 @@ function sameOrigin (A, B) { return false } -// https://fetch.spec.whatwg.org/#corb-check -function CORBCheck (request, response) { - // 1. If request’s initiator is "download", then return allowed. - if (request.initiator === 'download') { - return 'allowed' - } - - // 2. If request’s current URL’s scheme is not an HTTP(S) scheme, then return allowed. - if (!/^https?$/.test(request.currentURL.scheme)) { - return 'allowed' - } - - // 3. Let mimeType be the result of extracting a MIME type from response’s header list. - const mimeType = response.headersList.get('content-type') - - // 4. If mimeType is failure, then return allowed. - if (mimeType === '') { - return 'allowed' - } - - // 5. If response’s status is 206 and mimeType is a CORB-protected MIME type, then return blocked. - - const isCORBProtectedMIME = - (/^text\/html\b/.test(mimeType) || - /^application\/javascript\b/.test(mimeType) || - /^application\/xml\b/.test(mimeType)) && !/^application\/xml\+svg\b/.test(mimeType) - - if (response.status === 206 && isCORBProtectedMIME) { - return 'blocked' - } - - // 6. If determine nosniff with response’s header list is true and mimeType is a CORB-protected MIME type or its essence is "text/plain", then return blocked. - // https://fetch.spec.whatwg.org/#determinenosniff - if (response.headersList.get('x-content-type-options') && isCORBProtectedMIME) { - return 'blocked' - } - - // 7. Return allowed. - return 'allowed' -} - function createDeferredPromise () { let res let rej @@ -384,6 +344,23 @@ function normalizeMethod (method) { : method } +// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string +function serializeJavascriptValueToJSONString (value) { + // 1. Let result be ? Call(%JSON.stringify%, undefined, « value »). + const result = JSON.stringify(value) + + // 2. If result is undefined, then throw a TypeError. + if (result === undefined) { + throw new TypeError('Value is not JSON serializable') + } + + // 3. Assert: result is a string. + assert(typeof result === 'string') + + // 4. Return result. + return result +} + module.exports = { isAborted, isCancelled, @@ -412,6 +389,6 @@ module.exports = { isFileLike, isValidReasonPhrase, sameOrigin, - CORBCheck, - normalizeMethod + normalizeMethod, + serializeJavascriptValueToJSONString } diff --git a/deps/undici/src/lib/mock/mock-agent.js b/deps/undici/src/lib/mock/mock-agent.js index 093da5e50a9..828e8af174d 100644 --- a/deps/undici/src/lib/mock/mock-agent.js +++ b/deps/undici/src/lib/mock/mock-agent.js @@ -96,6 +96,12 @@ class MockAgent extends Dispatcher { this[kNetConnect] = false } + // This is required to bypass issues caused by using global symbols - see: + // https://github.com/nodejs/undici/issues/1447 + get isMockActive () { + return this[kIsMockActive] + } + [kMockAgentSet] (origin, dispatcher) { this[kClients].set(origin, new FakeWeakRef(dispatcher)) } diff --git a/deps/undici/src/lib/mock/mock-utils.js b/deps/undici/src/lib/mock/mock-utils.js index a81f05af045..a9ab94f36ae 100644 --- a/deps/undici/src/lib/mock/mock-utils.js +++ b/deps/undici/src/lib/mock/mock-utils.js @@ -6,7 +6,6 @@ const { kMockAgent, kOriginalDispatch, kOrigin, - kIsMockActive, kGetNetConnect } = require('./mock-symbols') @@ -302,7 +301,7 @@ function buildMockDispatch () { const originalDispatch = this[kOriginalDispatch] return function dispatch (opts, handler) { - if (agent[kIsMockActive]) { + if (agent.isMockActive) { try { mockDispatch.call(this, opts, handler) } catch (error) { diff --git a/deps/undici/src/lib/proxy-agent.js b/deps/undici/src/lib/proxy-agent.js index 2da6c7a5036..37a41680617 100644 --- a/deps/undici/src/lib/proxy-agent.js +++ b/deps/undici/src/lib/proxy-agent.js @@ -1,7 +1,6 @@ 'use strict' const { kProxy, kClose, kDestroy } = require('./core/symbols') -const { URL } = require('url') const Agent = require('./agent') const DispatcherBase = require('./dispatcher-base') const { InvalidArgumentError } = require('./core/errors') diff --git a/deps/undici/src/package.json b/deps/undici/src/package.json index 763eed02078..b30753c16d9 100644 --- a/deps/undici/src/package.json +++ b/deps/undici/src/package.json @@ -1,6 +1,6 @@ { "name": "undici", - "version": "5.2.0", + "version": "5.3.0", "description": "An HTTP/1.1 client, written from scratch for Node.js", "homepage": "https://undici.nodejs.org", "bugs": { @@ -48,7 +48,7 @@ "lint:fix": "standard --fix | snazzy", "test": "npm run test:tap && npm run test:node-fetch && npm run test:fetch && npm run test:jest && tsd", "test:node-fetch": "node scripts/verifyVersion.js 16 || mocha test/node-fetch", - "test:fetch": "node scripts/verifyVersion.js 16 || tap test/fetch/*.js", + "test:fetch": "node scripts/verifyVersion.js 16 || (npm run build:node && tap test/fetch/*.js)", "test:jest": "jest", "test:tap": "tap test/*.js test/diagnostics-channel/*.js", "test:tdd": "tap test/*.js test/diagnostics-channel/*.js -w", @@ -91,6 +91,7 @@ "sinon": "^14.0.0", "snazzy": "^9.0.0", "standard": "^17.0.0", + "table": "^6.8.0", "tap": "^16.1.0", "tsd": "^0.20.0", "wait-on": "^6.0.0" diff --git a/deps/undici/src/types/diagnostics-channel.d.ts b/deps/undici/src/types/diagnostics-channel.d.ts new file mode 100644 index 00000000000..8bb1926506e --- /dev/null +++ b/deps/undici/src/types/diagnostics-channel.d.ts @@ -0,0 +1,66 @@ +import { Socket } from "net"; +import { connector } from "./connector"; +import { HttpMethod } from "./dispatcher"; + +declare namespace DiagnosticsChannel { + interface Request { + origin?: string | URL; + completed: boolean; + method?: HttpMethod; + path: string; + headers: string; + addHeader(key: string, value: string): Request; + } + interface Response { + statusCode: number; + statusText: string; + headers: Array<Buffer>; + } + type Error = unknown; + interface ConnectParams { + host: URL["host"]; + hostname: URL["hostname"]; + protocol: URL["protocol"]; + port: URL["port"]; + servername: string | null; + } + type Connector = typeof connector; + export interface RequestCreateMessage { + request: Request; + } + export interface RequestBodySentMessage { + request: Request; + } + export interface RequestHeadersMessage { + request: Request; + response: Response; + } + export interface RequestTrailersMessage { + request: Request; + trailers: Array<Buffer>; + } + export interface RequestErrorMessage { + request: Request; + error: Error; + } + export interface ClientSendHeadersMessage { + request: Request; + headers: string; + socket: Socket; + } + export interface ClientBeforeConnectMessage { + connectParams: ConnectParams; + connector: Connector; + } + export interface ClientConnectedMessage { + socket: Socket; + connectParams: ConnectParams; + connector: Connector; + } + export interface ClientConnectErrorMessage { + error: Error; + socket: Socket; + connectParams: ConnectParams; + connector: Connector; + } +} diff --git a/deps/undici/src/types/dispatcher.d.ts b/deps/undici/src/types/dispatcher.d.ts index 9b2af26e6d2..4bc3241ac3d 100644 --- a/deps/undici/src/types/dispatcher.d.ts +++ b/deps/undici/src/types/dispatcher.d.ts @@ -47,6 +47,8 @@ declare namespace Dispatcher { body?: string | Buffer | Uint8Array | Readable | null | FormData; /** Default: `null` */ headers?: IncomingHttpHeaders | string[] | null; + /** Query string params to be embedded in the request URL. Default: `null` */ + query?: Record<string, any>; /** Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline have completed. Default: `true` if `method` is `HEAD` or `GET`. */ idempotent?: boolean; /** Upgrade the request. Should be used to specify the kind of upgrade i.e. `'Websocket'`. Default: `method === 'CONNECT' || null`. */ @@ -55,6 +57,8 @@ declare namespace Dispatcher { headersTimeout?: number | null; /** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use 0 to disable it entirely. Defaults to 30 seconds. */ bodyTimeout?: number | null; + /** Whether Undici should throw an error upon receiving a 4xx or 5xx response from the server. Defaults to false */ + throwOnError?: boolean; } export interface ConnectOptions { path: string; diff --git a/deps/undici/src/types/fetch.d.ts b/deps/undici/src/types/fetch.d.ts index 1b8692b251c..47a08dd0510 100644 --- a/deps/undici/src/types/fetch.d.ts +++ b/deps/undici/src/types/fetch.d.ts @@ -101,19 +101,19 @@ type RequestDestination = | 'xslt' export interface RequestInit { - readonly method?: string - readonly keepalive?: boolean - readonly headers?: HeadersInit - readonly body?: BodyInit - readonly redirect?: RequestRedirect - readonly integrity?: string - readonly signal?: AbortSignal - readonly credentials?: RequestCredentials - readonly mode?: RequestMode - readonly referrer?: string - readonly referrerPolicy?: ReferrerPolicy - readonly window?: null - readonly dispatcher?: Dispatcher + method?: string + keepalive?: boolean + headers?: HeadersInit + body?: BodyInit + redirect?: RequestRedirect + integrity?: string + signal?: AbortSignal + credentials?: RequestCredentials + mode?: RequestMode + referrer?: string + referrerPolicy?: ReferrerPolicy + window?: null + dispatcher?: Dispatcher } export type ReferrerPolicy = @@ -199,5 +199,6 @@ export declare class Response implements BodyMixin { readonly clone: () => Response static error (): Response + static json(data: any, init?: ResponseInit): Response static redirect (url: string | URL, status: ResponseRedirectStatus): Response } diff --git a/deps/undici/undici.js b/deps/undici/undici.js index 86749222920..16c98d7469e 100644 --- a/deps/undici/undici.js +++ b/deps/undici/undici.js @@ -1,8 +1,14 @@ "use strict"; +var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; +var __publicField = (obj, key, value) => { + __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + return value; +}; // lib/core/errors.js var require_errors = __commonJS({ @@ -58,6 +64,18 @@ var require_errors = __commonJS({ this.code = "UND_ERR_BODY_TIMEOUT"; } }; + var ResponseStatusCodeError = class extends UndiciError { + constructor(message, statusCode, headers) { + super(message); + Error.captureStackTrace(this, ResponseStatusCodeError); + this.name = "ResponseStatusCodeError"; + this.message = message || "Response Status Code Error"; + this.code = "UND_ERR_RESPONSE_STATUS_CODE"; + this.status = statusCode; + this.statusCode = statusCode; + this.headers = headers; + } + }; var InvalidArgumentError = class extends UndiciError { constructor(message) { super(message); @@ -176,6 +194,7 @@ var require_errors = __commonJS({ BodyTimeoutError, RequestContentLengthMismatchError, ConnectTimeoutError, + ResponseStatusCodeError, InvalidArgumentError, InvalidReturnValueError, RequestAbortedError, @@ -664,6 +683,40 @@ var require_util = __commonJS({ function isBlobLike(object) { return Blob && object instanceof Blob || object && typeof object === "object" && (typeof object.stream === "function" || typeof object.arrayBuffer === "function") && /^(Blob|File)$/.test(object[Symbol.toStringTag]); } + function isObject(val) { + return val !== null && typeof val === "object"; + } + function encode(val) { + return encodeURIComponent(val); + } + function buildURL(url, queryParams) { + if (url.includes("?") || url.includes("#")) { + throw new Error('Query params cannot be passed when url already contains "?" or "#".'); + } + if (!isObject(queryParams)) { + throw new Error("Query params must be an object"); + } + const parts = []; + for (let [key, val] of Object.entries(queryParams)) { + if (val === null || typeof val === "undefined") { + continue; + } + if (!Array.isArray(val)) { + val = [val]; + } + for (const v of val) { + if (isObject(v)) { + throw new Error("Passing object as a query param is not supported, please serialize to string up-front"); + } + parts.push(encode(key) + "=" + encode(v)); + } + } + const serializedParams = parts.join("&"); + if (serializedParams) { + url += "?" + serializedParams; + } + return url; + } function parseURL(url) { if (typeof url === "string") { url = new URL(url); @@ -911,7 +964,8 @@ var require_util = __commonJS({ isBuffer, validateHandler, getSocketInfo, - isFormDataLike + isFormDataLike, + buildURL }; } }); @@ -1056,9 +1110,6 @@ var require_file = __commonJS({ return this[kState].lastModified; } get [Symbol.toStringTag]() { - if (!(this instanceof File)) { - throw new TypeError("Illegal invocation"); - } return this.constructor.name; } }; @@ -1123,9 +1174,6 @@ var require_file = __commonJS({ return this[kState].lastModified; } get [Symbol.toStringTag]() { - if (!(this instanceof FileLike)) { - throw new TypeError("Illegal invocation"); - } return "File"; } }; @@ -1140,6 +1188,7 @@ var require_util2 = __commonJS({ var { redirectStatus } = require_constants(); var { performance } = require("perf_hooks"); var { isBlobLike, toUSVString, ReadableStreamFrom } = require_util(); + var assert = require("assert"); var File; var badPorts = [ "1", @@ -1367,26 +1416,6 @@ var require_util2 = __commonJS({ } return false; } - function CORBCheck(request, response) { - if (request.initiator === "download") { - return "allowed"; - } - if (!/^https?$/.test(request.currentURL.scheme)) { - return "allowed"; - } - const mimeType = response.headersList.get("content-type"); - if (mimeType === "") { - return "allowed"; - } - const isCORBProtectedMIME = (/^text\/html\b/.test(mimeType) || /^application\/javascript\b/.test(mimeType) || /^application\/xml\b/.test(mimeType)) && !/^application\/xml\+svg\b/.test(mimeType); - if (response.status === 206 && isCORBProtectedMIME) { - return "blocked"; - } - if (response.headersList.get("x-content-type-options") && isCORBProtectedMIME) { - return "blocked"; - } - return "allowed"; - } function createDeferredPromise() { let res; let rej; @@ -1405,6 +1434,14 @@ var require_util2 = __commonJS({ function normalizeMethod(method) { return /^(DELETE|GET|HEAD|OPTIONS|POST|PUT)$/i.test(method) ? method.toUpperCase() : method; } + function serializeJavascriptValueToJSONString(value) { + const result = JSON.stringify(value); + if (result === void 0) { + throw new TypeError("Value is not JSON serializable"); + } + assert(typeof result === "string"); + return result; + } module2.exports = { isAborted, isCancelled, @@ -1433,8 +1470,8 @@ var require_util2 = __commonJS({ isFileLike, isValidReasonPhrase, sameOrigin, - CORBCheck, - normalizeMethod + normalizeMethod, + serializeJavascriptValueToJSONString }; } }); @@ -1447,7 +1484,7 @@ var require_formdata = __commonJS({ var { kState } = require_symbols2(); var { File, FileLike } = require_file(); var { Blob } = require("buffer"); - var FormData = class { + var _FormData = class { constructor(...args) { if (args.length > 0 && !(args[0]?.constructor?.name === "HTMLFormElement")) { throw new TypeError("Failed to construct 'FormData': parameter 1 is not of type 'HTMLFormElement'"); @@ -1455,7 +1492,7 @@ var require_formdata = __commonJS({ this[kState] = []; } append(...args) { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } if (args.length < 2) { @@ -1471,7 +1508,7 @@ var require_formdata = __commonJS({ this[kState].push(entry); } delete(...args) { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } if (args.length < 1) { @@ -1487,7 +1524,7 @@ var require_formdata = __commonJS({ this[kState] = next; } get(...args) { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } if (args.length < 1) { @@ -1501,7 +1538,7 @@ var require_formdata = __commonJS({ return this[kState][idx].value; } getAll(...args) { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } if (args.length < 1) { @@ -1511,7 +1548,7 @@ var require_formdata = __commonJS({ return this[kState].filter((entry) => entry.name === name).map((entry) => entry.value); } has(...args) { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } if (args.length < 1) { @@ -1521,7 +1558,7 @@ var require_formdata = __commonJS({ return this[kState].findIndex((entry) => entry.name === name) !== -1; } set(...args) { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } if (args.length < 2) { @@ -1546,13 +1583,10 @@ var require_formdata = __commonJS({ } } get [Symbol.toStringTag]() { - if (!(this instanceof FormData)) { - throw new TypeError("Illegal invocation"); - } return this.constructor.name; } *entries() { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } for (const pair of this) { @@ -1560,7 +1594,7 @@ var require_formdata = __commonJS({ } } *keys() { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } for (const [key] of this) { @@ -1568,7 +1602,7 @@ var require_formdata = __commonJS({ } } *values() { - if (!(this instanceof FormData)) { + if (!(this instanceof _FormData)) { throw new TypeError("Illegal invocation"); } for (const [, value] of this) { @@ -1581,6 +1615,8 @@ var require_formdata = __commonJS({ } } }; + var FormData = _FormData; + __publicField(FormData, "name", "FormData"); function makeEntry(name, value, filename) { const entry = { name: null, @@ -1596,7 +1632,7 @@ var require_formdata = __commonJS({ entry.value = value; return entry; } - module2.exports = { FormData: globalThis.FormData ?? FormData }; + module2.exports = { FormData }; } }); @@ -1833,8 +1869,8 @@ var require_request = __commonJS({ InvalidArgumentError, NotSupportedError } = require_errors(); - var util = require_util(); var assert = require("assert"); + var util = require_util(); var kHandler = Symbol("handler"); var channels = {}; var extractBody; @@ -1861,11 +1897,13 @@ var require_request = __commonJS({ method, body, headers, + query, idempotent, blocking, upgrade, headersTimeout, - bodyTimeout + bodyTimeout, + throwOnError }, handler) { if (typeof path !== "string") { throw new InvalidArgumentError("path must be a string"); @@ -1886,6 +1924,7 @@ var require_request = __commonJS({ } this.headersTimeout = headersTimeout; this.bodyTimeout = bodyTimeout; + this.throwOnError = throwOnError === true; this.method = method; if (body == null) { this.body = null; @@ -1907,7 +1946,7 @@ var require_request = __commonJS({ this.completed = false; this.aborted = false; this.upgrade = upgrade || null; - this.path = path; + this.path = query ? util.buildURL(path, query) : path; this.origin = origin; this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent; this.blocking = blocking == null ? false : blocking; @@ -4462,7 +4501,7 @@ var require_response = __commonJS({ var { extractBody, cloneBody, mixinBody } = require_body(); var util = require_util(); var { kEnumerableProperty } = util; - var { responseURL, isValidReasonPhrase, toUSVString, isCancelled, isAborted } = require_util2(); + var { responseURL, isValidReasonPhrase, toUSVString, isCancelled, isAborted, serializeJavascriptValueToJSONString } = require_util2(); var { redirectStatus, nullBodyStatus, @@ -4483,6 +4522,29 @@ var require_response = __commonJS({ responseObject[kHeaders][kRealm] = relevantRealm; return responseObject; } + static json(data, init = {}) { + if (arguments.length === 0) { + throw new TypeError("Failed to execute 'json' on 'Response': 1 argument required, but 0 present."); + } + if (init === null || typeof init !== "object") { + throw new TypeError(`Failed to execute 'json' on 'Response': init must be a RequestInit, found ${typeof init}.`); + } + init = { + status: 200, + statusText: "", + headers: new HeadersList(), + ...init + }; + const bytes = new TextEncoder("utf-8").encode(serializeJavascriptValueToJSONString(data)); + const body = extractBody(bytes); + const relevantRealm = { settingsObject: {} }; + const responseObject = new Response(); + responseObject[kRealm] = relevantRealm; + responseObject[kHeaders][kGuard] = "response"; + responseObject[kHeaders][kRealm] = relevantRealm; + initializeResponse(responseObject, init, { body: body[0], type: "application/json" }); + return responseObject; + } static redirect(...args) { const relevantRealm = { settingsObject: {} }; if (args.length < 1) { @@ -4517,48 +4579,19 @@ var require_response = __commonJS({ const body = args.length >= 1 ? args[0] : null; const init = args.length >= 2 ? args[1] ?? {} : {}; this[kRealm] = { settingsObject: {} }; - if ("status" in init && init.status !== void 0) { - if (!Number.isFinite(init.status)) { - throw new TypeError(); - } - if (init.status < 200 || init.status > 599) { - throw new RangeError(`Failed to construct 'Response': The status provided (${init.status}) is outside the range [200, 599].`); - } - } - if ("statusText" in init && init.statusText !== void 0) { - if (!isValidReasonPhrase(String(init.statusText))) { - throw new TypeError("Invalid statusText"); - } - } this[kState] = makeResponse({}); this[kHeaders] = new Headers(); this[kHeaders][kGuard] = "response"; this[kHeaders][kHeadersList] = this[kState].headersList; this[kHeaders][kRealm] = this[kRealm]; - if ("status" in init && init.status !== void 0) { - this[kState].status = init.status; - } - if ("statusText" in init && init.statusText !== void 0) { - this[kState].statusText = String(init.statusText); - } - if ("headers" in init) { - fill(this[kState].headersList, init.headers); - } + let bodyWithType = null; if (body != null) { - if (nullBodyStatus.includes(init.status)) { - throw new TypeError("Response with null body status cannot have body"); - } - const [extractedBody, contentType] = extractBody(body); - this[kState].body = extractedBody; - if (contentType && !this.headers.has("content-type")) { - this.headers.append("content-type", contentType); - } + const [extractedBody, type] = extractBody(body); + bodyWithType = { body: extractedBody, type }; } + initializeResponse(this, init, bodyWithType); } get [Symbol.toStringTag]() { - if (!(this instanceof Response)) { - throw new TypeError("Illegal invocation"); - } return this.constructor.name; } get type() { @@ -4746,6 +4779,34 @@ var require_response = __commonJS({ assert(isCancelled(fetchParams)); return isAborted(fetchParams) ? makeNetworkError(new AbortError()) : makeNetworkError(fetchParams.controller.terminated.reason); } + function initializeResponse(response, init, body) { + if (init.status != null && (init.status < 200 || init.status > 599)) { + throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.'); + } + if ("statusText" in init && init.statusText != null) { + if (!isValidReasonPhrase(String(init.statusText))) { + throw new TypeError("Invalid statusText"); + } + } + if ("status" in init && init.status != null) { + response[kState].status = init.status; + } + if ("statusText" in init && init.statusText != null) { + response[kState].statusText = init.statusText; + } + if ("headers" in init && init.headers != null) { + fill(response[kState].headersList, init.headers); + } + if (body) { + if (nullBodyStatus.includes(response.status)) { + throw new TypeError(); + } + response[kState].body = body.body; + if (body.type != null && !response[kState].headersList.has("Content-Type")) { + response[kState].headersList.append("content-type", body.type); + } + } + } module2.exports = { makeNetworkError, makeResponse, @@ -5020,9 +5081,6 @@ var require_request2 = __commonJS({ this[kState].body = finalBody; } get [Symbol.toStringTag]() { - if (!(this instanceof Request)) { - throw new TypeError("Illegal invocation"); - } return this.constructor.name; } get method() { @@ -5414,34 +5472,6 @@ var require_dataURL = __commonJS({ } }); -// lib/mock/mock-symbols.js -var require_mock_symbols = __commonJS({ - "lib/mock/mock-symbols.js"(exports2, module2) { - "use strict"; - module2.exports = { - kAgent: Symbol("agent"), - kOptions: Symbol("options"), - kFactory: Symbol("factory"), - kDispatches: Symbol("dispatches"), - kDispatchKey: Symbol("dispatch key"), - kDefaultHeaders: Symbol("default headers"), - kDefaultTrailers: Symbol("default trailers"), - kContentLength: Symbol("content length"), - kMockAgent: Symbol("mock agent"), - kMockAgentSet: Symbol("mock agent set"), - kMockAgentGet: Symbol("mock agent get"), - kMockDispatch: Symbol("mock dispatch"), - kClose: Symbol("close"), - kOriginalClose: Symbol("original agent close"), - kOrigin: Symbol("origin"), - kIsMockActive: Symbol("is mock active"), - kNetConnect: Symbol("net connect"), - kGetNetConnect: Symbol("get net connect"), - kConnected: Symbol("connected") - }; - } -}); - // lib/fetch/index.js var require_fetch = __commonJS({ "lib/fetch/index.js"(exports2, module2) { @@ -5475,7 +5505,6 @@ var require_fetch = __commonJS({ coarsenedSharedCurrentTime, createDeferredPromise, isBlobLike, - CORBCheck, sameOrigin, isCancelled, isAborted @@ -5496,7 +5525,6 @@ var require_fetch = __commonJS({ var { Readable, pipeline } = require("stream"); var { isErrored, isReadable } = require_util(); var { dataURLProcessor } = require_dataURL(); - var { kIsMockActive } = require_mock_symbols(); var { TransformStream } = require("stream/web"); var resolveObjectURL; var ReadableStream; @@ -5731,11 +5759,7 @@ var require_fetch = __commonJS({ return makeNetworkError('redirect mode cannot be "follow" for "no-cors" request'); } request.responseTainting = "opaque"; - const noCorsResponse = await schemeFetch(fetchParams); - if (noCorsResponse.status === 0 || CORBCheck(request, noCorsResponse) === "allowed") { - return noCorsResponse; - } - return makeResponse({ status: noCorsResponse.status }); + return await schemeFetch(fetchParams); } if (!/^https?:/.test(requestCurrentURL(request).protocol)) { return makeNetworkError("URL scheme must be a HTTP(S) scheme"); @@ -6275,7 +6299,7 @@ var require_fetch = __commonJS({ path: url.pathname + url.search, origin: url.origin, method: request.method, - body: fetchParams.controller.dispatcher[kIsMockActive] ? request.body && request.body.source : body, + body: fetchParams.controller.dispatcher.isMockActive ? request.body && request.body.source : body, headers: [...request.headersList].flat(), maxRedirections: 0, bodyTimeout: 3e5, |