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}