tonic/transport/server/
recover_error.rs1use crate::{
2 util::{OptionPin, OptionPinProj},
3 Status,
4};
5use futures_util::ready;
6use http::Response;
7use pin_project::pin_project;
8use std::{
9 future::Future,
10 pin::Pin,
11 task::{Context, Poll},
12};
13use tower::Service;
14
15#[derive(Debug, Clone)]
18pub(crate) struct RecoverError<S> {
19 inner: S,
20}
21
22impl<S> RecoverError<S> {
23 pub(crate) fn new(inner: S) -> Self {
24 Self { inner }
25 }
26}
27
28impl<S, R, ResBody> Service<R> for RecoverError<S>
29where
30 S: Service<R, Response = Response<ResBody>>,
31 S::Error: Into<crate::Error>,
32{
33 type Response = Response<MaybeEmptyBody<ResBody>>;
34 type Error = crate::Error;
35 type Future = ResponseFuture<S::Future>;
36
37 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
38 self.inner.poll_ready(cx).map_err(Into::into)
39 }
40
41 fn call(&mut self, req: R) -> Self::Future {
42 ResponseFuture {
43 inner: self.inner.call(req),
44 }
45 }
46}
47
48#[pin_project]
49pub(crate) struct ResponseFuture<F> {
50 #[pin]
51 inner: F,
52}
53
54impl<F, E, ResBody> Future for ResponseFuture<F>
55where
56 F: Future<Output = Result<Response<ResBody>, E>>,
57 E: Into<crate::Error>,
58{
59 type Output = Result<Response<MaybeEmptyBody<ResBody>>, crate::Error>;
60
61 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
62 let result: Result<Response<_>, crate::Error> =
63 ready!(self.project().inner.poll(cx)).map_err(Into::into);
64
65 match result {
66 Ok(response) => {
67 let response = response.map(MaybeEmptyBody::full);
68 Poll::Ready(Ok(response))
69 }
70 Err(err) => match Status::try_from_error(err) {
71 Ok(status) => {
72 let mut res = Response::new(MaybeEmptyBody::empty());
73 status.add_header(res.headers_mut()).unwrap();
74 Poll::Ready(Ok(res))
75 }
76 Err(err) => Poll::Ready(Err(err)),
77 },
78 }
79 }
80}
81
82#[pin_project]
83pub(crate) struct MaybeEmptyBody<B> {
84 #[pin]
85 inner: OptionPin<B>,
86}
87
88impl<B> MaybeEmptyBody<B> {
89 fn full(inner: B) -> Self {
90 Self {
91 inner: OptionPin::Some(inner),
92 }
93 }
94
95 fn empty() -> Self {
96 Self {
97 inner: OptionPin::None,
98 }
99 }
100}
101
102impl<B> http_body::Body for MaybeEmptyBody<B>
103where
104 B: http_body::Body + Send,
105{
106 type Data = B::Data;
107 type Error = B::Error;
108
109 fn poll_data(
110 self: Pin<&mut Self>,
111 cx: &mut Context<'_>,
112 ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
113 match self.project().inner.project() {
114 OptionPinProj::Some(b) => b.poll_data(cx),
115 OptionPinProj::None => Poll::Ready(None),
116 }
117 }
118
119 fn poll_trailers(
120 self: Pin<&mut Self>,
121 cx: &mut Context<'_>,
122 ) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
123 match self.project().inner.project() {
124 OptionPinProj::Some(b) => b.poll_trailers(cx),
125 OptionPinProj::None => Poll::Ready(Ok(None)),
126 }
127 }
128
129 fn is_end_stream(&self) -> bool {
130 match &self.inner {
131 OptionPin::Some(b) => b.is_end_stream(),
132 OptionPin::None => true,
133 }
134 }
135}