Skip to content

fix(codec): respect server's enabled encodings when selecting response compression#2655

Open
kv-cache wants to merge 1 commit into
grpc:masterfrom
kv-cache:fix/accept-encoding-respects-enabled
Open

fix(codec): respect server's enabled encodings when selecting response compression#2655
kv-cache wants to merge 1 commit into
grpc:masterfrom
kv-cache:fix/accept-encoding-respects-enabled

Conversation

@kv-cache
Copy link
Copy Markdown

@kv-cache kv-cache commented May 25, 2026

Motivation

CompressionEncoding::from_accept_encoding_header selects a response compression encoding based purely on the client's grpc-accept-encoding header and the compile-time feature set, not the runtime set of encodings the server enabled via Server::send_compressed(...).

When the server has only zstd enabled but the client advertises gzip,zstd,identity, tonic compresses the response with gzip, which is an encoding the server never opted into and would not have advertised back.

The sibling from_encoding_header (used for request decompression) already has the correct if enabled_encodings.is_enabled(...) guards per match arm. This PR brings the accept-side in line.

Observed in production with tonic 0.14.6: server configured send_compressed(Zstd) only, ~38% of server CPU in miniz_oxide::deflate. Worked around downstream with a tower layer that rewrites the incoming grpc-accept-encoding header to "zstd,identity" to force the buggy selection onto the correct path. After the fix, that work-around will no longer be needed.

The bug affects all four RPC kinds — from_accept_encoding_header is called from unary / server_streaming / client_streaming / streaming in tonic/src/server/grpc.rs.

Solution

Add a runtime is_enabled guard to each match arm of from_accept_encoding_header so the function returns the first encoding in the intersection of (server-enabled, client-accepted), preserving the client's preference order. Returns None if the intersection is empty so the server falls back to identity-encoded responses.

Adds three unit tests in mod tests of tonic/src/codec/compression.rs:

Test Server enabled Client grpc-accept-encoding Expected
from_accept_encoding_header_respects_server_enabled_encodings Zstd gzip,zstd,identity Some(Zstd)
from_accept_encoding_header_returns_none_when_no_overlap Zstd gzip,identity None
from_accept_encoding_header_uses_client_preference_order Zstd, Gzip gzip,zstd,identity Some(Gzip)

All three unit tests fail on master without the code fix; all pass with it. Existing tonic unit tests (79) and compression integration tests (63) continue to pass.

Backport note

This fix applies cleanly. Flagging that the v0.14.x branch head appears to be behind the tonic-v0.14.6 tag (latest commit there is chore: prepare v0.14.5 release), so I targeted master like v0.14.6 did. Happy to open a parallel PR against v0.14.x if maintainers want a backport.

@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented May 25, 2026

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: kv-cache / name: Vineeth Rao Kanaparthi (155107d)

…e compression

`CompressionEncoding::from_accept_encoding_header` picks an encoding
from the client's `grpc-accept-encoding` header gated only on
`cfg(feature = ...)`, not on whether the server actually enabled that
encoding via `.send_compressed(...)`. A server configured for Zstd
would gzip every response whenever a client listed `gzip` before
`zstd` in `grpc-accept-encoding` -- even though the server never
asked for gzip and would not have advertised it in
`into_accept_encoding_header_value`. The sibling
`from_encoding_header` already guards each match arm on
`enabled_encodings.is_enabled(...)`; this fix brings the accept-side
in line.

Observed in production: tonic 0.14.6 server set to
`send_compressed(Zstd)` only, clients sending `gzip,zstd,identity`,
~38% of server CPU spent in `miniz_oxide::deflate`.

Adds three unit tests covering:
  * server-only-Zstd, client lists gzip first  -> picks Zstd
  * no overlap between server and client lists -> picks None
  * both enabled, client prefers gzip          -> picks Gzip
@kv-cache kv-cache force-pushed the fix/accept-encoding-respects-enabled branch from 396063a to 155107d Compare May 25, 2026 20:47
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