fix: honour the web fetch timeout budget end to end (#249) - #265
Open
ankitranjan7 wants to merge 1 commit into
Open
fix: honour the web fetch timeout budget end to end (#249)#265ankitranjan7 wants to merge 1 commit into
ankitranjan7 wants to merge 1 commit into
Conversation
`--timeout` set a deadline the retry ladder respected, but the teardown did not: `proxy.close()` calls `server.close()`, which stays pending until every connection drains, and impit leaves keep-alive CONNECT tunnels open. A 5s budget against news.ycombinator.com took 68s — the ladder finished in 3.2s and the rest was the close waiting for the OS to drop the tunnels. The same dangling sockets then crashed the process with an unhandled 'error' event. Track every socket the proxy opens, including the upstream halves the HTTP server never sees, and destroy them in close(). Swallow socket errors so a destroyed peer cannot take down the process. Map an aborted fetch to the structured TimeoutError instead of leaking a DOMException. Measured after: 3.4s for the same command, and a host that never responds now fails at the deadline with `TIMEOUT: web fetch timed out after 3s`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
🟢 No documentation gap found — medium confidenceThe automated review found no documentation gap in the supplied changes. This review is advisory and does not block merging. |
adikulkarni006
approved these changes
Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #249.
Root cause
--timeoutalready set a deadline the retry ladder honoured — the ladder is not where the time went. InstrumentingwebFetchagainst the URL from the issue:Everything finished in 3.2s. The remaining 65s was
proxy.close(): it callsserver.close(), which stays pending until every connection drains, and impit leaves keep-alive CONNECT tunnels open. The budget was enforced on the requests and then thrown away in thefinally. The same dangling sockets surfaced later as an unhandled'error'event that killed the process instead of producing a usable error.Fix
src/fetch/safe-proxy.tstracks every socket it opens — server connections, the CONNECT client half, and the upstream halves the HTTP server never learns about — and destroys them inclose().server.close()then returns immediately.'error'handlers destroy rather than propagate, so a torn-down peer cannot crash the process.src/fetch/client.tsmaps an aborted fetch (AbortError/ DOMTimeoutError) to the structuredTimeoutError, so the budget being hit yieldscode: TIMEOUTand theTEMPFAILexit code rather than a leakedDOMException.Measured
web fetch --url https://news.ycombinator.com --timeout 5--timeout 3TimeoutError: web fetch timed out after 3sTests
src/fetch/safe-proxy.test.tsopens a CONNECT tunnel to a server that never replies and assertsclose()resolves in under a second — it hangs until the test timeout without this change. Plus twoclient.test.tscases: the proxy is closed when the ladder throws, and an aborted fetch surfaces asTIMEOUT.Full suite passes except
tests/e2e/plugin-management.test.ts, which fails identically onmain.Noted, not fixed
FETCH_BLOCKED: its CSP header namescdnjs.cloudflare.comandrecaptcha, andisChallengeResponsematches its markers against every header value. Separate defect, filed as [Bug]: web fetch challenge detection false-positives on CSP headers (news.ycombinator.com fails with FETCH_BLOCKED) #264. This PR bounds the budget regardless of how many tiers run.readBody()on an impit response is not bounded by any signal — impit'stimeoutcovers the request, not the body stream. Marked with aponytail:comment naming the ceiling rather than building a race for a case not yet observed.🤖 Generated with Claude Code