From bfab56d42d776f1cef8a2e82f5eeb13c143130bc Mon Sep 17 00:00:00 2001 From: Lucio Franco Date: Wed, 28 Aug 2019 20:09:43 -0400 Subject: [PATCH] Add trailing metadata and fix test --- tonic-interop/src/client.rs | 12 +++++-- tonic-interop/test.sh | 4 +-- tonic/src/codec/decode.rs | 64 ++++++++++++++++++++++++------------- tonic/src/status.rs | 2 +- 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/tonic-interop/src/client.rs b/tonic-interop/src/client.rs index 7d46f57..064a194 100644 --- a/tonic-interop/src/client.rs +++ b/tonic-interop/src/client.rs @@ -418,6 +418,7 @@ pub async fn custom_metadata(client: &mut Client, assertions: &mut Vec { state: State, direction: Direction, buf: BytesMut, + trailers: Option, } impl Unpin for Streaming {} @@ -44,14 +45,7 @@ impl Streaming { B::Error: Into, D: Decoder + Send + 'static, { - Self { - decoder: Box::new(decoder), - body: BoxBody::map_from(body), - state: State::ReadHeader, - direction: Direction::Response(status_code), - // FIXME: update this with a reasonable size - buf: BytesMut::with_capacity(1024 * 1024), - } + Self::new(decoder, body, Direction::Response(status_code)) } pub fn new_empty(decoder: D, body: B) -> Self @@ -61,17 +55,19 @@ impl Streaming { B::Error: Into, D: Decoder + Send + 'static, { - Self { - decoder: Box::new(decoder), - body: BoxBody::map_from(body), - state: State::ReadHeader, - direction: Direction::EmptyResponse, - // FIXME: update this with a reasonable size - buf: BytesMut::with_capacity(1024 * 1024), - } + Self::new(decoder, body, Direction::EmptyResponse) } pub fn new_request(decoder: D, body: B) -> Self + where + B: Body + Send + 'static, + B::Data: Into, + B::Error: Into, + D: Decoder + Send + 'static, + { + Self::new(decoder, body, Direction::Request) + } + fn new(decoder: D, body: B, direction: Direction) -> Self where B: Body + Send + 'static, B::Data: Into, @@ -82,23 +78,45 @@ impl Streaming { decoder: Box::new(decoder), body: BoxBody::map_from(body), state: State::ReadHeader, - direction: Direction::Request, + direction, // FIXME: update this with a reasonable size buf: BytesMut::with_capacity(1024 * 1024), + trailers: None, } } } impl Streaming { - // pub async fn message(&mut self) -> Option> { - // future::poll_fn(|cx| Pin::new(&mut *self).poll_next(cx)).await - // } + pub async fn message(&mut self) -> Option> { + future::poll_fn(|cx| Pin::new(&mut *self).poll_next(cx)).await + } - pub async fn trailers(&mut self) -> Result, Status> { + pub async fn trailers(mut self) -> Result, Status> { + // Shortcut to see if we already pulled the trailers in the stream step + // we need to do that so that the stream can error on trailing grpc-status + if let Some(trailers) = self.trailers { + return Ok(Some(trailers)); + } + + // To fetch the trailers we must clear the body and drop it. + while let Some(res) = self.message().await { + res?; + } + + // Since we call poll_trailers internally on poll_next we need to + // check if it got cached again. + if let Some(trailers) = self.trailers { + return Ok(Some(trailers)); + } + + + // Trailers were not caught during poll_next and thus lets poll for + // them manually. let map = future::poll_fn(|cx| unsafe { Pin::new_unchecked(&mut self.body) }.poll_trailers(cx)) .await .map_err(|e| Status::from_error(&e))?; + Ok(map.map(MetadataMap::from_headers)) } @@ -205,8 +223,10 @@ impl Stream for Streaming { if let Direction::Response(status) = self.direction { match ready!(unsafe { Pin::new_unchecked(&mut self.body) }.poll_trailers(cx)) { Ok(trailer) => { - if let Err(e) = crate::status::infer_grpc_status(trailer, status) { + if let Err(e) = crate::status::infer_grpc_status(trailer.as_ref(), status) { return Some(Err(e)).into(); + } else { + self.trailers = trailer.map(MetadataMap::from_headers); } } Err(e) => { diff --git a/tonic/src/status.rs b/tonic/src/status.rs index 49ec386..519b75f 100644 --- a/tonic/src/status.rs +++ b/tonic/src/status.rs @@ -312,7 +312,7 @@ impl Error for Status {} /// Take the `Status` value from `trailers` if it is available, else from `status_code`. /// pub(crate) fn infer_grpc_status( - trailers: Option, + trailers: Option<&HeaderMap>, status_code: http::StatusCode, ) -> Result<(), Status> { if let Some(trailers) = trailers {