deps: update undici to 7.9.0

PR-URL: https://github.com/nodejs/node/pull/58268
Reviewed-By: Matthew Aitken <maitken033380023@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
Node.js GitHub Bot 2025-05-18 12:09:46 -04:00 committed by GitHub
parent 9eb9c26b26
commit 73e7bd1c0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 793 additions and 620 deletions

View File

@ -261,12 +261,22 @@ const readableWebStream = response.body
const readableNodeStream = Readable.fromWeb(readableWebStream)
```
#### Specification Compliance
## Specification Compliance
This section documents parts of the [Fetch Standard](https://fetch.spec.whatwg.org) that Undici does
This section documents parts of the [HTTP/1.1](https://www.rfc-editor.org/rfc/rfc9110.html) and [Fetch Standard](https://fetch.spec.whatwg.org) that Undici does
not support or does not fully implement.
##### Garbage Collection
#### CORS
Unlike browsers, Undici does not implement CORS (Cross-Origin Resource Sharing) checks by default. This means:
- No preflight requests are automatically sent for cross-origin requests
- No validation of `Access-Control-Allow-Origin` headers is performed
- Requests to any origin are allowed regardless of the source
This behavior is intentional for server-side environments where CORS restrictions are typically unnecessary. If your application requires CORS-like protections, you will need to implement these checks manually.
#### Garbage Collection
* https://fetch.spec.whatwg.org/#garbage-collection
@ -307,7 +317,7 @@ const headers = await fetch(url, { method: 'HEAD' })
.then(res => res.headers)
```
##### Forbidden and Safelisted Header Names
#### Forbidden and Safelisted Header Names
* https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
* https://fetch.spec.whatwg.org/#forbidden-header-name
@ -316,7 +326,7 @@ const headers = await fetch(url, { method: 'HEAD' })
The [Fetch Standard](https://fetch.spec.whatwg.org) requires implementations to exclude certain headers from requests and responses. In browser environments, some headers are forbidden so the user agent remains in full control over them. In Undici, these constraints are removed to give more control to the user.
### `undici.upgrade([url, options]): Promise`
#### `undici.upgrade([url, options]): Promise`
Upgrade to a different protocol. See [MDN - HTTP - Protocol upgrade mechanism](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) for more details.
@ -378,12 +388,7 @@ Returns: `URL`
* **protocol** `string` (optional)
* **search** `string` (optional)
## Specification Compliance
This section documents parts of the HTTP/1.1 specification that Undici does
not support or does not fully implement.
### Expect
#### Expect
Undici does not support the `Expect` request header field. The request
body is always immediately sent and the `100 Continue` response will be
@ -391,7 +396,7 @@ ignored.
Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1
### Pipelining
#### Pipelining
Undici will only use pipelining if configured with a `pipelining` factor
greater than `1`. Also it is important to pass `blocking: false` to the
@ -412,7 +417,7 @@ aborted.
* Refs: https://tools.ietf.org/html/rfc2616#section-8.1.2.2
* Refs: https://tools.ietf.org/html/rfc7230#section-6.3.2
### Manual Redirect
#### Manual Redirect
Since it is not possible to manually follow an HTTP redirect on the server-side,
Undici returns the actual response instead of an `opaqueredirect` filtered one
@ -421,9 +426,9 @@ implementations in Deno and Cloudflare Workers.
Refs: https://fetch.spec.whatwg.org/#atomic-http-redirect-handling
## Workarounds
### Workarounds
### Network address family autoselection.
#### Network address family autoselection.
If you experience problem when connecting to a remote server that is resolved by your DNS servers to a IPv6 (AAAA record)
first, there are chances that your local router or ISP might have problem connecting to IPv6 networks. In that case

View File

@ -75,3 +75,9 @@ See [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatche
### `Agent.upgrade(options[, callback])`
See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).
### `Agent.stats()`
Returns an object of stats by origin in the format of `Record<string, TClientStats | TPoolStats>`
See [`PoolStats`](/docs/docs/api/PoolStats.md) and [`ClientStats`](/docs/docs/api/ClientStats.md).

View File

@ -0,0 +1,27 @@
# Class: ClientStats
Stats for a [Client](/docs/docs/api/Client.md).
## `new ClientStats(client)`
Arguments:
* **client** `Client` - Client from which to return stats.
## Instance Properties
### `ClientStats.connected`
Boolean if socket as open connection by this client.
### `ClientStats.pending`
Number of pending requests of this client.
### `ClientStats.running`
Number of currently active requests across this client.
### `ClientStats.size`
Number of active, pending, or queued requests of this clients.

View File

@ -19,7 +19,7 @@ diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {
console.log('completed', request.completed)
console.log('method', request.method)
console.log('path', request.path)
console.log('headers') // array of strings, e.g: ['foo', 'bar']
console.log('headers', request.headers) // array of strings, e.g: ['foo', 'bar']
request.addHeader('hello', 'world')
console.log('headers', request.headers) // e.g. ['foo', 'bar', 'hello', 'world']
})

View File

@ -20,6 +20,8 @@ Extends: [`AgentOptions`](/docs/docs/api/Agent.md#parameter-agentoptions)
* **ignoreTrailingSlash** `boolean` (optional) - Default: `false` - set the default value for `ignoreTrailingSlash` for interceptors.
* **acceptNonStandardSearchParameters** `boolean` (optional) - Default: `false` - set to `true` if the matcher should also accept non standard search parameters such as multi-value items specified with `[]` (e.g. `param[]=1&param[]=2&param[]=3`) and multi-value items which values are comma separated (e.g. `param=1,2,3`).
### Example - Basic MockAgent instantiation
This will instantiate the MockAgent. It will not do anything until registered as the agent to use with requests and mock interceptions are added.

View File

@ -76,17 +76,9 @@ class MemoryCacheStore {
const topLevelKey = `${key.origin}:${key.path}`
const now = Date.now()
const entry = this.#entries.get(topLevelKey)?.find((entry) => (
entry.deleteAt > now &&
entry.method === key.method &&
(entry.vary == null || Object.keys(entry.vary).every(headerName => {
if (entry.vary[headerName] === null) {
return key.headers[headerName] === undefined
}
const entries = this.#entries.get(topLevelKey)
return entry.vary[headerName] === key.headers[headerName]
}))
))
const entry = entries ? findEntry(key, entries, now) : null
return entry == null
? undefined
@ -140,10 +132,17 @@ class MemoryCacheStore {
entries = []
store.#entries.set(topLevelKey, entries)
}
entries.push(entry)
const previousEntry = findEntry(key, entries, Date.now())
if (previousEntry) {
const index = entries.indexOf(previousEntry)
entries.splice(index, 1, entry)
store.#size -= previousEntry.size
} else {
entries.push(entry)
store.#count += 1
}
store.#size += entry.size
store.#count += 1
if (store.#size > store.#maxSize || store.#count > store.#maxCount) {
for (const [key, entries] of store.#entries) {
@ -180,4 +179,18 @@ class MemoryCacheStore {
}
}
function findEntry (key, entries, now) {
return entries.find((entry) => (
entry.deleteAt > now &&
entry.method === key.method &&
(entry.vary == null || Object.keys(entry.vary).every(headerName => {
if (entry.vary[headerName] === null) {
return key.headers[headerName] === undefined
}
return entry.vary[headerName] === key.headers[headerName]
}))
))
}
module.exports = MemoryCacheStore

View File

@ -1,7 +1,7 @@
'use strict'
const { InvalidArgumentError } = require('../core/errors')
const { kClients, kRunning, kClose, kDestroy, kDispatch } = require('../core/symbols')
const { kClients, kRunning, kClose, kDestroy, kDispatch, kUrl } = require('../core/symbols')
const DispatcherBase = require('./dispatcher-base')
const Pool = require('./pool')
const Client = require('./client')
@ -110,6 +110,16 @@ class Agent extends DispatcherBase {
await Promise.all(destroyPromises)
}
get stats () {
const allClientStats = {}
for (const client of this[kClients].values()) {
if (client.stats) {
allClientStats[client[kUrl].origin] = client.stats
}
}
return allClientStats
}
}
module.exports = Agent

View File

@ -295,11 +295,13 @@ function writeH2 (client, request) {
if (Array.isArray(val)) {
for (let i = 0; i < val.length; i++) {
if (headers[key]) {
headers[key] += `,${val[i]}`
headers[key] += `, ${val[i]}`
} else {
headers[key] = val[i]
}
}
} else if (headers[key]) {
headers[key] += `, ${val}`
} else {
headers[key] = val
}

View File

@ -4,6 +4,7 @@ const assert = require('node:assert')
const net = require('node:net')
const http = require('node:http')
const util = require('../core/util.js')
const { ClientStats } = require('../util/stats.js')
const { channels } = require('../core/diagnostics.js')
const Request = require('../core/request.js')
const DispatcherBase = require('./dispatcher-base')
@ -260,6 +261,10 @@ class Client extends DispatcherBase {
this[kResume](true)
}
get stats () {
return new ClientStats(this)
}
get [kPending] () {
return this[kQueue].length - this[kPendingIdx]
}

View File

@ -1,9 +1,9 @@
'use strict'
const { PoolStats } = require('../util/stats.js')
const DispatcherBase = require('./dispatcher-base')
const FixedQueue = require('./fixed-queue')
const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require('../core/symbols')
const PoolStats = require('./pool-stats')
const kClients = Symbol('clients')
const kNeedDrain = Symbol('needDrain')
@ -16,7 +16,6 @@ const kOnConnectionError = Symbol('onConnectionError')
const kGetDispatcher = Symbol('get dispatcher')
const kAddClient = Symbol('add client')
const kRemoveClient = Symbol('remove client')
const kStats = Symbol('stats')
class PoolBase extends DispatcherBase {
constructor () {
@ -67,8 +66,6 @@ class PoolBase extends DispatcherBase {
this[kOnConnectionError] = (origin, targets, err) => {
pool.emit('connectionError', origin, [pool, ...targets], err)
}
this[kStats] = new PoolStats(this)
}
get [kBusy] () {
@ -108,7 +105,7 @@ class PoolBase extends DispatcherBase {
}
get stats () {
return this[kStats]
return new PoolStats(this)
}
async [kClose] () {

View File

@ -1,36 +0,0 @@
'use strict'
const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = require('../core/symbols')
const kPool = Symbol('pool')
class PoolStats {
constructor (pool) {
this[kPool] = pool
}
get connected () {
return this[kPool][kConnected]
}
get free () {
return this[kPool][kFree]
}
get pending () {
return this[kPool][kPending]
}
get queued () {
return this[kPool][kQueued]
}
get running () {
return this[kPool][kRunning]
}
get size () {
return this[kPool][kSize]
}
}
module.exports = PoolStats

View File

@ -20,7 +20,12 @@ const { AbortError } = require('../core/errors.js')
*/
function needsRevalidation (result, cacheControlDirectives) {
if (cacheControlDirectives?.['no-cache']) {
// Always revalidate requests with the no-cache directive
// Always revalidate requests with the no-cache request directive
return true
}
if (result.cacheControlDirectives?.['no-cache'] && !Array.isArray(result.cacheControlDirectives['no-cache'])) {
// Always revalidate requests with unqualified no-cache response directive
return true
}
@ -233,7 +238,7 @@ function handleResult (
}
let headers = {
...normaliseHeaders(opts),
...opts.headers,
'if-modified-since': new Date(result.cachedAt).toUTCString()
}
@ -319,6 +324,11 @@ module.exports = (opts = {}) => {
return dispatch(opts, handler)
}
opts = {
...opts,
headers: normaliseHeaders(opts)
}
const reqCacheControl = opts.headers?.['cache-control']
? parseCacheControlHeader(opts.headers['cache-control'])
: undefined

View File

@ -1,5 +1,5 @@
> undici@7.8.0 build:wasm
> undici@7.9.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

View File

@ -16,11 +16,12 @@ const {
kMockAgentIsCallHistoryEnabled,
kMockAgentAddCallHistoryLog,
kMockAgentMockCallHistoryInstance,
kMockAgentAcceptsNonStandardSearchParameters,
kMockCallHistoryAddLog
} = require('./mock-symbols')
const MockClient = require('./mock-client')
const MockPool = require('./mock-pool')
const { matchValue, buildAndValidateMockOptions } = require('./mock-utils')
const { matchValue, normalizeSearchParams, buildAndValidateMockOptions } = require('./mock-utils')
const { InvalidArgumentError, UndiciError } = require('../core/errors')
const Dispatcher = require('../dispatcher/dispatcher')
const PendingInterceptorsFormatter = require('./pending-interceptors-formatter')
@ -35,6 +36,7 @@ class MockAgent extends Dispatcher {
this[kNetConnect] = true
this[kIsMockActive] = true
this[kMockAgentIsCallHistoryEnabled] = mockOptions?.enableCallHistory ?? false
this[kMockAgentAcceptsNonStandardSearchParameters] = mockOptions?.acceptNonStandardSearchParameters ?? false
// Instantiate Agent and encapsulate
if (opts?.agent && typeof opts.agent.dispatch !== 'function') {
@ -67,7 +69,17 @@ class MockAgent extends Dispatcher {
this[kMockAgentAddCallHistoryLog](opts)
return this[kAgent].dispatch(opts, handler)
const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters]
const dispatchOpts = { ...opts }
if (acceptNonStandardSearchParameters && dispatchOpts.path) {
const [path, searchParams] = dispatchOpts.path.split('?')
const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters)
dispatchOpts.path = `${path}?${normalizedSearchParams}`
}
return this[kAgent].dispatch(dispatchOpts, handler)
}
async close () {

View File

@ -26,5 +26,6 @@ module.exports = {
kMockAgentRegisterCallHistory: Symbol('mock agent register mock call history'),
kMockAgentAddCallHistoryLog: Symbol('mock agent add call history log'),
kMockAgentIsCallHistoryEnabled: Symbol('mock agent is call history enabled'),
kMockAgentAcceptsNonStandardSearchParameters: Symbol('mock agent accepts non standard search parameters'),
kMockCallHistoryAddLog: Symbol('mock call history add log')
}

View File

@ -92,13 +92,42 @@ function matchHeaders (mockDispatch, headers) {
return true
}
function normalizeSearchParams (query) {
if (typeof query !== 'string') {
return query
}
const originalQp = new URLSearchParams(query)
const normalizedQp = new URLSearchParams()
for (let [key, value] of originalQp.entries()) {
key = key.replace('[]', '')
const valueRepresentsString = /^(['"]).*\1$/.test(value)
if (valueRepresentsString) {
normalizedQp.append(key, value)
continue
}
if (value.includes(',')) {
const values = value.split(',')
for (const v of values) {
normalizedQp.append(key, v)
}
continue
}
normalizedQp.append(key, value)
}
return normalizedQp
}
function safeUrl (path) {
if (typeof path !== 'string') {
return path
}
const pathSegments = path.split('?', 3)
if (pathSegments.length !== 2) {
return path
}
@ -376,6 +405,10 @@ function buildAndValidateMockOptions (opts) {
throw new InvalidArgumentError('options.enableCallHistory must to be a boolean')
}
if ('acceptNonStandardSearchParameters' in mockOptions && typeof mockOptions.acceptNonStandardSearchParameters !== 'boolean') {
throw new InvalidArgumentError('options.acceptNonStandardSearchParameters must to be a boolean')
}
return mockOptions
}
}
@ -395,5 +428,6 @@ module.exports = {
checkNetConnect,
buildAndValidateMockOptions,
getHeaderByName,
buildHeadersFromArray
buildHeadersFromArray,
normalizeSearchParams
}

View File

@ -12,13 +12,11 @@ function makeCacheKey (opts) {
throw new Error('opts.origin is undefined')
}
const headers = normaliseHeaders(opts)
return {
origin: opts.origin.toString(),
method: opts.method,
path: opts.path,
headers
headers: opts.headers
}
}

32
deps/undici/src/lib/util/stats.js vendored Normal file
View File

@ -0,0 +1,32 @@
'use strict'
const {
kConnected,
kPending,
kRunning,
kSize,
kFree,
kQueued
} = require('../core/symbols')
class ClientStats {
constructor (client) {
this.connected = client[kConnected]
this.pending = client[kPending]
this.running = client[kRunning]
this.size = client[kSize]
}
}
class PoolStats {
constructor (pool) {
this.connected = pool[kConnected]
this.free = pool[kFree]
this.pending = pool[kPending]
this.queued = pool[kQueued]
this.running = pool[kRunning]
this.size = pool[kSize]
}
}
module.exports = { ClientStats, PoolStats }

995
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.8.0",
"version": "7.9.0",
"description": "An HTTP/1.1 client, written from scratch for Node.js",
"homepage": "https://undici.nodejs.org",
"bugs": {

View File

@ -1,6 +1,8 @@
import { URL } from 'url'
import Pool from './pool'
import Dispatcher from './dispatcher'
import TClientStats from './client-stats'
import TPoolStats from './pool-stats'
export default Agent
@ -12,6 +14,8 @@ declare class Agent extends Dispatcher {
destroyed: boolean
/** Dispatches a request. */
dispatch (options: Agent.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean
/** Aggregate stats for a Agent by origin. */
readonly stats: Record<string, TClientStats | TPoolStats>
}
declare namespace Agent {

15
deps/undici/src/types/client-stats.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
import Client from './client'
export default ClientStats
declare class ClientStats {
constructor (pool: Client)
/** If socket has open connection. */
connected: boolean
/** Number of open socket connections in this client that do not have an active request. */
pending: number
/** Number of currently active requests of this client. */
running: number
/** Number of active, pending, or queued requests of this client. */
size: number
}

View File

@ -1,6 +1,7 @@
import { URL } from 'url'
import Dispatcher from './dispatcher'
import buildConnector from './connector'
import TClientStats from './client-stats'
type ClientConnectOptions = Omit<Dispatcher.ConnectOptions, 'origin'>
@ -15,6 +16,8 @@ export class Client extends Dispatcher {
closed: boolean
/** `true` after `client.destroyed()` has been called or `client.close()` has been called and the client shutdown has completed. */
destroyed: boolean
/** Aggregate stats for a Client. */
readonly stats: TClientStats
// Override dispatcher APIs.
override connect (
@ -84,13 +87,13 @@ export declare namespace Client {
/**
* @description Enables support for H2 if the server has assigned bigger priority to it through ALPN negotiation.
* @default false
*/
*/
allowH2?: boolean;
/**
* @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
*/
maxConcurrentStreams?: number;
}
export interface SocketInfo {
localAddress?: string

View File

@ -59,6 +59,9 @@ declare namespace MockAgent {
/** Ignore trailing slashes in the path */
ignoreTrailingSlash?: boolean;
/** Accept URLs with search parameters using non standard syntaxes. default false */
acceptNonStandardSearchParameters?: boolean;
/** Enable call history. you can either call MockAgent.enableCallHistory(). default false */
enableCallHistory?: boolean
}

101
deps/undici/undici.js vendored
View File

@ -1846,6 +1846,46 @@ var require_dispatcher_base = __commonJS({
}
});
// lib/util/stats.js
var require_stats = __commonJS({
"lib/util/stats.js"(exports2, module2) {
"use strict";
var {
kConnected,
kPending,
kRunning,
kSize,
kFree,
kQueued
} = require_symbols();
var ClientStats = class {
static {
__name(this, "ClientStats");
}
constructor(client) {
this.connected = client[kConnected];
this.pending = client[kPending];
this.running = client[kRunning];
this.size = client[kSize];
}
};
var PoolStats = class {
static {
__name(this, "PoolStats");
}
constructor(pool) {
this.connected = pool[kConnected];
this.free = pool[kFree];
this.pending = pool[kPending];
this.queued = pool[kQueued];
this.running = pool[kRunning];
this.size = pool[kSize];
}
};
module2.exports = { ClientStats, PoolStats };
}
});
// lib/dispatcher/fixed-queue.js
var require_fixed_queue = __commonJS({
"lib/dispatcher/fixed-queue.js"(exports2, module2) {
@ -1933,50 +1973,14 @@ var require_fixed_queue = __commonJS({
}
});
// lib/dispatcher/pool-stats.js
var require_pool_stats = __commonJS({
"lib/dispatcher/pool-stats.js"(exports2, module2) {
"use strict";
var { kFree, kConnected, kPending, kQueued, kRunning, kSize } = require_symbols();
var kPool = Symbol("pool");
var PoolStats = class {
static {
__name(this, "PoolStats");
}
constructor(pool) {
this[kPool] = pool;
}
get connected() {
return this[kPool][kConnected];
}
get free() {
return this[kPool][kFree];
}
get pending() {
return this[kPool][kPending];
}
get queued() {
return this[kPool][kQueued];
}
get running() {
return this[kPool][kRunning];
}
get size() {
return this[kPool][kSize];
}
};
module2.exports = PoolStats;
}
});
// lib/dispatcher/pool-base.js
var require_pool_base = __commonJS({
"lib/dispatcher/pool-base.js"(exports2, module2) {
"use strict";
var { PoolStats } = require_stats();
var DispatcherBase = require_dispatcher_base();
var FixedQueue = require_fixed_queue();
var { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = require_symbols();
var PoolStats = require_pool_stats();
var kClients = Symbol("clients");
var kNeedDrain = Symbol("needDrain");
var kQueue = Symbol("queue");
@ -1988,7 +1992,6 @@ var require_pool_base = __commonJS({
var kGetDispatcher = Symbol("get dispatcher");
var kAddClient = Symbol("add client");
var kRemoveClient = Symbol("remove client");
var kStats = Symbol("stats");
var PoolBase = class extends DispatcherBase {
static {
__name(this, "PoolBase");
@ -2028,7 +2031,6 @@ var require_pool_base = __commonJS({
this[kOnConnectionError] = (origin, targets, err) => {
pool.emit("connectionError", origin, [pool, ...targets], err);
};
this[kStats] = new PoolStats(this);
}
get [kBusy]() {
return this[kNeedDrain];
@ -2061,7 +2063,7 @@ var require_pool_base = __commonJS({
return ret;
}
get stats() {
return this[kStats];
return new PoolStats(this);
}
async [kClose]() {
if (this[kQueue].isEmpty()) {
@ -7687,11 +7689,13 @@ var require_client_h2 = __commonJS({
if (Array.isArray(val)) {
for (let i = 0; i < val.length; i++) {
if (headers[key]) {
headers[key] += `,${val[i]}`;
headers[key] += `, ${val[i]}`;
} else {
headers[key] = val[i];
}
}
} else if (headers[key]) {
headers[key] += `, ${val}`;
} else {
headers[key] = val;
}
@ -8066,6 +8070,7 @@ var require_client = __commonJS({
var net = require("node:net");
var http = require("node:http");
var util = require_util();
var { ClientStats } = require_stats();
var { channels } = require_diagnostics();
var Request = require_request();
var DispatcherBase = require_dispatcher_base();
@ -8275,6 +8280,9 @@ var require_client = __commonJS({
this[kPipelining] = value;
this[kResume](true);
}
get stats() {
return new ClientStats(this);
}
get [kPending]() {
return this[kQueue].length - this[kPendingIdx];
}
@ -8652,7 +8660,7 @@ var require_agent = __commonJS({
"lib/dispatcher/agent.js"(exports2, module2) {
"use strict";
var { InvalidArgumentError } = require_errors();
var { kClients, kRunning, kClose, kDestroy, kDispatch } = require_symbols();
var { kClients, kRunning, kClose, kDestroy, kDispatch, kUrl } = require_symbols();
var DispatcherBase = require_dispatcher_base();
var Pool = require_pool();
var Client = require_client();
@ -8735,6 +8743,15 @@ var require_agent = __commonJS({
this[kClients].clear();
await Promise.all(destroyPromises);
}
get stats() {
const allClientStats = {};
for (const client of this[kClients].values()) {
if (client.stats) {
allClientStats[client[kUrl].origin] = client.stats;
}
}
return allClientStats;
}
};
module2.exports = Agent;
}

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.8.0"
#define UNDICI_VERSION "7.9.0"
#endif // SRC_UNDICI_VERSION_H_