lest 0.2.1

A modular approach to a web server. Based on actix-web.
Documentation
use std::{collections::HashMap, path};

use actix_web::{http::header::ContentType, web::Payload, HttpRequest};
use libcoerced::{generic::text_verify, json::json_verify};
use serde_json::Value;

use crate::{models::{app::{App, ResponseTemplate}, guard::Guard::{Host, Port}, request::Request, response::Response, route::Route}, types::method::Method, util::{qs::parse_query, req::{extract_host, extract_port}, ua::parse_user_agent}};

use super::pathfinder::process_path;

fn frag_normalize_path(path: &str) -> String {
    let mut normalized = path.to_string();
    if normalized.starts_with("/") {
        normalized = normalized[1..].to_string();
    }
    if normalized.ends_with("/") {
        normalized = normalized[..normalized.len() - 1].to_string();
    }
    normalized
}

fn frag_parse_headers(req: &HttpRequest) -> Vec<(String, String)> {
    req.headers().iter().map(|(key, value)| {
        (key.to_string(), value.to_str().unwrap().to_string())
    }).collect()
}

fn frag_create_request(req: HttpRequest, stream: Payload, method: Method, match_url: &str) -> Result<Request, String> {
    let user_agent = parse_user_agent(req.headers().get("User-Agent").unwrap().to_str().unwrap().to_string());
    let headers = frag_parse_headers(&req);
    let body = String::new();
    let path = req.uri().path().to_string();
    let (valid, params) = process_path(match_url, &path).unwrap_or((false, HashMap::new()));
    if !valid {
        return Err("Invalid path.".to_string());
    }
    let host = extract_host(&req);
    let port = extract_port(&req);
    Ok(Request {
        host: host.unwrap_or("".to_string()),
        port: port.unwrap_or(0),
        method,
        user_agent,
        headers,
        body,
        query: parse_query(req.query_string().to_string()),
        path: req.path().to_string(),
        actix: req,
        stream,
        params,
    })
}

fn frag_build_response(response: Response) -> actix_web::HttpResponse {
    let mut builder = actix_web::HttpResponse::build(actix_web::http::StatusCode::from_u16(u16::from(response.status)).unwrap());
    if response.redirect.is_some() {
        return builder.insert_header(("Location", response.redirect.unwrap())).finish();
    }
    for (key, value) in response.headers {
        builder.insert_header((key, value));
    }
    builder.insert_header(("Set-Cookie", response.cookie));
    builder.content_type(response.content_type);
    builder.body(response.body.unwrap_or("".to_string()))
}

fn frag_verify_request(data: &Request, route: &Route) -> (bool, String) {
    match route.guard {
        Host(_) => {
            if data.host.is_empty() {
                return (false, "Host is empty.".to_string());
            }
        },
        Port(_) => {
            if data.port == 0 {
                return (false, "Port is 0 or invalid.".to_string());
            }
        },
        _ => {}
    }

    let guard = (&route.guard).verify(data);
    if guard.is_err() {
        return (false, guard.unwrap_err().to_string());
    }

    if data.method != route.method {
        return (false, "Method does not match.".to_string());
    }

    let request_verify = route.request_verify.as_ref();
    if let Some(left) = request_verify.left() {
        let s = text_verify(left, data.body.as_str());
        if !s {
            return (false, "Text verification failed.".to_string());
        }
        return (true, "".to_string());
    }
    if let Some(right) = request_verify.right() {
        if let Ok(json_body) = serde_json::from_str::<Value>(&data.body) {
            let s = json_verify(Box::new(right.clone()), json_body);
            if !s {
                return (false, "JSON verification failed.".to_string());
            }
            return (true, "".to_string());
        }
    }
    (true, "".to_string())
}
fn frag_verify_response(data: &Response, route: &Route) -> bool {
    for (status, verify) in route.response_verify.iter() {
        if status.into_u16() == data.status {
            let response_fn = verify.as_ref();
            if let Some(left) = response_fn.left() {
                if let Some(body) = data.body.clone() {
                    return text_verify(left, body.as_str());
                }
            }
            if let Some(right) = response_fn.right() {
                if let Some(body) = data.body.clone() {
                    if let Ok(json_body) = serde_json::from_str::<Value>(&body) {
                        return json_verify(Box::new(right.clone()), json_body);
                    }
                }
            }
        }
    }
    true
}

pub async fn frag_handle_request(req: HttpRequest, stream: Payload, rewritten_path: String, method: Method, obj: &App<'_>, full_global_root: &str, response_templates: &Vec<ResponseTemplate>) -> actix_web::HttpResponse {
    let mut path = rewritten_path.as_str();
    for fragment in &obj.fragments {
        let descriptor = fragment.describe();
        let fragment_root = frag_normalize_path(&descriptor.root);
        let global_root = frag_normalize_path(&(full_global_root.to_owned() + &fragment_root));
        let global_root_route = global_root.clone();

        if path == global_root || path == global_root + "/" {
            let rfn = descriptor.root_fn.as_ref().unwrap();
            let request = frag_create_request(req, stream, method, &descriptor.root);
            if request.is_err() {
                for template in response_templates {
                    match template {
                        ResponseTemplate::InvalidRequest(path) => {
                            let extension = path::Path::new(path).extension().unwrap();
                            let mut body = std::fs::read(path).unwrap();
                            if body.is_empty() {
                                body = b"Invalid request\n*DEVELOPER* - Your template file is empty!".to_vec();
                                return actix_web::HttpResponse::BadRequest().body(body);
                            }
                            if extension == "html" {
                                return actix_web::HttpResponse::BadRequest().content_type(ContentType::html()).body(body);
                            }
                            return actix_web::HttpResponse::BadRequest().body(body);
                        },
                        _ => {}
                    }
                }
                return actix_web::HttpResponse::BadRequest().body("Invalid request.");
            }

            let request = request.unwrap();

            let rvf = frag_verify_request(&request, rfn);
            if rvf.0 == false {
                for template in response_templates {
                    match template {
                        ResponseTemplate::RequestVerificationFailed(path) => {
                            let extension = path::Path::new(path).extension().unwrap();
                            let mut body = std::fs::read(path).unwrap();
                            let body_str = String::from_utf8(body.clone()).unwrap();
                            body = if extension == "html" {
                                str::replace(&body_str, "{{error}}", ("<div>".to_string() + &str::replace(&rvf.1, "\n", "<br>") + "</div>").as_str()).into_bytes()
                            } else {
                                str::replace(&body_str, "{{error}}", &rvf.1).into_bytes()
                            };
                            if body.is_empty() {
                                body = ("Request verification failed:\n".to_string() + &rvf.1 + "\n*DEVELOPER* - Your template file is empty!").into_bytes();
                                return actix_web::HttpResponse::BadRequest().body(body);
                            }
                            if extension == "html" {
                                return actix_web::HttpResponse::BadRequest().content_type(ContentType::html()).body(body);
                            }
                            return actix_web::HttpResponse::BadRequest().body(body);
                        },
                        _ => {}
                    }
                }
                return actix_web::HttpResponse::BadRequest().body("Request verification failed:\n".to_string() + &rvf.1);
            }

            let response = (rfn.function)(request).await;
            if response.is_err() {
                return actix_web::HttpResponse::InternalServerError().body(response.unwrap_err().to_string());
            }

            let response = response.unwrap();

            if !frag_verify_response(&response, rfn) {
                for template in response_templates {
                    match template {
                        ResponseTemplate::ResponseVerificationFailed(path) => {
                            let extension = path::Path::new(path).extension().unwrap();
                            let mut body = std::fs::read(path).unwrap();
                            if body.is_empty() {
                                body = b"Response verification failed\n*DEVELOPER* - Your template file is empty!".to_vec();
                                return actix_web::HttpResponse::InternalServerError().body(body);
                            }
                            if extension == "html" {
                                return actix_web::HttpResponse::InternalServerError().content_type(ContentType::html()).body(body);
                            }
                            return actix_web::HttpResponse::InternalServerError().body(body);
                        },
                        _ => {}
                    }
                }
                return actix_web::HttpResponse::InternalServerError().body("Response verification failed.");
            }

            return frag_build_response(response);
        }

        for (route, route_box) in descriptor.routes {
            let mut full_path = global_root_route.clone() + "/" + &route;
            let route = route.to_string();
            if &route == "/" {
                full_path = global_root_route.clone();
            }
            if path.ends_with("/") {
                path = &path[..path.len() - 1];
            }

            if method != route_box.method {
                continue;
            }

            let http_parts = path.split("/").collect::<Vec<&str>>();
            let route_parts = full_path.split("/").collect::<Vec<&str>>();

            let mut okay = true;
            for (http, route) in http_parts.iter().zip(route_parts.iter()) {
                if route.starts_with(":") {
                    continue;
                }
                if route == &"*" {
                    continue;
                }
                if route == &"**" {
                    break;
                }
                if http != route {
                    okay = false;
                    break;
                }
            }

            if !okay {
                continue;
            }

            let request = frag_create_request(req, stream, method, &full_path);

            if request.is_err() {
                for template in response_templates {
                    match template {
                        ResponseTemplate::InvalidRequest(path) => {
                            let extension = path::Path::new(path).extension().unwrap();
                            let mut body = std::fs::read(path).unwrap();
                            if body.is_empty() {
                                body = b"Invalid request\n*DEVELOPER* - Your template file is empty!".to_vec();
                                return actix_web::HttpResponse::BadRequest().body(body);
                            }
                            if extension == "html" {
                                return actix_web::HttpResponse::BadRequest().content_type(ContentType::html()).body(body);
                            }
                            return actix_web::HttpResponse::BadRequest().body(body);
                        },
                        _ => {}
                    }
                }
                return actix_web::HttpResponse::BadRequest().body("Invalid request.");
            }

            let request = request.unwrap();

            let rvf = frag_verify_request(&request, &route_box);
            if rvf.0 == false {
                for template in response_templates {
                    match template {
                        ResponseTemplate::RequestVerificationFailed(path) => {
                            let extension = path::Path::new(path).extension().unwrap();
                            let mut body = std::fs::read(path).unwrap();
                            let body_str = String::from_utf8(body.clone()).unwrap();
                            body = if extension == "html" {
                                str::replace(&body_str, "{{error}}", ("<div>".to_string() + &str::replace(&rvf.1, "\n", "<br>") + "</div>").as_str()).into_bytes()
                            } else {
                                str::replace(&body_str, "{{error}}", &rvf.1).into_bytes()
                            };
                            if body.is_empty() {
                                body = ("Request verification failed:\n".to_string() + &rvf.1 + "\n*DEVELOPER* - Your template file is empty!").into_bytes();
                                return actix_web::HttpResponse::BadRequest().body(body);
                            }
                            if extension == "html" {
                                return actix_web::HttpResponse::BadRequest().content_type(ContentType::html()).body(body);
                            }
                            return actix_web::HttpResponse::BadRequest().body(body);
                        },
                        _ => {}
                    }
                }
                return actix_web::HttpResponse::BadRequest().body("Request verification failed:\n".to_string() + &rvf.1);
            }

            let response = (route_box.function)(request).await;
            if response.is_err() {
                return actix_web::HttpResponse::InternalServerError().body(response.unwrap_err().to_string());
            }

            let response = response.unwrap();

            if !frag_verify_response(&response, &route_box) {
                for template in response_templates {
                    match template {
                        ResponseTemplate::ResponseVerificationFailed(path) => {
                            let extension = path::Path::new(path).extension().unwrap();
                            let mut body = std::fs::read(path).unwrap();
                            if body.is_empty() {
                                body = b"Response verification failed\n*DEVELOPER* - Your template file is empty!".to_vec();
                                return actix_web::HttpResponse::InternalServerError().body(body);
                            }
                            if extension == "html" {
                                return actix_web::HttpResponse::InternalServerError().content_type(ContentType::html()).body(body);
                            }
                            return actix_web::HttpResponse::InternalServerError().body(body);
                        },
                        _ => {}
                    }
                }
                return actix_web::HttpResponse::InternalServerError().body("Response verification failed.");
            }

            return frag_build_response(response);
        }
    }
    for template in response_templates {
        match template {
            ResponseTemplate::NotFound(path) => {
                let extension = path::Path::new(path).extension().unwrap();
                let mut body = std::fs::read(path).unwrap();
                if body.is_empty() {
                    body = b"Not found\n*DEVELOPER* - Your template file is empty!".to_vec();
                    return actix_web::HttpResponse::NotFound().body(body);
                }
                if extension == "html" {
                    return actix_web::HttpResponse::NotFound().content_type(ContentType::html()).body(body);
                }
                return actix_web::HttpResponse::NotFound().body(body);
            },
            _ => {}
        }
    }
    actix_web::HttpResponse::NotFound().body("Not found.")
}