fix: do not await the release on a cancellation path (#2319 round 25)
`ReadableStream.cancel()` adopts the underlying source's cancel promise, which is permitted never to settle — so awaiting `releaseBody` while propagating a cancellation can hang the very thing it was meant to end. The five exceptional exits in the compat probe loop now start the release without waiting on it: the release is a courtesy to the connection pool, and the caller's abort or timeout must reach it regardless. The two non-exceptional awaits are unchanged. Same class as the tee teardown in round 24, one file over. Also moves the `redactUrlQuery` import above DEFAULT_OAUTH_REQUEST_TIMEOUT_MS's JSDoc, which it had split from the constant — so hover and API tooling see the doc again. Verified by mutation: awaiting the release makes the new test time out. 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
78f82475c4
commit
b4c02865e4
1 changed files with 53 additions and 7 deletions
|
|
@ -578,6 +578,36 @@ describe("probe cancellation (#2319)", () => {
|
|||
expect(cancels).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not wait on a cancel that never settles", async () => {
|
||||
// ⚠️ `ReadableStream.cancel()` adopts the underlying source's cancel
|
||||
// promise, which is permitted never to settle — so awaiting the release on
|
||||
// a path that is propagating a cancellation would hang the very thing it
|
||||
// was meant to end.
|
||||
const discovery = new Response(
|
||||
new ReadableStream<Uint8Array>({
|
||||
start(ctrl) {
|
||||
ctrl.enqueue(new TextEncoder().encode("not found"));
|
||||
},
|
||||
cancel() {
|
||||
return new Promise<void>(() => {});
|
||||
},
|
||||
}),
|
||||
{ status: 404 },
|
||||
);
|
||||
const inner = vi.fn<typeof fetch>((input) =>
|
||||
String(input) === RFC8414
|
||||
? Promise.resolve(discovery)
|
||||
: new Promise<Response>(() => {}),
|
||||
);
|
||||
const wrapped = withRfc8414OidcCompat(inner);
|
||||
const reason = new Error("gone");
|
||||
|
||||
// Would hang if the release were awaited; the assertion is that it settles.
|
||||
await expect(
|
||||
wrapped(RFC8414, { signal: AbortSignal.abort(reason) }),
|
||||
).rejects.toBe(reason);
|
||||
});
|
||||
|
||||
it("still falls back to the original response on an ordinary probe failure", async () => {
|
||||
const inner = vi.fn<typeof fetch>((input) => {
|
||||
if (String(input) === RFC8414) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue