73 lines
2.3 KiB
Rust
73 lines
2.3 KiB
Rust
|
use crate::http::{
|
||
|
requests::{Request, RequestStatus},
|
||
|
responses::{Response, UnitOrBoxedError},
|
||
|
};
|
||
|
use std::{
|
||
|
collections::HashMap,
|
||
|
net::{Shutdown, TcpListener},
|
||
|
};
|
||
|
|
||
|
// Collection of handlers for requests
|
||
|
pub struct Handlers<FnT> {
|
||
|
matchers: HashMap<String, FnT>,
|
||
|
}
|
||
|
|
||
|
impl<FnT> Handlers<FnT>
|
||
|
where
|
||
|
FnT: Fn(Request) -> Response<'static>,
|
||
|
{
|
||
|
pub fn new() -> Self {
|
||
|
Handlers::<FnT> {
|
||
|
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(),
|
||
|
}
|
||
|
}
|
||
|
}
|