handlers.rs 3.00 KiB
use std::{
fs,
io::{BufRead, BufReader, Write},
net::TcpStream,
path::PathBuf,
};
use crate::routing::methods::Method;
use crate::routing::routes::{Data, Outcome, Request, Response, Status};
use super::file_handlers::NamedFile;
pub fn handle_connection(mut stream: TcpStream, _mountpoint: &str) {
let buf_reader = BufReader::new(&mut stream);
let http_request: Vec<String> = buf_reader
.lines()
.map(|result| result.unwrap())
.take_while(|line| !line.is_empty())
.collect();
let request_status_line = http_request.get(0);
if request_status_line == None {
return;
}
let request_status_line = request_status_line.unwrap();
let request = Request {
uri: &request_status_line.split(" ").nth(1).unwrap(),
headers: http_request.clone(),
method: Method::Get,
};
let data = Data {
buffer: vec![],
is_complete: true,
};
let handled_response = match handler(request, data) {
Outcome::Success(success) => (
format!("HTTP/1.1 {} OK\r\n", success.status.unwrap().code)
+ &success.headers.join("\r\n")
+ "\r\n\r\n",
success.body.get_data(),
),
Outcome::Failure(error) => {
let content = fs::read("./404.html").unwrap();
(
format!(
"HTTP/1.1 {} NOT FOUND\r\nContent-Length: {}\r\nContent-Type: text/html\r\n\r\n",
error.code,
content.len()
),
content,
)
}
Outcome::Forward(_) => (
format!("HTTP/1.1 {} NOT FOUND", 404),
fs::read("./404.html").unwrap(),
),
};
stream.write_all(handled_response.0.as_bytes()).unwrap();
stream.write(&handled_response.1).unwrap();
}
fn parse_request<'a>(request: &'a str, mountpoint: &'a str) -> Result<PathBuf, &'a str> {
let uri = request.split(" ").nth(1).unwrap();
if !uri.starts_with(mountpoint) {
return Err("Request isn't on mountpoint");
}
let path_elements = uri
.split_once(mountpoint)
.map(|(_, rest)| rest)
.unwrap()
.split("/")
.filter(|&val| val != ".." && val != "")
.collect::<Vec<&str>>();
Ok(PathBuf::from(format!("./{}", path_elements.join("/"))))
}
fn handler(request: Request, _data: Data) -> Outcome<Response, Status, Data> {
let response = fileserver(request.uri);
let response = match response {
Ok(dat) => Response {
headers: vec![
format!("Content-Length: {}", dat.content_len),
format!("Content-Type: {}", dat.content_type),
],
status: Some(Status { code: 200 }),
body: Box::new(dat),
},
Err(_) => return Outcome::Failure(Status { code: 404 }),
};
Outcome::Success(response)
}
fn fileserver(path: &str) -> Result<NamedFile, Status> {
NamedFile::open(PathBuf::from("static/".to_string() + path))
}