Skip to main content

xitca_web/app/
mod.rs

1mod object;
2mod router;
3
4use core::{
5    convert::Infallible,
6    fmt,
7    future::{Future, ready},
8    pin::Pin,
9};
10
11use std::error;
12
13use xitca_http::util::{
14    middleware::context::ContextBuilder,
15    service::router::{IntoObject, PathGen, RouteGen, RouteObject, TypedRoute},
16};
17
18use crate::{
19    body::{Body, Either, RequestBody, ResponseBody},
20    bytes::Bytes,
21    context::WebContext,
22    error::{Error, RouterError},
23    http::{WebRequest, WebResponse},
24    middleware::eraser::TypeEraser,
25    service::{EnclosedBuilder, EnclosedFnBuilder, MapBuilder, Service, ServiceExt, ready::ReadyService},
26};
27
28use self::{object::WebObject, router::AppRouter};
29
30/// composed application type with router, stateful context and default middlewares.
31pub struct App<R = (), CF = ()> {
32    router: R,
33    ctx_builder: CF,
34}
35
36type BoxFuture<C> = Pin<Box<dyn Future<Output = Result<C, Box<dyn fmt::Debug>>>>>;
37type CtxBuilder<C> = Box<dyn Fn() -> BoxFuture<C> + Send + Sync>;
38type DefaultWebObject<C> = WebObject<C, RequestBody, WebResponse, RouterError<Error>>;
39type DefaultAppRouter<C> = AppRouter<RouteObject<(), DefaultWebObject<C>, Infallible>>;
40
41// helper trait to poly between () and Box<dyn Fn()> as application state.
42pub trait IntoCtx {
43    type Ctx;
44
45    fn into_ctx(self) -> impl Fn() -> BoxFuture<Self::Ctx> + Send + Sync;
46}
47
48impl IntoCtx for () {
49    type Ctx = ();
50
51    fn into_ctx(self) -> impl Fn() -> BoxFuture<Self::Ctx> + Send + Sync {
52        || Box::pin(ready(Ok(())))
53    }
54}
55
56impl<C> IntoCtx for CtxBuilder<C> {
57    type Ctx = C;
58
59    fn into_ctx(self) -> impl Fn() -> BoxFuture<Self::Ctx> + Send + Sync {
60        self
61    }
62}
63
64/// type alias for concrete type of nested App.
65///
66/// # Example
67/// ```rust
68/// # use xitca_web::{handler::handler_service, App, NestApp, WebContext};
69/// // a function return an App instance.
70/// fn app() -> NestApp<usize> {
71///     App::new().at("/index", handler_service(|_: &WebContext<'_, usize>| async { "" }))
72/// }
73///
74/// // nest app would be registered with /v2 as prefix therefore "/v2/index" become accessible.
75/// App::new().at("/v2", app()).with_state(996usize);
76/// ```
77pub type NestApp<C> = App<DefaultAppRouter<C>>;
78
79impl App {
80    /// Construct a new application instance.
81    pub fn new<Obj>() -> App<AppRouter<Obj>> {
82        App {
83            router: AppRouter::new(),
84            ctx_builder: (),
85        }
86    }
87}
88
89impl<Obj, CF> App<AppRouter<Obj>, CF> {
90    /// insert routed service with given string literal as route path to application. services will be routed with following rules:
91    ///
92    /// # Static route
93    /// string literal matched against http request's uri path.
94    /// ```rust
95    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
96    /// # use xitca_unsafe_collection::futures::NowOrPanic;
97    /// # use xitca_web::{
98    /// #   handler::{handler_service, path::PathRef},
99    /// #   http::{Request, StatusCode},
100    /// #   route::get,
101    /// #   service::Service,
102    /// #   App
103    /// # };
104    /// // register string path to handler service.
105    /// let app = App::new().at("/users", get(handler_service(handler)));
106    ///
107    /// // handler function extract request's uri path.
108    /// async fn handler(PathRef(path): PathRef<'_>) -> StatusCode {
109    ///     assert_eq!(path, "/users");
110    ///     StatusCode::OK
111    /// }
112    ///
113    /// // boilerplate for starting application service in test. in real world this should be achieved
114    /// // through App::serve API
115    /// let app_service = app.finish().call(()).now_or_panic().unwrap();
116    ///
117    /// // get request with uri can be matched against registered route.
118    /// let req = Request::builder().uri("/users").body(Default::default())?;
119    ///
120    /// // execute application service where the request would match handler function
121    /// let res = app_service.call(req).now_or_panic()?;
122    /// assert_eq!(res.status(), StatusCode::OK);
123    ///
124    /// // http query is not included in the path matching.
125    /// let req = Request::builder().uri("/users?foo=bar").body(Default::default())?;
126    /// let res = app_service.call(req).now_or_panic()?;
127    /// assert_eq!(res.status(), StatusCode::OK);
128    ///
129    /// // any change on uri path would result in no match of route.
130    /// let req = Request::builder().uri("/users/").body(Default::default())?;
131    /// let res = app_service.call(req).now_or_panic()?;
132    /// assert_eq!(res.status(), StatusCode::NOT_FOUND);
133    ///
134    /// # Ok(())
135    /// # }
136    /// ```
137    ///
138    /// # Dynamic route
139    /// Along with static routes, the router also supports dynamic route segments. These can either be named or catch-all parameters:
140    ///
141    /// ## Named Parameters
142    /// Named parameters like `/{id}` match anything until the next `/` or the end of the path:
143    /// ```rust
144    /// # fn main() {
145    /// #   #[cfg(feature = "params")]
146    /// #   _main();
147    /// # }
148    /// #
149    /// # #[cfg(feature = "params")]
150    /// # fn _main() -> Result<(), Box<dyn std::error::Error>> {
151    /// # use xitca_unsafe_collection::futures::NowOrPanic;
152    /// # use xitca_web::{
153    /// #   handler::{handler_service, params::Params},
154    /// #   http::{Request, StatusCode},
155    /// #   route::get,
156    /// #   service::Service,
157    /// #   App
158    /// # };
159    /// // register named param pattern to handler service.
160    /// let app = App::new().at("/users/{id}", get(handler_service(handler)));
161    ///
162    /// // handler function try to extract a single key/value string pair from url params.
163    /// async fn handler(Params(val): Params<u32>) -> StatusCode {
164    ///     // the value matches the string literal and it's routing rule registered in App::at.
165    ///     assert_eq!(val, 996);
166    ///     StatusCode::OK
167    /// }
168    ///
169    /// // boilerplate for starting application service in test. in real world this should be achieved
170    /// // through App::serve API
171    /// let app_service = app.finish().call(()).now_or_panic().unwrap();
172    ///
173    /// // get request with uri can be matched against registered route.
174    /// let req = Request::builder().uri("/users/996").body(Default::default())?;
175    ///
176    /// // execute application service where the request would match handler function
177    /// let res = app_service.call(req).now_or_panic()?;
178    /// assert_eq!(res.status(), StatusCode::OK);
179    ///
180    /// // :x pattern only match till next /. and in following request's case it will not find a matching route.
181    /// let req = Request::builder().uri("/users/996/fool").body(Default::default())?;
182    /// let res = app_service.call(req).now_or_panic()?;
183    /// assert_eq!(res.status(), StatusCode::NOT_FOUND);
184    /// # Ok(())
185    /// # }
186    /// ```
187    ///
188    /// ## Catch-all Parameters
189    /// Catch-all parameters start with `*` and match everything after the `/`.
190    /// They must always be at the **end** of the route:
191    /// ```rust
192    /// # fn main() {
193    /// #   #[cfg(feature = "params")]
194    /// #   _main();
195    /// # }
196    /// #
197    /// # #[cfg(feature = "params")]
198    /// # fn _main() -> Result<(), Box<dyn std::error::Error>> {
199    /// # use xitca_unsafe_collection::futures::NowOrPanic;
200    /// # use xitca_web::{
201    /// #   handler::{handler_service, params::Params},
202    /// #   http::{Request, StatusCode},
203    /// #   route::get,
204    /// #   service::Service,
205    /// #   App
206    /// # };
207    /// // register named param pattern to handler service.
208    /// let app = App::new().at("/{*path}", get(handler_service(handler)));
209    ///
210    /// // handler function try to extract a single key/value string pair from url params.
211    /// async fn handler(Params(path): Params<String>) -> StatusCode {
212    ///     assert!(path.ends_with(".css"));
213    ///     StatusCode::OK
214    /// }
215    ///
216    /// // boilerplate for starting application service in test. in real world this should be achieved
217    /// // through App::serve API
218    /// let app_service = app.finish().call(()).now_or_panic().unwrap();
219    ///
220    /// // get request with uri can be matched against registered route.
221    /// let req = Request::builder().uri("/foo/bar.css").body(Default::default())?;
222    ///
223    /// // execute application service where the request would match handler function
224    /// let res = app_service.call(req).now_or_panic()?;
225    /// assert_eq!(res.status(), StatusCode::OK);
226    ///
227    /// // *x pattern match till the end of uri path. in following request's case it will match against handler
228    /// let req = Request::builder().uri("/foo/bar/baz.css").body(Default::default())?;
229    /// let res = app_service.call(req).now_or_panic()?;
230    /// assert_eq!(res.status(), StatusCode::OK);
231    /// # Ok(())
232    /// # }
233    /// ```
234    ///
235    /// ## Implicit catch-all parameters
236    /// Built in http services require catch-all params would implicitly utilize them to reduce user input.
237    /// ```rust
238    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
239    /// # use xitca_unsafe_collection::futures::NowOrPanic;
240    /// # use xitca_web::{
241    /// #   handler::{handler_service, path::PathRef},
242    /// #   http::{Request, StatusCode},
243    /// #   route::get,
244    /// #   service::Service,
245    /// #   App
246    /// # };
247    /// // when nesting App is used as a route service it will silently register it as a catch-call param.
248    /// let app = App::new().at("/users", App::new()
249    ///     .at("/", get(handler_service(handler)))
250    ///     .at("/996", get(handler_service(handler)))
251    /// );
252    ///
253    /// // handler function.
254    /// async fn handler() -> StatusCode {
255    ///     StatusCode::OK
256    /// }
257    ///
258    /// // boilerplate for starting application service in test. in real world this should be achieved
259    /// // through App::serve API
260    /// let app_service = app.finish().call(()).now_or_panic().unwrap();
261    ///
262    /// // get request with uri can be matched against registered route.
263    /// let req = Request::builder().uri("/users/").body(Default::default())?;
264    ///
265    /// // execute application service where the request would match handler function
266    /// let res = app_service.call(req).now_or_panic()?;
267    /// assert_eq!(res.status(), StatusCode::OK);
268    ///
269    /// let req = Request::builder().uri("/users/996").body(Default::default())?;
270    /// let res = app_service.call(req).now_or_panic()?;
271    /// assert_eq!(res.status(), StatusCode::OK);
272    /// # Ok(())
273    /// # }
274    /// ```
275    ///
276    /// ## Character literal escaping
277    /// The literal characters `{` and `}` may be included in a static route by escaping them with the same character.
278    /// For example, the `{` character is escaped with `{{`, and the `}` character is escaped with `}}`.
279    ///
280    /// ## Conflict Rules
281    /// Static and dynamic route segments are allowed to overlap. If they do, static segments will be given higher priority:
282    /// ```rust
283    /// # use xitca_web::{
284    /// #   handler::{html::Html, redirect::Redirect, handler_service},
285    /// #   route::get,
286    /// #   App
287    /// # };
288    /// let app = App::new()
289    ///     .at("/", Redirect::see_other("/index.html"))        // high priority
290    ///     .at("/index.html", Html("<h1>Hello,World!</h1>"))   // high priority
291    ///     .at("/{*path}", get(handler_service(handler)))        // low priority
292    ///     .serve();
293    ///
294    /// async fn handler() -> &'static str {
295    ///     "todo"
296    /// }
297    /// ```
298    /// Formally, a route consists of a list of segments separated by `/`, with an optional leading and trailing slash: `(/)<segment_1>/.../<segment_n>(/)`.
299    ///
300    /// Given set of routes, their overlapping segments may include, in order of priority:
301    ///
302    /// - Any number of static segments (`/a`, `/b`, ...).
303    /// - *One* of the following:
304    ///   - Any number of route parameters with a suffix (`/{x}a`, `/{x}b`, ...), prioritizing the longest suffix.
305    ///   - Any number of route parameters with a prefix (`/a{x}`, `/b{x}`, ...), prioritizing the longest prefix.
306    ///   - A single route parameter with both a prefix and a suffix (`/a{x}b`).
307    /// - *One* of the following;
308    ///   - A single standalone parameter (`/{x}`).
309    ///   - A single standalone catch-all parameter (`/{*rest}`). Note this only applies to the final route segment.
310    ///
311    /// Any other combination of route segments is considered ambiguous, and attempting to insert such a route will result in an error.
312    ///
313    /// The one exception to the above set of rules is that catch-all parameters are always considered to conflict with suffixed route parameters, i.e. that `/{*rest}`
314    /// and `/{x}suffix` are overlapping. This is due to an implementation detail of the routing tree that may be relaxed in the future.
315    pub fn at<F, C, B>(mut self, path: &str, builder: F) -> Self
316    where
317        F: RouteGen + Service + Send + Sync,
318        F::Response: for<'r> Service<WebContext<'r, C, B>>,
319        for<'r> WebContext<'r, C, B>: IntoObject<F::Route<F>, (), Object = Obj>,
320    {
321        self.router = self.router.insert(path, builder);
322        self
323    }
324
325    /// insert typed route service with given path to application.
326    pub fn at_typed<T, C>(mut self, typed: T) -> Self
327    where
328        T: TypedRoute<C, Route = Obj>,
329    {
330        self.router = self.router.insert_typed(typed);
331        self
332    }
333
334    /// merget two App instances into one.
335    ///
336    /// input App's state would be discarded in progress and Self's state would be preserved.
337    ///
338    /// only App instances without any middlewares enclosed can be merged.
339    ///
340    /// # Example
341    /// ```rust
342    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
343    /// # use xitca_unsafe_collection::futures::NowOrPanic;
344    /// # use xitca_web::{
345    /// #   handler::{handler_service, state::StateRef},
346    /// #   http::{Request, StatusCode},
347    /// #   route::get,
348    /// #   service::Service,
349    /// #   App
350    /// # };
351    /// // define routes in separate App instances.
352    /// let api_v1 = App::new()
353    ///     .at("/foo", get(handler_service(handler)))
354    ///     .at("/bar", get(handler_service(handler)));
355    ///
356    /// let api_v2 = App::new()
357    ///     .at("/baz", get(handler_service(handler)));
358    ///
359    /// // merge them into one App and attach state.
360    /// // note: any state set on api_v1 or api_v2 via with_state would be discarded;
361    /// // only the state on the final merged App is used.
362    /// let app = App::new()
363    ///     .merge(api_v1)
364    ///     .merge(api_v2)
365    ///     .with_state(996usize);
366    ///
367    /// async fn handler(StateRef(state): StateRef<'_, usize>) -> StatusCode {
368    ///     assert_eq!(996, *state);
369    ///     StatusCode::OK
370    /// }
371    ///
372    /// let service = app.finish().call(()).now_or_panic().unwrap();
373    ///
374    /// // all merged routes are accessible.
375    /// let req = Request::builder().uri("/foo").body(Default::default())?;
376    /// let res = service.call(req).now_or_panic()?;
377    /// assert_eq!(res.status(), StatusCode::OK);
378    ///
379    /// let req = Request::builder().uri("/baz").body(Default::default())?;
380    /// let res = service.call(req).now_or_panic()?;
381    /// assert_eq!(res.status(), StatusCode::OK);
382    /// # Ok(())
383    /// # }
384    /// ```
385    ///
386    /// Middlewares enclosed on the input App change its type and prevent merging.
387    /// Only bare Apps (no `.enclosed()` / `.enclosed_fn()`) can be passed to `merge`:
388    /// ```compile_fail
389    /// # use xitca_web::{
390    /// #   handler::handler_service,
391    /// #   http::StatusCode,
392    /// #   route::get,
393    /// #   App, WebContext
394    /// # };
395    /// # async fn mw<S, C, B>(s: &S, req: WebContext<'_, C, B>) {}
396    /// let other = App::new()
397    ///     .at("/foo", get(handler_service(|| async { StatusCode::OK })))
398    ///     .enclosed_fn(mw); // adding middleware changes the type
399    ///
400    /// // this will not compile because `other` is no longer `App<AppRouter<_>, _>`.
401    /// App::new().merge(other);
402    /// ```
403    pub fn merge<CF2>(mut self, other: App<AppRouter<Obj>, CF2>) -> Self {
404        self.router = self.router.merge(other.router);
405        self
406    }
407}
408
409impl<R, CF> App<R, CF> {
410    /// Construct App with a thread safe state that will be shared among all tasks and worker threads.
411    ///
412    /// State accessing is based on generic type approach where the State type and it's typed fields are generally
413    /// opaque to middleware and routing services of the application. In order to cast concrete type from generic
414    /// state type [std::borrow::Borrow] trait is utilized. See example below for explanation.
415    ///
416    /// # Example
417    /// ```rust
418    /// # use xitca_web::{
419    /// #   error::Error,
420    /// #   handler::{handler_service, state::{BorrowState, StateRef}, FromRequest},
421    /// #   service::Service,
422    /// #   App, WebContext
423    /// # };
424    /// // our typed state.
425    /// #[derive(Clone, Default)]
426    /// struct State {
427    ///     string: String,
428    ///     usize: usize
429    /// }
430    ///
431    /// // implement Borrow trait to enable borrowing &String type from State.
432    /// impl BorrowState<String> for State {
433    ///     fn borrow(&self) -> &String {
434    ///         &self.string
435    ///     }
436    /// }
437    ///
438    /// App::new()
439    ///     .with_state(State::default())// construct app with state type.
440    ///     .at("/", handler_service(index)) // a function service that have access to state.
441    ///     # .at("/nah", handler_service(|_: &WebContext<'_, State>| async { "used for infer type" }))
442    ///     .enclosed_fn(middleware_fn); // a function middleware that have access to state
443    ///
444    /// // the function service don't know what the real type of application state is.
445    /// // it only needs to know &String can be borrowed from it.
446    /// async fn index(_: StateRef<'_, String>) -> &'static str {
447    ///     ""
448    /// }
449    ///
450    /// // similar to function service. the middleware does not need to know the real type of C.
451    /// // it only needs to know it implement according trait.
452    /// async fn middleware_fn<S, C, Res>(service: &S, ctx: WebContext<'_, C>) -> Result<Res, Error>
453    /// where
454    ///     S: for<'r> Service<WebContext<'r, C>, Response = Res, Error = Error>,
455    ///     C: BorrowState<String> // annotate we want to borrow &String from generic C state type.
456    /// {
457    ///     // WebContext::state would return &C then we can call Borrow::borrow on it to get &String
458    ///     let _string = ctx.state().borrow();
459    ///     // or use extractor manually like in function service.
460    ///     let _string = StateRef::<'_, String>::from_request(&ctx).await?;
461    ///     service.call(ctx).await
462    /// }
463    /// ```
464    pub fn with_state<C>(self, state: C) -> App<R, CtxBuilder<C>>
465    where
466        C: Send + Sync + Clone + 'static,
467    {
468        self.with_async_state(move || ready(Ok::<_, Infallible>(state.clone())))
469    }
470
471    /// Construct App with async closure which it's output would be used as state.
472    /// async state is used to produce thread per core and/or non thread safe state copies.
473    /// The output state is not bound to `Send` and `Sync` auto traits.
474    pub fn with_async_state<CF1, Fut, C, E>(self, builder: CF1) -> App<R, CtxBuilder<C>>
475    where
476        CF1: Fn() -> Fut + Send + Sync + 'static,
477        Fut: Future<Output = Result<C, E>> + 'static,
478        E: fmt::Debug + 'static,
479    {
480        let ctx_builder = Box::new(move || {
481            let fut = builder();
482            Box::pin(async { fut.await.map_err(|e| Box::new(e) as Box<dyn fmt::Debug>) }) as _
483        });
484
485        App {
486            router: self.router,
487            ctx_builder,
488        }
489    }
490}
491
492impl<R, CF> App<R, CF>
493where
494    R: Service + Send + Sync,
495    R::Error: fmt::Debug + 'static,
496{
497    /// Enclose App with middleware type. Middleware must impl [Service] trait.
498    /// See [middleware](crate::middleware) for more.
499    pub fn enclosed<T>(self, transform: T) -> App<EnclosedBuilder<R, T>, CF>
500    where
501        T: Service<Result<R::Response, R::Error>>,
502    {
503        App {
504            router: self.router.enclosed(transform),
505            ctx_builder: self.ctx_builder,
506        }
507    }
508
509    /// Enclose App with function as middleware type.
510    /// See [middleware](crate::middleware) for more.
511    pub fn enclosed_fn<Req, T, O>(self, transform: T) -> App<EnclosedFnBuilder<R, T>, CF>
512    where
513        T: AsyncFn(&R::Response, Req) -> O + Clone,
514    {
515        App {
516            router: self.router.enclosed_fn(transform),
517            ctx_builder: self.ctx_builder,
518        }
519    }
520
521    /// Mutate `<<Self::Response as Service<Req>>::Future as Future>::Output` type with given
522    /// closure.
523    pub fn map<T, Res, ResMap>(self, mapper: T) -> App<MapBuilder<R, T>, CF>
524    where
525        T: Fn(Res) -> ResMap + Clone,
526        Self: Sized,
527    {
528        App {
529            router: self.router.map(mapper),
530            ctx_builder: self.ctx_builder,
531        }
532    }
533}
534
535impl<R, CF> App<R, CF>
536where
537    R: Service + Send + Sync,
538    R::Error: fmt::Debug + 'static,
539{
540    /// Finish App build. No other App method can be called afterwards.
541    pub fn finish<C, ResB, SE>(
542        self,
543    ) -> impl Service<
544        Response = impl ReadyService + Service<WebRequest, Response = WebResponse<EitherResBody<ResB>>, Error = Infallible>,
545        Error = impl fmt::Debug,
546    >
547    where
548        R::Response: ReadyService + for<'r> Service<WebContext<'r, C>, Response = WebResponse<ResB>, Error = SE>,
549        SE: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible>,
550        CF: IntoCtx<Ctx = C>,
551        C: 'static,
552    {
553        let App { ctx_builder, router } = self;
554        router
555            .enclosed(crate::middleware::WebContext)
556            .enclosed(ContextBuilder::new(ctx_builder.into_ctx()))
557    }
558
559    /// Finish App build. No other App method can be called afterwards.
560    pub fn finish_boxed<C, ResB, SE>(
561        self,
562    ) -> AppObject<impl ReadyService + Service<WebRequest, Response = WebResponse, Error = Infallible>>
563    where
564        R: 'static,
565        R::Response:
566            ReadyService + for<'r> Service<WebContext<'r, C>, Response = WebResponse<ResB>, Error = SE> + 'static,
567        SE: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible> + 'static,
568        ResB: Body<Data = Bytes> + 'static,
569        ResB::Error: error::Error + Send + Sync + 'static,
570        CF: IntoCtx<Ctx = C> + 'static,
571        C: 'static,
572    {
573        struct BoxApp<S>(S);
574
575        impl<S, Arg> Service<Arg> for BoxApp<S>
576        where
577            S: Service<Arg>,
578            S::Error: fmt::Debug + 'static,
579        {
580            type Response = S::Response;
581            type Error = Box<dyn fmt::Debug>;
582
583            async fn call(&self, arg: Arg) -> Result<Self::Response, Self::Error> {
584                self.0.call(arg).await.map_err(|e| Box::new(e) as _)
585            }
586        }
587
588        Box::new(BoxApp(self.finish().enclosed(TypeEraser::response_body())))
589    }
590
591    #[cfg(feature = "__server")]
592    /// Finish App build and serve is with [HttpServer]. No other App method can be called afterwards.
593    ///
594    /// [HttpServer]: crate::server::HttpServer
595    pub fn serve<C, ResB, SE>(
596        self,
597    ) -> crate::server::HttpServer<
598        impl Service<
599            Response = impl ReadyService
600                       + Service<WebRequest, Response = WebResponse<EitherResBody<ResB>>, Error = Infallible>,
601            Error = impl fmt::Debug,
602        >,
603    >
604    where
605        R: 'static,
606        R::Response: ReadyService + for<'r> Service<WebContext<'r, C>, Response = WebResponse<ResB>, Error = SE>,
607        SE: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible> + 'static,
608        ResB: 'static,
609        CF: IntoCtx<Ctx = C> + 'static,
610        C: 'static,
611    {
612        crate::server::HttpServer::serve(self.finish())
613    }
614}
615
616type EitherResBody<B> = Either<B, ResponseBody>;
617
618impl<R, F> PathGen for App<R, F>
619where
620    R: PathGen,
621{
622    fn path_gen(&mut self, prefix: &str) -> String {
623        self.router.path_gen(prefix)
624    }
625}
626
627impl<R, F> RouteGen for App<R, F>
628where
629    R: RouteGen,
630{
631    type Route<R1> = R::Route<R1>;
632
633    fn route_gen<R1>(route: R1) -> Self::Route<R1> {
634        R::route_gen(route)
635    }
636}
637
638impl<R, Arg> Service<Arg> for App<R>
639where
640    R: Service<Arg>,
641{
642    type Response = R::Response;
643    type Error = R::Error;
644
645    async fn call(&self, req: Arg) -> Result<Self::Response, Self::Error> {
646        self.router.call(req).await
647    }
648}
649
650impl<R, Arg, C> Service<Arg> for App<R, CtxBuilder<C>>
651where
652    R: Service<Arg>,
653{
654    type Response = NestAppService<C, R::Response>;
655    type Error = R::Error;
656
657    async fn call(&self, req: Arg) -> Result<Self::Response, Self::Error> {
658        let ctx = (self.ctx_builder)()
659            .await
660            .expect("fallible nested application state builder is not supported yet");
661        let service = self.router.call(req).await?;
662
663        Ok(NestAppService { ctx, service })
664    }
665}
666
667pub struct NestAppService<C, S> {
668    ctx: C,
669    service: S,
670}
671
672impl<'r, C1, C, S, SE> Service<WebContext<'r, C1>> for NestAppService<C, S>
673where
674    S: for<'r1> Service<WebContext<'r1, C>, Response = WebResponse, Error = SE>,
675    SE: Into<Error>,
676{
677    type Response = WebResponse;
678    type Error = Error;
679
680    async fn call(&self, ctx: WebContext<'r, C1>) -> Result<Self::Response, Self::Error> {
681        let WebContext { req, body, .. } = ctx;
682
683        self.service
684            .call(WebContext {
685                req,
686                body,
687                ctx: &self.ctx,
688            })
689            .await
690            .map_err(Into::into)
691    }
692}
693
694/// object safe [App] instance. used for case where naming [App]'s type is needed.
695pub type AppObject<S> =
696    Box<dyn xitca_service::object::ServiceObject<(), Response = S, Error = Box<dyn fmt::Debug>> + Send + Sync>;
697
698#[cfg(test)]
699mod test {
700    use xitca_unsafe_collection::futures::NowOrPanic;
701
702    use crate::{
703        handler::{
704            extension::ExtensionRef, extension::ExtensionsRef, handler_service, path::PathRef, state::StateRef,
705            uri::UriRef,
706        },
707        http::{Method, const_header_value::TEXT_UTF8, header::CONTENT_TYPE, request},
708        middleware::UncheckedReady,
709        route::get,
710    };
711
712    use super::*;
713
714    async fn middleware<S, C, B, Res, Err>(s: &S, req: WebContext<'_, C, B>) -> Result<Res, Err>
715    where
716        S: for<'r> Service<WebContext<'r, C, B>, Response = Res, Error = Err>,
717    {
718        s.call(req).await
719    }
720
721    #[allow(clippy::too_many_arguments)]
722    async fn handler(
723        _res: Result<UriRef<'_>, Error>,
724        _opt: Option<UriRef<'_>>,
725        _req: &WebRequest<()>,
726        StateRef(state): StateRef<'_, String>,
727        PathRef(path): PathRef<'_>,
728        UriRef(_): UriRef<'_>,
729        ExtensionRef(_): ExtensionRef<'_, Foo>,
730        ExtensionsRef(_): ExtensionsRef<'_>,
731        req: &WebContext<'_, String>,
732    ) -> String {
733        assert_eq!("state", state);
734        assert_eq!(state, req.state());
735        assert_eq!("/", path);
736        assert_eq!(path, req.req().uri().path());
737        state.to_string()
738    }
739
740    // Handler with no state extractor
741    async fn stateless_handler(_: PathRef<'_>) -> String {
742        String::from("debug")
743    }
744
745    #[derive(Clone)]
746    struct Middleware;
747
748    impl<S, E> Service<Result<S, E>> for Middleware {
749        type Response = MiddlewareService<S>;
750        type Error = E;
751
752        async fn call(&self, res: Result<S, E>) -> Result<Self::Response, Self::Error> {
753            res.map(MiddlewareService)
754        }
755    }
756
757    struct MiddlewareService<S>(S);
758
759    impl<'r, S, C, B, Res, Err> Service<WebContext<'r, C, B>> for MiddlewareService<S>
760    where
761        S: for<'r2> Service<WebContext<'r2, C, B>, Response = Res, Error = Err>,
762        C: 'r,
763        B: 'r,
764    {
765        type Response = Res;
766        type Error = Err;
767
768        async fn call(&self, mut req: WebContext<'r, C, B>) -> Result<Self::Response, Self::Error> {
769            self.0.call(req.reborrow()).await
770        }
771    }
772
773    #[allow(clippy::borrow_interior_mutable_const)]
774    #[test]
775    fn test_app() {
776        let state = String::from("state");
777
778        let service = App::new()
779            .at("/", get(handler_service(handler)))
780            .with_state(state)
781            .at(
782                "/stateless",
783                get(handler_service(stateless_handler)).head(handler_service(stateless_handler)),
784            )
785            .enclosed_fn(middleware)
786            .enclosed(Middleware)
787            .enclosed(UncheckedReady)
788            .finish()
789            .call(())
790            .now_or_panic()
791            .ok()
792            .unwrap();
793
794        let mut req = WebRequest::default();
795        req.extensions_mut().insert(Foo);
796
797        let res = service.call(req).now_or_panic().unwrap();
798
799        assert_eq!(res.status().as_u16(), 200);
800
801        assert_eq!(res.headers().get(CONTENT_TYPE).unwrap(), TEXT_UTF8);
802
803        let req = request::Builder::default()
804            .uri("/abc")
805            .body(Default::default())
806            .unwrap();
807
808        let res = service.call(req).now_or_panic().unwrap();
809
810        assert_eq!(res.status().as_u16(), 404);
811
812        let req = request::Builder::default()
813            .method(Method::POST)
814            .body(Default::default())
815            .unwrap();
816
817        let res = service.call(req).now_or_panic().unwrap();
818
819        assert_eq!(res.status().as_u16(), 405);
820    }
821
822    #[derive(Clone)]
823    struct Foo;
824
825    #[test]
826    fn app_nest_router() {
827        async fn handler(StateRef(state): StateRef<'_, String>, PathRef(path): PathRef<'_>) -> String {
828            assert_eq!("state", state);
829            assert_eq!("/scope/nest", path);
830            state.to_string()
831        }
832
833        fn app() -> NestApp<String> {
834            App::new().at("/nest", get(handler_service(handler)))
835        }
836
837        let state = String::from("state");
838        let service = App::new()
839            .with_state(state.clone())
840            .at("/root", get(handler_service(handler)))
841            .at("/scope", app())
842            .finish()
843            .call(())
844            .now_or_panic()
845            .ok()
846            .unwrap();
847
848        let req = request::Builder::default()
849            .uri("/scope/nest")
850            .body(Default::default())
851            .unwrap();
852
853        let res = service.call(req).now_or_panic().unwrap();
854
855        assert_eq!(res.status().as_u16(), 200);
856
857        async fn handler2(StateRef(state): StateRef<'_, usize>, PathRef(path): PathRef<'_>) -> String {
858            assert_eq!(996, *state);
859            assert_eq!("/scope/nest", path);
860            state.to_string()
861        }
862
863        let service = App::new()
864            .with_state(state)
865            .at("/root", get(handler_service(handler)))
866            .at(
867                "/scope",
868                App::new()
869                    .with_state(996usize)
870                    .at("/nest", get(handler_service(handler2))),
871            )
872            .finish()
873            .call(())
874            .now_or_panic()
875            .ok()
876            .unwrap();
877
878        let req = request::Builder::default()
879            .uri("/scope/nest")
880            .body(Default::default())
881            .unwrap();
882
883        let res = service.call(req).now_or_panic().unwrap();
884
885        assert_eq!(res.status().as_u16(), 200);
886    }
887}