mirror of
https://github.com/nodejs/node.git
synced 2025-12-28 16:07:39 +00:00
deps: update undici to 5.28.0
PR-URL: https://github.com/nodejs/node/pull/50915 Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
parent
59f57d289d
commit
2fa9503fee
2
deps/undici/src/docs/api/Client.md
vendored
2
deps/undici/src/docs/api/Client.md
vendored
@ -33,7 +33,7 @@ Returns: `Client`
|
||||
* **autoSelectFamily**: `boolean` (optional) - Default: depends on local Node version, on Node 18.13.0 and above is `false`. Enables a family autodetection algorithm that loosely implements section 5 of [RFC 8305](https://tools.ietf.org/html/rfc8305#section-5). See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details. This option is ignored if not supported by the current Node version.
|
||||
* **autoSelectFamilyAttemptTimeout**: `number` - Default: depends on local Node version, on Node 18.13.0 and above is `250`. The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. See [here](https://nodejs.org/api/net.html#socketconnectoptions-connectlistener) for more details.
|
||||
* **allowH2**: `boolean` - Default: `false`. Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
|
||||
* **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overriden by a SETTINGS remote frame.
|
||||
* **maxConcurrentStreams**: `number` - Default: `100`. Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.
|
||||
|
||||
#### Parameter: `ConnectOptions`
|
||||
|
||||
|
||||
42
deps/undici/src/docs/api/MockPool.md
vendored
42
deps/undici/src/docs/api/MockPool.md
vendored
@ -35,8 +35,7 @@ const mockPool = mockAgent.get('http://localhost:3000')
|
||||
|
||||
### `MockPool.intercept(options)`
|
||||
|
||||
This method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance, but each intercept is only used once.
|
||||
For example if you expect to make 2 requests inside a test, you need to call `intercept()` twice. Assuming you use `disableNetConnect()` you will get `MockNotMatchedError` on the second request when you only call `intercept()` once.
|
||||
This method defines the interception rules for matching against requests for a MockPool or MockPool. We can intercept multiple times on a single instance, but each intercept is only used once. For example if you expect to make 2 requests inside a test, you need to call `intercept()` twice. Assuming you use `disableNetConnect()` you will get `MockNotMatchedError` on the second request when you only call `intercept()` once.
|
||||
|
||||
When defining interception rules, all the rules must pass for a request to be intercepted. If a request is not intercepted, a real request will be attempted.
|
||||
|
||||
@ -54,11 +53,11 @@ Returns: `MockInterceptor` corresponding to the input options.
|
||||
|
||||
### Parameter: `MockPoolInterceptOptions`
|
||||
|
||||
* **path** `string | RegExp | (path: string) => boolean` - a matcher for the HTTP request path.
|
||||
* **path** `string | RegExp | (path: string) => boolean` - a matcher for the HTTP request path. When a `RegExp` or callback is used, it will match against the request path including all query parameters in alphabetical order. When a `string` is provided, the query parameters can be conveniently specified through the `MockPoolInterceptOptions.query` setting.
|
||||
* **method** `string | RegExp | (method: string) => boolean` - (optional) - a matcher for the HTTP request method. Defaults to `GET`.
|
||||
* **body** `string | RegExp | (body: string) => boolean` - (optional) - a matcher for the HTTP request body.
|
||||
* **headers** `Record<string, string | RegExp | (body: string) => boolean`> - (optional) - a matcher for the HTTP request headers. To be intercepted, a request must match all defined headers. Extra headers not defined here may (or may not) be included in the request and do not affect the interception in any way.
|
||||
* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params.
|
||||
* **query** `Record<string, any> | null` - (optional) - a matcher for the HTTP request query string params. Only applies when a `string` was provided for `MockPoolInterceptOptions.path`.
|
||||
|
||||
### Return: `MockInterceptor`
|
||||
|
||||
@ -458,6 +457,41 @@ const result3 = await request('http://localhost:3000/foo')
|
||||
// Will not match and make attempt a real request
|
||||
```
|
||||
|
||||
#### Example - Mocked request with path callback
|
||||
|
||||
```js
|
||||
import { MockAgent, setGlobalDispatcher, request } from 'undici'
|
||||
import querystring from 'querystring'
|
||||
|
||||
const mockAgent = new MockAgent()
|
||||
setGlobalDispatcher(mockAgent)
|
||||
|
||||
const mockPool = mockAgent.get('http://localhost:3000')
|
||||
|
||||
const matchPath = requestPath => {
|
||||
const [pathname, search] = requestPath.split('?')
|
||||
const requestQuery = querystring.parse(search)
|
||||
|
||||
if (!pathname.startsWith('/foo')) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!Object.keys(requestQuery).includes('foo') || requestQuery.foo !== 'bar') {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
mockPool.intercept({
|
||||
path: matchPath,
|
||||
method: 'GET'
|
||||
}).reply(200, 'foo')
|
||||
|
||||
const result = await request('http://localhost:3000/foo?foo=bar')
|
||||
// Will match and return mocked data
|
||||
```
|
||||
|
||||
### `MockPool.close()`
|
||||
|
||||
Closes the mock pool and de-registers from associated MockAgent.
|
||||
|
||||
108
deps/undici/src/docs/api/RetryHandler.md
vendored
Normal file
108
deps/undici/src/docs/api/RetryHandler.md
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
# Class: RetryHandler
|
||||
|
||||
Extends: `undici.DispatcherHandlers`
|
||||
|
||||
A handler class that implements the retry logic for a request.
|
||||
|
||||
## `new RetryHandler(dispatchOptions, retryHandlers, [retryOptions])`
|
||||
|
||||
Arguments:
|
||||
|
||||
- **options** `Dispatch.DispatchOptions & RetryOptions` (required) - It is an intersection of `Dispatcher.DispatchOptions` and `RetryOptions`.
|
||||
- **retryHandlers** `RetryHandlers` (required) - Object containing the `dispatch` to be used on every retry, and `handler` for handling the `dispatch` lifecycle.
|
||||
|
||||
Returns: `retryHandler`
|
||||
|
||||
### Parameter: `Dispatch.DispatchOptions & RetryOptions`
|
||||
|
||||
Extends: [`Dispatch.DispatchOptions`](Dispatcher.md#parameter-dispatchoptions).
|
||||
|
||||
#### `RetryOptions`
|
||||
|
||||
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
|
||||
- **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
|
||||
- **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
|
||||
- **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
|
||||
- **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
|
||||
- **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
|
||||
-
|
||||
- **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE']`
|
||||
- **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
|
||||
- **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN','ENETUNREACH', 'EHOSTDOWN',
|
||||
|
||||
**`RetryContext`**
|
||||
|
||||
- `state`: `RetryState` - Current retry state. It can be mutated.
|
||||
- `opts`: `Dispatch.DispatchOptions & RetryOptions` - Options passed to the retry handler.
|
||||
|
||||
### Parameter `RetryHandlers`
|
||||
|
||||
- **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandlers) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every retry.
|
||||
- **handler** Extends [`Dispatch.DispatchHandlers`](Dispatcher.md#dispatcherdispatchoptions-handler) (required) - Handler function to be called after the request is successful or the retries are exhausted.
|
||||
|
||||
Examples:
|
||||
|
||||
```js
|
||||
const client = new Client(`http://localhost:${server.address().port}`);
|
||||
const chunks = [];
|
||||
const handler = new RetryHandler(
|
||||
{
|
||||
...dispatchOptions,
|
||||
retryOptions: {
|
||||
// custom retry function
|
||||
retry: function (err, state, callback) {
|
||||
counter++;
|
||||
|
||||
if (err.code && err.code === "UND_ERR_DESTROYED") {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.statusCode === 206) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => callback(null), 1000);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
dispatch: (...args) => {
|
||||
return client.dispatch(...args);
|
||||
},
|
||||
handler: {
|
||||
onConnect() {},
|
||||
onBodySent() {},
|
||||
onHeaders(status, _rawHeaders, resume, _statusMessage) {
|
||||
// do something with headers
|
||||
},
|
||||
onData(chunk) {
|
||||
chunks.push(chunk);
|
||||
return true;
|
||||
},
|
||||
onComplete() {},
|
||||
onError() {
|
||||
// handle error properly
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
#### Example - Basic RetryHandler with defaults
|
||||
|
||||
```js
|
||||
const client = new Client(`http://localhost:${server.address().port}`);
|
||||
const handler = new RetryHandler(dispatchOptions, {
|
||||
dispatch: client.dispatch.bind(client),
|
||||
handler: {
|
||||
onConnect() {},
|
||||
onBodySent() {},
|
||||
onHeaders(status, _rawHeaders, resume, _statusMessage) {},
|
||||
onData(chunk) {},
|
||||
onComplete() {},
|
||||
onError(err) {},
|
||||
},
|
||||
});
|
||||
```
|
||||
2
deps/undici/src/index.js
vendored
2
deps/undici/src/index.js
vendored
@ -15,6 +15,7 @@ const MockAgent = require('./lib/mock/mock-agent')
|
||||
const MockPool = require('./lib/mock/mock-pool')
|
||||
const mockErrors = require('./lib/mock/mock-errors')
|
||||
const ProxyAgent = require('./lib/proxy-agent')
|
||||
const RetryHandler = require('./lib/handler/RetryHandler')
|
||||
const { getGlobalDispatcher, setGlobalDispatcher } = require('./lib/global')
|
||||
const DecoratorHandler = require('./lib/handler/DecoratorHandler')
|
||||
const RedirectHandler = require('./lib/handler/RedirectHandler')
|
||||
@ -36,6 +37,7 @@ module.exports.Pool = Pool
|
||||
module.exports.BalancedPool = BalancedPool
|
||||
module.exports.Agent = Agent
|
||||
module.exports.ProxyAgent = ProxyAgent
|
||||
module.exports.RetryHandler = RetryHandler
|
||||
|
||||
module.exports.DecoratorHandler = DecoratorHandler
|
||||
module.exports.RedirectHandler = RedirectHandler
|
||||
|
||||
65
deps/undici/src/lib/api/readable.js
vendored
65
deps/undici/src/lib/api/readable.js
vendored
@ -16,6 +16,8 @@ const kBody = Symbol('kBody')
|
||||
const kAbort = Symbol('abort')
|
||||
const kContentType = Symbol('kContentType')
|
||||
|
||||
const noop = () => {}
|
||||
|
||||
module.exports = class BodyReadable extends Readable {
|
||||
constructor ({
|
||||
resume,
|
||||
@ -149,37 +151,50 @@ module.exports = class BodyReadable extends Readable {
|
||||
return this[kBody]
|
||||
}
|
||||
|
||||
async dump (opts) {
|
||||
dump (opts) {
|
||||
let limit = opts && Number.isFinite(opts.limit) ? opts.limit : 262144
|
||||
const signal = opts && opts.signal
|
||||
const abortFn = () => {
|
||||
this.destroy()
|
||||
}
|
||||
let signalListenerCleanup
|
||||
|
||||
if (signal) {
|
||||
if (typeof signal !== 'object' || !('aborted' in signal)) {
|
||||
throw new InvalidArgumentError('signal must be an AbortSignal')
|
||||
}
|
||||
util.throwIfAborted(signal)
|
||||
signalListenerCleanup = util.addAbortListener(signal, abortFn)
|
||||
}
|
||||
try {
|
||||
for await (const chunk of this) {
|
||||
util.throwIfAborted(signal)
|
||||
limit -= Buffer.byteLength(chunk)
|
||||
if (limit < 0) {
|
||||
return
|
||||
try {
|
||||
if (typeof signal !== 'object' || !('aborted' in signal)) {
|
||||
throw new InvalidArgumentError('signal must be an AbortSignal')
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
util.throwIfAborted(signal)
|
||||
} finally {
|
||||
if (typeof signalListenerCleanup === 'function') {
|
||||
signalListenerCleanup()
|
||||
} else if (signalListenerCleanup) {
|
||||
signalListenerCleanup[Symbol.dispose]()
|
||||
util.throwIfAborted(signal)
|
||||
} catch (err) {
|
||||
return Promise.reject(err)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.closed) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const signalListenerCleanup = signal
|
||||
? util.addAbortListener(signal, () => {
|
||||
this.destroy()
|
||||
})
|
||||
: noop
|
||||
|
||||
this
|
||||
.on('close', function () {
|
||||
signalListenerCleanup()
|
||||
if (signal?.aborted) {
|
||||
reject(signal.reason || Object.assign(new Error('The operation was aborted'), { name: 'AbortError' }))
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
.on('error', noop)
|
||||
.on('data', function (chunk) {
|
||||
limit -= chunk.length
|
||||
if (limit <= 0) {
|
||||
this.destroy()
|
||||
}
|
||||
})
|
||||
.resume()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
deps/undici/src/lib/client.js
vendored
17
deps/undici/src/lib/client.js
vendored
@ -1183,7 +1183,7 @@ async function connect (client) {
|
||||
const idx = hostname.indexOf(']')
|
||||
|
||||
assert(idx !== -1)
|
||||
const ip = hostname.substr(1, idx - 1)
|
||||
const ip = hostname.substring(1, idx)
|
||||
|
||||
assert(net.isIP(ip))
|
||||
hostname = ip
|
||||
@ -1682,6 +1682,7 @@ function writeH2 (client, session, request) {
|
||||
return false
|
||||
}
|
||||
|
||||
/** @type {import('node:http2').ClientHttp2Stream} */
|
||||
let stream
|
||||
const h2State = client[kHTTP2SessionState]
|
||||
|
||||
@ -1777,14 +1778,10 @@ function writeH2 (client, session, request) {
|
||||
const shouldEndStream = method === 'GET' || method === 'HEAD'
|
||||
if (expectContinue) {
|
||||
headers[HTTP2_HEADER_EXPECT] = '100-continue'
|
||||
/**
|
||||
* @type {import('node:http2').ClientHttp2Stream}
|
||||
*/
|
||||
stream = session.request(headers, { endStream: shouldEndStream, signal })
|
||||
|
||||
stream.once('continue', writeBodyH2)
|
||||
} else {
|
||||
/** @type {import('node:http2').ClientHttp2Stream} */
|
||||
stream = session.request(headers, {
|
||||
endStream: shouldEndStream,
|
||||
signal
|
||||
@ -1796,7 +1793,9 @@ function writeH2 (client, session, request) {
|
||||
++h2State.openStreams
|
||||
|
||||
stream.once('response', headers => {
|
||||
if (request.onHeaders(Number(headers[HTTP2_HEADER_STATUS]), headers, stream.resume.bind(stream), '') === false) {
|
||||
const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers
|
||||
|
||||
if (request.onHeaders(Number(statusCode), realHeaders, stream.resume.bind(stream), '') === false) {
|
||||
stream.pause()
|
||||
}
|
||||
})
|
||||
@ -1972,7 +1971,11 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
|
||||
}
|
||||
}
|
||||
const onAbort = function () {
|
||||
onFinished(new RequestAbortedError())
|
||||
if (finished) {
|
||||
return
|
||||
}
|
||||
const err = new RequestAbortedError()
|
||||
queueMicrotask(() => onFinished(err))
|
||||
}
|
||||
const onFinished = function (err) {
|
||||
if (finished) {
|
||||
|
||||
16
deps/undici/src/lib/core/errors.js
vendored
16
deps/undici/src/lib/core/errors.js
vendored
@ -193,6 +193,19 @@ class ResponseExceededMaxSizeError extends UndiciError {
|
||||
}
|
||||
}
|
||||
|
||||
class RequestRetryError extends UndiciError {
|
||||
constructor (message, code, { headers, data }) {
|
||||
super(message)
|
||||
Error.captureStackTrace(this, RequestRetryError)
|
||||
this.name = 'RequestRetryError'
|
||||
this.message = message || 'Request retry error'
|
||||
this.code = 'UND_ERR_REQ_RETRY'
|
||||
this.statusCode = code
|
||||
this.data = data
|
||||
this.headers = headers
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
HTTPParserError,
|
||||
UndiciError,
|
||||
@ -212,5 +225,6 @@ module.exports = {
|
||||
NotSupportedError,
|
||||
ResponseContentLengthMismatchError,
|
||||
BalancedPoolMissingUpstreamError,
|
||||
ResponseExceededMaxSizeError
|
||||
ResponseExceededMaxSizeError,
|
||||
RequestRetryError
|
||||
}
|
||||
|
||||
12
deps/undici/src/lib/core/request.js
vendored
12
deps/undici/src/lib/core/request.js
vendored
@ -229,11 +229,7 @@ class Request {
|
||||
|
||||
onBodySent (chunk) {
|
||||
if (this[kHandler].onBodySent) {
|
||||
try {
|
||||
this[kHandler].onBodySent(chunk)
|
||||
} catch (err) {
|
||||
this.onError(err)
|
||||
}
|
||||
return this[kHandler].onBodySent(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,11 +239,7 @@ class Request {
|
||||
}
|
||||
|
||||
if (this[kHandler].onRequestSent) {
|
||||
try {
|
||||
this[kHandler].onRequestSent()
|
||||
} catch (err) {
|
||||
this.onError(err)
|
||||
}
|
||||
return this[kHandler].onRequestSent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
deps/undici/src/lib/core/symbols.js
vendored
3
deps/undici/src/lib/core/symbols.js
vendored
@ -57,5 +57,6 @@ module.exports = {
|
||||
kHTTP2BuildRequest: Symbol('http2 build request'),
|
||||
kHTTP1BuildRequest: Symbol('http1 build request'),
|
||||
kHTTP2CopyHeaders: Symbol('http2 copy headers'),
|
||||
kHTTPConnVersion: Symbol('http connection version')
|
||||
kHTTPConnVersion: Symbol('http connection version'),
|
||||
kRetryHandlerDefaultRetry: Symbol('retry agent default retry')
|
||||
}
|
||||
|
||||
34
deps/undici/src/lib/core/util.js
vendored
34
deps/undici/src/lib/core/util.js
vendored
@ -125,13 +125,13 @@ function getHostname (host) {
|
||||
const idx = host.indexOf(']')
|
||||
|
||||
assert(idx !== -1)
|
||||
return host.substr(1, idx - 1)
|
||||
return host.substring(1, idx)
|
||||
}
|
||||
|
||||
const idx = host.indexOf(':')
|
||||
if (idx === -1) return host
|
||||
|
||||
return host.substr(0, idx)
|
||||
return host.substring(0, idx)
|
||||
}
|
||||
|
||||
// IP addresses are not valid server names per RFC6066
|
||||
@ -228,7 +228,7 @@ function parseHeaders (headers, obj = {}) {
|
||||
|
||||
if (!val) {
|
||||
if (Array.isArray(headers[i + 1])) {
|
||||
obj[key] = headers[i + 1]
|
||||
obj[key] = headers[i + 1].map(x => x.toString('utf8'))
|
||||
} else {
|
||||
obj[key] = headers[i + 1].toString('utf8')
|
||||
}
|
||||
@ -431,16 +431,7 @@ function throwIfAborted (signal) {
|
||||
}
|
||||
}
|
||||
|
||||
let events
|
||||
function addAbortListener (signal, listener) {
|
||||
if (typeof Symbol.dispose === 'symbol') {
|
||||
if (!events) {
|
||||
events = require('events')
|
||||
}
|
||||
if (typeof events.addAbortListener === 'function' && 'aborted' in signal) {
|
||||
return events.addAbortListener(signal, listener)
|
||||
}
|
||||
}
|
||||
if ('addEventListener' in signal) {
|
||||
signal.addEventListener('abort', listener, { once: true })
|
||||
return () => signal.removeEventListener('abort', listener)
|
||||
@ -464,6 +455,21 @@ function toUSVString (val) {
|
||||
return `${val}`
|
||||
}
|
||||
|
||||
// Parsed accordingly to RFC 9110
|
||||
// https://www.rfc-editor.org/rfc/rfc9110#field.content-range
|
||||
function parseRangeHeader (range) {
|
||||
if (range == null || range === '') return { start: 0, end: null, size: null }
|
||||
|
||||
const m = range ? range.match(/^bytes (\d+)-(\d+)\/(\d+)?$/) : null
|
||||
return m
|
||||
? {
|
||||
start: parseInt(m[1]),
|
||||
end: m[2] ? parseInt(m[2]) : null,
|
||||
size: m[3] ? parseInt(m[3]) : null
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
const kEnumerableProperty = Object.create(null)
|
||||
kEnumerableProperty.enumerable = true
|
||||
|
||||
@ -497,7 +503,9 @@ module.exports = {
|
||||
buildURL,
|
||||
throwIfAborted,
|
||||
addAbortListener,
|
||||
parseRangeHeader,
|
||||
nodeMajor,
|
||||
nodeMinor,
|
||||
nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13)
|
||||
nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13),
|
||||
safeHTTPMethods: ['GET', 'HEAD', 'OPTIONS', 'TRACE']
|
||||
}
|
||||
|
||||
152
deps/undici/src/lib/fetch/headers.js
vendored
152
deps/undici/src/lib/fetch/headers.js
vendored
@ -16,6 +16,13 @@ const assert = require('assert')
|
||||
const kHeadersMap = Symbol('headers map')
|
||||
const kHeadersSortedMap = Symbol('headers map sorted')
|
||||
|
||||
/**
|
||||
* @param {number} code
|
||||
*/
|
||||
function isHTTPWhiteSpaceCharCode (code) {
|
||||
return code === 0x00a || code === 0x00d || code === 0x009 || code === 0x020
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
|
||||
* @param {string} potentialValue
|
||||
@ -24,12 +31,12 @@ function headerValueNormalize (potentialValue) {
|
||||
// To normalize a byte sequence potentialValue, remove
|
||||
// any leading and trailing HTTP whitespace bytes from
|
||||
// potentialValue.
|
||||
let i = 0; let j = potentialValue.length
|
||||
|
||||
// Trimming the end with `.replace()` and a RegExp is typically subject to
|
||||
// ReDoS. This is safer and faster.
|
||||
let i = potentialValue.length
|
||||
while (/[\r\n\t ]/.test(potentialValue.charAt(--i)));
|
||||
return potentialValue.slice(0, i + 1).replace(/^[\r\n\t ]+/, '')
|
||||
while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(j - 1))) --j
|
||||
while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i))) ++i
|
||||
|
||||
return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j)
|
||||
}
|
||||
|
||||
function fill (headers, object) {
|
||||
@ -38,7 +45,8 @@ function fill (headers, object) {
|
||||
// 1. If object is a sequence, then for each header in object:
|
||||
// Note: webidl conversion to array has already been done.
|
||||
if (Array.isArray(object)) {
|
||||
for (const header of object) {
|
||||
for (let i = 0; i < object.length; ++i) {
|
||||
const header = object[i]
|
||||
// 1. If header does not contain exactly two items, then throw a TypeError.
|
||||
if (header.length !== 2) {
|
||||
throw webidl.errors.exception({
|
||||
@ -48,15 +56,16 @@ function fill (headers, object) {
|
||||
}
|
||||
|
||||
// 2. Append (header’s first item, header’s second item) to headers.
|
||||
headers.append(header[0], header[1])
|
||||
appendHeader(headers, header[0], header[1])
|
||||
}
|
||||
} else if (typeof object === 'object' && object !== null) {
|
||||
// Note: null should throw
|
||||
|
||||
// 2. Otherwise, object is a record, then for each key → value in object,
|
||||
// append (key, value) to headers
|
||||
for (const [key, value] of Object.entries(object)) {
|
||||
headers.append(key, value)
|
||||
const keys = Object.keys(object)
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
appendHeader(headers, keys[i], object[keys[i]])
|
||||
}
|
||||
} else {
|
||||
throw webidl.errors.conversionFailed({
|
||||
@ -67,6 +76,50 @@ function fill (headers, object) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://fetch.spec.whatwg.org/#concept-headers-append
|
||||
*/
|
||||
function appendHeader (headers, name, value) {
|
||||
// 1. Normalize value.
|
||||
value = headerValueNormalize(value)
|
||||
|
||||
// 2. If name is not a header name or value is not a
|
||||
// header value, then throw a TypeError.
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: 'Headers.append',
|
||||
value: name,
|
||||
type: 'header name'
|
||||
})
|
||||
} else if (!isValidHeaderValue(value)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: 'Headers.append',
|
||||
value,
|
||||
type: 'header value'
|
||||
})
|
||||
}
|
||||
|
||||
// 3. If headers’s guard is "immutable", then throw a TypeError.
|
||||
// 4. Otherwise, if headers’s guard is "request" and name is a
|
||||
// forbidden header name, return.
|
||||
// Note: undici does not implement forbidden header names
|
||||
if (headers[kGuard] === 'immutable') {
|
||||
throw new TypeError('immutable')
|
||||
} else if (headers[kGuard] === 'request-no-cors') {
|
||||
// 5. Otherwise, if headers’s guard is "request-no-cors":
|
||||
// TODO
|
||||
}
|
||||
|
||||
// 6. Otherwise, if headers’s guard is "response" and name is a
|
||||
// forbidden response-header name, return.
|
||||
|
||||
// 7. Append (name, value) to headers’s header list.
|
||||
return headers[kHeadersList].append(name, value)
|
||||
|
||||
// 8. If headers’s guard is "request-no-cors", then remove
|
||||
// privileged no-CORS request headers from headers
|
||||
}
|
||||
|
||||
class HeadersList {
|
||||
/** @type {[string, string][]|null} */
|
||||
cookies = null
|
||||
@ -75,7 +128,7 @@ class HeadersList {
|
||||
if (init instanceof HeadersList) {
|
||||
this[kHeadersMap] = new Map(init[kHeadersMap])
|
||||
this[kHeadersSortedMap] = init[kHeadersSortedMap]
|
||||
this.cookies = init.cookies
|
||||
this.cookies = init.cookies === null ? null : [...init.cookies]
|
||||
} else {
|
||||
this[kHeadersMap] = new Map(init)
|
||||
this[kHeadersSortedMap] = null
|
||||
@ -137,7 +190,7 @@ class HeadersList {
|
||||
// the first such header to value and remove the
|
||||
// others.
|
||||
// 2. Otherwise, append header (name, value) to list.
|
||||
return this[kHeadersMap].set(lowercaseName, { name, value })
|
||||
this[kHeadersMap].set(lowercaseName, { name, value })
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-header-list-delete
|
||||
@ -150,20 +203,18 @@ class HeadersList {
|
||||
this.cookies = null
|
||||
}
|
||||
|
||||
return this[kHeadersMap].delete(name)
|
||||
this[kHeadersMap].delete(name)
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#concept-header-list-get
|
||||
get (name) {
|
||||
// 1. If list does not contain name, then return null.
|
||||
if (!this.contains(name)) {
|
||||
return null
|
||||
}
|
||||
const value = this[kHeadersMap].get(name.toLowerCase())
|
||||
|
||||
// 1. If list does not contain name, then return null.
|
||||
// 2. Return the values of all headers in list whose name
|
||||
// is a byte-case-insensitive match for name,
|
||||
// separated from each other by 0x2C 0x20, in order.
|
||||
return this[kHeadersMap].get(name.toLowerCase())?.value ?? null
|
||||
return value === undefined ? null : value.value
|
||||
}
|
||||
|
||||
* [Symbol.iterator] () {
|
||||
@ -212,43 +263,7 @@ class Headers {
|
||||
name = webidl.converters.ByteString(name)
|
||||
value = webidl.converters.ByteString(value)
|
||||
|
||||
// 1. Normalize value.
|
||||
value = headerValueNormalize(value)
|
||||
|
||||
// 2. If name is not a header name or value is not a
|
||||
// header value, then throw a TypeError.
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: 'Headers.append',
|
||||
value: name,
|
||||
type: 'header name'
|
||||
})
|
||||
} else if (!isValidHeaderValue(value)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: 'Headers.append',
|
||||
value,
|
||||
type: 'header value'
|
||||
})
|
||||
}
|
||||
|
||||
// 3. If headers’s guard is "immutable", then throw a TypeError.
|
||||
// 4. Otherwise, if headers’s guard is "request" and name is a
|
||||
// forbidden header name, return.
|
||||
// Note: undici does not implement forbidden header names
|
||||
if (this[kGuard] === 'immutable') {
|
||||
throw new TypeError('immutable')
|
||||
} else if (this[kGuard] === 'request-no-cors') {
|
||||
// 5. Otherwise, if headers’s guard is "request-no-cors":
|
||||
// TODO
|
||||
}
|
||||
|
||||
// 6. Otherwise, if headers’s guard is "response" and name is a
|
||||
// forbidden response-header name, return.
|
||||
|
||||
// 7. Append (name, value) to headers’s header list.
|
||||
// 8. If headers’s guard is "request-no-cors", then remove
|
||||
// privileged no-CORS request headers from headers
|
||||
return this[kHeadersList].append(name, value)
|
||||
return appendHeader(this, name, value)
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-headers-delete
|
||||
@ -293,7 +308,7 @@ class Headers {
|
||||
// 7. Delete name from this’s header list.
|
||||
// 8. If this’s guard is "request-no-cors", then remove
|
||||
// privileged no-CORS request headers from this.
|
||||
return this[kHeadersList].delete(name)
|
||||
this[kHeadersList].delete(name)
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-headers-get
|
||||
@ -386,7 +401,7 @@ class Headers {
|
||||
// 7. Set (name, value) in this’s header list.
|
||||
// 8. If this’s guard is "request-no-cors", then remove
|
||||
// privileged no-CORS request headers from this
|
||||
return this[kHeadersList].set(name, value)
|
||||
this[kHeadersList].set(name, value)
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
|
||||
@ -422,7 +437,8 @@ class Headers {
|
||||
const cookies = this[kHeadersList].cookies
|
||||
|
||||
// 3. For each name of names:
|
||||
for (const [name, value] of names) {
|
||||
for (let i = 0; i < names.length; ++i) {
|
||||
const [name, value] = names[i]
|
||||
// 1. If name is `set-cookie`, then:
|
||||
if (name === 'set-cookie') {
|
||||
// 1. Let values be a list of all values of headers in list whose name
|
||||
@ -430,8 +446,8 @@ class Headers {
|
||||
|
||||
// 2. For each value of values:
|
||||
// 1. Append (name, value) to headers.
|
||||
for (const value of cookies) {
|
||||
headers.push([name, value])
|
||||
for (let j = 0; j < cookies.length; ++j) {
|
||||
headers.push([name, cookies[j]])
|
||||
}
|
||||
} else {
|
||||
// 2. Otherwise:
|
||||
@ -455,6 +471,12 @@ class Headers {
|
||||
keys () {
|
||||
webidl.brandCheck(this, Headers)
|
||||
|
||||
if (this[kGuard] === 'immutable') {
|
||||
const value = this[kHeadersSortedMap]
|
||||
return makeIterator(() => value, 'Headers',
|
||||
'key')
|
||||
}
|
||||
|
||||
return makeIterator(
|
||||
() => [...this[kHeadersSortedMap].values()],
|
||||
'Headers',
|
||||
@ -465,6 +487,12 @@ class Headers {
|
||||
values () {
|
||||
webidl.brandCheck(this, Headers)
|
||||
|
||||
if (this[kGuard] === 'immutable') {
|
||||
const value = this[kHeadersSortedMap]
|
||||
return makeIterator(() => value, 'Headers',
|
||||
'value')
|
||||
}
|
||||
|
||||
return makeIterator(
|
||||
() => [...this[kHeadersSortedMap].values()],
|
||||
'Headers',
|
||||
@ -475,6 +503,12 @@ class Headers {
|
||||
entries () {
|
||||
webidl.brandCheck(this, Headers)
|
||||
|
||||
if (this[kGuard] === 'immutable') {
|
||||
const value = this[kHeadersSortedMap]
|
||||
return makeIterator(() => value, 'Headers',
|
||||
'key+value')
|
||||
}
|
||||
|
||||
return makeIterator(
|
||||
() => [...this[kHeadersSortedMap].values()],
|
||||
'Headers',
|
||||
|
||||
8
deps/undici/src/lib/fetch/index.js
vendored
8
deps/undici/src/lib/fetch/index.js
vendored
@ -1957,7 +1957,7 @@ async function httpNetworkFetch (
|
||||
path: url.pathname + url.search,
|
||||
origin: url.origin,
|
||||
method: request.method,
|
||||
body: fetchParams.controller.dispatcher.isMockActive ? request.body && request.body.source : body,
|
||||
body: fetchParams.controller.dispatcher.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
|
||||
headers: request.headersList.entries,
|
||||
maxRedirections: 0,
|
||||
upgrade: request.mode === 'websocket' ? 'websocket' : undefined
|
||||
@ -2002,7 +2002,7 @@ async function httpNetworkFetch (
|
||||
location = val
|
||||
}
|
||||
|
||||
headers.append(key, val)
|
||||
headers[kHeadersList].append(key, val)
|
||||
}
|
||||
} else {
|
||||
const keys = Object.keys(headersList)
|
||||
@ -2016,7 +2016,7 @@ async function httpNetworkFetch (
|
||||
location = val
|
||||
}
|
||||
|
||||
headers.append(key, val)
|
||||
headers[kHeadersList].append(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2120,7 +2120,7 @@ async function httpNetworkFetch (
|
||||
const key = headersList[n + 0].toString('latin1')
|
||||
const val = headersList[n + 1].toString('latin1')
|
||||
|
||||
headers.append(key, val)
|
||||
headers[kHeadersList].append(key, val)
|
||||
}
|
||||
|
||||
resolve({
|
||||
|
||||
4
deps/undici/src/lib/fetch/request.js
vendored
4
deps/undici/src/lib/fetch/request.js
vendored
@ -316,11 +316,11 @@ class Request {
|
||||
// 2. If method is not a method or method is a forbidden method, then
|
||||
// throw a TypeError.
|
||||
if (!isValidHTTPToken(init.method)) {
|
||||
throw TypeError(`'${init.method}' is not a valid HTTP method.`)
|
||||
throw new TypeError(`'${init.method}' is not a valid HTTP method.`)
|
||||
}
|
||||
|
||||
if (forbiddenMethodsSet.has(method.toUpperCase())) {
|
||||
throw TypeError(`'${init.method}' HTTP method is unsupported.`)
|
||||
throw new TypeError(`'${init.method}' HTTP method is unsupported.`)
|
||||
}
|
||||
|
||||
// 3. Normalize method.
|
||||
|
||||
71
deps/undici/src/lib/fetch/util.js
vendored
71
deps/undici/src/lib/fetch/util.js
vendored
@ -103,52 +103,57 @@ function isValidReasonPhrase (statusText) {
|
||||
return true
|
||||
}
|
||||
|
||||
function isTokenChar (c) {
|
||||
return !(
|
||||
c >= 0x7f ||
|
||||
c <= 0x20 ||
|
||||
c === '(' ||
|
||||
c === ')' ||
|
||||
c === '<' ||
|
||||
c === '>' ||
|
||||
c === '@' ||
|
||||
c === ',' ||
|
||||
c === ';' ||
|
||||
c === ':' ||
|
||||
c === '\\' ||
|
||||
c === '"' ||
|
||||
c === '/' ||
|
||||
c === '[' ||
|
||||
c === ']' ||
|
||||
c === '?' ||
|
||||
c === '=' ||
|
||||
c === '{' ||
|
||||
c === '}'
|
||||
)
|
||||
/**
|
||||
* @see https://tools.ietf.org/html/rfc7230#section-3.2.6
|
||||
* @param {number} c
|
||||
*/
|
||||
function isTokenCharCode (c) {
|
||||
switch (c) {
|
||||
case 0x22:
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2c:
|
||||
case 0x2f:
|
||||
case 0x3a:
|
||||
case 0x3b:
|
||||
case 0x3c:
|
||||
case 0x3d:
|
||||
case 0x3e:
|
||||
case 0x3f:
|
||||
case 0x40:
|
||||
case 0x5b:
|
||||
case 0x5c:
|
||||
case 0x5d:
|
||||
case 0x7b:
|
||||
case 0x7d:
|
||||
// DQUOTE and "(),/:;<=>?@[\]{}"
|
||||
return false
|
||||
default:
|
||||
// VCHAR %x21-7E
|
||||
return c >= 0x21 && c <= 0x7e
|
||||
}
|
||||
}
|
||||
|
||||
// See RFC 7230, Section 3.2.6.
|
||||
// https://github.com/chromium/chromium/blob/d7da0240cae77824d1eda25745c4022757499131/third_party/blink/renderer/platform/network/http_parsers.cc#L321
|
||||
/**
|
||||
* @param {string} characters
|
||||
*/
|
||||
function isValidHTTPToken (characters) {
|
||||
if (!characters || typeof characters !== 'string') {
|
||||
if (characters.length === 0) {
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < characters.length; ++i) {
|
||||
const c = characters.charCodeAt(i)
|
||||
if (c > 0x7f || !isTokenChar(c)) {
|
||||
if (!isTokenCharCode(characters.charCodeAt(i))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#header-name
|
||||
// https://github.com/chromium/chromium/blob/b3d37e6f94f87d59e44662d6078f6a12de845d17/net/http/http_util.cc#L342
|
||||
/**
|
||||
* @see https://fetch.spec.whatwg.org/#header-name
|
||||
* @param {string} potentialValue
|
||||
*/
|
||||
function isValidHeaderName (potentialValue) {
|
||||
if (potentialValue.length === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
return isValidHTTPToken(potentialValue)
|
||||
}
|
||||
|
||||
|
||||
6
deps/undici/src/lib/fetch/webidl.js
vendored
6
deps/undici/src/lib/fetch/webidl.js
vendored
@ -427,12 +427,10 @@ webidl.converters.ByteString = function (V) {
|
||||
// 2. If the value of any element of x is greater than
|
||||
// 255, then throw a TypeError.
|
||||
for (let index = 0; index < x.length; index++) {
|
||||
const charCode = x.charCodeAt(index)
|
||||
|
||||
if (charCode > 255) {
|
||||
if (x.charCodeAt(index) > 255) {
|
||||
throw new TypeError(
|
||||
'Cannot convert argument to a ByteString because the character at ' +
|
||||
`index ${index} has a value of ${charCode} which is greater than 255.`
|
||||
`index ${index} has a value of ${x.charCodeAt(index)} which is greater than 255.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
336
deps/undici/src/lib/handler/RetryHandler.js
vendored
Normal file
336
deps/undici/src/lib/handler/RetryHandler.js
vendored
Normal file
@ -0,0 +1,336 @@
|
||||
const assert = require('node:assert')
|
||||
|
||||
const { kRetryHandlerDefaultRetry } = require('../core/symbols')
|
||||
const { RequestRetryError } = require('../core/errors')
|
||||
const { isDisturbed, parseHeaders, parseRangeHeader } = require('../core/util')
|
||||
|
||||
function calculateRetryAfterHeader (retryAfter) {
|
||||
const current = Date.now()
|
||||
const diff = new Date(retryAfter).getTime() - current
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
class RetryHandler {
|
||||
constructor (opts, handlers) {
|
||||
const { retryOptions, ...dispatchOpts } = opts
|
||||
const {
|
||||
// Retry scoped
|
||||
retry: retryFn,
|
||||
maxRetries,
|
||||
maxTimeout,
|
||||
minTimeout,
|
||||
timeoutFactor,
|
||||
// Response scoped
|
||||
methods,
|
||||
errorCodes,
|
||||
retryAfter,
|
||||
statusCodes
|
||||
} = retryOptions ?? {}
|
||||
|
||||
this.dispatch = handlers.dispatch
|
||||
this.handler = handlers.handler
|
||||
this.opts = dispatchOpts
|
||||
this.abort = null
|
||||
this.aborted = false
|
||||
this.retryOpts = {
|
||||
retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],
|
||||
retryAfter: retryAfter ?? true,
|
||||
maxTimeout: maxTimeout ?? 30 * 1000, // 30s,
|
||||
timeout: minTimeout ?? 500, // .5s
|
||||
timeoutFactor: timeoutFactor ?? 2,
|
||||
maxRetries: maxRetries ?? 5,
|
||||
// What errors we should retry
|
||||
methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],
|
||||
// Indicates which errors to retry
|
||||
statusCodes: statusCodes ?? [500, 502, 503, 504, 429],
|
||||
// List of errors to retry
|
||||
errorCodes: errorCodes ?? [
|
||||
'ECONNRESET',
|
||||
'ECONNREFUSED',
|
||||
'ENOTFOUND',
|
||||
'ENETDOWN',
|
||||
'ENETUNREACH',
|
||||
'EHOSTDOWN',
|
||||
'EHOSTUNREACH',
|
||||
'EPIPE'
|
||||
]
|
||||
}
|
||||
|
||||
this.retryCount = 0
|
||||
this.start = 0
|
||||
this.end = null
|
||||
this.etag = null
|
||||
this.resume = null
|
||||
|
||||
// Handle possible onConnect duplication
|
||||
this.handler.onConnect(reason => {
|
||||
this.aborted = true
|
||||
if (this.abort) {
|
||||
this.abort(reason)
|
||||
} else {
|
||||
this.reason = reason
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onRequestSent () {
|
||||
if (this.handler.onRequestSent) {
|
||||
this.handler.onRequestSent()
|
||||
}
|
||||
}
|
||||
|
||||
onUpgrade (statusCode, headers, socket) {
|
||||
if (this.handler.onUpgrade) {
|
||||
this.handler.onUpgrade(statusCode, headers, socket)
|
||||
}
|
||||
}
|
||||
|
||||
onConnect (abort) {
|
||||
if (this.aborted) {
|
||||
abort(this.reason)
|
||||
} else {
|
||||
this.abort = abort
|
||||
}
|
||||
}
|
||||
|
||||
onBodySent (chunk) {
|
||||
return this.handler.onBodySent(chunk)
|
||||
}
|
||||
|
||||
static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) {
|
||||
const { statusCode, code, headers } = err
|
||||
const { method, retryOptions } = opts
|
||||
const {
|
||||
maxRetries,
|
||||
timeout,
|
||||
maxTimeout,
|
||||
timeoutFactor,
|
||||
statusCodes,
|
||||
errorCodes,
|
||||
methods
|
||||
} = retryOptions
|
||||
let { counter, currentTimeout } = state
|
||||
|
||||
currentTimeout =
|
||||
currentTimeout != null && currentTimeout > 0 ? currentTimeout : timeout
|
||||
|
||||
// Any code that is not a Undici's originated and allowed to retry
|
||||
if (
|
||||
code &&
|
||||
code !== 'UND_ERR_REQ_RETRY' &&
|
||||
code !== 'UND_ERR_SOCKET' &&
|
||||
!errorCodes.includes(code)
|
||||
) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
// If a set of method are provided and the current method is not in the list
|
||||
if (Array.isArray(methods) && !methods.includes(method)) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
// If a set of status code are provided and the current status code is not in the list
|
||||
if (
|
||||
statusCode != null &&
|
||||
Array.isArray(statusCodes) &&
|
||||
!statusCodes.includes(statusCode)
|
||||
) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
// If we reached the max number of retries
|
||||
if (counter > maxRetries) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
|
||||
let retryAfterHeader = headers != null && headers['retry-after']
|
||||
if (retryAfterHeader) {
|
||||
retryAfterHeader = Number(retryAfterHeader)
|
||||
retryAfterHeader = isNaN(retryAfterHeader)
|
||||
? calculateRetryAfterHeader(retryAfterHeader)
|
||||
: retryAfterHeader * 1e3 // Retry-After is in seconds
|
||||
}
|
||||
|
||||
const retryTimeout =
|
||||
retryAfterHeader > 0
|
||||
? Math.min(retryAfterHeader, maxTimeout)
|
||||
: Math.min(currentTimeout * timeoutFactor ** counter, maxTimeout)
|
||||
|
||||
state.currentTimeout = retryTimeout
|
||||
|
||||
setTimeout(() => cb(null), retryTimeout)
|
||||
}
|
||||
|
||||
onHeaders (statusCode, rawHeaders, resume, statusMessage) {
|
||||
const headers = parseHeaders(rawHeaders)
|
||||
|
||||
this.retryCount += 1
|
||||
|
||||
if (statusCode >= 300) {
|
||||
this.abort(
|
||||
new RequestRetryError('Request failed', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// Checkpoint for resume from where we left it
|
||||
if (this.resume != null) {
|
||||
this.resume = null
|
||||
|
||||
if (statusCode !== 206) {
|
||||
return true
|
||||
}
|
||||
|
||||
const contentRange = parseRangeHeader(headers['content-range'])
|
||||
// If no content range
|
||||
if (!contentRange) {
|
||||
this.abort(
|
||||
new RequestRetryError('Content-Range mismatch', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// Let's start with a weak etag check
|
||||
if (this.etag != null && this.etag !== headers.etag) {
|
||||
this.abort(
|
||||
new RequestRetryError('ETag mismatch', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
const { start, size, end = size } = contentRange
|
||||
|
||||
assert(this.start === start, 'content-range mismatch')
|
||||
assert(this.end == null || this.end === end, 'content-range mismatch')
|
||||
|
||||
this.resume = resume
|
||||
return true
|
||||
}
|
||||
|
||||
if (this.end == null) {
|
||||
if (statusCode === 206) {
|
||||
// First time we receive 206
|
||||
const range = parseRangeHeader(headers['content-range'])
|
||||
|
||||
if (range == null) {
|
||||
return this.handler.onHeaders(
|
||||
statusCode,
|
||||
rawHeaders,
|
||||
resume,
|
||||
statusMessage
|
||||
)
|
||||
}
|
||||
|
||||
const { start, size, end = size } = range
|
||||
|
||||
assert(
|
||||
start != null && Number.isFinite(start) && this.start !== start,
|
||||
'content-range mismatch'
|
||||
)
|
||||
assert(Number.isFinite(start))
|
||||
assert(
|
||||
end != null && Number.isFinite(end) && this.end !== end,
|
||||
'invalid content-length'
|
||||
)
|
||||
|
||||
this.start = start
|
||||
this.end = end
|
||||
}
|
||||
|
||||
// We make our best to checkpoint the body for further range headers
|
||||
if (this.end == null) {
|
||||
const contentLength = headers['content-length']
|
||||
this.end = contentLength != null ? Number(contentLength) : null
|
||||
}
|
||||
|
||||
assert(Number.isFinite(this.start))
|
||||
assert(
|
||||
this.end == null || Number.isFinite(this.end),
|
||||
'invalid content-length'
|
||||
)
|
||||
|
||||
this.resume = resume
|
||||
this.etag = headers.etag != null ? headers.etag : null
|
||||
|
||||
return this.handler.onHeaders(
|
||||
statusCode,
|
||||
rawHeaders,
|
||||
resume,
|
||||
statusMessage
|
||||
)
|
||||
}
|
||||
|
||||
const err = new RequestRetryError('Request failed', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
})
|
||||
|
||||
this.abort(err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
onData (chunk) {
|
||||
this.start += chunk.length
|
||||
|
||||
return this.handler.onData(chunk)
|
||||
}
|
||||
|
||||
onComplete (rawTrailers) {
|
||||
this.retryCount = 0
|
||||
return this.handler.onComplete(rawTrailers)
|
||||
}
|
||||
|
||||
onError (err) {
|
||||
if (this.aborted || isDisturbed(this.opts.body)) {
|
||||
return this.handler.onError(err)
|
||||
}
|
||||
|
||||
this.retryOpts.retry(
|
||||
err,
|
||||
{
|
||||
state: { counter: this.retryCount++, currentTimeout: this.retryAfter },
|
||||
opts: { retryOptions: this.retryOpts, ...this.opts }
|
||||
},
|
||||
onRetry.bind(this)
|
||||
)
|
||||
|
||||
function onRetry (err) {
|
||||
if (err != null || this.aborted || isDisturbed(this.opts.body)) {
|
||||
return this.handler.onError(err)
|
||||
}
|
||||
|
||||
if (this.start !== 0) {
|
||||
this.opts = {
|
||||
...this.opts,
|
||||
headers: {
|
||||
...this.opts.headers,
|
||||
range: `bytes=${this.start}-${this.end ?? ''}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.dispatch(this.opts, this)
|
||||
} catch (err) {
|
||||
this.handler.onError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RetryHandler
|
||||
1523
deps/undici/src/package-lock.json
generated
vendored
1523
deps/undici/src/package-lock.json
generated
vendored
File diff suppressed because it is too large
Load Diff
3
deps/undici/src/package.json
vendored
3
deps/undici/src/package.json
vendored
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "undici",
|
||||
"version": "5.27.2",
|
||||
"version": "5.28.0",
|
||||
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
||||
"homepage": "https://undici.nodejs.org",
|
||||
"bugs": {
|
||||
@ -118,6 +118,7 @@
|
||||
"jsdom": "^22.1.0",
|
||||
"jsfuzz": "^1.0.15",
|
||||
"mocha": "^10.0.0",
|
||||
"mockttp": "^3.9.2",
|
||||
"p-timeout": "^3.2.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"proxy": "^1.0.2",
|
||||
|
||||
2
deps/undici/src/types/client.d.ts
vendored
2
deps/undici/src/types/client.d.ts
vendored
@ -77,7 +77,7 @@ export declare namespace Client {
|
||||
*/
|
||||
allowH2?: boolean;
|
||||
/**
|
||||
* @description Dictates the maximum number of concurrent streams for a single H2 session. It can be overriden by a SETTINGS remote frame.
|
||||
* @description Dictates the maximum number of concurrent streams for a single H2 session. It can be overridden by a SETTINGS remote frame.
|
||||
* @default 100
|
||||
*/
|
||||
maxConcurrentStreams?: number
|
||||
|
||||
2
deps/undici/src/types/dispatcher.d.ts
vendored
2
deps/undici/src/types/dispatcher.d.ts
vendored
@ -211,7 +211,7 @@ declare namespace Dispatcher {
|
||||
/** Invoked when request is upgraded either due to a `Upgrade` header or `CONNECT` method. */
|
||||
onUpgrade?(statusCode: number, headers: Buffer[] | string[] | null, socket: Duplex): void;
|
||||
/** Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. */
|
||||
onHeaders?(statusCode: number, headers: Buffer[] | string[] | null, resume: () => void): boolean;
|
||||
onHeaders?(statusCode: number, headers: Buffer[] | string[] | null, resume: () => void, statusText: string): boolean;
|
||||
/** Invoked when response payload data is received. */
|
||||
onData?(chunk: Buffer): boolean;
|
||||
/** Invoked when response payload and trailers have been received and the request has completed. */
|
||||
|
||||
4
deps/undici/src/types/index.d.ts
vendored
4
deps/undici/src/types/index.d.ts
vendored
@ -14,6 +14,7 @@ import MockPool from'./mock-pool'
|
||||
import MockAgent from'./mock-agent'
|
||||
import mockErrors from'./mock-errors'
|
||||
import ProxyAgent from'./proxy-agent'
|
||||
import RetryHandler from'./retry-handler'
|
||||
import { request, pipeline, stream, connect, upgrade } from './api'
|
||||
|
||||
export * from './cookies'
|
||||
@ -27,7 +28,7 @@ export * from './content-type'
|
||||
export * from './cache'
|
||||
export { Interceptable } from './mock-interceptor'
|
||||
|
||||
export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, setGlobalOrigin, getGlobalOrigin, MockClient, MockPool, MockAgent, mockErrors, ProxyAgent, RedirectHandler, DecoratorHandler }
|
||||
export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, setGlobalOrigin, getGlobalOrigin, MockClient, MockPool, MockAgent, mockErrors, ProxyAgent, RedirectHandler, DecoratorHandler, RetryHandler }
|
||||
export default Undici
|
||||
|
||||
declare namespace Undici {
|
||||
@ -35,6 +36,7 @@ declare namespace Undici {
|
||||
var Pool: typeof import('./pool').default;
|
||||
var RedirectHandler: typeof import ('./handlers').RedirectHandler
|
||||
var DecoratorHandler: typeof import ('./handlers').DecoratorHandler
|
||||
var RetryHandler: typeof import ('./retry-handler').default
|
||||
var createRedirectInterceptor: typeof import ('./interceptors').createRedirectInterceptor
|
||||
var BalancedPool: typeof import('./balanced-pool').default;
|
||||
var Client: typeof import('./client').default;
|
||||
|
||||
116
deps/undici/src/types/retry-handler.d.ts
vendored
Normal file
116
deps/undici/src/types/retry-handler.d.ts
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
import Dispatcher from "./dispatcher";
|
||||
|
||||
export default RetryHandler;
|
||||
|
||||
declare class RetryHandler implements Dispatcher.DispatchHandlers {
|
||||
constructor(
|
||||
options: Dispatcher.DispatchOptions & {
|
||||
retryOptions?: RetryHandler.RetryOptions;
|
||||
},
|
||||
retryHandlers: RetryHandler.RetryHandlers
|
||||
);
|
||||
}
|
||||
|
||||
declare namespace RetryHandler {
|
||||
export type RetryState = { counter: number; currentTimeout: number };
|
||||
|
||||
export type RetryContext = {
|
||||
state: RetryState;
|
||||
opts: Dispatcher.DispatchOptions & {
|
||||
retryOptions?: RetryHandler.RetryOptions;
|
||||
};
|
||||
}
|
||||
|
||||
export type OnRetryCallback = (result?: Error | null) => void;
|
||||
|
||||
export type RetryCallback = (
|
||||
err: Error,
|
||||
context: {
|
||||
state: RetryState;
|
||||
opts: Dispatcher.DispatchOptions & {
|
||||
retryOptions?: RetryHandler.RetryOptions;
|
||||
};
|
||||
},
|
||||
callback: OnRetryCallback
|
||||
) => number | null;
|
||||
|
||||
export interface RetryOptions {
|
||||
/**
|
||||
* Callback to be invoked on every retry iteration.
|
||||
* It receives the error, current state of the retry object and the options object
|
||||
* passed when instantiating the retry handler.
|
||||
*
|
||||
* @type {RetryCallback}
|
||||
* @memberof RetryOptions
|
||||
*/
|
||||
retry?: RetryCallback;
|
||||
/**
|
||||
* Maximum number of retries to allow.
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 5
|
||||
*/
|
||||
maxRetries?: number;
|
||||
/**
|
||||
* Max number of milliseconds allow between retries
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 30000
|
||||
*/
|
||||
maxTimeout?: number;
|
||||
/**
|
||||
* Initial number of milliseconds to wait before retrying for the first time.
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 500
|
||||
*/
|
||||
minTimeout?: number;
|
||||
/**
|
||||
* Factior to multiply the timeout factor between retries.
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof RetryOptions
|
||||
* @default 2
|
||||
*/
|
||||
timeoutFactor?: number;
|
||||
/**
|
||||
* It enables to automatically infer timeout between retries based on the `Retry-After` header.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof RetryOptions
|
||||
* @default true
|
||||
*/
|
||||
retryAfter?: boolean;
|
||||
/**
|
||||
* HTTP methods to retry.
|
||||
*
|
||||
* @type {Dispatcher.HttpMethod[]}
|
||||
* @memberof RetryOptions
|
||||
* @default ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],
|
||||
*/
|
||||
methods?: Dispatcher.HttpMethod[];
|
||||
/**
|
||||
* Error codes to be retried. e.g. `ECONNRESET`, `ENOTFOUND`, `ETIMEDOUT`, `ECONNREFUSED`, etc.
|
||||
*
|
||||
* @type {string[]}
|
||||
* @default ['ECONNRESET','ECONNREFUSED','ENOTFOUND','ENETDOWN','ENETUNREACH','EHOSTDOWN','EHOSTUNREACH','EPIPE']
|
||||
*/
|
||||
errorCodes?: string[];
|
||||
/**
|
||||
* HTTP status codes to be retried.
|
||||
*
|
||||
* @type {number[]}
|
||||
* @memberof RetryOptions
|
||||
* @default [500, 502, 503, 504, 429],
|
||||
*/
|
||||
statusCodes?: number[];
|
||||
}
|
||||
|
||||
export interface RetryHandlers {
|
||||
dispatch: Dispatcher["dispatch"];
|
||||
handler: Dispatcher.DispatchHandlers;
|
||||
}
|
||||
}
|
||||
240
deps/undici/undici.js
vendored
240
deps/undici/undici.js
vendored
@ -68,7 +68,8 @@ var require_symbols = __commonJS({
|
||||
kHTTP2BuildRequest: Symbol("http2 build request"),
|
||||
kHTTP1BuildRequest: Symbol("http1 build request"),
|
||||
kHTTP2CopyHeaders: Symbol("http2 copy headers"),
|
||||
kHTTPConnVersion: Symbol("http connection version")
|
||||
kHTTPConnVersion: Symbol("http connection version"),
|
||||
kRetryHandlerDefaultRetry: Symbol("retry agent default retry")
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -323,6 +324,21 @@ var require_errors = __commonJS({
|
||||
this.code = "UND_ERR_RES_EXCEEDED_MAX_SIZE";
|
||||
}
|
||||
};
|
||||
var RequestRetryError = class _RequestRetryError extends UndiciError {
|
||||
static {
|
||||
__name(this, "RequestRetryError");
|
||||
}
|
||||
constructor(message, code, { headers, data }) {
|
||||
super(message);
|
||||
Error.captureStackTrace(this, _RequestRetryError);
|
||||
this.name = "RequestRetryError";
|
||||
this.message = message || "Request retry error";
|
||||
this.code = "UND_ERR_REQ_RETRY";
|
||||
this.statusCode = code;
|
||||
this.data = data;
|
||||
this.headers = headers;
|
||||
}
|
||||
};
|
||||
module2.exports = {
|
||||
HTTPParserError,
|
||||
UndiciError,
|
||||
@ -342,7 +358,8 @@ var require_errors = __commonJS({
|
||||
NotSupportedError,
|
||||
ResponseContentLengthMismatchError,
|
||||
BalancedPoolMissingUpstreamError,
|
||||
ResponseExceededMaxSizeError
|
||||
ResponseExceededMaxSizeError,
|
||||
RequestRetryError
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -439,12 +456,12 @@ var require_util = __commonJS({
|
||||
if (host[0] === "[") {
|
||||
const idx2 = host.indexOf("]");
|
||||
assert(idx2 !== -1);
|
||||
return host.substr(1, idx2 - 1);
|
||||
return host.substring(1, idx2);
|
||||
}
|
||||
const idx = host.indexOf(":");
|
||||
if (idx === -1)
|
||||
return host;
|
||||
return host.substr(0, idx);
|
||||
return host.substring(0, idx);
|
||||
}
|
||||
__name(getHostname, "getHostname");
|
||||
function getServerName(host) {
|
||||
@ -527,7 +544,7 @@ var require_util = __commonJS({
|
||||
let val = obj[key];
|
||||
if (!val) {
|
||||
if (Array.isArray(headers[i + 1])) {
|
||||
obj[key] = headers[i + 1];
|
||||
obj[key] = headers[i + 1].map((x) => x.toString("utf8"));
|
||||
} else {
|
||||
obj[key] = headers[i + 1].toString("utf8");
|
||||
}
|
||||
@ -689,16 +706,7 @@ var require_util = __commonJS({
|
||||
}
|
||||
}
|
||||
__name(throwIfAborted, "throwIfAborted");
|
||||
var events;
|
||||
function addAbortListener(signal, listener) {
|
||||
if (typeof Symbol.dispose === "symbol") {
|
||||
if (!events) {
|
||||
events = require("events");
|
||||
}
|
||||
if (typeof events.addAbortListener === "function" && "aborted" in signal) {
|
||||
return events.addAbortListener(signal, listener);
|
||||
}
|
||||
}
|
||||
if ("addEventListener" in signal) {
|
||||
signal.addEventListener("abort", listener, { once: true });
|
||||
return () => signal.removeEventListener("abort", listener);
|
||||
@ -717,6 +725,17 @@ var require_util = __commonJS({
|
||||
return `${val}`;
|
||||
}
|
||||
__name(toUSVString, "toUSVString");
|
||||
function parseRangeHeader(range) {
|
||||
if (range == null || range === "")
|
||||
return { start: 0, end: null, size: null };
|
||||
const m = range ? range.match(/^bytes (\d+)-(\d+)\/(\d+)?$/) : null;
|
||||
return m ? {
|
||||
start: parseInt(m[1]),
|
||||
end: m[2] ? parseInt(m[2]) : null,
|
||||
size: m[3] ? parseInt(m[3]) : null
|
||||
} : null;
|
||||
}
|
||||
__name(parseRangeHeader, "parseRangeHeader");
|
||||
var kEnumerableProperty = /* @__PURE__ */ Object.create(null);
|
||||
kEnumerableProperty.enumerable = true;
|
||||
module2.exports = {
|
||||
@ -749,9 +768,11 @@ var require_util = __commonJS({
|
||||
buildURL,
|
||||
throwIfAborted,
|
||||
addAbortListener,
|
||||
parseRangeHeader,
|
||||
nodeMajor,
|
||||
nodeMinor,
|
||||
nodeHasAutoSelectFamily: nodeMajor > 18 || nodeMajor === 18 && nodeMinor >= 13
|
||||
nodeHasAutoSelectFamily: nodeMajor > 18 || nodeMajor === 18 && nodeMinor >= 13,
|
||||
safeHTTPMethods: ["GET", "HEAD", "OPTIONS", "TRACE"]
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -1056,17 +1077,37 @@ var require_util2 = __commonJS({
|
||||
return true;
|
||||
}
|
||||
__name(isValidReasonPhrase, "isValidReasonPhrase");
|
||||
function isTokenChar(c) {
|
||||
return !(c >= 127 || c <= 32 || c === "(" || c === ")" || c === "<" || c === ">" || c === "@" || c === "," || c === ";" || c === ":" || c === "\\" || c === '"' || c === "/" || c === "[" || c === "]" || c === "?" || c === "=" || c === "{" || c === "}");
|
||||
function isTokenCharCode(c) {
|
||||
switch (c) {
|
||||
case 34:
|
||||
case 40:
|
||||
case 41:
|
||||
case 44:
|
||||
case 47:
|
||||
case 58:
|
||||
case 59:
|
||||
case 60:
|
||||
case 61:
|
||||
case 62:
|
||||
case 63:
|
||||
case 64:
|
||||
case 91:
|
||||
case 92:
|
||||
case 93:
|
||||
case 123:
|
||||
case 125:
|
||||
return false;
|
||||
default:
|
||||
return c >= 33 && c <= 126;
|
||||
}
|
||||
}
|
||||
__name(isTokenChar, "isTokenChar");
|
||||
__name(isTokenCharCode, "isTokenCharCode");
|
||||
function isValidHTTPToken(characters) {
|
||||
if (!characters || typeof characters !== "string") {
|
||||
if (characters.length === 0) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < characters.length; ++i) {
|
||||
const c = characters.charCodeAt(i);
|
||||
if (c > 127 || !isTokenChar(c)) {
|
||||
if (!isTokenCharCode(characters.charCodeAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1074,9 +1115,6 @@ var require_util2 = __commonJS({
|
||||
}
|
||||
__name(isValidHTTPToken, "isValidHTTPToken");
|
||||
function isValidHeaderName(potentialValue) {
|
||||
if (potentialValue.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return isValidHTTPToken(potentialValue);
|
||||
}
|
||||
__name(isValidHeaderName, "isValidHeaderName");
|
||||
@ -1829,10 +1867,9 @@ var require_webidl = __commonJS({
|
||||
webidl.converters.ByteString = function(V) {
|
||||
const x = webidl.converters.DOMString(V);
|
||||
for (let index = 0; index < x.length; index++) {
|
||||
const charCode = x.charCodeAt(index);
|
||||
if (charCode > 255) {
|
||||
if (x.charCodeAt(index) > 255) {
|
||||
throw new TypeError(
|
||||
`Cannot convert argument to a ByteString because the character at index ${index} has a value of ${charCode} which is greater than 255.`
|
||||
`Cannot convert argument to a ByteString because the character at index ${index} has a value of ${x.charCodeAt(index)} which is greater than 255.`
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1953,27 +1990,36 @@ var require_headers = __commonJS({
|
||||
var assert = require("assert");
|
||||
var kHeadersMap = Symbol("headers map");
|
||||
var kHeadersSortedMap = Symbol("headers map sorted");
|
||||
function isHTTPWhiteSpaceCharCode(code) {
|
||||
return code === 10 || code === 13 || code === 9 || code === 32;
|
||||
}
|
||||
__name(isHTTPWhiteSpaceCharCode, "isHTTPWhiteSpaceCharCode");
|
||||
function headerValueNormalize(potentialValue) {
|
||||
let i = potentialValue.length;
|
||||
while (/[\r\n\t ]/.test(potentialValue.charAt(--i)))
|
||||
;
|
||||
return potentialValue.slice(0, i + 1).replace(/^[\r\n\t ]+/, "");
|
||||
let i = 0;
|
||||
let j = potentialValue.length;
|
||||
while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(j - 1)))
|
||||
--j;
|
||||
while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i)))
|
||||
++i;
|
||||
return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j);
|
||||
}
|
||||
__name(headerValueNormalize, "headerValueNormalize");
|
||||
function fill(headers, object) {
|
||||
if (Array.isArray(object)) {
|
||||
for (const header of object) {
|
||||
for (let i = 0; i < object.length; ++i) {
|
||||
const header = object[i];
|
||||
if (header.length !== 2) {
|
||||
throw webidl.errors.exception({
|
||||
header: "Headers constructor",
|
||||
message: `expected name/value pair to be length 2, found ${header.length}.`
|
||||
});
|
||||
}
|
||||
headers.append(header[0], header[1]);
|
||||
appendHeader(headers, header[0], header[1]);
|
||||
}
|
||||
} else if (typeof object === "object" && object !== null) {
|
||||
for (const [key, value] of Object.entries(object)) {
|
||||
headers.append(key, value);
|
||||
const keys = Object.keys(object);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
appendHeader(headers, keys[i], object[keys[i]]);
|
||||
}
|
||||
} else {
|
||||
throw webidl.errors.conversionFailed({
|
||||
@ -1984,6 +2030,28 @@ var require_headers = __commonJS({
|
||||
}
|
||||
}
|
||||
__name(fill, "fill");
|
||||
function appendHeader(headers, name, value) {
|
||||
value = headerValueNormalize(value);
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: "Headers.append",
|
||||
value: name,
|
||||
type: "header name"
|
||||
});
|
||||
} else if (!isValidHeaderValue(value)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: "Headers.append",
|
||||
value,
|
||||
type: "header value"
|
||||
});
|
||||
}
|
||||
if (headers[kGuard] === "immutable") {
|
||||
throw new TypeError("immutable");
|
||||
} else if (headers[kGuard] === "request-no-cors") {
|
||||
}
|
||||
return headers[kHeadersList].append(name, value);
|
||||
}
|
||||
__name(appendHeader, "appendHeader");
|
||||
var HeadersList = class _HeadersList {
|
||||
static {
|
||||
__name(this, "HeadersList");
|
||||
@ -1994,7 +2062,7 @@ var require_headers = __commonJS({
|
||||
if (init instanceof _HeadersList) {
|
||||
this[kHeadersMap] = new Map(init[kHeadersMap]);
|
||||
this[kHeadersSortedMap] = init[kHeadersSortedMap];
|
||||
this.cookies = init.cookies;
|
||||
this.cookies = init.cookies === null ? null : [...init.cookies];
|
||||
} else {
|
||||
this[kHeadersMap] = new Map(init);
|
||||
this[kHeadersSortedMap] = null;
|
||||
@ -2036,7 +2104,7 @@ var require_headers = __commonJS({
|
||||
if (lowercaseName === "set-cookie") {
|
||||
this.cookies = [value];
|
||||
}
|
||||
return this[kHeadersMap].set(lowercaseName, { name, value });
|
||||
this[kHeadersMap].set(lowercaseName, { name, value });
|
||||
}
|
||||
// https://fetch.spec.whatwg.org/#concept-header-list-delete
|
||||
delete(name) {
|
||||
@ -2045,14 +2113,12 @@ var require_headers = __commonJS({
|
||||
if (name === "set-cookie") {
|
||||
this.cookies = null;
|
||||
}
|
||||
return this[kHeadersMap].delete(name);
|
||||
this[kHeadersMap].delete(name);
|
||||
}
|
||||
// https://fetch.spec.whatwg.org/#concept-header-list-get
|
||||
get(name) {
|
||||
if (!this.contains(name)) {
|
||||
return null;
|
||||
}
|
||||
return this[kHeadersMap].get(name.toLowerCase())?.value ?? null;
|
||||
const value = this[kHeadersMap].get(name.toLowerCase());
|
||||
return value === void 0 ? null : value.value;
|
||||
}
|
||||
*[Symbol.iterator]() {
|
||||
for (const [name, { value }] of this[kHeadersMap]) {
|
||||
@ -2087,25 +2153,7 @@ var require_headers = __commonJS({
|
||||
webidl.argumentLengthCheck(arguments, 2, { header: "Headers.append" });
|
||||
name = webidl.converters.ByteString(name);
|
||||
value = webidl.converters.ByteString(value);
|
||||
value = headerValueNormalize(value);
|
||||
if (!isValidHeaderName(name)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: "Headers.append",
|
||||
value: name,
|
||||
type: "header name"
|
||||
});
|
||||
} else if (!isValidHeaderValue(value)) {
|
||||
throw webidl.errors.invalidArgument({
|
||||
prefix: "Headers.append",
|
||||
value,
|
||||
type: "header value"
|
||||
});
|
||||
}
|
||||
if (this[kGuard] === "immutable") {
|
||||
throw new TypeError("immutable");
|
||||
} else if (this[kGuard] === "request-no-cors") {
|
||||
}
|
||||
return this[kHeadersList].append(name, value);
|
||||
return appendHeader(this, name, value);
|
||||
}
|
||||
// https://fetch.spec.whatwg.org/#dom-headers-delete
|
||||
delete(name) {
|
||||
@ -2126,7 +2174,7 @@ var require_headers = __commonJS({
|
||||
if (!this[kHeadersList].contains(name)) {
|
||||
return;
|
||||
}
|
||||
return this[kHeadersList].delete(name);
|
||||
this[kHeadersList].delete(name);
|
||||
}
|
||||
// https://fetch.spec.whatwg.org/#dom-headers-get
|
||||
get(name) {
|
||||
@ -2180,7 +2228,7 @@ var require_headers = __commonJS({
|
||||
throw new TypeError("immutable");
|
||||
} else if (this[kGuard] === "request-no-cors") {
|
||||
}
|
||||
return this[kHeadersList].set(name, value);
|
||||
this[kHeadersList].set(name, value);
|
||||
}
|
||||
// https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
|
||||
getSetCookie() {
|
||||
@ -2199,10 +2247,11 @@ var require_headers = __commonJS({
|
||||
const headers = [];
|
||||
const names = [...this[kHeadersList]].sort((a, b) => a[0] < b[0] ? -1 : 1);
|
||||
const cookies = this[kHeadersList].cookies;
|
||||
for (const [name, value] of names) {
|
||||
for (let i = 0; i < names.length; ++i) {
|
||||
const [name, value] = names[i];
|
||||
if (name === "set-cookie") {
|
||||
for (const value2 of cookies) {
|
||||
headers.push([name, value2]);
|
||||
for (let j = 0; j < cookies.length; ++j) {
|
||||
headers.push([name, cookies[j]]);
|
||||
}
|
||||
} else {
|
||||
assert(value !== null);
|
||||
@ -2214,6 +2263,14 @@ var require_headers = __commonJS({
|
||||
}
|
||||
keys() {
|
||||
webidl.brandCheck(this, _Headers);
|
||||
if (this[kGuard] === "immutable") {
|
||||
const value = this[kHeadersSortedMap];
|
||||
return makeIterator(
|
||||
() => value,
|
||||
"Headers",
|
||||
"key"
|
||||
);
|
||||
}
|
||||
return makeIterator(
|
||||
() => [...this[kHeadersSortedMap].values()],
|
||||
"Headers",
|
||||
@ -2222,6 +2279,14 @@ var require_headers = __commonJS({
|
||||
}
|
||||
values() {
|
||||
webidl.brandCheck(this, _Headers);
|
||||
if (this[kGuard] === "immutable") {
|
||||
const value = this[kHeadersSortedMap];
|
||||
return makeIterator(
|
||||
() => value,
|
||||
"Headers",
|
||||
"value"
|
||||
);
|
||||
}
|
||||
return makeIterator(
|
||||
() => [...this[kHeadersSortedMap].values()],
|
||||
"Headers",
|
||||
@ -2230,6 +2295,14 @@ var require_headers = __commonJS({
|
||||
}
|
||||
entries() {
|
||||
webidl.brandCheck(this, _Headers);
|
||||
if (this[kGuard] === "immutable") {
|
||||
const value = this[kHeadersSortedMap];
|
||||
return makeIterator(
|
||||
() => value,
|
||||
"Headers",
|
||||
"key+value"
|
||||
);
|
||||
}
|
||||
return makeIterator(
|
||||
() => [...this[kHeadersSortedMap].values()],
|
||||
"Headers",
|
||||
@ -5991,10 +6064,10 @@ var require_request = __commonJS({
|
||||
if (init.method !== void 0) {
|
||||
let method = init.method;
|
||||
if (!isValidHTTPToken(init.method)) {
|
||||
throw TypeError(`'${init.method}' is not a valid HTTP method.`);
|
||||
throw new TypeError(`'${init.method}' is not a valid HTTP method.`);
|
||||
}
|
||||
if (forbiddenMethodsSet.has(method.toUpperCase())) {
|
||||
throw TypeError(`'${init.method}' HTTP method is unsupported.`);
|
||||
throw new TypeError(`'${init.method}' HTTP method is unsupported.`);
|
||||
}
|
||||
method = normalizeMethod(init.method);
|
||||
request.method = method;
|
||||
@ -7132,11 +7205,7 @@ var require_request2 = __commonJS({
|
||||
}
|
||||
onBodySent(chunk) {
|
||||
if (this[kHandler].onBodySent) {
|
||||
try {
|
||||
this[kHandler].onBodySent(chunk);
|
||||
} catch (err) {
|
||||
this.onError(err);
|
||||
}
|
||||
return this[kHandler].onBodySent(chunk);
|
||||
}
|
||||
}
|
||||
onRequestSent() {
|
||||
@ -7144,11 +7213,7 @@ var require_request2 = __commonJS({
|
||||
channels.bodySent.publish({ request: this });
|
||||
}
|
||||
if (this[kHandler].onRequestSent) {
|
||||
try {
|
||||
this[kHandler].onRequestSent();
|
||||
} catch (err) {
|
||||
this.onError(err);
|
||||
}
|
||||
return this[kHandler].onRequestSent();
|
||||
}
|
||||
}
|
||||
onConnect(abort) {
|
||||
@ -8939,7 +9004,7 @@ var require_client = __commonJS({
|
||||
if (hostname[0] === "[") {
|
||||
const idx = hostname.indexOf("]");
|
||||
assert(idx !== -1);
|
||||
const ip = hostname.substr(1, idx - 1);
|
||||
const ip = hostname.substring(1, idx);
|
||||
assert(net.isIP(ip));
|
||||
hostname = ip;
|
||||
}
|
||||
@ -9385,7 +9450,8 @@ upgrade: ${upgrade}\r
|
||||
}
|
||||
++h2State.openStreams;
|
||||
stream.once("response", (headers2) => {
|
||||
if (request.onHeaders(Number(headers2[HTTP2_HEADER_STATUS]), headers2, stream.resume.bind(stream), "") === false) {
|
||||
const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers2;
|
||||
if (request.onHeaders(Number(statusCode), realHeaders, stream.resume.bind(stream), "") === false) {
|
||||
stream.pause();
|
||||
}
|
||||
});
|
||||
@ -9529,7 +9595,11 @@ upgrade: ${upgrade}\r
|
||||
}
|
||||
}, "onDrain");
|
||||
const onAbort = /* @__PURE__ */ __name(function() {
|
||||
onFinished(new RequestAbortedError());
|
||||
if (finished) {
|
||||
return;
|
||||
}
|
||||
const err = new RequestAbortedError();
|
||||
queueMicrotask(() => onFinished(err));
|
||||
}, "onAbort");
|
||||
const onFinished = /* @__PURE__ */ __name(function(err) {
|
||||
if (finished) {
|
||||
@ -10930,7 +11000,7 @@ var require_fetch = __commonJS({
|
||||
path: url.pathname + url.search,
|
||||
origin: url.origin,
|
||||
method: request.method,
|
||||
body: fetchParams.controller.dispatcher.isMockActive ? request.body && request.body.source : body,
|
||||
body: fetchParams.controller.dispatcher.isMockActive ? request.body && (request.body.source || request.body.stream) : body,
|
||||
headers: request.headersList.entries,
|
||||
maxRedirections: 0,
|
||||
upgrade: request.mode === "websocket" ? "websocket" : void 0
|
||||
@ -10963,7 +11033,7 @@ var require_fetch = __commonJS({
|
||||
} else if (key.toLowerCase() === "location") {
|
||||
location = val;
|
||||
}
|
||||
headers.append(key, val);
|
||||
headers[kHeadersList].append(key, val);
|
||||
}
|
||||
} else {
|
||||
const keys = Object.keys(headersList);
|
||||
@ -10974,7 +11044,7 @@ var require_fetch = __commonJS({
|
||||
} else if (key.toLowerCase() === "location") {
|
||||
location = val;
|
||||
}
|
||||
headers.append(key, val);
|
||||
headers[kHeadersList].append(key, val);
|
||||
}
|
||||
}
|
||||
this.body = new Readable({ read: resume });
|
||||
@ -11042,7 +11112,7 @@ var require_fetch = __commonJS({
|
||||
for (let n = 0; n < headersList.length; n += 2) {
|
||||
const key = headersList[n + 0].toString("latin1");
|
||||
const val = headersList[n + 1].toString("latin1");
|
||||
headers.append(key, val);
|
||||
headers[kHeadersList].append(key, val);
|
||||
}
|
||||
resolve({
|
||||
status,
|
||||
|
||||
@ -29,7 +29,7 @@ This a list of all the dependencies:
|
||||
* [postject 1.0.0-alpha.6][]
|
||||
* [simdjson 3.6.0][]
|
||||
* [simdutf 4.0.4][]
|
||||
* [undici 5.27.2][]
|
||||
* [undici 5.28.0][]
|
||||
* [uvwasi 0.0.19][]
|
||||
* [V8 11.8.172.12][]
|
||||
* [zlib 1.2.13.1-motley-5daffc7][]
|
||||
@ -297,7 +297,7 @@ a C++ library for fast JSON parsing.
|
||||
The [simdutf](https://github.com/simdutf/simdutf) dependency is
|
||||
a C++ library for fast UTF-8 decoding and encoding.
|
||||
|
||||
### undici 5.27.2
|
||||
### undici 5.28.0
|
||||
|
||||
The [undici](https://github.com/nodejs/undici) dependency is an HTTP/1.1 client,
|
||||
written from scratch for Node.js..
|
||||
@ -352,7 +352,7 @@ performance improvements not currently available in standard zlib.
|
||||
[postject 1.0.0-alpha.6]: #postject-100-alpha6
|
||||
[simdjson 3.6.0]: #simdutf-360
|
||||
[simdutf 4.0.4]: #simdutf-404
|
||||
[undici 5.27.2]: #undici-5272
|
||||
[undici 5.28.0]: #undici-5280
|
||||
[update-openssl-action]: ../../../.github/workflows/update-openssl.yml
|
||||
[uvwasi 0.0.19]: #uvwasi-0019
|
||||
[v8 11.8.172.12]: #v8-11817212
|
||||
|
||||
@ -2,5 +2,5 @@
|
||||
// Refer to tools/dep_updaters/update-undici.sh
|
||||
#ifndef SRC_UNDICI_VERSION_H_
|
||||
#define SRC_UNDICI_VERSION_H_
|
||||
#define UNDICI_VERSION "5.27.2"
|
||||
#define UNDICI_VERSION "5.28.0"
|
||||
#endif // SRC_UNDICI_VERSION_H_
|
||||
|
||||
Loading…
Reference in New Issue
Block a user