davisjr/
router.rs

1use http::{Request, Response};
2use hyper::Body;
3
4use crate::{app::App, errors::*, handler::Handler, path::Path, HTTPResult, TransientState};
5
6#[derive(Clone)]
7pub(crate) struct Route<S: Clone + Send, T: TransientState + 'static> {
8    method: http::Method,
9    path: Path,
10    handler: Handler<S, T>,
11}
12
13impl<S: Clone + Send, T: TransientState> PartialEq for Route<S, T> {
14    fn eq(&self, other: &Self) -> bool {
15        self.method.to_string() == other.method.to_string() && self.path.eq(&other.path)
16    }
17}
18
19impl<S: Clone + Send, T: TransientState> Eq for Route<S, T> {}
20
21impl<S: Clone + Send, T: TransientState> PartialOrd for Route<S, T> {
22    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
23        Some(self.cmp(other))
24    }
25}
26
27impl<S: Clone + Send, T: TransientState> Ord for Route<S, T> {
28    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
29        let left = self.method.to_string() + " " + &self.path.to_string();
30        let right = other.method.to_string() + " " + &other.path.to_string();
31
32        left.to_string().cmp(&right.to_string())
33    }
34}
35
36impl<S: Clone + Send, T: TransientState> Route<S, T> {
37    fn new(
38        method: http::Method,
39        path: String,
40        handler: Handler<S, T>,
41    ) -> Result<Self, ServerError> {
42        Ok(Self {
43            method,
44            handler,
45            path: Path::new(path)?,
46        })
47    }
48
49    async fn dispatch(
50        &self,
51        provided: String,
52        req: Request<hyper::Body>,
53        app: App<S, T>,
54        state: T,
55    ) -> HTTPResult<T> {
56        let params = self.path.extract(provided)?;
57
58        if self.method != req.method() {
59            return Err(Error::StatusCode(
60                http::StatusCode::NOT_FOUND,
61                String::new(),
62            ));
63        }
64
65        self.handler.perform(req, None, params, app, state).await
66    }
67}
68
69#[derive(Clone)]
70pub(crate) struct Router<S: Clone + Send, T: TransientState + 'static>(Vec<Route<S, T>>);
71
72impl<S: Clone + Send, T: TransientState + Clone + Send> Router<S, T> {
73    pub fn new() -> Self {
74        Self(Vec::new())
75    }
76
77    pub(crate) fn add(
78        &mut self,
79        method: http::Method,
80        path: String,
81        ch: Handler<S, T>,
82    ) -> Result<Self, ServerError> {
83        self.0.push(Route::new(method, path, ch)?);
84        Ok(self.clone())
85    }
86
87    pub(crate) async fn dispatch(
88        &self,
89        req: Request<Body>,
90        app: App<S, T>,
91    ) -> Result<Response<Body>, Error> {
92        let path = req.uri().path().to_string();
93
94        for route in self.0.clone() {
95            if route.path.matches(path.to_string())? && route.method.eq(req.method()) {
96                let (_, response, _) = route
97                    .dispatch(path.to_string(), req, app, T::initial())
98                    .await?;
99                if response.is_none() {
100                    return Err(Error::StatusCode(
101                        http::StatusCode::NOT_FOUND,
102                        String::new(),
103                    ));
104                }
105
106                return Ok(response.unwrap());
107            }
108        }
109
110        Err(Error::StatusCode(
111            http::StatusCode::METHOD_NOT_ALLOWED,
112            String::new(),
113        ))
114    }
115}
116
117mod tests {
118    #[tokio::test]
119    async fn test_route_dynamic() {
120        use http::{Method, Request, Response};
121        use hyper::Body;
122
123        use crate::{app::App, handler::Handler, HTTPResult, NoState, Params};
124
125        use super::Route;
126
127        #[derive(Clone)]
128        struct State;
129
130        async fn handler_dynamic(
131            req: Request<Body>,
132            _response: Option<Response<Body>>,
133            params: Params,
134            _app: App<State, NoState>,
135            _state: NoState,
136        ) -> HTTPResult<NoState> {
137            return Ok((
138                req,
139                Some(Response::builder().status(400).body(Body::from(format!(
140                    "hello, {}",
141                    *params.get("name").unwrap()
142                )))?),
143                NoState {},
144            ));
145        }
146
147        let route = Route::new(
148            Method::GET,
149            "/a/:name/c".to_string(),
150            Handler::new(
151                |req, resp, params, app, state| {
152                    Box::pin(handler_dynamic(req, resp, params, app, state))
153                },
154                None,
155            ),
156        )
157        .unwrap();
158
159        assert!(route
160            .dispatch("/a".to_string(), Request::default(), App::new(), NoState {})
161            .await
162            .is_err());
163        assert!(route
164            .dispatch(
165                "/a/b/c".to_string(),
166                Request::builder()
167                    .method(Method::POST)
168                    .body(Body::from("one=two".as_bytes()))
169                    .unwrap(),
170                App::new(),
171                NoState {},
172            )
173            .await
174            .is_err());
175
176        for name in vec![
177            "erik", "adam", "sean", "travis", "joseph", "grant", "joy", "steve", "marc",
178        ] {
179            assert!(route
180                .dispatch(
181                    "/a/:name/c".to_string(),
182                    Request::default(),
183                    App::new(),
184                    NoState {}
185                )
186                .await
187                .is_ok());
188
189            let path = format!("/a/{}/c", name);
190
191            let body = hyper::body::to_bytes(
192                route
193                    .dispatch(path.clone(), Request::default(), App::new(), NoState {})
194                    .await
195                    .unwrap()
196                    .1
197                    .unwrap()
198                    .body_mut(),
199            )
200            .await
201            .unwrap();
202
203            assert_eq!(body, format!("hello, {}", name).as_bytes());
204
205            let status = route
206                .dispatch(path, Request::default(), App::new(), NoState {})
207                .await
208                .unwrap()
209                .1
210                .unwrap()
211                .status();
212
213            assert_eq!(status, 400);
214        }
215    }
216
217    #[tokio::test]
218    async fn test_route_static() {
219        use http::{Method, Request, Response};
220        use hyper::Body;
221
222        use crate::{app::App, handler::Handler, HTTPResult, NoState, Params};
223
224        use super::Route;
225
226        #[derive(Clone)]
227        struct State;
228
229        async fn handler_static(
230            req: Request<Body>,
231            _response: Option<Response<Body>>,
232            _params: Params,
233            _app: App<State, NoState>,
234            _state: NoState,
235        ) -> HTTPResult<NoState> {
236            return Ok((
237                req,
238                Some(
239                    Response::builder()
240                        .status(400)
241                        .body(Body::from("hello, world".as_bytes()))?,
242                ),
243                NoState {},
244            ));
245        }
246
247        let route = Route::new(
248            Method::GET,
249            "/a/b/c".to_string(),
250            Handler::new(
251                |req, resp, params, app, state| {
252                    Box::pin(handler_static(req, resp, params, app, state))
253                },
254                None,
255            ),
256        )
257        .unwrap();
258
259        assert!(route
260            .dispatch("/a".to_string(), Request::default(), App::new(), NoState {})
261            .await
262            .is_err());
263        assert!(route
264            .dispatch(
265                "/a/b/c".to_string(),
266                Request::builder()
267                    .method(Method::POST)
268                    .body(Body::from("one=two".as_bytes()))
269                    .unwrap(),
270                App::new(),
271                NoState {},
272            )
273            .await
274            .is_err());
275
276        assert!(route
277            .dispatch(
278                "/a/b/c".to_string(),
279                Request::default(),
280                App::new(),
281                NoState {}
282            )
283            .await
284            .is_ok());
285
286        let body = hyper::body::to_bytes(
287            route
288                .dispatch(
289                    "/a/b/c".to_string(),
290                    Request::default(),
291                    App::new(),
292                    NoState {},
293                )
294                .await
295                .unwrap()
296                .1
297                .unwrap()
298                .body_mut(),
299        )
300        .await
301        .unwrap();
302
303        assert_eq!(body, "hello, world".as_bytes());
304
305        let status = route
306            .dispatch(
307                "/a/b/c".to_string(),
308                Request::default(),
309                App::new(),
310                NoState {},
311            )
312            .await
313            .unwrap()
314            .1
315            .unwrap()
316            .status();
317
318        assert_eq!(status, 400);
319    }
320
321    #[tokio::test]
322    async fn test_router() {
323        use super::Router;
324        use crate::{
325            app::App, compose_handler, handler::Handler, HTTPResult, Params, TransientState,
326        };
327        use http::{Method, Request, Response};
328        use hyper::Body;
329
330        #[derive(Clone)]
331        struct HelloState {
332            name: Option<String>,
333        }
334
335        impl TransientState for HelloState {
336            fn initial() -> Self {
337                Self { name: None }
338            }
339        }
340
341        #[derive(Clone)]
342        struct State;
343
344        async fn handler_dynamic(
345            req: Request<Body>,
346            _response: Option<Response<Body>>,
347            params: Params,
348            _app: App<State, HelloState>,
349            mut state: HelloState,
350        ) -> HTTPResult<HelloState> {
351            let name = params.get("name").unwrap().clone();
352            state.name = Some(name.clone());
353
354            return Ok((
355                req,
356                Some(
357                    Response::builder()
358                        .status(200)
359                        .body(Body::from(format!(
360                            "hello, {}",
361                            state.clone().name.unwrap()
362                        )))
363                        .unwrap(),
364                ),
365                state,
366            ));
367        }
368
369        async fn handler_continued(
370            req: Request<Body>,
371            _response: Option<Response<Body>>,
372            _params: Params,
373            _app: App<State, HelloState>,
374            state: HelloState,
375        ) -> HTTPResult<HelloState> {
376            return Ok((
377                req,
378                Some(
379                    Response::builder()
380                        .status(200)
381                        .body(Body::from(format!(
382                            "hello, {}",
383                            state.clone().name.unwrap()
384                        )))
385                        .unwrap(),
386                ),
387                state,
388            ));
389        }
390
391        async fn handler_static(
392            req: Request<Body>,
393            _response: Option<Response<Body>>,
394            _params: Params,
395            _app: App<State, HelloState>,
396            _state: HelloState,
397        ) -> HTTPResult<HelloState> {
398            return Ok((
399                req,
400                Some(
401                    Response::builder()
402                        .status(400)
403                        .body(Body::from("hello, world".as_bytes()))?,
404                ),
405                HelloState::initial(),
406            ));
407        }
408
409        let mut router = Router::new();
410
411        router
412            .add(
413                Method::GET,
414                "/a/b/c".to_string(),
415                Handler::new(
416                    |req, resp, params, app, state| {
417                        Box::pin(handler_static(req, resp, params, app, state))
418                    },
419                    None,
420                ),
421            )
422            .unwrap();
423
424        router
425            .add(
426                Method::GET,
427                "/c/b/a/:name".to_string(),
428                Handler::new(
429                    |req, resp, params, app, state| {
430                        Box::pin(handler_dynamic(req, resp, params, app, state))
431                    },
432                    None,
433                ),
434            )
435            .unwrap();
436
437        router
438            .add(
439                Method::GET,
440                "/with_state/:name".to_string(),
441                compose_handler!(handler_dynamic, handler_continued),
442            )
443            .unwrap();
444
445        let response = router
446            .dispatch(
447                Request::builder()
448                    .uri("/a/b/c")
449                    .method(Method::GET)
450                    .body(Body::default())
451                    .unwrap(),
452                App::new(),
453            )
454            .await;
455        assert!(response.is_ok());
456
457        let body = hyper::body::to_bytes(response.unwrap()).await.unwrap();
458        assert_eq!(body, "hello, world".as_bytes());
459
460        for name in vec![
461            "erik", "adam", "sean", "travis", "joseph", "grant", "joy", "steve", "marc",
462        ] {
463            let response = router
464                .dispatch(
465                    Request::builder()
466                        .uri(&format!("/c/b/a/{}", name))
467                        .method(Method::GET)
468                        .body(Body::default())
469                        .unwrap(),
470                    App::new(),
471                )
472                .await;
473            assert!(response.is_ok());
474
475            let body = hyper::body::to_bytes(response.unwrap()).await.unwrap();
476            assert_eq!(body, format!("hello, {}", name).as_bytes());
477
478            let response = router
479                .dispatch(
480                    Request::builder()
481                        .uri(&format!("/with_state/{}", name))
482                        .method(Method::GET)
483                        .body(Body::default())
484                        .unwrap(),
485                    App::new(),
486                )
487                .await;
488
489            assert!(response.is_ok());
490
491            let body = hyper::body::to_bytes(response.unwrap()).await.unwrap();
492            assert_eq!(body, format!("hello, {}", name).as_bytes());
493        }
494
495        for bad_route in vec!["/", "/bad", "/bad/route", "/a/b/c/param", "/c/b/a/0/bad"] {
496            let response = router
497                .dispatch(
498                    Request::builder()
499                        .uri(bad_route)
500                        .method(Method::GET)
501                        .body(Body::default())
502                        .unwrap(),
503                    App::new(),
504                )
505                .await;
506            assert!(response.is_err());
507        }
508    }
509}