@openclaw/slack 2026.9.6 → 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-DP1Bcwj4.mjs → account-inspect-DWBXpER6.mjs} +1 -1
- package/dist/.setup/{accounts-BBHvg0pY.mjs → accounts-D_N9IqNA.mjs} +23 -1
- package/dist/.setup/{action-runtime-B01d85zt.mjs → action-runtime-DfWvuiZd.mjs} +26 -41
- package/dist/.setup/action-runtime.runtime-Ck4bAWOX.mjs +2 -0
- package/dist/.setup/{actions-DKCtBgCI.mjs → actions-Df0CzcDl.mjs} +88 -175
- package/dist/.setup/{approval-handler.runtime-DMszKn_7.mjs → approval-handler.runtime-Ed6Sw5VK.mjs} +37 -47
- package/dist/.setup/{approval-message-updates-Tb_qRokK.mjs → approval-message-updates-BadvtA7R.mjs} +1 -2
- package/dist/.setup/{channel-CErYZ5zw.mjs → channel-C7bgPSTE.mjs} +181 -273
- package/dist/.setup/channel-meta-C443t-lI.mjs +27 -0
- package/dist/.setup/{channel-type-Dxk8VJUb.mjs → channel-type-ClOttrZ5.mjs} +13 -21
- package/dist/.setup/{channel.setup-BVoUtdPn.mjs → channel.setup-Dh2jzAjN.mjs} +35 -42
- package/dist/.setup/{config-schema-CyL279e7.mjs → config-schema-BZCxmHKG.mjs} +24 -21
- package/dist/.setup/{directory-config-CQTSH45W.mjs → directory-config-BOUWH3hR.mjs} +7 -10
- package/dist/.setup/{directory-live-BCcj89QV.mjs → directory-live-B2O4B_Ho.mjs} +9 -21
- package/dist/.setup/doctor-contract-CRN1Z_c7.mjs +342 -0
- package/dist/.setup/{message-tool-api-C0PTFaNV.mjs → message-tool-api-Cu4lwlGy.mjs} +34 -48
- package/dist/.setup/{provider-C-jPaGt2.mjs → monitor-BPVfkumU.mjs} +2181 -2693
- package/dist/.setup/{outbound-adapter-A7Wdn8ZT.mjs → outbound-adapter-Bv48XUjq.mjs} +10 -21
- package/dist/.setup/{pipeline.runtime-BlPOexmJ.mjs → pipeline.runtime-B1H63Frf.mjs} +311 -457
- package/dist/.setup/{plugin-routes-C3X0eVaK.mjs → plugin-routes-B8tooDLj.mjs} +1 -1
- package/dist/.setup/{policy-BApW53pI.mjs → policy-D-jfM174.mjs} +5 -5
- package/dist/.setup/{probe-DiYIMPJo.mjs → probe-B2Y9qc_z.mjs} +101 -39
- package/dist/.setup/{registry-bbjH7IHX.mjs → registry-CIbMfuf8.mjs} +6 -2
- package/dist/.setup/{relay-source-DfOnjJTE.mjs → relay-source-BpKdjsll.mjs} +10 -12
- package/dist/.setup/{replies-BBAJxUOQ.mjs → replies-BAnUtr2f.mjs} +16 -76
- package/dist/.setup/{reply-blocks-Bm8kf6mK.mjs → reply-blocks-SE1kTE3I.mjs} +27 -154
- package/dist/.setup/{resolve-channels-BXbMBbJD.mjs → resolve-channels-XXBn8UU_.mjs} +4 -5
- package/dist/.setup/{resolve-users-65WqvIqD.mjs → resolve-users-DW0ngD03.mjs} +20 -22
- package/dist/.setup/{scopes-h3YB-mMs.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-Cp6omkOg.mjs → send-BOVLQi3n.mjs} +84 -171
- package/dist/.setup/{session-status-Cf7AvWOt.mjs → session-status-DscajIsd.mjs} +315 -238
- package/dist/.setup/{setup-plugin-api-dLPkL06L.mjs → setup-plugin-api-DTtbTKNk.mjs} +1 -1
- package/dist/.setup/{setup-surface-D3DLcCxu.mjs → setup-surface-Dn7QgWRE.mjs} +24 -56
- package/dist/.setup/{shared-CJ3arAFL.mjs → shared-qau-uDGl.mjs} +30 -56
- package/dist/.setup/{slash-dispatch.runtime-HFy_XvyO.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/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/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/openclaw.plugin.json +31 -3
- package/package.json +7 -7
- package/dist/.setup/accounts.runtime-DN-PPocC.mjs +0 -2
- package/dist/.setup/action-runtime.runtime-C8R8CUan.mjs +0 -2
- package/dist/.setup/doctor-contract-CtMUqjvk.mjs +0 -186
- package/dist/.setup/monitor-_Sht7Dqy.mjs +0 -13
- package/dist/.setup/rolldown-runtime-D7D4PA-g.mjs +0 -13
- package/dist/.setup/send.runtime-B4caZU6T.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
|
@@ -1,30 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const util = require('../core/util')
|
|
4
|
-
const { kBodyUsed } = require('../core/symbols')
|
|
5
4
|
const assert = require('node:assert')
|
|
6
5
|
const { InvalidArgumentError } = require('../core/errors')
|
|
7
|
-
const
|
|
6
|
+
const { kRequestOrigin } = require('../core/symbols')
|
|
8
7
|
|
|
9
8
|
const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
|
|
10
9
|
|
|
11
|
-
const kBody = Symbol('body')
|
|
12
|
-
|
|
13
10
|
const noop = () => {}
|
|
14
11
|
|
|
15
|
-
class BodyAsyncIterable {
|
|
16
|
-
constructor (body) {
|
|
17
|
-
this[kBody] = body
|
|
18
|
-
this[kBodyUsed] = false
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async * [Symbol.asyncIterator] () {
|
|
22
|
-
assert(!this[kBodyUsed], 'disturbed')
|
|
23
|
-
this[kBodyUsed] = true
|
|
24
|
-
yield * this[kBody]
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
12
|
class RedirectHandler {
|
|
29
13
|
static buildDispatch (dispatcher, maxRedirections) {
|
|
30
14
|
if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) {
|
|
@@ -40,53 +24,34 @@ class RedirectHandler {
|
|
|
40
24
|
throw new InvalidArgumentError('maxRedirections must be a positive number')
|
|
41
25
|
}
|
|
42
26
|
|
|
27
|
+
if (opts.throwOnMaxRedirect != null && typeof opts.throwOnMaxRedirect !== 'boolean') {
|
|
28
|
+
throw new InvalidArgumentError('throwOnMaxRedirect must be a boolean')
|
|
29
|
+
}
|
|
30
|
+
|
|
43
31
|
this.dispatch = dispatch
|
|
44
32
|
this.location = null
|
|
45
|
-
const { maxRedirections: _, ...cleanOpts } = opts
|
|
33
|
+
const { maxRedirections: _, stripHeadersOnRedirect, stripHeadersOnCrossOriginRedirect, ...cleanOpts } = opts
|
|
46
34
|
this.opts = cleanOpts // opts must be a copy, exclude maxRedirections
|
|
35
|
+
this.opts.body = util.wrapRequestBody(this.opts.body)
|
|
36
|
+
this.stripHeadersOnRedirect = normalizeStripHeaders(stripHeadersOnRedirect, 'stripHeadersOnRedirect')
|
|
37
|
+
this.stripHeadersOnCrossOriginRedirect = normalizeStripHeaders(stripHeadersOnCrossOriginRedirect, 'stripHeadersOnCrossOriginRedirect')
|
|
47
38
|
this.maxRedirections = maxRedirections
|
|
48
39
|
this.handler = handler
|
|
49
40
|
this.history = []
|
|
50
|
-
|
|
51
|
-
if (util.isStream(this.opts.body)) {
|
|
52
|
-
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
|
|
53
|
-
// so that it can be dispatched again?
|
|
54
|
-
// TODO (fix): Do we need 100-expect support to provide a way to do this properly?
|
|
55
|
-
if (util.bodyLength(this.opts.body) === 0) {
|
|
56
|
-
this.opts.body
|
|
57
|
-
.on('data', function () {
|
|
58
|
-
assert(false)
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (typeof this.opts.body.readableDidRead !== 'boolean') {
|
|
63
|
-
this.opts.body[kBodyUsed] = false
|
|
64
|
-
EE.prototype.on.call(this.opts.body, 'data', function () {
|
|
65
|
-
this[kBodyUsed] = true
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
} else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') {
|
|
69
|
-
// TODO (fix): We can't access ReadableStream internal state
|
|
70
|
-
// to determine whether or not it has been disturbed. This is just
|
|
71
|
-
// a workaround.
|
|
72
|
-
this.opts.body = new BodyAsyncIterable(this.opts.body)
|
|
73
|
-
} else if (
|
|
74
|
-
this.opts.body &&
|
|
75
|
-
typeof this.opts.body !== 'string' &&
|
|
76
|
-
!ArrayBuffer.isView(this.opts.body) &&
|
|
77
|
-
util.isIterable(this.opts.body) &&
|
|
78
|
-
!util.isFormDataLike(this.opts.body)
|
|
79
|
-
) {
|
|
80
|
-
// TODO: Should we allow re-using iterable if !this.opts.idempotent
|
|
81
|
-
// or through some other flag?
|
|
82
|
-
this.opts.body = new BodyAsyncIterable(this.opts.body)
|
|
83
|
-
}
|
|
84
41
|
}
|
|
85
42
|
|
|
86
43
|
onRequestStart (controller, context) {
|
|
87
44
|
this.handler.onRequestStart?.(controller, { ...context, history: this.history })
|
|
88
45
|
}
|
|
89
46
|
|
|
47
|
+
onBodySent (chunk) {
|
|
48
|
+
this.handler.onBodySent?.(chunk)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onRequestSent () {
|
|
52
|
+
this.handler.onRequestSent?.()
|
|
53
|
+
}
|
|
54
|
+
|
|
90
55
|
onRequestUpgrade (controller, statusCode, headers, socket) {
|
|
91
56
|
this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
|
|
92
57
|
}
|
|
@@ -96,15 +61,19 @@ class RedirectHandler {
|
|
|
96
61
|
throw new Error('max redirects')
|
|
97
62
|
}
|
|
98
63
|
|
|
64
|
+
let removeContentHeaders = statusCode === 303
|
|
65
|
+
|
|
99
66
|
// https://tools.ietf.org/html/rfc7231#section-6.4.2
|
|
100
67
|
// https://fetch.spec.whatwg.org/#http-redirect-fetch
|
|
101
68
|
// In case of HTTP 301 or 302 with POST, change the method to GET
|
|
69
|
+
// QUERY is safe (RFC 10008) and should not change method like GET.
|
|
102
70
|
if ((statusCode === 301 || statusCode === 302) && this.opts.method === 'POST') {
|
|
103
71
|
this.opts.method = 'GET'
|
|
104
72
|
if (util.isStream(this.opts.body)) {
|
|
105
73
|
util.destroy(this.opts.body.on('error', noop))
|
|
106
74
|
}
|
|
107
75
|
this.opts.body = null
|
|
76
|
+
removeContentHeaders = true
|
|
108
77
|
}
|
|
109
78
|
|
|
110
79
|
// https://tools.ietf.org/html/rfc7231#section-6.4.4
|
|
@@ -121,8 +90,12 @@ class RedirectHandler {
|
|
|
121
90
|
? null
|
|
122
91
|
: headers.location
|
|
123
92
|
|
|
124
|
-
|
|
125
|
-
|
|
93
|
+
const requestOrigin = this.opts[kRequestOrigin] === undefined
|
|
94
|
+
? this.opts.origin
|
|
95
|
+
: this.opts[kRequestOrigin]
|
|
96
|
+
|
|
97
|
+
if (requestOrigin) {
|
|
98
|
+
this.history.push(new URL(this.opts.path, requestOrigin))
|
|
126
99
|
}
|
|
127
100
|
|
|
128
101
|
if (!this.location) {
|
|
@@ -130,7 +103,10 @@ class RedirectHandler {
|
|
|
130
103
|
return
|
|
131
104
|
}
|
|
132
105
|
|
|
133
|
-
const
|
|
106
|
+
const baseUrl = requestOrigin
|
|
107
|
+
? new URL(this.opts.path, requestOrigin)
|
|
108
|
+
: undefined
|
|
109
|
+
const { origin, pathname, search } = util.parseURL(new URL(this.location, baseUrl))
|
|
134
110
|
const path = search ? `${pathname}${search}` : pathname
|
|
135
111
|
|
|
136
112
|
// Check for redirect loops by seeing if we've already visited this URL in our history
|
|
@@ -144,11 +120,12 @@ class RedirectHandler {
|
|
|
144
120
|
}
|
|
145
121
|
|
|
146
122
|
// Remove headers referring to the original URL.
|
|
147
|
-
// By default it is Host only
|
|
123
|
+
// By default it is Host only. A 303 or a 301/302 POST-to-GET redirect also removes all Content-* headers.
|
|
148
124
|
// https://tools.ietf.org/html/rfc7231#section-6.4
|
|
149
|
-
this.opts.headers = cleanRequestHeaders(this.opts.headers,
|
|
125
|
+
this.opts.headers = cleanRequestHeaders(this.opts.headers, removeContentHeaders, requestOrigin !== origin, this.stripHeadersOnRedirect, this.stripHeadersOnCrossOriginRedirect)
|
|
150
126
|
this.opts.path = path
|
|
151
127
|
this.opts.origin = origin
|
|
128
|
+
this.opts[kRequestOrigin] = origin
|
|
152
129
|
this.opts.query = null
|
|
153
130
|
}
|
|
154
131
|
|
|
@@ -198,26 +175,49 @@ class RedirectHandler {
|
|
|
198
175
|
}
|
|
199
176
|
|
|
200
177
|
// https://tools.ietf.org/html/rfc7231#section-6.4.4
|
|
201
|
-
function shouldRemoveHeader (header, removeContent, unknownOrigin) {
|
|
202
|
-
|
|
203
|
-
|
|
178
|
+
function shouldRemoveHeader (header, removeContent, unknownOrigin, stripHeaders, stripHeadersOnCrossOrigin) {
|
|
179
|
+
const name = util.headerNameToString(header)
|
|
180
|
+
if (name === 'host') {
|
|
181
|
+
return true
|
|
204
182
|
}
|
|
205
|
-
if (
|
|
183
|
+
if (stripHeaders?.has(name) || (unknownOrigin && stripHeadersOnCrossOrigin?.has(name))) {
|
|
206
184
|
return true
|
|
207
185
|
}
|
|
208
|
-
if (
|
|
209
|
-
|
|
186
|
+
if (removeContent && name.startsWith('content-')) {
|
|
187
|
+
return true
|
|
188
|
+
}
|
|
189
|
+
if (unknownOrigin) {
|
|
210
190
|
return name === 'authorization' || name === 'cookie' || name === 'proxy-authorization'
|
|
211
191
|
}
|
|
212
192
|
return false
|
|
213
193
|
}
|
|
214
194
|
|
|
215
195
|
// https://tools.ietf.org/html/rfc7231#section-6.4
|
|
216
|
-
function
|
|
196
|
+
function normalizeStripHeaders (headers, optionName) {
|
|
197
|
+
if (headers == null) {
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!Array.isArray(headers)) {
|
|
202
|
+
throw new InvalidArgumentError(`${optionName} must be an array`)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const normalized = new Set()
|
|
206
|
+
for (const header of headers) {
|
|
207
|
+
if (typeof header !== 'string') {
|
|
208
|
+
throw new InvalidArgumentError(`${optionName} must contain header names`)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
normalized.add(util.headerNameToString(header))
|
|
212
|
+
}
|
|
213
|
+
return normalized
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function cleanRequestHeaders (headers, removeContent, unknownOrigin, stripHeaders, stripHeadersOnCrossOrigin) {
|
|
217
217
|
const ret = []
|
|
218
218
|
if (Array.isArray(headers)) {
|
|
219
219
|
for (let i = 0; i < headers.length; i += 2) {
|
|
220
|
-
if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin)) {
|
|
220
|
+
if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin, stripHeaders, stripHeadersOnCrossOrigin)) {
|
|
221
221
|
ret.push(headers[i], headers[i + 1])
|
|
222
222
|
}
|
|
223
223
|
}
|
|
@@ -225,7 +225,7 @@ function cleanRequestHeaders (headers, removeContent, unknownOrigin) {
|
|
|
225
225
|
const entries = util.hasSafeIterator(headers) ? headers : Object.entries(headers)
|
|
226
226
|
|
|
227
227
|
for (const [key, value] of entries) {
|
|
228
|
-
if (!shouldRemoveHeader(key, removeContent, unknownOrigin)) {
|
|
228
|
+
if (!shouldRemoveHeader(key, removeContent, unknownOrigin, stripHeaders, stripHeadersOnCrossOrigin)) {
|
|
229
229
|
ret.push(key, value)
|
|
230
230
|
}
|
|
231
231
|
}
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
const assert = require('node:assert')
|
|
3
3
|
|
|
4
4
|
const { kRetryHandlerDefaultRetry } = require('../core/symbols')
|
|
5
|
-
const { RequestRetryError } = require('../core/errors')
|
|
6
|
-
const WrapHandler = require('./wrap-handler')
|
|
5
|
+
const { RequestRetryError, RequestAbortedError } = require('../core/errors')
|
|
7
6
|
const {
|
|
8
7
|
isDisturbed,
|
|
9
8
|
parseRangeHeader,
|
|
@@ -12,7 +11,7 @@ const {
|
|
|
12
11
|
|
|
13
12
|
function calculateRetryAfterHeader (retryAfter) {
|
|
14
13
|
const retryTime = new Date(retryAfter).getTime()
|
|
15
|
-
return isNaN(retryTime) ?
|
|
14
|
+
return isNaN(retryTime) ? null : retryTime - Date.now()
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
function validatePartialResponseContentLength (headers, range, statusCode, retryCount) {
|
|
@@ -35,6 +34,51 @@ function validatePartialResponseContentLength (headers, range, statusCode, retry
|
|
|
35
34
|
}
|
|
36
35
|
}
|
|
37
36
|
|
|
37
|
+
// A stable controller handed to the downstream handler for the lifetime of the
|
|
38
|
+
// request. Each transparent retry/resume is a *separate* dispatch with its
|
|
39
|
+
// *own* connection controller. Without a stable proxy the downstream body keeps
|
|
40
|
+
// flow-controlling the original (now-dead) controller while data flows on the
|
|
41
|
+
// new one: backpressure pauses the new connection's controller, but the
|
|
42
|
+
// consumer's resume() targets the old one, so the resumed body stalls forever.
|
|
43
|
+
// The proxy always forwards to the controller of the currently active connection.
|
|
44
|
+
// An abort is additionally reported to the handler so it can cancel a pending
|
|
45
|
+
// retry backoff instead of letting the request hang until the backoff elapses.
|
|
46
|
+
// The notification is a private callback the handler hands over on construction,
|
|
47
|
+
// so nothing outside the handler can trigger it.
|
|
48
|
+
class RetryController {
|
|
49
|
+
#onAbort
|
|
50
|
+
|
|
51
|
+
constructor (onAbort) {
|
|
52
|
+
this.#onAbort = onAbort
|
|
53
|
+
this.target = null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pause () { this.target?.pause() }
|
|
57
|
+
resume () { this.target?.resume() }
|
|
58
|
+
|
|
59
|
+
abort (reason) {
|
|
60
|
+
this.target?.abort(reason)
|
|
61
|
+
this.#onAbort(reason)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get paused () { return this.target?.paused ?? false }
|
|
65
|
+
get aborted () { return this.target?.aborted ?? false }
|
|
66
|
+
get reason () { return this.target?.reason ?? null }
|
|
67
|
+
get rawHeaders () { return this.target?.rawHeaders ?? null }
|
|
68
|
+
set rawHeaders (value) {
|
|
69
|
+
if (this.target) {
|
|
70
|
+
this.target.rawHeaders = value
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get rawTrailers () { return this.target?.rawTrailers ?? null }
|
|
75
|
+
set rawTrailers (value) {
|
|
76
|
+
if (this.target) {
|
|
77
|
+
this.target.rawTrailers = value
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
38
82
|
class RetryHandler {
|
|
39
83
|
constructor (opts, { dispatch, handler }) {
|
|
40
84
|
const { retryOptions, ...dispatchOpts } = opts
|
|
@@ -55,7 +99,7 @@ class RetryHandler {
|
|
|
55
99
|
|
|
56
100
|
this.error = null
|
|
57
101
|
this.dispatch = dispatch
|
|
58
|
-
this.handler =
|
|
102
|
+
this.handler = handler
|
|
59
103
|
this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }
|
|
60
104
|
this.retryOpts = {
|
|
61
105
|
throwOnError: throwOnError ?? true,
|
|
@@ -66,7 +110,7 @@ class RetryHandler {
|
|
|
66
110
|
timeoutFactor: timeoutFactor ?? 2,
|
|
67
111
|
maxRetries: maxRetries ?? 5,
|
|
68
112
|
// What errors we should retry
|
|
69
|
-
methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'],
|
|
113
|
+
methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY'],
|
|
70
114
|
// Indicates which errors to retry
|
|
71
115
|
statusCodes: statusCodes ?? [500, 502, 503, 504, 429],
|
|
72
116
|
// List of errors to retry
|
|
@@ -89,6 +133,18 @@ class RetryHandler {
|
|
|
89
133
|
this.start = 0
|
|
90
134
|
this.end = null
|
|
91
135
|
this.etag = null
|
|
136
|
+
this.statusCode = null
|
|
137
|
+
this.headers = null
|
|
138
|
+
this.controllerProxy = new RetryController(reason => this.#onAbort(reason))
|
|
139
|
+
// A retry decision is in flight (the policy may be holding a backoff
|
|
140
|
+
// timer). While pending, a consumer abort cancels the wait.
|
|
141
|
+
this.retryPending = false
|
|
142
|
+
// Backoff timer returned by the retry policy, so #onAbort can cancel it.
|
|
143
|
+
// Null for custom policies that do not return their timer.
|
|
144
|
+
this.retryTimer = null
|
|
145
|
+
// Set once an abort during the backoff delivered the terminal error
|
|
146
|
+
// downstream; late policy callbacks and connection errors are then moot.
|
|
147
|
+
this.aborted = false
|
|
92
148
|
}
|
|
93
149
|
|
|
94
150
|
onResponseStartWithRetry (controller, statusCode, headers, statusMessage, err) {
|
|
@@ -99,11 +155,11 @@ class RetryHandler {
|
|
|
99
155
|
// The downstream handler already received the response from an
|
|
100
156
|
// earlier attempt. Forwarding this response would replace the
|
|
101
157
|
// downstream body and leave the original body pending forever.
|
|
102
|
-
this.handler.onResponseError?.(
|
|
158
|
+
this.handler.onResponseError?.(this.controllerProxy, err)
|
|
103
159
|
} else {
|
|
104
160
|
this.headersSent = true
|
|
105
161
|
this.checkpointResponseEnd(headers)
|
|
106
|
-
this.handler.onResponseStart?.(
|
|
162
|
+
this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
|
|
107
163
|
}
|
|
108
164
|
} else {
|
|
109
165
|
this.error = err
|
|
@@ -115,21 +171,28 @@ class RetryHandler {
|
|
|
115
171
|
if (isDisturbed(this.opts.body)) {
|
|
116
172
|
this.headersSent = true
|
|
117
173
|
this.checkpointResponseEnd(headers)
|
|
118
|
-
this.handler.onResponseStart?.(
|
|
174
|
+
this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
|
|
119
175
|
return
|
|
120
176
|
}
|
|
121
177
|
|
|
122
178
|
function shouldRetry (passedErr) {
|
|
179
|
+
if (this.aborted) {
|
|
180
|
+
// Aborted while the policy was deciding; the decision is moot.
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
this.retryPending = false
|
|
184
|
+
this.retryTimer = null
|
|
185
|
+
|
|
123
186
|
if (passedErr) {
|
|
124
187
|
if (this.headersSent) {
|
|
125
188
|
// The downstream handler already received the response from an
|
|
126
189
|
// earlier attempt. Forwarding this response would replace the
|
|
127
190
|
// downstream body and leave the original body pending forever.
|
|
128
|
-
this.handler.onResponseError?.(
|
|
191
|
+
this.handler.onResponseError?.(this.controllerProxy, passedErr)
|
|
129
192
|
} else {
|
|
130
193
|
this.headersSent = true
|
|
131
194
|
this.checkpointResponseEnd(headers)
|
|
132
|
-
this.handler.onResponseStart?.(
|
|
195
|
+
this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
|
|
133
196
|
}
|
|
134
197
|
controller.resume()
|
|
135
198
|
return
|
|
@@ -139,15 +202,25 @@ class RetryHandler {
|
|
|
139
202
|
controller.resume()
|
|
140
203
|
}
|
|
141
204
|
|
|
205
|
+
// The pause()/resume() pair (here and in shouldRetry) acts on THIS
|
|
206
|
+
// connection's controller -- never the downstream proxy. We hold this exact
|
|
207
|
+
// connection while the retry policy decides (possibly after a timeout) and
|
|
208
|
+
// must resume the same one. Routing this through controllerProxy would risk
|
|
209
|
+
// resuming a different connection if a later dispatch re-points the proxy in
|
|
210
|
+
// between, leaving this one paused forever -- the very stall the proxy exists
|
|
211
|
+
// to prevent.
|
|
142
212
|
controller.pause()
|
|
143
|
-
|
|
213
|
+
// The default policy returns its backoff timer so an abort can cancel it;
|
|
214
|
+
// a custom policy may return anything (or nothing), which is ignored.
|
|
215
|
+
this.retryPending = true
|
|
216
|
+
this.retryTimer = this.retryOpts.retry(
|
|
144
217
|
err,
|
|
145
218
|
{
|
|
146
219
|
state: { counter: this.retryCount },
|
|
147
220
|
opts: { retryOptions: this.retryOpts, ...this.opts }
|
|
148
221
|
},
|
|
149
222
|
shouldRetry.bind(this)
|
|
150
|
-
)
|
|
223
|
+
) ?? null
|
|
151
224
|
}
|
|
152
225
|
|
|
153
226
|
checkpointResponseEnd (headers) {
|
|
@@ -165,13 +238,27 @@ class RetryHandler {
|
|
|
165
238
|
}
|
|
166
239
|
|
|
167
240
|
onRequestStart (controller, context) {
|
|
241
|
+
// request.js creates a fresh RequestController per dispatch and passes that
|
|
242
|
+
// same instance to every later callback of the dispatch. onRequestStart is
|
|
243
|
+
// the first callback (it is where the controller is created), so re-pointing
|
|
244
|
+
// the proxy here is enough to keep it on the active connection across every
|
|
245
|
+
// transparent retry/resume.
|
|
246
|
+
this.controllerProxy.target = controller
|
|
168
247
|
if (!this.headersSent) {
|
|
169
|
-
this.handler.onRequestStart?.(
|
|
248
|
+
this.handler.onRequestStart?.(this.controllerProxy, context)
|
|
170
249
|
}
|
|
171
250
|
}
|
|
172
251
|
|
|
173
|
-
|
|
174
|
-
this.handler.
|
|
252
|
+
onBodySent (chunk) {
|
|
253
|
+
this.handler.onBodySent?.(chunk)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
onRequestSent () {
|
|
257
|
+
this.handler.onRequestSent?.()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
onRequestUpgrade (_controller, statusCode, headers, socket) {
|
|
261
|
+
this.handler.onRequestUpgrade?.(this.controllerProxy, statusCode, headers, socket)
|
|
175
262
|
}
|
|
176
263
|
|
|
177
264
|
static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) {
|
|
@@ -184,7 +271,8 @@ class RetryHandler {
|
|
|
184
271
|
timeoutFactor,
|
|
185
272
|
statusCodes,
|
|
186
273
|
errorCodes,
|
|
187
|
-
methods
|
|
274
|
+
methods,
|
|
275
|
+
retryAfter
|
|
188
276
|
} = retryOptions
|
|
189
277
|
const { counter } = state
|
|
190
278
|
|
|
@@ -216,7 +304,7 @@ class RetryHandler {
|
|
|
216
304
|
return
|
|
217
305
|
}
|
|
218
306
|
|
|
219
|
-
let retryAfterHeader = headers?.['retry-after']
|
|
307
|
+
let retryAfterHeader = retryAfter === false ? undefined : headers?.['retry-after']
|
|
220
308
|
if (retryAfterHeader) {
|
|
221
309
|
retryAfterHeader = Number(retryAfterHeader)
|
|
222
310
|
retryAfterHeader = Number.isNaN(retryAfterHeader)
|
|
@@ -225,29 +313,28 @@ class RetryHandler {
|
|
|
225
313
|
}
|
|
226
314
|
|
|
227
315
|
const retryTimeout =
|
|
228
|
-
retryAfterHeader
|
|
229
|
-
?
|
|
230
|
-
:
|
|
231
|
-
|
|
232
|
-
|
|
316
|
+
retryAfterHeader === 0
|
|
317
|
+
? 0
|
|
318
|
+
: retryAfterHeader > 0
|
|
319
|
+
? Math.min(retryAfterHeader, maxTimeout)
|
|
320
|
+
: Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)
|
|
321
|
+
|
|
322
|
+
// Return the backoff timer so the handler can cancel it when the
|
|
323
|
+
// consumer aborts while the retry decision is pending.
|
|
324
|
+
return setTimeout(() => cb(null), retryTimeout)
|
|
233
325
|
}
|
|
234
326
|
|
|
235
327
|
onResponseStart (controller, statusCode, headers, statusMessage) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (statusCode >= 300) {
|
|
240
|
-
const err = new RequestRetryError('Request failed', statusCode, {
|
|
241
|
-
headers,
|
|
242
|
-
data: {
|
|
243
|
-
count: this.retryCount
|
|
244
|
-
}
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
this.onResponseStartWithRetry(controller, statusCode, headers, statusMessage, err)
|
|
328
|
+
if (statusCode < 200) {
|
|
329
|
+
this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
|
|
248
330
|
return
|
|
249
331
|
}
|
|
250
332
|
|
|
333
|
+
this.error = null
|
|
334
|
+
this.retryCount += 1
|
|
335
|
+
this.statusCode = statusCode
|
|
336
|
+
this.headers = headers
|
|
337
|
+
|
|
251
338
|
// Checkpoint for resume from where we left it
|
|
252
339
|
if (this.headersSent) {
|
|
253
340
|
// Only Partial Content 206 supposed to provide Content-Range,
|
|
@@ -294,15 +381,27 @@ class RetryHandler {
|
|
|
294
381
|
return
|
|
295
382
|
}
|
|
296
383
|
|
|
384
|
+
if (statusCode >= 300) {
|
|
385
|
+
const err = new RequestRetryError('Request failed', statusCode, {
|
|
386
|
+
headers,
|
|
387
|
+
data: {
|
|
388
|
+
count: this.retryCount
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
this.onResponseStartWithRetry(controller, statusCode, headers, statusMessage, err)
|
|
393
|
+
return
|
|
394
|
+
}
|
|
395
|
+
|
|
297
396
|
if (this.end == null) {
|
|
298
397
|
if (statusCode === 206) {
|
|
299
398
|
// First time we receive 206
|
|
300
399
|
const range = parseRangeHeader(headers['content-range'])
|
|
301
400
|
|
|
302
|
-
if (range == null) {
|
|
401
|
+
if (range == null || range.end == null) {
|
|
303
402
|
this.headersSent = true
|
|
304
403
|
this.handler.onResponseStart?.(
|
|
305
|
-
|
|
404
|
+
this.controllerProxy,
|
|
306
405
|
statusCode,
|
|
307
406
|
headers,
|
|
308
407
|
statusMessage
|
|
@@ -324,7 +423,7 @@ class RetryHandler {
|
|
|
324
423
|
}
|
|
325
424
|
|
|
326
425
|
// We make our best to checkpoint the body for further range headers
|
|
327
|
-
if (this.end == null) {
|
|
426
|
+
if (this.end == null && this.opts.method !== 'HEAD') {
|
|
328
427
|
const contentLength = headers['content-length']
|
|
329
428
|
this.end = contentLength != null ? Number(contentLength) - 1 : null
|
|
330
429
|
}
|
|
@@ -351,7 +450,7 @@ class RetryHandler {
|
|
|
351
450
|
|
|
352
451
|
this.headersSent = true
|
|
353
452
|
this.handler.onResponseStart?.(
|
|
354
|
-
|
|
453
|
+
this.controllerProxy,
|
|
355
454
|
statusCode,
|
|
356
455
|
headers,
|
|
357
456
|
statusMessage
|
|
@@ -364,30 +463,40 @@ class RetryHandler {
|
|
|
364
463
|
}
|
|
365
464
|
}
|
|
366
465
|
|
|
367
|
-
onResponseData (
|
|
466
|
+
onResponseData (_controller, chunk) {
|
|
368
467
|
if (this.error) {
|
|
369
468
|
return
|
|
370
469
|
}
|
|
371
470
|
|
|
372
471
|
this.start += chunk.length
|
|
373
472
|
|
|
374
|
-
this.handler.onResponseData?.(
|
|
473
|
+
this.handler.onResponseData?.(this.controllerProxy, chunk)
|
|
375
474
|
}
|
|
376
475
|
|
|
377
|
-
onResponseEnd (
|
|
476
|
+
onResponseEnd (_controller, trailers) {
|
|
378
477
|
if (this.error && this.retryOpts.throwOnError) {
|
|
379
478
|
throw this.error
|
|
380
479
|
}
|
|
381
480
|
|
|
382
481
|
if (!this.error) {
|
|
482
|
+
// Verify that the received body length matches the expected range
|
|
483
|
+
// when we have a finite end position (from Content-Length or Content-Range)
|
|
484
|
+
if (this.end != null && Number.isFinite(this.end)) {
|
|
485
|
+
if (this.start !== this.end + 1) {
|
|
486
|
+
throw new RequestRetryError('Content-Range mismatch', this.statusCode, {
|
|
487
|
+
headers: this.headers,
|
|
488
|
+
data: { count: this.retryCount }
|
|
489
|
+
})
|
|
490
|
+
}
|
|
491
|
+
}
|
|
383
492
|
this.retryCount = 0
|
|
384
|
-
return this.handler.onResponseEnd?.(
|
|
493
|
+
return this.handler.onResponseEnd?.(this.controllerProxy, trailers)
|
|
385
494
|
}
|
|
386
495
|
|
|
387
|
-
this.retry(
|
|
496
|
+
this.retry()
|
|
388
497
|
}
|
|
389
498
|
|
|
390
|
-
retry (
|
|
499
|
+
retry () {
|
|
391
500
|
if (this.start !== 0) {
|
|
392
501
|
const headers = { range: `bytes=${this.start}-${this.end ?? ''}` }
|
|
393
502
|
|
|
@@ -409,23 +518,38 @@ class RetryHandler {
|
|
|
409
518
|
this.retryCountCheckpoint = this.retryCount
|
|
410
519
|
this.dispatch(this.opts, this)
|
|
411
520
|
} catch (err) {
|
|
412
|
-
this.handler.onResponseError?.(
|
|
521
|
+
this.handler.onResponseError?.(this.controllerProxy, err)
|
|
413
522
|
}
|
|
414
523
|
}
|
|
415
524
|
|
|
416
525
|
onResponseError (controller, err) {
|
|
526
|
+
if (this.aborted) {
|
|
527
|
+
// #onAbort already delivered the terminal error downstream; the late
|
|
528
|
+
// error of the torn-down connection must not be forwarded twice.
|
|
529
|
+
return
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// controller is THIS failed connection (not the proxy): we inspect whether
|
|
533
|
+
// the consumer aborted it to decide retry-vs-propagate.
|
|
417
534
|
if (controller?.aborted || isDisturbed(this.opts.body) || (this.headersSent && !this.resume)) {
|
|
418
|
-
this.handler.onResponseError?.(
|
|
535
|
+
this.handler.onResponseError?.(this.controllerProxy, err)
|
|
419
536
|
return
|
|
420
537
|
}
|
|
421
538
|
|
|
422
539
|
function shouldRetry (returnedErr) {
|
|
540
|
+
if (this.aborted) {
|
|
541
|
+
// Aborted while the policy was deciding; the decision is moot.
|
|
542
|
+
return
|
|
543
|
+
}
|
|
544
|
+
this.retryPending = false
|
|
545
|
+
this.retryTimer = null
|
|
546
|
+
|
|
423
547
|
if (!returnedErr) {
|
|
424
|
-
this.retry(
|
|
548
|
+
this.retry()
|
|
425
549
|
return
|
|
426
550
|
}
|
|
427
551
|
|
|
428
|
-
this.handler?.onResponseError?.(
|
|
552
|
+
this.handler?.onResponseError?.(this.controllerProxy, returnedErr)
|
|
429
553
|
}
|
|
430
554
|
|
|
431
555
|
// We reconcile in case of a mix between network errors
|
|
@@ -439,14 +563,31 @@ class RetryHandler {
|
|
|
439
563
|
this.retryCount += 1
|
|
440
564
|
}
|
|
441
565
|
|
|
442
|
-
this.
|
|
566
|
+
this.retryPending = true
|
|
567
|
+
this.retryTimer = this.retryOpts.retry(
|
|
443
568
|
err,
|
|
444
569
|
{
|
|
445
570
|
state: { counter: this.retryCount },
|
|
446
571
|
opts: { retryOptions: this.retryOpts, ...this.opts }
|
|
447
572
|
},
|
|
448
573
|
shouldRetry.bind(this)
|
|
449
|
-
)
|
|
574
|
+
) ?? null
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
#onAbort (reason) {
|
|
578
|
+
// A consumer abort lands on the controller proxy. If the retry policy is
|
|
579
|
+
// still deciding (typically holding a backoff timer), cancel the wait and
|
|
580
|
+
// surface the abort immediately instead of letting the request hang until
|
|
581
|
+
// the backoff elapses.
|
|
582
|
+
if (!this.retryPending) {
|
|
583
|
+
return
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
this.aborted = true
|
|
587
|
+
this.retryPending = false
|
|
588
|
+
clearTimeout(this.retryTimer)
|
|
589
|
+
this.retryTimer = null
|
|
590
|
+
this.handler.onResponseError?.(this.controllerProxy, reason ?? new RequestAbortedError())
|
|
450
591
|
}
|
|
451
592
|
}
|
|
452
593
|
|