Skip to content

Commit 7e62a77

Browse files
nodejs-github-bottargos
authored andcommitted
deps: update undici to 7.10.0
PR-URL: #58445 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: Matthew Aitken <[email protected]>
1 parent fcc881d commit 7e62a77

File tree

16 files changed

+354
-78
lines changed

16 files changed

+354
-78
lines changed

deps/undici/src/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ typings/
6363
# lock files
6464
package-lock.json
6565
yarn.lock
66+
pnpm-lock.yaml
6667

6768
# IDE files
6869
.idea

deps/undici/src/docs/docs/api/CacheStore.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,28 @@ The `MemoryCacheStore` stores the responses in-memory.
1313

1414
**Options**
1515

16+
- `maxSize` - The maximum total size in bytes of all stored responses. Default `Infinity`.
1617
- `maxCount` - The maximum amount of responses to store. Default `Infinity`.
17-
- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached.
18+
- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached. Default `Infinity`.
19+
20+
### Getters
21+
22+
#### `MemoryCacheStore.size`
23+
24+
Returns the current total size in bytes of all stored responses.
25+
26+
### Methods
27+
28+
#### `MemoryCacheStore.isFull()`
29+
30+
Returns a boolean indicating whether the cache has reached its maximum size or count.
31+
32+
### Events
33+
34+
#### `'maxSizeExceeded'`
35+
36+
Emitted when the cache exceeds its maximum size or count limits. The event payload contains `size`, `maxSize`, `count`, and `maxCount` properties.
37+
1838

1939
### `SqliteCacheStore`
2040

@@ -26,7 +46,7 @@ The `SqliteCacheStore` is only exposed if the `node:sqlite` api is present.
2646

2747
- `location` - The location of the SQLite database to use. Default `:memory:`.
2848
- `maxCount` - The maximum number of entries to store in the database. Default `Infinity`.
29-
- `maxEntrySize` - The maximum size in bytes that a resposne's body can be. If a response's body is greater than or equal to this, the response will not be cached. Default `Infinity`.
49+
- `maxEntrySize` - The maximum size in bytes that a response's body can be. If a response's body is greater than or equal to this, the response will not be cached. Default `Infinity`.
3050

3151
## Defining a Custom Cache Store
3252

deps/undici/src/docs/docs/api/Pool.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Extends: [`ClientOptions`](/docs/docs/api/Client.md#parameter-clientoptions)
1919

2020
* **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)`
2121
* **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `Pool` instance will create an unlimited amount of `Client` instances.
22+
* **clientTtl** `number | null` (optional) - Default: `null` - The amount of time before a `Client` instance is removed from the `Pool` and closed. When set to `null`, `Client` instances will not be removed or closed based on age.
2223

2324
## Instance Properties
2425

deps/undici/src/docs/docs/api/ProxyAgent.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ For detailed information on the parsing process and potential validation errors,
2525
* **clientFactory** `(origin: URL, opts: Object) => Dispatcher` (optional) - Default: `(origin, opts) => new Pool(origin, opts)`
2626
* **requestTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the request. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).
2727
* **proxyTls** `BuildOptions` (optional) - Options object passed when creating the underlying socket via the connector builder for the proxy server. It extends from [`Client#ConnectOptions`](/docs/docs/api/Client.md#parameter-connectoptions).
28+
* **proxyTunnel** `boolean` (optional) - By default, ProxyAgent will request that the Proxy facilitate a tunnel between the endpoint and the agent. Setting `proxyTunnel` to false avoids issuing a CONNECT extension, and includes the endpoint domain and path in each request.
2829

2930
Examples:
3031

deps/undici/src/lib/cache/memory-cache-store.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const { Writable } = require('node:stream')
4+
const { EventEmitter } = require('node:events')
45
const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
56

67
/**
@@ -12,20 +13,23 @@ const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
1213

1314
/**
1415
* @implements {CacheStore}
16+
* @extends {EventEmitter}
1517
*/
16-
class MemoryCacheStore {
18+
class MemoryCacheStore extends EventEmitter {
1719
#maxCount = Infinity
1820
#maxSize = Infinity
1921
#maxEntrySize = Infinity
2022

2123
#size = 0
2224
#count = 0
2325
#entries = new Map()
26+
#hasEmittedMaxSizeEvent = false
2427

2528
/**
2629
* @param {import('../../types/cache-interceptor.d.ts').default.MemoryCacheStoreOpts | undefined} [opts]
2730
*/
2831
constructor (opts) {
32+
super()
2933
if (opts) {
3034
if (typeof opts !== 'object') {
3135
throw new TypeError('MemoryCacheStore options must be an object')
@@ -66,6 +70,22 @@ class MemoryCacheStore {
6670
}
6771
}
6872

73+
/**
74+
* Get the current size of the cache in bytes
75+
* @returns {number} The current size of the cache in bytes
76+
*/
77+
get size () {
78+
return this.#size
79+
}
80+
81+
/**
82+
* Check if the cache is full (either max size or max count reached)
83+
* @returns {boolean} True if the cache is full, false otherwise
84+
*/
85+
isFull () {
86+
return this.#size >= this.#maxSize || this.#count >= this.#maxCount
87+
}
88+
6989
/**
7090
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} req
7191
* @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}
@@ -144,7 +164,20 @@ class MemoryCacheStore {
144164

145165
store.#size += entry.size
146166

167+
// Check if cache is full and emit event if needed
147168
if (store.#size > store.#maxSize || store.#count > store.#maxCount) {
169+
// Emit maxSizeExceeded event if we haven't already
170+
if (!store.#hasEmittedMaxSizeEvent) {
171+
store.emit('maxSizeExceeded', {
172+
size: store.#size,
173+
maxSize: store.#maxSize,
174+
count: store.#count,
175+
maxCount: store.#maxCount
176+
})
177+
store.#hasEmittedMaxSizeEvent = true
178+
}
179+
180+
// Perform eviction
148181
for (const [key, entries] of store.#entries) {
149182
for (const entry of entries.splice(0, entries.length / 2)) {
150183
store.#size -= entry.size
@@ -154,6 +187,11 @@ class MemoryCacheStore {
154187
store.#entries.delete(key)
155188
}
156189
}
190+
191+
// Reset the event flag after eviction
192+
if (store.#size < store.#maxSize && store.#count < store.#maxCount) {
193+
store.#hasEmittedMaxSizeEvent = false
194+
}
157195
}
158196

159197
callback(null)

deps/undici/src/lib/dispatcher/agent.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,35 @@ class Agent extends DispatcherBase {
4545
}
4646

4747
this[kOnConnect] = (origin, targets) => {
48+
const result = this[kClients].get(origin)
49+
if (result) {
50+
result.count += 1
51+
}
4852
this.emit('connect', origin, [this, ...targets])
4953
}
5054

5155
this[kOnDisconnect] = (origin, targets, err) => {
56+
const result = this[kClients].get(origin)
57+
if (result) {
58+
result.count -= 1
59+
if (result.count <= 0) {
60+
this[kClients].delete(origin)
61+
result.dispatcher.destroy()
62+
}
63+
}
5264
this.emit('disconnect', origin, [this, ...targets], err)
5365
}
5466

5567
this[kOnConnectionError] = (origin, targets, err) => {
68+
// TODO: should this decrement result.count here?
5669
this.emit('connectionError', origin, [this, ...targets], err)
5770
}
5871
}
5972

6073
get [kRunning] () {
6174
let ret = 0
62-
for (const client of this[kClients].values()) {
63-
ret += client[kRunning]
75+
for (const { dispatcher } of this[kClients].values()) {
76+
ret += dispatcher[kRunning]
6477
}
6578
return ret
6679
}
@@ -73,28 +86,25 @@ class Agent extends DispatcherBase {
7386
throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.')
7487
}
7588

76-
let dispatcher = this[kClients].get(key)
77-
89+
const result = this[kClients].get(key)
90+
let dispatcher = result && result.dispatcher
7891
if (!dispatcher) {
7992
dispatcher = this[kFactory](opts.origin, this[kOptions])
8093
.on('drain', this[kOnDrain])
8194
.on('connect', this[kOnConnect])
8295
.on('disconnect', this[kOnDisconnect])
8396
.on('connectionError', this[kOnConnectionError])
8497

85-
// This introduces a tiny memory leak, as dispatchers are never removed from the map.
86-
// TODO(mcollina): remove te timer when the client/pool do not have any more
87-
// active connections.
88-
this[kClients].set(key, dispatcher)
98+
this[kClients].set(key, { count: 0, dispatcher })
8999
}
90100

91101
return dispatcher.dispatch(opts, handler)
92102
}
93103

94104
async [kClose] () {
95105
const closePromises = []
96-
for (const client of this[kClients].values()) {
97-
closePromises.push(client.close())
106+
for (const { dispatcher } of this[kClients].values()) {
107+
closePromises.push(dispatcher.close())
98108
}
99109
this[kClients].clear()
100110

@@ -103,8 +113,8 @@ class Agent extends DispatcherBase {
103113

104114
async [kDestroy] (err) {
105115
const destroyPromises = []
106-
for (const client of this[kClients].values()) {
107-
destroyPromises.push(client.destroy(err))
116+
for (const { dispatcher } of this[kClients].values()) {
117+
destroyPromises.push(dispatcher.destroy(err))
108118
}
109119
this[kClients].clear()
110120

@@ -113,9 +123,9 @@ class Agent extends DispatcherBase {
113123

114124
get stats () {
115125
const allClientStats = {}
116-
for (const client of this[kClients].values()) {
117-
if (client.stats) {
118-
allClientStats[client[kUrl].origin] = client.stats
126+
for (const { dispatcher } of this[kClients].values()) {
127+
if (dispatcher.stats) {
128+
allClientStats[dispatcher[kUrl].origin] = dispatcher.stats
119129
}
120130
}
121131
return allClientStats

deps/undici/src/lib/dispatcher/pool.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const {
55
kClients,
66
kNeedDrain,
77
kAddClient,
8-
kGetDispatcher
8+
kGetDispatcher,
9+
kRemoveClient
910
} = require('./pool-base')
1011
const Client = require('./client')
1112
const {
@@ -35,6 +36,7 @@ class Pool extends PoolBase {
3536
autoSelectFamily,
3637
autoSelectFamilyAttemptTimeout,
3738
allowH2,
39+
clientTtl,
3840
...options
3941
} = {}) {
4042
if (connections != null && (!Number.isFinite(connections) || connections < 0)) {
@@ -65,12 +67,20 @@ class Pool extends PoolBase {
6567

6668
this[kConnections] = connections || null
6769
this[kUrl] = util.parseOrigin(origin)
68-
this[kOptions] = { ...util.deepClone(options), connect, allowH2 }
70+
this[kOptions] = { ...util.deepClone(options), connect, allowH2, clientTtl }
6971
this[kOptions].interceptors = options.interceptors
7072
? { ...options.interceptors }
7173
: undefined
7274
this[kFactory] = factory
7375

76+
this.on('connect', (origin, targets) => {
77+
if (clientTtl != null && clientTtl > 0) {
78+
for (const target of targets) {
79+
Object.assign(target, { ttl: Date.now() })
80+
}
81+
}
82+
})
83+
7484
this.on('connectionError', (origin, targets, error) => {
7585
// If a connection error occurs, we remove the client from the pool,
7686
// and emit a connectionError event. They will not be re-used.
@@ -87,8 +97,12 @@ class Pool extends PoolBase {
8797
}
8898

8999
[kGetDispatcher] () {
100+
const clientTtlOption = this[kOptions].clientTtl
90101
for (const client of this[kClients]) {
91-
if (!client[kNeedDrain]) {
102+
// check ttl of client and if it's stale, remove it from the pool
103+
if (clientTtlOption != null && clientTtlOption > 0 && client.ttl && ((Date.now() - client.ttl) > clientTtlOption)) {
104+
this[kRemoveClient](client)
105+
} else if (!client[kNeedDrain]) {
92106
return client
93107
}
94108
}

0 commit comments

Comments
 (0)