Skip to content

HTTP: cancel unread lazy fetch bodies for HEAD responses - #44227

Open
vladislav-miroshnikov wants to merge 1 commit into
oven-sh:mainfrom
vladislav-miroshnikov:claude/head-lazy-body-wave
Open

vladislav-miroshnikov wants to merge 1 commit into
oven-sh:mainfrom
vladislav-miroshnikov:claude/head-lazy-body-wave

Conversation

@vladislav-miroshnikov

Copy link
Copy Markdown

What does this PR do?

Fixes #43970. An untouched fetch Response can still have a pending body producer when a HEAD response discards it, so there is no ReadableStream to cancel and the upstream request stays open. Take and cancel that pending FetchResponseBody producer while preserving the pending-consumer guard and the existing stream cancellation path.

This covers the untouched-fetch case explicitly left outside #43973; materialized stream behavior is unchanged.

How did you verify your code works?

The new regression reproduces the missing upstream abort on Bun 1.4.2 and passes on the patched main debug build. It sends three HEAD requests, waits for the upstream abort events and checks a later GET. The existing serve, reused-response and pending-promise-abort suites pass 368 tests with 1 skip, including the held response.text() HEAD case. Local HTTP tests run without the host proxy variables.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@vladislav-miroshnikov

Copy link
Copy Markdown
Author

@Jarred-Sumner, could you check the pending-producer cleanup here? This keeps the consumer guard from #44014 and only adds cancellation for an unmaterialized FetchResponseBody. The adjacent response.text() HEAD regression passes with the new upstream-abort test.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: oven-sh/bun/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 2db73c05-b988-4040-a03e-692cc75f295d

📥 Commits

Reviewing files that changed from the base of the PR and between 9f70da0 and 0c14a9c.

📒 Files selected for processing (2)
  • src/runtime/server/RequestContext.rs
  • test/js/bun/http/serve.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.


Walkthrough

When a HEAD response has a Locked body without a readable stream, cancel_unread_body now cancels its producer if it is a FetchResponseBody. A regression test checks that proxied upstream requests abort and a separate response still succeeds.

Changes

HEAD response body cancellation

Layer / File(s) Summary
Cancel pending fetch response bodies
src/runtime/server/RequestContext.rs, test/js/bun/http/serve.test.ts
When no readable stream exists, cancel_unread_body takes and cancels the producer only if it is a FetchResponseBody. The test checks that three proxied HEAD requests abort upstream requests, return empty 200 responses, and do not prevent a separate /ok response.

Suggested reviewers: jarred-sumner

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 0c14a

The HEAD-response cleanup change and its regression coverage present no identified blocker to merging after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 0c14a

The change addresses upstream requests left open by discarded HEAD response bodies. The reviewed path preserves active body readers and confines cancellation to the response’s own fetch operation. No security issue was identified in that path, though security coverage is not complete.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — A client can reach this cleanup through an application’s HEAD response path, but the new cancellation follows the producer attached to that discarded response rather than selecting another request’s transport.

Trust Boundaries and Controls

  • observed — Consumer ownership remains the first control: cleanup returns for an active locked-body consumer and cancels only an unconsumed FetchResponseBody producer when no stream exists.

Resilience and Maintainability Implications

  • observed — Repeated transport aborts do not schedule another shutdown, and abandonment still releases response-body resources.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: canceling unread lazy fetch bodies for HEAD responses.
Description check ✅ Passed The description includes both required sections. It explains the problem, implementation, scope, and verification results with sufficient detail.
Linked Issues check ✅ Passed Issue #43970 requires cancellation of an unmaterialized pending fetch body for proxied HEAD responses. The PR updates cancel_unread_body to cancel a locked FetchResponseBody producer when no reada…
Out of Scope Changes check ✅ Passed The PR changes only src/runtime/server/RequestContext.rs and the related serve.test.ts test. The source change implements the issue fix. The test verifies upstream abort and connection reuse. No u…

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
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.

Bun.serve: HEAD of a proxied fetch() Response holds the upstream request open, and fetch() stops after 256 requests

1 participant