deps: update undici to 7.28.0 · nodejs/node@cf44df3 · GitHub
Skip to content

Commit cf44df3

Browse files
nodejs-github-botaduh95
authored andcommitted
deps: update undici to 7.28.0
PR-URL: #63703 Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent f95bedd commit cf44df3

37 files changed

Lines changed: 1766 additions & 1092 deletions

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

Lines changed: 3 additions & 0 deletions

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,33 @@ Arguments:
8080

8181
Returns: `Cookie[]`
8282

83+
## `parseCookie(cookie)`
84+
85+
Parses a single `Set-Cookie` header value into a `Cookie` object.
86+
87+
```js
88+
import { parseCookie } from 'undici'
89+
90+
console.log(parseCookie('undici=getSetCookies; Secure; SameSite=Lax'))
91+
// {
92+
// name: 'undici',
93+
// value: 'getSetCookies',
94+
// secure: true,
95+
// sameSite: 'Lax'
96+
// }
97+
```
98+
99+
Notes:
100+
101+
* The cookie value is returned as it appears in the header. Percent-encoded sequences such as `%20` or `%0D%0A` are **not** decoded.
102+
* `sameSite` is only set for exact case-insensitive matches of `Strict`, `Lax`, or `None`.
103+
104+
Arguments:
105+
106+
* **cookie** `string`
107+
108+
Returns: `Cookie | null`
109+
83110
## `setCookie(headers, cookie)`
84111

85112
Appends a cookie to the `Set-Cookie` header.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Extends: [`PoolOptions`](/docs/docs/api/Pool.md#parameter-pooloptions)
2222
* **password** `string` (optional) - SOCKS5 proxy password for authentication. Can also be provided in the proxy URL.
2323
* **connect** `Function` (optional) - Custom connector function for the proxy connection.
2424
* **proxyTls** `BuildOptions` (optional) - TLS options for the proxy connection (when using SOCKS5 over TLS).
25+
* **requestTls** `BuildOptions` (optional) - TLS options applied to the HTTPS connection to the target server through the SOCKS5 tunnel. Use this to configure `ca`, `cert`, `key`, `rejectUnauthorized`, `servername`, etc. for the target HTTPS endpoint.
2526

2627
Examples:
2728

deps/undici/src/lib/api/api-request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class RequestHandler extends AsyncResource {
2121
throw new InvalidArgumentError('invalid callback')
2222
}
2323

24-
if (highWaterMark && (typeof highWaterMark !== 'number' || highWaterMark < 0)) {
24+
if (highWaterMark != null && (!Number.isFinite(highWaterMark) || highWaterMark < 0)) {
2525
throw new InvalidArgumentError('invalid highWaterMark')
2626
}
2727

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ module.exports = class SqliteCacheStore {
216216
SELECT
217217
id
218218
FROM cacheInterceptorV${VERSION}
219-
ORDER BY cachedAt DESC
219+
ORDER BY cachedAt ASC
220220
LIMIT ?
221221
)
222222
`)
@@ -283,7 +283,6 @@ module.exports = class SqliteCacheStore {
283283
existingValue.id
284284
)
285285
} else {
286-
this.#prune()
287286
// New response, let's insert it
288287
this.#insertValueQuery.run(
289288
url,
@@ -299,6 +298,7 @@ module.exports = class SqliteCacheStore {
299298
value.cachedAt,
300299
value.staleAt
301300
)
301+
this.#prune()
302302
}
303303
}
304304

@@ -409,7 +409,7 @@ module.exports = class SqliteCacheStore {
409409
const now = Date.now()
410410
for (const value of values) {
411411
if (now >= value.deleteAt && !canBeExpired) {
412-
return undefined
412+
continue
413413
}
414414

415415
let matches = true

deps/undici/src/lib/core/connect.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ const SessionCache = class WeakSessionCache {
3838
return
3939
}
4040

41+
if (this._sessionCache.has(sessionKey)) {
42+
this._sessionCache.delete(sessionKey)
43+
} else if (this._sessionCache.size >= this._maxCachedSessions) {
44+
for (const [key, ref] of this._sessionCache) {
45+
if (ref.deref() === undefined) {
46+
this._sessionCache.delete(key)
47+
return
48+
}
49+
}
50+
51+
const oldest = this._sessionCache.keys().next()
52+
if (!oldest.done) {
53+
this._sessionCache.delete(oldest.value)
54+
}
55+
}
56+
4157
this._sessionCache.set(sessionKey, new WeakRef(session))
4258
this._sessionRegistry.register(session, sessionKey)
4359
}

deps/undici/src/lib/core/request.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ const { headerNameLowerCasedRecord } = require('./constants')
2727
// Verifies that a given path is valid does not contain control chars \x00 to \x20
2828
const invalidPathRegex = /[^\u0021-\u00ff]/
2929

30+
function isValidContentLengthHeaderValue (val) {
31+
if (typeof val !== 'string' || val.length === 0) {
32+
return false
33+
}
34+
35+
for (let i = 0; i < val.length; i++) {
36+
const charCode = val.charCodeAt(i)
37+
if (charCode < 48 || charCode > 57) {
38+
return false
39+
}
40+
}
41+
42+
return true
43+
}
44+
3045
const kHandler = Symbol('handler')
3146

3247
class Request {
@@ -402,10 +417,10 @@ function processHeader (request, key, val) {
402417
if (request.contentLength !== null) {
403418
throw new InvalidArgumentError('duplicate content-length header')
404419
}
405-
request.contentLength = parseInt(val, 10)
406-
if (!Number.isFinite(request.contentLength)) {
420+
if (!isValidContentLengthHeaderValue(val)) {
407421
throw new InvalidArgumentError('invalid content-length header')
408422
}
423+
request.contentLength = parseInt(val, 10)
409424
} else if (request.contentType === null && headerName === 'content-type') {
410425
request.contentType = val
411426
request.headers.push(key, val)

deps/undici/src/lib/core/socks5-client.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { debuglog } = require('node:util')
77
const { parseAddress } = require('./socks5-utils')
88

99
const debug = debuglog('undici:socks5')
10+
const EMPTY_BUFFER = Buffer.alloc(0)
1011

1112
// SOCKS5 constants
1213
const SOCKS_VERSION = 0x05
@@ -51,6 +52,7 @@ const STATES = {
5152
INITIAL: 'initial',
5253
HANDSHAKING: 'handshaking',
5354
AUTHENTICATING: 'authenticating',
55+
AUTHENTICATED: 'authenticated',
5456
CONNECTING: 'connecting',
5557
CONNECTED: 'connected',
5658
ERROR: 'error',
@@ -72,7 +74,10 @@ class Socks5Client extends EventEmitter {
7274
this.socket = socket
7375
this.options = options
7476
this.state = STATES.INITIAL
75-
this.buffer = Buffer.alloc(0)
77+
this.buffer = EMPTY_BUFFER
78+
this.onSocketData = this.onData.bind(this)
79+
this.onSocketError = this.onError.bind(this)
80+
this.onSocketClose = this.onClose.bind(this)
7681

7782
// Authentication settings
7883
this.authMethods = []
@@ -82,9 +87,9 @@ class Socks5Client extends EventEmitter {
8287
this.authMethods.push(AUTH_METHODS.NO_AUTH)
8388

8489
// Socket event handlers
85-
this.socket.on('data', this.onData.bind(this))
86-
this.socket.on('error', this.onError.bind(this))
87-
this.socket.on('close', this.onClose.bind(this))
90+
this.socket.on('data', this.onSocketData)
91+
this.socket.on('error', this.onSocketError)
92+
this.socket.on('close', this.onSocketClose)
8893
}
8994

9095
/**
@@ -139,6 +144,11 @@ class Socks5Client extends EventEmitter {
139144
}
140145
}
141146

147+
markAuthenticated () {
148+
this.state = STATES.AUTHENTICATED
149+
this.emit('authenticated')
150+
}
151+
142152
/**
143153
* Start the SOCKS5 handshake
144154
*/
@@ -189,7 +199,7 @@ class Socks5Client extends EventEmitter {
189199
debug('server selected auth method', method)
190200

191201
if (method === AUTH_METHODS.NO_AUTH) {
192-
this.emit('authenticated')
202+
this.markAuthenticated()
193203
} else if (method === AUTH_METHODS.USERNAME_PASSWORD) {
194204
this.state = STATES.AUTHENTICATING
195205
this.sendAuthRequest()
@@ -254,7 +264,7 @@ class Socks5Client extends EventEmitter {
254264

255265
this.buffer = this.buffer.subarray(2)
256266
debug('authentication successful')
257-
this.emit('authenticated')
267+
this.markAuthenticated()
258268
}
259269

260270
/**
@@ -263,8 +273,12 @@ class Socks5Client extends EventEmitter {
263273
* @param {number} port - Target port
264274
*/
265275
connect (address, port) {
266-
if (this.state === STATES.CONNECTED) {
267-
throw new InvalidArgumentError('Already connected')
276+
if (this.state === STATES.CONNECTING || this.state === STATES.CONNECTED) {
277+
throw new InvalidArgumentError('Connection already in progress')
278+
}
279+
280+
if (this.state !== STATES.AUTHENTICATED) {
281+
throw new InvalidArgumentError('Client must be authenticated before CONNECT')
268282
}
269283

270284
debug('connecting to', address, port)
@@ -363,8 +377,9 @@ class Socks5Client extends EventEmitter {
363377

364378
const boundPort = this.buffer.readUInt16BE(offset)
365379

366-
this.buffer = this.buffer.subarray(responseLength)
380+
this.buffer = EMPTY_BUFFER
367381
this.state = STATES.CONNECTED
382+
this.socket.removeListener('data', this.onSocketData)
368383

369384
debug('connected, bound address:', boundAddress, 'port:', boundPort)
370385
this.emit('connected', { address: boundAddress, port: boundPort })

deps/undici/src/lib/core/socks5-utils.js

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,34 +46,43 @@ function parseAddress (address) {
4646
*/
4747
function parseIPv6 (address) {
4848
const buffer = Buffer.alloc(16)
49-
const parts = address.split(':')
50-
let partIndex = 0
51-
let bufferIndex = 0
49+
let normalizedAddress = address
50+
51+
// Expand an embedded IPv4 tail into the last two IPv6 groups.
52+
if (address.includes('.')) {
53+
const lastColonIndex = address.lastIndexOf(':')
54+
const ipv4Part = address.slice(lastColonIndex + 1)
55+
56+
if (net.isIPv4(ipv4Part)) {
57+
const octets = ipv4Part.split('.').map(Number)
58+
const high = ((octets[0] << 8) | octets[1]).toString(16)
59+
const low = ((octets[2] << 8) | octets[3]).toString(16)
60+
normalizedAddress = `${address.slice(0, lastColonIndex)}:${high}:${low}`
61+
}
62+
}
5263

5364
// Handle compressed notation (::)
54-
const doubleColonIndex = address.indexOf('::')
65+
const doubleColonIndex = normalizedAddress.indexOf('::')
5566
if (doubleColonIndex !== -1) {
56-
// Count non-empty parts
57-
const nonEmptyParts = parts.filter(p => p.length > 0).length
58-
const skipParts = 8 - nonEmptyParts
59-
60-
for (let i = 0; i < parts.length; i++) {
61-
if (parts[i] === '' && i === doubleColonIndex / 3) {
62-
// Skip empty parts for ::
63-
bufferIndex += skipParts * 2
64-
} else if (parts[i] !== '') {
65-
const value = parseInt(parts[i], 16)
66-
buffer.writeUInt16BE(value, bufferIndex)
67-
bufferIndex += 2
68-
}
67+
const before = normalizedAddress.slice(0, doubleColonIndex)
68+
const after = normalizedAddress.slice(doubleColonIndex + 2)
69+
const beforeParts = before === '' ? [] : before.split(':')
70+
const afterParts = after === '' ? [] : after.split(':')
71+
72+
let bufferIndex = 0
73+
for (const part of beforeParts) {
74+
buffer.writeUInt16BE(parseInt(part, 16), bufferIndex)
75+
bufferIndex += 2
76+
}
77+
bufferIndex = 16 - afterParts.length * 2
78+
for (const part of afterParts) {
79+
buffer.writeUInt16BE(parseInt(part, 16), bufferIndex)
80+
bufferIndex += 2
6981
}
7082
} else {
71-
// No compression, parse normally
72-
for (const part of parts) {
73-
if (part === '') continue
74-
const value = parseInt(part, 16)
75-
buffer.writeUInt16BE(value, partIndex * 2)
76-
partIndex++
83+
const parts = normalizedAddress.split(':')
84+
for (let i = 0; i < parts.length; i++) {
85+
buffer.writeUInt16BE(parseInt(parts[i], 16), i * 2)
7786
}
7887
}
7988

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

Lines changed: 1 addition & 1 deletion

0 commit comments

Comments
 (0)