Skip to content

lib,test: fix resuming channel writes after window adjustment - #1525

Open
shuxueshuxue wants to merge 1 commit into
mscdex:masterfrom
shuxueshuxue:fix-window-resume
Open

shuxueshuxue wants to merge 1 commit into
mscdex:masterfrom
shuxueshuxue:fix-window-resume

Conversation

@shuxueshuxue

Copy link
Copy Markdown

When a channel write has to wait for the remote window, the remaining data is stored on the channel (_chunk for stdout, _chunkErr for stderr) and written when the next CHANNEL_WINDOW_ADJUST arrives. The resume code in both lib/client.js and lib/server.js has two problems:

  1. _chunk is never cleared after it is resumed. If stderr later has to wait for the window, _waitWindow is set again, and the next adjustment finds the old _chunk first and writes it again. The stdout stream then contains duplicated bytes, and its callback runs a second time.
  2. Only one of stdout/stderr is resumed per adjustment (if (_chunk) … else if (_chunkErr) …), so a stderr write waiting behind stdout is never resumed.

We hit this with a server-side exec channel carrying a length-prefixed binary protocol on stdout. Occasionally the client's decoder read a frame header from the middle of a payload, and stderr output never arrived.

Reproduction

The test added to test/test-exec.js does this:

  • The server writes 3 MiB to stdout. That is more than the client's default window, so the write waits once and is then resumed.
  • 100 ms later it writes 3 MiB to stderr. That also waits for the window, while no stdout write is pending.
  • Then it writes another 1 MiB to stdout.
  • The client pauses the stream for 300 ms at the start, so the window really runs out.

Before this change the test times out: stderr is never delivered, and stdout does not match what was written. With this change, stdout and stderr arrive intact.

Fix

Take both pending writes off the channel before resuming them, then resume stdout and stderr.

npm test and npm run lint pass.

When a channel write had to wait for the remote window, the remaining
data was kept in `_chunk` (or `_chunkErr` for stderr) and written on the
next CHANNEL_WINDOW_ADJUST. Two problems with that:

* `_chunk` was never cleared after it was resumed, so if stderr later
  had to wait for the window, the next adjustment wrote the old stdout
  chunk again instead of resuming stderr. The stdout stream then
  contains duplicated bytes.
* Only one of stdout/stderr was resumed per adjustment, so a stderr
  write waiting behind stdout was never resumed.

Take both pending writes off the channel before resuming them, and
resume stdout and stderr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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