Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compression drops gRPC status trailers #546

Open
1 task done
caffeine-addictt opened this issue Feb 25, 2025 · 1 comment
Open
1 task done

Compression drops gRPC status trailers #546

caffeine-addictt opened this issue Feb 25, 2025 · 1 comment
Labels
C-bug Category: This is a bug.

Comments

@caffeine-addictt
Copy link

  • I have looked for existing issues (including closed) about this

I'm unsure if this is related to #414.

Bug Report

Platform

Linux archlinux 6.13.3-arch1-1 x86_64 GNU/Linux

Crates

tonic = "0.12.3"
tower = { version = "0.5.2", features = ["full"] }
tower-http = { version = "0.6.2", features = ["full"] }

Description

Looks like adding CompressionLayer::new() causes gRPC trailers to get dropped.

More

This gets thrown by the client:

❯ grpcurl ...

...

Response trailers received:
(empty)
Sent 1 request and received 0 responses
ERROR:
  Code: Internal
  Message: server closed the stream without sending trailers

And connection error in the servers gets thrown (sometimes) - maybe specifically this is an issue of grpcurl + tonic?

RUST_LOG=debug.

DEBUG Connection{peer=Server}: h2::codec::framed_write: send frame=Headers { stream_id: StreamId(1), flags: (0x4: END_HEADERS) }
DEBUG Connection{peer=Server}: h2::codec::framed_write: send frame=Data { stream_id: StreamId(1), flags: (0x1: END_STREAM) }
DEBUG Connection{peer=Server}: h2::codec::framed_read: received frame=Ping { ack: false, payload: [2, 4, 16, 16, 9, 14, 7, 7] }
DEBUG Connection{peer=Server}: h2::codec::framed_write: send frame=Ping { ack: true, payload: [2, 4, 16, 16, 9, 14, 7, 7] }
DEBUG tonic::transport::server: failed serving connection: connection error

Expected

Error status trailers should not be dropped by compression.

This is what I get when I remove the .layer(CompressionLayer::new()) line:

❯ grpcurl ...

...

Sent 1 request and received 0 responses
ERROR:
  Code: Unauthenticated
  Message: test error
...
DEBUG Connection{peer=Server}: h2::codec::framed_write: send frame=Headers { stream_id: StreamId(1), flags: (0x5: END_HEADERS | END_STREAM) }

Simple reproduction

Server::builder()
    .layer(CompressionLayer::new()) // just have this layer
    .add_service(...)
    .serve(addr)
    .await?;

Speculation

I have not looked into the implementation internally yet, but I'm assuming compression just drops the whole request if the content body is empty?

@seanmonstar seanmonstar added the C-bug Category: This is a bug. label Feb 25, 2025
@seanmonstar
Copy link
Collaborator

I believe what happens is that the Compression layer wraps the impl Body into a different interface (likely an AsyncRead) that the compression library can use. In that case, since AsyncRead only deals in data bytes, all other frames are dropped, such as the trailers.

One solution could be to make those wrapper types have a slot to store trailers when received, and then once the compression is done, check if trailers are in that slot before saying the body has ended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

2 participants