mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
deps: update undici to 6.20.0
PR-URL: https://github.com/nodejs/node/pull/55329 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
This commit is contained in:
1 parent
ff3cc3b2ab
commit
34ca36a397
27 files changed
+1532
-612
No files matched your search
@@ -84,3 +84,6 @@ undici-fetch.js
|
||||
.npmrc
|
||||
|
||||
.tap
|
||||
|
||||
# File generated by /test/request-timeout.js
|
||||
test/request-timeout.10mb.bin
|
||||
Vendored
+3
@@ -11,3 +11,6 @@ lib/llhttp/llhttp.wasm
|
||||
!index.d.ts
|
||||
!docs/docs/**/*
|
||||
!scripts/strip-comments.js
|
||||
|
||||
# File generated by /test/request-timeout.js
|
||||
test/request-timeout.10mb.bin
|
||||
+197
@@ -984,6 +984,203 @@ client.dispatch(
|
||||
);
|
||||
```
|
||||
|
||||
##### `Response Error Interceptor`
|
||||
|
||||
**Introduction**
|
||||
|
||||
The Response Error Interceptor is designed to handle HTTP response errors efficiently. It intercepts responses and throws detailed errors for responses with status codes indicating failure (4xx, 5xx). This interceptor enhances error handling by providing structured error information, including response headers, data, and status codes.
|
||||
|
||||
**ResponseError Class**
|
||||
|
||||
The `ResponseError` class extends the `UndiciError` class and encapsulates detailed error information. It captures the response status code, headers, and data, providing a structured way to handle errors.
|
||||
|
||||
**Definition**
|
||||
|
||||
```js
|
||||
class ResponseError extends UndiciError {
|
||||
constructor (message, code, { headers, data }) {
|
||||
super(message);
|
||||
this.name = 'ResponseError';
|
||||
this.message = message || 'Response error';
|
||||
this.code = 'UND_ERR_RESPONSE';
|
||||
this.statusCode = code;
|
||||
this.data = data;
|
||||
this.headers = headers;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Interceptor Handler**
|
||||
|
||||
The interceptor's handler class extends `DecoratorHandler` and overrides methods to capture response details and handle errors based on the response status code.
|
||||
|
||||
**Methods**
|
||||
|
||||
- **onConnect**: Initializes response properties.
|
||||
- **onHeaders**: Captures headers and status code. Decodes body if content type is `application/json` or `text/plain`.
|
||||
- **onData**: Appends chunks to the body if status code indicates an error.
|
||||
- **onComplete**: Finalizes error handling, constructs a `ResponseError`, and invokes the `onError` method.
|
||||
- **onError**: Propagates errors to the handler.
|
||||
|
||||
**Definition**
|
||||
|
||||
```js
|
||||
class Handler extends DecoratorHandler {
|
||||
// Private properties
|
||||
#handler;
|
||||
#statusCode;
|
||||
#contentType;
|
||||
#decoder;
|
||||
#headers;
|
||||
#body;
|
||||
|
||||
constructor (opts, { handler }) {
|
||||
super(handler);
|
||||
this.#handler = handler;
|
||||
}
|
||||
|
||||
onConnect (abort) {
|
||||
this.#statusCode = 0;
|
||||
this.#contentType = null;
|
||||
this.#decoder = null;
|
||||
this.#headers = null;
|
||||
this.#body = '';
|
||||
return this.#handler.onConnect(abort);
|
||||
}
|
||||
|
||||
onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
|
||||
this.#statusCode = statusCode;
|
||||
this.#headers = headers;
|
||||
this.#contentType = headers['content-type'];
|
||||
|
||||
if (this.#statusCode < 400) {
|
||||
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers);
|
||||
}
|
||||
|
||||
if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
|
||||
this.#decoder = new TextDecoder('utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
onData (chunk) {
|
||||
if (this.#statusCode < 400) {
|
||||
return this.#handler.onData(chunk);
|
||||
}
|
||||
this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? '';
|
||||
}
|
||||
|
||||
onComplete (rawTrailers) {
|
||||
if (this.#statusCode >= 400) {
|
||||
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? '';
|
||||
if (this.#contentType === 'application/json') {
|
||||
try {
|
||||
this.#body = JSON.parse(this.#body);
|
||||
} catch {
|
||||
// Do nothing...
|
||||
}
|
||||
}
|
||||
|
||||
let err;
|
||||
const stackTraceLimit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = 0;
|
||||
try {
|
||||
err = new ResponseError('Response Error', this.#statusCode, this.#headers, this.#body);
|
||||
} finally {
|
||||
Error.stackTraceLimit = stackTraceLimit;
|
||||
}
|
||||
|
||||
this.#handler.onError(err);
|
||||
} else {
|
||||
this.#handler.onComplete(rawTrailers);
|
||||
}
|
||||
}
|
||||
|
||||
onError (err) {
|
||||
this.#handler.onError(err);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (dispatch) => (opts, handler) => opts.throwOnError
|
||||
? dispatch(opts, new Handler(opts, { handler }))
|
||||
: dispatch(opts, handler);
|
||||
```
|
||||
|
||||
**Tests**
|
||||
|
||||
Unit tests ensure the interceptor functions correctly, handling both error and non-error responses appropriately.
|
||||
|
||||
**Example Tests**
|
||||
|
||||
- **No Error if `throwOnError` is False**:
|
||||
|
||||
```js
|
||||
test('should not error if request is not meant to throw error', async (t) => {
|
||||
const opts = { throwOnError: false };
|
||||
const handler = { onError: () => {}, onData: () => {}, onComplete: () => {} };
|
||||
const interceptor = createResponseErrorInterceptor((opts, handler) => handler.onComplete());
|
||||
assert.doesNotThrow(() => interceptor(opts, handler));
|
||||
});
|
||||
```
|
||||
|
||||
- **Error if Status Code is in Specified Error Codes**:
|
||||
|
||||
```js
|
||||
test('should error if request status code is in the specified error codes', async (t) => {
|
||||
const opts = { throwOnError: true, statusCodes: [500] };
|
||||
const response = { statusCode: 500 };
|
||||
let capturedError;
|
||||
const handler = {
|
||||
onError: (err) => { capturedError = err; },
|
||||
onData: () => {},
|
||||
onComplete: () => {}
|
||||
};
|
||||
|
||||
const interceptor = createResponseErrorInterceptor((opts, handler) => {
|
||||
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
|
||||
handler.onError(new Error('Response Error'));
|
||||
} else {
|
||||
handler.onComplete();
|
||||
}
|
||||
});
|
||||
|
||||
interceptor({ ...opts, response }, handler);
|
||||
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
assert(capturedError, 'Expected error to be captured but it was not.');
|
||||
assert.strictEqual(capturedError.message, 'Response Error');
|
||||
assert.strictEqual(response.statusCode, 500);
|
||||
});
|
||||
```
|
||||
|
||||
- **No Error if Status Code is Not in Specified Error Codes**:
|
||||
|
||||
```js
|
||||
test('should not error if request status code is not in the specified error codes', async (t) => {
|
||||
const opts = { throwOnError: true, statusCodes: [500] };
|
||||
const response = { statusCode: 404 };
|
||||
const handler = {
|
||||
onError: () => {},
|
||||
onData: () => {},
|
||||
onComplete: () => {}
|
||||
};
|
||||
|
||||
const interceptor = createResponseErrorInterceptor((opts, handler) => {
|
||||
if (opts.throwOnError && opts.statusCodes.includes(response.statusCode)) {
|
||||
handler.onError(new Error('Response Error'));
|
||||
} else {
|
||||
handler.onComplete();
|
||||
}
|
||||
});
|
||||
|
||||
assert.doesNotThrow(() => interceptor({ ...opts, response }, handler));
|
||||
});
|
||||
```
|
||||
|
||||
**Conclusion**
|
||||
|
||||
The Response Error Interceptor provides a robust mechanism for handling HTTP response errors by capturing detailed error information and propagating it through a structured `ResponseError` class. This enhancement improves error handling and debugging capabilities in applications using the interceptor.
|
||||
|
||||
## Instance Events
|
||||
|
||||
### Event: `'connect'`
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ 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.
|
||||
- **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => number | null` (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)
|
||||
|
||||
+2
-2
@@ -50,9 +50,9 @@ class UpgradeHandler extends AsyncResource {
|
||||
}
|
||||
|
||||
onUpgrade (statusCode, rawHeaders, socket) {
|
||||
const { callback, opaque, context } = this
|
||||
assert(statusCode === 101)
|
||||
|
||||
assert.strictEqual(statusCode, 101)
|
||||
const { callback, opaque, context } = this
|
||||
|
||||
removeSignal(this)
|
||||
|
||||
|
||||
Vendored
+73
-31
@@ -4,6 +4,9 @@ const net = require('node:net')
|
||||
const assert = require('node:assert')
|
||||
const util = require('./util')
|
||||
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
|
||||
const timers = require('../util/timers')
|
||||
|
||||
function noop () {}
|
||||
|
||||
let tls // include tls conditionally since it is not always available
|
||||
|
||||
@@ -91,9 +94,11 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
||||
servername = servername || options.servername || util.getServerName(host) || null
|
||||
|
||||
const sessionKey = servername || hostname
|
||||
assert(sessionKey)
|
||||
|
||||
const session = customSession || sessionCache.get(sessionKey) || null
|
||||
|
||||
assert(sessionKey)
|
||||
port = port || 443
|
||||
|
||||
socket = tls.connect({
|
||||
highWaterMark: 16384, // TLS in node can't have bigger HWM anyway...
|
||||
@@ -104,7 +109,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
||||
// TODO(HTTP/2): Add support for h2c
|
||||
ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'],
|
||||
socket: httpSocket, // upgrade socket connection
|
||||
port: port || 443,
|
||||
port,
|
||||
host: hostname
|
||||
})
|
||||
|
||||
@@ -115,11 +120,14 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
||||
})
|
||||
} else {
|
||||
assert(!httpSocket, 'httpSocket can only be sent on TLS update')
|
||||
|
||||
port = port || 80
|
||||
|
||||
socket = net.connect({
|
||||
highWaterMark: 64 * 1024, // Same as nodejs fs streams.
|
||||
...options,
|
||||
localAddress,
|
||||
port: port || 80,
|
||||
port,
|
||||
host: hostname
|
||||
})
|
||||
}
|
||||
@@ -130,12 +138,12 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
||||
socket.setKeepAlive(true, keepAliveInitialDelay)
|
||||
}
|
||||
|
||||
const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout)
|
||||
const clearConnectTimeout = setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port })
|
||||
|
||||
socket
|
||||
.setNoDelay(true)
|
||||
.once(protocol === 'https:' ? 'secureConnect' : 'connect', function () {
|
||||
cancelTimeout()
|
||||
queueMicrotask(clearConnectTimeout)
|
||||
|
||||
if (callback) {
|
||||
const cb = callback
|
||||
@@ -144,7 +152,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
||||
}
|
||||
})
|
||||
.on('error', function (err) {
|
||||
cancelTimeout()
|
||||
queueMicrotask(clearConnectTimeout)
|
||||
|
||||
if (callback) {
|
||||
const cb = callback
|
||||
@@ -157,36 +165,70 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
|
||||
}
|
||||
}
|
||||
|
||||
function setupTimeout (onConnectTimeout, timeout) {
|
||||
if (!timeout) {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
let s1 = null
|
||||
let s2 = null
|
||||
const timeoutId = setTimeout(() => {
|
||||
// setImmediate is added to make sure that we prioritize socket error events over timeouts
|
||||
s1 = setImmediate(() => {
|
||||
if (process.platform === 'win32') {
|
||||
// Windows needs an extra setImmediate probably due to implementation differences in the socket logic
|
||||
s2 = setImmediate(() => onConnectTimeout())
|
||||
} else {
|
||||
onConnectTimeout()
|
||||
/**
|
||||
* @param {WeakRef<net.Socket>} socketWeakRef
|
||||
* @param {object} opts
|
||||
* @param {number} opts.timeout
|
||||
* @param {string} opts.hostname
|
||||
* @param {number} opts.port
|
||||
* @returns {() => void}
|
||||
*/
|
||||
const setupConnectTimeout = process.platform === 'win32'
|
||||
? (socketWeakRef, opts) => {
|
||||
if (!opts.timeout) {
|
||||
return noop
|
||||
}
|
||||
})
|
||||
}, timeout)
|
||||
return () => {
|
||||
clearTimeout(timeoutId)
|
||||
clearImmediate(s1)
|
||||
clearImmediate(s2)
|
||||
}
|
||||
}
|
||||
|
||||
function onConnectTimeout (socket) {
|
||||
let s1 = null
|
||||
let s2 = null
|
||||
const fastTimer = timers.setFastTimeout(() => {
|
||||
// setImmediate is added to make sure that we prioritize socket error events over timeouts
|
||||
s1 = setImmediate(() => {
|
||||
// Windows needs an extra setImmediate probably due to implementation differences in the socket logic
|
||||
s2 = setImmediate(() => onConnectTimeout(socketWeakRef.deref(), opts))
|
||||
})
|
||||
}, opts.timeout)
|
||||
return () => {
|
||||
timers.clearFastTimeout(fastTimer)
|
||||
clearImmediate(s1)
|
||||
clearImmediate(s2)
|
||||
}
|
||||
}
|
||||
: (socketWeakRef, opts) => {
|
||||
if (!opts.timeout) {
|
||||
return noop
|
||||
}
|
||||
|
||||
let s1 = null
|
||||
const fastTimer = timers.setFastTimeout(() => {
|
||||
// setImmediate is added to make sure that we prioritize socket error events over timeouts
|
||||
s1 = setImmediate(() => {
|
||||
onConnectTimeout(socketWeakRef.deref(), opts)
|
||||
})
|
||||
}, opts.timeout)
|
||||
return () => {
|
||||
timers.clearFastTimeout(fastTimer)
|
||||
clearImmediate(s1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {net.Socket} socket
|
||||
* @param {object} opts
|
||||
* @param {number} opts.timeout
|
||||
* @param {string} opts.hostname
|
||||
* @param {number} opts.port
|
||||
*/
|
||||
function onConnectTimeout (socket, opts) {
|
||||
let message = 'Connect Timeout Error'
|
||||
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
|
||||
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')})`
|
||||
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`
|
||||
} else {
|
||||
message += ` (attempted address: ${opts.hostname}:${opts.port},`
|
||||
}
|
||||
|
||||
message += ` timeout: ${opts.timeout}ms)`
|
||||
|
||||
util.destroy(socket, new ConnectTimeoutError(message))
|
||||
}
|
||||
|
||||
|
||||
Vendored
+13
@@ -195,6 +195,18 @@ class RequestRetryError extends UndiciError {
|
||||
}
|
||||
}
|
||||
|
||||
class ResponseError extends UndiciError {
|
||||
constructor (message, code, { headers, data }) {
|
||||
super(message)
|
||||
this.name = 'ResponseError'
|
||||
this.message = message || 'Response error'
|
||||
this.code = 'UND_ERR_RESPONSE'
|
||||
this.statusCode = code
|
||||
this.data = data
|
||||
this.headers = headers
|
||||
}
|
||||
}
|
||||
|
||||
class SecureProxyConnectionError extends UndiciError {
|
||||
constructor (cause, message, options) {
|
||||
super(message, { cause, ...(options ?? {}) })
|
||||
@@ -227,5 +239,6 @@ module.exports = {
|
||||
BalancedPoolMissingUpstreamError,
|
||||
ResponseExceededMaxSizeError,
|
||||
RequestRetryError,
|
||||
ResponseError,
|
||||
SecureProxyConnectionError
|
||||
}
|
||||
Vendored
+1
-1
@@ -233,7 +233,7 @@ function getServerName (host) {
|
||||
return null
|
||||
}
|
||||
|
||||
assert.strictEqual(typeof host, 'string')
|
||||
assert(typeof host === 'string')
|
||||
|
||||
const servername = getHostname(host)
|
||||
if (net.isIP(servername)) {
|
||||
|
||||
+63
-43
@@ -85,35 +85,35 @@ async function lazyllhttp () {
|
||||
return 0
|
||||
},
|
||||
wasm_on_status: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p)
|
||||
assert(currentParser.ptr === p)
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset
|
||||
return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer, start, len)) || 0
|
||||
},
|
||||
wasm_on_message_begin: (p) => {
|
||||
assert.strictEqual(currentParser.ptr, p)
|
||||
assert(currentParser.ptr === p)
|
||||
return currentParser.onMessageBegin() || 0
|
||||
},
|
||||
wasm_on_header_field: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p)
|
||||
assert(currentParser.ptr === p)
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset
|
||||
return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer, start, len)) || 0
|
||||
},
|
||||
wasm_on_header_value: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p)
|
||||
assert(currentParser.ptr === p)
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset
|
||||
return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer, start, len)) || 0
|
||||
},
|
||||
wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => {
|
||||
assert.strictEqual(currentParser.ptr, p)
|
||||
assert(currentParser.ptr === p)
|
||||
return currentParser.onHeadersComplete(statusCode, Boolean(upgrade), Boolean(shouldKeepAlive)) || 0
|
||||
},
|
||||
wasm_on_body: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p)
|
||||
assert(currentParser.ptr === p)
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset
|
||||
return currentParser.onBody(new FastBuffer(currentBufferRef.buffer, start, len)) || 0
|
||||
},
|
||||
wasm_on_message_complete: (p) => {
|
||||
assert.strictEqual(currentParser.ptr, p)
|
||||
assert(currentParser.ptr === p)
|
||||
return currentParser.onMessageComplete() || 0
|
||||
}
|
||||
|
||||
@@ -131,9 +131,17 @@ let currentBufferRef = null
|
||||
let currentBufferSize = 0
|
||||
let currentBufferPtr = null
|
||||
|
||||
const TIMEOUT_HEADERS = 1
|
||||
const TIMEOUT_BODY = 2
|
||||
const TIMEOUT_IDLE = 3
|
||||
const USE_NATIVE_TIMER = 0
|
||||
const USE_FAST_TIMER = 1
|
||||
|
||||
// Use fast timers for headers and body to take eventual event loop
|
||||
// latency into account.
|
||||
const TIMEOUT_HEADERS = 2 | USE_FAST_TIMER
|
||||
const TIMEOUT_BODY = 4 | USE_FAST_TIMER
|
||||
|
||||
// Use native timers to ignore event loop latency for keep-alive
|
||||
// handling.
|
||||
const TIMEOUT_KEEP_ALIVE = 8 | USE_NATIVE_TIMER
|
||||
|
||||
class Parser {
|
||||
constructor (client, socket, { exports }) {
|
||||
@@ -164,26 +172,39 @@ class Parser {
|
||||
this.maxResponseSize = client[kMaxResponseSize]
|
||||
}
|
||||
|
||||
setTimeout (value, type) {
|
||||
this.timeoutType = type
|
||||
if (value !== this.timeoutValue) {
|
||||
timers.clearTimeout(this.timeout)
|
||||
if (value) {
|
||||
this.timeout = timers.setTimeout(onParserTimeout, value, this)
|
||||
// istanbul ignore else: only for jest
|
||||
if (this.timeout.unref) {
|
||||
this.timeout.unref()
|
||||
}
|
||||
} else {
|
||||
setTimeout (delay, type) {
|
||||
// If the existing timer and the new timer are of different timer type
|
||||
// (fast or native) or have different delay, we need to clear the existing
|
||||
// timer and set a new one.
|
||||
if (
|
||||
delay !== this.timeoutValue ||
|
||||
(type & USE_FAST_TIMER) ^ (this.timeoutType & USE_FAST_TIMER)
|
||||
) {
|
||||
// If a timeout is already set, clear it with clearTimeout of the fast
|
||||
// timer implementation, as it can clear fast and native timers.
|
||||
if (this.timeout) {
|
||||
timers.clearTimeout(this.timeout)
|
||||
this.timeout = null
|
||||
}
|
||||
this.timeoutValue = value
|
||||
|
||||
if (delay) {
|
||||
if (type & USE_FAST_TIMER) {
|
||||
this.timeout = timers.setFastTimeout(onParserTimeout, delay, new WeakRef(this))
|
||||
} else {
|
||||
this.timeout = setTimeout(onParserTimeout, delay, new WeakRef(this))
|
||||
this.timeout.unref()
|
||||
}
|
||||
}
|
||||
|
||||
this.timeoutValue = delay
|
||||
} else if (this.timeout) {
|
||||
// istanbul ignore else: only for jest
|
||||
if (this.timeout.refresh) {
|
||||
this.timeout.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
this.timeoutType = type
|
||||
}
|
||||
|
||||
resume () {
|
||||
@@ -288,7 +309,7 @@ class Parser {
|
||||
this.llhttp.llhttp_free(this.ptr)
|
||||
this.ptr = null
|
||||
|
||||
timers.clearTimeout(this.timeout)
|
||||
this.timeout && timers.clearTimeout(this.timeout)
|
||||
this.timeout = null
|
||||
this.timeoutValue = null
|
||||
this.timeoutType = null
|
||||
@@ -363,20 +384,19 @@ class Parser {
|
||||
const { upgrade, client, socket, headers, statusCode } = this
|
||||
|
||||
assert(upgrade)
|
||||
assert(client[kSocket] === socket)
|
||||
assert(!socket.destroyed)
|
||||
assert(!this.paused)
|
||||
assert((headers.length & 1) === 0)
|
||||
|
||||
const request = client[kQueue][client[kRunningIdx]]
|
||||
assert(request)
|
||||
|
||||
assert(!socket.destroyed)
|
||||
assert(socket === client[kSocket])
|
||||
assert(!this.paused)
|
||||
assert(request.upgrade || request.method === 'CONNECT')
|
||||
|
||||
this.statusCode = null
|
||||
this.statusText = ''
|
||||
this.shouldKeepAlive = null
|
||||
|
||||
assert(this.headers.length % 2 === 0)
|
||||
this.headers = []
|
||||
this.headersSize = 0
|
||||
|
||||
@@ -433,7 +453,7 @@ class Parser {
|
||||
return -1
|
||||
}
|
||||
|
||||
assert.strictEqual(this.timeoutType, TIMEOUT_HEADERS)
|
||||
assert(this.timeoutType === TIMEOUT_HEADERS)
|
||||
|
||||
this.statusCode = statusCode
|
||||
this.shouldKeepAlive = (
|
||||
@@ -466,7 +486,7 @@ class Parser {
|
||||
return 2
|
||||
}
|
||||
|
||||
assert(this.headers.length % 2 === 0)
|
||||
assert((this.headers.length & 1) === 0)
|
||||
this.headers = []
|
||||
this.headersSize = 0
|
||||
|
||||
@@ -523,7 +543,7 @@ class Parser {
|
||||
const request = client[kQueue][client[kRunningIdx]]
|
||||
assert(request)
|
||||
|
||||
assert.strictEqual(this.timeoutType, TIMEOUT_BODY)
|
||||
assert(this.timeoutType === TIMEOUT_BODY)
|
||||
if (this.timeout) {
|
||||
// istanbul ignore else: only for jest
|
||||
if (this.timeout.refresh) {
|
||||
@@ -556,11 +576,12 @@ class Parser {
|
||||
return
|
||||
}
|
||||
|
||||
assert(statusCode >= 100)
|
||||
assert((this.headers.length & 1) === 0)
|
||||
|
||||
const request = client[kQueue][client[kRunningIdx]]
|
||||
assert(request)
|
||||
|
||||
assert(statusCode >= 100)
|
||||
|
||||
this.statusCode = null
|
||||
this.statusText = ''
|
||||
this.bytesRead = 0
|
||||
@@ -568,7 +589,6 @@ class Parser {
|
||||
this.keepAlive = ''
|
||||
this.connection = ''
|
||||
|
||||
assert(this.headers.length % 2 === 0)
|
||||
this.headers = []
|
||||
this.headersSize = 0
|
||||
|
||||
@@ -587,7 +607,7 @@ class Parser {
|
||||
client[kQueue][client[kRunningIdx]++] = null
|
||||
|
||||
if (socket[kWriting]) {
|
||||
assert.strictEqual(client[kRunning], 0)
|
||||
assert(client[kRunning] === 0)
|
||||
// Response completed before request.
|
||||
util.destroy(socket, new InformationalError('reset'))
|
||||
return constants.ERROR.PAUSED
|
||||
@@ -613,19 +633,19 @@ class Parser {
|
||||
}
|
||||
|
||||
function onParserTimeout (parser) {
|
||||
const { socket, timeoutType, client } = parser
|
||||
const { socket, timeoutType, client, paused } = parser.deref()
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (timeoutType === TIMEOUT_HEADERS) {
|
||||
if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) {
|
||||
assert(!parser.paused, 'cannot be paused while waiting for headers')
|
||||
assert(!paused, 'cannot be paused while waiting for headers')
|
||||
util.destroy(socket, new HeadersTimeoutError())
|
||||
}
|
||||
} else if (timeoutType === TIMEOUT_BODY) {
|
||||
if (!parser.paused) {
|
||||
if (!paused) {
|
||||
util.destroy(socket, new BodyTimeoutError())
|
||||
}
|
||||
} else if (timeoutType === TIMEOUT_IDLE) {
|
||||
} else if (timeoutType === TIMEOUT_KEEP_ALIVE) {
|
||||
assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue])
|
||||
util.destroy(socket, new InformationalError('socket idle timeout'))
|
||||
}
|
||||
@@ -646,10 +666,10 @@ async function connectH1 (client, socket) {
|
||||
socket[kParser] = new Parser(client, socket, llhttpInstance)
|
||||
|
||||
addListener(socket, 'error', function (err) {
|
||||
const parser = this[kParser]
|
||||
|
||||
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
|
||||
|
||||
const parser = this[kParser]
|
||||
|
||||
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
|
||||
// to the user.
|
||||
if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) {
|
||||
@@ -803,8 +823,8 @@ function resumeH1 (client) {
|
||||
}
|
||||
|
||||
if (client[kSize] === 0) {
|
||||
if (socket[kParser].timeoutType !== TIMEOUT_IDLE) {
|
||||
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_IDLE)
|
||||
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
|
||||
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE)
|
||||
}
|
||||
} else if (client[kRunning] > 0 && socket[kParser].statusCode < 200) {
|
||||
if (socket[kParser].timeoutType !== TIMEOUT_HEADERS) {
|
||||
|
||||
+4
@@ -385,6 +385,10 @@ function onError (client, err) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Client} client
|
||||
* @returns
|
||||
*/
|
||||
async function connect (client) {
|
||||
assert(!client[kConnecting])
|
||||
assert(!client[kHTTPContext])
|
||||
|
||||
+12
-2
@@ -192,8 +192,18 @@ class RetryHandler {
|
||||
if (this.resume != null) {
|
||||
this.resume = null
|
||||
|
||||
if (statusCode !== 206) {
|
||||
return true
|
||||
// Only Partial Content 206 supposed to provide Content-Range,
|
||||
// any other status code that partially consumed the payload
|
||||
// should not be retry because it would result in downstream
|
||||
// wrongly concatanete multiple responses.
|
||||
if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {
|
||||
this.abort(
|
||||
new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
|
||||
headers,
|
||||
data: { count: this.retryCount }
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
const contentRange = parseRangeHeader(headers['content-range'])
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
'use strict'
|
||||
|
||||
const { parseHeaders } = require('../core/util')
|
||||
const DecoratorHandler = require('../handler/decorator-handler')
|
||||
const { ResponseError } = require('../core/errors')
|
||||
|
||||
class Handler extends DecoratorHandler {
|
||||
#handler
|
||||
#statusCode
|
||||
#contentType
|
||||
#decoder
|
||||
#headers
|
||||
#body
|
||||
|
||||
constructor (opts, { handler }) {
|
||||
super(handler)
|
||||
this.#handler = handler
|
||||
}
|
||||
|
||||
onConnect (abort) {
|
||||
this.#statusCode = 0
|
||||
this.#contentType = null
|
||||
this.#decoder = null
|
||||
this.#headers = null
|
||||
this.#body = ''
|
||||
|
||||
return this.#handler.onConnect(abort)
|
||||
}
|
||||
|
||||
onHeaders (statusCode, rawHeaders, resume, statusMessage, headers = parseHeaders(rawHeaders)) {
|
||||
this.#statusCode = statusCode
|
||||
this.#headers = headers
|
||||
this.#contentType = headers['content-type']
|
||||
|
||||
if (this.#statusCode < 400) {
|
||||
return this.#handler.onHeaders(statusCode, rawHeaders, resume, statusMessage, headers)
|
||||
}
|
||||
|
||||
if (this.#contentType === 'application/json' || this.#contentType === 'text/plain') {
|
||||
this.#decoder = new TextDecoder('utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
onData (chunk) {
|
||||
if (this.#statusCode < 400) {
|
||||
return this.#handler.onData(chunk)
|
||||
}
|
||||
|
||||
this.#body += this.#decoder?.decode(chunk, { stream: true }) ?? ''
|
||||
}
|
||||
|
||||
onComplete (rawTrailers) {
|
||||
if (this.#statusCode >= 400) {
|
||||
this.#body += this.#decoder?.decode(undefined, { stream: false }) ?? ''
|
||||
|
||||
if (this.#contentType === 'application/json') {
|
||||
try {
|
||||
this.#body = JSON.parse(this.#body)
|
||||
} catch {
|
||||
// Do nothing...
|
||||
}
|
||||
}
|
||||
|
||||
let err
|
||||
const stackTraceLimit = Error.stackTraceLimit
|
||||
Error.stackTraceLimit = 0
|
||||
try {
|
||||
err = new ResponseError('Response Error', this.#statusCode, this.#headers, this.#body)
|
||||
} finally {
|
||||
Error.stackTraceLimit = stackTraceLimit
|
||||
}
|
||||
|
||||
this.#handler.onError(err)
|
||||
} else {
|
||||
this.#handler.onComplete(rawTrailers)
|
||||
}
|
||||
}
|
||||
|
||||
onError (err) {
|
||||
this.#handler.onError(err)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (dispatch) => (opts, handler) => opts.throwOnError
|
||||
? dispatch(opts, new Handler(opts, { handler }))
|
||||
: dispatch(opts, handler)
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
|
||||
> undici@6.19.8 prebuild:wasm
|
||||
> undici@6.20.0 prebuild:wasm
|
||||
> node build/wasm.js --prebuild
|
||||
|
||||
> docker build --platform=linux/x86_64 -t llhttp_wasm_builder -f /home/runner/work/node/node/deps/undici/src/build/Dockerfile /home/runner/work/node/node/deps/undici/src
|
||||
|
||||
|
||||
|
||||
> undici@6.19.8 build:wasm
|
||||
> undici@6.20.0 build:wasm
|
||||
> node build/wasm.js --docker
|
||||
|
||||
> docker run --rm -t --platform=linux/x86_64 --user 1001:127 --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/lib/llhttp,target=/home/node/undici/lib/llhttp llhttp_wasm_builder node build/wasm.js
|
||||
|
||||
+4
@@ -118,6 +118,10 @@ function matchKey (mockDispatch, { path, method, body, headers }) {
|
||||
function getResponseData (data) {
|
||||
if (Buffer.isBuffer(data)) {
|
||||
return data
|
||||
} else if (data instanceof Uint8Array) {
|
||||
return data
|
||||
} else if (data instanceof ArrayBuffer) {
|
||||
return data
|
||||
} else if (typeof data === 'object') {
|
||||
return JSON.stringify(data)
|
||||
} else {
|
||||
|
||||
Vendored
+367
-43
@@ -1,99 +1,423 @@
|
||||
'use strict'
|
||||
|
||||
const TICK_MS = 499
|
||||
/**
|
||||
* This module offers an optimized timer implementation designed for scenarios
|
||||
* where high precision is not critical.
|
||||
*
|
||||
* The timer achieves faster performance by using a low-resolution approach,
|
||||
* with an accuracy target of within 500ms. This makes it particularly useful
|
||||
* for timers with delays of 1 second or more, where exact timing is less
|
||||
* crucial.
|
||||
*
|
||||
* It's important to note that Node.js timers are inherently imprecise, as
|
||||
* delays can occur due to the event loop being blocked by other operations.
|
||||
* Consequently, timers may trigger later than their scheduled time.
|
||||
*/
|
||||
|
||||
let fastNow = Date.now()
|
||||
/**
|
||||
* The fastNow variable contains the internal fast timer clock value.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
let fastNow = 0
|
||||
|
||||
/**
|
||||
* RESOLUTION_MS represents the target resolution time in milliseconds.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1000
|
||||
*/
|
||||
const RESOLUTION_MS = 1e3
|
||||
|
||||
/**
|
||||
* TICK_MS defines the desired interval in milliseconds between each tick.
|
||||
* The target value is set to half the resolution time, minus 1 ms, to account
|
||||
* for potential event loop overhead.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 499
|
||||
*/
|
||||
const TICK_MS = (RESOLUTION_MS >> 1) - 1
|
||||
|
||||
/**
|
||||
* fastNowTimeout is a Node.js timer used to manage and process
|
||||
* the FastTimers stored in the `fastTimers` array.
|
||||
*
|
||||
* @type {NodeJS.Timeout}
|
||||
*/
|
||||
let fastNowTimeout
|
||||
|
||||
/**
|
||||
* The kFastTimer symbol is used to identify FastTimer instances.
|
||||
*
|
||||
* @type {Symbol}
|
||||
*/
|
||||
const kFastTimer = Symbol('kFastTimer')
|
||||
|
||||
/**
|
||||
* The fastTimers array contains all active FastTimers.
|
||||
*
|
||||
* @type {FastTimer[]}
|
||||
*/
|
||||
const fastTimers = []
|
||||
|
||||
function onTimeout () {
|
||||
fastNow = Date.now()
|
||||
/**
|
||||
* These constants represent the various states of a FastTimer.
|
||||
*/
|
||||
|
||||
let len = fastTimers.length
|
||||
/**
|
||||
* The `NOT_IN_LIST` constant indicates that the FastTimer is not included
|
||||
* in the `fastTimers` array. Timers with this status will not be processed
|
||||
* during the next tick by the `onTick` function.
|
||||
*
|
||||
* A FastTimer can be re-added to the `fastTimers` array by invoking the
|
||||
* `refresh` method on the FastTimer instance.
|
||||
*
|
||||
* @type {-2}
|
||||
*/
|
||||
const NOT_IN_LIST = -2
|
||||
|
||||
/**
|
||||
* The `TO_BE_CLEARED` constant indicates that the FastTimer is scheduled
|
||||
* for removal from the `fastTimers` array. A FastTimer in this state will
|
||||
* be removed in the next tick by the `onTick` function and will no longer
|
||||
* be processed.
|
||||
*
|
||||
* This status is also set when the `clear` method is called on the FastTimer instance.
|
||||
*
|
||||
* @type {-1}
|
||||
*/
|
||||
const TO_BE_CLEARED = -1
|
||||
|
||||
/**
|
||||
* The `PENDING` constant signifies that the FastTimer is awaiting processing
|
||||
* in the next tick by the `onTick` function. Timers with this status will have
|
||||
* their `_idleStart` value set and their status updated to `ACTIVE` in the next tick.
|
||||
*
|
||||
* @type {0}
|
||||
*/
|
||||
const PENDING = 0
|
||||
|
||||
/**
|
||||
* The `ACTIVE` constant indicates that the FastTimer is active and waiting
|
||||
* for its timer to expire. During the next tick, the `onTick` function will
|
||||
* check if the timer has expired, and if so, it will execute the associated callback.
|
||||
*
|
||||
* @type {1}
|
||||
*/
|
||||
const ACTIVE = 1
|
||||
|
||||
/**
|
||||
* The onTick function processes the fastTimers array.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function onTick () {
|
||||
/**
|
||||
* Increment the fastNow value by the TICK_MS value, despite the actual time
|
||||
* that has passed since the last tick. This approach ensures independence
|
||||
* from the system clock and delays caused by a blocked event loop.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
fastNow += TICK_MS
|
||||
|
||||
/**
|
||||
* The `idx` variable is used to iterate over the `fastTimers` array.
|
||||
* Expired timers are removed by replacing them with the last element in the array.
|
||||
* Consequently, `idx` is only incremented when the current element is not removed.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
let idx = 0
|
||||
|
||||
/**
|
||||
* The len variable will contain the length of the fastTimers array
|
||||
* and will be decremented when a FastTimer should be removed from the
|
||||
* fastTimers array.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
let len = fastTimers.length
|
||||
|
||||
while (idx < len) {
|
||||
/**
|
||||
* @type {FastTimer}
|
||||
*/
|
||||
const timer = fastTimers[idx]
|
||||
|
||||
if (timer.state === 0) {
|
||||
timer.state = fastNow + timer.delay - TICK_MS
|
||||
} else if (timer.state > 0 && fastNow >= timer.state) {
|
||||
timer.state = -1
|
||||
timer.callback(timer.opaque)
|
||||
// If the timer is in the ACTIVE state and the timer has expired, it will
|
||||
// be processed in the next tick.
|
||||
if (timer._state === PENDING) {
|
||||
// Set the _idleStart value to the fastNow value minus the TICK_MS value
|
||||
// to account for the time the timer was in the PENDING state.
|
||||
timer._idleStart = fastNow - TICK_MS
|
||||
timer._state = ACTIVE
|
||||
} else if (
|
||||
timer._state === ACTIVE &&
|
||||
fastNow >= timer._idleStart + timer._idleTimeout
|
||||
) {
|
||||
timer._state = TO_BE_CLEARED
|
||||
timer._idleStart = -1
|
||||
timer._onTimeout(timer._timerArg)
|
||||
}
|
||||
|
||||
if (timer.state === -1) {
|
||||
timer.state = -2
|
||||
if (idx !== len - 1) {
|
||||
fastTimers[idx] = fastTimers.pop()
|
||||
} else {
|
||||
fastTimers.pop()
|
||||
if (timer._state === TO_BE_CLEARED) {
|
||||
timer._state = NOT_IN_LIST
|
||||
|
||||
// Move the last element to the current index and decrement len if it is
|
||||
// not the only element in the array.
|
||||
if (--len !== 0) {
|
||||
fastTimers[idx] = fastTimers[len]
|
||||
}
|
||||
len -= 1
|
||||
} else {
|
||||
idx += 1
|
||||
++idx
|
||||
}
|
||||
}
|
||||
|
||||
if (fastTimers.length > 0) {
|
||||
// Set the length of the fastTimers array to the new length and thus
|
||||
// removing the excess FastTimers elements from the array.
|
||||
fastTimers.length = len
|
||||
|
||||
// If there are still active FastTimers in the array, refresh the Timer.
|
||||
// If there are no active FastTimers, the timer will be refreshed again
|
||||
// when a new FastTimer is instantiated.
|
||||
if (fastTimers.length !== 0) {
|
||||
refreshTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
function refreshTimeout () {
|
||||
if (fastNowTimeout?.refresh) {
|
||||
// If the fastNowTimeout is already set, refresh it.
|
||||
if (fastNowTimeout) {
|
||||
fastNowTimeout.refresh()
|
||||
// fastNowTimeout is not instantiated yet, create a new Timer.
|
||||
} else {
|
||||
clearTimeout(fastNowTimeout)
|
||||
fastNowTimeout = setTimeout(onTimeout, TICK_MS)
|
||||
fastNowTimeout = setTimeout(onTick, TICK_MS)
|
||||
|
||||
// If the Timer has an unref method, call it to allow the process to exit if
|
||||
// there are no other active handles.
|
||||
if (fastNowTimeout.unref) {
|
||||
fastNowTimeout.unref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Timeout {
|
||||
constructor (callback, delay, opaque) {
|
||||
this.callback = callback
|
||||
this.delay = delay
|
||||
this.opaque = opaque
|
||||
/**
|
||||
* The `FastTimer` class is a data structure designed to store and manage
|
||||
* timer information.
|
||||
*/
|
||||
class FastTimer {
|
||||
[kFastTimer] = true
|
||||
|
||||
// -2 not in timer list
|
||||
// -1 in timer list but inactive
|
||||
// 0 in timer list waiting for time
|
||||
// > 0 in timer list waiting for time to expire
|
||||
this.state = -2
|
||||
/**
|
||||
* The state of the timer, which can be one of the following:
|
||||
* - NOT_IN_LIST (-2)
|
||||
* - TO_BE_CLEARED (-1)
|
||||
* - PENDING (0)
|
||||
* - ACTIVE (1)
|
||||
*
|
||||
* @type {-2|-1|0|1}
|
||||
* @private
|
||||
*/
|
||||
_state = NOT_IN_LIST
|
||||
|
||||
/**
|
||||
* The number of milliseconds to wait before calling the callback.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
_idleTimeout = -1
|
||||
|
||||
/**
|
||||
* The time in milliseconds when the timer was started. This value is used to
|
||||
* calculate when the timer should expire.
|
||||
*
|
||||
* @type {number}
|
||||
* @default -1
|
||||
* @private
|
||||
*/
|
||||
_idleStart = -1
|
||||
|
||||
/**
|
||||
* The function to be executed when the timer expires.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
_onTimeout
|
||||
|
||||
/**
|
||||
* The argument to be passed to the callback when the timer expires.
|
||||
*
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
_timerArg
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should wait
|
||||
* before the specified function or code is executed.
|
||||
* @param {*} arg
|
||||
*/
|
||||
constructor (callback, delay, arg) {
|
||||
this._onTimeout = callback
|
||||
this._idleTimeout = delay
|
||||
this._timerArg = arg
|
||||
|
||||
this.refresh()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timer's start time to the current time, and reschedules the timer
|
||||
* to call its callback at the previously specified duration adjusted to the
|
||||
* current time.
|
||||
* Using this on a timer that has already called its callback will reactivate
|
||||
* the timer.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
refresh () {
|
||||
if (this.state === -2) {
|
||||
// In the special case that the timer is not in the list of active timers,
|
||||
// add it back to the array to be processed in the next tick by the onTick
|
||||
// function.
|
||||
if (this._state === NOT_IN_LIST) {
|
||||
fastTimers.push(this)
|
||||
if (!fastNowTimeout || fastTimers.length === 1) {
|
||||
refreshTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
this.state = 0
|
||||
// If the timer is the only active timer, refresh the fastNowTimeout for
|
||||
// better resolution.
|
||||
if (!fastNowTimeout || fastTimers.length === 1) {
|
||||
refreshTimeout()
|
||||
}
|
||||
|
||||
// Setting the state to PENDING will cause the timer to be reset in the
|
||||
// next tick by the onTick function.
|
||||
this._state = PENDING
|
||||
}
|
||||
|
||||
/**
|
||||
* The `clear` method cancels the timer, preventing it from executing.
|
||||
*
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
clear () {
|
||||
this.state = -1
|
||||
// Set the state to TO_BE_CLEARED to mark the timer for removal in the next
|
||||
// tick by the onTick function.
|
||||
this._state = TO_BE_CLEARED
|
||||
|
||||
// Reset the _idleStart value to -1 to indicate that the timer is no longer
|
||||
// active.
|
||||
this._idleStart = -1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This module exports a setTimeout and clearTimeout function that can be
|
||||
* used as a drop-in replacement for the native functions.
|
||||
*/
|
||||
module.exports = {
|
||||
setTimeout (callback, delay, opaque) {
|
||||
return delay <= 1e3
|
||||
? setTimeout(callback, delay, opaque)
|
||||
: new Timeout(callback, delay, opaque)
|
||||
/**
|
||||
* The setTimeout() method sets a timer which executes a function once the
|
||||
* timer expires.
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should
|
||||
* wait before the specified function or code is executed.
|
||||
* @param {*} [arg] An optional argument to be passed to the callback function
|
||||
* when the timer expires.
|
||||
* @returns {NodeJS.Timeout|FastTimer}
|
||||
*/
|
||||
setTimeout (callback, delay, arg) {
|
||||
// If the delay is less than or equal to the RESOLUTION_MS value return a
|
||||
// native Node.js Timer instance.
|
||||
return delay <= RESOLUTION_MS
|
||||
? setTimeout(callback, delay, arg)
|
||||
: new FastTimer(callback, delay, arg)
|
||||
},
|
||||
/**
|
||||
* The clearTimeout method cancels an instantiated Timer previously created
|
||||
* by calling setTimeout.
|
||||
*
|
||||
* @param {NodeJS.Timeout|FastTimer} timeout
|
||||
*/
|
||||
clearTimeout (timeout) {
|
||||
if (timeout instanceof Timeout) {
|
||||
// If the timeout is a FastTimer, call its own clear method.
|
||||
if (timeout[kFastTimer]) {
|
||||
/**
|
||||
* @type {FastTimer}
|
||||
*/
|
||||
timeout.clear()
|
||||
// Otherwise it is an instance of a native NodeJS.Timeout, so call the
|
||||
// Node.js native clearTimeout function.
|
||||
} else {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The setFastTimeout() method sets a fastTimer which executes a function once
|
||||
* the timer expires.
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should
|
||||
* wait before the specified function or code is executed.
|
||||
* @param {*} [arg] An optional argument to be passed to the callback function
|
||||
* when the timer expires.
|
||||
* @returns {FastTimer}
|
||||
*/
|
||||
setFastTimeout (callback, delay, arg) {
|
||||
return new FastTimer(callback, delay, arg)
|
||||
},
|
||||
/**
|
||||
* The clearTimeout method cancels an instantiated FastTimer previously
|
||||
* created by calling setFastTimeout.
|
||||
*
|
||||
* @param {FastTimer} timeout
|
||||
*/
|
||||
clearFastTimeout (timeout) {
|
||||
timeout.clear()
|
||||
},
|
||||
/**
|
||||
* The now method returns the value of the internal fast timer clock.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
now () {
|
||||
return fastNow
|
||||
},
|
||||
/**
|
||||
* Trigger the onTick function to process the fastTimers array.
|
||||
* Exported for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
* @param {number} [delay=0] The delay in milliseconds to add to the now value.
|
||||
*/
|
||||
tick (delay = 0) {
|
||||
fastNow += delay - RESOLUTION_MS + 1
|
||||
onTick()
|
||||
onTick()
|
||||
},
|
||||
/**
|
||||
* Reset FastTimers.
|
||||
* Exported for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
*/
|
||||
reset () {
|
||||
fastNow = 0
|
||||
fastTimers.length = 0
|
||||
clearTimeout(fastNowTimeout)
|
||||
fastNowTimeout = null
|
||||
},
|
||||
/**
|
||||
* Exporting for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
*/
|
||||
kFastTimer
|
||||
}
|
||||
+12
-2
@@ -87,11 +87,21 @@ function multipartFormDataParser (input, mimeType) {
|
||||
// the first byte.
|
||||
const position = { position: 0 }
|
||||
|
||||
// Note: undici addition, allow \r\n before the body.
|
||||
if (input[0] === 0x0d && input[1] === 0x0a) {
|
||||
// Note: undici addition, allows leading and trailing CRLFs.
|
||||
while (input[position.position] === 0x0d && input[position.position + 1] === 0x0a) {
|
||||
position.position += 2
|
||||
}
|
||||
|
||||
let trailing = input.length
|
||||
|
||||
while (input[trailing - 1] === 0x0a && input[trailing - 2] === 0x0d) {
|
||||
trailing -= 2
|
||||
}
|
||||
|
||||
if (trailing !== input.length) {
|
||||
input = input.subarray(0, trailing)
|
||||
}
|
||||
|
||||
// 5. While true:
|
||||
while (true) {
|
||||
// 5.1. If position points to a sequence of bytes starting with 0x2D 0x2D
|
||||
|
||||
+16
-4
@@ -2150,9 +2150,15 @@ async function httpNetworkFetch (
|
||||
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
||||
}))
|
||||
} else if (coding === 'deflate') {
|
||||
decoders.push(createInflate())
|
||||
decoders.push(createInflate({
|
||||
flush: zlib.constants.Z_SYNC_FLUSH,
|
||||
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
||||
}))
|
||||
} else if (coding === 'br') {
|
||||
decoders.push(zlib.createBrotliDecompress())
|
||||
decoders.push(zlib.createBrotliDecompress({
|
||||
flush: zlib.constants.BROTLI_OPERATION_FLUSH,
|
||||
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
|
||||
}))
|
||||
} else {
|
||||
decoders.length = 0
|
||||
break
|
||||
@@ -2160,13 +2166,19 @@ async function httpNetworkFetch (
|
||||
}
|
||||
}
|
||||
|
||||
const onError = this.onError.bind(this)
|
||||
|
||||
resolve({
|
||||
status,
|
||||
statusText,
|
||||
headersList,
|
||||
body: decoders.length
|
||||
? pipeline(this.body, ...decoders, () => { })
|
||||
: this.body.on('error', () => { })
|
||||
? pipeline(this.body, ...decoders, (err) => {
|
||||
if (err) {
|
||||
this.onError(err)
|
||||
}
|
||||
}).on('error', onError)
|
||||
: this.body.on('error', onError)
|
||||
})
|
||||
|
||||
return true
|
||||
|
||||
+16
-4
@@ -1340,6 +1340,14 @@ function buildContentRange (rangeStart, rangeEnd, fullLength) {
|
||||
// interpreted as a zlib stream, otherwise it's interpreted as a
|
||||
// raw deflate stream.
|
||||
class InflateStream extends Transform {
|
||||
#zlibOptions
|
||||
|
||||
/** @param {zlib.ZlibOptions} [zlibOptions] */
|
||||
constructor (zlibOptions) {
|
||||
super()
|
||||
this.#zlibOptions = zlibOptions
|
||||
}
|
||||
|
||||
_transform (chunk, encoding, callback) {
|
||||
if (!this._inflateStream) {
|
||||
if (chunk.length === 0) {
|
||||
@@ -1347,8 +1355,8 @@ class InflateStream extends Transform {
|
||||
return
|
||||
}
|
||||
this._inflateStream = (chunk[0] & 0x0F) === 0x08
|
||||
? zlib.createInflate()
|
||||
: zlib.createInflateRaw()
|
||||
? zlib.createInflate(this.#zlibOptions)
|
||||
: zlib.createInflateRaw(this.#zlibOptions)
|
||||
|
||||
this._inflateStream.on('data', this.push.bind(this))
|
||||
this._inflateStream.on('end', () => this.push(null))
|
||||
@@ -1367,8 +1375,12 @@ class InflateStream extends Transform {
|
||||
}
|
||||
}
|
||||
|
||||
function createInflate () {
|
||||
return new InflateStream()
|
||||
/**
|
||||
* @param {zlib.ZlibOptions} [zlibOptions]
|
||||
* @returns {InflateStream}
|
||||
*/
|
||||
function createInflate (zlibOptions) {
|
||||
return new InflateStream(zlibOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+284
-271
File diff suppressed because it is too large.
Load diff
Vendored
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "undici",
|
||||
"version": "6.19.8",
|
||||
"version": "6.20.0",
|
||||
"description": "An HTTP/1.1 client, written from scratch for Node.js",
|
||||
"homepage": "https://undici.nodejs.org",
|
||||
"bugs": {
|
||||
@@ -105,7 +105,7 @@
|
||||
"@fastify/busboy": "2.1.1",
|
||||
"@matteo.collina/tspl": "^0.1.1",
|
||||
"@sinonjs/fake-timers": "^11.1.0",
|
||||
"@types/node": "^18.0.3",
|
||||
"@types/node": "~18.19.50",
|
||||
"abort-controller": "^3.0.0",
|
||||
"borp": "^0.15.0",
|
||||
"c8": "^10.0.0",
|
||||
|
||||
-2
@@ -2,8 +2,6 @@ import { MessageEvent, ErrorEvent } from './websocket'
|
||||
import Dispatcher from './dispatcher'
|
||||
|
||||
import {
|
||||
EventTarget,
|
||||
Event,
|
||||
EventListenerOptions,
|
||||
AddEventListenerOptions,
|
||||
EventListenerOrEventListenerObject
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/// <reference types="node" />
|
||||
|
||||
import { Blob } from 'buffer'
|
||||
import { DOMException, Event, EventInit, EventTarget } from './patch'
|
||||
import { DOMException, EventInit } from './patch'
|
||||
|
||||
export declare class FileReader {
|
||||
__proto__: EventTarget & FileReader
|
||||
|
||||
+3
-1
@@ -7,9 +7,11 @@ declare namespace Interceptors {
|
||||
export type DumpInterceptorOpts = { maxSize?: number }
|
||||
export type RetryInterceptorOpts = RetryHandler.RetryOptions
|
||||
export type RedirectInterceptorOpts = { maxRedirections?: number }
|
||||
|
||||
export type ResponseErrorInterceptorOpts = { throwOnError: boolean }
|
||||
|
||||
export function createRedirectInterceptor(opts: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
||||
export function dump(opts?: DumpInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
||||
export function retry(opts?: RetryInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
||||
export function redirect(opts?: RedirectInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
||||
export function responseError(opts?: ResponseErrorInterceptorOpts): Dispatcher.DispatcherComposeInterceptor
|
||||
}
|
||||
Vendored
-38
@@ -6,44 +6,6 @@ export type DOMException = typeof globalThis extends { DOMException: infer T }
|
||||
? T
|
||||
: any
|
||||
|
||||
export type EventTarget = typeof globalThis extends { EventTarget: infer T }
|
||||
? T
|
||||
: {
|
||||
addEventListener(
|
||||
type: string,
|
||||
listener: any,
|
||||
options?: any,
|
||||
): void
|
||||
dispatchEvent(event: Event): boolean
|
||||
removeEventListener(
|
||||
type: string,
|
||||
listener: any,
|
||||
options?: any | boolean,
|
||||
): void
|
||||
}
|
||||
|
||||
export type Event = typeof globalThis extends { Event: infer T }
|
||||
? T
|
||||
: {
|
||||
readonly bubbles: boolean
|
||||
cancelBubble: () => void
|
||||
readonly cancelable: boolean
|
||||
readonly composed: boolean
|
||||
composedPath(): [EventTarget?]
|
||||
readonly currentTarget: EventTarget | null
|
||||
readonly defaultPrevented: boolean
|
||||
readonly eventPhase: 0 | 2
|
||||
readonly isTrusted: boolean
|
||||
preventDefault(): void
|
||||
returnValue: boolean
|
||||
readonly srcElement: EventTarget | null
|
||||
stopImmediatePropagation(): void
|
||||
stopPropagation(): void
|
||||
readonly target: EventTarget | null
|
||||
readonly timeStamp: number
|
||||
readonly type: string
|
||||
}
|
||||
|
||||
export interface EventInit {
|
||||
bubbles?: boolean
|
||||
cancelable?: boolean
|
||||
|
||||
Vendored
-2
@@ -3,8 +3,6 @@
|
||||
import type { Blob } from 'buffer'
|
||||
import type { MessagePort } from 'worker_threads'
|
||||
import {
|
||||
EventTarget,
|
||||
Event,
|
||||
EventInit,
|
||||
EventListenerOptions,
|
||||
AddEventListenerOptions,
|
||||
|
||||
Vendored
+366
-159
@@ -247,6 +247,20 @@ var require_errors = __commonJS({
|
||||
this.headers = headers;
|
||||
}
|
||||
};
|
||||
var ResponseError = class extends UndiciError {
|
||||
static {
|
||||
__name(this, "ResponseError");
|
||||
}
|
||||
constructor(message, code, { headers, data }) {
|
||||
super(message);
|
||||
this.name = "ResponseError";
|
||||
this.message = message || "Response error";
|
||||
this.code = "UND_ERR_RESPONSE";
|
||||
this.statusCode = code;
|
||||
this.data = data;
|
||||
this.headers = headers;
|
||||
}
|
||||
};
|
||||
var SecureProxyConnectionError = class extends UndiciError {
|
||||
static {
|
||||
__name(this, "SecureProxyConnectionError");
|
||||
@@ -281,6 +295,7 @@ var require_errors = __commonJS({
|
||||
BalancedPoolMissingUpstreamError,
|
||||
ResponseExceededMaxSizeError,
|
||||
RequestRetryError,
|
||||
ResponseError,
|
||||
SecureProxyConnectionError
|
||||
};
|
||||
}
|
||||
@@ -1266,7 +1281,7 @@ var require_util = __commonJS({
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
assert.strictEqual(typeof host, "string");
|
||||
assert(typeof host === "string");
|
||||
const servername = getHostname(host);
|
||||
if (net.isIP(servername)) {
|
||||
return "";
|
||||
@@ -2166,6 +2181,242 @@ var require_request = __commonJS({
|
||||
}
|
||||
});
|
||||
|
||||
// lib/util/timers.js
|
||||
var require_timers = __commonJS({
|
||||
"lib/util/timers.js"(exports2, module2) {
|
||||
"use strict";
|
||||
var fastNow = 0;
|
||||
var RESOLUTION_MS = 1e3;
|
||||
var TICK_MS = (RESOLUTION_MS >> 1) - 1;
|
||||
var fastNowTimeout;
|
||||
var kFastTimer = Symbol("kFastTimer");
|
||||
var fastTimers = [];
|
||||
var NOT_IN_LIST = -2;
|
||||
var TO_BE_CLEARED = -1;
|
||||
var PENDING = 0;
|
||||
var ACTIVE = 1;
|
||||
function onTick() {
|
||||
fastNow += TICK_MS;
|
||||
let idx = 0;
|
||||
let len = fastTimers.length;
|
||||
while (idx < len) {
|
||||
const timer = fastTimers[idx];
|
||||
if (timer._state === PENDING) {
|
||||
timer._idleStart = fastNow - TICK_MS;
|
||||
timer._state = ACTIVE;
|
||||
} else if (timer._state === ACTIVE && fastNow >= timer._idleStart + timer._idleTimeout) {
|
||||
timer._state = TO_BE_CLEARED;
|
||||
timer._idleStart = -1;
|
||||
timer._onTimeout(timer._timerArg);
|
||||
}
|
||||
if (timer._state === TO_BE_CLEARED) {
|
||||
timer._state = NOT_IN_LIST;
|
||||
if (--len !== 0) {
|
||||
fastTimers[idx] = fastTimers[len];
|
||||
}
|
||||
} else {
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
fastTimers.length = len;
|
||||
if (fastTimers.length !== 0) {
|
||||
refreshTimeout();
|
||||
}
|
||||
}
|
||||
__name(onTick, "onTick");
|
||||
function refreshTimeout() {
|
||||
if (fastNowTimeout) {
|
||||
fastNowTimeout.refresh();
|
||||
} else {
|
||||
clearTimeout(fastNowTimeout);
|
||||
fastNowTimeout = setTimeout(onTick, TICK_MS);
|
||||
if (fastNowTimeout.unref) {
|
||||
fastNowTimeout.unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
__name(refreshTimeout, "refreshTimeout");
|
||||
var FastTimer = class {
|
||||
static {
|
||||
__name(this, "FastTimer");
|
||||
}
|
||||
[kFastTimer] = true;
|
||||
/**
|
||||
* The state of the timer, which can be one of the following:
|
||||
* - NOT_IN_LIST (-2)
|
||||
* - TO_BE_CLEARED (-1)
|
||||
* - PENDING (0)
|
||||
* - ACTIVE (1)
|
||||
*
|
||||
* @type {-2|-1|0|1}
|
||||
* @private
|
||||
*/
|
||||
_state = NOT_IN_LIST;
|
||||
/**
|
||||
* The number of milliseconds to wait before calling the callback.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
_idleTimeout = -1;
|
||||
/**
|
||||
* The time in milliseconds when the timer was started. This value is used to
|
||||
* calculate when the timer should expire.
|
||||
*
|
||||
* @type {number}
|
||||
* @default -1
|
||||
* @private
|
||||
*/
|
||||
_idleStart = -1;
|
||||
/**
|
||||
* The function to be executed when the timer expires.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
_onTimeout;
|
||||
/**
|
||||
* The argument to be passed to the callback when the timer expires.
|
||||
*
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
_timerArg;
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should wait
|
||||
* before the specified function or code is executed.
|
||||
* @param {*} arg
|
||||
*/
|
||||
constructor(callback, delay, arg) {
|
||||
this._onTimeout = callback;
|
||||
this._idleTimeout = delay;
|
||||
this._timerArg = arg;
|
||||
this.refresh();
|
||||
}
|
||||
/**
|
||||
* Sets the timer's start time to the current time, and reschedules the timer
|
||||
* to call its callback at the previously specified duration adjusted to the
|
||||
* current time.
|
||||
* Using this on a timer that has already called its callback will reactivate
|
||||
* the timer.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
refresh() {
|
||||
if (this._state === NOT_IN_LIST) {
|
||||
fastTimers.push(this);
|
||||
}
|
||||
if (!fastNowTimeout || fastTimers.length === 1) {
|
||||
refreshTimeout();
|
||||
}
|
||||
this._state = PENDING;
|
||||
}
|
||||
/**
|
||||
* The `clear` method cancels the timer, preventing it from executing.
|
||||
*
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
clear() {
|
||||
this._state = TO_BE_CLEARED;
|
||||
this._idleStart = -1;
|
||||
}
|
||||
};
|
||||
module2.exports = {
|
||||
/**
|
||||
* The setTimeout() method sets a timer which executes a function once the
|
||||
* timer expires.
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should
|
||||
* wait before the specified function or code is executed.
|
||||
* @param {*} [arg] An optional argument to be passed to the callback function
|
||||
* when the timer expires.
|
||||
* @returns {NodeJS.Timeout|FastTimer}
|
||||
*/
|
||||
setTimeout(callback, delay, arg) {
|
||||
return delay <= RESOLUTION_MS ? setTimeout(callback, delay, arg) : new FastTimer(callback, delay, arg);
|
||||
},
|
||||
/**
|
||||
* The clearTimeout method cancels an instantiated Timer previously created
|
||||
* by calling setTimeout.
|
||||
*
|
||||
* @param {NodeJS.Timeout|FastTimer} timeout
|
||||
*/
|
||||
clearTimeout(timeout) {
|
||||
if (timeout[kFastTimer]) {
|
||||
timeout.clear();
|
||||
} else {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The setFastTimeout() method sets a fastTimer which executes a function once
|
||||
* the timer expires.
|
||||
* @param {Function} callback A function to be executed after the timer
|
||||
* expires.
|
||||
* @param {number} delay The time, in milliseconds that the timer should
|
||||
* wait before the specified function or code is executed.
|
||||
* @param {*} [arg] An optional argument to be passed to the callback function
|
||||
* when the timer expires.
|
||||
* @returns {FastTimer}
|
||||
*/
|
||||
setFastTimeout(callback, delay, arg) {
|
||||
return new FastTimer(callback, delay, arg);
|
||||
},
|
||||
/**
|
||||
* The clearTimeout method cancels an instantiated FastTimer previously
|
||||
* created by calling setFastTimeout.
|
||||
*
|
||||
* @param {FastTimer} timeout
|
||||
*/
|
||||
clearFastTimeout(timeout) {
|
||||
timeout.clear();
|
||||
},
|
||||
/**
|
||||
* The now method returns the value of the internal fast timer clock.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
now() {
|
||||
return fastNow;
|
||||
},
|
||||
/**
|
||||
* Trigger the onTick function to process the fastTimers array.
|
||||
* Exported for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
* @param {number} [delay=0] The delay in milliseconds to add to the now value.
|
||||
*/
|
||||
tick(delay = 0) {
|
||||
fastNow += delay - RESOLUTION_MS + 1;
|
||||
onTick();
|
||||
onTick();
|
||||
},
|
||||
/**
|
||||
* Reset FastTimers.
|
||||
* Exported for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
*/
|
||||
reset() {
|
||||
fastNow = 0;
|
||||
fastTimers.length = 0;
|
||||
clearTimeout(fastNowTimeout);
|
||||
fastNowTimeout = null;
|
||||
},
|
||||
/**
|
||||
* Exporting for testing purposes only.
|
||||
* Marking as deprecated to discourage any use outside of testing.
|
||||
* @deprecated
|
||||
*/
|
||||
kFastTimer
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// lib/core/connect.js
|
||||
var require_connect = __commonJS({
|
||||
"lib/core/connect.js"(exports2, module2) {
|
||||
@@ -2174,6 +2425,10 @@ var require_connect = __commonJS({
|
||||
var assert = require("node:assert");
|
||||
var util = require_util();
|
||||
var { InvalidArgumentError, ConnectTimeoutError } = require_errors();
|
||||
var timers = require_timers();
|
||||
function noop() {
|
||||
}
|
||||
__name(noop, "noop");
|
||||
var tls;
|
||||
var SessionCache;
|
||||
if (global.FinalizationRegistry && !(process.env.NODE_V8_COVERAGE || process.env.UNDICI_NO_FG)) {
|
||||
@@ -2246,8 +2501,9 @@ var require_connect = __commonJS({
|
||||
}
|
||||
servername = servername || options.servername || util.getServerName(host) || null;
|
||||
const sessionKey = servername || hostname;
|
||||
const session = customSession || sessionCache.get(sessionKey) || null;
|
||||
assert(sessionKey);
|
||||
const session = customSession || sessionCache.get(sessionKey) || null;
|
||||
port = port || 443;
|
||||
socket = tls.connect({
|
||||
highWaterMark: 16384,
|
||||
// TLS in node can't have bigger HWM anyway...
|
||||
@@ -2259,7 +2515,7 @@ var require_connect = __commonJS({
|
||||
ALPNProtocols: allowH2 ? ["http/1.1", "h2"] : ["http/1.1"],
|
||||
socket: httpSocket,
|
||||
// upgrade socket connection
|
||||
port: port || 443,
|
||||
port,
|
||||
host: hostname
|
||||
});
|
||||
socket.on("session", function(session2) {
|
||||
@@ -2267,12 +2523,13 @@ var require_connect = __commonJS({
|
||||
});
|
||||
} else {
|
||||
assert(!httpSocket, "httpSocket can only be sent on TLS update");
|
||||
port = port || 80;
|
||||
socket = net.connect({
|
||||
highWaterMark: 64 * 1024,
|
||||
// Same as nodejs fs streams.
|
||||
...options,
|
||||
localAddress,
|
||||
port: port || 80,
|
||||
port,
|
||||
host: hostname
|
||||
});
|
||||
}
|
||||
@@ -2280,16 +2537,16 @@ var require_connect = __commonJS({
|
||||
const keepAliveInitialDelay = options.keepAliveInitialDelay === void 0 ? 6e4 : options.keepAliveInitialDelay;
|
||||
socket.setKeepAlive(true, keepAliveInitialDelay);
|
||||
}
|
||||
const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout);
|
||||
const clearConnectTimeout = setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port });
|
||||
socket.setNoDelay(true).once(protocol === "https:" ? "secureConnect" : "connect", function() {
|
||||
cancelTimeout();
|
||||
queueMicrotask(clearConnectTimeout);
|
||||
if (callback) {
|
||||
const cb = callback;
|
||||
callback = null;
|
||||
cb(null, this);
|
||||
}
|
||||
}).on("error", function(err) {
|
||||
cancelTimeout();
|
||||
queueMicrotask(clearConnectTimeout);
|
||||
if (callback) {
|
||||
const cb = callback;
|
||||
callback = null;
|
||||
@@ -2300,34 +2557,45 @@ var require_connect = __commonJS({
|
||||
}, "connect");
|
||||
}
|
||||
__name(buildConnector, "buildConnector");
|
||||
function setupTimeout(onConnectTimeout2, timeout) {
|
||||
if (!timeout) {
|
||||
return () => {
|
||||
};
|
||||
var setupConnectTimeout = process.platform === "win32" ? (socketWeakRef, opts) => {
|
||||
if (!opts.timeout) {
|
||||
return noop;
|
||||
}
|
||||
let s1 = null;
|
||||
let s2 = null;
|
||||
const timeoutId = setTimeout(() => {
|
||||
const fastTimer = timers.setFastTimeout(() => {
|
||||
s1 = setImmediate(() => {
|
||||
if (process.platform === "win32") {
|
||||
s2 = setImmediate(() => onConnectTimeout2());
|
||||
} else {
|
||||
onConnectTimeout2();
|
||||
}
|
||||
s2 = setImmediate(() => onConnectTimeout(socketWeakRef.deref(), opts));
|
||||
});
|
||||
}, timeout);
|
||||
}, opts.timeout);
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
timers.clearFastTimeout(fastTimer);
|
||||
clearImmediate(s1);
|
||||
clearImmediate(s2);
|
||||
};
|
||||
}
|
||||
__name(setupTimeout, "setupTimeout");
|
||||
function onConnectTimeout(socket) {
|
||||
} : (socketWeakRef, opts) => {
|
||||
if (!opts.timeout) {
|
||||
return noop;
|
||||
}
|
||||
let s1 = null;
|
||||
const fastTimer = timers.setFastTimeout(() => {
|
||||
s1 = setImmediate(() => {
|
||||
onConnectTimeout(socketWeakRef.deref(), opts);
|
||||
});
|
||||
}, opts.timeout);
|
||||
return () => {
|
||||
timers.clearFastTimeout(fastTimer);
|
||||
clearImmediate(s1);
|
||||
};
|
||||
};
|
||||
function onConnectTimeout(socket, opts) {
|
||||
let message = "Connect Timeout Error";
|
||||
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
|
||||
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(", ")})`;
|
||||
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(", ")},`;
|
||||
} else {
|
||||
message += ` (attempted address: ${opts.hostname}:${opts.port},`;
|
||||
}
|
||||
message += ` timeout: ${opts.timeout}ms)`;
|
||||
util.destroy(socket, new ConnectTimeoutError(message));
|
||||
}
|
||||
__name(onConnectTimeout, "onConnectTimeout");
|
||||
@@ -2335,94 +2603,6 @@ var require_connect = __commonJS({
|
||||
}
|
||||
});
|
||||
|
||||
// lib/util/timers.js
|
||||
var require_timers = __commonJS({
|
||||
"lib/util/timers.js"(exports2, module2) {
|
||||
"use strict";
|
||||
var TICK_MS = 499;
|
||||
var fastNow = Date.now();
|
||||
var fastNowTimeout;
|
||||
var fastTimers = [];
|
||||
function onTimeout() {
|
||||
fastNow = Date.now();
|
||||
let len = fastTimers.length;
|
||||
let idx = 0;
|
||||
while (idx < len) {
|
||||
const timer = fastTimers[idx];
|
||||
if (timer.state === 0) {
|
||||
timer.state = fastNow + timer.delay - TICK_MS;
|
||||
} else if (timer.state > 0 && fastNow >= timer.state) {
|
||||
timer.state = -1;
|
||||
timer.callback(timer.opaque);
|
||||
}
|
||||
if (timer.state === -1) {
|
||||
timer.state = -2;
|
||||
if (idx !== len - 1) {
|
||||
fastTimers[idx] = fastTimers.pop();
|
||||
} else {
|
||||
fastTimers.pop();
|
||||
}
|
||||
len -= 1;
|
||||
} else {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
if (fastTimers.length > 0) {
|
||||
refreshTimeout();
|
||||
}
|
||||
}
|
||||
__name(onTimeout, "onTimeout");
|
||||
function refreshTimeout() {
|
||||
if (fastNowTimeout?.refresh) {
|
||||
fastNowTimeout.refresh();
|
||||
} else {
|
||||
clearTimeout(fastNowTimeout);
|
||||
fastNowTimeout = setTimeout(onTimeout, TICK_MS);
|
||||
if (fastNowTimeout.unref) {
|
||||
fastNowTimeout.unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
__name(refreshTimeout, "refreshTimeout");
|
||||
var Timeout = class {
|
||||
static {
|
||||
__name(this, "Timeout");
|
||||
}
|
||||
constructor(callback, delay, opaque) {
|
||||
this.callback = callback;
|
||||
this.delay = delay;
|
||||
this.opaque = opaque;
|
||||
this.state = -2;
|
||||
this.refresh();
|
||||
}
|
||||
refresh() {
|
||||
if (this.state === -2) {
|
||||
fastTimers.push(this);
|
||||
if (!fastNowTimeout || fastTimers.length === 1) {
|
||||
refreshTimeout();
|
||||
}
|
||||
}
|
||||
this.state = 0;
|
||||
}
|
||||
clear() {
|
||||
this.state = -1;
|
||||
}
|
||||
};
|
||||
module2.exports = {
|
||||
setTimeout(callback, delay, opaque) {
|
||||
return delay <= 1e3 ? setTimeout(callback, delay, opaque) : new Timeout(callback, delay, opaque);
|
||||
},
|
||||
clearTimeout(timeout) {
|
||||
if (timeout instanceof Timeout) {
|
||||
timeout.clear();
|
||||
} else {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// lib/llhttp/utils.js
|
||||
var require_utils = __commonJS({
|
||||
"lib/llhttp/utils.js"(exports2) {
|
||||
@@ -4523,13 +4703,19 @@ var require_util2 = __commonJS({
|
||||
static {
|
||||
__name(this, "InflateStream");
|
||||
}
|
||||
#zlibOptions;
|
||||
/** @param {zlib.ZlibOptions} [zlibOptions] */
|
||||
constructor(zlibOptions) {
|
||||
super();
|
||||
this.#zlibOptions = zlibOptions;
|
||||
}
|
||||
_transform(chunk, encoding, callback) {
|
||||
if (!this._inflateStream) {
|
||||
if (chunk.length === 0) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
this._inflateStream = (chunk[0] & 15) === 8 ? zlib.createInflate() : zlib.createInflateRaw();
|
||||
this._inflateStream = (chunk[0] & 15) === 8 ? zlib.createInflate(this.#zlibOptions) : zlib.createInflateRaw(this.#zlibOptions);
|
||||
this._inflateStream.on("data", this.push.bind(this));
|
||||
this._inflateStream.on("end", () => this.push(null));
|
||||
this._inflateStream.on("error", (err) => this.destroy(err));
|
||||
@@ -4544,8 +4730,8 @@ var require_util2 = __commonJS({
|
||||
callback();
|
||||
}
|
||||
};
|
||||
function createInflate() {
|
||||
return new InflateStream();
|
||||
function createInflate(zlibOptions) {
|
||||
return new InflateStream(zlibOptions);
|
||||
}
|
||||
__name(createInflate, "createInflate");
|
||||
function extractMimeType(headers) {
|
||||
@@ -4984,9 +5170,16 @@ var require_formdata_parser = __commonJS({
|
||||
const boundary = Buffer.from(`--${boundaryString}`, "utf8");
|
||||
const entryList = [];
|
||||
const position = { position: 0 };
|
||||
if (input[0] === 13 && input[1] === 10) {
|
||||
while (input[position.position] === 13 && input[position.position + 1] === 10) {
|
||||
position.position += 2;
|
||||
}
|
||||
let trailing = input.length;
|
||||
while (input[trailing - 1] === 10 && input[trailing - 2] === 13) {
|
||||
trailing -= 2;
|
||||
}
|
||||
if (trailing !== input.length) {
|
||||
input = input.subarray(0, trailing);
|
||||
}
|
||||
while (true) {
|
||||
if (input.subarray(position.position, position.position + boundary.length).equals(boundary)) {
|
||||
position.position += boundary.length;
|
||||
@@ -5584,35 +5777,35 @@ var require_client_h1 = __commonJS({
|
||||
return 0;
|
||||
},
|
||||
wasm_on_status: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p);
|
||||
assert(currentParser.ptr === p);
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset;
|
||||
return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer, start, len)) || 0;
|
||||
},
|
||||
wasm_on_message_begin: (p) => {
|
||||
assert.strictEqual(currentParser.ptr, p);
|
||||
assert(currentParser.ptr === p);
|
||||
return currentParser.onMessageBegin() || 0;
|
||||
},
|
||||
wasm_on_header_field: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p);
|
||||
assert(currentParser.ptr === p);
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset;
|
||||
return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer, start, len)) || 0;
|
||||
},
|
||||
wasm_on_header_value: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p);
|
||||
assert(currentParser.ptr === p);
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset;
|
||||
return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer, start, len)) || 0;
|
||||
},
|
||||
wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => {
|
||||
assert.strictEqual(currentParser.ptr, p);
|
||||
assert(currentParser.ptr === p);
|
||||
return currentParser.onHeadersComplete(statusCode, Boolean(upgrade), Boolean(shouldKeepAlive)) || 0;
|
||||
},
|
||||
wasm_on_body: (p, at, len) => {
|
||||
assert.strictEqual(currentParser.ptr, p);
|
||||
assert(currentParser.ptr === p);
|
||||
const start = at - currentBufferPtr + currentBufferRef.byteOffset;
|
||||
return currentParser.onBody(new FastBuffer(currentBufferRef.buffer, start, len)) || 0;
|
||||
},
|
||||
wasm_on_message_complete: (p) => {
|
||||
assert.strictEqual(currentParser.ptr, p);
|
||||
assert(currentParser.ptr === p);
|
||||
return currentParser.onMessageComplete() || 0;
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
@@ -5627,9 +5820,11 @@ var require_client_h1 = __commonJS({
|
||||
var currentBufferRef = null;
|
||||
var currentBufferSize = 0;
|
||||
var currentBufferPtr = null;
|
||||
var TIMEOUT_HEADERS = 1;
|
||||
var TIMEOUT_BODY = 2;
|
||||
var TIMEOUT_IDLE = 3;
|
||||
var USE_NATIVE_TIMER = 0;
|
||||
var USE_FAST_TIMER = 1;
|
||||
var TIMEOUT_HEADERS = 2 | USE_FAST_TIMER;
|
||||
var TIMEOUT_BODY = 4 | USE_FAST_TIMER;
|
||||
var TIMEOUT_KEEP_ALIVE = 8 | USE_NATIVE_TIMER;
|
||||
var Parser = class {
|
||||
static {
|
||||
__name(this, "Parser");
|
||||
@@ -5658,24 +5853,27 @@ var require_client_h1 = __commonJS({
|
||||
this.connection = "";
|
||||
this.maxResponseSize = client[kMaxResponseSize];
|
||||
}
|
||||
setTimeout(value, type) {
|
||||
this.timeoutType = type;
|
||||
if (value !== this.timeoutValue) {
|
||||
timers.clearTimeout(this.timeout);
|
||||
if (value) {
|
||||
this.timeout = timers.setTimeout(onParserTimeout, value, this);
|
||||
if (this.timeout.unref) {
|
||||
this.timeout.unref();
|
||||
}
|
||||
} else {
|
||||
setTimeout(delay, type) {
|
||||
if (delay !== this.timeoutValue || type & USE_FAST_TIMER ^ this.timeoutType & USE_FAST_TIMER) {
|
||||
if (this.timeout) {
|
||||
timers.clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
this.timeoutValue = value;
|
||||
if (delay) {
|
||||
if (type & USE_FAST_TIMER) {
|
||||
this.timeout = timers.setFastTimeout(onParserTimeout, delay, new WeakRef(this));
|
||||
} else {
|
||||
this.timeout = setTimeout(onParserTimeout, delay, new WeakRef(this));
|
||||
this.timeout.unref();
|
||||
}
|
||||
}
|
||||
this.timeoutValue = delay;
|
||||
} else if (this.timeout) {
|
||||
if (this.timeout.refresh) {
|
||||
this.timeout.refresh();
|
||||
}
|
||||
}
|
||||
this.timeoutType = type;
|
||||
}
|
||||
resume() {
|
||||
if (this.socket.destroyed || !this.paused) {
|
||||
@@ -5752,7 +5950,7 @@ var require_client_h1 = __commonJS({
|
||||
assert(currentParser == null);
|
||||
this.llhttp.llhttp_free(this.ptr);
|
||||
this.ptr = null;
|
||||
timers.clearTimeout(this.timeout);
|
||||
this.timeout && timers.clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
this.timeoutValue = null;
|
||||
this.timeoutType = null;
|
||||
@@ -5811,16 +6009,16 @@ var require_client_h1 = __commonJS({
|
||||
onUpgrade(head) {
|
||||
const { upgrade, client, socket, headers, statusCode } = this;
|
||||
assert(upgrade);
|
||||
assert(client[kSocket] === socket);
|
||||
assert(!socket.destroyed);
|
||||
assert(!this.paused);
|
||||
assert((headers.length & 1) === 0);
|
||||
const request = client[kQueue][client[kRunningIdx]];
|
||||
assert(request);
|
||||
assert(!socket.destroyed);
|
||||
assert(socket === client[kSocket]);
|
||||
assert(!this.paused);
|
||||
assert(request.upgrade || request.method === "CONNECT");
|
||||
this.statusCode = null;
|
||||
this.statusText = "";
|
||||
this.shouldKeepAlive = null;
|
||||
assert(this.headers.length % 2 === 0);
|
||||
this.headers = [];
|
||||
this.headersSize = 0;
|
||||
socket.unshift(head);
|
||||
@@ -5859,7 +6057,7 @@ var require_client_h1 = __commonJS({
|
||||
util.destroy(socket, new SocketError("bad upgrade", util.getSocketInfo(socket)));
|
||||
return -1;
|
||||
}
|
||||
assert.strictEqual(this.timeoutType, TIMEOUT_HEADERS);
|
||||
assert(this.timeoutType === TIMEOUT_HEADERS);
|
||||
this.statusCode = statusCode;
|
||||
this.shouldKeepAlive = shouldKeepAlive || // Override llhttp value which does not allow keepAlive for HEAD.
|
||||
request.method === "HEAD" && !socket[kReset] && this.connection.toLowerCase() === "keep-alive";
|
||||
@@ -5881,7 +6079,7 @@ var require_client_h1 = __commonJS({
|
||||
this.upgrade = true;
|
||||
return 2;
|
||||
}
|
||||
assert(this.headers.length % 2 === 0);
|
||||
assert((this.headers.length & 1) === 0);
|
||||
this.headers = [];
|
||||
this.headersSize = 0;
|
||||
if (this.shouldKeepAlive && client[kPipelining]) {
|
||||
@@ -5925,7 +6123,7 @@ var require_client_h1 = __commonJS({
|
||||
}
|
||||
const request = client[kQueue][client[kRunningIdx]];
|
||||
assert(request);
|
||||
assert.strictEqual(this.timeoutType, TIMEOUT_BODY);
|
||||
assert(this.timeoutType === TIMEOUT_BODY);
|
||||
if (this.timeout) {
|
||||
if (this.timeout.refresh) {
|
||||
this.timeout.refresh();
|
||||
@@ -5949,16 +6147,16 @@ var require_client_h1 = __commonJS({
|
||||
if (upgrade) {
|
||||
return;
|
||||
}
|
||||
assert(statusCode >= 100);
|
||||
assert((this.headers.length & 1) === 0);
|
||||
const request = client[kQueue][client[kRunningIdx]];
|
||||
assert(request);
|
||||
assert(statusCode >= 100);
|
||||
this.statusCode = null;
|
||||
this.statusText = "";
|
||||
this.bytesRead = 0;
|
||||
this.contentLength = "";
|
||||
this.keepAlive = "";
|
||||
this.connection = "";
|
||||
assert(this.headers.length % 2 === 0);
|
||||
this.headers = [];
|
||||
this.headersSize = 0;
|
||||
if (statusCode < 200) {
|
||||
@@ -5971,7 +6169,7 @@ var require_client_h1 = __commonJS({
|
||||
request.onComplete(headers);
|
||||
client[kQueue][client[kRunningIdx]++] = null;
|
||||
if (socket[kWriting]) {
|
||||
assert.strictEqual(client[kRunning], 0);
|
||||
assert(client[kRunning] === 0);
|
||||
util.destroy(socket, new InformationalError("reset"));
|
||||
return constants.ERROR.PAUSED;
|
||||
} else if (!shouldKeepAlive) {
|
||||
@@ -5988,17 +6186,17 @@ var require_client_h1 = __commonJS({
|
||||
}
|
||||
};
|
||||
function onParserTimeout(parser) {
|
||||
const { socket, timeoutType, client } = parser;
|
||||
const { socket, timeoutType, client, paused } = parser.deref();
|
||||
if (timeoutType === TIMEOUT_HEADERS) {
|
||||
if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) {
|
||||
assert(!parser.paused, "cannot be paused while waiting for headers");
|
||||
assert(!paused, "cannot be paused while waiting for headers");
|
||||
util.destroy(socket, new HeadersTimeoutError());
|
||||
}
|
||||
} else if (timeoutType === TIMEOUT_BODY) {
|
||||
if (!parser.paused) {
|
||||
if (!paused) {
|
||||
util.destroy(socket, new BodyTimeoutError());
|
||||
}
|
||||
} else if (timeoutType === TIMEOUT_IDLE) {
|
||||
} else if (timeoutType === TIMEOUT_KEEP_ALIVE) {
|
||||
assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue]);
|
||||
util.destroy(socket, new InformationalError("socket idle timeout"));
|
||||
}
|
||||
@@ -6016,8 +6214,8 @@ var require_client_h1 = __commonJS({
|
||||
socket[kBlocking] = false;
|
||||
socket[kParser] = new Parser(client, socket, llhttpInstance);
|
||||
addListener(socket, "error", function(err) {
|
||||
const parser = this[kParser];
|
||||
assert(err.code !== "ERR_TLS_CERT_ALTNAME_INVALID");
|
||||
const parser = this[kParser];
|
||||
if (err.code === "ECONNRESET" && parser.statusCode && !parser.shouldKeepAlive) {
|
||||
parser.onMessageComplete();
|
||||
return;
|
||||
@@ -6125,8 +6323,8 @@ var require_client_h1 = __commonJS({
|
||||
socket[kNoRef] = false;
|
||||
}
|
||||
if (client[kSize] === 0) {
|
||||
if (socket[kParser].timeoutType !== TIMEOUT_IDLE) {
|
||||
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_IDLE);
|
||||
if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) {
|
||||
socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE);
|
||||
}
|
||||
} else if (client[kRunning] > 0 && socket[kParser].statusCode < 200) {
|
||||
if (socket[kParser].timeoutType !== TIMEOUT_HEADERS) {
|
||||
@@ -10889,22 +11087,31 @@ var require_fetch = __commonJS({
|
||||
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
||||
}));
|
||||
} else if (coding === "deflate") {
|
||||
decoders.push(createInflate());
|
||||
decoders.push(createInflate({
|
||||
flush: zlib.constants.Z_SYNC_FLUSH,
|
||||
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
||||
}));
|
||||
} else if (coding === "br") {
|
||||
decoders.push(zlib.createBrotliDecompress());
|
||||
decoders.push(zlib.createBrotliDecompress({
|
||||
flush: zlib.constants.BROTLI_OPERATION_FLUSH,
|
||||
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
|
||||
}));
|
||||
} else {
|
||||
decoders.length = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const onError = this.onError.bind(this);
|
||||
resolve({
|
||||
status,
|
||||
statusText,
|
||||
headersList,
|
||||
body: decoders.length ? pipeline(this.body, ...decoders, () => {
|
||||
}) : this.body.on("error", () => {
|
||||
})
|
||||
body: decoders.length ? pipeline(this.body, ...decoders, (err) => {
|
||||
if (err) {
|
||||
this.onError(err);
|
||||
}
|
||||
}).on("error", onError) : this.body.on("error", onError)
|
||||
});
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -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 "6.19.8"
|
||||
#define UNDICI_VERSION "6.20.0"
|
||||
#endif // SRC_UNDICI_VERSION_H_
|
||||
Reference in new issue
Block a user