rusty_web/
lib.rs

1pub mod status;
2pub mod parser;
3pub mod request;
4pub mod headers;
5pub mod response;
6
7pub mod paths {
8    use crate::request::Request;
9    use crate::response::Response;
10
11    pub type Paths = Vec<Path<fn(Request, Response)>>;
12    pub type SinglePath = Path<fn(Request, Response)>;
13
14    /// Path accepts pathname and view
15    pub struct Path<T> {
16        pub name: String,
17        pub view: T,
18    }
19
20    impl<T> Path<T> {
21        pub fn new(name: &str, view: T) -> Self {
22            let name = name.to_string();
23
24            return Self {
25                name,
26                view,
27            };
28        }
29    }
30}
31
32
33pub mod server {
34    use std::net::{Shutdown, TcpListener, TcpStream};
35    use std::sync::{Arc, RwLock};
36    use std::sync::atomic::{AtomicBool, Ordering};
37    use std::thread::spawn;
38    use crate::headers::{parse_request_method_header, extract_headers};
39    use crate::paths::{Paths, SinglePath};
40    use crate::request::{Request};
41    use crate::response::Response;
42
43    /// Example usage
44    /// ```rust
45    /// use rusty_web::paths::{Path, Paths};
46    /// use rusty_web::request::Request;
47    /// use rusty_web::response::Response;
48    /// use rusty_web::server::run_server;
49    /// use rusty_web::status::Status;
50    ///
51    /// fn home(request: Request, mut response: Response) {
52    ///    response.html(Status::Ok, "Home Page".to_string()).send();
53    /// }
54    ///
55    /// fn main() {
56    ///    let paths: Paths = vec![
57    ///         Path::new("/", home),
58    ///    ];
59    ///
60    ///    run_server("0.0.0.0:8080", paths);
61    /// }
62    /// ```
63    pub fn run_server(listen_address: &str, paths: Paths) {
64        println!("Running server in: http://{}", listen_address);
65        let tcp = TcpListener::bind(listen_address);
66
67        match tcp {
68            Ok(listener) => {
69                listen_connections(listener, paths);
70            }
71
72            Err(_) => {
73                eprintln!("Failed to listen stream");
74            }
75        }
76    }
77
78    pub fn listen_connections(listener: TcpListener, paths: Paths) {
79        let paths_lock = Arc::new(RwLock::new(paths));
80
81        for stream in listener.incoming() {
82            match stream {
83                Ok(stream) => {
84                    let paths = Arc::clone(&paths_lock);
85
86                    spawn(move || {
87                        serve_client(stream, paths);
88                    });
89                }
90
91                Err(error) => {
92                    print!("Error receiving stream: {}", error);
93                }
94            }
95        }
96    }
97
98    pub struct Context {
99        /// A same tcp stream can be used to serve multiple pages. Setting accept_next will continue
100        /// to use same connection. Make sure to set `accept_next` to false if request
101        /// body is not read completely. It is passed to both Request struct.
102        pub accept_next: AtomicBool,
103    }
104
105    impl Context {
106        pub fn dont_wait(&self) {
107            self.accept_next.store(false, Ordering::Relaxed);
108        }
109    }
110
111    fn serve_client(stream: TcpStream, paths: Arc<RwLock<Paths>>) {
112        let context = Context {
113            accept_next: AtomicBool::new(true),
114        };
115
116        let context_ref = Arc::new(context);
117
118        while context_ref.accept_next.load(Ordering::Relaxed) {
119            let stream = stream.try_clone().expect("Error cloning stream");
120            decode_request(stream, paths.clone(), context_ref.clone());
121        }
122    }
123
124    pub fn decode_request(mut stream: TcpStream, paths: Arc<RwLock<Paths>>,
125                          context: Arc<Context>) {
126        let mut header_start = String::new();
127        let mut partial_body_bytes = Vec::new();
128
129        const MAX_HEADER_SIZE: usize = 1024 * 1024; // 1 MiB
130        let headers_result = extract_headers(
131            &mut stream,
132            &mut header_start,
133            &mut partial_body_bytes,
134            MAX_HEADER_SIZE,
135        );
136
137        if !headers_result.is_ok() {
138            context.accept_next.store(false, Ordering::Relaxed);
139            return;
140        }
141
142        let headers = headers_result.unwrap();
143
144        let request_info = parse_request_method_header(&header_start.as_str());
145        if !request_info.is_some() {
146            context.accept_next.store(false, Ordering::Relaxed);
147            let _ = stream.shutdown(Shutdown::Both);
148            return;
149        }
150
151        let (method, raw_path) = request_info.unwrap();
152
153        // These states are shared among request and response
154        let body_read = Arc::new(AtomicBool::from(false));
155        let body_parsed = Arc::new(AtomicBool::from(false));
156
157        let mut request = Request::new(context, stream, method, raw_path, headers,
158                                       body_read.clone(), body_parsed.clone());
159        request.setup();
160
161        // Some bytes are read unintentionally from the body. Set read value in the struct.
162        request.set_partial_body_bytes(partial_body_bytes);
163
164        let mut matched_view: Option<&SinglePath> = None;
165
166        let binding = paths.read().unwrap();
167        for path in binding.iter() {
168            if request.pathname == path.name {
169                matched_view = Some(&path);
170            }
171        }
172
173        if let Some(view) = matched_view {
174            serve_page(request, view);
175        } else {
176            serve_not_found(request);
177        }
178    }
179
180    fn serve_page(request: Request, matched_path: &SinglePath) {
181        let response = Response::new(request.clone());
182        (matched_path.view)(request, response);
183    }
184
185    fn serve_not_found(request: Request) {
186        let mut response = Response::new(request);
187        response.html(404, "404 NOT FOUND".to_string());
188        response.send();
189    }
190}