use std::{
collections::{HashMap, VecDeque},
net::IpAddr,
time::{Duration, Instant},
};
use crate::constants;
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct RecentByIp {
pub by_time: VecDeque<(IpAddr, Instant)>,
pub by_ip: HashMap<IpAddr, usize>,
pub max_connections_per_ip: usize,
pub time_limit: Duration,
}
impl Default for RecentByIp {
fn default() -> Self {
Self::new(None, None)
}
}
impl RecentByIp {
pub fn new(time_limit: Option<Duration>, max_connections_per_ip: Option<usize>) -> Self {
let (by_time, by_ip) = Default::default();
Self {
by_time,
by_ip,
time_limit: time_limit.unwrap_or(constants::MIN_PEER_RECONNECTION_DELAY),
max_connections_per_ip: max_connections_per_ip
.unwrap_or(constants::DEFAULT_MAX_CONNS_PER_IP),
}
}
pub fn is_past_limit_or_add(&mut self, ip: IpAddr) -> bool {
let now = Instant::now();
self.prune_by_time(now);
let count = self.by_ip.entry(ip).or_default();
if *count >= self.max_connections_per_ip {
true
} else {
*count += 1;
self.by_time.push_back((ip, now));
false
}
}
fn prune_by_time(&mut self, now: Instant) {
let age_limit = now - self.time_limit;
let split_off_idx = self.by_time.partition_point(|&(_, time)| time <= age_limit);
let updated_by_time = self.by_time.split_off(split_off_idx);
for (ip, _) in &self.by_time {
if let Some(count) = self.by_ip.get_mut(ip) {
*count -= 1;
if *count == 0 {
self.by_ip.remove(ip);
}
}
}
self.by_time = updated_by_time;
}
}