use super::*;
#[derive(Debug, Clone)]
pub struct DnsCacheEntry {
pub addrs: Vec<SocketAddr>,
pub expires_at: Instant,
}
impl DnsCacheEntry {
pub fn is_expired(&self, now: Instant) -> bool {
now >= self.expires_at
}
}
#[derive(Debug)]
pub struct DnsCache {
entries: HashMap<(String, u16), DnsCacheEntry>,
capacity: usize,
}
impl DnsCache {
pub fn new(capacity: usize) -> Self {
Self {
entries: HashMap::new(),
capacity,
}
}
pub fn insert(
&mut self,
host: &str,
port: u16,
addrs: Vec<SocketAddr>,
ttl: Duration,
now: Instant,
) {
if ttl.is_zero() || addrs.is_empty() || self.capacity == 0 {
return;
}
let key = (normalize_host(host), port);
if !self.entries.contains_key(&key) && self.entries.len() >= self.capacity {
self.evict_earliest();
}
let expires_at = match now.checked_add(ttl) {
Some(expiry) => expiry,
None => now,
};
drop(
self.entries
.insert(key, DnsCacheEntry { addrs, expires_at }),
);
}
pub fn lookup(&mut self, host: &str, port: u16, now: Instant) -> Option<Vec<SocketAddr>> {
let key = (normalize_host(host), port);
match self.entries.get(&key) {
Some(entry) if !entry.is_expired(now) => Some(entry.addrs.clone()),
Some(_) => {
drop(self.entries.remove(&key));
None
}
None => None,
}
}
pub fn remove(&mut self, host: &str, port: u16) {
drop(self.entries.remove(&(normalize_host(host), port)));
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn capacity(&self) -> usize {
self.capacity
}
fn evict_earliest(&mut self) {
let victim = self
.entries
.iter()
.min_by_key(|(_, entry)| entry.expires_at)
.map(|(key, _)| key.clone());
if let Some(victim) = victim {
drop(self.entries.remove(&victim));
}
}
}