hyperium / tonic

A native gRPC client & server implementation with async/await support.
https://docs.rs/tonic
MIT License
9.76k stars 997 forks source link

"Invalid Compression Flag: 10" error with custom channel type #1040

Closed stuqdog closed 2 years ago

stuqdog commented 2 years ago

env

tonic v0.7.2 tonic-build v0.7.2 mac os 12.2

Hiya! I'm running into an issue trying to implement the call() method for a custom channel type. I am creating my own http response based on the return value from a sender, which is then boxed. In relevant part:

fn call(&mut self, request: http::Request<BoxBody>) -> Self::Future {
    let fut = async move {
        let recv = self.message_receiver.recv()?; // this is a vec<u8> representation of a valid proto type

        http::response::Response::builder()
            .status(StatusCode::Ok)
            .header("content-type", "application/grpc+tonic")
            .version(Version::HTTP_2)
            .body(Body::from(recv))
    };
    Box::pin(fut)
}

I would have expected this to work fine (there are no compilation issues), but in testing I get the following error message:

Error: status: Internal, message: "protocol error: received message with invalid compression flag: 10 (valid flags are 0 and 1) while receiving response with status: 200 OK", details: [], metadata: MetadataMap { headers: {"content-type": "application/grpc+tonic"} }

I saw a similar issue arising in a recently filed bug report (https://github.com/hyperium/tonic/issues/1029) but the solution in that case doesn't seem like it would work for me. I am not defining my own codec or encoding/decoding, but rather using auto-generated proto methods that rely on ProstCodec. The auto-generated methods all work as expected when using tonic's default channel type.

Any help understanding this or how to solve it would be greatly appreciated!

stuqdog commented 2 years ago

Resolved. For folks who run into a similar issue: the body of the http message must start with one byte (0 or 1) that indicates whether or not the message is compressed, and then 4 bytes indicating the remaining length of the message. Example code:

let mut message_buf = vec![0u8];
message_buf.write_u32::<BigEndian>(len).unwrap();
message_buf.append(&mut recv);