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