diff --git a/tower-http/CHANGELOG.md b/tower-http/CHANGELOG.md index 020c0d06..5ac55120 100644 --- a/tower-http/CHANGELOG.md +++ b/tower-http/CHANGELOG.md @@ -21,7 +21,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Fixed -- None. +- **trace:** Correctly identify gRPC requests in default `on_response` callback ([#278]) + +[#278]: https://github.com/tower-rs/tower-http/pull/278 # 0.3.4 (June 06, 2022) diff --git a/tower-http/src/trace/on_response.rs b/tower-http/src/trace/on_response.rs index 09f44e58..573de2e4 100644 --- a/tower-http/src/trace/on_response.rs +++ b/tower-http/src/trace/on_response.rs @@ -264,10 +264,23 @@ impl OnResponse for DefaultOnResponse { fn status(res: &Response) -> Option { use crate::classify::grpc_errors_as_failures::ParsedGrpcStatus; + // gRPC-over-HTTP2 uses the "application/grpc[+format]" content type, and gRPC-Web uses + // "application/grpc-web[+format]" or "application/grpc-web-text[+format]", where "format" is + // the message format, e.g. +proto, +json. + // + // So, valid grpc content types include (but are not limited to): + // - application/grpc + // - application/grpc+proto + // - application/grpc-web+proto + // - application/grpc-web-text+proto + // + // For simplicity, we simply check that the content type starts with "application/grpc". let is_grpc = res .headers() .get(http::header::CONTENT_TYPE) - .map_or(false, |value| value == "application/grpc"); + .map_or(false, |value| { + value.as_bytes().starts_with("application/grpc".as_bytes()) + }); if is_grpc { match crate::classify::grpc_errors_as_failures::classify_grpc_metadata(