fix: clean up abort listener after response lifecycle#1910
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f49bf26187
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const text = await response.text(); | ||
| return text as unknown as T; | ||
| })(); | ||
| })().finally(() => cleanup?.()); |
There was a problem hiding this comment.
Preserve abort forwarding for binary responses
For __binaryResponse endpoints such as file downloads and audio speech, this finally runs immediately after returning the raw Response, before the caller has consumed response.body with blob(), arrayBuffer(), or a reader. Because fetchWithTimeout() only passes the SDK controller signal to fetch, removing the caller-signal listener here means aborting the user-provided signal during the subsequent binary body read no longer aborts the underlying response body; cleanup needs to be deferred until the returned Response is consumed/cancelled (or handled separately for raw responses).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in dd0e115. Binary/raw responses now return a Response whose body is wrapped with the same idempotent cleanup callback, so the caller abort listener is not removed when the raw Response is handed back. It stays active until the binary body is consumed, cancelled, or errors, preserving caller abort forwarding during text()/arrayBuffer()/reader consumption while still removing the listener afterward. Added regression coverage for successful binary body consumption and abort during binary body reading.
f49bf26 to
dd0e115
Compare
Changes being requested
Fixes #1811.
This keeps the caller-provided
AbortSignallistener long enough to preserve user abort behavior while the response body is still being consumed, then removes it once the response lifecycle has settled:fetchWithTimeout()now returns theResponseplus an idempotent cleanup callback for the caller-signal listener.Stream.fromSSEResponse()so the listener is removed when iteration finishes or is closed/broken.Difference from existing PRs
I re-checked the existing PRs for #1811 before opening this one.
AbortSignallistener.fetchWithTimeout()caller signal listener from fetchWithTimeout does not remove abort event listener on successful completion, preventing process exit on Deno #1811.Additional context & links
The motivating Deno behavior is that a listener attached to
AbortSignal.timeout()can keep the timeout timer ref'd until it fires. Removing the listener after successful body/stream completion lets successful requests exit promptly while still preserving abort behavior during body consumption.Tests
./node_modules/.bin/jest tests/index.test.ts -t "caller abort listener|caller abort forwarding" --runInBand./node_modules/.bin/jest tests/index.test.ts --runInBand./node_modules/.bin/prettier --check src/client.ts src/core/streaming.ts src/internal/parse.ts tests/index.test.ts./node_modules/.bin/eslint src/client.ts src/core/streaming.ts src/internal/parse.ts tests/index.test.tsgit diff --check origin/master...HEAD./scripts/build