xitca_http/util/middleware/
catch_unwind.rs

1//! middleware for catching panic.
2
3use core::any::Any;
4
5use xitca_service::{Service, pipeline::PipelineE};
6
7/// builder for middleware catching panic and unwind it to [`CatchUnwindError`].
8pub struct CatchUnwind;
9
10impl<S, E> Service<Result<S, E>> for CatchUnwind {
11    type Response = service::CatchUnwindService<S>;
12    type Error = E;
13
14    async fn call(&self, arg: Result<S, E>) -> Result<Self::Response, Self::Error> {
15        arg.map(service::CatchUnwindService)
16    }
17}
18
19/// type alias for branched catch unwind error. The First variant is panic message and the
20/// Second variant is Service::Error produced by inner/next service CatchUnwind enclosed.
21pub type CatchUnwindError<E> = PipelineE<Box<dyn Any + Send>, E>;
22
23mod service {
24    use core::panic::AssertUnwindSafe;
25
26    use xitca_service::ready::ReadyService;
27    use xitca_unsafe_collection::futures::CatchUnwind;
28
29    use super::*;
30
31    pub struct CatchUnwindService<S>(pub(super) S);
32
33    impl<S, Req> Service<Req> for CatchUnwindService<S>
34    where
35        S: Service<Req>,
36    {
37        type Response = S::Response;
38        type Error = CatchUnwindError<S::Error>;
39
40        async fn call(&self, req: Req) -> Result<Self::Response, Self::Error> {
41            CatchUnwind::new(AssertUnwindSafe(self.0.call(req)))
42                .await
43                .map_err(CatchUnwindError::First)?
44                .map_err(CatchUnwindError::Second)
45        }
46    }
47
48    impl<S> ReadyService for CatchUnwindService<S>
49    where
50        S: ReadyService,
51    {
52        type Ready = S::Ready;
53
54        #[inline]
55        async fn ready(&self) -> Self::Ready {
56            self.0.ready().await
57        }
58    }
59}