Skip to content

fix(remote): deliver a line the inflater still holds - #537

Merged
Tryanks merged 1 commit into
mainfrom
fix/remote-inflate-stall
Sep 24, 2026
Merged

Tryanks merged 1 commit into
mainfrom
fix/remote-inflate-stall

Conversation

@Tryanks

@Tryanks Tryanks commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Behaviour

Over iroh (a phone attached to a Mac), a highly compressible protocol line could be held back until the peer sent its next line. Observed: the phone's Settings → Usage refresh kept spinning although the host finished the usage check in ~3 s and emitted ProvidersReplaced with the check cleared.

Cause: LineStream::read_line (added in #514's raw-deflate stream) called decompress_vec once per fill_buf. When the line expands far beyond compressed.len() * 4 + 1024, the output buffer fills while every input byte is already consumed; the rest of the line, newline included, stays inside the inflater, and read_line goes back to waiting on the network.

Fix: keep inflating the chunk until the output buffer has spare room — the same termination rule the writer's deflate helper already uses. The loop is bounded by the chunk fill_buf returned, so no separate size cap is needed inside it; the existing max check still runs before the next read.

Both directions share LineStream (host.rs bridge and client.rs relay), so both are fixed. No other streaming decompression exists in the workspace (the ACP registry gunzips a complete download).

Structure

  • LineStream<R = LineReader> is generic over AsyncBufRead so the test can feed it from an in-memory duplex; callers are unchanged.
  • compress_line is the writer's per-line encoding, extracted so the test produces exactly what LineWriter sends.

Test

a_highly_compressible_line_arrives_without_waiting_for_the_next: one complete compressed line arrives and the peer keeps the stream open. Contract: the line is returned without further input. Without the fix it times out after 5 s (verified); with it, it passes.

Checks run

  • cargo test -p tcode-traverse — pass
  • cargo fmt --all --check — pass
  • cargo clippy -p tcode-traverse --all-targets --all-features --locked -- -D warnings — pass
  • Full workspace, mobile and Web checks left to CI. Not verified on a phone yet; both the phone and the Mac need a build with this fix.

A highly compressible line (a ProvidersReplaced that repeats the model
catalogs, for example) could fill LineStream's output buffer while every
input byte was already consumed, leaving the rest of the line, newline
included, inside the inflater. read_line then waited on the network, so the
line only surfaced when the peer sent another one: a phone showed the Usage
refresh spinning long after the host had finished the check.

Keep inflating until the output buffer has spare room, the same rule the
writer already uses for the deflater.
@Tryanks
Tryanks merged commit eaee07e into main Sep 24, 2026
7 checks passed
@Tryanks
Tryanks deleted the fix/remote-inflate-stall branch September 24, 2026 06:53
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