Skip to content
Snippets Groups Projects
Forked from codecraft / WebServer
64 commits behind the upstream repository.
handlers.rs 4.46 KiB
use std::path::PathBuf;

use tokio::{io::{AsyncReadExt, BufReader, AsyncBufReadExt, AsyncWriteExt}, net::TcpStream};

use crate::handling::{
    file_handlers::NamedFile,
    request::Request,
    response::{Outcome, Response, ResponseBody, Status},
    routes::Data,
};
use crate::setup::MountPoint;

pub async fn handle_connection(mut stream: TcpStream, mountpoints: Vec<MountPoint<'_>>) {
    let mut buf_reader = BufReader::new(&mut stream);
    let mut http_request: Vec<String> = Vec::with_capacity(100);
    loop {
        let mut buffer = String::new();
        let _ = buf_reader.read_line(&mut buffer).await.unwrap();
        if buffer == "\r\n" {
            break;
        }
        http_request.push(buffer);
    }

    println!("{:#?}", http_request);

    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: request_status_line
            .split(" ")
            .next()
            .unwrap()
            .parse()
            .unwrap(),
    };

    let mut data = Data {
        is_complete: false,
        buffer: vec![],
    };
    if request.can_have_body() {
        let length = if let Some(len) = request
            .headers
            .iter()
            .filter(|header| header.contains("Content-Length: "))
            .clone()
            .map(|header| {
                let header = header.strip_prefix("Content-Length: ").unwrap();
                header.trim().parse::<usize>()
            })
            .next()
        {
            if let Ok(size) = len {
                size
            } else {
                0
            }
        } else {
            0
        };
        let mut buffer: Vec<u8> = vec![];
        buf_reader.read_buf(&mut buffer).await.unwrap();
        if buffer.len() != length {
            let respone = len_not_defined(Status::LengthRequired).await;
            stream.write_all(respone.0.as_bytes()).await.unwrap();
            stream.write(&respone.1).await.unwrap();
            return;
        }
        data.is_complete = true;
        data.buffer = buffer;
    }

    let mut handled_response: Option<Outcome<Response, Status, Data>> = None;
    for mountpoint in mountpoints {
        if !request.uri.starts_with(mountpoint.mountpoint) {
            continue;
        }
        let mounted_request_uri = request.uri.strip_prefix(mountpoint.mountpoint).unwrap();
        for route in mountpoint.routes {
            if route.method != request.method {
                continue;
            }
            if !route.compare_uri(mounted_request_uri) {
                continue;
            }
            handled_response = Some((route.handler)(
                Request {
                    uri: mounted_request_uri,
                    ..request.clone()
                },
                data.clone(),
            ));

            if let Some(Outcome::Forward(_)) = handled_response {
                continue;
            }
            break;
        }
    }

    let response = match handled_response {
        Some(val) => match val {
            Outcome::Success(success) => (
                format!("HTTP/1.1 {}\r\n", success.status.unwrap())
                    + &success.headers.join("\r\n")
                    + "\r\n\r\n",
                success.body.get_data(),
            ),
            Outcome::Failure(error) => failure_handler(error).await,
            Outcome::Forward(_) => failure_handler(Status::NotFound).await,
        },
        None => failure_handler(Status::NotFound).await,
    };

    stream.write_all(response.0.as_bytes()).await.unwrap();
    stream.write(&response.1).await.unwrap();
}

async fn failure_handler(status: Status) -> (String, Vec<u8>) {
    let page_404 = NamedFile::open(PathBuf::from("404.html")).unwrap();
    (
        format!(
            "HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}\r\n\r\n",
            status,
            page_404.get_len(),
            page_404.get_mime()
        ),
        page_404.get_data(),
    )
}

async fn len_not_defined(status: Status) -> (String, Vec<u8>) {
    let page_411 = NamedFile::open(PathBuf::from("411.html")).unwrap();
    (
        format!(
            "HTTP/1.1 {status}\r\nContent-Length: {}\r\nContent-Type: {}\r\n\r\n",
            page_411.get_len(),
            page_411.get_mime()
        ),
        page_411.get_data(),
    )
}