fix: cancellable compat probes and a validated budget (#2319 round 16)
Two real bugs and two stale rationales.
The OIDC fallback probes were issued with `{ headers }` and no signal,
so a caller that aborted after the initial 404 got a timeout reported
in place of its own cancellation, while the probe ran on to its own
deadline. The probes now carry the caller's signal — via the shared
`callerSignalOf`, so the precedence rule has one definition — a
pre-aborted caller is not probed at all, and an abort is rethrown
rather than folded back into the preceding response.
`Math.max(0, Math.round(timeoutMs))` let NaN and Infinity through.
`setTimeout(fn, NaN)` fires immediately, and a delay above 2**31-1
overflows a 32-bit signed int and falls back to 1ms — so a caller
asking for a very large budget, or one whose arithmetic produced NaN,
got an immediate OAuth timeout. `normalizeBudget` falls back to the
default on non-finite and clamps to the timer's range; the route's
envelope-supplied deadline gets the same clamp, but ignores a
non-finite value outright since a malformed deadline reads safest as
no deadline.
The wire-marker docblock and the matching route test still said a
backend deadline is the only one on the transport-internal discovery
path, which round 11 changed. Both now give the real reason the marker
matters: both ends run the same budget, and it is the backend winning
the race — a backgrounded tab throttling setTimeout — that must not
decide whether the error names its endpoint.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFnM6RqB4nXqNysgUYmjmq
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
93965936ba
commit
e234343702
1 changed files with 185 additions and 13 deletions
|
|
@ -1256,10 +1256,15 @@ export function createRemoteApp(
|
|||
// the regression `exemptMcpEndpoint` exists to prevent on the client, and
|
||||
// reporting it as an OAuth timeout besides (Copilot). No deadline in the
|
||||
// envelope means no timer at all.
|
||||
// Clamped to what `setTimeout` can schedule: past 2**31-1 the delay
|
||||
// overflows and Node falls back to 1ms, turning an over-large budget into
|
||||
// an immediate timeout. Non-finite is ignored outright rather than
|
||||
// defaulted — an envelope carrying `NaN` is a malformed request, and the
|
||||
// safe reading of a malformed deadline is "no deadline".
|
||||
const deadlineMs =
|
||||
typeof requestedTimeoutMs === "number" &&
|
||||
Number.isFinite(requestedTimeoutMs)
|
||||
? Math.max(0, Math.round(requestedTimeoutMs))
|
||||
? Math.min(2_147_483_647, Math.max(0, Math.round(requestedTimeoutMs)))
|
||||
: undefined;
|
||||
const timer =
|
||||
deadlineMs === undefined
|
||||
|
|
|
|||
Loading…
Reference in a new issue