1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2025-01-31 06:22:59 +00:00
comunicapiv3/src/helpers/requests_limit_helper.rs

131 lines
3.0 KiB
Rust
Raw Normal View History

2021-01-22 19:39:14 +01:00
//! # Requests limit helper
//!
//! Handle the limitation of requests, depending on threshold criterias
2021-01-23 09:44:34 +01:00
2021-01-22 19:39:14 +01:00
use std::sync::{Arc, Mutex};
use crate::constants::LIMIT_COUNTER_LIFETIME;
2021-01-23 09:44:34 +01:00
use crate::controllers::routes::{LimitPolicy, Route};
use crate::data::error::{ExecError, ResultBoxError};
use crate::data::http_request_handler::HttpRequestHandler;
2021-01-22 19:39:14 +01:00
use crate::utils::date_utils;
2021-01-23 09:44:34 +01:00
use crate::utils::date_utils::time;
2021-01-22 19:39:14 +01:00
/// Information about a IP address limitation
struct IpInfo {
2021-01-23 09:44:34 +01:00
ip: String,
2021-01-22 19:39:14 +01:00
time_start: u64,
2021-01-23 09:44:34 +01:00
uri: String,
2021-01-22 19:39:14 +01:00
count: u64,
}
/// Limits cache
2021-01-23 09:44:34 +01:00
type Cache = Vec<IpInfo>;
2021-01-22 19:39:14 +01:00
static mut LIMITS_CACHE: Option<Arc<Mutex<Cache>>> = None;
/// Initialize limit cache storage
pub fn init() {
2021-01-23 09:44:34 +01:00
let limits_cache = Vec::new();
2021-01-22 19:39:14 +01:00
let limits_cache = Some(Arc::new(Mutex::new(limits_cache)));
unsafe {
LIMITS_CACHE = limits_cache;
}
}
/// Get access to the cache. This resource must absolutely be released as quickly as possible
2021-01-23 09:44:34 +01:00
fn get_cache() -> ResultBoxError<Arc<Mutex<Cache>>> {
let cache;
2021-01-22 19:39:14 +01:00
unsafe {
2021-01-23 09:44:34 +01:00
cache = LIMITS_CACHE.as_ref().unwrap().clone();
2021-01-22 19:39:14 +01:00
}
Ok(cache)
}
/// Clean cached information
pub fn clean_cache() -> ResultBoxError {
let time = date_utils::time();
2021-01-23 09:44:34 +01:00
let mut i = 0;
2021-01-22 19:39:14 +01:00
let cache = get_cache()?;
2021-01-23 09:44:34 +01:00
let mut cache = cache.lock().unwrap();
while i < cache.len() {
if cache[i].time_start + LIMIT_COUNTER_LIFETIME < time {
cache.remove(i);
} else {
i = i + 1;
}
}
Ok(())
}
/// Trigger limit helper at the beginning of requests
pub fn trigger_before(req: &HttpRequestHandler, route: &Route) -> ResultBoxError {
if route.limit_policy.is_none() {
return Ok(());
}
let max_count = route.limit_policy.get_count();
let ip = req.remote_ip();
let cache = get_cache()?;
let found = cache.lock().unwrap()
2021-01-22 19:39:14 +01:00
.iter()
2021-01-23 09:44:34 +01:00
.find(
|k| k.uri.eq(route.uri)
&& k.count >= max_count
&& k.ip.eq(&ip))
.is_some();
2021-01-22 19:39:14 +01:00
2021-01-23 09:44:34 +01:00
if found {
return Err(ExecError::boxed_new("Limit exceeded!"));
2021-01-22 19:39:14 +01:00
}
2021-01-23 09:44:34 +01:00
Ok(())
}
/// Trigger limit at the end of the request
pub fn trigger_after(is_success: bool, req: &HttpRequestHandler, route: &Route) -> ResultBoxError {
let need_trigger = match (&route.limit_policy, is_success)
{
(LimitPolicy::NONE, _) => false,
(LimitPolicy::ANY(_), _) => true,
(LimitPolicy::SUCCESS(_), res) => res,
(LimitPolicy::FAILURE(_), res) => !res,
};
if !need_trigger {
return Ok(());
}
let ip = req.remote_ip();
let cache = get_cache()?;
let mut cache = cache.lock().unwrap();
// We search for existing entry
for i in 0..cache.len() {
if cache[i].ip.eq(&ip) && cache[i].uri.eq(route.uri) {
cache[i].count += 1;
return Ok(());
}
}
// Otherwise we must add the entry to the table
cache.push(IpInfo {
ip,
time_start: time(),
uri: route.uri.to_string(),
count: 1,
});
2021-01-22 19:39:14 +01:00
Ok(())
}