deps: update undici to 7.8.0

PR-URL: https://github.com/nodejs/node/pull/57770
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Matthew Aitken <maitken033380023@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
This commit is contained in:
Node.js GitHub Bot 2025-04-19 13:14:51 -04:00 committed by GitHub
parent afd318db67
commit e273ddf7a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1174 additions and 660 deletions

View File

@ -194,7 +194,7 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
* **method** `string`
* **reset** `boolean` (optional) - Default: `false` - If `false`, the request will attempt to create a long-living connection by sending the `connection: keep-alive` header,otherwise will attempt to close it immediately after response by sending `connection: close` within the request and closing the socket afterwards.
* **body** `string | Buffer | Uint8Array | stream.Readable | Iterable | AsyncIterable | null` (optional) - Default: `null`
* **headers** `UndiciHeaders | string[]` (optional) - Default: `null`.
* **headers** `UndiciHeaders` (optional) - Default: `null`.
* **query** `Record<string, any> | null` (optional) - Default: `null` - Query string params to be embedded in the request URL. Note that both keys and values of query are encoded using `encodeURIComponent`. If for some reason you need to send them unencoded, embed query params into path directly instead.
* **idempotent** `boolean` (optional) - Default: `true` if `method` is `'HEAD'` or `'GET'` - Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline has completed.
* **blocking** `boolean` (optional) - Default: `method !== 'HEAD'` - Whether the response is expected to take a long time and would end up blocking the pipeline. When this is set to `true` further pipelining will be avoided on the same connection until headers have been received.

View File

@ -0,0 +1,262 @@
# Class: H2CClient
Extends: `undici.Dispatcher`
A basic H2C client.
**Example**
```js
const { createServer } = require('node:http2')
const { once } = require('node:events')
const { H2CClient } = require('undici')
const server = createServer((req, res) => {
res.writeHead(200)
res.end('Hello, world!')
})
server.listen()
once(server, 'listening').then(() => {
const client = new H2CClient(`http://localhost:${server.address().port}/`)
const response = await client.request({ path: '/', method: 'GET' })
console.log(response.statusCode) // 200
response.body.text.then((text) => {
console.log(text) // Hello, world!
})
})
```
## `new H2CClient(url[, options])`
Arguments:
- **url** `URL | string` - Should only include the **protocol, hostname, and port**. It only supports `http` protocol.
- **options** `H2CClientOptions` (optional)
Returns: `H2CClient`
### Parameter: `H2CClientOptions`
- **bodyTimeout** `number | null` (optional) - Default: `300e3` - The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Defaults to 300 seconds. Please note the `timeout` will be reset if you keep writing data to the socket everytime.
- **headersTimeout** `number | null` (optional) - Default: `300e3` - The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers while not sending the request. Defaults to 300 seconds.
- **keepAliveMaxTimeout** `number | null` (optional) - Default: `600e3` - The maximum allowed `keepAliveTimeout`, in milliseconds, when overridden by _keep-alive_ hints from the server. Defaults to 10 minutes.
- **keepAliveTimeout** `number | null` (optional) - Default: `4e3` - The timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by _keep-alive_ hints from the server. See [MDN: HTTP - Headers - Keep-Alive directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive#directives) for more details. Defaults to 4 seconds.
- **keepAliveTimeoutThreshold** `number | null` (optional) - Default: `2e3` - A number of milliseconds subtracted from server _keep-alive_ hints when overriding `keepAliveTimeout` to account for timing inaccuracies caused by e.g. transport latency. Defaults to 2 seconds.
- **maxHeaderSize** `number | null` (optional) - Default: `--max-http-header-size` or `16384` - The maximum length of request headers in bytes. Defaults to Node.js' --max-http-header-size or 16KiB.
- **maxResponseSize** `number | null` (optional) - Default: `-1` - The maximum length of response body in bytes. Set to `-1` to disable.
- **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.
- **pipelining** `number | null` (optional) - Default to `maxConcurrentStreams` - The amount of concurrent requests sent over a single HTTP/2 session in accordance with [RFC-7540](https://httpwg.org/specs/rfc7540.html#StreamsLayer) Stream specification. Streams can be closed up by remote server at any time.
- **connect** `ConnectOptions | null` (optional) - Default: `null`.
- **strictContentLength** `Boolean` (optional) - Default: `true` - Whether to treat request content length mismatches as errors. If true, an error is thrown when the request content-length header doesn't match the length of the request body.
- **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.
#### Parameter: `H2CConnectOptions`
- **socketPath** `string | null` (optional) - Default: `null` - An IPC endpoint, either Unix domain socket or Windows named pipe.
- **timeout** `number | null` (optional) - In milliseconds, Default `10e3`.
- **servername** `string | null` (optional)
- **keepAlive** `boolean | null` (optional) - Default: `true` - TCP keep-alive enabled
- **keepAliveInitialDelay** `number | null` (optional) - Default: `60000` - TCP keep-alive interval for the socket in milliseconds
### Example - Basic Client instantiation
This will instantiate the undici H2CClient, but it will not connect to the origin until something is queued. Consider using `client.connect` to prematurely connect to the origin, or just call `client.request`.
```js
"use strict";
import { H2CClient } from "undici";
const client = new H2CClient("http://localhost:3000");
```
## Instance Methods
### `H2CClient.close([callback])`
Implements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).
### `H2CClient.destroy([error, callback])`
Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).
Waits until socket is closed before invoking the callback (or returning a promise if no callback is provided).
### `H2CClient.connect(options[, callback])`
See [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).
### `H2CClient.dispatch(options, handlers)`
Implements [`Dispatcher.dispatch(options, handlers)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).
### `H2CClient.pipeline(options, handler)`
See [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).
### `H2CClient.request(options[, callback])`
See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
### `H2CClient.stream(options, factory[, callback])`
See [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).
### `H2CClient.upgrade(options[, callback])`
See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).
## Instance Properties
### `H2CClient.closed`
- `boolean`
`true` after `H2CClient.close()` has been called.
### `H2CClient.destroyed`
- `boolean`
`true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed.
### `H2CClient.pipelining`
- `number`
Property to get and set the pipelining factor.
## Instance Events
### Event: `'connect'`
See [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).
Parameters:
- **origin** `URL`
- **targets** `Array<Dispatcher>`
Emitted when a socket has been created and connected. The client will connect once `client.size > 0`.
#### Example - Client connect event
```js
import { createServer } from "node:http2";
import { H2CClient } from "undici";
import { once } from "events";
const server = createServer((request, response) => {
response.end("Hello, World!");
}).listen();
await once(server, "listening");
const client = new H2CClient(`http://localhost:${server.address().port}`);
client.on("connect", (origin) => {
console.log(`Connected to ${origin}`); // should print before the request body statement
});
try {
const { body } = await client.request({
path: "/",
method: "GET",
});
body.setEncoding("utf-8");
body.on("data", console.log);
client.close();
server.close();
} catch (error) {
console.error(error);
client.close();
server.close();
}
```
### Event: `'disconnect'`
See [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).
Parameters:
- **origin** `URL`
- **targets** `Array<Dispatcher>`
- **error** `Error`
Emitted when socket has disconnected. The error argument of the event is the error which caused the socket to disconnect. The client will reconnect if or once `client.size > 0`.
#### Example - Client disconnect event
```js
import { createServer } from "node:http2";
import { H2CClient } from "undici";
import { once } from "events";
const server = createServer((request, response) => {
response.destroy();
}).listen();
await once(server, "listening");
const client = new H2CClient(`http://localhost:${server.address().port}`);
client.on("disconnect", (origin) => {
console.log(`Disconnected from ${origin}`);
});
try {
await client.request({
path: "/",
method: "GET",
});
} catch (error) {
console.error(error.message);
client.close();
server.close();
}
```
### Event: `'drain'`
Emitted when pipeline is no longer busy.
See [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).
#### Example - Client drain event
```js
import { createServer } from "node:http2";
import { H2CClient } from "undici";
import { once } from "events";
const server = createServer((request, response) => {
response.end("Hello, World!");
}).listen();
await once(server, "listening");
const client = new H2CClient(`http://localhost:${server.address().port}`);
client.on("drain", () => {
console.log("drain event");
client.close();
server.close();
});
const requests = [
client.request({ path: "/", method: "GET" }),
client.request({ path: "/", method: "GET" }),
client.request({ path: "/", method: "GET" }),
];
await Promise.all(requests);
console.log("requests completed");
```
### Event: `'error'`
Invoked for users errors such as throwing in the `onError` handler.

View File

@ -477,7 +477,7 @@ This method returns any pending interceptors registered on a mock agent. A pendi
Returns: `PendingInterceptor[]` (where `PendingInterceptor` is a `MockDispatch` with an additional `origin: string`)
#### Example - List all pending inteceptors
#### Example - List all pending interceptors
```js
const agent = new MockAgent()

View File

@ -8,6 +8,7 @@ const Agent = require('./lib/dispatcher/agent')
const ProxyAgent = require('./lib/dispatcher/proxy-agent')
const EnvHttpProxyAgent = require('./lib/dispatcher/env-http-proxy-agent')
const RetryAgent = require('./lib/dispatcher/retry-agent')
const H2CClient = require('./lib/dispatcher/h2c-client')
const errors = require('./lib/core/errors')
const util = require('./lib/core/util')
const { InvalidArgumentError } = errors
@ -33,6 +34,7 @@ module.exports.Agent = Agent
module.exports.ProxyAgent = ProxyAgent
module.exports.EnvHttpProxyAgent = EnvHttpProxyAgent
module.exports.RetryAgent = RetryAgent
module.exports.H2CClient = H2CClient
module.exports.RetryHandler = RetryHandler
module.exports.DecoratorHandler = DecoratorHandler

View File

@ -115,6 +115,11 @@ module.exports = class SqliteCacheStore {
this.#db = new DatabaseSync(opts?.location ?? ':memory:')
this.#db.exec(`
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = memory;
PRAGMA optimize;
CREATE TABLE IF NOT EXISTS cacheInterceptorV${VERSION} (
-- Data specific to us
id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -134,9 +139,8 @@ module.exports = class SqliteCacheStore {
staleAt INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_url ON cacheInterceptorV${VERSION}(url);
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_method ON cacheInterceptorV${VERSION}(method);
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_deleteAt ON cacheInterceptorV${VERSION}(deleteAt);
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_getValuesQuery ON cacheInterceptorV${VERSION}(url, method, deleteAt);
CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_deleteByUrlQuery ON cacheInterceptorV${VERSION}(deleteAt);
`)
this.#getValuesQuery = this.#db.prepare(`
@ -346,7 +350,7 @@ module.exports = class SqliteCacheStore {
}
#prune () {
if (this.size <= this.#maxCount) {
if (Number.isFinite(this.#maxCount) && this.size <= this.#maxCount) {
return 0
}

View File

@ -3,10 +3,7 @@
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 () {}
const { InvalidArgumentError } = require('./errors')
let tls // include tls conditionally since it is not always available
@ -106,7 +103,6 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
servername,
session,
localAddress,
// TODO(HTTP/2): Add support for h2c
ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'],
socket: httpSocket, // upgrade socket connection
port,
@ -138,7 +134,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
socket.setKeepAlive(true, keepAliveInitialDelay)
}
const clearConnectTimeout = setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port })
const clearConnectTimeout = util.setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port })
socket
.setNoDelay(true)
@ -165,76 +161,4 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess
}
}
/**
* @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
}
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) {
// The socket could be already garbage collected
if (socket == null) {
return
}
let message = 'Connect Timeout Error'
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
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))
}
module.exports = buildConnector

View File

@ -9,7 +9,8 @@ const { Blob } = require('node:buffer')
const nodeUtil = require('node:util')
const { stringify } = require('node:querystring')
const { EventEmitter: EE } = require('node:events')
const { InvalidArgumentError } = require('./errors')
const timers = require('../util/timers')
const { InvalidArgumentError, ConnectTimeoutError } = require('./errors')
const { headerNameLowerCasedRecord } = require('./constants')
const { tree } = require('./tree')
@ -28,6 +29,8 @@ class BodyAsyncIterable {
}
}
function noop () {}
/**
* @param {*} body
* @returns {*}
@ -837,6 +840,78 @@ function errorRequest (client, request, err) {
}
}
/**
* @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
}
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) {
// The socket could be already garbage collected
if (socket == null) {
return
}
let message = 'Connect Timeout Error'
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},`
} else {
message += ` (attempted address: ${opts.hostname}:${opts.port},`
}
message += ` timeout: ${opts.timeout}ms)`
destroy(socket, new ConnectTimeoutError(message))
}
const kEnumerableProperty = Object.create(null)
kEnumerableProperty.enumerable = true
@ -908,5 +983,6 @@ module.exports = {
nodeMajor,
nodeMinor,
safeHTTPMethods: Object.freeze(['GET', 'HEAD', 'OPTIONS', 'TRACE']),
wrapRequestBody
wrapRequestBody,
setupConnectTimeout
}

View File

@ -208,6 +208,7 @@ function onHttp2SessionGoAway (errorCode) {
assert(client[kRunning] === 0)
client.emit('disconnect', client[kUrl], [client], err)
client.emit('connectionError', client[kUrl], [client], err)
client[kResume]()
}

View File

@ -0,0 +1,122 @@
'use strict'
const { connect } = require('node:net')
const { kClose, kDestroy } = require('../core/symbols')
const { InvalidArgumentError } = require('../core/errors')
const util = require('../core/util')
const Client = require('./client')
const DispatcherBase = require('./dispatcher-base')
class H2CClient extends DispatcherBase {
#client = null
constructor (origin, clientOpts) {
super()
if (typeof origin === 'string') {
origin = new URL(origin)
}
if (origin.protocol !== 'http:') {
throw new InvalidArgumentError(
'h2c-client: Only h2c protocol is supported'
)
}
const { connect, maxConcurrentStreams, pipelining, ...opts } =
clientOpts ?? {}
let defaultMaxConcurrentStreams = 100
let defaultPipelining = 100
if (
maxConcurrentStreams != null &&
Number.isInteger(maxConcurrentStreams) &&
maxConcurrentStreams > 0
) {
defaultMaxConcurrentStreams = maxConcurrentStreams
}
if (pipelining != null && Number.isInteger(pipelining) && pipelining > 0) {
defaultPipelining = pipelining
}
if (defaultPipelining > defaultMaxConcurrentStreams) {
throw new InvalidArgumentError(
'h2c-client: pipelining cannot be greater than maxConcurrentStreams'
)
}
this.#client = new Client(origin, {
...opts,
connect: this.#buildConnector(connect),
maxConcurrentStreams: defaultMaxConcurrentStreams,
pipelining: defaultPipelining,
allowH2: true
})
}
#buildConnector (connectOpts) {
return (opts, callback) => {
const timeout = connectOpts?.connectOpts ?? 10e3
const { hostname, port, pathname } = opts
const socket = connect({
...opts,
host: hostname,
port,
pathname
})
// Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket
if (opts.keepAlive == null || opts.keepAlive) {
const keepAliveInitialDelay =
opts.keepAliveInitialDelay == null ? 60e3 : opts.keepAliveInitialDelay
socket.setKeepAlive(true, keepAliveInitialDelay)
}
socket.alpnProtocol = 'h2'
const clearConnectTimeout = util.setupConnectTimeout(
new WeakRef(socket),
{ timeout, hostname, port }
)
socket
.setNoDelay(true)
.once('connect', function () {
queueMicrotask(clearConnectTimeout)
if (callback) {
const cb = callback
callback = null
cb(null, this)
}
})
.on('error', function (err) {
queueMicrotask(clearConnectTimeout)
if (callback) {
const cb = callback
callback = null
cb(err)
}
})
return socket
}
}
dispatch (opts, handler) {
return this.#client.dispatch(opts, handler)
}
async [kClose] () {
await this.#client.close()
}
async [kDestroy] () {
await this.#client.destroy()
}
}
module.exports = H2CClient

View File

@ -1,5 +1,5 @@
> undici@7.6.0 build:wasm
> undici@7.8.0 build:wasm
> node build/wasm.js --docker
> docker run --rm --platform=linux/x86_64 --user 1001:118 --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/lib/llhttp,target=/home/node/build/lib/llhttp --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/build,target=/home/node/build/build --mount type=bind,source=/home/runner/work/node/node/deps/undici/src/deps,target=/home/node/build/deps -t ghcr.io/nodejs/wasm-builder@sha256:975f391d907e42a75b8c72eb77c782181e941608687d4d8694c3e9df415a0970 node build/wasm.js

592
deps/undici/src/package-lock.json generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "undici",
"version": "7.6.0",
"version": "7.8.0",
"description": "An HTTP/1.1 client, written from scratch for Node.js",
"homepage": "https://undici.nodejs.org",
"bugs": {
@ -80,7 +80,7 @@
"test:fuzzing": "node test/fuzzing/fuzzing.test.js",
"test:fetch": "npm run build:node && borp --timeout 180000 --expose-gc --concurrency 1 -p \"test/fetch/*.js\" && npm run test:webidl && npm run test:busboy",
"test:h2": "npm run test:h2:core && npm run test:h2:fetch",
"test:h2:core": "borp -p \"test/http2*.js\"",
"test:h2:core": "borp -p \"test/+(http2|h2)*.js\"",
"test:h2:fetch": "npm run build:node && borp -p \"test/fetch/http2*.js\"",
"test:interceptors": "borp -p \"test/interceptors/*.js\"",
"test:jest": "cross-env NODE_V8_COVERAGE= jest",
@ -116,7 +116,7 @@
"c8": "^10.0.0",
"cross-env": "^7.0.3",
"dns-packet": "^5.4.0",
"esbuild": "^0.24.0",
"esbuild": "^0.25.2",
"eslint": "^9.9.0",
"fast-check": "^3.17.1",
"https-pem": "^3.0.0",

View File

@ -12,6 +12,8 @@ type AbortSignal = unknown
export default Dispatcher
export type UndiciHeaders = Record<string, string | string[]> | IncomingHttpHeaders | string[] | Iterable<[string, string | string[] | undefined]> | null
/** Dispatcher is the core API used to dispatch requests. */
declare class Dispatcher extends EventEmitter {
/** Dispatches a request. This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this. */
@ -103,7 +105,7 @@ declare namespace Dispatcher {
/** Default: `null` */
body?: string | Buffer | Uint8Array | Readable | null | FormData;
/** Default: `null` */
headers?: Record<string, string | string[]> | IncomingHttpHeaders | string[] | Iterable<[string, string | string[] | undefined]> | null;
headers?: UndiciHeaders;
/** Query string params to be embedded in the request URL. Default: `null` */
query?: Record<string, any>;
/** Whether the requests can be safely retried or not. If `false` the request won't be sent until all preceding requests in the pipeline have completed. Default: `true` if `method` is `HEAD` or `GET`. */
@ -127,7 +129,7 @@ declare namespace Dispatcher {
origin: string | URL;
path: string;
/** Default: `null` */
headers?: IncomingHttpHeaders | string[] | null;
headers?: UndiciHeaders;
/** Default: `null` */
signal?: AbortSignal | EventEmitter | null;
/** This argument parameter is passed through to `ConnectData` */
@ -164,7 +166,7 @@ declare namespace Dispatcher {
/** Default: `'GET'` */
method?: string;
/** Default: `null` */
headers?: IncomingHttpHeaders | string[] | null;
headers?: UndiciHeaders;
/** A string of comma separated protocols, in descending preference order. Default: `'Websocket'` */
protocol?: string;
/** Default: `null` */

75
deps/undici/src/types/h2c-client.d.ts vendored Normal file
View File

@ -0,0 +1,75 @@
import { URL } from 'url'
import Dispatcher from './dispatcher'
import buildConnector from './connector'
type H2ClientOptions = Omit<Dispatcher.ConnectOptions, 'origin'>
/**
* A basic H2C client, mapped on top a single TCP connection. Pipelining is disabled by default.
*/
export class H2CClient extends Dispatcher {
constructor (url: string | URL, options?: H2CClient.Options)
/** Property to get and set the pipelining factor. */
pipelining: number
/** `true` after `client.close()` has been called. */
closed: boolean
/** `true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. */
destroyed: boolean
// Override dispatcher APIs.
override connect (
options: H2ClientOptions
): Promise<Dispatcher.ConnectData>
override connect (
options: H2ClientOptions,
callback: (err: Error | null, data: Dispatcher.ConnectData) => void
): void
}
export declare namespace H2CClient {
export interface Options {
/** The maximum length of request headers in bytes. Default: Node.js' `--max-http-header-size` or `16384` (16KiB). */
maxHeaderSize?: number;
/** The amount of time, in milliseconds, the parser will wait to receive the complete HTTP headers (Node 14 and above only). Default: `300e3` milliseconds (300s). */
headersTimeout?: number;
/** TODO */
connectTimeout?: number;
/** The timeout after which a request will time out, in milliseconds. Monitors time between receiving body data. Use `0` to disable it entirely. Default: `300e3` milliseconds (300s). */
bodyTimeout?: number;
/** the timeout, in milliseconds, after which a socket without active requests will time out. Monitors time between activity on a connected socket. This value may be overridden by *keep-alive* hints from the server. Default: `4e3` milliseconds (4s). */
keepAliveTimeout?: number;
/** the maximum allowed `idleTimeout`, in milliseconds, when overridden by *keep-alive* hints from the server. Default: `600e3` milliseconds (10min). */
keepAliveMaxTimeout?: number;
/** A number of milliseconds subtracted from server *keep-alive* hints when overriding `idleTimeout` to account for timing inaccuracies caused by e.g. transport latency. Default: `1e3` milliseconds (1s). */
keepAliveTimeoutThreshold?: number;
/** TODO */
socketPath?: string;
/** The amount of concurrent requests to be sent over the single TCP/TLS connection according to [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.3.2). Default: `1`. */
pipelining?: number;
/** If `true`, an error is thrown when the request content-length header doesn't match the length of the request body. Default: `true`. */
strictContentLength?: boolean;
/** TODO */
maxCachedSessions?: number;
/** TODO */
maxRedirections?: number;
/** TODO */
connect?: Omit<Partial<buildConnector.BuildOptions>, 'allowH2'> | buildConnector.connector;
/** TODO */
maxRequestsPerClient?: number;
/** TODO */
localAddress?: string;
/** Max response body size in bytes, -1 is disabled */
maxResponseSize?: number;
/** Enables a family autodetection algorithm that loosely implements section 5 of RFC 8305. */
autoSelectFamily?: boolean;
/** The amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the `autoSelectFamily` option. */
autoSelectFamilyAttemptTimeout?: number;
/**
* @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
}
}
export default H2CClient

View File

@ -6,6 +6,7 @@ import { RedirectHandler, DecoratorHandler } from './handlers'
import BalancedPool from './balanced-pool'
import Client from './client'
import H2CClient from './h2c-client'
import buildConnector from './connector'
import errors from './errors'
import Agent from './agent'
@ -32,7 +33,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, interceptors, MockClient, MockPool, MockAgent, MockCallHistory, MockCallHistoryLog, mockErrors, ProxyAgent, EnvHttpProxyAgent, RedirectHandler, DecoratorHandler, RetryHandler, RetryAgent }
export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, setGlobalOrigin, getGlobalOrigin, interceptors, MockClient, MockPool, MockAgent, MockCallHistory, MockCallHistoryLog, mockErrors, ProxyAgent, EnvHttpProxyAgent, RedirectHandler, DecoratorHandler, RetryHandler, RetryAgent, H2CClient }
export default Undici
declare namespace Undici {
@ -43,6 +44,7 @@ declare namespace Undici {
const RetryHandler: typeof import ('./retry-handler').default
const BalancedPool: typeof import('./balanced-pool').default
const Client: typeof import('./client').default
const H2CClient: typeof import('./h2c-client').default
const buildConnector: typeof import('./connector').default
const errors: typeof import('./errors').default
const Agent: typeof import('./agent').default

View File

@ -1,6 +1,7 @@
/// <reference types="node" />
import type { Blob } from 'buffer'
import type { ReadableStream, WritableStream } from 'stream/web'
import type { MessagePort } from 'worker_threads'
import {
EventInit,

581
deps/undici/undici.js vendored
View File

@ -499,6 +499,242 @@ var require_dispatcher = __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/constants.js
var require_constants = __commonJS({
"lib/core/constants.js"(exports2, module2) {
@ -789,7 +1025,8 @@ var require_util = __commonJS({
var nodeUtil = require("node:util");
var { stringify } = require("node:querystring");
var { EventEmitter: EE } = require("node:events");
var { InvalidArgumentError } = require_errors();
var timers = require_timers();
var { InvalidArgumentError, ConnectTimeoutError } = require_errors();
var { headerNameLowerCasedRecord } = require_constants();
var { tree } = require_tree();
var [nodeMajor, nodeMinor] = process.versions.node.split(".", 2).map((v) => Number(v));
@ -807,6 +1044,9 @@ var require_util = __commonJS({
yield* this[kBody];
}
};
function noop() {
}
__name(noop, "noop");
function wrapRequestBody(body) {
if (isStream(body)) {
if (bodyLength(body) === 0) {
@ -1264,6 +1504,51 @@ var require_util = __commonJS({
}
}
__name(errorRequest, "errorRequest");
var setupConnectTimeout = process.platform === "win32" ? (socketWeakRef, opts) => {
if (!opts.timeout) {
return noop;
}
let s1 = null;
let s2 = null;
const fastTimer = timers.setFastTimeout(() => {
s1 = setImmediate(() => {
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(() => {
s1 = setImmediate(() => {
onConnectTimeout(socketWeakRef.deref(), opts);
});
}, opts.timeout);
return () => {
timers.clearFastTimeout(fastTimer);
clearImmediate(s1);
};
};
function onConnectTimeout(socket, opts) {
if (socket == null) {
return;
}
let message = "Connect Timeout Error";
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(", ")},`;
} else {
message += ` (attempted address: ${opts.hostname}:${opts.port},`;
}
message += ` timeout: ${opts.timeout}ms)`;
destroy(socket, new ConnectTimeoutError(message));
}
__name(onConnectTimeout, "onConnectTimeout");
var kEnumerableProperty = /* @__PURE__ */ Object.create(null);
kEnumerableProperty.enumerable = true;
var normalizedMethodRecordsBase = {
@ -1330,7 +1615,8 @@ var require_util = __commonJS({
nodeMajor,
nodeMinor,
safeHTTPMethods: Object.freeze(["GET", "HEAD", "OPTIONS", "TRACE"]),
wrapRequestBody
wrapRequestBody,
setupConnectTimeout
};
}
});
@ -2374,242 +2660,6 @@ 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) {
@ -2617,11 +2667,7 @@ var require_connect = __commonJS({
var net = require("node:net");
var assert = require("node:assert");
var util = require_util();
var { InvalidArgumentError, ConnectTimeoutError } = require_errors();
var timers = require_timers();
function noop() {
}
__name(noop, "noop");
var { InvalidArgumentError } = require_errors();
var tls;
var SessionCache;
if (global.FinalizationRegistry && !(process.env.NODE_V8_COVERAGE || process.env.UNDICI_NO_FG)) {
@ -2704,7 +2750,6 @@ var require_connect = __commonJS({
servername,
session,
localAddress,
// TODO(HTTP/2): Add support for h2c
ALPNProtocols: allowH2 ? ["http/1.1", "h2"] : ["http/1.1"],
socket: httpSocket,
// upgrade socket connection
@ -2730,7 +2775,7 @@ var require_connect = __commonJS({
const keepAliveInitialDelay = options.keepAliveInitialDelay === void 0 ? 6e4 : options.keepAliveInitialDelay;
socket.setKeepAlive(true, keepAliveInitialDelay);
}
const clearConnectTimeout = setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port });
const clearConnectTimeout = util.setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port });
socket.setNoDelay(true).once(protocol === "https:" ? "secureConnect" : "connect", function() {
queueMicrotask(clearConnectTimeout);
if (callback) {
@ -2750,51 +2795,6 @@ var require_connect = __commonJS({
}, "connect");
}
__name(buildConnector, "buildConnector");
var setupConnectTimeout = process.platform === "win32" ? (socketWeakRef, opts) => {
if (!opts.timeout) {
return noop;
}
let s1 = null;
let s2 = null;
const fastTimer = timers.setFastTimeout(() => {
s1 = setImmediate(() => {
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(() => {
s1 = setImmediate(() => {
onConnectTimeout(socketWeakRef.deref(), opts);
});
}, opts.timeout);
return () => {
timers.clearFastTimeout(fastTimer);
clearImmediate(s1);
};
};
function onConnectTimeout(socket, opts) {
if (socket == null) {
return;
}
let message = "Connect Timeout Error";
if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) {
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");
module2.exports = buildConnector;
}
});
@ -7619,6 +7619,7 @@ var require_client_h2 = __commonJS({
}
assert(client[kRunning] === 0);
client.emit("disconnect", client[kUrl], [client], err);
client.emit("connectionError", client[kUrl], [client], err);
client[kResume]();
}
__name(onHttp2SessionGoAway, "onHttp2SessionGoAway");

View File

@ -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 "7.6.0"
#define UNDICI_VERSION "7.8.0"
#endif // SRC_UNDICI_VERSION_H_