restless_web/
app.rs

1use std::{io::ErrorKind::WouldBlock, net::SocketAddr};
2
3use tokio::io::AsyncReadExt;
4use tokio::net::{TcpListener, TcpStream};
5
6use once_cell::sync::Lazy;
7use tokio::net::tcp::ReadHalf;
8
9use crate::request::Req;
10use crate::response::Res;
11use crate::route::{PathItemType, Route, RouteCallback};
12use crate::route_handler::RouteHandler;
13
14const BASE_ADDR: &str = "127.0.0.1";
15
16pub struct App<'a> {
17    pub routes: Vec<Route<'a>>,
18}
19
20static mut APP: Lazy<App<'static>> = Lazy::new(|| App { routes: vec![] });
21
22impl App<'static> {
23    pub fn new() -> &'static mut Lazy<App<'static>> {
24        unsafe { &mut APP }
25    }
26
27    // TODO: Client error handle hook on connection
28    #[tokio::main]
29    pub async fn listen<F>(&'static self, port: u16, on_bound: F)
30    where
31        F: FnOnce(),
32    {
33        // TODO: Create `build_addr` function
34        let addr = format!("{}:{}", BASE_ADDR.to_owned(), port);
35
36        let listener = TcpListener::bind(addr.clone())
37            .await
38            .unwrap_or_else(|_| panic!("Can't bound at {}", addr));
39
40        on_bound();
41
42        loop {
43            let result = listener.accept().await;
44
45            tokio::spawn(async move {
46                match result {
47                    Ok((stream, addr)) => self.handle_stream(stream, addr).await,
48                    Err(err) => println!("Couldn't get client: {:?}", err),
49                }
50            });
51        }
52    }
53
54    async fn handle_stream<'a>(&'static self, mut socket: TcpStream, _addr: SocketAddr) {
55        let (mut read_half, write_half) = socket.split();
56
57        let raw_req = self.read_all(&mut read_half).await.unwrap();
58        let req = Req::new(&raw_req);
59        let res = Res::new();
60
61        match req {
62            Some(req) => {
63                let route = self.get_route(&req);
64
65                match route {
66                    Some(r) => {
67                        Res::send_outcome((r.callback)(req, res), write_half).await;
68                    }
69                    None => {
70                        Res::send_outcome(res.status(404), write_half).await;
71                    }
72                }
73            }
74            None => {
75                Res::send_outcome(res.status(400), write_half).await;
76            }
77        }
78    }
79
80    async fn read_all<'a>(&self, read_half: &mut ReadHalf<'a>) -> Result<String, std::io::Error> {
81        // https://stackoverflow.com/a/71949195
82        let mut buf: Vec<u8> = Vec::new();
83
84        // Solve would block problems
85        let mut firs_read_buf = [0u8; 2024];
86        let bytes_read = read_half.read(&mut firs_read_buf).await.unwrap();
87        buf.extend_from_slice(&firs_read_buf[..bytes_read]);
88
89        loop {
90            // Creating the buffer **after** the `await` prevents it from
91            // being stored in the async task.
92            let mut tmp_buf = [0u8; 1024];
93
94            // Try to read data, this may still fail with `WouldBlock`
95            // if the readiness event is a false positive.
96            match read_half.try_read(&mut tmp_buf) {
97                Ok(0) => break,
98                Ok(bytes_read) => buf.extend_from_slice(&tmp_buf[..bytes_read]),
99                Err(ref e) if e.kind() == WouldBlock => {
100                    break;
101                }
102                Err(e) => {
103                    return Err(e);
104                }
105            }
106        }
107
108        return Ok(std::str::from_utf8(&buf)
109            .unwrap()
110            .trim_matches(char::from(0))
111            .to_owned());
112    }
113
114    #[allow(dead_code)]
115    fn get_route<'a>(&'static self, req: &Req) -> Option<&Route<'a>> {
116        let mut res_route = None;
117
118        let req_paths = if req.path == "/" {
119            vec!["/"]
120        } else {
121            req.path.split_terminator('/').skip(1).collect::<Vec<_>>()
122            // NOTE(gr3yknigh1):          ^^^^^^^^ skipes first empty element
123        };
124
125        for route in &self.routes {
126            let mut is_compatible = true;
127
128            if route.paths.len() != req_paths.len() {
129                continue;
130            }
131
132            for (i, path) in route.paths.iter().enumerate().take(route.paths.len()) {
133                match path.r#type {
134                    PathItemType::Static => {
135                        if path.value != req_paths[i] {
136                            is_compatible = false;
137                            break;
138                        }
139                    }
140                    PathItemType::Dynamic => (),
141                }
142            }
143
144            if is_compatible {
145                res_route = Some(route);
146                break;
147            }
148        }
149
150        res_route
151    }
152}
153
154impl RouteHandler for App<'_> {
155    fn get(&mut self, path: &'static str, handler: RouteCallback) -> &mut Self {
156        self.routes.push(Route::new(path, handler, Some("GET")));
157        self
158    }
159
160    fn post(&mut self, path: &'static str, handler: RouteCallback) -> &mut Self {
161        self.routes.push(Route::new(path, handler, Some("POST")));
162        self
163    }
164
165    fn put(&mut self, path: &'static str, handler: RouteCallback) -> &mut Self {
166        self.routes.push(Route::new(path, handler, Some("PUT")));
167        self
168    }
169
170    fn delete(&mut self, path: &'static str, handler: RouteCallback) -> &mut Self {
171        self.routes.push(Route::new(path, handler, Some("DELETE")));
172        self
173    }
174
175    fn patch(&mut self, path: &'static str, handler: RouteCallback) -> &mut Self {
176        self.routes.push(Route::new(path, handler, Some("PATCH")));
177        self
178    }
179}