From e683ffef1fcbe0ace9cc696232489f5f6600e83f Mon Sep 17 00:00:00 2001 From: Lucio Franco Date: Thu, 7 May 2020 15:31:26 -0400 Subject: [PATCH] fix: base64 encode details header (#345) --- Cargo.toml | 3 +- tests/integration_tests/Cargo.toml | 21 ++++++++++ tests/integration_tests/build.rs | 3 ++ tests/integration_tests/proto/test.proto | 10 +++++ tests/integration_tests/src/lib.rs | 3 ++ tests/integration_tests/tests/status.rs | 52 ++++++++++++++++++++++++ tonic/src/status.rs | 11 ++++- 7 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 tests/integration_tests/Cargo.toml create mode 100644 tests/integration_tests/build.rs create mode 100644 tests/integration_tests/proto/test.proto create mode 100644 tests/integration_tests/src/lib.rs create mode 100644 tests/integration_tests/tests/status.rs diff --git a/Cargo.toml b/Cargo.toml index 1d036a6..2afbede 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,5 +14,6 @@ members = [ "tests/wellknown", "tests/extern_path/uuid", "tests/ambiguous_methods", - "tests/extern_path/my_application" + "tests/extern_path/my_application", + "tests/integration_tests" ] diff --git a/tests/integration_tests/Cargo.toml b/tests/integration_tests/Cargo.toml new file mode 100644 index 0000000..fbca006 --- /dev/null +++ b/tests/integration_tests/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "integration-tests" +version = "0.1.0" +authors = ["Lucio Franco "] +edition = "2018" +publish = false +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tonic = { path = "../../tonic" } +prost = "0.6" +futures-util = "0.3" +bytes = "0.5" + +[dev-dependencies] +tokio = { version = "0.2", features = ["macros", "rt-core", "tcp"] } + +[build-dependencies] +tonic-build = { path = "../../tonic-build" } diff --git a/tests/integration_tests/build.rs b/tests/integration_tests/build.rs new file mode 100644 index 0000000..a091e94 --- /dev/null +++ b/tests/integration_tests/build.rs @@ -0,0 +1,3 @@ +fn main() { + tonic_build::compile_protos("proto/test.proto").unwrap(); +} diff --git a/tests/integration_tests/proto/test.proto b/tests/integration_tests/proto/test.proto new file mode 100644 index 0000000..98452e9 --- /dev/null +++ b/tests/integration_tests/proto/test.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package test; + +service Test { + rpc UnaryCall(Input) returns (Output); +} + +message Input {} +message Output {} diff --git a/tests/integration_tests/src/lib.rs b/tests/integration_tests/src/lib.rs new file mode 100644 index 0000000..9b06ad2 --- /dev/null +++ b/tests/integration_tests/src/lib.rs @@ -0,0 +1,3 @@ +pub mod pb { + tonic::include_proto!("test"); +} diff --git a/tests/integration_tests/tests/status.rs b/tests/integration_tests/tests/status.rs new file mode 100644 index 0000000..cf750ac --- /dev/null +++ b/tests/integration_tests/tests/status.rs @@ -0,0 +1,52 @@ +use bytes::Bytes; +use futures_util::FutureExt; +use integration_tests::pb::{test_client, test_server, Input, Output}; +use std::time::Duration; +use tokio::sync::oneshot; +use tonic::{transport::Server, Code, Request, Response, Status}; + +#[tokio::test] +async fn status_with_details() { + struct Svc; + + #[tonic::async_trait] + impl test_server::Test for Svc { + async fn unary_call(&self, _: Request) -> Result, Status> { + Err(Status::with_details( + Code::ResourceExhausted, + "Too many requests", + Bytes::from_static(&[1]), + )) + } + } + + let svc = test_server::TestServer::new(Svc); + + let (tx, rx) = oneshot::channel::<()>(); + + let jh = tokio::spawn(async move { + Server::builder() + .add_service(svc) + .serve_with_shutdown("127.0.0.1:1337".parse().unwrap(), rx.map(drop)) + .await + .unwrap(); + }); + + tokio::time::delay_for(Duration::from_millis(100)).await; + + let mut channel = test_client::TestClient::connect("http://127.0.0.1:1337") + .await + .unwrap(); + + let err = channel + .unary_call(Request::new(Input {})) + .await + .unwrap_err(); + + assert_eq!(err.message(), "Too many requests"); + assert_eq!(err.details(), &[1]); + + tx.send(()).unwrap(); + + jh.await.unwrap(); +} diff --git a/tonic/src/status.rs b/tonic/src/status.rs index 90ea9ee..9ee6c73 100644 --- a/tonic/src/status.rs +++ b/tonic/src/status.rs @@ -334,9 +334,14 @@ impl Status { .map(|cow| cow.to_string()) }) .unwrap_or_else(|| Ok(String::new())); + let details = header_map .get(GRPC_STATUS_DETAILS_HEADER) - .map(|h| Bytes::copy_from_slice(h.as_bytes())) + .map(|h| { + base64::decode(h.as_bytes()) + .expect("Invalid status header, expected base64 encoded value") + }) + .map(Bytes::from) .unwrap_or_else(Bytes::new); match error_message { Ok(message) => Status { @@ -404,10 +409,12 @@ impl Status { /// Create a new `Status` with the associated code, message, and binary details field. pub fn with_details(code: Code, message: impl Into, details: Bytes) -> Status { + let details = base64::encode_config(&details[..], base64::STANDARD_NO_PAD); + Status { code, message: message.into(), - details: details, + details: details.into(), } } }