Skip to content

stream: preserve mutable chunks in Web Stream adapters - #64579

Open
seungwoo505 wants to merge 4 commits into
nodejs:mainfrom
seungwoo505:fix-writable-to-web-write
Open

seungwoo505 wants to merge 4 commits into
nodejs:mainfrom
seungwoo505:fix-writable-to-web-write

Conversation

@seungwoo505

@seungwoo505 seungwoo505 commented Jul 18, 2026 •

Copy link
Copy Markdown

When a Node.js writable is converted with Writable.toWeb(), the Web Streams
write() promise can currently settle as soon as the native write() call
returns true. That return value only represents backpressure, so the native
stream may still retain the supplied mutable BufferSource. Reusing the buffer
after awaiting the Web write can therefore change bytes that have not yet been
consumed.

This change:

  • waits for the native per-write callback when the stream is an uncorked,
    unmodified Writable;
  • coordinates callback completion with drain, aborts, and native stream
    errors;
  • passes a private same-brand BufferSource copy for Duplex, corked, overridden,
    and legacy write paths where waiting for a callback would change existing
    completion behavior;
  • preserves HTTP input validation before fallback copies; and
  • preserves SharedArrayBuffer backing for cloned views.

The original native Writable.prototype.write and
OutgoingMessage.prototype.write methods are captured so patched methods and
accessors are classified and invoked consistently.

Validation included:

  • make -j4 test (full test suite passed)
  • the changed Web Streams adapter, Duplex, and compression tests
  • the existing Writable, Duplex, and Web Streams adapter test set
  • CompressionStream WPT tests
  • repeated async regression tests
  • ESLint and git diff --check

Fixes: #64549

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net
  • @nodejs/streams

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Jul 18, 2026
@seungwoo505
seungwoo505 marked this pull request as ready for review July 18, 2026 16:14
Comment thread lib/_http_outgoing.js Outdated
@@ -1260,4 +1262,5 @@ module.exports = {
validateHeaderName,
validateHeaderValue,
OutgoingMessage,
outgoingMessagePrototypeWrite,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why expose this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The Web Streams adapter uses this export to compare the active write() method with the original OutgoingMessage.prototype.write.
This distinction preserves the native validation behavior without applying it to overridden write() implementations.
The reference is captured here so a later prototype monkey-patch is not mistaken for the original method.

@seungwoo505
seungwoo505 force-pushed the fix-writable-to-web-write branch from 97f5318 to c08321a Compare July 27, 2026 12:18
@seungwoo505

seungwoo505 commented Jul 27, 2026 •

Copy link
Copy Markdown
Author

Hi @bjohansebas, just a friendly follow-up on this.
I’ve answered the question above and rebased the PR onto the latest main, resolving the merge conflict.
The build, relevant Web Streams tests, and ESLint all pass locally.
When you have time, could you please take another look?
Thank you!

@bjohansebas bjohansebas added stream Issues and PRs related to Node.js streams. web streams Issues and PRs related to the Web Streams API. labels Jul 27, 2026
@mcollina

Copy link
Copy Markdown
Member

Sorry for the radio silence. Can you rebase again?

@seungwoo505

Copy link
Copy Markdown
Author

Sorry, I just saw your message.
I’ll rebase it by the end of today

Wait for native write callbacks when they can safely represent chunk
consumption. For Duplex streams, corked writes, and custom or legacy
write methods, pass private BufferSource copies to preserve completion
timing.

Coordinate callback completion with backpressure, aborts, and stream
errors so a settled Web Streams write no longer exposes mutable bytes
still retained by the native stream.

Preserve native HTTP validation and SharedArrayBuffer backing when
fallback copies are required.

Signed-off-by: seungwoo <zoozoo1302@gmail.com>
Cover native callback completion, fallback copies, aborts, and error
propagation for mutable BufferSource chunks passed to Node.js Web
Streams adapters.

Verify HTTP validation, SharedArrayBuffer backing, and Duplex and
compression paths.

Signed-off-by: seungwoo <zoozoo1302@gmail.com>
Rely on native stream validation after copying array buffer views.
Keep object-mode writes on their existing completion timing, and remove
adapter-only HTTP detection and prototype capture.

Signed-off-by: seungwoo <zoozoo1302@gmail.com>
Remove overlapping override coverage. Verify object-mode writes settle
independently of native callbacks. Exercise the Writable.toWeb(Duplex)
path and keep native HTTP validation coverage.

Assisted-by: Codex
Signed-off-by: seungwoo <zoozoo1302@gmail.com>
@seungwoo505
seungwoo505 force-pushed the fix-writable-to-web-write branch from c08321a to 062b9a0 Compare September 28, 2026 10:50
@seungwoo505

seungwoo505 commented Sep 28, 2026 •

Copy link
Copy Markdown
Author

Hi @mcollina, I’ve rebased the PR onto the latest main and resolved the conflicts.
git diff --check, make -j4, make test, the relevant Web Streams tests, and the targeted JavaScript lint checks all pass locally.
Could you please take a look when you have a chance?
Thanks!

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

Labels

lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. stream Issues and PRs related to Node.js streams. web streams Issues and PRs related to the Web Streams API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

stream: Writable.toWeb()/Duplex.toWeb() settles write() before a mutable chunk is consumed

4 participants