Skip to content

fix(fetch): dump the body of a 3xx response that will be followed - #5729

Open
zeexzeex wants to merge 1 commit into
nodejs:mainfrom
zeexzeex:fix/fetch-dump-redirect-body
Open

fix(fetch): dump the body of a 3xx response that will be followed#5729
zeexzeex wants to merge 1 commit into
nodejs:mainfrom
zeexzeex:fix/fetch-dump-redirect-body

Conversation

@zeexzeex

Copy link
Copy Markdown
Contributor

This relates to...

Fixes #5728

Rationale

When fetch() follows a redirect, the body of the 3xx response is pushed into a Readable that nothing ever reads. Once the buffered body exceeds the stream highWaterMark, push() returns false and the socket is paused.

Nothing resumes it. The call that resumes the socket lives in that stream's read(), and read() only fires when a consumer pulls from the stream. For a followed redirect the response is never exposed to the caller, so no consumer is ever attached: resuming the socket requires reading the body, and the reader only appears once the redirect chain has been followed. The connection stays in a running state and is never returned to the pool.

With a pooled dispatcher this is worse than a single stalled request, since every redirect pins a connection until the connections limit is exhausted. In the reported case a 301 carrying a 128 KiB body with Agent({ connections: 1 }) makes fetch() never settle, and the follow-up request never reaches the server at all.

RedirectHandler already discards 3xx bodies on the dispatcher path, citing RFC 7231 section 6.4. This applies the same behaviour to the fetch path.

Changes

The fix reuses the willFollow value that onResponseStart already computes for content-encoding handling, and discards the chunks in onResponseData instead of pushing them into the stream.

The flag is stored on the dispatch handler rather than on fetchParams.controller. The controller is created once per fetch() call and shared across the whole redirect chain, so setting dump there would also discard the final response body. The handler object is created per dispatch, which scopes the change to the redirect response alone.

Features

N/A

Bug Fixes

  • The body of a 3xx response that will be followed is now discarded as it arrives, so the connection is released instead of being pinned for the lifetime of the redirect chain.

Breaking Changes and Deprecations

N/A

Status

The body of a redirect response is never exposed when the redirect is
followed, so nothing ever reads the stream it is pushed into. Once the
buffered body exceeds the stream highWaterMark the socket is paused and
never resumed, which keeps the connection in a running state. With a
pooled dispatcher every redirect pins a connection until the pool is
exhausted.

The dispatcher path already discards 3xx bodies in RedirectHandler, so
do the same in the fetch path.

The flag lives on the dispatch handler rather than on the fetch
controller because the controller is shared across the whole redirect
chain, and setting it there would also discard the final response body.

Signed-off-by: Avocado <ujubongbong@gmail.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.46%. Comparing base (fc3450d) to head (b869351).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5729      +/-   ##
==========================================
- Coverage   93.47%   93.46%   -0.01%     
==========================================
  Files         110      110              
  Lines       38908    38915       +7     
==========================================
+ Hits        36368    36372       +4     
- Misses       2540     2543       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fetch follow does not dump 3xx bodies and hangs when the redirect body is large

2 participants