lest 0.2.1

A modular approach to a web server. Based on actix-web.
Documentation
use std::{fmt::{self, Display, Formatter}, sync::Mutex, time::Duration};

use crate::util::req::extract_real_ip;

use super::request::Request;

/// A `Guard` is a condition that must be met in order for a route to be accessed.
pub enum Guard {
    /// The host must match the given string.
    Host(&'static str),
    /// The port must match the given number.
    Port(u16), 
    /// The header must match the given key and value.
    Header(&'static str, &'static str),
    /// The query must match the given key and value.
    Query(&'static str, &'static str),
    /// All of the guards in the array must be met.
    Combine(Box<[Guard]>),
    /// The parameter must be present.
    Param(&'static str, &'static str),
    /// A function must be met.
    Fn(fn(&Request) -> bool),
    /// Any request will pass this guard.
    Any(),
    /// Too many requests within a number of seconds will fail.
    Rate(u32, u32),
}

impl Guard {
    pub fn verify(&self, request: &Request) -> Result<bool, &'static str> {
        verify_guard(self, request)
    }
}

impl Display for Guard {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Guard::Host(host) => write!(f, "Host: {}", host),
            Guard::Port(port) => write!(f, "Port: {}", port),
            Guard::Header(header, value) => write!(f, "Header: {} = {}", header, value),
            Guard::Query(query, value) => write!(f, "Query: {} = {}", query, value),
            Guard::Combine(guards) => {
                write!(f, "Combine: [")?;
                for guard in guards.iter() {
                    write!(f, "{}, ", guard)?;
                }
                write!(f, "]")
            },
            Guard::Param(param, value) => write!(f, "Param: {} = {}", param, value),
            Guard::Fn(_) => write!(f, "Function"),
            Guard::Rate(limit, duration) => write!(f, "Rate: {} requests per {:?}", limit, duration),
            Guard::Any() => write!(f, "Any"),
        }
    }
}

impl Default for Guard {
    fn default() -> Self {
        Guard::Any()
    }
}

static RATE_LIMITS: Mutex<Vec<(String, u32, u32, u32, u32)>> = Mutex::new(Vec::new());

/// Verifies that the guard is met by the request.
pub fn verify_guard(guard: &Guard, request: &Request) -> Result<bool, &'static str> {
    match guard {
        Guard::Host(host) => if request.host == *host {
            Ok(true)
        } else {
            Err("Invalid host.")
        },
        Guard::Port(port) => if request.port == *port {
            Ok(true)
        } else {
            Err("Invalid port.")
        },
        Guard::Header(header, value) => if request.get_header(header).map_or(false, |v| v == *value) {
            Ok(true)
        } else {
            Err("Header is not present or does not match.")
        },
        Guard::Query(query, value) => if request.get_query(query).map_or(false, |v| v == *value) {
            Ok(true)
        } else {
            Err("Query parameter is not present or does not match.")
        },
        Guard::Combine(guards) => {
            let erred = guards.iter().map(|g| verify_guard(g, request)).collect::<Vec<Result<bool, &'static str>>>();
            let erred_clone = erred.clone();
            if erred.into_iter().all(|e| e.is_ok() && e.unwrap()) {
                return Ok(true);
            } else {
                let mut guards_v: Vec<&'static str> = Vec::new();
                guards_v.push("One or more guards failed:");
                for erre in erred_clone.into_iter() {
                    if erre.is_ok() && erre.unwrap() {
                        continue;
                    }
                    let err = erre.unwrap_err();
                    guards_v.push(err);
                }
                let stringified: &'static str = Box::leak(guards_v.join("\n").into_boxed_str());
                return Err(stringified);
            }
        },
        Guard::Param(param, value) => if request.get_param(param).map_or(false, |v| v == *value) {
            Ok(true)
        } else {
            Err("Parameter is not present.")
        },
        Guard::Fn(f) => if f(request) {
            Ok(true)
        } else {
            Err("A custom guard failed.")
        },
        Guard::Rate(limit, duration) => {
            let rl = &RATE_LIMITS;
            let limits = rl.lock();
            if limits.is_err() {
                return Err("Unable to process a guard.");
            }
            let mut limits = limits.unwrap();
            let ip = extract_real_ip(&request.actix).unwrap_or_default();
            let mut found = false;
            for limit_entry in limits.iter_mut() {
                if limit_entry.0 == ip {
                    found = true;
                    if limit_entry.1 >= limit_entry.4 {
                        return Err("Rate limit exceeded.");
                    }
                    limit_entry.1 += 1;
                    break;
                }
            }
            if !found {
                limits.push((ip, 1, 0, *duration, *limit));
            }
            Ok(true)
        },
        Guard::Any() => Ok(true),
    }
}

/// Starts the event loop, resetting the rate limits for each guard.
pub fn start_guard_event_loop() {
    std::thread::spawn(|| {
        loop {
            std::thread::sleep(Duration::from_secs(1));
            let rl = &RATE_LIMITS;
            let mut limits = rl.lock().unwrap();
            for limit in limits.iter_mut() {
                limit.2 += 1;
                if limit.2 >= limit.3 {
                    limit.1 = 0;
                    limit.2 = 0;
                }
            }
        }
    });
}