Skip to main content

xitca_web/middleware/
context.rs

1use crate::service::Service;
2
3/// middleware for bridging `xitca-http` and `xitca-web` types. useful for utilizing xitca-web
4/// when manually constructing http services.
5///
6/// # Examples
7/// ```rust
8/// use xitca_http::util::{
9///     middleware::context::ContextBuilder,
10///     service::{handler::handler_service, route::get, Router}
11/// };
12/// use xitca_service::{Service, ServiceExt};
13/// use xitca_web::{error::Error, handler::state::StateRef};
14///
15/// // handler function depending on xitca-web types.
16/// async fn handler(StateRef(_): StateRef<'_, ()>) -> Error {
17///     xitca_web::error::ErrorStatus::bad_request().into()
18/// }
19///
20/// # async fn test() {
21/// // low level router from xitca-http
22/// let router = Router::new()
23///     // utilize handler function with xitca-web types
24///     .insert("/", get(handler_service(handler)))
25///     // utilize xitca-web's middleware to xitca-http's router
26///     .enclosed(xitca_web::middleware::CatchUnwind)
27///     // converting xitca-http types to xitca-web types with web context middleware.
28///     .enclosed(xitca_web::middleware::WebContext)
29///     // xitca-http's context builder middleware is also needed for helping type conversion.
30///     // this middleware is tasked with constructing typed application state and pass down it
31///     // to xitca_web's WebContext middleware. In case of absence of application state a tuple
32///     // is constructed to act as the default type.
33///     .enclosed(ContextBuilder::new(|| async { Ok::<_, std::convert::Infallible>(()) }));
34///
35/// // call the router service and observe the output. in real world the service caller should be a http
36/// // library and server.
37/// let res = router
38///     .call(())
39///     .await
40///     .unwrap()
41///     .call(Default::default())
42///     .await
43///     .unwrap();
44///
45/// assert_eq!(res.status(), xitca_http::http::StatusCode::BAD_REQUEST);
46/// # }
47/// ```
48pub struct WebContext;
49
50impl<S, E> Service<Result<S, E>> for WebContext {
51    type Response = service::WebContextService<S>;
52    type Error = E;
53
54    async fn call(&self, res: Result<S, E>) -> Result<Self::Response, Self::Error> {
55        res.map(|service| service::WebContextService { service })
56    }
57}
58
59mod service {
60    use core::{cell::RefCell, convert::Infallible};
61
62    use xitca_http::util::middleware::context::Context;
63
64    use crate::{
65        body::{Either, ResponseBody},
66        context::WebContext,
67        http::{WebRequest, WebResponse},
68        service::{Service, ready::ReadyService},
69    };
70
71    pub struct WebContextService<S> {
72        pub(super) service: S,
73    }
74
75    type EitherResBody<B> = Either<B, ResponseBody>;
76
77    impl<'c, S, C, ResB, SE> Service<Context<'c, WebRequest, C>> for WebContextService<S>
78    where
79        S: for<'r> Service<WebContext<'r, C>, Response = WebResponse<ResB>, Error = SE>,
80        SE: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible>,
81    {
82        type Response = WebResponse<EitherResBody<ResB>>;
83        type Error = Infallible;
84
85        async fn call(&self, ctx: Context<'c, WebRequest, C>) -> Result<Self::Response, Self::Error> {
86            let (req, state) = ctx.into_parts();
87            let (parts, ext) = req.into_parts();
88            let (ext, body) = ext.replace_body(());
89            let mut req = WebRequest::from_parts(parts, ext);
90            let mut body = RefCell::new(body);
91            let mut ctx = WebContext::new(&mut req, &mut body, state);
92
93            match self.service.call(ctx.reborrow()).await {
94                Ok(res) => Ok(res.map(Either::left)),
95                Err(e) => e.call(ctx).await.map(|res| res.map(Either::right)),
96            }
97        }
98    }
99
100    impl<S> ReadyService for WebContextService<S>
101    where
102        S: ReadyService,
103    {
104        type Ready = S::Ready;
105
106        #[inline]
107        async fn ready(&self) -> Self::Ready {
108            self.service.ready().await
109        }
110    }
111}