Skip to content

Commit 857be5a

Browse files
deps: update undici to 6.29.1
PR-URL: #66765 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Richard Lau <richard.lau@ibm.com>
1 parent 9b16463 commit 857be5a

18 files changed

Lines changed: 559 additions & 119 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Returns: `Client`
2929
* **webSocket** `WebSocketOptions` (optional) - WebSocket-specific configuration options.
3030
* **maxFragments** `number` (optional) - Default: `131072` - Maximum number of fragments in a message. Set to 0 to disable the limit.
3131
* **maxPayloadSize** `number` (optional) - Default: `134217728` (128 MB) - Maximum allowed payload size in bytes for WebSocket messages. Applied to uncompressed messages, compressed frame payloads, and decompressed (permessage-deflate) messages. Set to 0 to disable the limit.
32+
* **eventSource** `EventSourceOptions` (optional) - EventSource-specific configuration options.
33+
* **maxEventSize** `number` (optional) - The maximum total bytes of data and metadata field values retained for a pending EventSource event. Each unfinished line is separately limited to this value plus seven syntax bytes. The number of `data:` lines per event and input chunks buffered for an unfinished line are each limited to `min(131072, max(1024, floor(maxEventSize / 128)))` to bound object overhead. Set to `0` to disable these limits. Default: `buffer.kStringMaxLength`.
3234
* **pipelining** `number | null` (optional) - Default: `1` - 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). Carefully consider your workload and environment before enabling concurrent requests as pipelining may reduce performance if used incorrectly. Pipelining is sensitive to network stack settings as well as head of line blocking caused by e.g. long running requests. Set to `0` to disable keep-alive connections.
3335
* **connect** `ConnectOptions | Function | null` (optional) - Default: `null`.
3436
* **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.

‎deps/undici/src/docs/docs/api/DiagnosticsChannel.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) =>
4040

4141
## `undici:request:headers`
4242

43-
This message is published after the response headers have been received, i.e. the response has been completed.
43+
This message is published after the response headers have been received. This includes a successful CONNECT or
44+
protocol upgrade response.
4445

4546
```js
4647
import diagnosticsChannel from 'diagnostics_channel'
@@ -57,6 +58,7 @@ diagnosticsChannel.channel('undici:request:headers').subscribe(({ request, respo
5758
## `undici:request:trailers`
5859

5960
This message is published after the response body and trailers have been received, i.e. the response has been completed.
61+
After an upgraded socket is passed to the request handler, this message is published with an empty `trailers` array.
6062

6163
```js
6264
import diagnosticsChannel from 'diagnostics_channel'

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

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,77 @@ class Request {
263263
}
264264
}
265265

266-
onUpgrade (statusCode, headers, socket) {
266+
/**
267+
* @param {number|null} statusCode
268+
* @param {Buffer[]|null} headers
269+
* @param {import('node:stream').Duplex} socket
270+
* @param {string} [statusText]
271+
*/
272+
onUpgrade (statusCode, headers, socket, statusText = '') {
273+
this.onFinally()
274+
267275
assert(!this.aborted)
268276
assert(!this.completed)
269277

270-
return this[kHandler].onUpgrade(statusCode, headers, socket)
278+
if (statusCode !== null) {
279+
this.#publishUpgradeHeaders(statusCode, headers, statusText)
280+
}
281+
282+
const result = this[kHandler].onUpgrade(statusCode, headers, socket)
283+
284+
if (!this.aborted) {
285+
this.completed = true
286+
if (statusCode !== null) {
287+
this.#publishUpgradeTrailers()
288+
}
289+
}
290+
291+
return result
292+
}
293+
294+
/**
295+
* @param {number} statusCode
296+
* @param {import('node:http2').IncomingHttpHeaders} headers
297+
* @param {(headers: import('node:http2').IncomingHttpHeaders) => Buffer[]} parseHeaders
298+
* @param {string} [statusText]
299+
*/
300+
onUpgradeResponse (statusCode, headers, parseHeaders, statusText = '') {
301+
assert(!this.aborted)
302+
assert(this.completed)
303+
304+
if (channels.headers.hasSubscribers) {
305+
this.#publishUpgradeHeaders(statusCode, parseHeaders(headers), statusText)
306+
}
307+
this.#publishUpgradeTrailers()
308+
}
309+
310+
/**
311+
* @param {Error} error
312+
*/
313+
onUpgradeError (error) {
314+
assert(!this.aborted)
315+
assert(this.completed)
316+
317+
if (channels.error.hasSubscribers) {
318+
channels.error.publish({ request: this, error })
319+
}
320+
}
321+
322+
/**
323+
* @param {number} statusCode
324+
* @param {Buffer[]} headers
325+
* @param {string} statusText
326+
*/
327+
#publishUpgradeHeaders (statusCode, headers, statusText) {
328+
if (channels.headers.hasSubscribers) {
329+
channels.headers.publish({ request: this, response: { statusCode, headers, statusText } })
330+
}
331+
}
332+
333+
#publishUpgradeTrailers () {
334+
if (channels.trailers.hasSubscribers) {
335+
channels.trailers.publish({ request: this, trailers: [] })
336+
}
271337
}
272338

273339
onComplete (trailers) {

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function defaultFactory (origin, opts) {
2323
}
2424

2525
class Agent extends DispatcherBase {
26-
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
26+
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, tls, ...options } = {}) {
2727
if (typeof factory !== 'function') {
2828
throw new InvalidArgumentError('factory must be a function.')
2929
}
@@ -42,11 +42,15 @@ class Agent extends DispatcherBase {
4242
connect = { ...connect }
4343
}
4444

45+
if (tls && typeof tls !== 'function') {
46+
tls = { ...tls }
47+
}
48+
4549
this[kInterceptors] = options.interceptors?.Agent && Array.isArray(options.interceptors.Agent)
4650
? options.interceptors.Agent
4751
: [createRedirectInterceptor({ maxRedirections })]
4852

49-
this[kOptions] = { ...util.deepClone(options), connect }
53+
this[kOptions] = { ...util.deepClone(options), connect, tls }
5054
this[kOptions].interceptors = options.interceptors
5155
? { ...options.interceptors }
5256
: undefined

‎deps/undici/src/lib/dispatcher/client-h1.js‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ class Parser {
432432
}
433433

434434
onUpgrade (head) {
435-
const { upgrade, client, socket, headers, statusCode } = this
435+
const { upgrade, client, socket, headers, statusCode, statusText } = this
436436

437437
assert(upgrade)
438438
assert(client[kSocket] === socket)
@@ -467,9 +467,10 @@ class Parser {
467467
client.emit('disconnect', client[kUrl], [client], new InformationalError('upgrade'))
468468

469469
try {
470-
request.onUpgrade(statusCode, headers, socket)
471-
} catch (err) {
472-
util.destroy(socket, err)
470+
request.onUpgrade(statusCode, headers, socket, statusText)
471+
} catch (error) {
472+
util.errorRequest(client, request, error)
473+
util.destroy(socket, error)
473474
}
474475

475476
client[kResume]()
@@ -726,8 +727,6 @@ async function connectH1 (client, socket) {
726727
socket[kParser] = new Parser(client, socket, llhttpInstance)
727728

728729
addListener(socket, 'error', function (err) {
729-
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
730-
731730
const parser = this[kParser]
732731

733732
// On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded
@@ -1050,12 +1049,22 @@ function writeH1 (client, request) {
10501049
const socket = client[kSocket]
10511050
clearIdleSocketValidation(socket)
10521051

1053-
const abort = (err) => {
1054-
if (request.aborted || request.completed) {
1052+
/**
1053+
* @param {Error} [error]
1054+
*/
1055+
const abort = (error) => {
1056+
if (request.aborted) {
1057+
return
1058+
}
1059+
1060+
if (request.completed) {
1061+
if (request.upgrade || request.method === 'CONNECT') {
1062+
util.destroy(socket, new InformationalError('aborted'))
1063+
}
10551064
return
10561065
}
10571066

1058-
util.errorRequest(client, request, err || new RequestAbortedError())
1067+
util.errorRequest(client, request, error || new RequestAbortedError())
10591068

10601069
util.destroy(body)
10611070
util.destroy(socket, new InformationalError('aborted'))

‎deps/undici/src/lib/dispatcher/client-h2.js‎

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

33
const assert = require('node:assert')
4+
const { errorMonitor } = require('node:events')
45
const { pipeline } = require('node:stream')
56
const util = require('../core/util.js')
67
const {
@@ -77,6 +78,15 @@ function parseH2Headers (headers) {
7778
return result
7879
}
7980

81+
/**
82+
* @param {import('node:http2').IncomingHttpHeaders} headers
83+
* @returns {Buffer[]}
84+
*/
85+
function parseH2ResponseHeaders (headers) {
86+
const { [HTTP2_HEADER_STATUS]: _statusCode, ...realHeaders } = headers
87+
return parseH2Headers(realHeaders)
88+
}
89+
8090
async function connectH2 (client, socket) {
8191
client[kSocket] = socket
8292

@@ -126,8 +136,6 @@ async function connectH2 (client, socket) {
126136
socket[kHTTP2Session] = session
127137

128138
util.addListener(socket, 'error', function (err) {
129-
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
130-
131139
this[kError] = err
132140

133141
this[kClient][kOnError](err)
@@ -201,8 +209,6 @@ function resumeH2 (client) {
201209
}
202210

203211
function onHttp2SessionError (err) {
204-
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
205-
206212
this[kSocket][kError] = err
207213
this[kClient][kOnError](err)
208214
}
@@ -297,22 +303,32 @@ function writeH2 (client, request) {
297303
headers[HTTP2_HEADER_AUTHORITY] = host || `${hostname}${port ? `:${port}` : ''}`
298304
headers[HTTP2_HEADER_METHOD] = method
299305

300-
const abort = (err) => {
301-
if (request.aborted || request.completed) {
306+
/**
307+
* @param {Error} [error]
308+
*/
309+
const abort = (error) => {
310+
if (request.aborted) {
302311
return
303312
}
304313

305-
err = err || new RequestAbortedError()
314+
if (request.completed) {
315+
if (method === 'CONNECT' && stream != null) {
316+
util.destroy(stream, error || new RequestAbortedError())
317+
}
318+
return
319+
}
306320

307-
util.errorRequest(client, request, err)
321+
error = error || new RequestAbortedError()
322+
323+
util.errorRequest(client, request, error)
308324

309325
if (stream != null) {
310-
util.destroy(stream, err)
326+
util.destroy(stream, error)
311327
}
312328

313329
// We do not destroy the socket as we can continue using the session
314330
// the stream get's destroyed and the session remains to create new streams
315-
util.destroy(body, err)
331+
util.destroy(body, error)
316332
client[kQueue][client[kRunningIdx]++] = null
317333
client[kResume]()
318334
}
@@ -331,25 +347,57 @@ function writeH2 (client, request) {
331347

332348
if (method === 'CONNECT') {
333349
session.ref()
334-
// We are already connected, streams are pending, first request
335-
// will create a new stream. We trigger a request to create the stream and wait until
336-
// `ready` event is triggered
337350
// We disabled endStream to allow the user to write to the stream
338351
stream = session.request(headers, { endStream: false, signal })
352+
let upgradeResponseFinished = false
353+
354+
/**
355+
* @param {import('node:http2').IncomingHttpHeaders} headers
356+
*/
357+
const onResponse = (headers) => {
358+
upgradeResponseFinished = true
359+
stream.off(errorMonitor, onUpgradeError)
360+
request.onUpgradeResponse(Number(headers[HTTP2_HEADER_STATUS]), headers, parseH2ResponseHeaders)
361+
}
339362

340-
if (stream.id && !stream.pending) {
341-
request.onUpgrade(null, null, stream)
342-
++session[kOpenStreams]
343-
client[kQueue][client[kRunningIdx]++] = null
344-
} else {
345-
stream.once('ready', () => {
363+
/**
364+
* @param {Error} error
365+
*/
366+
const onUpgradeError = (error) => {
367+
upgradeResponseFinished = true
368+
stream.off('response', onResponse)
369+
request.onUpgradeError(error)
370+
}
371+
372+
const onReady = () => {
373+
try {
346374
request.onUpgrade(null, null, stream)
347-
++session[kOpenStreams]
348-
client[kQueue][client[kRunningIdx]++] = null
349-
})
375+
} catch (error) {
376+
stream.off('response', onResponse)
377+
abort(error)
378+
return
379+
}
380+
381+
if (request.aborted) {
382+
return
383+
}
384+
385+
stream.off('error', abort)
386+
stream.once(errorMonitor, onUpgradeError)
387+
client[kQueue][client[kRunningIdx]++] = null
350388
}
351389

390+
stream.once('response', onResponse)
391+
stream.once('error', abort)
392+
++session[kOpenStreams]
393+
onReady()
394+
352395
stream.once('close', () => {
396+
if (!upgradeResponseFinished && request.completed) {
397+
stream.off('response', onResponse)
398+
stream.off(errorMonitor, onUpgradeError)
399+
request.onUpgradeError(new InformationalError(`HTTP/2: "stream error" received - code ${stream.rstCode}`))
400+
}
353401
session[kOpenStreams] -= 1
354402
if (session[kOpenStreams] === 0) session.unref()
355403
})

‎deps/undici/src/lib/dispatcher/client.js‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,10 @@ class Client extends DispatcherBase {
107107
// h2
108108
maxConcurrentStreams,
109109
allowH2,
110-
webSocket
110+
webSocket,
111+
eventSource
111112
} = {}) {
112-
super({ webSocket })
113+
super({ webSocket, eventSource })
113114

114115
if (keepAlive !== undefined) {
115116
throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead')

‎deps/undici/src/lib/dispatcher/dispatcher-base.js‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
const buffer = require('node:buffer')
34
const Dispatcher = require('./dispatcher')
45
const {
56
ClientDestroyedError,
@@ -12,6 +13,7 @@ const kOnDestroyed = Symbol('onDestroyed')
1213
const kOnClosed = Symbol('onClosed')
1314
const kInterceptedDispatch = Symbol('Intercepted Dispatch')
1415
const kWebSocketOptions = Symbol('webSocketOptions')
16+
const kEventSourceOptions = Symbol('eventSourceOptions')
1517

1618
class DispatcherBase extends Dispatcher {
1719
constructor (opts) {
@@ -22,6 +24,7 @@ class DispatcherBase extends Dispatcher {
2224
this[kClosed] = false
2325
this[kOnClosed] = []
2426
this[kWebSocketOptions] = opts?.webSocket ?? {}
27+
this[kEventSourceOptions] = opts?.eventSource ?? {}
2528
}
2629

2730
get webSocketOptions () {
@@ -31,6 +34,12 @@ class DispatcherBase extends Dispatcher {
3134
}
3235
}
3336

37+
get eventSourceOptions () {
38+
return {
39+
maxEventSize: this[kEventSourceOptions].maxEventSize ?? buffer.kStringMaxLength
40+
}
41+
}
42+
3443
get destroyed () {
3544
return this[kDestroyed]
3645
}

0 commit comments

Comments
 (0)