Skip to content

Fix/http encoder stream flush - #4227

Open
matthid wants to merge 3 commits into
actix:mainfrom
matthid:fix/http-encoder-stream-flush
Open

Fix/http encoder stream flush#4227
matthid wants to merge 3 commits into
actix:mainfrom
matthid:fix/http-encoder-stream-flush

Conversation

@matthid

@matthid matthid commented Sep 5, 2026

Copy link
Copy Markdown

The background of this change is that I deploy my services behind cloud flare. Cloud flare has a timeout on stale connections. Usually this is solved with streaming. However once compression is enabled this no longer works hence this patch.
It was created by AI.

AI Generated:

Stream compressors buffer small writes internally: after writing a body chunk, Encoder::poll_next's take() returns nothing and the compressed bytes stay inside the encoder until finish() runs at stream end. A streaming response body (SSE, for example) wrapped in the Compress middleware is therefore delivered as a single burst when the stream completes, not incrementally as chunks are produced — the first chunk only ever carries the bare gzip header.

Flush the encoder whenever an in-place or blocking write produces no output so each body chunk reaches the client as it is produced. Large writes that produce output immediately are unaffected; full-body compression is unchanged apart from the added sync markers.

Regression test gates the body on a channel and asserts compressed bytes for the first events arrive before the gate is released; without the flush it times out because nothing but the gzip header is sent before the stream ends.

PR Type

Bug Fix

PR_TYPE

PR Checklist

  • Tests for the changes have been added / updated.
  • Documentation comments have been added / updated.
  • A changelog entry has been made for the appropriate packages.
  • Format code with the latest stable rustfmt.
  • (Team) Label with affected crates and semver status.

Overview

Stream compressors buffer small writes internally: after writing a body
chunk, Encoder::poll_next's take() returns nothing and the compressed
bytes stay inside the encoder until finish() runs at stream end. A
streaming response body (SSE, for example) wrapped in the Compress
middleware is therefore delivered as a single burst when the stream
completes, not incrementally as chunks are produced — the first chunk
only ever carries the bare gzip header.

Flush the encoder whenever an in-place or blocking write produces no
output so each body chunk reaches the client as it is produced. Large
writes that produce output immediately are unaffected; full-body
compression is unchanged apart from the added sync markers.

Regression test gates the body on a channel and asserts compressed
bytes for the first events arrive before the gate is released; without
the flush it times out because nothing but the gzip header is sent
before the stream ends.
@github-actions github-actions Bot added A-http project: actix-http A-web project: actix-web labels Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-http project: actix-http A-web project: actix-web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant