use std::{fmt::{self, Display, Formatter}, sync::Mutex, time::Duration};
use crate::util::req::extract_real_ip;
use super::request::Request;
pub enum Guard {
Host(&'static str),
Port(u16),
Header(&'static str, &'static str),
Query(&'static str, &'static str),
Combine(Box<[Guard]>),
Param(&'static str, &'static str),
Fn(fn(&Request) -> bool),
Any(),
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());
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),
}
}
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;
}
}
}
});
}