1use std::collections::hash_map::{Entry, HashMap};
7
8use crate::{MediaType, Method, Request, Response, StatusCode, Version};
9
10pub use crate::common::RouteError;
11
12pub trait EndpointHandler<T>: Sync + Send {
14 fn handle_request(&self, req: &Request, arg: &T) -> Response;
16}
17
18pub struct HttpRoutes<T> {
20 server_id: String,
21 prefix: String,
22 media_type: MediaType,
23 routes: HashMap<String, Box<dyn EndpointHandler<T> + Sync + Send>>,
25}
26
27impl<T: Send> HttpRoutes<T> {
28 pub fn new(server_id: String, prefix: String) -> Self {
30 HttpRoutes {
31 server_id,
32 prefix,
33 media_type: MediaType::ApplicationJson,
34 routes: HashMap::new(),
35 }
36 }
37
38 pub fn add_route(
45 &mut self,
46 method: Method,
47 path: String,
48 handler: Box<dyn EndpointHandler<T> + Sync + Send>,
49 ) -> Result<(), RouteError> {
50 let full_path = format!("{}:{}{}", method.to_str(), self.prefix, path);
51 match self.routes.entry(full_path.clone()) {
52 Entry::Occupied(_) => Err(RouteError::HandlerExist(full_path)),
53 Entry::Vacant(entry) => {
54 entry.insert(handler);
55 Ok(())
56 }
57 }
58 }
59
60 pub fn handle_http_request(&self, request: &Request, argument: &T) -> Response {
89 let path = format!(
90 "{}:{}",
91 request.method().to_str(),
92 request.uri().get_abs_path()
93 );
94 let mut response = match self.routes.get(&path) {
95 Some(route) => route.handle_request(request, argument),
96 None => Response::new(Version::Http11, StatusCode::NotFound),
97 };
98
99 response.set_server(&self.server_id);
100 response.set_content_type(self.media_type);
101 response
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 struct HandlerArg(bool);
110
111 struct MockHandler {}
112
113 impl EndpointHandler<HandlerArg> for MockHandler {
114 fn handle_request(&self, _req: &Request, _arg: &HandlerArg) -> Response {
115 Response::new(Version::Http11, StatusCode::OK)
116 }
117 }
118
119 #[test]
120 fn test_create_router() {
121 let mut router = HttpRoutes::new("Mock_Server".to_string(), "/api/v1".to_string());
122 let handler = MockHandler {};
123 let res = router.add_route(Method::Get, "/func1".to_string(), Box::new(handler));
124 assert!(res.is_ok());
125 assert!(router.routes.contains_key("GET:/api/v1/func1"));
126
127 let handler = MockHandler {};
128 match router.add_route(Method::Get, "/func1".to_string(), Box::new(handler)) {
129 Err(RouteError::HandlerExist(_)) => {}
130 _ => panic!("add_route() should return error for path with existing handler"),
131 }
132
133 let handler = MockHandler {};
134 let res = router.add_route(Method::Put, "/func1".to_string(), Box::new(handler));
135 assert!(res.is_ok());
136
137 let handler = MockHandler {};
138 let res = router.add_route(Method::Get, "/func2".to_string(), Box::new(handler));
139 assert!(res.is_ok());
140 }
141
142 #[test]
143 fn test_handle_http_request() {
144 let mut router = HttpRoutes::new("Mock_Server".to_string(), "/api/v1".to_string());
145 let handler = MockHandler {};
146 router
147 .add_route(Method::Get, "/func1".to_string(), Box::new(handler))
148 .unwrap();
149
150 let request =
151 Request::try_from(b"GET http://localhost/api/v1/func2 HTTP/1.1\r\n\r\n", None).unwrap();
152 let arg = HandlerArg(true);
153 let reply = router.handle_http_request(&request, &arg);
154 assert_eq!(reply.status(), StatusCode::NotFound);
155 }
156}