-
-
Notifications
You must be signed in to change notification settings - Fork 664
fix: ensure connection timeouts are cancelled when fetch is aborted #4412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
mcollina
wants to merge
8
commits into
main
Choose a base branch
from
fix-fetch-abort-connection-timeout
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
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
This test reproduces a bug where aborting a fetch request to an unresponsive host prevents the Node.js process from exiting cleanly. The process hangs for ~8-10 seconds due to internal timers/handles remaining referenced in the event loop after the abort signal. The test uses a worker thread to isolate the issue and demonstrates that while the fetch operation completes and is aborted within ~1s, the process takes significantly longer to exit. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Matteo Collina <[email protected]>
…4405 This commit addresses the process hanging issue when aborting requests to unresponsive hosts. Instead of monkey-patching the destroy method, we now properly handle the close event to ensure timers and handles are cleaned up when connections are closed or aborted. This prevents internal timers from keeping the event loop alive after a request has been aborted, allowing the Node.js process to exit cleanly. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Matteo Collina <[email protected]>
Remove monkey-patching of socket.destroy and use the canonical 'close' event instead for cleanup. This follows Node.js core patterns and is more maintainable. The close event is the definitive cleanup event for all Node.js streams and sockets, covering all closure scenarios (destroy, timeout, error, abort, etc.) without modifying existing methods. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Matteo Collina <[email protected]>
Restore util.js from main branch and remove unused kClearConnectTimeout symbol since the improved implementation using close event listeners doesn't require these changes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Matteo Collina <[email protected]>
The close event listener approach doesn't actually solve the process hanging issue. The problem likely lies in fetch's FinalizationRegistry logic, not in the connect timeout handling. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Enhanced abort handling in multiple areas: - Added removeAllListeners() to Fetch.abort() - Improved connection.destroy() to clean up references - Added dispatch-level abort handling for terminated controller - Added cleanup of abort listener in main fetch function However, the process still hangs for ~8 seconds when aborting requests to unresponsive hosts. The fetch itself aborts correctly (TypeError), but underlying timers/handles remain active, preventing clean process exit. The issue appears to be in the Agent/Client layer where internal connection timeouts are not properly cancelled on abort. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
Added mechanism to track and cleanup sockets during connection when Client is destroyed: - Added kConnectingAbort symbol to track abort function - Store socket reference in connector callback for cleanup - Call socket.destroy() when Client is destroyed during connection However, the issue persists because for unresponsive hosts: - The connector callback is never called (connection hangs at TCP level) - The socket is created but setupConnectTimeout manages the cleanup - Internal timers from setupConnectTimeout are not properly cancelled on abort Root cause: setupConnectTimeout in lib/core/util.js creates timers that are not cancelled when fetch is aborted early, keeping event loop alive. Need to ensure connection timeouts are cancelled when fetch controller aborts. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
This commit fixes a process hanging issue when aborting fetch requests to unresponsive hosts. Previously, the connection timeout mechanism (setupConnectTimeout) created timers that were not cancelled when a fetch was aborted early, keeping the Node.js event loop alive. Changes: - Expose clearConnectTimeout function on socket in lib/core/connect.js - Cancel connection timeout in Client abort logic in lib/dispatcher/client.js - Add comprehensive test for fetch abort to unresponsive host scenarios The fix ensures that when a fetch is aborted while connecting to an unresponsive host, all internal timers are properly cleaned up, allowing the Node.js process to exit cleanly instead of hanging. Fixes #4405 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Matteo Collina <[email protected]>
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.
Summary
Fixes issue where aborting fetch requests to unresponsive hosts would cause the Node.js process to hang for 8+ seconds instead of exiting cleanly.
Problem
When calling
fetch()
with an unresponsive host and then aborting the request early, the process would hang waiting for internal connection timeouts to expire. This was caused by connection timeout timers insetupConnectTimeout
that were not being cancelled when the fetch was aborted.Root Cause
fetch()
creates a connection to an unresponsive hostsetupConnectTimeout
inlib/core/util.js
creates internal timersfetch()
is aborted, the connection attempt continues in backgroundSolution
lib/core/connect.js
: ExposeclearConnectTimeout
function on socketlib/dispatcher/client.js
: Cancel connection timeout when client is destroyed during connectiontest/fetch/issue-4405.js
Verification
Test Results
Fixes #4405
🤖 Generated with Claude Code