- Added detailed shared-stream/buffer/provider logging to trace lag, cache persistence, and session/provider lifecycle events.

- Connection registration failures now trigger an explicit disconnect so zombie sockets don’t linger.
- Shared stream shutdown now drops registry locks before releasing provider handles to prevent cross-lock stalls.
This commit is contained in:
euzu
2025-11-18 21:52:09 +01:00
parent 75f5a17461
commit 17e85fcfa4
10 changed files with 18 additions and 23 deletions
Generated
+3 -3
View File
@@ -1096,7 +1096,7 @@ dependencies = [
[[package]]
name = "frontend"
version = "3.2.3"
version = "3.2.4"
dependencies = [
"anyhow",
"base64",
@@ -3765,7 +3765,7 @@ dependencies = [
[[package]]
name = "shared"
version = "3.2.3"
version = "3.2.4"
dependencies = [
"base64",
"bitflags 2.10.0",
@@ -4314,7 +4314,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "tuliprox"
version = "3.2.3"
version = "3.2.4"
dependencies = [
"arc-swap",
"async-compression",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tuliprox"
version = "3.2.3"
version = "3.2.4"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+1 -1
View File
@@ -22,7 +22,7 @@ use axum::response::IntoResponse;
use chrono::{DateTime, Utc};
use futures::{StreamExt, TryStreamExt};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use log::{debug, error, info, log_enabled, trace, warn};
use log::{debug, error, log_enabled, trace, warn};
use reqwest::header::RETRY_AFTER;
use serde::Serialize;
use shared::model::{Claims, InputFetchMethod, PlaylistEntry, PlaylistItemType, StreamChannel, TargetType, UserConnectionPermission, XtreamCluster};
+5 -10
View File
@@ -260,14 +260,11 @@ impl ActiveUserManager {
let mut user_connections = self.connections.write().await;
// needs to be registered through socket connection to avoid race time conditions through short disconnect
if !user_connections
.key_by_addr
.contains_key(&fingerprint.addr) {
if !user_connections.key_by_addr.contains_key(&fingerprint.addr) {
return None;
}
user_connections
.key_by_addr.insert(fingerprint.addr, username.to_string());
user_connections.key_by_addr.insert(fingerprint.addr, username.to_string());
let connection_data = user_connections.by_key
.entry(username.to_string())
@@ -304,12 +301,10 @@ impl ActiveUserManager {
country,
);
connection_data.connections += 1;
let connections = connection_data.connections;
connection_data.streams.push(stream_info.clone());
user_connections
.key_by_addr
.insert(fingerprint.addr, username.to_string());
debug!( "Added new connection for {username} at {} (active_user_connections={})",
fingerprint.addr, connection_data.connections);
user_connections.key_by_addr.insert(fingerprint.addr, username.to_string());
debug!( "Added new connection for {username} at {} (active_user_connections={connections})", fingerprint.addr);
stream_info
}
+1 -1
View File
@@ -74,7 +74,7 @@ impl ConnectionManager {
self.event_manager.send_event(EventMessage::ActiveUser(ActiveUserConnectionChange::Updated(stream_info)));
} else {
warn!("Failed to register connection for user {username} at {}; disconnecting client", fingerprint.addr);
let _ = self.kick_connection(&fingerprint.addr).await;
let _ = self.kick_connection(&fingerprint.addr);
}
}
@@ -1,6 +1,6 @@
use crate::api::model::StreamError;
use bytes::Bytes;
use log::{debug, error, info, warn};
use log::{debug, error, warn};
use std::collections::VecDeque;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -97,7 +97,7 @@ where
if !self.completed {
self.completed = true;
let size = self.size.load(Ordering::SeqCst);
debug!("Persisted {} bytes to cache resource", size);
debug!("Persisted {size} bytes to cache resource");
(self.callback)(size);
}
}
+1 -1
View File
@@ -109,7 +109,7 @@ impl From<&ConfigInputAliasDto> for ConfigInputAlias {
Self {
id: dto.id,
name: dto.name.clone(),
url: get_base_url_from_str(&dto.url).map_or_else(|| dto.url.clone(), |base_url| base_url),
url: get_base_url_from_str(&dto.url).unwrap_or_else(|| dto.url.clone()),
username: dto.username.clone(),
password: dto.password.clone(),
priority: dto.priority,
+1 -1
View File
@@ -256,7 +256,7 @@ pub fn create_vod_info_from_item(target: &ConfigTarget, user: &ProxyUserCredenti
let name = &pli.name;
let extension = pli.get_additional_property("container_extension")
.map_or_else(|| extract_extension_from_url(&pli.url).map_or_else(String::new, std::string::ToString::to_string),
|v| get_string_from_serde_value(&v).map_or_else(String::new, |v| v));
|v| get_string_from_serde_value(&v).unwrap_or_default());
let added = last_updated / 1000;
format!(r#"{{
"info": {{}},
+2 -2
View File
@@ -1,10 +1,10 @@
[package]
name = "frontend"
version = "3.2.3"
version = "3.2.4"
edition = "2021"
[dependencies]
shared = { version = "3.2.3", path = "../shared" }
shared = { version = "3.2.4", path = "../shared" }
chrono = "0"
yew = "0.21"
yew-router = "0.18"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "shared"
version = "3.2.3"
version = "3.2.4"
edition = "2021"
[dependencies]