guest_server/
lib.rs

1use std::{
2    collections::HashMap,
3    io::{BufRead, BufReader, Error, ErrorKind, Read, Write},
4    net::{SocketAddr, TcpListener, TcpStream},
5    sync::{Arc, RwLock},
6    thread,
7};
8
9/// A simple HTTP server implementation.
10///
11/// # Example
12///
13/// Create a server instance and register a GET route:
14///
15/// ```rust
16/// use guest_server::{Server,HttpResponse};
17///
18/// let mut server = Server::new();
19/// server.get("/", home);
20///
21/// fn home(query_params: Option<Vec<u8>>) -> HttpResponse {
22///       HttpResponse::new(200, Some("Hello, World!".to_string()))
23/// }
24///
25/// server.listener(80);
26/// ```
27///
28/// # Description
29/// This simple example shows how to create a 'Server' instance.
30/// Registers a GET route and simulates an HTTP request to obtain the response.
31
32#[derive(Eq, PartialEq, Hash, Clone)]
33pub enum HttpMethod {
34    GET,
35    POST,
36}
37
38type Routes = Arc<
39    RwLock<
40        HashMap<
41            (HttpMethod, String),
42            Arc<dyn Fn(Option<Vec<u8>>) -> HttpResponse + Send + Sync + 'static>,
43        >,
44    >,
45>;
46
47/// Represents an HTTP server.
48///
49/// This server listens for incoming HTTP requests, dispatches them to the correct handler based on the
50/// method and path, and sends back appropriate HTTP responses. It supports GET and POST routes.
51///
52/// The server is multi-threaded, handling each incoming connection in a new thread.
53pub struct Server {
54    routes: Routes, // A map storing routes and their associated handler functions.
55}
56
57impl Server {
58    /// Creates and initializes a new server instance.
59    ///
60    /// # Returns
61    /// A new instance of `Server` with an empty route configuration.
62    pub fn new() -> Self {
63        Self {
64            routes: Arc::new(RwLock::new(HashMap::new())),
65        }
66    }
67
68    /// Registers a route with a specific HTTP method, path, and handler.
69    ///
70    /// # Parameters
71    /// - 'method' : The HTTP method (GET, POST) for this route.
72    /// - 'path' : The route path (e.g., '/home').
73    /// - 'handler' : The closure that processes the request for this path.
74    fn route<F>(&mut self, method: HttpMethod, path: &str, handler: F)
75    where
76        F: Fn(Option<Vec<u8>>) -> HttpResponse + Send + Sync + 'static,
77    {
78        self.routes
79            .write()
80            .unwrap()
81            .insert((method, path.to_string()), Arc::new(handler));
82    }
83
84    /// Registers a GET route with a specified path and handler.
85    ///
86    /// # Parameters
87    /// - 'path' : The route path to register, e.g., '/home'.
88    /// - 'handler' : The closure that processes the request for this path.
89    pub fn get<F>(&mut self, path: &str, handler: F)
90    where
91        F: Fn(Option<Vec<u8>>) -> HttpResponse + Send + Sync + 'static,
92    {
93        self.route(HttpMethod::GET, path, handler);
94    }
95
96    /// Registers a POST route with a specified path and handler.
97    ///
98    /// # Parameters
99    /// - 'path' : The route path to register, e.g., '/submit'.
100    /// - 'handler' : The closure that processes the request for this path.
101    ///
102    /// # Example
103    ///
104    /// ```rust
105    /// use guest_server::{Server,HttpResponse};
106    /// let mut server = Server::new();
107    /// server.post("/submit",submit);
108    /// fn submit(body: Option<Vec<u8>>) -> HttpResponse {
109    ///     HttpResponse::new(200, Some("{\"key\":\"value\"}".to_string())).insert_header("Content-Type","application/json")
110    /// }
111    /// server.listener(8080);
112    /// ```
113    pub fn post<F>(&mut self, path: &str, handler: F)
114    where
115        F: Fn(Option<Vec<u8>>) -> HttpResponse + Send + Sync + 'static,
116    {
117        self.route(HttpMethod::POST, path, handler);
118    }
119
120    /// Starts the server and listens for incoming connections on the specified port.
121    ///
122    /// # Parameters
123    /// - 'port' : The port number to listen on.
124    pub fn listener(&self, port: u16) {
125        let addr: SocketAddr = format!("127.0.0.1:{}", port).parse().unwrap();
126        let listener = TcpListener::bind(addr).unwrap();
127        // Listen for incoming connections
128        for stream in listener.incoming() {
129            match stream {
130                Ok(stream) => {
131                    let routes = Arc::clone(&self.routes);
132                    thread::spawn(move || {
133                        if let Err(e) = Server::handle_connection(routes, stream) {
134                            eprintln!("Connection failed: {}", e);
135                        }
136                    });
137                }
138                Err(e) => eprintln!("Failed to accept connection: {}", e),
139            }
140        }
141    }
142
143    /// Handles the incoming TCP connection, processes the HTTP request, and sends back a response.
144    ///
145    /// # Parameters
146    /// - `routes`: The `Routes` object containing the routing information. This is used to match the
147    ///   incoming HTTP request's path and method to the appropriate handler function.
148    /// - `stream`: The TCP stream representing the connection to the client. This is used to read
149    ///   the request and send the response back to the client. The stream is mutable because it will
150    ///   be written to as part of generating the HTTP response.
151    fn handle_connection(routes: Routes, mut stream: TcpStream) -> Result<(), Error> {
152        let mut reader = BufReader::new(&stream);
153        let mut buffer_request = Vec::new();
154        let mut header_parsed = false;
155        let mut content_length = 0;
156        let mut method = Option::None;
157        let mut path = String::new();
158
159        loop {
160            let mut line = String::new();
161            let bytes_read = reader.read_line(&mut line)?;
162
163            if bytes_read == 0 {
164                break;
165            }
166
167            buffer_request.extend_from_slice(line.as_bytes());
168
169            if line == "\r\n" {
170                header_parsed = true;
171                break;
172            }
173
174            if line.starts_with("GET") || line.starts_with("POST") {
175                let parts: Vec<&str> = line.split_whitespace().collect();
176                if parts.len() >= 2 {
177                    method = match parts[0] {
178                        "GET" => Some(HttpMethod::GET),
179                        "POST" => Some(HttpMethod::POST),
180                        _ => None,
181                    };
182                    path = parts[1].to_string();
183                }
184            }
185
186            if line.to_lowercase().starts_with("content-length:") {
187                if let Ok(length) = line["content-length:".len()..].trim().parse::<usize>() {
188                    content_length = length;
189                }
190            }
191        }
192
193        if !header_parsed {
194            return Err(Error::new(ErrorKind::InvalidData, "Incomplete header"));
195        }
196
197        let mut body = Vec::new();
198        if content_length > 0 {
199            body.resize(content_length, 0);
200            reader.read_exact(&mut body)?;
201        }
202
203        buffer_request.extend_from_slice(&body);
204
205        let response = if let Some(method) = method {
206            Server::processing_response(&routes, body, method, path)
207        } else {
208            HttpResponse::new(405, None)
209        };
210
211        let res = Server::generate_http_response(&response);
212        Server::send_response(&mut stream, res);
213
214        Ok(())
215    }
216
217    /// Processes the HTTP response based on the method and path, invoking the registered handler.
218    ///
219    /// # Parameters
220    /// - 'routes' : A shared reference to the routes configuration.
221    /// - 'body' : The body of the request as a vector of bytes.
222    /// - 'method' : The HTTP method (GET, POST) for the request.
223    /// - 'path' : The requested path for the route.
224    ///
225    /// # Returns
226    /// The generated HttpResponse based on the handler or a 404 response if no handler is found.
227
228    fn processing_response(
229        routes: &Routes,
230        body: Vec<u8>,
231        method: HttpMethod,
232        path: String,
233    ) -> HttpResponse {
234        routes
235            .read()
236            .unwrap()
237            .get(&(method, path))
238            .cloned()
239            .map_or_else(
240                || HttpResponse::new(404, None),
241                |handler| handler(Some(body)),
242            )
243    }
244
245    /// Sends an HTTP response to the client.
246    ///
247    /// # Parameters
248    /// - 'stream' : The TCP stream to send the response over.
249    /// - 'response' : The response content (HTTP status, headers, body) to be sent.
250    ///
251    /// # Notes
252    /// This function writes the full HTTP response to the provided stream.
253    /// It logs an error if the response cannot be sent.
254    fn send_response(stream: &mut TcpStream, response: Vec<u8>) {
255        if let Err(e) = stream.write_all(&response) {
256            eprintln!("Failed to send response: {}", e);
257        }
258    }
259
260    /// Generates the full HTTP response string, including status code, headers, and body.
261    ///
262    /// # Parameters
263    /// - 'response' : The HttpResponse object containing status, headers, and body.
264    ///
265    /// # Returns
266    /// A vector of bytes representing the full HTTP response.
267    fn generate_http_response(response: &HttpResponse) -> Vec<u8> {
268        let mut response_string = format!(
269            "HTTP/1.1 {} {}\r\n",
270            response.status_code,
271            response.get_status_message() // Retrieves the status message based on status code
272        );
273        for (key, value) in &response.headers {
274            response_string.push_str(&format!("{}: {}\r\n", key, value)); // Add headers to the response
275        }
276        response_string.push_str("\r\n");
277
278        let mut res = response_string.into_bytes();
279        if let Some(body) = &response.body {
280            res.extend_from_slice(body.as_bytes()); // Append the response body if it exists
281        }
282
283        res
284    }
285}
286
287/// Represents an HTTP response, including status code, headers, and body.
288pub struct HttpResponse {
289    pub status_code: u16,
290    pub headers: HashMap<String, String>,
291    pub body: Option<String>,
292}
293
294impl HttpResponse {
295    /// Creates a new HttpResponse with the specified status code and body.
296    ///
297    /// # Parameters
298    /// - 'status_code' : The HTTP status code (e.g., 200, 404).
299    /// - 'body' : The response body content (optional).
300    pub fn new(status_code: u16, body: Option<String>) -> Self {
301        let mut headers = HashMap::new();
302        let default_content_type = if let Some(ref b) = body {
303            if b.starts_with('{') && b.ends_with('}') {
304                "application/json".to_string()
305            } else {
306                "text/plain".to_string()
307            }
308        } else {
309            "text/plain".to_string()
310        };
311        headers.insert("Content-Type".to_string(), default_content_type);
312        if let Some(ref b) = body {
313            headers.insert("Content-Length".to_string(), b.len().to_string());
314        }
315
316        HttpResponse {
317            status_code,
318            headers,
319            body,
320        }
321    }
322
323    /// Adds Cross-Origin Resource Sharing (CORS) headers to the `HttpResponse`.
324    ///
325    /// This method sets the `Access-Control-Allow-Origin` header to `*`, allowing
326    /// requests from any origin. This is useful for enabling CORS in scenarios where
327    /// the response needs to be accessible across different domains.
328    ///
329    /// # Usage
330    ///
331    /// ```rust
332    /// let response = HttpResponse::new(200, Some("Hello, World!".to_string())).cors();
333    /// ```
334    ///
335    /// # Returns
336    /// - `Self`: The `HttpResponse` instance with the CORS header added.
337    pub fn cors(mut self) -> Self {
338        self.headers
339            .insert("Access-Control-Allow-Origin".to_string(), "*".to_string());
340        self
341    }
342
343    /// Adds or updates a single header field.
344    ///
345    /// # Parameters
346    /// - 'key' : The header key.
347    /// - 'value' : The header value.
348    pub fn insert_header(mut self, key: &str, value: &str) -> Self {
349        self.headers.insert(key.to_string(), value.to_string());
350        self
351    }
352
353    /// Adds or updates multiple header fields in batch.
354    ///
355    /// # Parameters
356    /// - 'new_headers' : A HashMap containing the new header fields.
357    pub fn insert_headers(mut self, new_headers: HashMap<String, String>) -> Self {
358        self.headers.extend(new_headers);
359        self
360    }
361
362    /// Retrieves the description message for the status code.
363    ///
364    /// # Returns
365    /// A string representing the status message for the given status code.
366    pub fn get_status_message(&self) -> &'static str {
367        match self.status_code {
368            200 => "OK",
369            201 => "Created",
370            400 => "Bad Request",
371            404 => "Not Found",
372            500 => "Internal Server Error",
373            _ => "Unknown Status",
374        }
375    }
376}