Skip to content

Commit a2ca9d8

Browse files
jpolitzclaude
andcommitted
Rewrite shareurl race more declaratively (Promise.any + side-race)
Joe says: I think I constrained Claude so much in this session that it had to say things I like below :-) This is post-convo with Ben where we were still unhappy with (a) variable names and (b) closed over mutable variables instead of declarative promise APIs. Claude says: Following a second pass of code review: the previous version used a new Promise((resolve, reject) => { let gotSomeResponse = false; ... }) block with a flag tracking which side had won. That conflated two concerns — "first success wins the response" and "abort proxy iff direct beat it" — into one imperative state machine. This pass separates them. The race is expressed as three composed Promise primitives: - directP: direct fetch, gated on content-type verification (rejects if direct returned the wrong shape, so it drops out of the response race entirely). - shouldProxyPromise (Promise.race): false iff direct verified before the timeout; locks the per-host policy for the page-load. - directFinishedSuccessfullyAndFirstP (Promise.race): does direct settle before proxy, AND was that settlement a verified success? Only then do we proxyCtrl.abort(). This keeps the abort safe — we never abort once proxy has already returned headers, which would error the caller's body stream mid-read. - responsePromise (Promise.any): whichever of directP or proxyP fulfills first. Tested both healthy and DevTools-blocked flows. Observed the timing edge case where both proxy and direct return 200 within a few ms of each other: shouldProxy still locks 'direct' as expected, since the verdict is decoupled from whether the abort caught proxy in time. Also adds a comment explaining the deliberate non-handling of caller- provided init.signal: the signal overwrite means a caller aborting won't cancel the proxy fetch, but the alternative (event-listener forwarding or AbortSignal.any) is more complexity than the not-quite-fully-aborted case justifies right now. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 480e8a8 commit a2ca9d8

1 file changed

Lines changed: 30 additions & 21 deletions

File tree

src/web/js/beforePyret.js

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,36 +59,45 @@ function _shareurlFetch(shouldProxy, fetchInput, fetchInit) {
5959

6060
function _shareurlRace(fetchInput, fetchInit) {
6161
const proxyCtrl = new AbortController();
62+
// NOTE(joe): The signal overwrite is technically not the right fetch()
63+
// polyfill. If the caller elsewhere in the codebase provided a different
64+
// signal (which in the fetch API is only for aborting as of April '26), that
65+
// caller aborting through that signal won't cancel the proxy fetch.
66+
// I'm OK letting that case slip through here in exchange for not having a
67+
// bunch of extra event handler forwarding
6268
const proxyP = _origFetch(_shareurlProxyUrl(fetchInput),
6369
Object.assign({}, fetchInit, { signal: proxyCtrl.signal }));
64-
const directP = _origFetch(fetchInput, fetchInit);
70+
const directP = _origFetch(fetchInput, fetchInit).then(r => {
71+
if (!_shareurlVerifyDirect(r)) throw new Error('direct request failed');
72+
return r;
73+
});
6574

66-
// shouldProxy is decided from direct's headers — a timeout flips us to
67-
// true if direct hangs at the network level (no headers, no error).
75+
// shouldProxy: false iff direct verified before the timeout, else true.
76+
// Whether to proxy is decided solely on whether direct succeeds or not
6877
const shouldProxyPromise = Promise.race([
69-
directP.then(r => !_shareurlVerifyDirect(r), () => true),
78+
directP.then(() => false, () => true),
7079
new Promise(resolve => setTimeout(() => resolve(true), SHAREURL_DIRECT_TIMEOUT_MS)),
7180
]);
7281

73-
// Caller's response: direct if its headers verify (and abort the proxy
74-
// fetch to stop wasting server bandwidth); otherwise proxy. If proxy also
75-
// fails, surface its error.
76-
const responsePromise = new Promise((resolve, reject) => {
77-
let gotSomeResponse = false;
78-
directP.then(r => {
79-
if (!gotSomeResponse && _shareurlVerifyDirect(r)) {
80-
gotSomeResponse = true;
81-
proxyCtrl.abort();
82-
resolve(r);
83-
}
84-
}).catch(() => { /* wait for proxy to resolve or reject */ });
85-
proxyP.then(r => {
86-
if (!gotSomeResponse) { gotSomeResponse = true; resolve(r); }
87-
}).catch(e => {
88-
if (!gotSomeResponse) reject(e);
89-
});
82+
// Settlement-order check: if direct verifies before proxy returns, abort
83+
// the in-flight proxy to stop wasting server bandwidth. We must NOT
84+
// abort once proxy has already returned, since by then the caller is
85+
// reading proxy's body and aborting would error its stream mid-read.
86+
const directFinishedSuccessfullyAndFirstP = Promise.race([
87+
directP.then(() => true, () => false),
88+
proxyP.then(() => false, () => false),
89+
]);
90+
directFinishedSuccessfullyAndFirstP.then(directFirst => {
91+
if (directFirst) proxyCtrl.abort();
9092
});
9193

94+
// Caller's response: whichever of direct-verified or proxy fulfills
95+
// first. If both fail, surface proxy's error (the more authoritative
96+
// upstream — direct's may just be 'direct-not-verified').
97+
const responsePromise = Promise.any([directP, proxyP]).catch(
98+
aggErr => Promise.reject(aggErr.errors[1] || aggErr.errors[0])
99+
);
100+
92101
return { responsePromise, shouldProxyPromise };
93102
}
94103

0 commit comments

Comments
 (0)