1 use std::collections::HashMap;
2 use std::convert::Infallible;
3 use std::sync::{Arc, Mutex};
4 
5 use hyper::server::Server;
6 use hyper::service::{make_service_fn, service_fn};
7 use hyper::{Body, Method, Request, Response};
8 use tower::util::BoxCloneService;
9 use tower::Service as _;
10 
11 // GET /
index(_req: Request<Body>) -> hyper::Result<Response<Body>>12 async fn index(_req: Request<Body>) -> hyper::Result<Response<Body>> {
13     Ok(Response::new(Body::from("Hello, world!")))
14 }
15 
16 // GET /blog
blog(_req: Request<Body>) -> hyper::Result<Response<Body>>17 async fn blog(_req: Request<Body>) -> hyper::Result<Response<Body>> {
18     Ok(Response::new(Body::from("...")))
19 }
20 
21 // 404 handler
not_found(_req: Request<Body>) -> hyper::Result<Response<Body>>22 async fn not_found(_req: Request<Body>) -> hyper::Result<Response<Body>> {
23     Ok(Response::builder().status(404).body(Body::empty()).unwrap())
24 }
25 
26 // We can use `BoxCloneService` to erase the type of each handler service.
27 //
28 // We still need a `Mutex` around each service because `BoxCloneService` doesn't
29 // require the service to implement `Sync`.
30 type Service = Mutex<BoxCloneService<Request<Body>, Response<Body>, hyper::Error>>;
31 
32 // We use a `HashMap` to hold a `Router` for each HTTP method. This allows us
33 // to register the same route for multiple methods.
34 type Router = HashMap<Method, matchit::Router<Service>>;
35 
route(router: Arc<Router>, req: Request<Body>) -> hyper::Result<Response<Body>>36 async fn route(router: Arc<Router>, req: Request<Body>) -> hyper::Result<Response<Body>> {
37     // find the subrouter for this request method
38     let router = match router.get(req.method()) {
39         Some(router) => router,
40         // if there are no routes for this method, respond with 405 Method Not Allowed
41         None => return Ok(Response::builder().status(405).body(Body::empty()).unwrap()),
42     };
43 
44     // find the service for this request path
45     match router.at(req.uri().path()) {
46         Ok(found) => {
47             // lock the service for a very short time, just to clone the service
48             let mut service = found.value.lock().unwrap().clone();
49             service.call(req).await
50         }
51         // if we there is no matching service, call the 404 handler
52         Err(_) => not_found(req).await,
53     }
54 }
55 
56 #[tokio::main]
main()57 async fn main() {
58     // Create a router and register our routes.
59     let mut router = Router::new();
60 
61     // GET / => `index`
62     router
63         .entry(Method::GET)
64         .or_default()
65         .insert("/", BoxCloneService::new(service_fn(index)).into())
66         .unwrap();
67 
68     // GET /blog => `blog`
69     router
70         .entry(Method::GET)
71         .or_default()
72         .insert("/blog", BoxCloneService::new(service_fn(blog)).into())
73         .unwrap();
74 
75     // boilerplate for the hyper service
76     let router = Arc::new(router);
77     let make_service = make_service_fn(|_| {
78         let router = router.clone();
79         async { Ok::<_, Infallible>(service_fn(move |request| route(router.clone(), request))) }
80     });
81 
82     // run the server
83     Server::bind(&([127, 0, 0, 1], 3000).into())
84         .serve(make_service)
85         .await
86         .unwrap()
87 }
88