gnostr_web/
lib.rs

1use bytes::Bytes;
2use hyper::{
3    body::to_bytes,
4    service::{make_service_fn, service_fn},
5    Body, Request, Server,
6};
7use route_recognizer::Params;
8use router::Router;
9use std::sync::Arc;
10
11type Response = hyper::Response<hyper::Body>;
12type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
13
14#[derive(Clone, Debug)]
15pub struct AppState {
16    pub state_thing: String,
17}
18
19#[derive(Debug)]
20pub struct Context {
21    pub state: AppState,
22    pub req: Request<Body>,
23    pub params: Params,
24    body_bytes: Option<Bytes>,
25}
26
27impl Context {
28    pub fn new(state: AppState, req: Request<Body>, params: Params) -> Context {
29        Context {
30            state,
31            req,
32            params,
33            body_bytes: None,
34        }
35    }
36
37    pub async fn body_json<T: serde::de::DeserializeOwned>(&mut self) -> Result<T, Error> {
38        let body_bytes = match self.body_bytes {
39            Some(ref v) => v,
40            _ => {
41                let body = to_bytes(self.req.body_mut()).await?;
42                self.body_bytes = Some(body);
43                self.body_bytes.as_ref().expect("body_bytes was set above")
44            }
45        };
46        Ok(serde_json::from_slice(&body_bytes)?)
47    }
48}
49
50
51
52pub mod headers;
53pub mod parser;
54pub mod request;
55pub mod response;
56pub mod status;
57pub mod router;
58pub mod handler;
59
60pub mod paths {
61    use crate::request::Request;
62    use crate::response::Response;
63
64    pub type Paths = Vec<Path<fn(Request, Response)>>;
65    pub type SinglePath = Path<fn(Request, Response)>;
66
67    /// Path accepts pathname and view
68    pub struct Path<T> {
69        pub name: String,
70        pub view: T,
71    }
72
73    impl<T> Path<T> {
74        pub fn new(name: &str, view: T) -> Self {
75            let name = name.to_string();
76
77            return Self { name, view };
78        }
79    }
80}
81
82pub mod server {
83    use crate::headers::{extract_headers, parse_request_method_header};
84    use crate::paths::{Paths, SinglePath};
85    use crate::request::Request;
86    use crate::response::Response;
87    use std::net::{Shutdown, TcpListener, TcpStream, Ipv4Addr, UdpSocket};
88    use std::sync::atomic::{AtomicBool, Ordering};
89    use std::sync::{Arc, RwLock};
90    use std::thread::spawn;
91
92    /// Example usage
93    /// ```rust
94    /// use gnostr_web::paths::{Path, Paths};
95    /// use gnostr_web::request::Request;
96    /// use gnostr_web::response::Response;
97    /// use gnostr_web::server::run_server;
98    /// use gnostr_web::status::Status;
99    ///
100    /// fn home(request: Request, mut response: Response) {
101    ///    response.html(Status::Ok, "Home Page".to_string()).send();
102    /// }
103    ///
104    /// fn main() {
105    ///    let paths: Paths = vec![
106    ///         Path::new("/", home),
107    ///    ];
108    ///
109    ///    run_server("0.0.0.0:8080", paths);
110    /// }
111    /// ```
112
113
114    //use std::net::{Ipv4Addr, TcpListener, UdpSocket};
115    use std::str::FromStr;
116    pub type Port = u16;
117
118
119    pub fn is_tcp_port_available(host: &str, p: Port) -> bool {
120        matches!(
121            TcpListener::bind((Ipv4Addr::from_str(host).unwrap(), p)).is_ok(),
122            true
123        ) 
124    }
125
126    pub fn is_udp_port_available(host: &str, p: Port) -> bool {
127        matches!(
128            UdpSocket::bind((Ipv4Addr::from_str(host).unwrap(), p)).is_ok(),
129            true
130        )
131    }
132
133    pub fn check_port(host: &str, port: Port) -> bool {
134        is_tcp_port_available(host, port) && is_udp_port_available(host, port)
135    }
136
137
138    #[cfg(test)]
139    mod tests {
140        #[cfg(test)]
141        use crate::server::check_port;
142        fn test_is_free() {
143            assert!(check_port("127.0.0.1", 32200));
144        }
145    }
146
147
148    pub fn get_available_port() -> Option<u16> {
149        (8000..9000).find(|port| port_is_available(*port))
150    }
151
152    pub fn port_is_available(port: u16) -> bool {
153        match TcpListener::bind(("0.0.0.0", port)) {
154            Ok(_) => true,
155            Err(_) => false,
156        }
157    }
158
159    pub fn run_server(listen_address: &str, paths: Paths) {
160        println!("\nhttp://{}", listen_address);
161
162        let v: Vec<&str> = listen_address.split(":").collect();
163        print!("\nv[0]={:?}", v[0]);
164        print!("\nv[1]={:?}", v[1]);
165
166        // use std::ops::Deref;
167        // let port: &u16 = v[1] as u16;
168        //let port: &u16 = &(v[1] as u16);
169        //if port_is_available(port.deref(v[1]) as u16) {}
170        if port_is_available(8080 as u16) {
171            print!("\n8080 port_is_available");
172            //std::process::exit(0);
173        } else {
174            print!("\nNOT!!! 8080 port_is_available");
175            std::process::exit(0);
176        }
177        // //gnostr-hyper
178        // if port_is_available(8081 as u16) {
179        //     print!("\n8081 port_is_available");
180        //     //std::process::exit(0);
181        // } else {
182        //     print!("\nNOT!!! 8081 port_is_available");
183        //     std::process::exit(0);
184        // }
185
186        let tcp = TcpListener::bind(listen_address);
187
188        match tcp {
189            Ok(listener) => {
190                listen_connections(listener, paths);
191            }
192
193            Err(_) => {
194                eprintln!("Failed to listen stream");
195            }
196        }
197    }
198
199    pub fn listen_connections(listener: TcpListener, paths: Paths) {
200        let paths_lock = Arc::new(RwLock::new(paths));
201
202        for stream in listener.incoming() {
203            match stream {
204                Ok(stream) => {
205                    let paths = Arc::clone(&paths_lock);
206
207                    spawn(move || {
208                        serve_client(stream, paths);
209                    });
210                }
211
212                Err(error) => {
213                    print!("Error receiving stream: {}", error);
214                }
215            }
216        }
217    }
218
219    pub struct Context {
220        /// A same tcp stream can be used to serve multiple pages. Setting accept_next will continue
221        /// to use same connection. Make sure to set `accept_next` to false if request
222        /// body is not read completely. It is passed to both Request struct.
223        pub accept_next: AtomicBool,
224    }
225
226    impl Context {
227        pub fn dont_wait(&self) {
228            self.accept_next.store(false, Ordering::Relaxed);
229        }
230    }
231
232    fn serve_client(stream: TcpStream, paths: Arc<RwLock<Paths>>) {
233        let context = Context {
234            accept_next: AtomicBool::new(true),
235        };
236
237        let context_ref = Arc::new(context);
238
239        while context_ref.accept_next.load(Ordering::Relaxed) {
240            let stream = stream.try_clone().expect("Error cloning stream");
241            decode_request(stream, paths.clone(), context_ref.clone());
242        }
243    }
244
245    pub fn decode_request(mut stream: TcpStream, paths: Arc<RwLock<Paths>>, context: Arc<Context>) {
246        let mut header_start = String::new();
247        let mut partial_body_bytes = Vec::new();
248
249        const MAX_HEADER_SIZE: usize = 1024 * 1024; // 1 MiB
250        let headers_result = extract_headers(
251            &mut stream,
252            &mut header_start,
253            &mut partial_body_bytes,
254            MAX_HEADER_SIZE,
255        );
256
257        if !headers_result.is_ok() {
258            context.accept_next.store(false, Ordering::Relaxed);
259            return;
260        }
261
262        let headers = headers_result.unwrap();
263
264        let request_info = parse_request_method_header(&header_start.as_str());
265        if !request_info.is_some() {
266            context.accept_next.store(false, Ordering::Relaxed);
267            let _ = stream.shutdown(Shutdown::Both);
268            return;
269        }
270
271        let (method, raw_path) = request_info.unwrap();
272
273        // These states are shared among request and response
274        let body_read = Arc::new(AtomicBool::from(false));
275        let body_parsed = Arc::new(AtomicBool::from(false));
276
277        let mut request = Request::new(
278            context,
279            stream,
280            method,
281            raw_path,
282            headers,
283            body_read.clone(),
284            body_parsed.clone(),
285        );
286        request.setup();
287
288        // Some bytes are read unintentionally from the body. Set read value in the struct.
289        request.set_partial_body_bytes(partial_body_bytes);
290
291        let mut matched_view: Option<&SinglePath> = None;
292
293        let binding = paths.read().unwrap();
294        for path in binding.iter() {
295            if request.pathname == path.name {
296                matched_view = Some(&path);
297            }
298        }
299
300        if let Some(view) = matched_view {
301            serve_page(request, view);
302        } else {
303            serve_not_found(request);
304        }
305    }
306
307    fn serve_page(request: Request, matched_path: &SinglePath) {
308        let response = Response::new(request.clone());
309        (matched_path.view)(request, response);
310    }
311
312    fn serve_not_found(request: Request) {
313        let mut response = Response::new(request);
314        response.html(404, "404 NOT FOUND".to_string());
315        response.send();
316    }
317}