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}