haws/
lib.rs

1//! a crate for making a customizable http web server, similar to express from node.js or bao from bun.js
2//! <br>
3//! example:
4//! ```
5//! use haws::handlers::{ AppHandler };
6//! use haws::types::{ RequestBuffer };
7//! fn main() {
8//!     fn index(_buffer: RequestBuffer) -> String {
9//!         return "<h1>Hello world</h1>".to_string();
10//!     }
11//!     fn err_page(_buffer: RequestBuffer) -> String {
12//!         return "<h1>404 page not found</h1>".to_string();
13//!    }
14//!     let mut app = AppHandler::new("localhost".to_string(), 3000);
15//!     // if you put a '/' in the path paramater then every time the client navigates to the root of the page or just "localhost:3000" not "localhost:3000/home" or anything like that just the root no extra path after "localhost:3000", once you navigate to this route it will return the html in the dest attribute
16//!     app.route("/".to_string(), &index);
17//!     //  if you put a '.' in the path paramater then every time the client navigates to a route that doesn't exist it will return the html in the dest attribute
18//!     app.route(".".to_string(), &err_page);
19//!     app.serve();
20//! }
21
22//! ```
23pub mod handlers {
24    //! The module that contains all the handler structs
25    use std::net::TcpListener;
26    use std::net::TcpStream;
27    use std::io::prelude::*;
28    use std::collections::HashMap;
29    use colored;
30
31    pub struct AppHandler<'a> {
32        host: String,
33        port: i32,
34        routes: HashMap<String, &'a dyn Fn([u8; 1024]) -> String>,
35    }
36    impl<'a> AppHandler<'a> {
37        pub fn new(host: String, port: i32) -> AppHandler<'a> {
38            AppHandler {
39                host: host,
40                port: port,
41                routes: HashMap::new(),
42            }
43        }
44        pub fn route(&mut self, path: String, dest: &'a (dyn (Fn([u8; 1024]) -> String) + 'a)) {
45            self.routes.insert(path, dest);
46        }
47        pub fn serve(&mut self) {
48            let mut found_err_page = false;
49            self.routes.retain(|key, _value| {
50                
51                if *key == ".".to_string() {
52                    found_err_page = true;
53                }
54                true
55            });
56            if !found_err_page {
57                println!("Error: web server has no error page");
58                println!("Help: consider adding a error page");
59                println!("{}", colored::Colorize::green("app.route(\".\".to_string(), &err_page_function);"));
60                println!("Note: replace err_page_function with the name of your own function that returns html for the error page");
61                println!("Note: remember to add a parameter with type RequestBuffer ({})", colored::Colorize::green("fn err_page(buffer: RequestBuffer) -> String"));
62                println!("Note: remember to {} if you haven't already", colored::Colorize::green("use haws::types::{RequestBuffer};"));
63                panic!("UnsafeWebserver")
64            }
65            let mut handle_connection = |mut stream: TcpStream| {
66                let mut buffer = [0; 1024];
67                stream.read(&mut buffer).unwrap();
68                let mut gets = vec![];
69                let mut paths = vec![];
70                
71                self.routes.retain(|key, _value| {
72                    if *key != ".".to_string() {
73                    gets.push(format!("GET {} HTTP/1.1\r\n", key));
74                    paths.push(format!("{}", key));
75                    } 
76                    true
77                });
78                let mut idx: usize = 0;
79                let mut contents: String = String::new();
80                let mut found_route = false;
81                for g in gets.iter() {
82                    if buffer.starts_with(String::as_bytes(g)) {
83                        contents = self.routes.get(paths.get(idx).unwrap()).unwrap()(buffer);
84                        found_route = true;
85                    }
86                    idx += 1;
87                }
88                if !found_route {
89                    
90                    contents = self.routes.get(&".".to_string()).unwrap()(buffer);
91                    
92                }
93
94
95                let response = format!(
96                    "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
97                    contents.len(),
98                    contents
99                );
100                stream.write(response.as_bytes()).unwrap();
101                stream.flush().unwrap();
102            };
103
104            let listener = TcpListener::bind(format!("{}:{}", self.host, self.port)).unwrap();
105            for stream in listener.incoming() {
106                let ustream = stream.unwrap();
107
108                handle_connection(ustream);
109            }
110        }
111    }
112}
113
114pub mod types {
115    //! The module that contains all the types and type aliases
116    pub type RequestBuffer = [u8; 1024];
117
118}