@openclaw/slack 2026.9.8 → 2026.10.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.setup/{account-inspect-DWBXpER6.mjs → account-inspect-Cs-7DXXW.mjs} +1 -1
- package/dist/.setup/{accounts-D_N9IqNA.mjs → accounts-CDgkV7Cm.mjs} +4 -16
- package/dist/.setup/{action-runtime-DfWvuiZd.mjs → action-runtime-DJ1daRAZ.mjs} +5 -7
- package/dist/.setup/action-runtime.runtime-CdWL2mLC.mjs +2 -0
- package/dist/.setup/{actions-Df0CzcDl.mjs → actions-DDoAJnrH.mjs} +16 -35
- package/dist/.setup/{approval-handler.runtime-Ed6Sw5VK.mjs → approval-handler.runtime-BcE_shWc.mjs} +5 -5
- package/dist/.setup/{approval-message-updates-BadvtA7R.mjs → approval-message-updates-EgKGmFXx.mjs} +1 -1
- package/dist/.setup/{channel-C7bgPSTE.mjs → channel-DgKSu8Iv.mjs} +46 -48
- package/dist/.setup/{channel-type-ClOttrZ5.mjs → channel-type-B9zYYzBU.mjs} +4 -4
- package/dist/.setup/{channel.setup-Dh2jzAjN.mjs → channel.setup-BYIlwbQ7.mjs} +27 -27
- package/dist/.setup/{config-schema-BZCxmHKG.mjs → config-schema-BH0K9-Um.mjs} +4 -0
- package/dist/.setup/{directory-config-BOUWH3hR.mjs → directory-config-B_by7zrc.mjs} +2 -2
- package/dist/.setup/{directory-live-B2O4B_Ho.mjs → directory-live-C48wazJt.mjs} +3 -3
- package/dist/.setup/{doctor-contract-CRN1Z_c7.mjs → doctor-contract-Cd4-VzIM.mjs} +28 -28
- package/dist/.setup/{message-tool-api-Cu4lwlGy.mjs → message-tool-api-CcjLPN3v.mjs} +2 -2
- package/dist/.setup/{monitor-BPVfkumU.mjs → monitor-BuYBDmm7.mjs} +190 -315
- package/dist/.setup/{outbound-adapter-Bv48XUjq.mjs → outbound-adapter-BZFkK-mw.mjs} +6 -17
- package/dist/.setup/{pipeline.runtime-B1H63Frf.mjs → pipeline.runtime-CIepKmZt.mjs} +241 -247
- package/dist/.setup/{policy-D-jfM174.mjs → policy-KKTqEiDv.mjs} +12 -22
- package/dist/.setup/{probe-B2Y9qc_z.mjs → probe-BVlJ3dp0.mjs} +4 -7
- package/dist/.setup/{relay-source-BpKdjsll.mjs → relay-source-CC3h6M7y.mjs} +2 -2
- package/dist/.setup/{replies-BAnUtr2f.mjs → replies-DHbuthIR.mjs} +12 -26
- package/dist/.setup/{reply-blocks-SE1kTE3I.mjs → reply-blocks-CKnO4BSG.mjs} +4 -29
- package/dist/.setup/{resolve-channels-XXBn8UU_.mjs → resolve-channels-HR0TgArR.mjs} +2 -2
- package/dist/.setup/{resolve-users-DW0ngD03.mjs → resolve-users-BNYBhF0q.mjs} +4 -4
- package/dist/.setup/rolldown-runtime-B4FMCO8f.mjs +28 -0
- package/dist/.setup/{scopes-CasuDvaa.mjs → scopes-DrDbyUH2.mjs} +1 -1
- package/dist/.setup/secret-contract-B-Qm6aQQ.mjs +65 -0
- package/dist/.setup/{secret-contract-api-Dp6290Vg.mjs → secret-contract-api-DmUVxIRs.mjs} +1 -1
- package/dist/.setup/{security-audit-BqDc-6VL.mjs → security-audit-C1nOGzJa.mjs} +1 -1
- package/dist/.setup/{send-BOVLQi3n.mjs → send-CgPI3Zj8.mjs} +22 -53
- package/dist/.setup/{session-status-DscajIsd.mjs → session-status-D46GHwOV.mjs} +39 -49
- package/dist/.setup/{setup-plugin-api-DTtbTKNk.mjs → setup-plugin-api-ZrHs0N4U.mjs} +1 -1
- package/dist/.setup/{setup-surface-Dn7QgWRE.mjs → setup-surface-83lxvE2y.mjs} +10 -13
- package/dist/.setup/{shared-qau-uDGl.mjs → shared-VtmYNj-v.mjs} +53 -57
- package/dist/.setup/{slash-dispatch.runtime--AEzLVBF.mjs → slash-dispatch.runtime-gBj_uaLM.mjs} +1 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/api.js +13 -13
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/control-ui/2f4b1e827be6b68ced2ea0d8b7da8529a4eafbe72be27ee37e1064bdd530ec10/index.js +1 -0
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +1 -1
- package/dist/message-tool-api.js +1 -1
- package/dist/runtime-api.js +10 -10
- package/dist/secret-contract-api.js +1 -1
- package/dist/security-contract-api.js +1 -1
- package/dist/setup-entry.js +2 -2
- package/dist/setup-plugin-api.js +1 -1
- package/node_modules/undici/README.md +10 -1
- package/node_modules/undici/docs/docs/api/Agent.md +8 -8
- package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +9 -6
- package/node_modules/undici/docs/docs/api/Dispatcher.md +9 -0
- package/node_modules/undici/docs/docs/api/Errors.md +25 -0
- package/node_modules/undici/docs/docs/api/Fetch.md +10 -0
- package/node_modules/undici/docs/docs/api/Interceptors.md +4 -3
- package/node_modules/undici/docs/docs/api/Pool.md +5 -6
- package/node_modules/undici/docs/docs/index.md +10 -1
- package/node_modules/undici/lib/api/readable.js +1 -2
- package/node_modules/undici/lib/core/request.js +23 -2
- package/node_modules/undici/lib/core/util.js +0 -39
- package/node_modules/undici/lib/dispatcher/client-h1.js +11 -4
- package/node_modules/undici/lib/dispatcher/client-h2.js +52 -8
- package/node_modules/undici/lib/dispatcher/client.js +6 -1
- package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +21 -6
- package/node_modules/undici/lib/dispatcher/pool-base.js +63 -2
- package/node_modules/undici/lib/dispatcher/pool.js +90 -7
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +16 -3
- package/node_modules/undici/lib/handler/deduplication-handler.js +24 -11
- package/node_modules/undici/lib/handler/redirect-handler.js +5 -0
- package/node_modules/undici/lib/handler/retry-handler.js +72 -13
- package/node_modules/undici/lib/interceptor/cache.js +4 -1
- package/node_modules/undici/lib/interceptor/decompress.js +236 -69
- package/node_modules/undici/lib/web/cache/cache.js +17 -8
- package/node_modules/undici/lib/web/cache/cachestorage.js +11 -5
- package/node_modules/undici/lib/web/cookies/index.js +19 -7
- package/node_modules/undici/lib/web/cookies/util.js +3 -1
- package/node_modules/undici/lib/web/eventsource/eventsource.js +25 -1
- package/node_modules/undici/lib/web/fetch/body.js +22 -15
- package/node_modules/undici/lib/web/fetch/data-url.js +6 -6
- package/node_modules/undici/lib/web/fetch/formdata.js +30 -36
- package/node_modules/undici/lib/web/fetch/headers.js +34 -31
- package/node_modules/undici/lib/web/fetch/index.js +46 -25
- package/node_modules/undici/lib/web/fetch/request.js +74 -79
- package/node_modules/undici/lib/web/fetch/response.js +43 -44
- package/node_modules/undici/lib/web/fetch/util.js +7 -7
- package/node_modules/undici/lib/web/infra/index.js +16 -1
- package/node_modules/undici/lib/web/webidl/index.js +2 -14
- package/node_modules/undici/lib/web/websocket/connection.js +33 -12
- package/node_modules/undici/lib/web/websocket/events.js +43 -26
- package/node_modules/undici/lib/web/websocket/receiver.js +2 -0
- package/node_modules/undici/lib/web/websocket/stream/websocketerror.js +13 -10
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +19 -4
- package/node_modules/undici/lib/web/websocket/websocket.js +44 -41
- package/node_modules/undici/package.json +1 -1
- package/node_modules/undici/types/diagnostics-channel.d.ts +2 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/node_modules/undici/types/webidl.d.ts +13 -7
- package/openclaw.plugin.json +4 -1
- package/package.json +14 -6
- package/dist/.setup/action-runtime.runtime-Ck4bAWOX.mjs +0 -2
- package/dist/.setup/secret-contract-BaZcu6P_.mjs +0 -82
- package/dist/.setup/streaming-compat-C5Wut626.mjs +0 -62
|
@@ -5,6 +5,9 @@ const {
|
|
|
5
5
|
kClients,
|
|
6
6
|
kNeedDrain,
|
|
7
7
|
kAddClient,
|
|
8
|
+
kDrainQueue,
|
|
9
|
+
kOnClientBusy,
|
|
10
|
+
kOnClientDrain,
|
|
8
11
|
kGetDispatcher,
|
|
9
12
|
kHasDispatcher,
|
|
10
13
|
kRemoveClient
|
|
@@ -14,17 +17,41 @@ const {
|
|
|
14
17
|
InvalidArgumentError
|
|
15
18
|
} = require('../core/errors')
|
|
16
19
|
const util = require('../core/util')
|
|
17
|
-
const { kUrl } = require('../core/symbols')
|
|
20
|
+
const { kConnecting, kHTTPContext, kUrl } = require('../core/symbols')
|
|
18
21
|
const buildConnector = require('../core/connect')
|
|
19
22
|
|
|
20
23
|
const kOptions = Symbol('options')
|
|
21
24
|
const kConnections = Symbol('connections')
|
|
22
25
|
const kFactory = Symbol('factory')
|
|
26
|
+
const kProtocol = Symbol('protocol')
|
|
27
|
+
const kProtocolProbe = Symbol('protocol probe')
|
|
23
28
|
|
|
24
29
|
function defaultFactory (origin, opts) {
|
|
25
30
|
return new Client(origin, opts)
|
|
26
31
|
}
|
|
27
32
|
|
|
33
|
+
function shouldCreateProtocolProbe (pool, dispatcher) {
|
|
34
|
+
return dispatcher instanceof Client &&
|
|
35
|
+
pool[kProtocol] !== 'h1' &&
|
|
36
|
+
(pool[kOptions].useH2c === true || (pool[kUrl].protocol === 'https:' && pool[kOptions].allowH2 !== false))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createClient (pool) {
|
|
40
|
+
const dispatcher = pool[kFactory](pool[kUrl], pool[kOptions])
|
|
41
|
+
|
|
42
|
+
// HTTPS does not reveal whether the peer selected h1 or h2 until ALPN
|
|
43
|
+
// completes. While h2 is still possible, let one Client probe the protocol
|
|
44
|
+
// and keep later requests in the Pool queue instead of opening one TLS
|
|
45
|
+
// connection per request. A confirmed h1 connection disables this gate and
|
|
46
|
+
// restores the usual Pool fan-out.
|
|
47
|
+
if (shouldCreateProtocolProbe(pool, dispatcher)) {
|
|
48
|
+
pool[kProtocolProbe] = dispatcher
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pool[kAddClient](dispatcher)
|
|
52
|
+
return dispatcher
|
|
53
|
+
}
|
|
54
|
+
|
|
28
55
|
class Pool extends PoolBase {
|
|
29
56
|
constructor (origin, {
|
|
30
57
|
connections,
|
|
@@ -72,6 +99,8 @@ class Pool extends PoolBase {
|
|
|
72
99
|
this[kUrl] = util.parseOrigin(origin)
|
|
73
100
|
this[kOptions] = { ...util.deepClone(options), connect, allowH2, useH2c, clientTtl, socketPath }
|
|
74
101
|
this[kFactory] = factory
|
|
102
|
+
this[kProtocol] = null
|
|
103
|
+
this[kProtocolProbe] = null
|
|
75
104
|
|
|
76
105
|
this.on('connect', (origin, targets) => {
|
|
77
106
|
if (clientTtl != null && clientTtl > 0) {
|
|
@@ -79,13 +108,42 @@ class Pool extends PoolBase {
|
|
|
79
108
|
Object.assign(target, { ttl: Date.now() })
|
|
80
109
|
}
|
|
81
110
|
}
|
|
111
|
+
|
|
112
|
+
const client = targets[targets.length - 1]
|
|
113
|
+
if (client instanceof Client) {
|
|
114
|
+
this[kProtocol] = client[kHTTPContext]?.version
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (client === this[kProtocolProbe]) {
|
|
118
|
+
// An h2 Client's drain event releases the requests accumulated during
|
|
119
|
+
// negotiation onto that Client. If ALPN selected h1, release the probe
|
|
120
|
+
// immediately and restore normal Pool fan-out instead.
|
|
121
|
+
if (this[kProtocol] !== 'h2') {
|
|
122
|
+
this[kProtocolProbe] = null
|
|
123
|
+
this[kDrainQueue](origin, targets.slice(1))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
this.on('disconnect', (origin, targets) => {
|
|
129
|
+
if (targets.includes(this[kProtocolProbe])) {
|
|
130
|
+
this[kProtocolProbe] = null
|
|
131
|
+
this[kDrainQueue](origin, targets.slice(1))
|
|
132
|
+
}
|
|
82
133
|
})
|
|
83
134
|
|
|
84
|
-
this.on('connectionError', (origin, targets
|
|
135
|
+
this.on('connectionError', (origin, targets) => {
|
|
136
|
+
let resumeQueued = false
|
|
137
|
+
|
|
85
138
|
// If a connection error occurs, we remove the client from the pool,
|
|
86
139
|
// and emit a connectionError event. They will not be re-used.
|
|
87
140
|
// Fixes https://github.com/nodejs/undici/issues/3895
|
|
88
141
|
for (const target of targets) {
|
|
142
|
+
if (target === this[kProtocolProbe]) {
|
|
143
|
+
this[kProtocolProbe] = null
|
|
144
|
+
resumeQueued = true
|
|
145
|
+
}
|
|
146
|
+
|
|
89
147
|
// Do not use kRemoveClient here, as it will close the client,
|
|
90
148
|
// but the client cannot be closed in this state.
|
|
91
149
|
const idx = this[kClients].indexOf(target)
|
|
@@ -93,9 +151,29 @@ class Pool extends PoolBase {
|
|
|
93
151
|
this[kClients].splice(idx, 1)
|
|
94
152
|
}
|
|
95
153
|
}
|
|
154
|
+
|
|
155
|
+
if (resumeQueued) {
|
|
156
|
+
this[kDrainQueue](origin, targets.slice(1))
|
|
157
|
+
}
|
|
96
158
|
})
|
|
97
159
|
}
|
|
98
160
|
|
|
161
|
+
[kOnClientBusy] (client) {
|
|
162
|
+
if (
|
|
163
|
+
this[kProtocolProbe] === null &&
|
|
164
|
+
client[kConnecting] &&
|
|
165
|
+
shouldCreateProtocolProbe(this, client)
|
|
166
|
+
) {
|
|
167
|
+
this[kProtocolProbe] = client
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
[kOnClientDrain] (client) {
|
|
172
|
+
if (client === this[kProtocolProbe]) {
|
|
173
|
+
this[kProtocolProbe] = null
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
99
177
|
[kGetDispatcher] () {
|
|
100
178
|
const clientTtlOption = this[kOptions].clientTtl
|
|
101
179
|
for (let i = 0; i < this[kClients].length; i++) {
|
|
@@ -110,10 +188,12 @@ class Pool extends PoolBase {
|
|
|
110
188
|
}
|
|
111
189
|
}
|
|
112
190
|
|
|
191
|
+
if (this[kProtocolProbe] !== null) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
113
195
|
if (!this[kConnections] || this[kClients].length < this[kConnections]) {
|
|
114
|
-
|
|
115
|
-
this[kAddClient](dispatcher)
|
|
116
|
-
return dispatcher
|
|
196
|
+
return createClient(this)
|
|
117
197
|
}
|
|
118
198
|
}
|
|
119
199
|
|
|
@@ -130,9 +210,12 @@ class Pool extends PoolBase {
|
|
|
130
210
|
}
|
|
131
211
|
}
|
|
132
212
|
|
|
213
|
+
if (this[kProtocolProbe] !== null) {
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
|
|
133
217
|
if (!this[kConnections] || this[kClients].length < this[kConnections]) {
|
|
134
|
-
|
|
135
|
-
this[kAddClient](dispatcher)
|
|
218
|
+
createClient(this)
|
|
136
219
|
return true
|
|
137
220
|
}
|
|
138
221
|
|
|
@@ -325,8 +325,8 @@ class ProxyAgent extends DispatcherBase {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
/**
|
|
328
|
-
* @param {string[] | Record<string, string>} headers
|
|
329
|
-
* @returns {Record<string, string>}
|
|
328
|
+
* @param {string[] | Record<string, string> | Iterable<[string, string | string[] | undefined]>} headers
|
|
329
|
+
* @returns {Record<string, string | string[] | undefined>}
|
|
330
330
|
*/
|
|
331
331
|
function buildHeaders (headers) {
|
|
332
332
|
// When using undici.fetch, the headers list is stored
|
|
@@ -355,7 +355,20 @@ function buildHeaders (headers) {
|
|
|
355
355
|
const headersPair = {}
|
|
356
356
|
|
|
357
357
|
for (const [key, value] of headers) {
|
|
358
|
-
headersPair
|
|
358
|
+
if (!Object.hasOwn(headersPair, key)) {
|
|
359
|
+
headersPair[key] = value
|
|
360
|
+
continue
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const previous = headersPair[key]
|
|
364
|
+
const values = []
|
|
365
|
+
if (previous !== undefined) {
|
|
366
|
+
values.push(...(Array.isArray(previous) ? previous : [previous]))
|
|
367
|
+
}
|
|
368
|
+
if (value !== undefined) {
|
|
369
|
+
values.push(...(Array.isArray(value) ? value : [value]))
|
|
370
|
+
}
|
|
371
|
+
headersPair[key] = values.length > 1 ? values : values[0]
|
|
359
372
|
}
|
|
360
373
|
|
|
361
374
|
return headersPair
|
|
@@ -251,8 +251,11 @@ class DeduplicationHandler {
|
|
|
251
251
|
return
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
// Remove the entry before callbacks can synchronously dispatch a retry.
|
|
255
|
+
this.#cleanup()
|
|
255
256
|
this.#primaryHandler.onResponseEnd?.(controller, trailers)
|
|
257
|
+
// A throwing end callback must still be handled by onResponseError.
|
|
258
|
+
this.#completed = true
|
|
256
259
|
|
|
257
260
|
for (const waitingHandler of this.#waitingHandlers) {
|
|
258
261
|
if (waitingHandler.done || waitingHandler.controller.aborted) {
|
|
@@ -267,22 +270,21 @@ class DeduplicationHandler {
|
|
|
267
270
|
continue
|
|
268
271
|
}
|
|
269
272
|
|
|
270
|
-
if (waitingHandler.controller.paused
|
|
273
|
+
if (waitingHandler.controller.paused) {
|
|
271
274
|
waitingHandler.pendingTrailers = trailers
|
|
272
275
|
continue
|
|
273
276
|
}
|
|
274
277
|
|
|
275
278
|
try {
|
|
276
279
|
waitingHandler.handler.onResponseEnd?.(waitingHandler.controller, trailers)
|
|
277
|
-
} catch {
|
|
278
|
-
|
|
280
|
+
} catch (err) {
|
|
281
|
+
this.#errorWaitingHandler(waitingHandler, err)
|
|
279
282
|
}
|
|
280
283
|
|
|
281
284
|
waitingHandler.done = true
|
|
282
285
|
}
|
|
283
286
|
|
|
284
287
|
this.#pruneDoneWaitingHandlers()
|
|
285
|
-
this.#onComplete?.()
|
|
286
288
|
}
|
|
287
289
|
|
|
288
290
|
/**
|
|
@@ -296,6 +298,7 @@ class DeduplicationHandler {
|
|
|
296
298
|
|
|
297
299
|
this.#aborted = true
|
|
298
300
|
this.#completed = true
|
|
301
|
+
this.#cleanup()
|
|
299
302
|
|
|
300
303
|
this.#primaryHandler.onResponseError?.(controller, err)
|
|
301
304
|
|
|
@@ -304,7 +307,12 @@ class DeduplicationHandler {
|
|
|
304
307
|
}
|
|
305
308
|
|
|
306
309
|
this.#waitingHandlers = []
|
|
307
|
-
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
#cleanup () {
|
|
313
|
+
const onComplete = this.#onComplete
|
|
314
|
+
this.#onComplete = null
|
|
315
|
+
onComplete?.()
|
|
308
316
|
}
|
|
309
317
|
|
|
310
318
|
/**
|
|
@@ -346,8 +354,8 @@ class DeduplicationHandler {
|
|
|
346
354
|
) {
|
|
347
355
|
try {
|
|
348
356
|
waitingHandler.handler.onResponseEnd?.(waitingHandler.controller, waitingHandler.pendingTrailers)
|
|
349
|
-
} catch {
|
|
350
|
-
|
|
357
|
+
} catch (err) {
|
|
358
|
+
this.#errorWaitingHandler(waitingHandler, err)
|
|
351
359
|
}
|
|
352
360
|
|
|
353
361
|
waitingHandler.pendingTrailers = null
|
|
@@ -405,7 +413,7 @@ class DeduplicationHandler {
|
|
|
405
413
|
|
|
406
414
|
if (waitingHandler.bufferedBytes > this.#maxBufferSize) {
|
|
407
415
|
const err = new RequestAbortedError(`Deduplicated waiting handler exceeded maxBufferSize (${this.#maxBufferSize} bytes) while paused`)
|
|
408
|
-
|
|
416
|
+
waitingHandler.controller.abort(err)
|
|
409
417
|
}
|
|
410
418
|
}
|
|
411
419
|
|
|
@@ -454,8 +462,13 @@ class DeduplicationHandler {
|
|
|
454
462
|
waitingHandler.bufferedChunks = []
|
|
455
463
|
waitingHandler.bufferedBytes = 0
|
|
456
464
|
|
|
457
|
-
//
|
|
458
|
-
|
|
465
|
+
// A response failure is not a consumer abort: retry handlers must be able
|
|
466
|
+
// to retry it just as they would a failure of the primary request.
|
|
467
|
+
try {
|
|
468
|
+
waitingHandler.handler.onResponseError?.(waitingHandler.controller, err)
|
|
469
|
+
} catch {
|
|
470
|
+
// Ignore errors from waiting handlers
|
|
471
|
+
}
|
|
459
472
|
}
|
|
460
473
|
|
|
461
474
|
#pruneDoneWaitingHandlers () {
|
|
@@ -57,6 +57,11 @@ class RedirectHandler {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
onResponseStart (controller, statusCode, headers, statusMessage) {
|
|
60
|
+
if (statusCode < 200) {
|
|
61
|
+
this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) {
|
|
61
66
|
throw new Error('max redirects')
|
|
62
67
|
}
|
|
@@ -47,34 +47,51 @@ function validatePartialResponseContentLength (headers, range, statusCode, retry
|
|
|
47
47
|
// so nothing outside the handler can trigger it.
|
|
48
48
|
class RetryController {
|
|
49
49
|
#onAbort
|
|
50
|
+
#paused = false
|
|
51
|
+
#target = null
|
|
50
52
|
|
|
51
53
|
constructor (onAbort) {
|
|
52
54
|
this.#onAbort = onAbort
|
|
53
|
-
this.target = null
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
set target (target) {
|
|
58
|
+
this.#target = target
|
|
59
|
+
if (this.#paused) {
|
|
60
|
+
target?.pause()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get target () { return this.#target }
|
|
65
|
+
|
|
66
|
+
pause () {
|
|
67
|
+
this.#paused = true
|
|
68
|
+
this.#target?.pause()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
resume () {
|
|
72
|
+
this.#paused = false
|
|
73
|
+
this.#target?.resume()
|
|
74
|
+
}
|
|
58
75
|
|
|
59
76
|
abort (reason) {
|
|
60
|
-
this
|
|
77
|
+
this.#target?.abort(reason)
|
|
61
78
|
this.#onAbort(reason)
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
get paused () { return this
|
|
65
|
-
get aborted () { return this
|
|
66
|
-
get reason () { return this
|
|
67
|
-
get rawHeaders () { return this
|
|
81
|
+
get paused () { return this.#paused || (this.#target?.paused ?? false) }
|
|
82
|
+
get aborted () { return this.#target?.aborted ?? false }
|
|
83
|
+
get reason () { return this.#target?.reason ?? null }
|
|
84
|
+
get rawHeaders () { return this.#target?.rawHeaders ?? null }
|
|
68
85
|
set rawHeaders (value) {
|
|
69
|
-
if (this
|
|
70
|
-
this
|
|
86
|
+
if (this.#target) {
|
|
87
|
+
this.#target.rawHeaders = value
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
get rawTrailers () { return this
|
|
91
|
+
get rawTrailers () { return this.#target?.rawTrailers ?? null }
|
|
75
92
|
set rawTrailers (value) {
|
|
76
|
-
if (this
|
|
77
|
-
this
|
|
93
|
+
if (this.#target) {
|
|
94
|
+
this.#target.rawTrailers = value
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
97
|
}
|
|
@@ -142,6 +159,12 @@ class RetryHandler {
|
|
|
142
159
|
// Backoff timer returned by the retry policy, so #onAbort can cancel it.
|
|
143
160
|
// Null for custom policies that do not return their timer.
|
|
144
161
|
this.retryTimer = null
|
|
162
|
+
// A response can complete while its controller is paused if the peer closes
|
|
163
|
+
// the connection. Hold its body until the retry policy decides whether to
|
|
164
|
+
// discard it for a retry or forward it as the final response.
|
|
165
|
+
this.pendingResponseData = null
|
|
166
|
+
this.pendingResponseTrailers = null
|
|
167
|
+
this.pendingResponseEnded = false
|
|
145
168
|
// Set once an abort during the backoff delivered the terminal error
|
|
146
169
|
// downstream; late policy callbacks and connection errors are then moot.
|
|
147
170
|
this.aborted = false
|
|
@@ -183,6 +206,13 @@ class RetryHandler {
|
|
|
183
206
|
this.retryPending = false
|
|
184
207
|
this.retryTimer = null
|
|
185
208
|
|
|
209
|
+
const pendingData = this.pendingResponseData
|
|
210
|
+
const pendingTrailers = this.pendingResponseTrailers
|
|
211
|
+
const pendingEnd = this.pendingResponseEnded
|
|
212
|
+
this.pendingResponseData = null
|
|
213
|
+
this.pendingResponseTrailers = null
|
|
214
|
+
this.pendingResponseEnded = false
|
|
215
|
+
|
|
186
216
|
if (passedErr) {
|
|
187
217
|
if (this.headersSent) {
|
|
188
218
|
// The downstream handler already received the response from an
|
|
@@ -193,6 +223,15 @@ class RetryHandler {
|
|
|
193
223
|
this.headersSent = true
|
|
194
224
|
this.checkpointResponseEnd(headers)
|
|
195
225
|
this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
|
|
226
|
+
controller.resume()
|
|
227
|
+
|
|
228
|
+
if (pendingEnd) {
|
|
229
|
+
for (const chunk of pendingData) {
|
|
230
|
+
this.onResponseData(controller, chunk)
|
|
231
|
+
}
|
|
232
|
+
this.onResponseEnd(controller, pendingTrailers)
|
|
233
|
+
}
|
|
234
|
+
return
|
|
196
235
|
}
|
|
197
236
|
controller.resume()
|
|
198
237
|
return
|
|
@@ -200,6 +239,9 @@ class RetryHandler {
|
|
|
200
239
|
|
|
201
240
|
this.error = err
|
|
202
241
|
controller.resume()
|
|
242
|
+
if (pendingEnd) {
|
|
243
|
+
this.onResponseEnd(controller, pendingTrailers)
|
|
244
|
+
}
|
|
203
245
|
}
|
|
204
246
|
|
|
205
247
|
// The pause()/resume() pair (here and in shouldRetry) acts on THIS
|
|
@@ -213,6 +255,9 @@ class RetryHandler {
|
|
|
213
255
|
// The default policy returns its backoff timer so an abort can cancel it;
|
|
214
256
|
// a custom policy may return anything (or nothing), which is ignored.
|
|
215
257
|
this.retryPending = true
|
|
258
|
+
this.pendingResponseData = []
|
|
259
|
+
this.pendingResponseTrailers = null
|
|
260
|
+
this.pendingResponseEnded = false
|
|
216
261
|
this.retryTimer = this.retryOpts.retry(
|
|
217
262
|
err,
|
|
218
263
|
{
|
|
@@ -464,6 +509,11 @@ class RetryHandler {
|
|
|
464
509
|
}
|
|
465
510
|
|
|
466
511
|
onResponseData (_controller, chunk) {
|
|
512
|
+
if (this.pendingResponseData !== null) {
|
|
513
|
+
this.pendingResponseData.push(chunk)
|
|
514
|
+
return
|
|
515
|
+
}
|
|
516
|
+
|
|
467
517
|
if (this.error) {
|
|
468
518
|
return
|
|
469
519
|
}
|
|
@@ -474,6 +524,12 @@ class RetryHandler {
|
|
|
474
524
|
}
|
|
475
525
|
|
|
476
526
|
onResponseEnd (_controller, trailers) {
|
|
527
|
+
if (this.pendingResponseData !== null) {
|
|
528
|
+
this.pendingResponseTrailers = trailers
|
|
529
|
+
this.pendingResponseEnded = true
|
|
530
|
+
return
|
|
531
|
+
}
|
|
532
|
+
|
|
477
533
|
if (this.error && this.retryOpts.throwOnError) {
|
|
478
534
|
throw this.error
|
|
479
535
|
}
|
|
@@ -587,6 +643,9 @@ class RetryHandler {
|
|
|
587
643
|
this.retryPending = false
|
|
588
644
|
clearTimeout(this.retryTimer)
|
|
589
645
|
this.retryTimer = null
|
|
646
|
+
this.pendingResponseData = null
|
|
647
|
+
this.pendingResponseTrailers = null
|
|
648
|
+
this.pendingResponseEnded = false
|
|
590
649
|
this.handler.onResponseError?.(this.controllerProxy, reason ?? new RequestAbortedError())
|
|
591
650
|
}
|
|
592
651
|
}
|
|
@@ -330,6 +330,8 @@ function sendCachedValue (handler, opts, result, age, context, isStale) {
|
|
|
330
330
|
assert(!stream.destroyed, 'stream should not be destroyed')
|
|
331
331
|
assert(!stream.readableDidRead, 'stream should not be readableDidRead')
|
|
332
332
|
|
|
333
|
+
let aborted = false
|
|
334
|
+
|
|
333
335
|
const controller = {
|
|
334
336
|
rawHeaders: [],
|
|
335
337
|
rawTrailers: [],
|
|
@@ -343,12 +345,13 @@ function sendCachedValue (handler, opts, result, age, context, isStale) {
|
|
|
343
345
|
return stream.isPaused()
|
|
344
346
|
},
|
|
345
347
|
get aborted () {
|
|
346
|
-
return
|
|
348
|
+
return aborted
|
|
347
349
|
},
|
|
348
350
|
get reason () {
|
|
349
351
|
return stream.errored
|
|
350
352
|
},
|
|
351
353
|
abort (reason) {
|
|
354
|
+
aborted = true
|
|
352
355
|
stream.destroy(reason ?? new AbortError())
|
|
353
356
|
}
|
|
354
357
|
}
|