use crate::http::{ requests::{Request, RequestStatus}, responses::{Response, UnitOrBoxedError}, }; use std::{ collections::HashMap, net::{Shutdown, TcpListener}, }; // Collection of handlers for requests pub struct Handlers { matchers: HashMap, } impl Handlers where FnT: Fn(Request) -> Response<'static>, { pub fn new() -> Self { Handlers:: { matchers: HashMap::new(), } } /// Add a request handler /// /// !! Be sure to bind this Handlers struct to a TcpListener to actually handle requests !! /// /// path: Path to match (no trailing /) /// handler: Function to handle the request. Must return a Response pub fn add_handler(&mut self, path: &str, handler: FnT) { self.matchers.insert(path.to_string(), handler); } /// Bind these handlers to a listener in order to handle incoming requests /// You will need to pass in a TcpListener /// /// !! Call this *after* adding all your handlers with add_handler !! /// /// listener: TcpListener to bind to pub fn bind(&self, listener: TcpListener) { for stream in listener.incoming() { match stream { Ok(stream) => { let request = match Request::parse_stream(stream) { RequestStatus::Ok(req) => req, RequestStatus::MalformedHTTP(stream) => { stream.shutdown(Shutdown::Both).unwrap_or_else(|_| { eprintln!("Failed to close malformed HTTP stream") }); return; } }; self.handle_req(request) .unwrap_or_else(|_| eprintln!("Failed to send handle request")); } Err(e) => { eprintln!("Failed to establish connection: {}", e); return; } } } } fn handle_req(&self, req: Request) -> UnitOrBoxedError { match self.matchers.get(&req.url.path) { Some(handler) => (*handler)(req).send(), None => Response::new(req, 404, "Not Found").send(), } } }