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)) }