haro/app.rs
1use std::collections::HashMap;
2use std::net::{TcpListener, TcpStream};
3use std::num::NonZeroUsize;
4use std::sync::Arc;
5use std::thread::available_parallelism;
6
7use log::{debug, info};
8
9use crate::http::conn::Conn;
10use crate::middleware::Middleware;
11use crate::pool::ThreadPool;
12use crate::router::Router;
13use crate::{DynHandler, Handler, Request, Response};
14
15/// A web Application with routes and middlewares
16pub struct Application {
17 addr: &'static str,
18 num_threads: usize,
19 router: Router,
20 middlewares: Vec<Middleware>,
21}
22
23impl Application {
24 /// Create a new `Application` instance
25 /// # Examples
26 /// ```
27 /// use haro::Application;
28 ///
29 /// let mut app = Application::new("0:12345");
30 /// ```
31 pub fn new(addr: &'static str) -> Self {
32 env_logger::init();
33 let router = Router::default();
34 let middlewares = Vec::new();
35 let default_num_threads = NonZeroUsize::new(8).unwrap();
36 let num_threads = available_parallelism().unwrap_or(default_num_threads).get();
37 Self {
38 addr,
39 num_threads,
40 router,
41 middlewares,
42 }
43 }
44
45 /// Set thread worker pool size for `Application`
46 /// # Examples
47 /// ```
48 /// use haro::Application;
49 ///
50 /// let mut app = Application::new("0:8080").num_threads(64);
51 /// ```
52 pub fn num_threads(mut self, n: usize) -> Self {
53 self.num_threads = n;
54 self
55 }
56
57 /// Add a middleware into an `Application`
58 /// # Example
59 /// ```
60 /// use haro::{Application, middleware};
61 ///
62 /// let mut app = Application::new("0:8080");
63 /// app.middleware(middleware::logging);
64 /// ```
65 pub fn middleware<M>(&mut self, middleware: M)
66 where
67 M: Fn(DynHandler) -> DynHandler + Send + Sync + 'static,
68 {
69 self.middlewares.push(Arc::new(middleware));
70 }
71
72 /// Add a route using a function or closure
73 /// # Example
74 /// ```
75 /// use haro::{Application, Request, Response, middleware};
76 ///
77 /// let mut app = Application::new("0:8080");
78 /// app.route("/", |_| Response::str("Hello Haro"));
79 /// app.route("/hello", hello);
80 ///
81 /// fn hello(_:Request) -> Response {
82 /// Response::str("Hello Haro")
83 /// }
84 /// ```
85 pub fn route<F>(&mut self, pattern: &'static str, f: F)
86 where
87 F: Fn(Request) -> Response + Send + Sync + 'static,
88 {
89 self.router.add(pattern, f);
90 }
91
92 /// Add a route using a trait type
93 /// # Example
94 /// ```
95 /// use haro::{Application, Request, Response, Handler};
96 ///
97 /// let mut app = Application::new("0:8080");
98 /// let hello_handler = HelloHandler{name:"Haro".to_string()};
99 /// app.route_handler("/hello", hello_handler);
100 ///
101 /// struct HelloHandler {
102 /// name: String,
103 /// }
104 ///
105 /// impl Handler for HelloHandler {
106 /// fn call(&self, _: Request) -> Response {
107 /// Response::str(format!("hello {}", self.name))
108 /// }
109 /// }
110 pub fn route_handler<H>(&mut self, pattern: &'static str, h: H)
111 where
112 H: Handler + Send + Sync + 'static,
113 {
114 self.router.add_handler(pattern, h);
115 }
116
117 /// Send a request to an `Application`, usually used in test
118 /// # Examples
119 /// ```
120 /// use std::collections::HashMap;
121 /// use haro::{Application, Request, Response};
122 ///
123 /// fn test_handler(_:Request) -> Response {
124 /// Response::str("test")
125 /// }
126 ///
127 /// let mut app = Application::new("0:12345");
128 /// app.route("/", test_handler);
129 ///
130 /// let res = app.request("get", "/", HashMap::new(), &Vec::new());
131 /// assert_eq!("test".as_bytes(), res.body());
132 /// ```
133 pub fn request(
134 &self,
135 method: &str,
136 uri: &str,
137 headers: HashMap<String, String>,
138 body: &[u8],
139 ) -> Response {
140 let mut req = Request::new(method, uri, headers, body);
141 let (params, mut handler) = self.router.dispatch(req.path());
142 req.params = params;
143
144 // TODO: how much benefits to move applying middlewares at begging to avoid do it every time in a new request.
145 // apply middleware in reverse order
146 for middleware in self.middlewares.iter().rev() {
147 handler = middleware(handler);
148 }
149 handler(req)
150 }
151
152 /// Run the application, start listening on the specify address and start a worker pool to handle requests
153 /// # Examples
154 /// ```no_run
155 /// use std::collections::HashMap;
156 /// use haro::{Application, Request, Response};
157 ///
158 /// fn test_handler(_:Request) -> Response {
159 /// Response::str("test")
160 /// }
161 ///
162 /// let mut app = Application::new("0:12345");
163 /// app.run()
164 /// ```
165 pub fn run(&self) {
166 info!("Started web server on addr {}", self.addr);
167 debug!("routes: \n {:}", self.router);
168 let pool = ThreadPool::new(self.num_threads);
169
170 let listener = TcpListener::bind(self.addr).unwrap();
171 for stream in listener.incoming() {
172 let stream = stream.unwrap();
173 // TODO: anyway to avoid clone?
174 let router = self.router.clone();
175 let middlewares = self.middlewares.clone();
176 pool.execute(|| {
177 handle_connection(router, middlewares, stream);
178 });
179 }
180 }
181}
182
183fn handle_connection(router: Router, middlewares: Vec<Middleware>, stream: TcpStream) {
184 let mut conn = Conn::from(stream);
185 let mut req = Request::from(&mut conn);
186 let (params, mut handler) = router.dispatch(req.path());
187 req.params = params;
188
189 // apply middleware in reverse order
190 for middleware in middlewares.iter().rev() {
191 handler = middleware(handler);
192 }
193 let res = handler(req);
194
195 conn.write_all(res.to_string().as_bytes());
196 conn.flush();
197}
198
199#[cfg(test)]
200mod tests {
201 use super::Application;
202
203 #[test]
204 fn it_works() {
205 Application::new("0:65530").num_threads(2);
206 }
207}