@openclaw/slack 2026.9.5 → 2026.9.7
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/assets/icon.png +0 -0
- package/dist/.setup/{account-inspect-B86OaUEt.mjs → account-inspect-DWBXpER6.mjs} +1 -1
- package/dist/.setup/{accounts-C1FspRF-.mjs → accounts-D_N9IqNA.mjs} +33 -10
- package/dist/.setup/{action-runtime-Aa-h24Fq.mjs → action-runtime-DfWvuiZd.mjs} +46 -65
- package/dist/.setup/action-runtime.runtime-Ck4bAWOX.mjs +2 -0
- package/dist/.setup/{actions-BvGUGk3w.mjs → actions-Df0CzcDl.mjs} +148 -204
- package/dist/.setup/{approval-handler.runtime-BcZNF-VP.mjs → approval-handler.runtime-Ed6Sw5VK.mjs} +37 -47
- package/dist/.setup/{approval-message-updates-Cy1cxfBn.mjs → approval-message-updates-BadvtA7R.mjs} +1 -2
- package/dist/.setup/{channel-DrBm0NFw.mjs → channel-C7bgPSTE.mjs} +192 -280
- package/dist/.setup/channel-meta-C443t-lI.mjs +27 -0
- package/dist/.setup/{channel-type-osC0XOES.mjs → channel-type-ClOttrZ5.mjs} +13 -21
- package/dist/.setup/{channel.setup-BSY5hzVz.mjs → channel.setup-Dh2jzAjN.mjs} +35 -42
- package/dist/.setup/{config-schema-DrY3S7Ee.mjs → config-schema-BZCxmHKG.mjs} +32 -21
- package/dist/.setup/{directory-config-CTs7YyuU.mjs → directory-config-BOUWH3hR.mjs} +7 -10
- package/dist/.setup/{directory-live-Btq0063Q.mjs → directory-live-B2O4B_Ho.mjs} +9 -21
- package/dist/.setup/doctor-contract-CRN1Z_c7.mjs +342 -0
- package/dist/.setup/{message-tool-api-DRnDCVdY.mjs → message-tool-api-Cu4lwlGy.mjs} +34 -48
- package/dist/.setup/{provider-w36VTgZ3.mjs → monitor-BPVfkumU.mjs} +2191 -2704
- package/dist/.setup/{outbound-adapter-K3Ek1iSR.mjs → outbound-adapter-Bv48XUjq.mjs} +10 -21
- package/dist/.setup/{pipeline.runtime-BZqGcbfi.mjs → pipeline.runtime-B1H63Frf.mjs} +901 -892
- package/dist/.setup/{plugin-routes-C3X0eVaK.mjs → plugin-routes-B8tooDLj.mjs} +1 -1
- package/dist/.setup/{policy-Db_QWCZZ.mjs → policy-D-jfM174.mjs} +5 -5
- package/dist/.setup/{probe-64C6k3_Q.mjs → probe-B2Y9qc_z.mjs} +101 -39
- package/dist/.setup/{registry-bbjH7IHX.mjs → registry-CIbMfuf8.mjs} +6 -2
- package/dist/.setup/{relay-source-CDqY76Vp.mjs → relay-source-BpKdjsll.mjs} +10 -12
- package/dist/.setup/{replies-CWFhjS9S.mjs → replies-BAnUtr2f.mjs} +16 -76
- package/dist/.setup/{reply-blocks-9uCeAJts.mjs → reply-blocks-SE1kTE3I.mjs} +27 -154
- package/dist/.setup/{resolve-channels-x8fX1SEt.mjs → resolve-channels-XXBn8UU_.mjs} +4 -5
- package/dist/.setup/{resolve-users-B8BpBnH2.mjs → resolve-users-DW0ngD03.mjs} +20 -22
- package/dist/.setup/{scopes-CJ-6l4v4.mjs → scopes-CasuDvaa.mjs} +6 -17
- package/dist/.setup/{secret-contract-DxmvbxBF.mjs → secret-contract-BaZcu6P_.mjs} +15 -20
- package/dist/.setup/{secret-contract-api-D9UfYujm.mjs → secret-contract-api-Dp6290Vg.mjs} +1 -1
- package/dist/.setup/{security-audit-BypHqcq5.mjs → security-audit-BqDc-6VL.mjs} +1 -1
- package/dist/.setup/{send-6GCb3lBP.mjs → send-BOVLQi3n.mjs} +102 -199
- package/dist/.setup/{session-status-BhG6pdiH.mjs → session-status-DscajIsd.mjs} +323 -250
- package/dist/.setup/{setup-plugin-api-DtCEByQB.mjs → setup-plugin-api-DTtbTKNk.mjs} +1 -1
- package/dist/.setup/{setup-surface-DsE0hsYY.mjs → setup-surface-Dn7QgWRE.mjs} +24 -56
- package/dist/.setup/{shared-BNmqCPgQ.mjs → shared-qau-uDGl.mjs} +30 -56
- package/dist/.setup/{slash-dispatch.runtime-Io4ASZsY.mjs → slash-dispatch.runtime--AEzLVBF.mjs} +1 -1
- package/dist/.setup/{streaming-compat-B-e0mqM0.mjs → streaming-compat-C5Wut626.mjs} +31 -8
- package/dist/.setup/{target-parsing-DEsPjngB.mjs → target-parsing-FFE26kOV.mjs} +1 -7
- package/dist/.setup/{targets-Cku2KCvg.mjs → targets-C6LZQL1Q.mjs} +1 -1
- package/dist/.setup/{thread-ts-CukseRVL.mjs → thread-ts-Cvt7WD6B.mjs} +0 -1
- package/dist/account-inspect-api.js +1 -1
- package/dist/api.js +18 -17
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +1 -1
- package/dist/http-routes-api.js +1 -1
- package/dist/index.js +1 -1
- package/dist/message-tool-api.js +1 -1
- package/dist/runtime-api.js +11 -12
- 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/@slack/bolt/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/child_process.d.ts +1 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/crypto.d.ts +20 -8
- package/node_modules/@slack/bolt/node_modules/@types/node/ffi.d.ts +14 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/http2.d.ts +3 -2
- package/node_modules/@slack/bolt/node_modules/@types/node/net.d.ts +16 -4
- package/node_modules/@slack/bolt/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/bolt/node_modules/@types/node/process.d.ts +1 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/quic.d.ts +39 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/stream/iter.d.ts +9 -12
- package/node_modules/@slack/bolt/node_modules/@types/node/test/reporters.d.ts +1 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/test.d.ts +72 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/tls.d.ts +14 -10
- package/node_modules/@slack/bolt/node_modules/@types/node/vm.d.ts +2 -1
- package/node_modules/@slack/bolt/node_modules/@types/node/web-globals/streams.d.ts +4 -0
- package/node_modules/@slack/bolt/node_modules/@types/node/worker_threads.d.ts +4 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/README.md +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/child_process.d.ts +1 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/crypto.d.ts +20 -8
- package/node_modules/@slack/web-api/node_modules/@types/node/ffi.d.ts +14 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/http2.d.ts +3 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/net.d.ts +16 -4
- package/node_modules/@slack/web-api/node_modules/@types/node/package.json +2 -2
- package/node_modules/@slack/web-api/node_modules/@types/node/process.d.ts +1 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/quic.d.ts +39 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/stream/iter.d.ts +9 -12
- package/node_modules/@slack/web-api/node_modules/@types/node/test/reporters.d.ts +1 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/test.d.ts +72 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/tls.d.ts +14 -10
- package/node_modules/@slack/web-api/node_modules/@types/node/vm.d.ts +2 -1
- package/node_modules/@slack/web-api/node_modules/@types/node/web-globals/streams.d.ts +4 -0
- package/node_modules/@slack/web-api/node_modules/@types/node/worker_threads.d.ts +4 -1
- package/node_modules/get-east-asian-width/index.js +8 -3
- package/node_modules/get-east-asian-width/lookup-data.js +1 -1
- package/node_modules/get-east-asian-width/lookup.js +4 -4
- package/node_modules/get-east-asian-width/package.json +1 -1
- package/node_modules/typebox/build/compile/code.mjs +1 -2
- package/node_modules/typebox/build/compile/validator.mjs +5 -1
- package/node_modules/typebox/build/format/iri.mjs +16 -9
- package/node_modules/typebox/build/guard/emit.mjs +2 -2
- package/node_modules/typebox/build/guard/guard.d.mts +7 -5
- package/node_modules/typebox/build/guard/guard.mjs +14 -9
- package/node_modules/typebox/build/guard/index.mjs +6 -0
- package/node_modules/typebox/build/guard/unicode/unicode.d.mts +6 -0
- package/node_modules/typebox/build/guard/unicode/unicode.mjs +35 -0
- package/node_modules/typebox/build/guard/{string.mjs → unicode/unicode_segment.mjs} +16 -19
- package/node_modules/typebox/build/schema/build.mjs +2 -3
- package/node_modules/typebox/build/schema/engine/_pathing.d.mts +1 -0
- package/node_modules/typebox/build/schema/engine/_pathing.mjs +11 -0
- package/node_modules/typebox/build/schema/engine/_refine.mjs +13 -5
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dependencies.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/format.mjs +9 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/properties.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -1
- package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/ref.d.mts +1 -1
- package/node_modules/typebox/build/schema/engine/ref.mjs +2 -2
- package/node_modules/typebox/build/schema/engine/schema.mjs +24 -24
- package/node_modules/typebox/build/schema/parse.mjs +3 -0
- package/node_modules/typebox/build/schema/pointer/pointer.d.mts +1 -6
- package/node_modules/typebox/build/schema/pointer/pointer.mjs +47 -64
- package/node_modules/typebox/build/system/settings/internal.d.mts +2 -0
- package/node_modules/typebox/build/system/settings/internal.mjs +20 -0
- package/node_modules/typebox/build/system/settings/settings.d.mts +11 -0
- package/node_modules/typebox/build/system/settings/settings.mjs +14 -0
- package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
- package/node_modules/typebox/build/type/extends/inference.mjs +15 -2
- package/node_modules/typebox/build/type/extends/tuple.d.mts +2 -1
- package/node_modules/typebox/build/type/extends/tuple.mjs +21 -1
- package/node_modules/typebox/build/value/parse/parse.mjs +10 -4
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +3 -1
- package/node_modules/undici/README.md +81 -25
- package/node_modules/undici/docs/docs/api/Agent.md +249 -42
- 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 +407 -162
- package/node_modules/undici/docs/docs/api/ClientStats.md +80 -14
- package/node_modules/undici/docs/docs/api/Connector.md +118 -35
- 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 +490 -1055
- package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +105 -93
- package/node_modules/undici/docs/docs/api/Errors.md +503 -28
- package/node_modules/undici/docs/docs/api/EventSource.md +326 -13
- package/node_modules/undici/docs/docs/api/Fetch.md +681 -30
- package/node_modules/undici/docs/docs/api/GlobalInstallation.md +78 -96
- package/node_modules/undici/docs/docs/api/H2CClient.md +298 -176
- package/node_modules/undici/docs/docs/api/Interceptors.md +418 -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 +236 -41
- 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 -55
- package/node_modules/undici/docs/docs/api/RetryAgent.md +113 -27
- package/node_modules/undici/docs/docs/api/RetryHandler.md +161 -79
- package/node_modules/undici/docs/docs/api/RoundRobinPool.md +166 -72
- package/node_modules/undici/docs/docs/api/SnapshotAgent.md +262 -517
- package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +177 -196
- 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 +233 -0
- 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/getting-started.md +281 -0
- 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/index.js +4 -2
- package/node_modules/undici/lib/api/api-connect.js +13 -11
- package/node_modules/undici/lib/api/api-pipeline.js +26 -13
- package/node_modules/undici/lib/api/api-request.js +51 -21
- package/node_modules/undici/lib/api/api-stream.js +81 -20
- package/node_modules/undici/lib/api/api-upgrade.js +21 -11
- package/node_modules/undici/lib/api/readable.js +45 -9
- package/node_modules/undici/lib/cache/memory-cache-store.js +3 -3
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +3 -1
- package/node_modules/undici/lib/core/connect.js +46 -7
- package/node_modules/undici/lib/core/constants.js +1 -24
- package/node_modules/undici/lib/core/errors.js +22 -2
- package/node_modules/undici/lib/core/request.js +100 -18
- package/node_modules/undici/lib/core/symbols.js +4 -0
- package/node_modules/undici/lib/core/util.js +105 -48
- package/node_modules/undici/lib/dispatcher/agent.js +51 -32
- package/node_modules/undici/lib/dispatcher/balanced-pool.js +23 -27
- package/node_modules/undici/lib/dispatcher/client-h1.js +86 -24
- package/node_modules/undici/lib/dispatcher/client-h2.js +1091 -361
- package/node_modules/undici/lib/dispatcher/client.js +109 -36
- package/node_modules/undici/lib/dispatcher/dispatcher-base.js +19 -6
- package/node_modules/undici/lib/dispatcher/dispatcher.js +15 -5
- package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +110 -0
- package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +37 -8
- package/node_modules/undici/lib/dispatcher/h2c-client.js +4 -4
- package/node_modules/undici/lib/dispatcher/pool-base.js +28 -10
- package/node_modules/undici/lib/dispatcher/pool.js +30 -5
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +95 -19
- package/node_modules/undici/lib/dispatcher/retry-agent.js +3 -0
- package/node_modules/undici/lib/dispatcher/round-robin-pool.js +32 -10
- package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +153 -63
- package/node_modules/undici/lib/global.js +37 -14
- package/node_modules/undici/lib/handler/cache-handler.js +49 -7
- package/node_modules/undici/lib/handler/cache-revalidation-handler.js +10 -0
- package/node_modules/undici/lib/handler/decorator-handler.js +8 -3
- package/node_modules/undici/lib/handler/deduplication-handler.js +12 -6
- package/node_modules/undici/lib/handler/redirect-handler.js +66 -66
- package/node_modules/undici/lib/handler/retry-handler.js +191 -50
- package/node_modules/undici/lib/interceptor/cache.js +75 -25
- package/node_modules/undici/lib/interceptor/decompress.js +5 -2
- package/node_modules/undici/lib/interceptor/deduplicate.js +5 -4
- package/node_modules/undici/lib/interceptor/dns.js +12 -1
- package/node_modules/undici/lib/interceptor/redirect.js +3 -3
- package/node_modules/undici/lib/llhttp/llhttp-wasm.js +1 -1
- package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +1 -1
- package/node_modules/undici/lib/mock/mock-agent.js +21 -9
- package/node_modules/undici/lib/mock/mock-call-history.js +1 -1
- package/node_modules/undici/lib/mock/mock-interceptor.js +25 -7
- package/node_modules/undici/lib/mock/mock-utils.js +281 -47
- package/node_modules/undici/lib/mock/snapshot-agent.js +25 -7
- package/node_modules/undici/lib/mock/snapshot-recorder.js +38 -3
- package/node_modules/undici/lib/util/cache.js +92 -10
- package/node_modules/undici/lib/util/runtime-features.js +3 -34
- package/node_modules/undici/lib/web/cache/cache.js +6 -8
- package/node_modules/undici/lib/web/cookies/parse.js +150 -146
- package/node_modules/undici/lib/web/cookies/util.js +1 -1
- package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +28 -1
- package/node_modules/undici/lib/web/eventsource/eventsource.js +20 -21
- package/node_modules/undici/lib/web/eventsource/util.js +32 -1
- package/node_modules/undici/lib/web/fetch/body.js +48 -10
- package/node_modules/undici/lib/web/fetch/constants.js +1 -1
- package/node_modules/undici/lib/web/fetch/formdata.js +21 -2
- package/node_modules/undici/lib/web/fetch/index.js +242 -223
- package/node_modules/undici/lib/web/fetch/request.js +35 -6
- package/node_modules/undici/lib/web/fetch/response.js +1 -3
- package/node_modules/undici/lib/web/fetch/util.js +10 -2
- package/node_modules/undici/lib/web/webidl/index.js +5 -7
- package/node_modules/undici/lib/web/websocket/frame.js +1 -7
- package/node_modules/undici/lib/web/websocket/receiver.js +1 -1
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +10 -10
- package/node_modules/undici/lib/web/websocket/websocket.js +25 -3
- package/node_modules/undici/package.json +10 -10
- package/node_modules/undici/types/agent.d.ts +0 -2
- package/node_modules/undici/types/client.d.ts +69 -24
- package/node_modules/undici/types/connector.d.ts +1 -0
- package/node_modules/undici/types/cookies.d.ts +1 -1
- package/node_modules/undici/types/dispatcher.d.ts +8 -30
- package/node_modules/undici/types/dispatcher1-wrapper.d.ts +7 -0
- package/node_modules/undici/types/errors.d.ts +10 -0
- package/node_modules/undici/types/fetch.d.ts +5 -1
- package/node_modules/undici/types/formdata.d.ts +0 -6
- package/node_modules/undici/types/h2c-client.d.ts +6 -6
- package/node_modules/undici/types/handlers.d.ts +2 -0
- package/node_modules/undici/types/header.d.ts +5 -0
- package/node_modules/undici/types/index.d.ts +3 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/node_modules/undici/types/mock-agent.d.ts +11 -10
- package/node_modules/undici/types/mock-interceptor.d.ts +5 -1
- package/node_modules/undici/types/pool.d.ts +0 -2
- package/node_modules/undici/types/proxy-agent.d.ts +9 -2
- package/node_modules/undici/types/round-robin-pool.d.ts +0 -2
- package/node_modules/undici/types/snapshot-agent.d.ts +4 -0
- package/node_modules/undici/types/socks5-proxy-agent.d.ts +4 -2
- package/node_modules/undici/types/webidl.d.ts +0 -1
- package/node_modules/zod/README.md +2 -2
- package/node_modules/zod/compile.cjs +19 -0
- package/node_modules/zod/index.cjs +19 -0
- package/node_modules/zod/locales/index.cjs +19 -0
- package/node_modules/zod/mini/index.cjs +19 -0
- package/node_modules/zod/package.json +8 -7
- package/node_modules/zod/src/v3/helpers/errorUtil.ts +1 -1
- package/node_modules/zod/src/v4/classic/external.ts +2 -0
- package/node_modules/zod/src/v4/classic/from-json-schema.ts +248 -25
- package/node_modules/zod/src/v4/classic/schemas.ts +243 -168
- package/node_modules/zod/src/v4/classic/tests/assignability.test.ts +60 -9
- package/node_modules/zod/src/v4/classic/tests/bigint.test.ts +6 -0
- package/node_modules/zod/src/v4/classic/tests/continuability.test.ts +2 -2
- package/node_modules/zod/src/v4/classic/tests/cyclic-data.test.ts +265 -0
- package/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +112 -6
- package/node_modules/zod/src/v4/classic/tests/enum.test.ts +9 -0
- package/node_modules/zod/src/v4/classic/tests/error.test.ts +130 -1
- package/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +157 -0
- package/node_modules/zod/src/v4/classic/tests/iban.test.ts +81 -0
- package/node_modules/zod/src/v4/classic/tests/instance-footprint.test.ts +40 -2
- package/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +27 -7
- package/node_modules/zod/src/v4/classic/tests/issue-schema.test.ts +88 -56
- package/node_modules/zod/src/v4/classic/tests/number.test.ts +39 -0
- package/node_modules/zod/src/v4/classic/tests/object.test.ts +56 -0
- package/node_modules/zod/src/v4/classic/tests/optin-ladder.test.ts +3 -2
- package/node_modules/zod/src/v4/classic/tests/prefault.test.ts +70 -0
- package/node_modules/zod/src/v4/classic/tests/properties.test.ts +141 -0
- package/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +46 -1
- package/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +19 -0
- package/node_modules/zod/src/v4/classic/tests/string.test.ts +95 -1
- package/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +33 -1
- package/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +234 -38
- package/node_modules/zod/src/v4/classic/tests/validate.test.ts +569 -0
- package/node_modules/zod/src/v4/core/api.ts +44 -28
- package/node_modules/zod/src/v4/core/checks.ts +62 -102
- package/node_modules/zod/src/v4/core/compile.ts +218 -93
- package/node_modules/zod/src/v4/core/core.ts +1 -3
- package/node_modules/zod/src/v4/core/doc.ts +6 -2
- package/node_modules/zod/src/v4/core/errors.ts +3 -6
- package/node_modules/zod/src/v4/core/json-schema-generator.ts +2 -2
- package/node_modules/zod/src/v4/core/json-schema-processors.ts +185 -81
- package/node_modules/zod/src/v4/core/memoizer.ts +75 -28
- package/node_modules/zod/src/v4/core/parse.ts +37 -16
- package/node_modules/zod/src/v4/core/regexes.ts +14 -4
- package/node_modules/zod/src/v4/core/schemas.ts +296 -132
- package/node_modules/zod/src/v4/core/tests/cached.test.ts +54 -0
- package/node_modules/zod/src/v4/core/tests/compile-differential.test.ts +35 -0
- package/node_modules/zod/src/v4/core/tests/compile-generative.test.ts +445 -0
- package/node_modules/zod/src/v4/core/tests/compile.test.ts +399 -2
- package/node_modules/zod/src/v4/core/tests/email-regex.test.ts +140 -0
- package/node_modules/zod/src/v4/core/tests/locales/tg.test.ts +85 -0
- package/node_modules/zod/src/v4/core/tests/polyfill-collision.test.ts +46 -0
- package/node_modules/zod/src/v4/core/tests/url-no-canparse.test.ts +118 -16
- package/node_modules/zod/src/v4/core/to-json-schema.ts +9 -7
- package/node_modules/zod/src/v4/core/util.ts +218 -147
- package/node_modules/zod/src/v4/core/versions.ts +2 -2
- package/node_modules/zod/src/v4/locales/ar.ts +2 -0
- package/node_modules/zod/src/v4/locales/az.ts +2 -0
- package/node_modules/zod/src/v4/locales/be.ts +2 -0
- package/node_modules/zod/src/v4/locales/bg.ts +2 -0
- package/node_modules/zod/src/v4/locales/bn.ts +2 -0
- package/node_modules/zod/src/v4/locales/ca.ts +2 -0
- package/node_modules/zod/src/v4/locales/ckb.ts +2 -0
- package/node_modules/zod/src/v4/locales/cs.ts +2 -0
- package/node_modules/zod/src/v4/locales/da.ts +2 -0
- package/node_modules/zod/src/v4/locales/de.ts +2 -0
- package/node_modules/zod/src/v4/locales/el.ts +2 -0
- package/node_modules/zod/src/v4/locales/en.ts +2 -0
- package/node_modules/zod/src/v4/locales/eo.ts +2 -0
- package/node_modules/zod/src/v4/locales/es.ts +2 -0
- package/node_modules/zod/src/v4/locales/fa.ts +2 -0
- package/node_modules/zod/src/v4/locales/fi.ts +2 -0
- package/node_modules/zod/src/v4/locales/fr-CA.ts +2 -0
- package/node_modules/zod/src/v4/locales/fr.ts +2 -0
- package/node_modules/zod/src/v4/locales/gu.ts +2 -0
- package/node_modules/zod/src/v4/locales/he.ts +2 -0
- package/node_modules/zod/src/v4/locales/hi.ts +2 -0
- package/node_modules/zod/src/v4/locales/hr.ts +2 -0
- package/node_modules/zod/src/v4/locales/hu.ts +2 -0
- package/node_modules/zod/src/v4/locales/hy.ts +2 -0
- package/node_modules/zod/src/v4/locales/id.ts +2 -0
- package/node_modules/zod/src/v4/locales/index.ts +1 -0
- package/node_modules/zod/src/v4/locales/is.ts +2 -0
- package/node_modules/zod/src/v4/locales/it.ts +2 -0
- package/node_modules/zod/src/v4/locales/ja.ts +2 -0
- package/node_modules/zod/src/v4/locales/ka.ts +2 -0
- package/node_modules/zod/src/v4/locales/km.ts +2 -0
- package/node_modules/zod/src/v4/locales/kn.ts +2 -0
- package/node_modules/zod/src/v4/locales/ko.ts +2 -0
- package/node_modules/zod/src/v4/locales/lt.ts +2 -0
- package/node_modules/zod/src/v4/locales/mk.ts +2 -0
- package/node_modules/zod/src/v4/locales/ms.ts +2 -0
- package/node_modules/zod/src/v4/locales/ne.ts +2 -0
- package/node_modules/zod/src/v4/locales/nl.ts +2 -0
- package/node_modules/zod/src/v4/locales/nn.ts +2 -0
- package/node_modules/zod/src/v4/locales/no.ts +2 -0
- package/node_modules/zod/src/v4/locales/ota.ts +2 -0
- package/node_modules/zod/src/v4/locales/pl.ts +2 -0
- package/node_modules/zod/src/v4/locales/ps.ts +2 -0
- package/node_modules/zod/src/v4/locales/pt-BR.ts +2 -0
- package/node_modules/zod/src/v4/locales/pt.ts +2 -0
- package/node_modules/zod/src/v4/locales/ro.ts +2 -0
- package/node_modules/zod/src/v4/locales/ru.ts +2 -0
- package/node_modules/zod/src/v4/locales/sk.ts +2 -0
- package/node_modules/zod/src/v4/locales/sl.ts +2 -0
- package/node_modules/zod/src/v4/locales/sv.ts +2 -0
- package/node_modules/zod/src/v4/locales/ta.ts +2 -0
- package/node_modules/zod/src/v4/locales/tg.ts +135 -0
- package/node_modules/zod/src/v4/locales/th.ts +2 -0
- package/node_modules/zod/src/v4/locales/tk.ts +2 -0
- package/node_modules/zod/src/v4/locales/tr.ts +2 -0
- package/node_modules/zod/src/v4/locales/uk.ts +2 -0
- package/node_modules/zod/src/v4/locales/ur.ts +2 -0
- package/node_modules/zod/src/v4/locales/uz.ts +2 -0
- package/node_modules/zod/src/v4/locales/vi.ts +2 -0
- package/node_modules/zod/src/v4/locales/yo.ts +2 -0
- package/node_modules/zod/src/v4/locales/zh-CN.ts +2 -0
- package/node_modules/zod/src/v4/locales/zh-TW.ts +2 -0
- package/node_modules/zod/src/v4/mini/external.ts +2 -0
- package/node_modules/zod/src/v4/mini/schemas.ts +32 -7
- package/node_modules/zod/src/v4/mini/tests/checks.test.ts +1 -1
- package/node_modules/zod/src/v4/mini/tests/computed.test.ts +11 -17
- package/node_modules/zod/src/v4/mini/tests/index.test.ts +86 -0
- package/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +16 -0
- package/node_modules/zod/src/v4/mini/tests/string.test.ts +23 -0
- package/node_modules/zod/v3/ZodError.cjs +19 -0
- package/node_modules/zod/v3/errors.cjs +19 -0
- package/node_modules/zod/v3/external.cjs +19 -0
- package/node_modules/zod/v3/helpers/enumUtil.cjs +19 -0
- package/node_modules/zod/v3/helpers/errorUtil.cjs +20 -1
- package/node_modules/zod/v3/helpers/errorUtil.js +1 -1
- package/node_modules/zod/v3/helpers/parseUtil.cjs +19 -0
- package/node_modules/zod/v3/helpers/partialUtil.cjs +19 -0
- package/node_modules/zod/v3/helpers/typeAliases.cjs +19 -0
- package/node_modules/zod/v3/helpers/util.cjs +19 -0
- package/node_modules/zod/v3/index.cjs +19 -0
- package/node_modules/zod/v3/standard-schema.cjs +19 -0
- package/node_modules/zod/v3/types.cjs +19 -0
- package/node_modules/zod/v4/classic/checks.cjs +19 -0
- package/node_modules/zod/v4/classic/coerce.cjs +19 -0
- package/node_modules/zod/v4/classic/compat.cjs +19 -0
- package/node_modules/zod/v4/classic/deep-partial.cjs +19 -0
- package/node_modules/zod/v4/classic/errors.cjs +19 -0
- package/node_modules/zod/v4/classic/external.cjs +22 -1
- package/node_modules/zod/v4/classic/external.d.cts +1 -1
- package/node_modules/zod/v4/classic/external.d.ts +1 -1
- package/node_modules/zod/v4/classic/external.js +1 -1
- package/node_modules/zod/v4/classic/from-json-schema.cjs +264 -24
- package/node_modules/zod/v4/classic/from-json-schema.js +245 -24
- package/node_modules/zod/v4/classic/in-out.cjs +19 -0
- package/node_modules/zod/v4/classic/index.cjs +19 -0
- package/node_modules/zod/v4/classic/iso.cjs +19 -0
- package/node_modules/zod/v4/classic/parse.cjs +19 -0
- package/node_modules/zod/v4/classic/schemas.cjs +100 -35
- package/node_modules/zod/v4/classic/schemas.d.cts +15 -1
- package/node_modules/zod/v4/classic/schemas.d.ts +15 -1
- package/node_modules/zod/v4/classic/schemas.js +77 -33
- package/node_modules/zod/v4/core/api.cjs +45 -22
- package/node_modules/zod/v4/core/api.d.cts +12 -6
- package/node_modules/zod/v4/core/api.d.ts +12 -6
- package/node_modules/zod/v4/core/api.js +25 -22
- package/node_modules/zod/v4/core/checks.cjs +51 -97
- package/node_modules/zod/v4/core/checks.d.cts +17 -1
- package/node_modules/zod/v4/core/checks.d.ts +17 -1
- package/node_modules/zod/v4/core/checks.js +31 -96
- package/node_modules/zod/v4/core/compile.cjs +173 -78
- package/node_modules/zod/v4/core/compile.d.cts +18 -4
- package/node_modules/zod/v4/core/compile.d.ts +18 -4
- package/node_modules/zod/v4/core/compile.js +154 -79
- package/node_modules/zod/v4/core/core.cjs +20 -1
- package/node_modules/zod/v4/core/core.js +1 -1
- package/node_modules/zod/v4/core/doc.cjs +26 -2
- package/node_modules/zod/v4/core/doc.js +7 -2
- package/node_modules/zod/v4/core/errors.cjs +21 -5
- package/node_modules/zod/v4/core/errors.js +2 -5
- package/node_modules/zod/v4/core/index.cjs +19 -0
- package/node_modules/zod/v4/core/json-schema-generator.cjs +20 -1
- package/node_modules/zod/v4/core/json-schema-generator.js +2 -2
- package/node_modules/zod/v4/core/json-schema-processors.cjs +188 -72
- package/node_modules/zod/v4/core/json-schema-processors.d.cts +16 -0
- package/node_modules/zod/v4/core/json-schema-processors.d.ts +16 -0
- package/node_modules/zod/v4/core/json-schema-processors.js +170 -74
- package/node_modules/zod/v4/core/json-schema.cjs +19 -0
- package/node_modules/zod/v4/core/memoizer.cjs +111 -23
- package/node_modules/zod/v4/core/memoizer.js +69 -23
- package/node_modules/zod/v4/core/parse.cjs +56 -16
- package/node_modules/zod/v4/core/parse.js +37 -16
- package/node_modules/zod/v4/core/regexes.cjs +31 -6
- package/node_modules/zod/v4/core/regexes.d.cts +3 -0
- package/node_modules/zod/v4/core/regexes.d.ts +3 -0
- package/node_modules/zod/v4/core/regexes.js +10 -4
- package/node_modules/zod/v4/core/registries.cjs +19 -0
- package/node_modules/zod/v4/core/schemas.cjs +302 -114
- package/node_modules/zod/v4/core/schemas.d.cts +28 -8
- package/node_modules/zod/v4/core/schemas.d.ts +28 -8
- package/node_modules/zod/v4/core/schemas.js +279 -113
- package/node_modules/zod/v4/core/standard-schema.cjs +19 -0
- package/node_modules/zod/v4/core/to-json-schema.cjs +28 -7
- package/node_modules/zod/v4/core/to-json-schema.d.cts +4 -3
- package/node_modules/zod/v4/core/to-json-schema.d.ts +4 -3
- package/node_modules/zod/v4/core/to-json-schema.js +7 -6
- package/node_modules/zod/v4/core/util.cjs +177 -143
- package/node_modules/zod/v4/core/util.d.cts +25 -4
- package/node_modules/zod/v4/core/util.d.ts +25 -4
- package/node_modules/zod/v4/core/util.js +156 -143
- package/node_modules/zod/v4/core/versions.cjs +21 -2
- package/node_modules/zod/v4/core/versions.d.cts +1 -1
- package/node_modules/zod/v4/core/versions.d.ts +1 -1
- package/node_modules/zod/v4/core/versions.js +2 -2
- package/node_modules/zod/v4/core/visit.cjs +19 -0
- package/node_modules/zod/v4/index.cjs +19 -0
- package/node_modules/zod/v4/locales/ar.cjs +2 -0
- package/node_modules/zod/v4/locales/ar.js +2 -0
- package/node_modules/zod/v4/locales/az.cjs +2 -0
- package/node_modules/zod/v4/locales/az.js +2 -0
- package/node_modules/zod/v4/locales/be.cjs +2 -0
- package/node_modules/zod/v4/locales/be.js +2 -0
- package/node_modules/zod/v4/locales/bg.cjs +2 -0
- package/node_modules/zod/v4/locales/bg.js +2 -0
- package/node_modules/zod/v4/locales/bn.cjs +2 -0
- package/node_modules/zod/v4/locales/bn.js +2 -0
- package/node_modules/zod/v4/locales/ca.cjs +2 -0
- package/node_modules/zod/v4/locales/ca.js +2 -0
- package/node_modules/zod/v4/locales/ckb.cjs +2 -0
- package/node_modules/zod/v4/locales/ckb.js +2 -0
- package/node_modules/zod/v4/locales/cs.cjs +2 -0
- package/node_modules/zod/v4/locales/cs.js +2 -0
- package/node_modules/zod/v4/locales/da.cjs +2 -0
- package/node_modules/zod/v4/locales/da.js +2 -0
- package/node_modules/zod/v4/locales/de.cjs +2 -0
- package/node_modules/zod/v4/locales/de.js +2 -0
- package/node_modules/zod/v4/locales/el.cjs +2 -0
- package/node_modules/zod/v4/locales/el.js +2 -0
- package/node_modules/zod/v4/locales/en.cjs +2 -0
- package/node_modules/zod/v4/locales/en.js +2 -0
- package/node_modules/zod/v4/locales/eo.cjs +2 -0
- package/node_modules/zod/v4/locales/eo.js +2 -0
- package/node_modules/zod/v4/locales/es.cjs +2 -0
- package/node_modules/zod/v4/locales/es.js +2 -0
- package/node_modules/zod/v4/locales/fa.cjs +2 -0
- package/node_modules/zod/v4/locales/fa.js +2 -0
- package/node_modules/zod/v4/locales/fi.cjs +2 -0
- package/node_modules/zod/v4/locales/fi.js +2 -0
- package/node_modules/zod/v4/locales/fr-CA.cjs +2 -0
- package/node_modules/zod/v4/locales/fr-CA.js +2 -0
- package/node_modules/zod/v4/locales/fr.cjs +2 -0
- package/node_modules/zod/v4/locales/fr.js +2 -0
- package/node_modules/zod/v4/locales/gu.cjs +2 -0
- package/node_modules/zod/v4/locales/gu.js +2 -0
- package/node_modules/zod/v4/locales/he.cjs +2 -0
- package/node_modules/zod/v4/locales/he.js +2 -0
- package/node_modules/zod/v4/locales/hi.cjs +2 -0
- package/node_modules/zod/v4/locales/hi.js +2 -0
- package/node_modules/zod/v4/locales/hr.cjs +2 -0
- package/node_modules/zod/v4/locales/hr.js +2 -0
- package/node_modules/zod/v4/locales/hu.cjs +2 -0
- package/node_modules/zod/v4/locales/hu.js +2 -0
- package/node_modules/zod/v4/locales/hy.cjs +2 -0
- package/node_modules/zod/v4/locales/hy.js +2 -0
- package/node_modules/zod/v4/locales/id.cjs +2 -0
- package/node_modules/zod/v4/locales/id.js +2 -0
- package/node_modules/zod/v4/locales/index.cjs +22 -1
- package/node_modules/zod/v4/locales/index.d.cts +1 -0
- package/node_modules/zod/v4/locales/index.d.ts +1 -0
- package/node_modules/zod/v4/locales/index.js +1 -0
- package/node_modules/zod/v4/locales/is.cjs +2 -0
- package/node_modules/zod/v4/locales/is.js +2 -0
- package/node_modules/zod/v4/locales/it.cjs +2 -0
- package/node_modules/zod/v4/locales/it.js +2 -0
- package/node_modules/zod/v4/locales/ja.cjs +2 -0
- package/node_modules/zod/v4/locales/ja.js +2 -0
- package/node_modules/zod/v4/locales/ka.cjs +2 -0
- package/node_modules/zod/v4/locales/ka.js +2 -0
- package/node_modules/zod/v4/locales/km.cjs +2 -0
- package/node_modules/zod/v4/locales/km.js +2 -0
- package/node_modules/zod/v4/locales/kn.cjs +2 -0
- package/node_modules/zod/v4/locales/kn.js +2 -0
- package/node_modules/zod/v4/locales/ko.cjs +2 -0
- package/node_modules/zod/v4/locales/ko.js +2 -0
- package/node_modules/zod/v4/locales/lt.cjs +2 -0
- package/node_modules/zod/v4/locales/lt.js +2 -0
- package/node_modules/zod/v4/locales/mk.cjs +2 -0
- package/node_modules/zod/v4/locales/mk.js +2 -0
- package/node_modules/zod/v4/locales/ms.cjs +2 -0
- package/node_modules/zod/v4/locales/ms.js +2 -0
- package/node_modules/zod/v4/locales/ne.cjs +2 -0
- package/node_modules/zod/v4/locales/ne.js +2 -0
- package/node_modules/zod/v4/locales/nl.cjs +2 -0
- package/node_modules/zod/v4/locales/nl.js +2 -0
- package/node_modules/zod/v4/locales/nn.cjs +2 -0
- package/node_modules/zod/v4/locales/nn.js +2 -0
- package/node_modules/zod/v4/locales/no.cjs +2 -0
- package/node_modules/zod/v4/locales/no.js +2 -0
- package/node_modules/zod/v4/locales/ota.cjs +2 -0
- package/node_modules/zod/v4/locales/ota.js +2 -0
- package/node_modules/zod/v4/locales/pl.cjs +2 -0
- package/node_modules/zod/v4/locales/pl.js +2 -0
- package/node_modules/zod/v4/locales/ps.cjs +2 -0
- package/node_modules/zod/v4/locales/ps.js +2 -0
- package/node_modules/zod/v4/locales/pt-BR.cjs +2 -0
- package/node_modules/zod/v4/locales/pt-BR.js +2 -0
- package/node_modules/zod/v4/locales/pt.cjs +2 -0
- package/node_modules/zod/v4/locales/pt.js +2 -0
- package/node_modules/zod/v4/locales/ro.cjs +2 -0
- package/node_modules/zod/v4/locales/ro.js +2 -0
- package/node_modules/zod/v4/locales/ru.cjs +2 -0
- package/node_modules/zod/v4/locales/ru.js +2 -0
- package/node_modules/zod/v4/locales/sk.cjs +2 -0
- package/node_modules/zod/v4/locales/sk.js +2 -0
- package/node_modules/zod/v4/locales/sl.cjs +2 -0
- package/node_modules/zod/v4/locales/sl.js +2 -0
- package/node_modules/zod/v4/locales/sv.cjs +2 -0
- package/node_modules/zod/v4/locales/sv.js +2 -0
- package/node_modules/zod/v4/locales/ta.cjs +2 -0
- package/node_modules/zod/v4/locales/ta.js +2 -0
- package/node_modules/zod/v4/locales/tg.cjs +144 -0
- package/node_modules/zod/v4/locales/tg.d.cts +5 -0
- package/node_modules/zod/v4/locales/tg.d.ts +4 -0
- package/node_modules/zod/v4/locales/tg.js +117 -0
- package/node_modules/zod/v4/locales/th.cjs +2 -0
- package/node_modules/zod/v4/locales/th.js +2 -0
- package/node_modules/zod/v4/locales/tk.cjs +2 -0
- package/node_modules/zod/v4/locales/tk.js +2 -0
- package/node_modules/zod/v4/locales/tr.cjs +2 -0
- package/node_modules/zod/v4/locales/tr.js +2 -0
- package/node_modules/zod/v4/locales/uk.cjs +2 -0
- package/node_modules/zod/v4/locales/uk.js +2 -0
- package/node_modules/zod/v4/locales/ur.cjs +2 -0
- package/node_modules/zod/v4/locales/ur.js +2 -0
- package/node_modules/zod/v4/locales/uz.cjs +2 -0
- package/node_modules/zod/v4/locales/uz.js +2 -0
- package/node_modules/zod/v4/locales/vi.cjs +2 -0
- package/node_modules/zod/v4/locales/vi.js +2 -0
- package/node_modules/zod/v4/locales/yo.cjs +2 -0
- package/node_modules/zod/v4/locales/yo.js +2 -0
- package/node_modules/zod/v4/locales/zh-CN.cjs +2 -0
- package/node_modules/zod/v4/locales/zh-CN.js +2 -0
- package/node_modules/zod/v4/locales/zh-TW.cjs +2 -0
- package/node_modules/zod/v4/locales/zh-TW.js +2 -0
- package/node_modules/zod/v4/mini/checks.cjs +19 -0
- package/node_modules/zod/v4/mini/coerce.cjs +19 -0
- package/node_modules/zod/v4/mini/deep-partial.cjs +19 -0
- package/node_modules/zod/v4/mini/external.cjs +22 -1
- package/node_modules/zod/v4/mini/external.d.cts +1 -1
- package/node_modules/zod/v4/mini/external.d.ts +1 -1
- package/node_modules/zod/v4/mini/external.js +1 -1
- package/node_modules/zod/v4/mini/in-out.cjs +19 -0
- package/node_modules/zod/v4/mini/index.cjs +19 -0
- package/node_modules/zod/v4/mini/iso.cjs +19 -0
- package/node_modules/zod/v4/mini/parse.cjs +19 -0
- package/node_modules/zod/v4/mini/schemas.cjs +44 -8
- package/node_modules/zod/v4/mini/schemas.d.cts +6 -1
- package/node_modules/zod/v4/mini/schemas.d.ts +6 -1
- package/node_modules/zod/v4/mini/schemas.js +21 -6
- package/node_modules/zod/v4-mini/index.cjs +19 -0
- package/openclaw.plugin.json +49 -3
- package/package.json +8 -8
- package/dist/.setup/accounts.runtime-BpWv-6-2.mjs +0 -2
- package/dist/.setup/action-runtime.runtime-DwH3NdpT.mjs +0 -2
- package/dist/.setup/doctor-contract-CtMUqjvk.mjs +0 -186
- package/dist/.setup/monitor-Dw5uuKWM.mjs +0 -13
- package/dist/.setup/rolldown-runtime-D7D4PA-g.mjs +0 -13
- package/dist/.setup/send.runtime-BmqRP-WG.mjs +0 -2
- package/dist/.setup/slash-commands.runtime-BpSH0b3q.mjs +0 -2
- package/dist/.setup/slash-skill-commands.runtime-Bi1JWZHY.mjs +0 -2
- package/node_modules/undici/lib/handler/unwrap-handler.js +0 -106
- package/node_modules/undici/lib/handler/wrap-handler.js +0 -105
- package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
- package/node_modules/undici/lib/util/promise.js +0 -28
- /package/node_modules/typebox/build/guard/{string.d.mts → unicode/unicode_segment.d.mts} +0 -0
|
@@ -10,7 +10,8 @@ const {
|
|
|
10
10
|
InformationalError,
|
|
11
11
|
InvalidArgumentError,
|
|
12
12
|
HeadersTimeoutError,
|
|
13
|
-
BodyTimeoutError
|
|
13
|
+
BodyTimeoutError,
|
|
14
|
+
ResponseExceededMaxSizeError
|
|
14
15
|
} = require('../core/errors.js')
|
|
15
16
|
const {
|
|
16
17
|
kUrl,
|
|
@@ -26,24 +27,38 @@ const {
|
|
|
26
27
|
kStrictContentLength,
|
|
27
28
|
kOnError,
|
|
28
29
|
kMaxConcurrentStreams,
|
|
29
|
-
kPingInterval,
|
|
30
30
|
kHTTP2Session,
|
|
31
|
-
|
|
32
|
-
kHTTP2ConnectionWindowSize,
|
|
31
|
+
kHostAuthority,
|
|
33
32
|
kResume,
|
|
34
33
|
kSize,
|
|
35
34
|
kHTTPContext,
|
|
36
35
|
kClosed,
|
|
37
|
-
|
|
36
|
+
kKeepAliveDefaultTimeout,
|
|
38
37
|
kHeadersTimeout,
|
|
38
|
+
kBodyTimeout,
|
|
39
39
|
kEnableConnectProtocol,
|
|
40
40
|
kRemoteSettings,
|
|
41
41
|
kHTTP2Stream,
|
|
42
|
-
kHTTP2SessionState
|
|
42
|
+
kHTTP2SessionState,
|
|
43
|
+
kHTTP2Options,
|
|
44
|
+
kMaxResponseSize
|
|
43
45
|
} = require('../core/symbols.js')
|
|
44
46
|
const { channels } = require('../core/diagnostics.js')
|
|
45
47
|
|
|
46
48
|
const kOpenStreams = Symbol('open streams')
|
|
49
|
+
const kRequestStreamId = Symbol('request stream id')
|
|
50
|
+
const kRequestStream = Symbol('request stream')
|
|
51
|
+
const kRequestStreamCleanup = Symbol('request stream cleanup')
|
|
52
|
+
const kRequestStreamState = Symbol('request stream state')
|
|
53
|
+
const kReceivedGoAway = Symbol('received goaway')
|
|
54
|
+
const kGoAwayReplayAttempts = Symbol('goaway replay attempts')
|
|
55
|
+
const kRefusedStreamRetry = Symbol('refused stream retry')
|
|
56
|
+
|
|
57
|
+
// RFC 9113 section 8.7: a client SHOULD NOT automatically retry a request more
|
|
58
|
+
// than once. Without a budget a peer that keeps refusing turns one request into
|
|
59
|
+
// an unbounded connect/refuse/reconnect loop that never settles and starves the
|
|
60
|
+
// event loop.
|
|
61
|
+
const MAX_GOAWAY_REPLAY_ATTEMPTS = 1
|
|
47
62
|
|
|
48
63
|
let extractBody
|
|
49
64
|
|
|
@@ -66,40 +81,180 @@ const {
|
|
|
66
81
|
HTTP2_HEADER_EXPECT,
|
|
67
82
|
HTTP2_HEADER_STATUS,
|
|
68
83
|
HTTP2_HEADER_PROTOCOL,
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
NGHTTP2_NO_ERROR,
|
|
85
|
+
NGHTTP2_REFUSED_STREAM
|
|
71
86
|
}
|
|
72
87
|
} = http2
|
|
73
88
|
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
function getGoAwayError (session, errorCode) {
|
|
90
|
+
return session[kError] ||
|
|
91
|
+
(errorCode === NGHTTP2_NO_ERROR
|
|
92
|
+
? new InformationalError(`HTTP/2: "GOAWAY" frame received with code ${errorCode}`)
|
|
93
|
+
: new SocketError(`HTTP/2: "GOAWAY" frame received with code ${errorCode}`, util.getSocketInfo(session[kSocket])))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function resetHttp2Session (session, err) {
|
|
97
|
+
const client = session[kClient]
|
|
98
|
+
const socket = session[kSocket]
|
|
99
|
+
|
|
100
|
+
if (client[kHTTP2Session] === session) {
|
|
101
|
+
client[kSocket] = null
|
|
102
|
+
client[kHTTPContext] = null
|
|
103
|
+
client[kHTTP2Session] = null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (socket != null && socket[kError] == null) {
|
|
107
|
+
socket[kError] = err
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!session.closed && !session.destroyed) {
|
|
111
|
+
try {
|
|
112
|
+
session.destroy(err)
|
|
113
|
+
} catch {}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
util.destroy(socket, err)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getGoAwayPendingIdx (client, lastStreamID) {
|
|
120
|
+
const maxAcceptedStreamID = Number.isInteger(lastStreamID) ? lastStreamID : Number.MAX_SAFE_INTEGER
|
|
121
|
+
|
|
122
|
+
for (let i = client[kRunningIdx]; i < client[kPendingIdx]; i++) {
|
|
123
|
+
const request = client[kQueue][i]
|
|
124
|
+
|
|
125
|
+
if (request == null) {
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (typeof request[kRequestStreamId] !== 'number' || request[kRequestStreamId] > maxAcceptedStreamID) {
|
|
130
|
+
return i
|
|
88
131
|
}
|
|
89
132
|
}
|
|
90
133
|
|
|
91
|
-
return
|
|
134
|
+
return client[kPendingIdx]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function detachRequestFromStream (request) {
|
|
138
|
+
request[kRequestStreamId] = null
|
|
139
|
+
request[kRequestStream] = null
|
|
140
|
+
request[kRequestStreamCleanup] = null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function bindRequestToStream (request, stream, cleanup) {
|
|
144
|
+
const previousCleanup = request[kRequestStreamCleanup]
|
|
145
|
+
const previousStream = request[kRequestStream]
|
|
146
|
+
detachRequestFromStream(request)
|
|
147
|
+
previousCleanup?.(previousStream)
|
|
148
|
+
request[kRequestStreamId] = stream.id
|
|
149
|
+
request[kRequestStream] = stream
|
|
150
|
+
request[kRequestStreamCleanup] = cleanup
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function clearRequestStream (request) {
|
|
154
|
+
const cleanup = request[kRequestStreamCleanup]
|
|
155
|
+
const stream = request[kRequestStream]
|
|
156
|
+
detachRequestFromStream(request)
|
|
157
|
+
cleanup?.(stream)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function requeueUnsentRequest (client, request) {
|
|
161
|
+
client[kQueue].splice(client[kPendingIdx] + 1, 0, request)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function completeRequest (client, request, resetPendingIdx = false) {
|
|
165
|
+
const queue = client[kQueue]
|
|
166
|
+
const runningIdx = client[kRunningIdx]
|
|
167
|
+
|
|
168
|
+
// In-order completion: clear the request and advance without splicing.
|
|
169
|
+
// The client's resume loop compacts cleared slots once the index grows.
|
|
170
|
+
if (runningIdx < client[kPendingIdx] && queue[runningIdx] === request) {
|
|
171
|
+
queue[runningIdx] = null
|
|
172
|
+
client[kRunningIdx] = runningIdx + 1
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const index = queue.indexOf(request, runningIdx)
|
|
177
|
+
|
|
178
|
+
if (index === -1 || index >= client[kPendingIdx]) {
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
queue.splice(index, 1)
|
|
183
|
+
client[kPendingIdx]--
|
|
184
|
+
|
|
185
|
+
if (resetPendingIdx && client[kPendingIdx] < client[kRunningIdx]) {
|
|
186
|
+
client[kPendingIdx] = client[kRunningIdx]
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function canReplayRequest (request) {
|
|
191
|
+
const { body } = request
|
|
192
|
+
|
|
193
|
+
return body == null || util.isBuffer(body) || util.isBlobLike(body)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Count a GOAWAY refusal against the request's replay budget. A peer that
|
|
197
|
+
// refuses every connection must eventually surface an error to the caller
|
|
198
|
+
// rather than being retried forever. Kept separate from canReplayRequest so
|
|
199
|
+
// that the REFUSED_STREAM retry, which has its own single-attempt limit, does
|
|
200
|
+
// not consume this budget just by asking whether the body can be replayed.
|
|
201
|
+
function registerGoAwayRefusal (request) {
|
|
202
|
+
const attempts = (request[kGoAwayReplayAttempts] ?? 0) + 1
|
|
203
|
+
request[kGoAwayReplayAttempts] = attempts
|
|
204
|
+
|
|
205
|
+
return attempts <= MAX_GOAWAY_REPLAY_ATTEMPTS
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function closeStream (stream, code = NGHTTP2_REFUSED_STREAM) {
|
|
209
|
+
if (stream != null && !stream.destroyed && !stream.closed) {
|
|
210
|
+
try {
|
|
211
|
+
stream.close(code)
|
|
212
|
+
} catch {}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function detachRequestStreamForClose (request) {
|
|
217
|
+
const stream = request[kRequestStream]
|
|
218
|
+
|
|
219
|
+
clearRequestStream(request)
|
|
220
|
+
severRequestStream(stream)
|
|
221
|
+
|
|
222
|
+
return stream
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Unbind a stream from its request for good. releaseRequestStream() alone
|
|
226
|
+
// leaves the 'close' listener attached and kRequestStreamState populated, so a
|
|
227
|
+
// stream abandoned here would still run completeRequestStream() later — and
|
|
228
|
+
// splice out the request that has since been requeued onto another session.
|
|
229
|
+
function severRequestStream (stream) {
|
|
230
|
+
if (stream == null || stream[kRequestStreamState] == null) {
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
stream[kRequestStreamState] = null
|
|
235
|
+
stream.off('close', completeRequestStream)
|
|
236
|
+
// Upgrade streams use their own close cleanup, which would otherwise release
|
|
237
|
+
// the session a second time after the stream has been severed for GOAWAY.
|
|
238
|
+
stream.off('close', onUpgradeStreamClose)
|
|
239
|
+
|
|
240
|
+
if (stream[kHTTP2Session] != null) {
|
|
241
|
+
closeStreamSession(stream)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!stream.destroyed && !stream.closed) {
|
|
245
|
+
stream.once('error', noop)
|
|
246
|
+
}
|
|
92
247
|
}
|
|
93
248
|
|
|
94
249
|
function connectH2 (client, socket) {
|
|
95
250
|
client[kSocket] = socket
|
|
96
251
|
|
|
97
|
-
const http2InitialWindowSize = client[
|
|
98
|
-
const http2ConnectionWindowSize = client[
|
|
252
|
+
const http2InitialWindowSize = client[kHTTP2Options].sessionOptions?.initialWindowSize
|
|
253
|
+
const http2ConnectionWindowSize = client[kHTTP2Options].connectionWindowSize
|
|
99
254
|
|
|
100
255
|
const session = http2.connect(client[kUrl], {
|
|
101
256
|
createConnection: () => socket,
|
|
102
|
-
peerMaxConcurrentStreams: client[
|
|
257
|
+
peerMaxConcurrentStreams: client[kHTTP2Options].maxConcurrentStreams,
|
|
103
258
|
settings: {
|
|
104
259
|
// TODO(metcoder95): add support for PUSH
|
|
105
260
|
enablePush: false,
|
|
@@ -112,10 +267,20 @@ function connectH2 (client, socket) {
|
|
|
112
267
|
session[kClient] = client
|
|
113
268
|
session[kSocket] = socket
|
|
114
269
|
session[kHTTP2SessionState] = {
|
|
270
|
+
idleTimeout: null,
|
|
271
|
+
// Armed while the peer advertises MAX_CONCURRENT_STREAMS = 0 and we have
|
|
272
|
+
// work that cannot start. See setNoStreamsTimeout.
|
|
273
|
+
noStreamsTimeout: null,
|
|
274
|
+
// Sockets start out ref'd. Session ref/unref proxies to the socket, so a
|
|
275
|
+
// single cached flag lets us skip redundant uv ref/unref calls, provided
|
|
276
|
+
// every ref/unref of the session or its socket goes through
|
|
277
|
+
// refH2Session/unrefH2Session.
|
|
278
|
+
refed: true,
|
|
115
279
|
ping: {
|
|
116
|
-
interval: client[
|
|
280
|
+
interval: client[kHTTP2Options].pingInterval === 0 ? null : setInterval(onHttp2SendPing, client[kHTTP2Options].pingInterval, session).unref()
|
|
117
281
|
}
|
|
118
282
|
}
|
|
283
|
+
session[kReceivedGoAway] = false
|
|
119
284
|
// We set it to true by default in a best-effort; however once connected to an H2 server
|
|
120
285
|
// we will check if extended CONNECT protocol is supported or not
|
|
121
286
|
// and set this value accordingly.
|
|
@@ -130,14 +295,13 @@ function connectH2 (client, socket) {
|
|
|
130
295
|
|
|
131
296
|
util.addListener(session, 'error', onHttp2SessionError)
|
|
132
297
|
util.addListener(session, 'frameError', onHttp2FrameError)
|
|
133
|
-
util.addListener(session, 'end', onHttp2SessionEnd)
|
|
134
298
|
util.addListener(session, 'goaway', onHttp2SessionGoAway)
|
|
135
299
|
util.addListener(session, 'close', onHttp2SessionClose)
|
|
136
300
|
util.addListener(session, 'remoteSettings', onHttp2RemoteSettings)
|
|
137
301
|
// TODO (@metcoder95): implement SETTINGS support
|
|
138
302
|
// util.addListener(session, 'localSettings', onHttp2RemoteSettings)
|
|
139
303
|
|
|
140
|
-
session
|
|
304
|
+
unrefH2Session(session)
|
|
141
305
|
|
|
142
306
|
client[kHTTP2Session] = session
|
|
143
307
|
socket[kHTTP2Session] = session
|
|
@@ -187,27 +351,25 @@ function connectH2 (client, socket) {
|
|
|
187
351
|
* @returns {boolean}
|
|
188
352
|
*/
|
|
189
353
|
busy (request) {
|
|
354
|
+
if (session[kRemoteSettings] === false && client[kRunning] > 0) {
|
|
355
|
+
return true
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (client[kRunning] >= client[kMaxConcurrentStreams]) {
|
|
359
|
+
return true
|
|
360
|
+
}
|
|
361
|
+
|
|
190
362
|
if (request != null) {
|
|
191
363
|
if (client[kRunning] > 0) {
|
|
192
364
|
// We are already processing requests
|
|
193
365
|
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
|
|
366
|
+
// Unlike HTTP/1.1 pipelining, HTTP/2 multiplexes requests on
|
|
367
|
+
// independent streams, so non-idempotent requests can be dispatched
|
|
368
|
+
// concurrently. Retry eligibility is handled by stream/session error
|
|
369
|
+
// handling instead of by serializing all non-idempotent requests.
|
|
198
370
|
// Don't dispatch an upgrade until all preceding requests have completed.
|
|
199
371
|
// Possibly, we do not have remote settings confirmed yet.
|
|
200
372
|
if ((request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false) return true
|
|
201
|
-
// Request with stream or iterator body can error while other requests
|
|
202
|
-
// are inflight and indirectly error those as well.
|
|
203
|
-
// Ensure this doesn't happen by waiting for inflight
|
|
204
|
-
// to complete before dispatching.
|
|
205
|
-
|
|
206
|
-
// Request with stream or iterator body cannot be retried.
|
|
207
|
-
// Ensure that no other requests are inflight and
|
|
208
|
-
// could cause failure.
|
|
209
|
-
if (util.bodyLength(request.body) !== 0 &&
|
|
210
|
-
(util.isStream(request.body) || util.isAsyncIterable(request.body) || util.isFormDataLike(request.body))) return true
|
|
211
373
|
} else {
|
|
212
374
|
return (request.upgrade === 'websocket' || request.method === 'CONNECT') && session[kRemoteSettings] === false
|
|
213
375
|
}
|
|
@@ -218,22 +380,154 @@ function connectH2 (client, socket) {
|
|
|
218
380
|
}
|
|
219
381
|
}
|
|
220
382
|
|
|
383
|
+
// Session ref/unref proxies to the underlying socket, so refH2Session and
|
|
384
|
+
// unrefH2Session cover both and can skip the call when the cached ref state
|
|
385
|
+
// already matches.
|
|
386
|
+
function refH2Session (session) {
|
|
387
|
+
const state = session[kHTTP2SessionState]
|
|
388
|
+
|
|
389
|
+
if (state.refed === false) {
|
|
390
|
+
state.refed = true
|
|
391
|
+
session.ref()
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function unrefH2Session (session) {
|
|
396
|
+
const state = session[kHTTP2SessionState]
|
|
397
|
+
|
|
398
|
+
if (state.refed === true) {
|
|
399
|
+
state.refed = false
|
|
400
|
+
session.unref()
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
221
404
|
function resumeH2 (client) {
|
|
222
405
|
const socket = client[kSocket]
|
|
406
|
+
const session = client[kHTTP2Session]
|
|
223
407
|
|
|
224
408
|
if (socket?.destroyed === false) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
409
|
+
if (client[kSize] === 0 || client[kMaxConcurrentStreams] === 0) {
|
|
410
|
+
unrefH2Session(session)
|
|
411
|
+
} else {
|
|
412
|
+
refH2Session(session)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (client[kSize] === 0 && session[kOpenStreams] === 0) {
|
|
416
|
+
setHttp2IdleTimeout(session)
|
|
417
|
+
} else {
|
|
418
|
+
clearHttp2IdleTimeout(session)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (client[kMaxConcurrentStreams] === 0 && client[kRunning] === 0 && client[kPending] > 0) {
|
|
422
|
+
setNoStreamsTimeout(session)
|
|
232
423
|
} else {
|
|
233
|
-
|
|
234
|
-
|
|
424
|
+
clearNoStreamsTimeout(session)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function clearNoStreamsTimeout (session) {
|
|
430
|
+
const state = session[kHTTP2SessionState]
|
|
431
|
+
|
|
432
|
+
if (state?.noStreamsTimeout != null) {
|
|
433
|
+
clearTimeout(state.noStreamsTimeout)
|
|
434
|
+
state.noStreamsTimeout = null
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// A peer is allowed to advertise SETTINGS_MAX_CONCURRENT_STREAMS = 0 to refuse
|
|
439
|
+
// new streams (RFC 9113 §6.5.2), and is expected to raise it again later. Until
|
|
440
|
+
// it does, busy() reports the client as permanently busy and queued requests
|
|
441
|
+
// cannot open a stream — which means no per-stream timeout covers them, and no
|
|
442
|
+
// reconnect can happen either, so the SETTINGS frame that would lift the limit
|
|
443
|
+
// can never arrive. Give the peer headersTimeout to start honouring requests
|
|
444
|
+
// before failing them; a request that cannot even be sent has missed the same
|
|
445
|
+
// deadline as one whose headers never arrive.
|
|
446
|
+
function setNoStreamsTimeout (session) {
|
|
447
|
+
const client = session[kClient]
|
|
448
|
+
const state = session[kHTTP2SessionState]
|
|
449
|
+
const timeout = client[kHeadersTimeout]
|
|
450
|
+
|
|
451
|
+
if (!timeout || state.noStreamsTimeout != null) {
|
|
452
|
+
return
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
state.noStreamsTimeout = setTimeout(onNoStreamsTimeout, timeout, session).unref()
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function onNoStreamsTimeout (session) {
|
|
459
|
+
const client = session[kClient]
|
|
460
|
+
const state = session[kHTTP2SessionState]
|
|
461
|
+
|
|
462
|
+
state.noStreamsTimeout = null
|
|
463
|
+
|
|
464
|
+
if (
|
|
465
|
+
client[kHTTP2Session] !== session ||
|
|
466
|
+
client[kMaxConcurrentStreams] !== 0 ||
|
|
467
|
+
client[kRunning] !== 0 ||
|
|
468
|
+
client[kPending] === 0
|
|
469
|
+
) {
|
|
470
|
+
return
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const err = new HeadersTimeoutError(
|
|
474
|
+
`HTTP/2: server did not accept a new stream within ${client[kHeadersTimeout]}`
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
const requests = client[kQueue].splice(client[kPendingIdx])
|
|
478
|
+
for (let i = 0; i < requests.length; i++) {
|
|
479
|
+
if (requests[i] != null) {
|
|
480
|
+
util.errorRequest(client, requests[i], err)
|
|
235
481
|
}
|
|
236
482
|
}
|
|
483
|
+
|
|
484
|
+
// Drop the unusable session so the next request gets a fresh connection,
|
|
485
|
+
// whose SETTINGS may well allow streams again.
|
|
486
|
+
session[kError] = err
|
|
487
|
+
resetHttp2Session(session, err)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function clearHttp2IdleTimeout (session) {
|
|
491
|
+
const state = session[kHTTP2SessionState]
|
|
492
|
+
|
|
493
|
+
if (state?.idleTimeout != null) {
|
|
494
|
+
clearTimeout(state.idleTimeout)
|
|
495
|
+
state.idleTimeout = null
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function setHttp2IdleTimeout (session) {
|
|
500
|
+
const client = session[kClient]
|
|
501
|
+
|
|
502
|
+
if (client[kHTTP2Session] !== session || session.closed || session.destroyed) {
|
|
503
|
+
return
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (session[kOpenStreams] !== 0 || client[kSize] !== 0) {
|
|
507
|
+
clearHttp2IdleTimeout(session)
|
|
508
|
+
return
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const state = session[kHTTP2SessionState]
|
|
512
|
+
if (state.idleTimeout == null) {
|
|
513
|
+
state.idleTimeout = setTimeout(onHttp2SessionIdleTimeout, client[kKeepAliveDefaultTimeout], session).unref()
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function onHttp2SessionIdleTimeout (session) {
|
|
518
|
+
const client = session[kClient]
|
|
519
|
+
const socket = session[kSocket]
|
|
520
|
+
const state = session[kHTTP2SessionState]
|
|
521
|
+
|
|
522
|
+
state.idleTimeout = null
|
|
523
|
+
|
|
524
|
+
if (client[kHTTP2Session] !== session || session[kOpenStreams] !== 0 || client[kSize] !== 0 || session.closed || session.destroyed) {
|
|
525
|
+
return
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const err = new InformationalError('socket idle timeout')
|
|
529
|
+
socket[kError] = err
|
|
530
|
+
util.destroy(socket, err)
|
|
237
531
|
}
|
|
238
532
|
|
|
239
533
|
function applyConnectionWindowSize (connectionWindowSize) {
|
|
@@ -280,7 +574,7 @@ function onHttp2SendPing (session) {
|
|
|
280
574
|
|
|
281
575
|
function onPing (err, duration) {
|
|
282
576
|
const client = this[kClient]
|
|
283
|
-
const socket = this[
|
|
577
|
+
const socket = this[kSocket]
|
|
284
578
|
|
|
285
579
|
if (err != null) {
|
|
286
580
|
const error = new InformationalError(`HTTP/2: "PING" errored - type ${err.message}`)
|
|
@@ -296,23 +590,26 @@ function onHttp2SessionError (err) {
|
|
|
296
590
|
assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID')
|
|
297
591
|
|
|
298
592
|
this[kSocket][kError] = err
|
|
593
|
+
|
|
594
|
+
if (this[kReceivedGoAway]) {
|
|
595
|
+
return
|
|
596
|
+
}
|
|
597
|
+
|
|
299
598
|
this[kClient][kOnError](err)
|
|
300
599
|
}
|
|
301
600
|
|
|
302
601
|
function onHttp2FrameError (type, code, id) {
|
|
303
602
|
if (id === 0) {
|
|
603
|
+
if (this[kReceivedGoAway]) {
|
|
604
|
+
return
|
|
605
|
+
}
|
|
606
|
+
|
|
304
607
|
const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`)
|
|
305
608
|
this[kSocket][kError] = err
|
|
306
609
|
this[kClient][kOnError](err)
|
|
307
610
|
}
|
|
308
611
|
}
|
|
309
612
|
|
|
310
|
-
function onHttp2SessionEnd () {
|
|
311
|
-
const err = new SocketError('other side closed', util.getSocketInfo(this[kSocket]))
|
|
312
|
-
this.destroy(err)
|
|
313
|
-
util.destroy(this[kSocket], err)
|
|
314
|
-
}
|
|
315
|
-
|
|
316
613
|
/**
|
|
317
614
|
* This is the root cause of #3011
|
|
318
615
|
* We need to handle GOAWAY frames properly, and trigger the session close
|
|
@@ -320,80 +617,82 @@ function onHttp2SessionEnd () {
|
|
|
320
617
|
*
|
|
321
618
|
* @this {import('http2').ClientHttp2Session}
|
|
322
619
|
* @param {number} errorCode
|
|
620
|
+
* @param {number} lastStreamID
|
|
323
621
|
*/
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// sit at the head instead of the one that actually finished, which both lost
|
|
327
|
-
// requests and left phantom running slots behind.
|
|
328
|
-
function completeRequest (client, request, resetPendingIdx = false) {
|
|
329
|
-
const queue = client[kQueue]
|
|
330
|
-
const runningIdx = client[kRunningIdx]
|
|
331
|
-
|
|
332
|
-
// In-order completion: clear the request and advance without splicing.
|
|
333
|
-
// The client's resume loop compacts cleared slots once the index grows.
|
|
334
|
-
if (runningIdx < client[kPendingIdx] && queue[runningIdx] === request) {
|
|
335
|
-
queue[runningIdx] = null
|
|
336
|
-
client[kRunningIdx] = runningIdx + 1
|
|
622
|
+
function onHttp2SessionGoAway (errorCode, lastStreamID) {
|
|
623
|
+
if (this[kReceivedGoAway]) {
|
|
337
624
|
return
|
|
338
625
|
}
|
|
339
626
|
|
|
340
|
-
|
|
627
|
+
this[kReceivedGoAway] = true
|
|
341
628
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
629
|
+
const err = getGoAwayError(this, errorCode)
|
|
630
|
+
const client = this[kClient]
|
|
631
|
+
const previousPendingIdx = client[kPendingIdx]
|
|
632
|
+
const pendingIdx = getGoAwayPendingIdx(client, lastStreamID)
|
|
633
|
+
const retriableRequests = []
|
|
634
|
+
const streamsToClose = []
|
|
345
635
|
|
|
346
|
-
|
|
347
|
-
|
|
636
|
+
// Closing one stream after GOAWAY can synchronously emit frameError on
|
|
637
|
+
// sibling streams. Detach all affected requests first so those errors do
|
|
638
|
+
// not fail requests that are about to be requeued.
|
|
639
|
+
for (let i = pendingIdx; i < previousPendingIdx; i++) {
|
|
640
|
+
const request = client[kQueue][i]
|
|
348
641
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
}
|
|
642
|
+
if (request != null) {
|
|
643
|
+
streamsToClose.push(detachRequestStreamForClose(request))
|
|
353
644
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
645
|
+
if (canReplayRequest(request) && registerGoAwayRefusal(request)) {
|
|
646
|
+
retriableRequests.push(request)
|
|
647
|
+
} else {
|
|
648
|
+
util.errorRequest(client, request, err)
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
358
652
|
|
|
359
|
-
|
|
360
|
-
|
|
653
|
+
for (let i = 0; i < streamsToClose.length; i++) {
|
|
654
|
+
closeStream(streamsToClose[i])
|
|
655
|
+
}
|
|
361
656
|
|
|
362
|
-
|
|
363
|
-
|
|
657
|
+
if (pendingIdx !== previousPendingIdx) {
|
|
658
|
+
const remainingPendingRequests = client[kQueue].slice(previousPendingIdx)
|
|
659
|
+
client[kQueue].length = pendingIdx
|
|
660
|
+
client[kQueue].push(...retriableRequests, ...remainingPendingRequests)
|
|
661
|
+
}
|
|
364
662
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
663
|
+
if (client[kHTTP2Session] === this) {
|
|
664
|
+
client[kSocket] = null
|
|
665
|
+
client[kHTTPContext] = null
|
|
666
|
+
client[kHTTP2Session] = null
|
|
667
|
+
}
|
|
368
668
|
|
|
369
|
-
|
|
669
|
+
clearHttp2IdleTimeout(this)
|
|
670
|
+
clearNoStreamsTimeout(this)
|
|
370
671
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const request = client[kQueue][client[kRunningIdx]]
|
|
374
|
-
client[kQueue][client[kRunningIdx]++] = null
|
|
375
|
-
if (request != null) {
|
|
376
|
-
util.errorRequest(client, request, err)
|
|
377
|
-
}
|
|
378
|
-
client[kPendingIdx] = client[kRunningIdx]
|
|
672
|
+
if (!this.closed && !this.destroyed) {
|
|
673
|
+
this.close()
|
|
379
674
|
}
|
|
380
675
|
|
|
381
|
-
|
|
676
|
+
client[kPendingIdx] = pendingIdx
|
|
382
677
|
|
|
383
678
|
client.emit('disconnect', client[kUrl], [client], err)
|
|
384
|
-
client.emit('connectionError', client[kUrl], [client], err)
|
|
385
679
|
|
|
386
680
|
client[kResume]()
|
|
387
681
|
}
|
|
388
682
|
|
|
389
683
|
function onHttp2SessionClose () {
|
|
390
|
-
const { [kClient]: client, [kHTTP2SessionState]: state } = this
|
|
391
|
-
const { [kSocket]: socket } = client
|
|
684
|
+
const { [kClient]: client, [kHTTP2SessionState]: state, [kSocket]: socket } = this
|
|
392
685
|
|
|
393
|
-
const err =
|
|
686
|
+
const err = socket[kError] || this[kError] || new SocketError('closed', util.getSocketInfo(socket))
|
|
394
687
|
|
|
395
|
-
client[
|
|
396
|
-
|
|
688
|
+
if (client[kHTTP2Session] === this) {
|
|
689
|
+
client[kSocket] = null
|
|
690
|
+
client[kHTTPContext] = null
|
|
691
|
+
client[kHTTP2Session] = null
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
clearHttp2IdleTimeout(this)
|
|
695
|
+
clearNoStreamsTimeout(this)
|
|
397
696
|
|
|
398
697
|
if (state.ping.interval != null) {
|
|
399
698
|
clearInterval(state.ping.interval)
|
|
@@ -417,15 +716,27 @@ function onHttp2SessionClose () {
|
|
|
417
716
|
function onHttp2SocketClose () {
|
|
418
717
|
const err = this[kError] || new SocketError('closed', util.getSocketInfo(this))
|
|
419
718
|
|
|
420
|
-
const
|
|
719
|
+
const session = this[kHTTP2Session]
|
|
720
|
+
const client = session[kClient]
|
|
721
|
+
|
|
722
|
+
if (client[kSocket] !== this) {
|
|
723
|
+
// Ignore stale socket closes from a detached GOAWAY session and from any
|
|
724
|
+
// session that has already been replaced. If the session was detached
|
|
725
|
+
// without a GOAWAY and there is no replacement yet, we still need the
|
|
726
|
+
// close event to flush the client state.
|
|
727
|
+
if (session[kReceivedGoAway] || (client[kHTTP2Session] != null && client[kHTTP2Session] !== session)) {
|
|
728
|
+
return
|
|
729
|
+
}
|
|
730
|
+
}
|
|
421
731
|
|
|
422
732
|
client[kSocket] = null
|
|
423
733
|
client[kHTTPContext] = null
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
this[kHTTP2Session].destroy(err)
|
|
734
|
+
if (client[kHTTP2Session] === session) {
|
|
735
|
+
client[kHTTP2Session] = null
|
|
427
736
|
}
|
|
428
737
|
|
|
738
|
+
session.destroy(err)
|
|
739
|
+
|
|
429
740
|
client[kPendingIdx] = client[kRunningIdx]
|
|
430
741
|
|
|
431
742
|
assert(client[kRunning] === 0)
|
|
@@ -440,7 +751,11 @@ function onHttp2SocketError (err) {
|
|
|
440
751
|
|
|
441
752
|
this[kError] = err
|
|
442
753
|
|
|
443
|
-
this[
|
|
754
|
+
if (this[kHTTP2Session]?.[kReceivedGoAway]) {
|
|
755
|
+
return
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
this[kHTTP2Session]?.[kClient]?.[kOnError](err)
|
|
444
759
|
}
|
|
445
760
|
|
|
446
761
|
function onHttp2SocketEnd () {
|
|
@@ -451,33 +766,77 @@ function onSocketClose () {
|
|
|
451
766
|
this[kClosed] = true
|
|
452
767
|
}
|
|
453
768
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
769
|
+
function noop () {}
|
|
770
|
+
|
|
771
|
+
function closeStreamSession (stream) {
|
|
772
|
+
const session = stream[kHTTP2Session]
|
|
773
|
+
|
|
774
|
+
stream[kHTTP2Session] = null
|
|
775
|
+
session[kOpenStreams] -= 1
|
|
776
|
+
if (session[kOpenStreams] === 0) {
|
|
777
|
+
unrefH2Session(session)
|
|
778
|
+
setHttp2IdleTimeout(session)
|
|
779
|
+
}
|
|
457
780
|
}
|
|
458
781
|
|
|
459
|
-
function
|
|
460
|
-
|
|
461
|
-
// bodyTimeout for both made headersTimeout a no-op over HTTP/2.
|
|
462
|
-
const headersTimeout = request.headersTimeout ?? client[kHeadersTimeout]
|
|
463
|
-
const bodyTimeout = request.bodyTimeout ?? client[kBodyTimeout]
|
|
464
|
-
const session = client[kHTTP2Session]
|
|
465
|
-
const { method, path, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request
|
|
466
|
-
let { body } = request
|
|
782
|
+
function onUpgradeStreamClose () {
|
|
783
|
+
this.off('error', noop)
|
|
467
784
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
785
|
+
const state = this[kRequestStreamState]
|
|
786
|
+
this[kRequestStreamState] = null
|
|
787
|
+
|
|
788
|
+
failUpgradeStream(state, new InformationalError('HTTP/2: stream closed before response headers'))
|
|
789
|
+
closeStreamSession(this)
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Idempotent terminal cleanup, called from both 'end' and 'close': the
|
|
793
|
+
// null-state guard no-ops the later call.
|
|
794
|
+
function completeRequestStream () {
|
|
795
|
+
const state = this[kRequestStreamState]
|
|
796
|
+
|
|
797
|
+
if (state == null) {
|
|
798
|
+
return
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Release the stream first so request references are cleared,
|
|
802
|
+
// then complete the response with trailers if available.
|
|
803
|
+
releaseRequestStream(this)
|
|
804
|
+
|
|
805
|
+
if (state.pendingEnd && !state.request.aborted && !state.request.completed) {
|
|
806
|
+
state.request.onResponseEnd(state.trailers || {})
|
|
807
|
+
} else if (!state.request.aborted && !state.request.completed) {
|
|
808
|
+
// The stream closed without a complete response and without reporting an
|
|
809
|
+
// error. finalizeRequest() below frees the queue slot either way, so
|
|
810
|
+
// without this the request would simply vanish and its caller would never
|
|
811
|
+
// hear back.
|
|
812
|
+
util.errorRequest(
|
|
813
|
+
state.client,
|
|
814
|
+
state.request,
|
|
815
|
+
new InformationalError('HTTP/2: stream closed before the response was complete')
|
|
816
|
+
)
|
|
471
817
|
}
|
|
472
818
|
|
|
819
|
+
finalizeRequest(state)
|
|
820
|
+
closeStreamSession(this)
|
|
821
|
+
this[kRequestStreamState] = null
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2
|
|
825
|
+
function shouldSendContentLength (method) {
|
|
826
|
+
return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS' && method !== 'TRACE' && method !== 'CONNECT'
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function buildRequestHeaders (reqHeaders) {
|
|
473
830
|
const headers = {}
|
|
831
|
+
|
|
474
832
|
for (let n = 0; n < reqHeaders.length; n += 2) {
|
|
475
833
|
const key = reqHeaders[n + 0]
|
|
476
834
|
const val = reqHeaders[n + 1]
|
|
835
|
+
const current = headers[key]
|
|
477
836
|
|
|
478
837
|
if (key === 'cookie') {
|
|
479
|
-
if (
|
|
480
|
-
headers[key] = Array.isArray(
|
|
838
|
+
if (current != null) {
|
|
839
|
+
headers[key] = Array.isArray(current) ? (current.push(val), current) : [current, val]
|
|
481
840
|
} else {
|
|
482
841
|
headers[key] = val
|
|
483
842
|
}
|
|
@@ -485,30 +844,204 @@ function writeH2 (client, request) {
|
|
|
485
844
|
continue
|
|
486
845
|
}
|
|
487
846
|
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
} else if (headers[key]) {
|
|
497
|
-
headers[key] += `, ${val}`
|
|
498
|
-
} else {
|
|
499
|
-
headers[key] = val
|
|
847
|
+
if (typeof val === 'string') {
|
|
848
|
+
headers[key] = current ? `${current}, ${val}` : val
|
|
849
|
+
continue
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
for (let i = 0; i < val.length; i++) {
|
|
853
|
+
headers[key] = headers[key] ? `${headers[key]}, ${val[i]}` : val[i]
|
|
500
854
|
}
|
|
501
855
|
}
|
|
502
856
|
|
|
503
|
-
|
|
504
|
-
|
|
857
|
+
return headers
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function removeUpgradeStreamListeners (stream) {
|
|
861
|
+
stream.off('response', onUpgradeResponse)
|
|
862
|
+
stream.off('error', onUpgradeStreamError)
|
|
863
|
+
stream.off('end', onUpgradeStreamEnd)
|
|
864
|
+
stream.off('timeout', onUpgradeStreamTimeout)
|
|
865
|
+
stream.off('error', noop)
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function releaseUpgradeStream (stream) {
|
|
869
|
+
if (stream == null) {
|
|
870
|
+
return
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const state = stream[kRequestStreamState]
|
|
874
|
+
if (state == null) {
|
|
875
|
+
return
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const { request } = state
|
|
879
|
+
|
|
880
|
+
if (request[kRequestStream] === stream) {
|
|
881
|
+
detachRequestFromStream(request)
|
|
882
|
+
}
|
|
505
883
|
|
|
506
|
-
|
|
884
|
+
removeUpgradeStreamListeners(stream)
|
|
507
885
|
|
|
508
|
-
|
|
886
|
+
if (!stream.destroyed && !stream.closed) {
|
|
887
|
+
stream.once('error', noop)
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function failUpgradeStream (state, err) {
|
|
892
|
+
if (state == null) {
|
|
893
|
+
return
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
const { request } = state
|
|
897
|
+
if (state.responseReceived || request.aborted || request.completed) {
|
|
898
|
+
return
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
releaseUpgradeStream(state.stream)
|
|
902
|
+
state.abort(err, true)
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function onUpgradeStreamError () {
|
|
906
|
+
const state = this[kRequestStreamState]
|
|
907
|
+
|
|
908
|
+
if (typeof this.rstCode === 'number' && this.rstCode !== 0) {
|
|
909
|
+
failUpgradeStream(state, new InformationalError(`HTTP/2: "stream error" received - code ${this.rstCode}`))
|
|
910
|
+
} else {
|
|
911
|
+
failUpgradeStream(state, new InformationalError('HTTP/2: stream errored before response headers'))
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function onUpgradeStreamEnd () {
|
|
916
|
+
failUpgradeStream(this[kRequestStreamState], new InformationalError('HTTP/2: stream half-closed (remote)'))
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function onUpgradeStreamTimeout () {
|
|
920
|
+
const state = this[kRequestStreamState]
|
|
921
|
+
failUpgradeStream(state, new InformationalError(`HTTP/2: "stream timeout after ${state.headersTimeout}"`))
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function onUpgradeResponse (headers, _flags) {
|
|
925
|
+
const stream = this
|
|
926
|
+
const state = stream[kRequestStreamState]
|
|
927
|
+
const { request } = state
|
|
928
|
+
|
|
929
|
+
state.responseReceived = true
|
|
930
|
+
|
|
931
|
+
const statusCode = headers[HTTP2_HEADER_STATUS]
|
|
932
|
+
delete headers[HTTP2_HEADER_STATUS]
|
|
933
|
+
|
|
934
|
+
request.onRequestUpgrade(statusCode, headers, stream)
|
|
935
|
+
|
|
936
|
+
if (request.aborted || request.completed) {
|
|
937
|
+
return
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
removeUpgradeStreamListeners(stream)
|
|
941
|
+
detachRequestFromStream(request)
|
|
942
|
+
finalizeRequest(state)
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function setupUpgradeStream (stream, state) {
|
|
946
|
+
const { request, headersTimeout, session } = state
|
|
947
|
+
|
|
948
|
+
stream[kHTTP2Stream] = true
|
|
949
|
+
stream[kHTTP2Session] = session
|
|
950
|
+
stream[kRequestStreamState] = state
|
|
951
|
+
state.stream = stream
|
|
952
|
+
|
|
953
|
+
bindRequestToStream(request, stream, releaseUpgradeStream)
|
|
954
|
+
stream.once('response', onUpgradeResponse)
|
|
955
|
+
stream.on('error', onUpgradeStreamError)
|
|
956
|
+
stream.once('end', onUpgradeStreamEnd)
|
|
957
|
+
stream.on('timeout', onUpgradeStreamTimeout)
|
|
958
|
+
stream.once('close', onUpgradeStreamClose)
|
|
959
|
+
|
|
960
|
+
clearHttp2IdleTimeout(session)
|
|
961
|
+
++session[kOpenStreams]
|
|
962
|
+
stream.setTimeout(headersTimeout)
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function finalizeRequest (state, resetPendingIdx = false) {
|
|
966
|
+
if (state.requestFinalized) {
|
|
967
|
+
return
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
state.requestFinalized = true
|
|
971
|
+
completeRequest(state.client, state.request, resetPendingIdx)
|
|
972
|
+
|
|
973
|
+
state.client[kResume]()
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function openStream (client, request, session, abort, headers, options) {
|
|
977
|
+
try {
|
|
978
|
+
return session.request(headers, options)
|
|
979
|
+
} catch (err) {
|
|
980
|
+
// A GOAWAY'd session rejects new streams, same as an invalid session:
|
|
981
|
+
// reset and requeue on a fresh connection rather than the destroy + abort
|
|
982
|
+
// below, whose destroy(socket, err) can crash via an unhandled 'error'.
|
|
983
|
+
if (err?.code === 'ERR_HTTP2_INVALID_SESSION' || err?.code === 'ERR_HTTP2_GOAWAY_SESSION') {
|
|
984
|
+
const wrappedErr = new SocketError(err.message, util.getSocketInfo(session[kSocket]))
|
|
985
|
+
wrappedErr.cause = err
|
|
986
|
+
session[kError] = wrappedErr
|
|
987
|
+
resetHttp2Session(session, wrappedErr)
|
|
988
|
+
requeueUnsentRequest(client, request)
|
|
989
|
+
|
|
990
|
+
return null
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
const wrappedErr = new InformationalError(err.message, { cause: err })
|
|
994
|
+
session[kError] = wrappedErr
|
|
995
|
+
session[kSocket][kError] = wrappedErr
|
|
996
|
+
|
|
997
|
+
session.destroy(wrappedErr)
|
|
998
|
+
util.destroy(session[kSocket], wrappedErr)
|
|
999
|
+
abort(wrappedErr)
|
|
1000
|
+
|
|
1001
|
+
return null
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function writeH2 (client, request) {
|
|
1006
|
+
const headersTimeout = request.headersTimeout ?? client[kHeadersTimeout]
|
|
1007
|
+
const bodyTimeout = request.bodyTimeout ?? client[kBodyTimeout]
|
|
1008
|
+
const session = client[kHTTP2Session]
|
|
1009
|
+
const { method, path, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request
|
|
1010
|
+
|
|
1011
|
+
if (upgrade != null && upgrade !== 'websocket') {
|
|
1012
|
+
util.errorRequest(client, request, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`))
|
|
1013
|
+
return false
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const headers = buildRequestHeaders(reqHeaders)
|
|
1017
|
+
|
|
1018
|
+
headers[HTTP2_HEADER_AUTHORITY] = host || client[kHostAuthority]
|
|
509
1019
|
headers[HTTP2_HEADER_METHOD] = method
|
|
510
1020
|
|
|
511
|
-
|
|
1021
|
+
// Single pre-shaped state object shared by all stream event handlers.
|
|
1022
|
+
// All fields are declared up-front so the object keeps a stable hidden
|
|
1023
|
+
// class for the whole request lifetime.
|
|
1024
|
+
const state = {
|
|
1025
|
+
abort: null,
|
|
1026
|
+
body: request.body,
|
|
1027
|
+
bytesRead: 0,
|
|
1028
|
+
client,
|
|
1029
|
+
contentLength: null,
|
|
1030
|
+
expectsPayload: false,
|
|
1031
|
+
maxResponseSize: client[kMaxResponseSize],
|
|
1032
|
+
request,
|
|
1033
|
+
headersTimeout,
|
|
1034
|
+
bodyTimeout,
|
|
1035
|
+
requestFinalized: false,
|
|
1036
|
+
responseReceived: false,
|
|
1037
|
+
bodySent: false,
|
|
1038
|
+
pendingEnd: false,
|
|
1039
|
+
trailers: null,
|
|
1040
|
+
session,
|
|
1041
|
+
stream: null
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const abort = (err, resetPendingIdx = false) => {
|
|
512
1045
|
if (request.aborted || request.completed) {
|
|
513
1046
|
return
|
|
514
1047
|
}
|
|
@@ -517,29 +1050,44 @@ function writeH2 (client, request) {
|
|
|
517
1050
|
|
|
518
1051
|
util.errorRequest(client, request, err)
|
|
519
1052
|
|
|
520
|
-
if (stream != null) {
|
|
521
|
-
|
|
522
|
-
// let's ignore them
|
|
523
|
-
stream.removeAllListeners('data')
|
|
1053
|
+
if (state.stream != null) {
|
|
1054
|
+
clearRequestStream(request)
|
|
524
1055
|
|
|
525
|
-
// On Abort, we close the stream to send RST_STREAM frame
|
|
1056
|
+
// On Abort, we close the stream to send RST_STREAM frame.
|
|
1057
|
+
const stream = state.stream
|
|
526
1058
|
stream.close()
|
|
527
1059
|
|
|
1060
|
+
// close() alone leaves cleanup waiting on the 'close' event; on a busy,
|
|
1061
|
+
// long-lived multiplexed session that event can fail to fire, leaving the
|
|
1062
|
+
// native Http2Stream (and the whole request graph it pins) alive for the
|
|
1063
|
+
// session's life. Destroy the stream synchronously to release the handle
|
|
1064
|
+
// deterministically. Deferring the destroy (e.g. via setImmediate) leaks
|
|
1065
|
+
// the same way when the event loop is stalled and the callback never runs
|
|
1066
|
+
// under abort churn (#5558); close() has already queued the RST_STREAM
|
|
1067
|
+
// frame on the native session, so a synchronous destroy still sends it.
|
|
1068
|
+
if (!stream.destroyed) {
|
|
1069
|
+
util.destroy(stream)
|
|
1070
|
+
}
|
|
1071
|
+
|
|
528
1072
|
// We move the running index to the next request
|
|
529
1073
|
client[kOnError](err)
|
|
530
|
-
|
|
531
|
-
client[kResume]()
|
|
1074
|
+
finalizeRequest(state, resetPendingIdx)
|
|
532
1075
|
}
|
|
533
1076
|
|
|
534
1077
|
// We do not destroy the socket as we can continue using the session
|
|
535
1078
|
// the stream gets destroyed and the session remains to create new streams
|
|
536
|
-
util.destroy(body, err)
|
|
1079
|
+
util.destroy(state.body, err)
|
|
537
1080
|
}
|
|
538
1081
|
|
|
1082
|
+
state.abort = abort
|
|
1083
|
+
|
|
1084
|
+
/** @type {import('node:http2').ClientHttp2Stream} */
|
|
1085
|
+
let stream = null
|
|
1086
|
+
|
|
539
1087
|
try {
|
|
540
1088
|
// We are already connected, streams are pending.
|
|
541
1089
|
// We can call on connect, and wait for abort
|
|
542
|
-
request.
|
|
1090
|
+
request.onRequestStart(abort, null)
|
|
543
1091
|
} catch (err) {
|
|
544
1092
|
util.errorRequest(client, request, err)
|
|
545
1093
|
}
|
|
@@ -549,13 +1097,13 @@ function writeH2 (client, request) {
|
|
|
549
1097
|
}
|
|
550
1098
|
|
|
551
1099
|
if (upgrade || method === 'CONNECT') {
|
|
552
|
-
session
|
|
1100
|
+
refH2Session(session)
|
|
553
1101
|
|
|
554
1102
|
if (upgrade === 'websocket') {
|
|
555
1103
|
// We cannot upgrade to websocket if extended CONNECT protocol is not supported
|
|
556
1104
|
if (session[kEnableConnectProtocol] === false) {
|
|
557
1105
|
util.errorRequest(client, request, new InformationalError('HTTP/2: Extended CONNECT protocol not supported by server'))
|
|
558
|
-
session
|
|
1106
|
+
unrefH2Session(session)
|
|
559
1107
|
return false
|
|
560
1108
|
}
|
|
561
1109
|
|
|
@@ -573,33 +1121,12 @@ function writeH2 (client, request) {
|
|
|
573
1121
|
headers[HTTP2_HEADER_SCHEME] = protocol === 'http:' ? 'http' : 'https'
|
|
574
1122
|
}
|
|
575
1123
|
|
|
576
|
-
stream = session
|
|
577
|
-
stream
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
request.onUpgrade(statusCode, parseH2Headers(realHeaders), stream)
|
|
583
|
-
|
|
584
|
-
++session[kOpenStreams]
|
|
585
|
-
completeRequest(client, request)
|
|
586
|
-
})
|
|
587
|
-
|
|
588
|
-
stream.on('error', () => {
|
|
589
|
-
if (stream.rstCode === NGHTTP2_REFUSED_STREAM || stream.rstCode === NGHTTP2_CANCEL) {
|
|
590
|
-
// NGHTTP2_REFUSED_STREAM (7) or NGHTTP2_CANCEL (8)
|
|
591
|
-
// We do not treat those as errors as the server might
|
|
592
|
-
// not support websockets and refuse the stream
|
|
593
|
-
abort(new InformationalError(`HTTP/2: "stream error" received - code ${stream.rstCode}`))
|
|
594
|
-
}
|
|
595
|
-
})
|
|
596
|
-
|
|
597
|
-
stream.once('close', () => {
|
|
598
|
-
session[kOpenStreams] -= 1
|
|
599
|
-
if (session[kOpenStreams] === 0) session.unref()
|
|
600
|
-
})
|
|
601
|
-
|
|
602
|
-
stream.setTimeout(headersTimeout)
|
|
1124
|
+
stream = openStream(client, request, session, abort, headers, { endStream: false, signal })
|
|
1125
|
+
if (stream == null) {
|
|
1126
|
+
unrefH2Session(session)
|
|
1127
|
+
return false
|
|
1128
|
+
}
|
|
1129
|
+
setupUpgradeStream(stream, state)
|
|
603
1130
|
return true
|
|
604
1131
|
}
|
|
605
1132
|
|
|
@@ -608,21 +1135,12 @@ function writeH2 (client, request) {
|
|
|
608
1135
|
// will create a new stream. We trigger a request to create the stream and wait until
|
|
609
1136
|
// `ready` event is triggered
|
|
610
1137
|
// We disabled endStream to allow the user to write to the stream
|
|
611
|
-
stream = session
|
|
612
|
-
stream
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
++session[kOpenStreams]
|
|
618
|
-
completeRequest(client, request)
|
|
619
|
-
})
|
|
620
|
-
stream.on('error', abort)
|
|
621
|
-
stream.once('close', () => {
|
|
622
|
-
session[kOpenStreams] -= 1
|
|
623
|
-
if (session[kOpenStreams] === 0) session.unref()
|
|
624
|
-
})
|
|
625
|
-
stream.setTimeout(headersTimeout)
|
|
1138
|
+
stream = openStream(client, request, session, abort, headers, { endStream: false, signal })
|
|
1139
|
+
if (stream == null) {
|
|
1140
|
+
unrefH2Session(session)
|
|
1141
|
+
return false
|
|
1142
|
+
}
|
|
1143
|
+
setupUpgradeStream(stream, state)
|
|
626
1144
|
|
|
627
1145
|
return true
|
|
628
1146
|
}
|
|
@@ -644,9 +1162,14 @@ function writeH2 (client, request) {
|
|
|
644
1162
|
const expectsPayload = (
|
|
645
1163
|
method === 'PUT' ||
|
|
646
1164
|
method === 'POST' ||
|
|
647
|
-
method === 'PATCH'
|
|
1165
|
+
method === 'PATCH' ||
|
|
1166
|
+
method === 'QUERY' ||
|
|
1167
|
+
method === 'PROPFIND' ||
|
|
1168
|
+
method === 'PROPPATCH'
|
|
648
1169
|
)
|
|
649
1170
|
|
|
1171
|
+
let body = state.body
|
|
1172
|
+
|
|
650
1173
|
if (body && typeof body.read === 'function') {
|
|
651
1174
|
// Try to read EOF in order to get length.
|
|
652
1175
|
body.read(0)
|
|
@@ -668,7 +1191,7 @@ function writeH2 (client, request) {
|
|
|
668
1191
|
contentLength = request.contentLength
|
|
669
1192
|
}
|
|
670
1193
|
|
|
671
|
-
if (!expectsPayload) {
|
|
1194
|
+
if (contentLength === 0 && !expectsPayload) {
|
|
672
1195
|
// https://tools.ietf.org/html/rfc7230#section-3.3.2
|
|
673
1196
|
// A user agent SHOULD NOT send a Content-Length header field when
|
|
674
1197
|
// the request message does not contain a payload body and the method
|
|
@@ -693,7 +1216,7 @@ function writeH2 (client, request) {
|
|
|
693
1216
|
headers[HTTP2_HEADER_CONTENT_LENGTH] = `${contentLength}`
|
|
694
1217
|
}
|
|
695
1218
|
|
|
696
|
-
session
|
|
1219
|
+
refH2Session(session)
|
|
697
1220
|
|
|
698
1221
|
if (channels.sendHeaders.hasSubscribers) {
|
|
699
1222
|
let header = ''
|
|
@@ -704,195 +1227,382 @@ function writeH2 (client, request) {
|
|
|
704
1227
|
}
|
|
705
1228
|
|
|
706
1229
|
// TODO(metcoder95): add support for sending trailers
|
|
707
|
-
const shouldEndStream =
|
|
1230
|
+
const shouldEndStream = body === null || contentLength === 0
|
|
1231
|
+
|
|
1232
|
+
state.body = body
|
|
1233
|
+
state.contentLength = contentLength
|
|
1234
|
+
state.expectsPayload = expectsPayload
|
|
1235
|
+
|
|
708
1236
|
if (expectContinue) {
|
|
709
1237
|
headers[HTTP2_HEADER_EXPECT] = '100-continue'
|
|
710
|
-
|
|
711
|
-
stream[kHTTP2Stream] = true
|
|
712
|
-
|
|
713
|
-
stream.once('continue', writeBodyH2)
|
|
714
|
-
} else {
|
|
715
|
-
stream = session.request(headers, {
|
|
716
|
-
endStream: shouldEndStream,
|
|
717
|
-
signal
|
|
718
|
-
})
|
|
719
|
-
stream[kHTTP2Stream] = true
|
|
1238
|
+
}
|
|
720
1239
|
|
|
721
|
-
|
|
1240
|
+
stream = openStream(client, request, session, abort, headers, { endStream: shouldEndStream, signal })
|
|
1241
|
+
if (stream == null) {
|
|
1242
|
+
return false
|
|
722
1243
|
}
|
|
1244
|
+
stream[kHTTP2Stream] = true
|
|
1245
|
+
stream[kRequestStreamState] = state
|
|
1246
|
+
state.stream = stream
|
|
723
1247
|
|
|
724
1248
|
// Increment counter as we have new streams open
|
|
1249
|
+
clearHttp2IdleTimeout(session)
|
|
725
1250
|
++session[kOpenStreams]
|
|
726
|
-
stream.setTimeout(headersTimeout)
|
|
727
1251
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
stream.once('response', headers => {
|
|
732
|
-
const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers
|
|
733
|
-
request.onResponseStarted()
|
|
734
|
-
responseReceived = true
|
|
735
|
-
stream.setTimeout(bodyTimeout)
|
|
736
|
-
|
|
737
|
-
// Due to the stream nature, it is possible we face a race condition
|
|
738
|
-
// where the stream has been assigned, but the request has been aborted
|
|
739
|
-
// the request remains in-flight and headers hasn't been received yet
|
|
740
|
-
// for those scenarios, best effort is to destroy the stream immediately
|
|
741
|
-
// as there's no value to keep it open.
|
|
742
|
-
if (request.aborted) {
|
|
743
|
-
stream.removeAllListeners('data')
|
|
744
|
-
return
|
|
745
|
-
}
|
|
1252
|
+
if (headersTimeout) {
|
|
1253
|
+
stream.setTimeout(headersTimeout)
|
|
1254
|
+
}
|
|
746
1255
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
1256
|
+
stream[kHTTP2Session] = session
|
|
1257
|
+
stream.on('close', completeRequestStream)
|
|
750
1258
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
1259
|
+
bindRequestToStream(request, stream, releaseRequestStream)
|
|
1260
|
+
if (expectContinue) {
|
|
1261
|
+
stream.once('continue', writeBodyH2)
|
|
1262
|
+
}
|
|
1263
|
+
// The handlers below either remove themselves on first invocation or
|
|
1264
|
+
// become unreachable once the stream closes, so plain `on` avoids the
|
|
1265
|
+
// per-listener `once` wrapper allocation.
|
|
1266
|
+
stream.on('response', onResponse)
|
|
1267
|
+
stream.on('headers', onInterimResponse)
|
|
1268
|
+
stream.on('end', onEnd)
|
|
1269
|
+
stream.on('error', onError)
|
|
1270
|
+
stream.on('frameError', onFrameError)
|
|
1271
|
+
stream.on('aborted', onAborted)
|
|
1272
|
+
if (headersTimeout || bodyTimeout) {
|
|
1273
|
+
stream.on('timeout', onTimeout)
|
|
1274
|
+
}
|
|
1275
|
+
stream.on('trailers', onTrailers)
|
|
755
1276
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
})
|
|
760
|
-
})
|
|
1277
|
+
if (!expectContinue) {
|
|
1278
|
+
writeBodyH2.call(stream)
|
|
1279
|
+
}
|
|
761
1280
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
// If we received a response, this is a normal completion
|
|
765
|
-
if (responseReceived) {
|
|
766
|
-
if (!request.aborted && !request.completed) {
|
|
767
|
-
request.onComplete({})
|
|
768
|
-
}
|
|
1281
|
+
return true
|
|
1282
|
+
}
|
|
769
1283
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1284
|
+
function removeRequestStreamListeners (stream) {
|
|
1285
|
+
stream.off('error', noop)
|
|
1286
|
+
stream.off('continue', writeBodyH2)
|
|
1287
|
+
stream.off('response', onResponse)
|
|
1288
|
+
stream.off('headers', onInterimResponse)
|
|
1289
|
+
stream.off('end', onEnd)
|
|
1290
|
+
stream.off('error', onError)
|
|
1291
|
+
stream.off('frameError', onFrameError)
|
|
1292
|
+
stream.off('aborted', onAborted)
|
|
1293
|
+
stream.off('timeout', onTimeout)
|
|
1294
|
+
stream.off('trailers', onTrailers)
|
|
1295
|
+
stream.off('data', onData)
|
|
1296
|
+
}
|
|
780
1297
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
session.unref()
|
|
786
|
-
}
|
|
1298
|
+
function releaseRequestStream (stream) {
|
|
1299
|
+
if (stream == null) {
|
|
1300
|
+
return
|
|
1301
|
+
}
|
|
787
1302
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if (!request.aborted && !request.completed) {
|
|
793
|
-
abort(new InformationalError('HTTP/2: stream closed before the response was complete'))
|
|
794
|
-
}
|
|
795
|
-
})
|
|
1303
|
+
const state = stream[kRequestStreamState]
|
|
1304
|
+
if (state == null) {
|
|
1305
|
+
return
|
|
1306
|
+
}
|
|
796
1307
|
|
|
797
|
-
|
|
798
|
-
stream.removeAllListeners('data')
|
|
799
|
-
abort(err)
|
|
800
|
-
})
|
|
1308
|
+
const { request } = state
|
|
801
1309
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
})
|
|
1310
|
+
if (request[kRequestStream] === stream) {
|
|
1311
|
+
detachRequestFromStream(request)
|
|
1312
|
+
}
|
|
806
1313
|
|
|
807
|
-
stream
|
|
808
|
-
|
|
809
|
-
|
|
1314
|
+
// A closed or destroyed stream cannot emit further events; leaving the
|
|
1315
|
+
// listeners in place saves the removal scans (they are collected with
|
|
1316
|
+
// the stream). All handlers bail out when the stream state is gone.
|
|
1317
|
+
if (!stream.destroyed && !stream.closed) {
|
|
1318
|
+
removeRequestStreamListeners(stream)
|
|
1319
|
+
stream.once('error', noop)
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
810
1322
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
: new HeadersTimeoutError(`HTTP/2: "headers timeout after ${headersTimeout}"`)
|
|
815
|
-
stream.removeAllListeners('data')
|
|
816
|
-
session[kOpenStreams] -= 1
|
|
1323
|
+
function onData (chunk) {
|
|
1324
|
+
const stream = this
|
|
1325
|
+
const state = stream[kRequestStreamState]
|
|
817
1326
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1327
|
+
if (state == null) {
|
|
1328
|
+
return
|
|
1329
|
+
}
|
|
821
1330
|
|
|
822
|
-
|
|
823
|
-
})
|
|
1331
|
+
const { request, maxResponseSize } = state
|
|
824
1332
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1333
|
+
if (request.aborted || request.completed) {
|
|
1334
|
+
return
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
if (maxResponseSize > -1 && state.bytesRead + chunk.length > maxResponseSize) {
|
|
1338
|
+
// Unlike HTTP/1.1, which destroys the socket because it cannot abandon one
|
|
1339
|
+
// response without losing framing, resetting the offending stream leaves
|
|
1340
|
+
// the session usable for its siblings.
|
|
1341
|
+
state.abort(new ResponseExceededMaxSizeError())
|
|
1342
|
+
return
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
state.bytesRead += chunk.length
|
|
1346
|
+
|
|
1347
|
+
if (request.onResponseData(chunk) === false) {
|
|
1348
|
+
stream.pause()
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
function onInterimResponse (headers) {
|
|
1353
|
+
const stream = this
|
|
1354
|
+
const state = stream[kRequestStreamState]
|
|
1355
|
+
|
|
1356
|
+
if (state == null) {
|
|
1357
|
+
return
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
const { request } = state
|
|
1361
|
+
|
|
1362
|
+
if (request.aborted || request.completed) {
|
|
1363
|
+
return
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// node http2 emits 'headers' for interim (1xx) informational responses,
|
|
1367
|
+
// while the final response arrives via 'response'. Forward these to the
|
|
1368
|
+
// handler so that onInfo is invoked, matching the HTTP/1 behaviour and the
|
|
1369
|
+
// documented onInfo contract.
|
|
1370
|
+
const statusCode = headers[HTTP2_HEADER_STATUS]
|
|
1371
|
+
delete headers[HTTP2_HEADER_STATUS]
|
|
1372
|
+
|
|
1373
|
+
request.onResponseStart(Number(statusCode), headers, noop, '')
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
function onResponse (headers) {
|
|
1377
|
+
const stream = this
|
|
1378
|
+
const state = stream[kRequestStreamState]
|
|
1379
|
+
|
|
1380
|
+
if (state == null) {
|
|
1381
|
+
return
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const { request } = state
|
|
1385
|
+
|
|
1386
|
+
stream.off('response', onResponse)
|
|
1387
|
+
|
|
1388
|
+
// Final response received while still awaiting 100 (Continue): the body won't
|
|
1389
|
+
// be sent, so close our half or the stream stays open and never completes.
|
|
1390
|
+
if (state.body != null && !state.bodySent && !stream.writableEnded) {
|
|
1391
|
+
stream.removeListener('continue', writeBodyH2)
|
|
1392
|
+
stream.end()
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
const statusCode = headers[HTTP2_HEADER_STATUS]
|
|
1396
|
+
delete headers[HTTP2_HEADER_STATUS]
|
|
1397
|
+
request.onResponseStarted()
|
|
1398
|
+
state.responseReceived = true
|
|
1399
|
+
|
|
1400
|
+
if (state.headersTimeout || state.bodyTimeout) {
|
|
1401
|
+
stream.setTimeout(state.bodyTimeout)
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// Due to the stream nature, it is possible we face a race condition
|
|
1405
|
+
// where the stream has been assigned, but the request has been aborted
|
|
1406
|
+
// or already completed and headers hasn't been received yet. A late
|
|
1407
|
+
// 'response' delivered after completion would call request.onResponseStart
|
|
1408
|
+
// post-completion, tripping its `assert(!this.completed)` (an uncatchable
|
|
1409
|
+
// throw on the http2 event tick). Guard `completed` here as onEnd/onTrailers
|
|
1410
|
+
// already do; best effort is to release the stream immediately as there's
|
|
1411
|
+
// no value to keep it open.
|
|
1412
|
+
if (request.aborted || request.completed) {
|
|
1413
|
+
releaseRequestStream(stream)
|
|
1414
|
+
return
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
if (request.onResponseStart(Number(statusCode), headers, stream.resume.bind(stream), '') === false) {
|
|
1418
|
+
stream.pause()
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
stream.on('data', onData)
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function onEnd () {
|
|
1425
|
+
const stream = this
|
|
1426
|
+
const state = stream[kRequestStreamState]
|
|
1427
|
+
|
|
1428
|
+
if (state == null) {
|
|
1429
|
+
return
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
const { request } = state
|
|
1433
|
+
|
|
1434
|
+
stream.off('end', onEnd)
|
|
1435
|
+
|
|
1436
|
+
// onTrailers (which may fire after 'end' on Windows) has already stored
|
|
1437
|
+
// trailers on the state by now, so completing here still delivers them.
|
|
1438
|
+
if (state.responseReceived) {
|
|
1439
|
+
if (!request.aborted && !request.completed) {
|
|
1440
|
+
state.pendingEnd = true
|
|
1441
|
+
|
|
1442
|
+
// Complete on 'end': a blocked event loop can keep the stream's 'close'
|
|
1443
|
+
// from firing, stranding its buffers until OOM. Idempotent, so a later
|
|
1444
|
+
// 'close' no-ops.
|
|
1445
|
+
completeRequestStream.call(stream)
|
|
828
1446
|
}
|
|
1447
|
+
} else {
|
|
1448
|
+
// Stream ended without receiving a response - this is an error
|
|
1449
|
+
// (e.g., server destroyed the stream before sending headers)
|
|
1450
|
+
state.abort(new InformationalError('HTTP/2: stream half-closed (remote)'), true)
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
829
1453
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1454
|
+
function retryRefusedStream (stream, state) {
|
|
1455
|
+
const { client, request } = state
|
|
1456
|
+
|
|
1457
|
+
if (
|
|
1458
|
+
state.responseReceived ||
|
|
1459
|
+
request.aborted ||
|
|
1460
|
+
request.completed ||
|
|
1461
|
+
request[kRefusedStreamRetry] ||
|
|
1462
|
+
!canReplayRequest(request)
|
|
1463
|
+
) {
|
|
1464
|
+
return false
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// RFC 9113 section 8.7 permits retrying REFUSED_STREAM, but says clients
|
|
1468
|
+
// SHOULD NOT automatically retry the same request more than once.
|
|
1469
|
+
request[kRefusedStreamRetry] = true
|
|
1470
|
+
|
|
1471
|
+
// Detach the failed attempt before moving the request back to the pending
|
|
1472
|
+
// queue. The peer only reset this stream, so the HTTP/2 session remains
|
|
1473
|
+
// usable for the retry. Severing also drops the 'close' listener, so the
|
|
1474
|
+
// abandoned stream cannot later complete the retried request.
|
|
1475
|
+
detachRequestStreamForClose(request)
|
|
1476
|
+
state.stream = null
|
|
1477
|
+
state.requestFinalized = true
|
|
1478
|
+
|
|
1479
|
+
completeRequest(client, request)
|
|
1480
|
+
client[kQueue].splice(client[kPendingIdx], 0, request)
|
|
1481
|
+
client[kResume]()
|
|
833
1482
|
|
|
834
1483
|
return true
|
|
1484
|
+
}
|
|
835
1485
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1486
|
+
function onError (err) {
|
|
1487
|
+
const stream = this
|
|
1488
|
+
const state = stream[kRequestStreamState]
|
|
1489
|
+
|
|
1490
|
+
if (state == null) {
|
|
1491
|
+
return
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
stream.off('error', onError)
|
|
1495
|
+
|
|
1496
|
+
if (typeof stream.rstCode === 'number' && stream.rstCode !== NGHTTP2_NO_ERROR) {
|
|
1497
|
+
err.http2ErrorCode = stream.rstCode
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
if (
|
|
1501
|
+
stream.rstCode === NGHTTP2_REFUSED_STREAM &&
|
|
1502
|
+
retryRefusedStream(stream, state)
|
|
1503
|
+
) {
|
|
1504
|
+
return
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
state.abort(err)
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
function onFrameError (type, code) {
|
|
1511
|
+
const stream = this
|
|
1512
|
+
const state = stream[kRequestStreamState]
|
|
1513
|
+
|
|
1514
|
+
if (state == null) {
|
|
1515
|
+
return
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
stream.off('frameError', onFrameError)
|
|
1519
|
+
state.abort(new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`))
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
function onAborted () {
|
|
1523
|
+
this.off('data', onData)
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
function onTimeout () {
|
|
1527
|
+
const stream = this
|
|
1528
|
+
const state = stream[kRequestStreamState]
|
|
1529
|
+
|
|
1530
|
+
if (state == null) {
|
|
1531
|
+
return
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// Remove self so timeout doesn't fire again after we handle it
|
|
1535
|
+
stream.off('timeout', onTimeout)
|
|
1536
|
+
|
|
1537
|
+
const err = state.responseReceived
|
|
1538
|
+
? new BodyTimeoutError(`HTTP/2: "stream timeout after ${state.bodyTimeout}"`)
|
|
1539
|
+
: new HeadersTimeoutError(`HTTP/2: "headers timeout after ${state.headersTimeout}"`)
|
|
1540
|
+
state.abort(err)
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
function onTrailers (trailers) {
|
|
1544
|
+
const stream = this
|
|
1545
|
+
const state = stream[kRequestStreamState]
|
|
1546
|
+
|
|
1547
|
+
if (state == null) {
|
|
1548
|
+
return
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
const { request } = state
|
|
1552
|
+
|
|
1553
|
+
stream.off('trailers', onTrailers)
|
|
1554
|
+
stream.off('data', onData)
|
|
1555
|
+
|
|
1556
|
+
if (request.aborted || request.completed) {
|
|
1557
|
+
return
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// Store trailers for completeRequestStream to use when completing
|
|
1561
|
+
state.trailers = trailers
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function writeBodyH2 () {
|
|
1565
|
+
const stream = this
|
|
1566
|
+
const state = stream[kRequestStreamState]
|
|
1567
|
+
state.bodySent = true
|
|
1568
|
+
const { abort, body, client, contentLength, expectsPayload, request } = state
|
|
1569
|
+
|
|
1570
|
+
if (!body || contentLength === 0) {
|
|
1571
|
+
writeBuffer(
|
|
1572
|
+
abort,
|
|
1573
|
+
stream,
|
|
1574
|
+
null,
|
|
1575
|
+
client,
|
|
1576
|
+
request,
|
|
1577
|
+
client[kSocket],
|
|
1578
|
+
contentLength,
|
|
1579
|
+
expectsPayload
|
|
1580
|
+
)
|
|
1581
|
+
} else if (util.isBuffer(body)) {
|
|
1582
|
+
writeBuffer(
|
|
1583
|
+
abort,
|
|
1584
|
+
stream,
|
|
1585
|
+
body,
|
|
1586
|
+
client,
|
|
1587
|
+
request,
|
|
1588
|
+
client[kSocket],
|
|
1589
|
+
contentLength,
|
|
1590
|
+
expectsPayload
|
|
1591
|
+
)
|
|
1592
|
+
} else if (util.isBlobLike(body)) {
|
|
1593
|
+
if (typeof body.stream === 'function') {
|
|
1594
|
+
writeIterable(
|
|
850
1595
|
abort,
|
|
851
1596
|
stream,
|
|
852
|
-
body,
|
|
1597
|
+
body.stream(),
|
|
853
1598
|
client,
|
|
854
1599
|
request,
|
|
855
1600
|
client[kSocket],
|
|
856
1601
|
contentLength,
|
|
857
1602
|
expectsPayload
|
|
858
1603
|
)
|
|
859
|
-
} else
|
|
860
|
-
|
|
861
|
-
writeIterable(
|
|
862
|
-
abort,
|
|
863
|
-
stream,
|
|
864
|
-
body.stream(),
|
|
865
|
-
client,
|
|
866
|
-
request,
|
|
867
|
-
client[kSocket],
|
|
868
|
-
contentLength,
|
|
869
|
-
expectsPayload
|
|
870
|
-
)
|
|
871
|
-
} else {
|
|
872
|
-
writeBlob(
|
|
873
|
-
abort,
|
|
874
|
-
stream,
|
|
875
|
-
body,
|
|
876
|
-
client,
|
|
877
|
-
request,
|
|
878
|
-
client[kSocket],
|
|
879
|
-
contentLength,
|
|
880
|
-
expectsPayload
|
|
881
|
-
)
|
|
882
|
-
}
|
|
883
|
-
} else if (util.isStream(body)) {
|
|
884
|
-
writeStream(
|
|
885
|
-
abort,
|
|
886
|
-
client[kSocket],
|
|
887
|
-
expectsPayload,
|
|
888
|
-
stream,
|
|
889
|
-
body,
|
|
890
|
-
client,
|
|
891
|
-
request,
|
|
892
|
-
contentLength
|
|
893
|
-
)
|
|
894
|
-
} else if (util.isIterable(body)) {
|
|
895
|
-
writeIterable(
|
|
1604
|
+
} else {
|
|
1605
|
+
writeBlob(
|
|
896
1606
|
abort,
|
|
897
1607
|
stream,
|
|
898
1608
|
body,
|
|
@@ -902,9 +1612,31 @@ function writeH2 (client, request) {
|
|
|
902
1612
|
contentLength,
|
|
903
1613
|
expectsPayload
|
|
904
1614
|
)
|
|
905
|
-
} else {
|
|
906
|
-
assert(false)
|
|
907
1615
|
}
|
|
1616
|
+
} else if (util.isStream(body)) {
|
|
1617
|
+
writeStream(
|
|
1618
|
+
abort,
|
|
1619
|
+
client[kSocket],
|
|
1620
|
+
expectsPayload,
|
|
1621
|
+
stream,
|
|
1622
|
+
body,
|
|
1623
|
+
client,
|
|
1624
|
+
request,
|
|
1625
|
+
contentLength
|
|
1626
|
+
)
|
|
1627
|
+
} else if (util.isIterable(body)) {
|
|
1628
|
+
writeIterable(
|
|
1629
|
+
abort,
|
|
1630
|
+
stream,
|
|
1631
|
+
body,
|
|
1632
|
+
client,
|
|
1633
|
+
request,
|
|
1634
|
+
client[kSocket],
|
|
1635
|
+
contentLength,
|
|
1636
|
+
expectsPayload
|
|
1637
|
+
)
|
|
1638
|
+
} else {
|
|
1639
|
+
assert(false)
|
|
908
1640
|
}
|
|
909
1641
|
}
|
|
910
1642
|
|
|
@@ -963,8 +1695,6 @@ function writeStream (abort, socket, expectsPayload, h2stream, body, client, req
|
|
|
963
1695
|
}
|
|
964
1696
|
|
|
965
1697
|
async function writeBlob (abort, h2stream, body, client, request, socket, contentLength, expectsPayload) {
|
|
966
|
-
assert(contentLength === body.size, 'blob body must have content length')
|
|
967
|
-
|
|
968
1698
|
try {
|
|
969
1699
|
if (contentLength != null && contentLength !== body.size) {
|
|
970
1700
|
throw new RequestContentLengthMismatchError()
|