use std::{
collections::{HashMap, HashSet},
ops::{Deref, DerefMut},
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};
use dashmap::DashMap;
use once_cell::sync::Lazy;
use serde::Serialize;
static STORE: Lazy<Arc<Store>> = Lazy::new(Store::arc);
pub fn store() -> Arc<Store> {
STORE.clone()
}
#[derive(Debug)]
pub struct Store {
user_map: DashMap<String , HashSet<String >>,
hash_traffic_map: DashMap<String , Traffic>,
conns_map: DashMap<String , HashSet<String >>,
conn_map: DashMap<String , Connection>,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct Traffic {
pub up: usize,
pub down: usize,
}
#[derive(Clone, Debug, Default, Serialize)]
pub struct UserStats {
pub up: usize,
pub down: usize,
pub conn_count: usize,
}
#[derive(Clone, Debug, Serialize)]
pub struct Connection {
pub conn_id: String,
pub user: String,
pub hash: String,
pub peer_addr: String,
pub req_addr: String,
pub traffic: Traffic,
pub padding: bool,
pub create_at: u64,
}
impl Store {
pub fn arc() -> Arc<Self> {
Arc::new(Store {
user_map: DashMap::new(),
hash_traffic_map: DashMap::new(),
conns_map: DashMap::new(),
conn_map: DashMap::new(),
})
}
pub fn get_traffic_all(&self) -> HashMap<String, UserStats> {
self.user_map
.iter()
.filter_map(|m| {
let user = m.key().clone();
let conns = self.get_conns_by_user(&user);
let conn_count = conns.map(|v| v.len()).unwrap_or(0);
self.get_traffic_by_user(&user).map(|t| {
(
user,
UserStats {
up: t.up,
down: t.down,
conn_count,
},
)
})
})
.collect()
}
pub fn get_conns_all(&self) -> HashMap<String, Vec<Connection>> {
self.user_map
.iter()
.filter_map(|m| {
let user = m.key().clone();
self.get_conns_by_user(&user).map(|conns| (user, conns))
})
.collect()
}
pub fn get_traffic_by_user(&self, user: &str) -> Option<Traffic> {
if let Some(hashs) = self.user_map.get(user) {
let (up, down) = hashs
.iter()
.map(|h| self.get_traffic_by_hash(h))
.filter(|t| t.is_some())
.map(|t| t.map(|t| (t.get_up(), t.get_down())).unwrap_or((0, 0)))
.reduce(|(up1, down1), (up2, down2)| (up1 + up2, down1 + down2))
.unwrap_or((0, 0));
Some(Traffic { up, down })
} else {
None
}
}
pub fn get_traffic_by_hash(&self, hash: &str) -> Option<Traffic> {
self.hash_traffic_map.get(hash).map(|t| t.deref().clone())
}
pub fn get_conns_by_user(&self, user: &str) -> Option<Vec<Connection>> {
if let Some(hashs) = self.user_map.get(user) {
let conns = hashs
.iter()
.filter_map(|h| self.get_conns_by_hash(h))
.flatten()
.collect();
Some(conns)
} else {
None
}
}
pub fn get_conns_by_hash(&self, hash: &str) -> Option<Vec<Connection>> {
if let Some(conn_ids) = self.conns_map.get(hash) {
let conns = conn_ids
.iter()
.filter_map(|conn_id| self.conn_map.get(conn_id))
.map(|c| c.clone())
.collect();
Some(conns)
} else {
None
}
}
pub fn insert_conn<S>(
&self,
user: S,
hash: S,
conn_id: S,
peer_addr: S,
req_addr: S,
padding: bool,
) where
S: Into<String>,
{
let user = user.into();
let hash = hash.into();
let conn_id = conn_id.into();
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time failed!")
.as_secs();
let conn = Connection {
user: user.to_string(),
hash: hash.to_string(),
conn_id: conn_id.to_string(),
peer_addr: peer_addr.into(),
req_addr: req_addr.into(),
traffic: Default::default(),
padding,
create_at: timestamp,
};
self.insert_hash(&user, &hash);
self.insert_conns(&hash, &conn_id);
self.conn_map.insert(conn_id, conn);
}
fn insert_hash(&self, user: &str, hash: &str) {
if let Some(mut v) = self.user_map.get_mut(user) {
v.insert(hash.to_string());
} else {
self.user_map
.insert(user.to_string(), HashSet::from_iter([hash.to_string()]));
}
}
fn insert_conns(&self, hash: &str, conn_id: &str) {
if let Some(mut conns) = self.conns_map.get_mut(hash) {
conns.insert(conn_id.to_string());
} else {
self.conns_map
.insert(hash.to_string(), HashSet::from_iter([conn_id.to_string()]));
}
}
pub fn delete_conn(&self, conn_id: &str) {
if let Some((_, conn)) = self.conn_map.remove(conn_id) {
if let Some(mut conns) = self.conns_map.get_mut(&conn.hash) {
conns.retain(|v| v != conn_id);
}
}
}
pub fn add_up(&self, conn_id: &str, v: usize) {
if let Some(mut conn) = self.conn_map.get_mut(conn_id) {
let hash = &conn.hash;
self.add_up_by_hash(hash, v);
conn.deref_mut().traffic.add_up(v);
}
}
pub fn add_down(&self, conn_id: &str, v: usize) {
if let Some(mut conn) = self.conn_map.get_mut(conn_id) {
let hash = &conn.hash;
self.add_donw_by_hash(hash, v);
conn.deref_mut().traffic.add_down(v);
}
}
fn add_up_by_hash(&self, hash: &str, v: usize) {
if let Some(mut t) = self.hash_traffic_map.get_mut(hash) {
t.add_up(v);
} else {
self.hash_traffic_map
.insert(hash.to_string(), Traffic { up: v, down: 0 });
}
}
fn add_donw_by_hash(&self, hash: &str, v: usize) {
if let Some(mut t) = self.hash_traffic_map.get_mut(hash) {
t.add_down(v);
} else {
self.hash_traffic_map
.insert(hash.to_string(), Traffic { up: 0, down: v });
}
}
}
impl Traffic {
pub fn get_up(&self) -> usize {
self.up
}
pub fn get_down(&self) -> usize {
self.down
}
pub fn add_up(&mut self, v: usize) {
self.up += v
}
pub fn add_down(&mut self, v: usize) {
self.down += v
}
}
#[cfg(test)]
mod tests {
use super::Store;
#[test]
fn insert_and_aggregate_traffic_by_user() {
let store = Store::arc();
store.insert_conn(
"alice",
"hash-a",
"conn-1",
"127.0.0.1:1",
"example.com:443",
false,
);
store.insert_conn(
"alice",
"hash-b",
"conn-2",
"127.0.0.1:2",
"example.com:80",
true,
);
store.add_up("conn-1", 5);
store.add_down("conn-1", 7);
store.add_up("conn-2", 11);
let traffic = store.get_traffic_by_user("alice").unwrap();
let all = store.get_traffic_all();
assert_eq!(traffic.up, 16);
assert_eq!(traffic.down, 7);
assert_eq!(all.get("alice").unwrap().conn_count, 2);
}
#[test]
fn get_conns_by_hash_and_delete_conn_updates_views() {
let store = Store::arc();
store.insert_conn(
"alice",
"hash-a",
"conn-1",
"127.0.0.1:1",
"example.com:443",
false,
);
store.insert_conn(
"alice",
"hash-a",
"conn-2",
"127.0.0.1:2",
"example.com:80",
true,
);
let before = store.get_conns_by_hash("hash-a").unwrap();
store.delete_conn("conn-1");
let after = store.get_conns_by_hash("hash-a").unwrap();
assert_eq!(before.len(), 2);
assert_eq!(after.len(), 1);
assert_eq!(after[0].conn_id, "conn-2");
}
#[test]
fn unknown_user_returns_none() {
let store = Store::arc();
assert!(store.get_traffic_by_user("missing").is_none());
assert!(store.get_conns_by_user("missing").is_none());
}
}