Files
tuliprox/src/api/model/active_user_manager.rs
T

326 lines
11 KiB
Rust
Raw Normal View History

2025-04-30 11:04:01 +02:00
use crate::model::api_proxy::{ProxyUserCredentials, UserConnectionPermission};
use crate::model::config::Config;
use crate::utils::default_utils::{default_grace_period_millis, default_grace_period_timeout_secs};
use crate::utils::time_utils::current_time_secs;
use jsonwebtoken::get_current_timestamp;
2025-04-17 20:09:29 +02:00
use log::{debug, info};
use rand::RngCore;
2025-04-27 13:26:21 +02:00
use std::collections::HashMap;
use std::sync::Arc;
2025-04-30 11:04:01 +02:00
use std::sync::atomic::{AtomicU64, Ordering};
2025-03-11 14:56:10 +01:00
use tokio::sync::RwLock;
2025-02-06 20:31:51 +01:00
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;
});
}
}
#[derive(Clone, Debug)]
pub struct UserSession {
pub token: u32,
pub virtual_id: u32,
pub provider: String,
pub stream_url: String,
pub ts: u64,
pub permission: UserConnectionPermission,
}
struct UserConnectionData {
2025-04-28 19:52:59 +02:00
max_connections: u32,
connections: u32,
2025-04-27 13:26:21 +02:00
granted_grace: bool,
grace_ts: u64,
sessions: Vec<UserSession>,
}
impl UserConnectionData {
2025-04-30 11:04:01 +02:00
fn new(connections: u32, max_connections: u32) -> Self {
Self {
max_connections,
2025-04-30 11:04:01 +02:00
connections,
2025-04-27 13:26:21 +02:00
granted_grace: false,
grace_ts: 0,
sessions: Vec::new(),
}
}
}
2025-02-06 20:31:51 +01:00
pub struct ActiveUserManager {
2025-04-27 15:23:22 +02:00
grace_period_millis: u64,
grace_period_timeout_secs: u64,
log_active_user: bool,
user: Arc<RwLock<HashMap<String, UserConnectionData>>>,
2025-04-30 11:04:01 +02:00
gc_ts: Option<AtomicU64>,
2025-02-06 20:31:51 +01:00
}
impl ActiveUserManager {
2025-04-27 15:23:22 +02:00
pub fn new(config: &Config) -> Self {
let log_active_user = config.log.as_ref().is_some_and(|l| l.log_active_user);
let (grace_period_millis, grace_period_timeout_secs) = config.reverse_proxy.as_ref()
.and_then(|r| r.stream.as_ref())
.map_or_else(|| (default_grace_period_millis(), default_grace_period_timeout_secs()), |s| (s.grace_period_millis, s.grace_period_timeout_secs));
2025-02-06 20:31:51 +01:00
Self {
2025-04-27 15:23:22 +02:00
grace_period_millis,
grace_period_timeout_secs,
log_active_user,
2025-04-13 16:02:55 +02:00
user: Arc::new(RwLock::new(HashMap::new())),
2025-04-30 11:04:01 +02:00
gc_ts: Some(AtomicU64::new(current_time_secs())),
2025-02-06 20:31:51 +01:00
}
}
2025-04-16 20:28:02 +02:00
fn clone_inner(&self) -> Self {
Self {
2025-04-27 15:23:22 +02:00
grace_period_millis: self.grace_period_millis,
grace_period_timeout_secs: self.grace_period_timeout_secs,
log_active_user: self.log_active_user,
2025-04-16 20:28:02 +02:00
user: Arc::clone(&self.user),
2025-04-30 11:04:01 +02:00
gc_ts: None,
2025-04-16 20:28:02 +02:00
}
}
2025-03-11 14:56:10 +01:00
pub async fn user_connections(&self, username: &str) -> u32 {
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-04-30 11:04:01 +02:00
fn check_connection_permission(&self, username: &str, connection_data: &mut UserConnectionData) -> UserConnectionPermission {
let current_connections = connection_data.connections;
2025-04-30 11:04:01 +02:00
if current_connections < connection_data.max_connections {
// Reset grace period because user is back under max_connections
connection_data.granted_grace = false;
connection_data.grace_ts = 0;
return UserConnectionPermission::Allowed;
}
2025-04-30 11:04:01 +02:00
let now = get_current_timestamp();
// Check if user already used grace period
if connection_data.granted_grace {
if now - connection_data.grace_ts <= self.grace_period_timeout_secs {
// Grace timeout still active, deny connection
debug!("User access denied, grace exhausted, too many connections: {username}");
return UserConnectionPermission::Exhausted;
}
2025-04-30 11:04:01 +02:00
// Grace timeout expired, reset grace counters
connection_data.granted_grace = false;
connection_data.grace_ts = 0;
}
2025-04-30 11:04:01 +02:00
if self.grace_period_millis > 0 && current_connections == connection_data.max_connections {
// Allow grace period once
connection_data.granted_grace = true;
connection_data.grace_ts = now;
debug!("Granted grace period for user access: {username}");
return UserConnectionPermission::GracePeriod;
}
2025-04-30 11:04:01 +02:00
// Too many connections, no grace allowed
debug!("User access denied, too many connections: {username}");
UserConnectionPermission::Exhausted
}
2025-04-27 13:26:21 +02:00
pub async fn connection_permission(
&self,
username: &str,
max_connections: u32,
) -> UserConnectionPermission {
2025-04-27 15:23:22 +02:00
if max_connections > 0 {
if let Some(connection_data) = self.user.write().await.get_mut(username) {
2025-04-30 11:04:01 +02:00
return self.check_connection_permission(username, connection_data);
2025-04-27 15:23:22 +02:00
}
}
UserConnectionPermission::Allowed
}
2025-04-27 13:26:21 +02:00
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 {
self.user.read().await.values().map(|c| c.connections as usize).sum()
2025-02-06 20:31:51 +01:00
}
2025-04-28 19:52:59 +02:00
pub async fn add_connection(&self, username: &str, max_connections: u32) -> UserConnectionGuard {
2025-03-11 14:56:10 +01:00
let mut lock = self.user.write().await;
if let Some(connection_data) = lock.get_mut(username) {
connection_data.connections += 1;
2025-04-28 20:28:33 +02:00
connection_data.max_connections = max_connections;
2025-03-11 14:56:10 +01:00
} else {
2025-04-30 11:04:01 +02:00
lock.insert(username.to_string(), UserConnectionData::new(1, max_connections));
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;
if let Some(connection_data) = lock.get_mut(username) {
2025-04-27 13:26:21 +02:00
if connection_data.connections > 0 {
connection_data.connections -= 1;
}
// DO NOT reset granted_grace or grace_ts here!
// We must preserve the grace period state until connection_permission() checks it.
// if connection_data.connections == 0 {
// lock.remove(username);
// } else
// if connection_data.connections < connection_data.max_connections {
// connection_data.token = None;
// }
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;
}
fn find_user_session(token: u32, sessions: &[UserSession]) -> Option<&UserSession> {
2025-04-30 11:04:01 +02:00
sessions.iter().find(|&session| session.token == token)
}
fn new_user_session(virtual_id: u32, provider: &str, stream_url: &str, connection_permission: UserConnectionPermission) -> UserSession {
let session_token = rand::rng().next_u32();
UserSession {
token: session_token,
virtual_id,
provider: provider.to_string(),
stream_url: stream_url.to_string(),
ts: current_time_secs(),
permission: connection_permission,
}
}
2025-04-30 11:04:01 +02:00
pub async fn create_user_session(&self, user: &ProxyUserCredentials, virtual_id: u32, provider: &str, stream_url: &str, connection_permission: UserConnectionPermission) -> Option<u32> {
2025-04-30 11:30:09 +02:00
self.gc().await;
2025-04-28 19:52:59 +02:00
let mut lock = self.user.write().await;
2025-04-30 11:04:01 +02:00
if let Some(connection_data) = lock.get_mut(&user.username) {
let session = Self::new_user_session(virtual_id, provider, stream_url, connection_permission);
let token = session.token;
connection_data.sessions.push(session);
2025-04-30 11:04:01 +02:00
Some(token)
} else {
let mut connection_data = UserConnectionData::new(0, user.max_connections);
let session = Self::new_user_session(virtual_id, provider, stream_url, connection_permission);
let token = session.token;
connection_data.sessions.push(session);
lock.insert(user.username.to_string(), connection_data);
Some(token)
2025-04-28 19:52:59 +02:00
}
}
pub async fn get_user_session(&self, username: &str, token: u32) -> Option<UserSession> {
self.update_user_session(username, token).await
2025-04-28 19:52:59 +02:00
}
async fn update_user_session(&self, username: &str, token: u32) -> Option<UserSession> {
2025-04-28 19:52:59 +02:00
let mut lock = self.user.write().await;
if let Some(connection_data) = lock.get_mut(username) {
if connection_data.max_connections == 0 {
return Self::find_user_session(token, &connection_data.sessions).cloned();
}
// Separate mutable borrow of the session
let mut found_session_index = None;
for (i, session) in connection_data.sessions.iter().enumerate() {
if session.token == token {
found_session_index = Some(i);
break;
}
}
if let Some(index) = found_session_index {
let session_permission = connection_data.sessions[index].permission.clone();
if session_permission == UserConnectionPermission::GracePeriod {
2025-04-30 11:04:01 +02:00
let new_permission = self.check_connection_permission(username, connection_data);
connection_data.sessions[index].permission = new_permission;
}
return Some(connection_data.sessions[index].clone());
}
2025-04-28 19:52:59 +02:00
}
None
2025-04-28 19:52:59 +02:00
}
async fn log_active_user(&self) {
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-04-30 11:04:01 +02:00
async fn gc(&self) {
if let Some(gc_ts) = &self.gc_ts {
let ts = gc_ts.load(Ordering::SeqCst);
if current_time_secs() - ts > 10_800 { // 3 hours
let mut lock = self.user.write().await;
for (_, connection_data) in lock.iter_mut() {
connection_data.sessions.retain(|s| current_time_secs() - s.ts < 10_800);
}
2025-04-30 11:30:09 +02:00
gc_ts.store(current_time_secs(), Ordering::SeqCst);
2025-04-30 11:04:01 +02: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();
//
2025-04-28 20:28:33 +02:00
// 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);
// }
//
// }