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}