@openclaw/discord 2026.6.11 → 2026.6.34
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/action-runtime-api.js +1 -1
- package/dist/{api-4GFMoWu0.js → api-CjnGBhM3.js} +4 -1
- package/dist/api.js +14 -14
- package/dist/{approval-handler.runtime-SsFtZQA6.js → approval-handler.runtime-BWPQrzpP.js} +9 -4
- package/dist/{audit-CAnmyf2A.js → audit-BwJ_2OjL.js} +3 -3
- package/dist/{channel-YA7AbYP3.js → channel-DU4jnQgl.js} +15 -15
- package/dist/{channel-actions-BJ3n5Z1l.js → channel-actions-CwmIqnYN.js} +2 -2
- package/dist/{channel-actions.runtime-D2QTRgZ9.js → channel-actions.runtime-DvpTfaJ4.js} +5 -5
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-B1zDEAR7.js → channel.setup-glZ5Y98a.js} +2 -2
- package/dist/{components-Dm4c_ce6.js → components-j6lTZBsT.js} +1 -1
- package/dist/{conversation-identity-DZ0BD7xO.js → conversation-identity-36FFBwm5.js} +2 -2
- package/dist/{directory-config-BK1OhHa3.js → directory-config-BYMFzL29.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-DP3CmwGf.js → directory-live-9JtJCiu8.js} +2 -2
- package/dist/{handle-action.guild-admin-B2cOoU2D.js → handle-action.guild-admin-xxGYrp-I.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{manager.runtime-CPHxr9B3.js → manager.runtime-NxCd-PVt.js} +3 -3
- package/dist/{message-handler-CKaTgBIV.js → message-handler-cizxbaDc.js} +7 -6
- package/dist/{message-handler.preflight--eAiaewz.js → message-handler.preflight-CbD0u7U7.js} +10 -10
- package/dist/{message-handler.process-PhKAmrEd.js → message-handler.process-Do4EM591.js} +10 -10
- package/dist/{message-utils-O1vK3kPu.js → message-utils-CJeAoISH.js} +1 -1
- package/dist/{outbound-adapter-fO_7hbTh.js → outbound-adapter-Dxs6iKEq.js} +62 -131
- package/dist/{pluralkit-B3MzPaPU.js → pluralkit-DI6RYaVj.js} +1 -1
- package/dist/presence-cache-z1N8MbJ3.js +45 -0
- package/dist/{probe-CxnDikWm.js → probe-esE_Oqdm.js} +1 -1
- package/dist/{probe.runtime-DLFkSDVB.js → probe.runtime-C3Atgpvr.js} +1 -1
- package/dist/{provider-BJaJ20MY.js → provider-B-s93q9s.js} +76 -26
- package/dist/{provider-session.runtime-D6xc-60s.js → provider-session.runtime-BKMOqHpd.js} +3 -3
- package/dist/provider.runtime-CaTJrpnl.js +2 -0
- package/dist/{resolve-allowlist-common-BDpBb8dF.js → resolve-allowlist-common-3gB2Gpmm.js} +1 -1
- package/dist/{resolve-channels-iJIjYfRw.js → resolve-channels-B3gQf8QL.js} +3 -3
- package/dist/{resolve-users-_fv1mm1f.js → resolve-users-BO10y5fJ.js} +3 -3
- package/dist/{runtime-ywXByivr.js → runtime-CFlR3L9X.js} +8 -8
- package/dist/runtime-api.actions.js +2 -2
- package/dist/runtime-api.js +20 -20
- package/dist/runtime-api.lookup.js +5 -5
- package/dist/runtime-api.monitor-CRiBIQEI.js +6 -0
- package/dist/runtime-api.monitor.js +6 -5
- package/dist/runtime-api.send.js +5 -5
- package/dist/runtime-api.threads.js +3 -3
- package/dist/{send-Dc5uQX5H.js → send-JHxfT4Pk.js} +6 -4
- package/dist/{send.components-D4bl20xr.js → send.components-CN35E2Ck.js} +11 -6
- package/dist/{send.outbound-5axvwLkl.js → send.outbound-B-cA1Esv.js} +15 -8
- package/dist/{send.receipt-B21MspvT.js → send.receipt-CGecJDin.js} +10 -1
- package/dist/{send.shared-9082A2sO.js → send.shared-vcbfThdy.js} +107 -16
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{subagent-hooks-NTv6B4up.js → subagent-hooks-20wXhVzZ.js} +2 -2
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/{system-events-BzaI1WpN.js → system-events-DR27TE7p.js} +1 -1
- package/dist/{target-resolver-agnOrtOU.js → target-resolver-CDi1gI90.js} +2 -2
- package/dist/targets-C6yd3y8R.js +3 -0
- package/dist/{thread-bindings-DNitaHVh.js → thread-bindings-DU4T6nDo.js} +4 -4
- package/dist/{thread-bindings.discord-api-CjYNuvZa.js → thread-bindings.discord-api-CYkr-3Gj.js} +4 -4
- package/dist/{thread-bindings.manager-PZvJ4TJx.js → thread-bindings.manager-BqWs1UE2.js} +3 -3
- package/dist/{transcripts-source-Bi8N6h5X.js → transcripts-source-D-lBI5ZD.js} +1 -1
- package/dist/transcripts-source-api.js +1 -1
- package/dist/{typing-BdRqXUN3.js → typing-xr05bL-r.js} +1 -1
- package/node_modules/undici/README.md +8 -2
- package/node_modules/undici/docs/docs/api/Agent.md +248 -44
- package/node_modules/undici/docs/docs/api/BalancedPool.md +246 -48
- package/node_modules/undici/docs/docs/api/CacheStorage.md +171 -13
- package/node_modules/undici/docs/docs/api/CacheStore.md +294 -98
- package/node_modules/undici/docs/docs/api/Client.md +369 -179
- package/node_modules/undici/docs/docs/api/ClientStats.md +80 -14
- package/node_modules/undici/docs/docs/api/Connector.md +118 -36
- package/node_modules/undici/docs/docs/api/ContentType.md +65 -25
- package/node_modules/undici/docs/docs/api/Cookies.md +122 -71
- package/node_modules/undici/docs/docs/api/Debug.md +34 -18
- package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +364 -164
- package/node_modules/undici/docs/docs/api/Dispatcher.md +485 -1102
- package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +101 -87
- package/node_modules/undici/docs/docs/api/Errors.md +503 -40
- package/node_modules/undici/docs/docs/api/EventSource.md +294 -32
- package/node_modules/undici/docs/docs/api/Fetch.md +680 -31
- package/node_modules/undici/docs/docs/api/GlobalInstallation.md +78 -98
- package/node_modules/undici/docs/docs/api/H2CClient.md +298 -176
- package/node_modules/undici/docs/docs/api/Interceptors.md +414 -0
- package/node_modules/undici/docs/docs/api/MockAgent.md +333 -314
- package/node_modules/undici/docs/docs/api/MockCallHistory.md +272 -98
- package/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +189 -26
- package/node_modules/undici/docs/docs/api/MockClient.md +151 -33
- package/node_modules/undici/docs/docs/api/MockErrors.md +56 -5
- package/node_modules/undici/docs/docs/api/MockPool.md +321 -258
- package/node_modules/undici/docs/docs/api/Pool.md +235 -43
- package/node_modules/undici/docs/docs/api/PoolStats.md +119 -13
- package/node_modules/undici/docs/docs/api/ProxyAgent.md +179 -160
- package/node_modules/undici/docs/docs/api/RedirectHandler.md +238 -63
- package/node_modules/undici/docs/docs/api/RetryAgent.md +113 -26
- package/node_modules/undici/docs/docs/api/RetryHandler.md +161 -77
- package/node_modules/undici/docs/docs/api/RoundRobinPool.md +166 -72
- package/node_modules/undici/docs/docs/api/SnapshotAgent.md +264 -542
- package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +162 -187
- package/node_modules/undici/docs/docs/api/Util.md +68 -11
- package/node_modules/undici/docs/docs/api/WebSocket.md +475 -80
- package/node_modules/undici/docs/docs/api/api-lifecycle.md +100 -32
- package/node_modules/undici/docs/docs/best-practices/client-certificate.md +2 -0
- package/node_modules/undici/docs/docs/best-practices/crawling.md +3 -1
- package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +6 -4
- package/node_modules/undici/docs/docs/best-practices/mocking-request.md +9 -7
- package/node_modules/undici/docs/docs/best-practices/proxy.md +3 -1
- package/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +10 -8
- package/node_modules/undici/docs/docs/best-practices/writing-tests.md +2 -0
- package/node_modules/undici/docs/docs/{GettingStarted.md → getting-started.md} +22 -19
- package/node_modules/undici/docs/docs/index.md +784 -0
- package/node_modules/undici/docs/docs/site.json +125 -0
- package/node_modules/undici/docs/docs/type-map.json +79 -0
- package/node_modules/undici/lib/api/api-request.js +7 -1
- package/node_modules/undici/lib/api/readable.js +47 -2
- package/node_modules/undici/lib/cache/memory-cache-store.js +54 -9
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +7 -1
- package/node_modules/undici/lib/core/errors.js +20 -0
- package/node_modules/undici/lib/core/request.js +14 -3
- package/node_modules/undici/lib/core/util.js +30 -2
- package/node_modules/undici/lib/dispatcher/agent.js +3 -3
- package/node_modules/undici/lib/dispatcher/client-h1.js +65 -8
- package/node_modules/undici/lib/dispatcher/client-h2.js +265 -142
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +40 -6
- package/node_modules/undici/lib/global.js +36 -16
- package/node_modules/undici/lib/handler/cache-handler.js +269 -61
- package/node_modules/undici/lib/handler/cache-revalidation-handler.js +13 -3
- package/node_modules/undici/lib/handler/redirect-handler.js +6 -2
- package/node_modules/undici/lib/handler/retry-handler.js +87 -23
- package/node_modules/undici/lib/interceptor/cache.js +159 -52
- package/node_modules/undici/lib/interceptor/decompress.js +9 -2
- package/node_modules/undici/lib/mock/mock-interceptor.js +25 -7
- package/node_modules/undici/lib/mock/mock-utils.js +39 -10
- package/node_modules/undici/lib/util/cache.js +372 -68
- package/node_modules/undici/lib/util/date.js +23 -6
- package/node_modules/undici/lib/web/cookies/parse.js +3 -2
- package/node_modules/undici/lib/web/cookies/util.js +79 -9
- package/node_modules/undici/lib/web/eventsource/util.js +1 -1
- package/node_modules/undici/lib/web/fetch/body.js +2 -1
- package/node_modules/undici/lib/web/fetch/constants.js +1 -1
- package/node_modules/undici/lib/web/fetch/index.js +11 -2
- package/node_modules/undici/lib/web/fetch/response.js +1 -3
- package/node_modules/undici/lib/web/fetch/util.js +4 -1
- package/node_modules/undici/package.json +2 -2
- package/node_modules/undici/types/client.d.ts +3 -3
- package/node_modules/undici/types/cookies.d.ts +1 -1
- package/node_modules/undici/types/dispatcher.d.ts +3 -3
- package/node_modules/undici/types/errors.d.ts +10 -0
- package/node_modules/undici/types/handlers.d.ts +2 -0
- package/node_modules/undici/types/mock-interceptor.d.ts +5 -1
- package/node_modules/undici/types/proxy-agent.d.ts +7 -0
- package/npm-shrinkwrap.json +7 -7
- package/package.json +5 -5
- package/dist/gateway-registry-DPxmW0Db.js +0 -74
- package/dist/provider.runtime-jNlHxUSW.js +0 -2
- package/dist/runtime-api.monitor-Bb5AaRzg.js +0 -6
- package/dist/targets-ClUPljCU.js +0 -3
|
@@ -154,13 +154,24 @@ function requeueUnsentRequest (client, request) {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
function completeRequest (client, request, resetPendingIdx = false) {
|
|
157
|
-
const
|
|
157
|
+
const queue = client[kQueue]
|
|
158
|
+
const runningIdx = client[kRunningIdx]
|
|
159
|
+
|
|
160
|
+
// In-order completion: clear the request and advance without splicing.
|
|
161
|
+
// The client's resume loop compacts cleared slots once the index grows.
|
|
162
|
+
if (runningIdx < client[kPendingIdx] && queue[runningIdx] === request) {
|
|
163
|
+
queue[runningIdx] = null
|
|
164
|
+
client[kRunningIdx] = runningIdx + 1
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const index = queue.indexOf(request, runningIdx)
|
|
158
169
|
|
|
159
170
|
if (index === -1 || index >= client[kPendingIdx]) {
|
|
160
171
|
return
|
|
161
172
|
}
|
|
162
173
|
|
|
163
|
-
|
|
174
|
+
queue.splice(index, 1)
|
|
164
175
|
client[kPendingIdx]--
|
|
165
176
|
|
|
166
177
|
if (resetPendingIdx && client[kPendingIdx] < client[kRunningIdx]) {
|
|
@@ -174,11 +185,7 @@ function canRetryRequestAfterGoAway (request) {
|
|
|
174
185
|
return body == null || util.isBuffer(body) || util.isBlobLike(body)
|
|
175
186
|
}
|
|
176
187
|
|
|
177
|
-
function
|
|
178
|
-
const stream = request[kRequestStream]
|
|
179
|
-
|
|
180
|
-
clearRequestStream(request)
|
|
181
|
-
|
|
188
|
+
function closeStream (stream, code = NGHTTP2_REFUSED_STREAM) {
|
|
182
189
|
if (stream != null && !stream.destroyed && !stream.closed) {
|
|
183
190
|
try {
|
|
184
191
|
stream.close(code)
|
|
@@ -186,6 +193,14 @@ function closeRequestStream (request, code = NGHTTP2_REFUSED_STREAM) {
|
|
|
186
193
|
}
|
|
187
194
|
}
|
|
188
195
|
|
|
196
|
+
function detachRequestStreamForClose (request) {
|
|
197
|
+
const stream = request[kRequestStream]
|
|
198
|
+
|
|
199
|
+
clearRequestStream(request)
|
|
200
|
+
|
|
201
|
+
return stream
|
|
202
|
+
}
|
|
203
|
+
|
|
189
204
|
function connectH2 (client, socket) {
|
|
190
205
|
client[kSocket] = socket
|
|
191
206
|
|
|
@@ -208,6 +223,11 @@ function connectH2 (client, socket) {
|
|
|
208
223
|
session[kSocket] = socket
|
|
209
224
|
session[kHTTP2SessionState] = {
|
|
210
225
|
idleTimeout: null,
|
|
226
|
+
// Sockets start out ref'd. Session ref/unref proxies to the socket, so a
|
|
227
|
+
// single cached flag lets us skip redundant uv ref/unref calls, provided
|
|
228
|
+
// every ref/unref of the session or its socket goes through
|
|
229
|
+
// refH2Session/unrefH2Session.
|
|
230
|
+
refed: true,
|
|
211
231
|
ping: {
|
|
212
232
|
interval: client[kPingInterval] === 0 ? null : setInterval(onHttp2SendPing, client[kPingInterval], session).unref()
|
|
213
233
|
}
|
|
@@ -227,14 +247,13 @@ function connectH2 (client, socket) {
|
|
|
227
247
|
|
|
228
248
|
util.addListener(session, 'error', onHttp2SessionError)
|
|
229
249
|
util.addListener(session, 'frameError', onHttp2FrameError)
|
|
230
|
-
util.addListener(session, 'end', onHttp2SessionEnd)
|
|
231
250
|
util.addListener(session, 'goaway', onHttp2SessionGoAway)
|
|
232
251
|
util.addListener(session, 'close', onHttp2SessionClose)
|
|
233
252
|
util.addListener(session, 'remoteSettings', onHttp2RemoteSettings)
|
|
234
253
|
// TODO (@metcoder95): implement SETTINGS support
|
|
235
254
|
// util.addListener(session, 'localSettings', onHttp2RemoteSettings)
|
|
236
255
|
|
|
237
|
-
session
|
|
256
|
+
unrefH2Session(session)
|
|
238
257
|
|
|
239
258
|
client[kHTTP2Session] = session
|
|
240
259
|
socket[kHTTP2Session] = session
|
|
@@ -303,16 +322,6 @@ function connectH2 (client, socket) {
|
|
|
303
322
|
// Don't dispatch an upgrade until all preceding requests have completed.
|
|
304
323
|
// Possibly, we do not have remote settings confirmed yet.
|
|
305
324
|
if ((request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false) return true
|
|
306
|
-
// Request with stream or iterator body can error while other requests
|
|
307
|
-
// are inflight and indirectly error those as well.
|
|
308
|
-
// Ensure this doesn't happen by waiting for inflight
|
|
309
|
-
// to complete before dispatching.
|
|
310
|
-
|
|
311
|
-
// Request with stream or iterator body cannot be retried.
|
|
312
|
-
// Ensure that no other requests are inflight and
|
|
313
|
-
// could cause failure.
|
|
314
|
-
if (util.bodyLength(request.body) !== 0 &&
|
|
315
|
-
(util.isStream(request.body) || util.isAsyncIterable(request.body) || util.isFormDataLike(request.body))) return true
|
|
316
325
|
} else {
|
|
317
326
|
return (request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false
|
|
318
327
|
}
|
|
@@ -323,17 +332,36 @@ function connectH2 (client, socket) {
|
|
|
323
332
|
}
|
|
324
333
|
}
|
|
325
334
|
|
|
335
|
+
// Session ref/unref proxies to the underlying socket, so refH2Session and
|
|
336
|
+
// unrefH2Session cover both and can skip the call when the cached ref state
|
|
337
|
+
// already matches.
|
|
338
|
+
function refH2Session (session) {
|
|
339
|
+
const state = session[kHTTP2SessionState]
|
|
340
|
+
|
|
341
|
+
if (state.refed === false) {
|
|
342
|
+
state.refed = true
|
|
343
|
+
session.ref()
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function unrefH2Session (session) {
|
|
348
|
+
const state = session[kHTTP2SessionState]
|
|
349
|
+
|
|
350
|
+
if (state.refed === true) {
|
|
351
|
+
state.refed = false
|
|
352
|
+
session.unref()
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
326
356
|
function resumeH2 (client) {
|
|
327
357
|
const socket = client[kSocket]
|
|
328
358
|
const session = client[kHTTP2Session]
|
|
329
359
|
|
|
330
360
|
if (socket?.destroyed === false) {
|
|
331
361
|
if (client[kSize] === 0 || client[kMaxConcurrentStreams] === 0) {
|
|
332
|
-
|
|
333
|
-
session.unref()
|
|
362
|
+
unrefH2Session(session)
|
|
334
363
|
} else {
|
|
335
|
-
|
|
336
|
-
session.ref()
|
|
364
|
+
refH2Session(session)
|
|
337
365
|
}
|
|
338
366
|
|
|
339
367
|
if (client[kSize] === 0 && session[kOpenStreams] === 0) {
|
|
@@ -447,23 +475,26 @@ function onHttp2SessionError (err) {
|
|
|
447
475
|
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
|
|
448
476
|
|
|
449
477
|
this[kSocket][kError] = err
|
|
478
|
+
|
|
479
|
+
if (this[kReceivedGoAway]) {
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
|
|
450
483
|
this[kClient][kOnError](err)
|
|
451
484
|
}
|
|
452
485
|
|
|
453
486
|
function onHttp2FrameError (type, code, id) {
|
|
454
487
|
if (id === 0) {
|
|
488
|
+
if (this[kReceivedGoAway]) {
|
|
489
|
+
return
|
|
490
|
+
}
|
|
491
|
+
|
|
455
492
|
const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`)
|
|
456
493
|
this[kSocket][kError] = err
|
|
457
494
|
this[kClient][kOnError](err)
|
|
458
495
|
}
|
|
459
496
|
}
|
|
460
497
|
|
|
461
|
-
function onHttp2SessionEnd () {
|
|
462
|
-
const err = new SocketError('other side closed', util.getSocketInfo(this[kSocket]))
|
|
463
|
-
this.destroy(err)
|
|
464
|
-
util.destroy(this[kSocket], err)
|
|
465
|
-
}
|
|
466
|
-
|
|
467
498
|
/**
|
|
468
499
|
* This is the root cause of #3011
|
|
469
500
|
* We need to handle GOAWAY frames properly, and trigger the session close
|
|
@@ -485,12 +516,16 @@ function onHttp2SessionGoAway (errorCode, lastStreamID) {
|
|
|
485
516
|
const previousPendingIdx = client[kPendingIdx]
|
|
486
517
|
const pendingIdx = getGoAwayPendingIdx(client, lastStreamID)
|
|
487
518
|
const retriableRequests = []
|
|
519
|
+
const streamsToClose = []
|
|
488
520
|
|
|
521
|
+
// Closing one stream after GOAWAY can synchronously emit frameError on
|
|
522
|
+
// sibling streams. Detach all affected requests first so those errors do
|
|
523
|
+
// not fail requests that are about to be requeued.
|
|
489
524
|
for (let i = pendingIdx; i < previousPendingIdx; i++) {
|
|
490
525
|
const request = client[kQueue][i]
|
|
491
526
|
|
|
492
527
|
if (request != null) {
|
|
493
|
-
|
|
528
|
+
streamsToClose.push(detachRequestStreamForClose(request))
|
|
494
529
|
|
|
495
530
|
if (canRetryRequestAfterGoAway(request)) {
|
|
496
531
|
retriableRequests.push(request)
|
|
@@ -500,6 +535,10 @@ function onHttp2SessionGoAway (errorCode, lastStreamID) {
|
|
|
500
535
|
}
|
|
501
536
|
}
|
|
502
537
|
|
|
538
|
+
for (let i = 0; i < streamsToClose.length; i++) {
|
|
539
|
+
closeStream(streamsToClose[i])
|
|
540
|
+
}
|
|
541
|
+
|
|
503
542
|
if (pendingIdx !== previousPendingIdx) {
|
|
504
543
|
const remainingPendingRequests = client[kQueue].slice(previousPendingIdx)
|
|
505
544
|
client[kQueue].length = pendingIdx
|
|
@@ -595,7 +634,11 @@ function onHttp2SocketError (err) {
|
|
|
595
634
|
|
|
596
635
|
this[kError] = err
|
|
597
636
|
|
|
598
|
-
this[
|
|
637
|
+
if (this[kHTTP2Session]?.[kReceivedGoAway]) {
|
|
638
|
+
return
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
this[kHTTP2Session]?.[kClient]?.[kOnError](err)
|
|
599
642
|
}
|
|
600
643
|
|
|
601
644
|
function onHttp2SocketEnd () {
|
|
@@ -614,7 +657,7 @@ function closeStreamSession (stream) {
|
|
|
614
657
|
stream[kHTTP2Session] = null
|
|
615
658
|
session[kOpenStreams] -= 1
|
|
616
659
|
if (session[kOpenStreams] === 0) {
|
|
617
|
-
session
|
|
660
|
+
unrefH2Session(session)
|
|
618
661
|
setHttp2IdleTimeout(session)
|
|
619
662
|
}
|
|
620
663
|
}
|
|
@@ -629,22 +672,24 @@ function onUpgradeStreamClose () {
|
|
|
629
672
|
closeStreamSession(this)
|
|
630
673
|
}
|
|
631
674
|
|
|
632
|
-
|
|
675
|
+
// Idempotent terminal cleanup, called from both 'end' and 'close': the
|
|
676
|
+
// null-state guard no-ops the later call.
|
|
677
|
+
function completeRequestStream () {
|
|
633
678
|
const state = this[kRequestStreamState]
|
|
634
679
|
|
|
635
|
-
if (state) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
680
|
+
if (state == null) {
|
|
681
|
+
return
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Release the stream first so request references are cleared,
|
|
685
|
+
// then complete the response with trailers if available.
|
|
686
|
+
releaseRequestStream(this)
|
|
639
687
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
state.finalizeRequest()
|
|
643
|
-
}
|
|
688
|
+
if (state.pendingEnd && !state.request.aborted && !state.request.completed) {
|
|
689
|
+
state.request.onResponseEnd(state.trailers || {})
|
|
644
690
|
}
|
|
645
691
|
|
|
646
|
-
|
|
647
|
-
this.off('error', noop)
|
|
692
|
+
finalizeRequest(state)
|
|
648
693
|
closeStreamSession(this)
|
|
649
694
|
this[kRequestStreamState] = null
|
|
650
695
|
}
|
|
@@ -767,7 +812,7 @@ function onUpgradeResponse (headers, _flags) {
|
|
|
767
812
|
|
|
768
813
|
removeUpgradeStreamListeners(stream)
|
|
769
814
|
detachRequestFromStream(request)
|
|
770
|
-
|
|
815
|
+
finalizeRequest(state)
|
|
771
816
|
}
|
|
772
817
|
|
|
773
818
|
function setupUpgradeStream (stream, state) {
|
|
@@ -790,12 +835,51 @@ function setupUpgradeStream (stream, state) {
|
|
|
790
835
|
stream.setTimeout(headersTimeout)
|
|
791
836
|
}
|
|
792
837
|
|
|
838
|
+
function finalizeRequest (state, resetPendingIdx = false) {
|
|
839
|
+
if (state.requestFinalized) {
|
|
840
|
+
return
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
state.requestFinalized = true
|
|
844
|
+
completeRequest(state.client, state.request, resetPendingIdx)
|
|
845
|
+
|
|
846
|
+
state.client[kResume]()
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function openStream (client, request, session, abort, headers, options) {
|
|
850
|
+
try {
|
|
851
|
+
return session.request(headers, options)
|
|
852
|
+
} catch (err) {
|
|
853
|
+
// A GOAWAY'd session rejects new streams, same as an invalid session:
|
|
854
|
+
// reset and requeue on a fresh connection rather than the destroy + abort
|
|
855
|
+
// below, whose destroy(socket, err) can crash via an unhandled 'error'.
|
|
856
|
+
if (err?.code === 'ERR_HTTP2_INVALID_SESSION' || err?.code === 'ERR_HTTP2_GOAWAY_SESSION') {
|
|
857
|
+
const wrappedErr = new SocketError(err.message, util.getSocketInfo(session[kSocket]))
|
|
858
|
+
wrappedErr.cause = err
|
|
859
|
+
session[kError] = wrappedErr
|
|
860
|
+
resetHttp2Session(session, wrappedErr)
|
|
861
|
+
requeueUnsentRequest(client, request)
|
|
862
|
+
|
|
863
|
+
return null
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const wrappedErr = new InformationalError(err.message, { cause: err })
|
|
867
|
+
session[kError] = wrappedErr
|
|
868
|
+
session[kSocket][kError] = wrappedErr
|
|
869
|
+
|
|
870
|
+
session.destroy(wrappedErr)
|
|
871
|
+
util.destroy(session[kSocket], wrappedErr)
|
|
872
|
+
abort(wrappedErr)
|
|
873
|
+
|
|
874
|
+
return null
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
793
878
|
function writeH2 (client, request) {
|
|
794
879
|
const headersTimeout = request.headersTimeout ?? client[kHeadersTimeout]
|
|
795
880
|
const bodyTimeout = request.bodyTimeout ?? client[kBodyTimeout]
|
|
796
881
|
const session = client[kHTTP2Session]
|
|
797
882
|
const { method, path, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request
|
|
798
|
-
let { body } = request
|
|
799
883
|
|
|
800
884
|
if (upgrade != null && upgrade !== 'websocket') {
|
|
801
885
|
util.errorRequest(client, request, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`))
|
|
@@ -804,22 +888,28 @@ function writeH2 (client, request) {
|
|
|
804
888
|
|
|
805
889
|
const headers = buildRequestHeaders(reqHeaders)
|
|
806
890
|
|
|
807
|
-
/** @type {import('node:http2').ClientHttp2Stream} */
|
|
808
|
-
let stream = null
|
|
809
|
-
|
|
810
891
|
headers[HTTP2_HEADER_AUTHORITY] = host || client[kHostAuthority]
|
|
811
892
|
headers[HTTP2_HEADER_METHOD] = method
|
|
812
893
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
894
|
+
// Single pre-shaped state object shared by all stream event handlers.
|
|
895
|
+
// All fields are declared up-front so the object keeps a stable hidden
|
|
896
|
+
// class for the whole request lifetime.
|
|
897
|
+
const state = {
|
|
898
|
+
abort: null,
|
|
899
|
+
body: request.body,
|
|
900
|
+
client,
|
|
901
|
+
contentLength: null,
|
|
902
|
+
expectsPayload: false,
|
|
903
|
+
request,
|
|
904
|
+
headersTimeout,
|
|
905
|
+
bodyTimeout,
|
|
906
|
+
requestFinalized: false,
|
|
907
|
+
responseReceived: false,
|
|
908
|
+
bodySent: false,
|
|
909
|
+
pendingEnd: false,
|
|
910
|
+
trailers: null,
|
|
911
|
+
session,
|
|
912
|
+
stream: null
|
|
823
913
|
}
|
|
824
914
|
|
|
825
915
|
const abort = (err, resetPendingIdx = false) => {
|
|
@@ -831,47 +921,39 @@ function writeH2 (client, request) {
|
|
|
831
921
|
|
|
832
922
|
util.errorRequest(client, request, err)
|
|
833
923
|
|
|
834
|
-
if (stream != null) {
|
|
924
|
+
if (state.stream != null) {
|
|
835
925
|
clearRequestStream(request)
|
|
836
926
|
|
|
837
|
-
// On Abort, we close the stream to send RST_STREAM frame
|
|
927
|
+
// On Abort, we close the stream to send RST_STREAM frame.
|
|
928
|
+
const stream = state.stream
|
|
838
929
|
stream.close()
|
|
839
930
|
|
|
931
|
+
// close() alone leaves cleanup waiting on the 'close' event; on a busy,
|
|
932
|
+
// long-lived multiplexed session that event can fail to fire, leaving the
|
|
933
|
+
// native Http2Stream (and the whole request graph it pins) alive for the
|
|
934
|
+
// session's life. Destroy the stream synchronously to release the handle
|
|
935
|
+
// deterministically. Deferring the destroy (e.g. via setImmediate) leaks
|
|
936
|
+
// the same way when the event loop is stalled and the callback never runs
|
|
937
|
+
// under abort churn (#5558); close() has already queued the RST_STREAM
|
|
938
|
+
// frame on the native session, so a synchronous destroy still sends it.
|
|
939
|
+
if (!stream.destroyed) {
|
|
940
|
+
util.destroy(stream)
|
|
941
|
+
}
|
|
942
|
+
|
|
840
943
|
// We move the running index to the next request
|
|
841
944
|
client[kOnError](err)
|
|
842
|
-
finalizeRequest(resetPendingIdx)
|
|
945
|
+
finalizeRequest(state, resetPendingIdx)
|
|
843
946
|
}
|
|
844
947
|
|
|
845
948
|
// We do not destroy the socket as we can continue using the session
|
|
846
949
|
// the stream gets destroyed and the session remains to create new streams
|
|
847
|
-
util.destroy(body, err)
|
|
950
|
+
util.destroy(state.body, err)
|
|
848
951
|
}
|
|
849
952
|
|
|
850
|
-
|
|
851
|
-
try {
|
|
852
|
-
return session.request(headers, options)
|
|
853
|
-
} catch (err) {
|
|
854
|
-
if (err?.code === 'ERR_HTTP2_INVALID_SESSION') {
|
|
855
|
-
const wrappedErr = new SocketError(err.message, util.getSocketInfo(session[kSocket]))
|
|
856
|
-
wrappedErr.cause = err
|
|
857
|
-
session[kError] = wrappedErr
|
|
858
|
-
resetHttp2Session(session, wrappedErr)
|
|
859
|
-
requeueUnsentRequest(client, request)
|
|
860
|
-
|
|
861
|
-
return null
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
const wrappedErr = new InformationalError(err.message, { cause: err })
|
|
865
|
-
session[kError] = wrappedErr
|
|
866
|
-
session[kSocket][kError] = wrappedErr
|
|
867
|
-
|
|
868
|
-
session.destroy(wrappedErr)
|
|
869
|
-
util.destroy(session[kSocket], wrappedErr)
|
|
870
|
-
abort(wrappedErr)
|
|
953
|
+
state.abort = abort
|
|
871
954
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}
|
|
955
|
+
/** @type {import('node:http2').ClientHttp2Stream} */
|
|
956
|
+
let stream = null
|
|
875
957
|
|
|
876
958
|
try {
|
|
877
959
|
// We are already connected, streams are pending.
|
|
@@ -886,24 +968,13 @@ function writeH2 (client, request) {
|
|
|
886
968
|
}
|
|
887
969
|
|
|
888
970
|
if (upgrade || method === 'CONNECT') {
|
|
889
|
-
session
|
|
890
|
-
|
|
891
|
-
const upgradeState = {
|
|
892
|
-
abort,
|
|
893
|
-
finalizeRequest,
|
|
894
|
-
request,
|
|
895
|
-
headersTimeout,
|
|
896
|
-
bodyTimeout,
|
|
897
|
-
responseReceived: false,
|
|
898
|
-
session,
|
|
899
|
-
stream: null
|
|
900
|
-
}
|
|
971
|
+
refH2Session(session)
|
|
901
972
|
|
|
902
973
|
if (upgrade === 'websocket') {
|
|
903
974
|
// We cannot upgrade to websocket if extended CONNECT protocol is not supported
|
|
904
975
|
if (session[kEnableConnectProtocol] === false) {
|
|
905
976
|
util.errorRequest(client, request, new InformationalError('HTTP/2: Extended CONNECT protocol not supported by server'))
|
|
906
|
-
session
|
|
977
|
+
unrefH2Session(session)
|
|
907
978
|
return false
|
|
908
979
|
}
|
|
909
980
|
|
|
@@ -921,12 +992,12 @@ function writeH2 (client, request) {
|
|
|
921
992
|
headers[HTTP2_HEADER_SCHEME] = protocol === 'http:' ? 'http' : 'https'
|
|
922
993
|
}
|
|
923
994
|
|
|
924
|
-
stream =
|
|
995
|
+
stream = openStream(client, request, session, abort, headers, { endStream: false, signal })
|
|
925
996
|
if (stream == null) {
|
|
926
|
-
session
|
|
997
|
+
unrefH2Session(session)
|
|
927
998
|
return false
|
|
928
999
|
}
|
|
929
|
-
setupUpgradeStream(stream,
|
|
1000
|
+
setupUpgradeStream(stream, state)
|
|
930
1001
|
return true
|
|
931
1002
|
}
|
|
932
1003
|
|
|
@@ -935,12 +1006,12 @@ function writeH2 (client, request) {
|
|
|
935
1006
|
// will create a new stream. We trigger a request to create the stream and wait until
|
|
936
1007
|
// `ready` event is triggered
|
|
937
1008
|
// We disabled endStream to allow the user to write to the stream
|
|
938
|
-
stream =
|
|
1009
|
+
stream = openStream(client, request, session, abort, headers, { endStream: false, signal })
|
|
939
1010
|
if (stream == null) {
|
|
940
|
-
session
|
|
1011
|
+
unrefH2Session(session)
|
|
941
1012
|
return false
|
|
942
1013
|
}
|
|
943
|
-
setupUpgradeStream(stream,
|
|
1014
|
+
setupUpgradeStream(stream, state)
|
|
944
1015
|
|
|
945
1016
|
return true
|
|
946
1017
|
}
|
|
@@ -968,6 +1039,8 @@ function writeH2 (client, request) {
|
|
|
968
1039
|
method === 'PROPPATCH'
|
|
969
1040
|
)
|
|
970
1041
|
|
|
1042
|
+
let body = state.body
|
|
1043
|
+
|
|
971
1044
|
if (body && typeof body.read === 'function') {
|
|
972
1045
|
// Try to read EOF in order to get length.
|
|
973
1046
|
body.read(0)
|
|
@@ -1014,7 +1087,7 @@ function writeH2 (client, request) {
|
|
|
1014
1087
|
headers[HTTP2_HEADER_CONTENT_LENGTH] = `${contentLength}`
|
|
1015
1088
|
}
|
|
1016
1089
|
|
|
1017
|
-
session
|
|
1090
|
+
refH2Session(session)
|
|
1018
1091
|
|
|
1019
1092
|
if (channels.sendHeaders.hasSubscribers) {
|
|
1020
1093
|
let header = ''
|
|
@@ -1026,26 +1099,16 @@ function writeH2 (client, request) {
|
|
|
1026
1099
|
|
|
1027
1100
|
// TODO(metcoder95): add support for sending trailers
|
|
1028
1101
|
const shouldEndStream = body === null || contentLength === 0
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
contentLength,
|
|
1034
|
-
expectsPayload,
|
|
1035
|
-
finalizeRequest,
|
|
1036
|
-
request,
|
|
1037
|
-
headersTimeout,
|
|
1038
|
-
bodyTimeout,
|
|
1039
|
-
responseReceived: false,
|
|
1040
|
-
session,
|
|
1041
|
-
stream: null
|
|
1042
|
-
}
|
|
1102
|
+
|
|
1103
|
+
state.body = body
|
|
1104
|
+
state.contentLength = contentLength
|
|
1105
|
+
state.expectsPayload = expectsPayload
|
|
1043
1106
|
|
|
1044
1107
|
if (expectContinue) {
|
|
1045
1108
|
headers[HTTP2_HEADER_EXPECT] = '100-continue'
|
|
1046
1109
|
}
|
|
1047
1110
|
|
|
1048
|
-
stream =
|
|
1111
|
+
stream = openStream(client, request, session, abort, headers, { endStream: shouldEndStream, signal })
|
|
1049
1112
|
if (stream == null) {
|
|
1050
1113
|
return false
|
|
1051
1114
|
}
|
|
@@ -1056,22 +1119,30 @@ function writeH2 (client, request) {
|
|
|
1056
1119
|
// Increment counter as we have new streams open
|
|
1057
1120
|
clearHttp2IdleTimeout(session)
|
|
1058
1121
|
++session[kOpenStreams]
|
|
1059
|
-
|
|
1122
|
+
|
|
1123
|
+
if (headersTimeout) {
|
|
1124
|
+
stream.setTimeout(headersTimeout)
|
|
1125
|
+
}
|
|
1060
1126
|
|
|
1061
1127
|
stream[kHTTP2Session] = session
|
|
1062
|
-
stream.
|
|
1128
|
+
stream.on('close', completeRequestStream)
|
|
1063
1129
|
|
|
1064
1130
|
bindRequestToStream(request, stream, releaseRequestStream)
|
|
1065
1131
|
if (expectContinue) {
|
|
1066
1132
|
stream.once('continue', writeBodyH2)
|
|
1067
1133
|
}
|
|
1068
|
-
|
|
1069
|
-
stream
|
|
1070
|
-
|
|
1071
|
-
stream.
|
|
1134
|
+
// The handlers below either remove themselves on first invocation or
|
|
1135
|
+
// become unreachable once the stream closes, so plain `on` avoids the
|
|
1136
|
+
// per-listener `once` wrapper allocation.
|
|
1137
|
+
stream.on('response', onResponse)
|
|
1138
|
+
stream.on('end', onEnd)
|
|
1139
|
+
stream.on('error', onError)
|
|
1140
|
+
stream.on('frameError', onFrameError)
|
|
1072
1141
|
stream.on('aborted', onAborted)
|
|
1073
|
-
|
|
1074
|
-
|
|
1142
|
+
if (headersTimeout || bodyTimeout) {
|
|
1143
|
+
stream.on('timeout', onTimeout)
|
|
1144
|
+
}
|
|
1145
|
+
stream.on('trailers', onTrailers)
|
|
1075
1146
|
|
|
1076
1147
|
if (!expectContinue) {
|
|
1077
1148
|
writeBodyH2.call(stream)
|
|
@@ -1109,16 +1180,24 @@ function releaseRequestStream (stream) {
|
|
|
1109
1180
|
detachRequestFromStream(request)
|
|
1110
1181
|
}
|
|
1111
1182
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1183
|
+
// A closed or destroyed stream cannot emit further events; leaving the
|
|
1184
|
+
// listeners in place saves the removal scans (they are collected with
|
|
1185
|
+
// the stream). All handlers bail out when the stream state is gone.
|
|
1114
1186
|
if (!stream.destroyed && !stream.closed) {
|
|
1187
|
+
removeRequestStreamListeners(stream)
|
|
1115
1188
|
stream.once('error', noop)
|
|
1116
1189
|
}
|
|
1117
1190
|
}
|
|
1118
1191
|
|
|
1119
1192
|
function onData (chunk) {
|
|
1120
1193
|
const stream = this
|
|
1121
|
-
const
|
|
1194
|
+
const state = stream[kRequestStreamState]
|
|
1195
|
+
|
|
1196
|
+
if (state == null) {
|
|
1197
|
+
return
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const { request } = state
|
|
1122
1201
|
|
|
1123
1202
|
if (request.aborted || request.completed) {
|
|
1124
1203
|
return
|
|
@@ -1132,22 +1211,40 @@ function onData (chunk) {
|
|
|
1132
1211
|
function onResponse (headers) {
|
|
1133
1212
|
const stream = this
|
|
1134
1213
|
const state = stream[kRequestStreamState]
|
|
1214
|
+
|
|
1215
|
+
if (state == null) {
|
|
1216
|
+
return
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1135
1219
|
const { request } = state
|
|
1136
1220
|
|
|
1137
1221
|
stream.off('response', onResponse)
|
|
1138
1222
|
|
|
1223
|
+
// Final response received while still awaiting 100 (Continue): the body won't
|
|
1224
|
+
// be sent, so close our half or the stream stays open and never completes.
|
|
1225
|
+
if (state.body != null && !state.bodySent && !stream.writableEnded) {
|
|
1226
|
+
stream.removeListener('continue', writeBodyH2)
|
|
1227
|
+
stream.end()
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1139
1230
|
const statusCode = headers[HTTP2_HEADER_STATUS]
|
|
1140
1231
|
delete headers[HTTP2_HEADER_STATUS]
|
|
1141
1232
|
request.onResponseStarted()
|
|
1142
1233
|
state.responseReceived = true
|
|
1143
|
-
|
|
1234
|
+
|
|
1235
|
+
if (state.headersTimeout || state.bodyTimeout) {
|
|
1236
|
+
stream.setTimeout(state.bodyTimeout)
|
|
1237
|
+
}
|
|
1144
1238
|
|
|
1145
1239
|
// Due to the stream nature, it is possible we face a race condition
|
|
1146
1240
|
// where the stream has been assigned, but the request has been aborted
|
|
1147
|
-
//
|
|
1148
|
-
//
|
|
1149
|
-
//
|
|
1150
|
-
|
|
1241
|
+
// or already completed and headers hasn't been received yet. A late
|
|
1242
|
+
// 'response' delivered after completion would call request.onResponseStart
|
|
1243
|
+
// post-completion, tripping its `assert(!this.completed)` (an uncatchable
|
|
1244
|
+
// throw on the http2 event tick). Guard `completed` here as onEnd/onTrailers
|
|
1245
|
+
// already do; best effort is to release the stream immediately as there's
|
|
1246
|
+
// no value to keep it open.
|
|
1247
|
+
if (request.aborted || request.completed) {
|
|
1151
1248
|
releaseRequestStream(stream)
|
|
1152
1249
|
return
|
|
1153
1250
|
}
|
|
@@ -1162,17 +1259,25 @@ function onResponse (headers) {
|
|
|
1162
1259
|
function onEnd () {
|
|
1163
1260
|
const stream = this
|
|
1164
1261
|
const state = stream[kRequestStreamState]
|
|
1262
|
+
|
|
1263
|
+
if (state == null) {
|
|
1264
|
+
return
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1165
1267
|
const { request } = state
|
|
1166
1268
|
|
|
1167
1269
|
stream.off('end', onEnd)
|
|
1168
1270
|
|
|
1169
|
-
//
|
|
1170
|
-
//
|
|
1171
|
-
// onTrailers (which may fire after 'end' on Windows) can
|
|
1172
|
-
// store trailers first.
|
|
1271
|
+
// onTrailers (which may fire after 'end' on Windows) has already stored
|
|
1272
|
+
// trailers on the state by now, so completing here still delivers them.
|
|
1173
1273
|
if (state.responseReceived) {
|
|
1174
1274
|
if (!request.aborted && !request.completed) {
|
|
1175
1275
|
state.pendingEnd = true
|
|
1276
|
+
|
|
1277
|
+
// Complete on 'end': a blocked event loop can keep the stream's 'close'
|
|
1278
|
+
// from firing, stranding its buffers until OOM. Idempotent, so a later
|
|
1279
|
+
// 'close' no-ops.
|
|
1280
|
+
completeRequestStream.call(stream)
|
|
1176
1281
|
}
|
|
1177
1282
|
} else {
|
|
1178
1283
|
// Stream ended without receiving a response - this is an error
|
|
@@ -1185,6 +1290,10 @@ function onError (err) {
|
|
|
1185
1290
|
const stream = this
|
|
1186
1291
|
const state = stream[kRequestStreamState]
|
|
1187
1292
|
|
|
1293
|
+
if (state == null) {
|
|
1294
|
+
return
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1188
1297
|
stream.off('error', onError)
|
|
1189
1298
|
state.abort(err)
|
|
1190
1299
|
}
|
|
@@ -1193,6 +1302,10 @@ function onFrameError (type, code) {
|
|
|
1193
1302
|
const stream = this
|
|
1194
1303
|
const state = stream[kRequestStreamState]
|
|
1195
1304
|
|
|
1305
|
+
if (state == null) {
|
|
1306
|
+
return
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1196
1309
|
stream.off('frameError', onFrameError)
|
|
1197
1310
|
state.abort(new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`))
|
|
1198
1311
|
}
|
|
@@ -1205,6 +1318,10 @@ function onTimeout () {
|
|
|
1205
1318
|
const stream = this
|
|
1206
1319
|
const state = stream[kRequestStreamState]
|
|
1207
1320
|
|
|
1321
|
+
if (state == null) {
|
|
1322
|
+
return
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1208
1325
|
// Remove self so timeout doesn't fire again after we handle it
|
|
1209
1326
|
stream.off('timeout', onTimeout)
|
|
1210
1327
|
|
|
@@ -1217,6 +1334,11 @@ function onTimeout () {
|
|
|
1217
1334
|
function onTrailers (trailers) {
|
|
1218
1335
|
const stream = this
|
|
1219
1336
|
const state = stream[kRequestStreamState]
|
|
1337
|
+
|
|
1338
|
+
if (state == null) {
|
|
1339
|
+
return
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1220
1342
|
const { request } = state
|
|
1221
1343
|
|
|
1222
1344
|
stream.off('trailers', onTrailers)
|
|
@@ -1226,13 +1348,14 @@ function onTrailers (trailers) {
|
|
|
1226
1348
|
return
|
|
1227
1349
|
}
|
|
1228
1350
|
|
|
1229
|
-
// Store trailers for
|
|
1351
|
+
// Store trailers for completeRequestStream to use when completing
|
|
1230
1352
|
state.trailers = trailers
|
|
1231
1353
|
}
|
|
1232
1354
|
|
|
1233
1355
|
function writeBodyH2 () {
|
|
1234
1356
|
const stream = this
|
|
1235
1357
|
const state = stream[kRequestStreamState]
|
|
1358
|
+
state.bodySent = true
|
|
1236
1359
|
const { abort, body, client, contentLength, expectsPayload, request } = state
|
|
1237
1360
|
|
|
1238
1361
|
if (!body || contentLength === 0) {
|