2025-02-06 20:31:51 +01:00
|
|
|
use std::collections::HashMap;
|
2025-04-13 16:02:55 +02:00
|
|
|
use std::sync::Arc;
|
2025-04-17 15:43:26 +02:00
|
|
|
use jsonwebtoken::get_current_timestamp;
|
2025-04-13 16:02:55 +02:00
|
|
|
use log::{info, trace};
|
2025-03-11 14:56:10 +01:00
|
|
|
use tokio::sync::RwLock;
|
2025-03-28 23:54:10 +01:00
|
|
|
use crate::model::api_proxy::UserConnectionPermission;
|
2025-02-06 20:31:51 +01:00
|
|
|
|
2025-04-17 15:43:26 +02:00
|
|
|
// TODO this should be bound to grace_period, because grace_period can be more than 2 secs
|
|
|
|
|
const GRACE_TIME_CHECK: u64 = 2;
|
|
|
|
|
|
2025-04-13 16:02:55 +02:00
|
|
|
pub struct UserConnectionGuard {
|
|
|
|
|
manager: Arc<ActiveUserManager>,
|
|
|
|
|
username: String,
|
|
|
|
|
}
|
|
|
|
|
impl Drop for UserConnectionGuard {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
let manager = self.manager.clone();
|
|
|
|
|
let username = self.username.clone();
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
manager.remove_connection(&username).await;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-17 15:43:26 +02:00
|
|
|
struct UserConnectionData {
|
|
|
|
|
connections: u32,
|
|
|
|
|
granted_grace: u32,
|
|
|
|
|
grace_ts: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl UserConnectionData {
|
|
|
|
|
fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
connections: 1,
|
|
|
|
|
granted_grace: 0,
|
|
|
|
|
grace_ts: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-06 20:31:51 +01:00
|
|
|
pub struct ActiveUserManager {
|
2025-04-17 15:43:26 +02:00
|
|
|
log_active_user: bool,
|
|
|
|
|
user: Arc<RwLock<HashMap<String, UserConnectionData>>>,
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for ActiveUserManager {
|
|
|
|
|
fn default() -> Self {
|
2025-04-13 16:02:55 +02:00
|
|
|
Self::new(false)
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ActiveUserManager {
|
2025-04-17 15:43:26 +02:00
|
|
|
pub fn new(log_active_user: bool) -> Self {
|
2025-02-06 20:31:51 +01:00
|
|
|
Self {
|
2025-04-17 15:43:26 +02:00
|
|
|
log_active_user,
|
2025-04-13 16:02:55 +02:00
|
|
|
user: Arc::new(RwLock::new(HashMap::new())),
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-16 20:28:02 +02:00
|
|
|
fn clone_inner(&self) -> Self {
|
|
|
|
|
Self {
|
2025-04-17 15:43:26 +02:00
|
|
|
log_active_user: self.log_active_user,
|
2025-04-16 20:28:02 +02:00
|
|
|
user: Arc::clone(&self.user),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-11 14:56:10 +01:00
|
|
|
pub async fn user_connections(&self, username: &str) -> u32 {
|
2025-04-17 15:43:26 +02:00
|
|
|
if let Some(connection_data) = self.user.read().await.get(username) {
|
|
|
|
|
return connection_data.connections;
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
0
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-28 23:54:10 +01:00
|
|
|
pub async fn connection_permission(&self, username: &str, max_connections: u32, grace_period: bool) -> UserConnectionPermission {
|
2025-04-17 15:43:26 +02:00
|
|
|
if let Some(connection_data) = self.user.write().await.get_mut(username) {
|
|
|
|
|
let current_connections = connection_data.connections;
|
|
|
|
|
if connection_data.granted_grace > 0 && (get_current_timestamp() - connection_data.grace_ts) > GRACE_TIME_CHECK {
|
|
|
|
|
trace!("User access denied, grace exhausted, too many connections: {username}");
|
|
|
|
|
return UserConnectionPermission::Exhausted;
|
|
|
|
|
}
|
2025-04-10 00:06:48 +02:00
|
|
|
let extra_con = u32::from(grace_period);
|
2025-03-28 23:54:10 +01:00
|
|
|
if current_connections < max_connections + extra_con {
|
2025-04-17 15:43:26 +02:00
|
|
|
connection_data.granted_grace += 1;
|
|
|
|
|
connection_data.grace_ts = get_current_timestamp();
|
2025-03-28 23:54:10 +01:00
|
|
|
return UserConnectionPermission::GracePeriod;
|
|
|
|
|
}
|
|
|
|
|
if current_connections >= max_connections {
|
|
|
|
|
trace!("User access denied, too many connections: {username}");
|
|
|
|
|
return UserConnectionPermission::Exhausted;
|
|
|
|
|
}
|
2025-04-17 15:43:26 +02:00
|
|
|
connection_data.granted_grace = 0;
|
2025-03-28 23:54:10 +01:00
|
|
|
}
|
|
|
|
|
UserConnectionPermission::Allowed
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-11 14:56:10 +01:00
|
|
|
pub async fn active_users(&self) -> usize {
|
|
|
|
|
self.user.read().await.len()
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
|
2025-03-11 14:56:10 +01:00
|
|
|
pub async fn active_connections(&self) -> usize {
|
2025-04-17 15:43:26 +02:00
|
|
|
self.user.read().await.values().map(|c| c.connections as usize).sum()
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
|
2025-04-13 16:02:55 +02:00
|
|
|
pub async fn add_connection(&self, username: &str) -> UserConnectionGuard {
|
2025-03-11 14:56:10 +01:00
|
|
|
let mut lock = self.user.write().await;
|
2025-04-17 15:43:26 +02:00
|
|
|
if let Some(connection_data) = lock.get_mut(username) {
|
|
|
|
|
connection_data.connections += 1;
|
2025-03-11 14:56:10 +01:00
|
|
|
} else {
|
2025-04-17 15:43:26 +02:00
|
|
|
lock.insert(username.to_string(), UserConnectionData::new());
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
2025-03-11 14:56:10 +01:00
|
|
|
drop(lock);
|
2025-04-13 16:02:55 +02:00
|
|
|
|
|
|
|
|
self.log_active_user().await;
|
|
|
|
|
|
|
|
|
|
UserConnectionGuard {
|
|
|
|
|
manager: Arc::new(self.clone_inner()),
|
|
|
|
|
username: username.to_string(),
|
|
|
|
|
}
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
|
2025-04-13 16:02:55 +02:00
|
|
|
async fn remove_connection(&self, username: &str) {
|
2025-03-11 14:56:10 +01:00
|
|
|
let mut lock = self.user.write().await;
|
2025-04-17 15:43:26 +02:00
|
|
|
if let Some(connection_data) = lock.get_mut(username) {
|
|
|
|
|
connection_data.connections -= 1;
|
|
|
|
|
if connection_data.connections == 0 {
|
2025-03-11 14:56:10 +01:00
|
|
|
lock.remove(username);
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-03-11 14:56:10 +01:00
|
|
|
drop(lock);
|
2025-04-13 16:02:55 +02:00
|
|
|
|
|
|
|
|
self.log_active_user().await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn log_active_user(&self) {
|
2025-04-17 15:43:26 +02:00
|
|
|
if self.log_active_user {
|
2025-04-13 16:02:55 +02:00
|
|
|
let user_count = self.active_users().await;
|
|
|
|
|
let user_connection_count = self.active_connections().await;
|
|
|
|
|
info!("Active Users: {user_count}, Active User Connections: {user_connection_count}");
|
|
|
|
|
}
|
2025-02-06 20:31:51 +01:00
|
|
|
}
|
2025-03-21 15:56:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// mod tests {
|
|
|
|
|
// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
|
|
|
|
|
// use std::time::Instant;
|
|
|
|
|
// use std::thread;
|
|
|
|
|
//
|
|
|
|
|
// fn benchmark(ordering: Ordering, iterations: usize) -> u128 {
|
|
|
|
|
// let counter = Arc::new(AtomicUsize::new(0));
|
|
|
|
|
// let start = Instant::now();
|
|
|
|
|
//
|
|
|
|
|
// let handles: Vec<_> = (0..32)
|
|
|
|
|
// .map(|_| {
|
|
|
|
|
// let counter_ref = Arc::clone(&counter);
|
|
|
|
|
// thread::spawn(move || {
|
|
|
|
|
// for _ in 0..iterations {
|
|
|
|
|
// counter_ref.fetch_add(1, ordering);
|
|
|
|
|
// }
|
|
|
|
|
// })
|
|
|
|
|
// })
|
|
|
|
|
// .collect();
|
|
|
|
|
//
|
|
|
|
|
// for handle in handles {
|
|
|
|
|
// handle.join().unwrap();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// let duration = start.elapsed();
|
|
|
|
|
// duration.as_millis()
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// #[test]
|
|
|
|
|
// fn test_ordering() {
|
|
|
|
|
// let iterations = 1_000_000;
|
|
|
|
|
//
|
|
|
|
|
// let time_acqrel = benchmark(Ordering::SeqCst, iterations);
|
|
|
|
|
// println!("AcqRel: {} ms", time_acqrel);
|
|
|
|
|
//
|
|
|
|
|
// let time_seqcst = benchmark(Ordering::SeqCst, iterations);
|
|
|
|
|
// println!("SeqCst: {} ms", time_seqcst);
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// }
|