Skip to content
Snippets Groups Projects
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))
}