gotham/router/route/
dispatch.rs

1//! Defines the route `Dispatcher` and supporting types.
2
3use futures_util::future::{self, FutureExt};
4use log::trace;
5use std::panic::RefUnwindSafe;
6use std::pin::Pin;
7
8use crate::handler::{Handler, HandlerFuture, NewHandler};
9use crate::pipeline::{PipelineHandleChain, PipelineSet};
10use crate::state::{request_id, State};
11
12/// Used by `Router` to dispatch requests via pipelines and finally into the configured `Handler`.
13pub trait Dispatcher: RefUnwindSafe {
14    /// Dispatches a request via pipelines and `Handler` represented by this `Dispatcher`.
15    fn dispatch(&self, state: State) -> Pin<Box<HandlerFuture>>;
16}
17
18/// Default implementation of the `Dispatcher` trait.
19pub struct DispatcherImpl<H, C, P>
20where
21    H: NewHandler,
22    C: PipelineHandleChain<P>,
23    P: RefUnwindSafe,
24{
25    new_handler: H,
26    pipeline_chain: C,
27    pipelines: PipelineSet<P>,
28}
29
30impl<H, C, P> DispatcherImpl<H, C, P>
31where
32    H: NewHandler,
33    H::Instance: 'static,
34    C: PipelineHandleChain<P>,
35    P: RefUnwindSafe,
36{
37    /// Creates a new `DispatcherImpl`.
38    ///
39    /// * `new_handler` - The `Handler` that will be called once the `pipeline_chain` is complete.
40    /// * `pipeline_chain` - A chain of `Pipeline` instance handles that indicate which `Pipelines`
41    ///   will be invoked.
42    /// * `pipelines` - All `Pipeline` instances, accessible by the handles provided in
43    ///   `pipeline_chain`.
44    pub fn new(new_handler: H, pipeline_chain: C, pipelines: PipelineSet<P>) -> Self {
45        DispatcherImpl {
46            new_handler,
47            pipeline_chain,
48            pipelines,
49        }
50    }
51}
52
53impl<H, C, P> Dispatcher for DispatcherImpl<H, C, P>
54where
55    H: NewHandler,
56    H::Instance: Send + 'static,
57    C: PipelineHandleChain<P>,
58    P: RefUnwindSafe,
59{
60    fn dispatch(&self, state: State) -> Pin<Box<HandlerFuture>> {
61        match self.new_handler.new_handler() {
62            Ok(h) => {
63                trace!("[{}] cloning handler", request_id(&state));
64                self.pipeline_chain
65                    .call(&self.pipelines, state, move |state| h.handle(state))
66            }
67            Err(e) => {
68                trace!("[{}] error cloning handler", request_id(&state));
69                future::err((state, e.into())).boxed()
70            }
71        }
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    use std::sync::Arc;
79
80    use hyper::{Body, Response, StatusCode};
81
82    use crate::middleware::{Middleware, NewMiddleware};
83    use crate::pipeline::{new_pipeline, new_pipeline_set};
84    use crate::state::StateData;
85    use crate::test::TestServer;
86
87    fn handler(state: State) -> (State, Response<Body>) {
88        let number = state.borrow::<Number>().value;
89        (
90            state,
91            Response::builder()
92                .status(StatusCode::OK)
93                .body(format!("{}", number).into())
94                .unwrap(),
95        )
96    }
97
98    #[derive(Clone)]
99    struct Number {
100        value: i32,
101    }
102
103    impl NewMiddleware for Number {
104        type Instance = Number;
105
106        fn new_middleware(&self) -> anyhow::Result<Number> {
107            Ok(self.clone())
108        }
109    }
110
111    impl Middleware for Number {
112        fn call<Chain>(self, mut state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
113        where
114            Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
115            Self: Sized,
116        {
117            state.put(self);
118            chain(state)
119        }
120    }
121
122    impl StateData for Number {}
123
124    struct Addition {
125        value: i32,
126    }
127
128    impl NewMiddleware for Addition {
129        type Instance = Addition;
130
131        fn new_middleware(&self) -> anyhow::Result<Addition> {
132            Ok(Addition { ..*self })
133        }
134    }
135
136    impl Middleware for Addition {
137        fn call<Chain>(self, mut state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
138        where
139            Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
140            Self: Sized,
141        {
142            state.borrow_mut::<Number>().value += self.value;
143            chain(state)
144        }
145    }
146
147    struct Multiplication {
148        value: i32,
149    }
150
151    impl NewMiddleware for Multiplication {
152        type Instance = Multiplication;
153
154        fn new_middleware(&self) -> anyhow::Result<Multiplication> {
155            Ok(Multiplication { ..*self })
156        }
157    }
158
159    impl Middleware for Multiplication {
160        fn call<Chain>(self, mut state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
161        where
162            Chain: FnOnce(State) -> Pin<Box<HandlerFuture>> + Send + 'static,
163            Self: Sized,
164        {
165            state.borrow_mut::<Number>().value *= self.value;
166            chain(state)
167        }
168    }
169
170    #[test]
171    fn pipeline_chain_ordering_test() {
172        let test_server = TestServer::new(|| {
173            Ok(move |state| {
174                let pipelines = new_pipeline_set();
175
176                let (pipelines, p1) = pipelines.add(
177                    new_pipeline()
178                        .add(Number { value: 0 }) // 0
179                        .add(Addition { value: 1 }) // 1
180                        .add(Multiplication { value: 2 }) // 2
181                        .build(),
182                );
183
184                let (pipelines, p2) = pipelines.add(
185                    new_pipeline()
186                        .add(Addition { value: 1 }) // 3
187                        .add(Multiplication { value: 2 }) // 6
188                        .build(),
189                );
190
191                let (pipelines, p3) = pipelines.add(
192                    new_pipeline()
193                        .add(Addition { value: 2 }) // 8
194                        .add(Multiplication { value: 3 }) // 24
195                        .build(),
196                );
197
198                let pipelines = Arc::new(pipelines);
199
200                let new_handler = || Ok(handler);
201
202                let pipeline_chain = (p3, (p2, (p1, ())));
203                let dispatcher = DispatcherImpl::new(new_handler, pipeline_chain, pipelines);
204                dispatcher.dispatch(state)
205            })
206        })
207        .unwrap();
208
209        let response = test_server
210            .client()
211            .get("http://localhost/")
212            .perform()
213            .unwrap();
214
215        let buf = response.read_body().unwrap();
216        assert_eq!(buf.as_slice(), b"24");
217    }
218}