Skip to content

fix(fetch): copy wrapped response inputs#46

Merged
medz merged 9 commits into
mainfrom
fix/response-wrapper-copy-semantics
Jun 22, 2026
Merged

fix(fetch): copy wrapped response inputs#46
medz merged 9 commits into
mainfrom
fix/response-wrapper-copy-semantics

Conversation

@medz

@medz medz commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • Define wrapped Response inputs as copy/clone semantics instead of aliasing the source host.
  • Apply ResponseInit status, statusText, and headers when copying wrapped Response, native Response, and JS web.Response inputs.
  • Add IO and Chrome regression tests that verify copied responses do not consume the source body and consumed sources fail like clone().

Root Cause

The IO and JS wrapper constructors matched Response inputs before normal construction and reused the wrapped host directly. That meant Response(source, init) ignored every init override, shared mutable body state with source, and could silently construct a response whose source body had already been consumed.

Validation

  • dart analyze
  • dart test --reporter expanded
  • dart test test/response_io_test.dart --reporter expanded
  • dart test -p chrome test/response_js_test.dart --reporter expanded
  • dart test -p chrome test/request_js_test.dart test/response_js_test.dart test/headers_js_test.dart test/url_search_params_js_test.dart test/_internal/web_fetch_utils_test.dart --reporter expanded

Closes #41
Refs #36

Summary by CodeRabbit

  • Bug Fixes

    • Fixed how response cloning/wrapping derives and preserves status, statusText, and headers, including correct application of provided overrides.
    • Improved body consumption independence between an original response and its clone, preventing unintended bodyUsed changes.
    • Preserves header behavior such as missing content-type during copy operations.
  • Tests

    • Added/extended coverage for cloning and copy semantics across wrapped and native responses, including override handling, header preservation, and consumed-body error cases.

@medz medz added the bug Something isn't working label Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@medz, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 29 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80901ae3-1a15-4646-a6ca-fb7d9835f394

📥 Commits

Reviewing files that changed from the base of the PR and between 46f2842 and 76786f1.

📒 Files selected for processing (4)
  • lib/src/fetch/response.io.dart
  • lib/src/fetch/response.js.dart
  • test/response_io_test.dart
  • test/response_js_test.dart
📝 Walkthrough

Walkthrough

Fixes Response(Response, init) factory semantics on both IO and JS platforms. Instead of aliasing the wrapped host, the factory now clones the source response when init is null and routes through new private conversion helpers to produce a fresh host with init overrides applied. Tests are added for cloning, init overrides, and consumed-body rejection.

Changes

Response copy/clone semantics

Layer / File(s) Summary
Factory switch routing for wrapped/native inputs
lib/src/fetch/response.io.dart, lib/src/fetch/response.js.dart
The Response factory switch is updated on both platforms: the (Response, null) case now clones the source host, and the (Response, init) case routes to new conversion helpers instead of reusing response._host directly.
Clone method host reconstruction
lib/src/fetch/response.io.dart, lib/src/fetch/response.js.dart
Clone method on both platforms reconstructs the host via explicit constructors wrapping cloned values or by building fresh native/web response instances with status/statusText/headers from ResponseInit, rather than passing through the public constructor.
Conversion helpers for clone-with-overrides
lib/src/fetch/response.io.dart, lib/src/fetch/response.js.dart
New static helpers (_nativeResponseFromWrappedResponse, _nativeResponseFromNativeResponse, _responseHostFromWrappedResponse, _webResponseFromWebResponse, _nativeResponseFromNativeWrappedResponse, _nativeResponseFromCopy, _bodyForNativeCopy) construct fresh response instances from source responses with init overrides; body inclusion gated on status-allowed-body check and content-type preservation handled when init.headers absent.
IO platform tests for clone/copy/consumed-rejection
test/response_io_test.dart
Tests covering: cloning wrapped Response preserves metadata without aliasing bodyUsed; init overrides applied to status/statusText/headers from wrapped and native sources; header deletions preserved when copying; StateError thrown when copying consumed response; null-body HttpClientResponse wrapping with init overrides.
JS platform tests for clone/copy/consumed-rejection
test/response_js_test.dart
Tests covering: cloning wrapped Response without aliasing bodyUsed state; init overrides applied when copying from wrapped, native, and web Response sources; header mutations preserved when copying; StateError thrown when copying consumed response; upstream body consumption remains independent until explicitly read.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Factory as Response factory
    participant Helpers as Conversion helpers
    participant Host as ResponseHost
    Caller->>Factory: Response(upstream, init)
    alt init is null
        Factory->>Host: clone source host
    else init provided
        Factory->>Helpers: _nativeResponseFromWrappedResponse(upstream, init)
        Helpers->>Helpers: extract status/statusText/headers from init or upstream
        Helpers->>Helpers: conditionally delete content-type
        Helpers->>Host: create fresh host with overridden values
    end
    Host-->>Factory: new host
    Factory-->>Caller: Response with cloned/converted host
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • medz/ht#11: Introduced and reworked ResponseInit-based Response construction semantics that this PR directly extends with clone-and-override behavior.
  • medz/ht#44: Changes Response handling of content-type; this PR adjusts Response cloning/copying to preserve when content-type is missing, while that PR introduces body-derived contentType auto-setting.

Poem

🐇 Hop hop, no more alias trap,
I cloned the response, closed the gap.
init overrides land just right,
consumed bodies blocked outright.
Body used? State error! No sneak!
Clean semantics, week by week. 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing the copy/clone behavior of wrapped Response inputs in the fetch API wrapper.
Linked Issues check ✅ Passed The PR fully addresses issue #41 objectives by implementing copy/clone semantics for wrapped Response inputs, applying ResponseInit overrides, preventing body state aliasing, and adding comprehensive regression tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing Response copy/clone semantics. Test additions focus on verifying copy behavior and preventing aliasing, with no unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/response-wrapper-copy-semantics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8af940213b

ℹ️ 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".

Comment thread lib/src/fetch/response.io.dart Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21b3f7bbad

ℹ️ 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".

Comment thread lib/src/fetch/response.io.dart Outdated
Comment thread lib/src/fetch/response.js.dart Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 49e8c10354

ℹ️ 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".

Comment thread lib/src/fetch/response.io.dart Outdated
Comment thread lib/src/fetch/response.js.dart Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/response_js_test.dart (1)

131-157: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider verifying upstream is still readable after consuming the copy.

The test verifies upstream.bodyUsed remains false after consuming the wrapped response (line 156), but unlike the equivalent tests for wrapped responses (lines 99) and native responses (lines 128), it doesn't actually read the upstream to confirm it's still readable. Adding expect(await upstream.text(), 'web body'); after line 156 would provide stronger evidence that the body is truly independent.

🧪 Optional test enhancement
       expect(upstream.bodyUsed, isFalse);
       expect(await response.text(), 'web body');
       expect(upstream.bodyUsed, isFalse);
+      expect(await upstream.text(), 'web body');
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/response_js_test.dart` around lines 131 - 157, The test 'applies init
overrides when copying web responses' verifies that upstream.bodyUsed remains
false after consuming the wrapped response, but it does not confirm that the
upstream response is still readable. Following the pattern used in the
equivalent tests for wrapped responses and native responses, add an assertion
after the final upstream.bodyUsed check to verify that upstream.text() can still
be called and returns the original body content 'web body', ensuring the body is
truly independent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/response_js_test.dart`:
- Around line 131-157: The test 'applies init overrides when copying web
responses' verifies that upstream.bodyUsed remains false after consuming the
wrapped response, but it does not confirm that the upstream response is still
readable. Following the pattern used in the equivalent tests for wrapped
responses and native responses, add an assertion after the final
upstream.bodyUsed check to verify that upstream.text() can still be called and
returns the original body content 'web body', ensuring the body is truly
independent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be13088c-2052-4724-95cc-1f5ea381df94

📥 Commits

Reviewing files that changed from the base of the PR and between b1dd60d and 49e8c10.

📒 Files selected for processing (4)
  • lib/src/fetch/response.io.dart
  • lib/src/fetch/response.js.dart
  • test/response_io_test.dart
  • test/response_js_test.dart

@medz

medz commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Accepted the CodeRabbit test enhancement and added the web.Response upstream readability assertion after consuming the copied response.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f99992ab9b

ℹ️ 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".

Comment thread lib/src/fetch/response.io.dart Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 025ce1e328

ℹ️ 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".

Comment thread lib/src/fetch/response.js.dart Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 46f28429a2

ℹ️ 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".

Comment thread lib/src/fetch/response.io.dart Outdated
Comment thread lib/src/fetch/response.js.dart Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: adea8f24f1

ℹ️ 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".

Comment thread lib/src/fetch/response.js.dart
Comment thread lib/src/fetch/response.js.dart Outdated
Comment thread lib/src/fetch/response.io.dart
@medz medz merged commit 530f6cc into main Jun 22, 2026
2 checks passed
@medz medz deleted the fix/response-wrapper-copy-semantics branch June 22, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Define wrapper Response(Response, init) semantics

1 participant