Skip to content

fix(peer): stop the request body upload on an early stream/cancel - #129

Merged
dinwwwh merged 1 commit into
mainfrom
claude/early-stream-cancel-ignored-500b8f
Sep 29, 2026
Merged

dinwwwh merged 1 commit into
mainfrom
claude/early-stream-cancel-ignored-500b8f

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Sep 29, 2026

Copy link
Copy Markdown
Member

When the server cancelled a streamed request body while the client was still sending the request message, ClientPeer ignored the stream/cancel and then uploaded the whole body anyway. The server threw every chunk away. For a long or endless body, the upload kept running until the request ended. The client now remembers an early stream/cancel and cancels the body instead of sending it.

This only happens on transports whose send resolves late, for example one that waits for the other side to acknowledge the message, and only when the request stays open after the cancel (for example, with a streamed response).

Fixes

  • An early stream/cancel is no longer lost: no body chunks are sent and the local body is cancelled.
  • Covers both octet-stream and event-stream request bodies.

Testing

  • Unit tests for both body types deliver stream/cancel while the request is being sent. They check that the body is cancelled and only the request message goes out.
  • An integration test covers the same case through the real ServerPeer and message encoding.
  • All three tests fail without the fix. All 255 peer tests pass, and lint and type-check are clean.
  • The connect() test helper gained a waitForRemote option. The existing "waits for full remote processing" test now uses it instead of its own copy of the connection code.

🤖 Generated with Claude Code

A stream/cancel that arrived while the request message was still being
sent found no transmitter yet and was dropped, so the client then
uploaded the whole request body and the server discarded it. ClientPeer
now remembers the cancel and cancels the body instead of transmitting it.

Co-Authored-By: Claude <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Sep 29, 2026

Copy link
Copy Markdown
@standard-server/aws-lambda

npm i https://pkg.pr.new/@standard-server/aws-lambda@129

@standard-server/core

npm i https://pkg.pr.new/@standard-server/core@129

@standard-server/fastify

npm i https://pkg.pr.new/@standard-server/fastify@129

@standard-server/fetch

npm i https://pkg.pr.new/@standard-server/fetch@129

@standard-server/node

npm i https://pkg.pr.new/@standard-server/node@129

@standard-server/peer

npm i https://pkg.pr.new/@standard-server/peer@129

@standard-server/shared

npm i https://pkg.pr.new/@standard-server/shared@129

commit: 55e1878

@codspeed

codspeed Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 108 skipped benchmarks1


Comparing claude/early-stream-cancel-ignored-500b8f (55e1878) with main (28db1dd)

Open in CodSpeed

Footnotes

  1. 108 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

  • An early stream/cancel is now remembered — ClientPeer records state.streamCancelled in message() and, after the request-message send resolves, transmitRequest() returns before clearing untransmittedBody or constructing a transmitter, so the finally releases the body via cancelStandardBody and no chunks go out.
  • Both streamed body kinds covered — the unit tests exercise octet-stream and event-stream request bodies, injecting stream/cancel from inside the request send.
  • connect() gains waitForRemote — each wire send can await the remote message(), so replies arrive while the send is in flight; the pre-existing late-send test now reuses the helper instead of its own copy.
  • Integration coverage — a ServerPeer/codec round-trip where the handler cancels the request body mid-send.

Verified locally: all 255 peer tests pass, type:check is clean, and reverting the two client.ts hunks makes all three new tests fail. The assertions are exact (send.mock.calls.map(...).toEqual(['request'])), so they genuinely pin the behavior rather than absorbing whatever is sent.

Pullfrog  | View workflow run | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

@dinwwwh
dinwwwh merged commit 7d2294c into main Sep 29, 2026
9 of 10 checks passed
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.

1 participant