use crate::prelude::*;
use threading::atomic;
#[inline]
#[must_use]
pub fn get_too_many_requests() -> Response<Bytes> {
let body = Bytes::from_static("<html>\
<head>\
<title>429 Too Many Requests</title>\
</head>\
<body>\
<center>\
<h1>429 Too Many Requests</h1>\
<hr>\
<p>You have requested resources from this server too many times. <i>Please Enhance Your Calm.</i></p>\
<p>Try to access this page again in a minute. If this error persists, please contact the website administrator.</p>\
</center>\
</body>\
</html>".as_bytes());
Response::builder()
.status(StatusCode::TOO_MANY_REQUESTS)
.header(
"content-type",
HeaderValue::from_static("text/html; charset=utf-8"),
)
.header("content-length", body.len().to_string())
.header("content-encoding", "identity")
.body(body)
.unwrap()
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
#[must_use]
pub enum Action {
Passed,
Send,
Drop,
}
#[derive(Debug, Clone)]
#[must_use]
pub struct Manager {
connection_map: Arc<dashmap::DashMap<IpAddr, usize>>,
max_requests: usize,
check_every: usize,
reset_seconds: f64,
time: Arc<(atomic::AtomicU64, atomic::AtomicU32)>,
iteration: Arc<atomic::AtomicUsize>,
}
impl LimitManager {
pub fn new(max_requests: usize, check_every: usize, reset_seconds: f64) -> Self {
let me = Self {
connection_map: Arc::new(dashmap::DashMap::new()),
max_requests,
check_every,
reset_seconds,
time: Arc::new((atomic::AtomicU64::new(0), atomic::AtomicU32::new(0))),
iteration: Arc::new(atomic::AtomicUsize::new(0)),
};
me.update_time();
me
}
fn update_time(&self) {
let dur = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("we're before 1970!?");
self.time.0.store(dur.as_secs(), atomic::Ordering::Relaxed);
self.time
.1
.store(dur.subsec_nanos(), atomic::Ordering::Relaxed);
}
fn get_time(&self) -> std::time::SystemTime {
let secs = self.time.0.load(atomic::Ordering::Relaxed);
let nanos = self.time.1.load(atomic::Ordering::Relaxed);
std::time::UNIX_EPOCH + Duration::new(secs, nanos)
}
pub fn disable(&mut self) -> &mut Self {
self.set_check_every(usize::MAX)
}
pub fn set_check_every(&mut self, check_every: usize) -> &mut Self {
self.check_every = check_every;
self
}
pub fn set_max_requests(&mut self, max_requests: usize) -> &mut Self {
self.max_requests = max_requests;
self
}
pub fn set_reset_seconds(&mut self, reset_seconds: f64) -> &mut Self {
self.reset_seconds = reset_seconds;
self
}
pub fn register(&self, addr: IpAddr) -> Action {
if self.check_every == usize::MAX
|| self.iteration.fetch_add(1, atomic::Ordering::Relaxed) + 1 < self.check_every
{
Action::Passed
} else {
self.iteration.store(0, atomic::Ordering::Release);
if self
.get_time()
.elapsed()
.unwrap_or(Duration::ZERO)
.as_secs_f64()
>= self.reset_seconds
{
self.update_time();
self.connection_map.clear();
Action::Passed
} else {
let requests = *self
.connection_map
.entry(addr)
.and_modify(|count| *count += 1)
.or_insert(1);
if requests <= self.max_requests {
Action::Passed
} else if requests <= self.max_requests * 3 {
Action::Send
} else {
Action::Drop
}
}
}
}
}
impl Default for LimitManager {
#[inline]
fn default() -> Self {
Self::new(10, 10, 10.)
}
}