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}