fix(codec): Fix streaming reponses w/ many status (#689)

Closes #681
This commit is contained in:
Lucio Franco
2021-07-01 10:25:43 -04:00
committed by GitHub
parent 2b60a00614
commit 737ace393d
8 changed files with 100 additions and 6 deletions
+5 -1
View File
@@ -258,7 +258,11 @@ impl<T> Stream for Streaming<T> {
match ready!(Pin::new(&mut self.body).poll_trailers(cx)) {
Ok(trailer) => {
if let Err(e) = crate::status::infer_grpc_status(trailer.as_ref(), status) {
return Some(Err(e)).into();
if let Some(e) = e {
return Some(Err(e)).into();
} else {
return Poll::Ready(None);
}
} else {
self.trailers = trailer.map(MetadataMap::from_headers);
}
+10 -1
View File
@@ -88,6 +88,7 @@ pub(crate) struct EncodeBody<S> {
inner: S,
error: Option<Status>,
role: Role,
is_end_stream: bool,
}
impl<S> EncodeBody<S>
@@ -99,6 +100,7 @@ where
inner,
error: None,
role: Role::Client,
is_end_stream: false,
}
}
@@ -107,6 +109,7 @@ where
inner,
error: None,
role: Role::Server,
is_end_stream: false,
}
}
}
@@ -119,7 +122,7 @@ where
type Error = Status;
fn is_end_stream(&self) -> bool {
false
self.is_end_stream
}
fn poll_data(
@@ -148,7 +151,13 @@ where
Role::Client => Poll::Ready(Ok(None)),
Role::Server => {
let self_proj = self.project();
if *self_proj.is_end_stream {
return Poll::Ready(Ok(None));
}
let status = if let Some(status) = self_proj.error.take() {
*self_proj.is_end_stream = true;
status
} else {
Status::new(Code::Ok, "")
+10 -3
View File
@@ -657,13 +657,13 @@ impl Error for Status {
pub(crate) fn infer_grpc_status(
trailers: Option<&HeaderMap>,
status_code: http::StatusCode,
) -> Result<(), Status> {
) -> Result<(), Option<Status>> {
if let Some(trailers) = trailers {
if let Some(status) = Status::from_header_map(&trailers) {
if status.code() == Code::Ok {
return Ok(());
} else {
return Err(status);
return Err(status.into());
}
}
}
@@ -678,6 +678,13 @@ pub(crate) fn infer_grpc_status(
| http::StatusCode::BAD_GATEWAY
| http::StatusCode::SERVICE_UNAVAILABLE
| http::StatusCode::GATEWAY_TIMEOUT => Code::Unavailable,
// We got a 200 but no trailers, we can infer that this request is finished.
//
// This can happen when a streaming response sends two Status but
// gRPC requires that we end the stream after the first status.
//
// https://github.com/hyperium/tonic/issues/681
http::StatusCode::OK => return Err(None),
_ => Code::Unknown,
};
@@ -686,7 +693,7 @@ pub(crate) fn infer_grpc_status(
status_code.as_u16(),
);
let status = Status::new(code, msg);
Err(status)
Err(status.into())
}
// ===== impl Code =====