Skip to content

Add streaming gzip and deflate response decoding - #954

Open
fewensa wants to merge 1 commit into
mainfrom
codeon/fewensa/FWN-372/a2-36d16692c6d9455bb13e2f270d30e204
Open

Add streaming gzip and deflate response decoding#954
fewensa wants to merge 1 commit into
mainfrom
codeon/fewensa/FWN-372/a2-36d16692c6d9455bb13e2f270d30e204

Conversation

@fewensa

@fewensa fewensa commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add incremental gzip and zlib-wrapped deflate decoding to sync and async HTTP/1.1 streaming body readers.
  • Reuse buffered Content-Encoding stack rules: reverse order, skip empty bodies, preserve unsupported or invalid stacks, strip CE/CL only after successful decode, and keep chunked trailers visible after compressed EOF.
  • Enforce decoded-size bounds on read_to_response while leaving direct body_mut reads and the buffered RawResponse path unchanged.

Test plan

  • cargo test -p rttp_client --features async --test test_http_basic --test test_http_async streaming_
  • cargo test -p rttp_client --features async --lib streaming_gzip
  • cargo test -p rttp_client --test test_response content_encoding_runtime
  • cargo test --test response_decoding_transport_matrix

codeon:
  version: 1
  authority: FWN-372
  description: |-
    Share gzip and zlib Content-Encoding stack selection between buffered RawResponse parsing and streaming body readers. Wrap sync and async ResponseBodyReaders with incremental reverse-order flate2 decoding while preserving empty-body skip, trailer visibility, CE/CL stripping only on success, raw headers for unsupported stacks, and decoded-size bounds on read_to_response.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-11T14:36:29.255701Z d942ccf PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d942ccf4e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +174 to +178
self.layers[index]
.write_all(&current)
.map_err(error::decode)?;
current = self.layers[index].take_output().map_err(error::decode)?;
self.record_layer_bytes(index, current.len())?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Enforce decoded limits before allocating layer output

For bounded read_to_response calls, each Vec-backed decoder expands the entire input before record_layer_bytes checks the configured limit. A highly compressible response can therefore allocate far beyond max_buffered_response_body_bytes before returning BodyTooLarge; stacked encodings amplify this because one layer's multi-megabyte output is passed wholesale into the next layer, potentially causing an OOM despite the configured bound. Use a size-limited output sink or otherwise stop decompression as soon as the layer crosses the limit.

Useful? React with 👍 / 👎.

Comment on lines +94 to +98
let mut binary = if self.body.content_decode_succeeded() {
strip_content_encoding_and_length_from_head(&self.head)?
} else {
self.head.clone()
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the original wire capture in Response::binary

When a compressed streaming response is converted with read_to_response, this branch replaces the wire headers and later appends the decoded body, so Response::binary() returns a synthesized decoded message rather than the original capture. This regresses the documented Response::binary() contract in response/mod.rs and differs from the buffered path, which decodes the exposed body while retaining the received bytes; consumers using the binary response for diagnostics, persistence, or signatures will receive altered data.

Useful? React with 👍 / 👎.

Comment on lines +55 to +56
if self.body.content_decode_succeeded() {
strip_content_encoding_and_length(&mut headers);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remove wire encoding headers before decoded reads

For the normal pattern of inspecting headers before consuming the body, content_decode_succeeded() is still false because it only becomes true after compressed EOF, yet body_mut().read() already returns decoded bytes. Consequently headers() reports Content-Encoding and the compressed Content-Length for a decoded stream—and its result changes after EOF—so a consumer honoring those headers can double-decode the body or use an incorrect length. The async implementation has the same condition.

Useful? React with 👍 / 👎.

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