Old Call Logic
This commit is contained in:
parent
fc2022c775
commit
7493bd4cb7
8 changed files with 1023 additions and 375 deletions
1217
Cargo.lock
generated
1217
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ansi_term = "*"
|
ansi_term = "*"
|
||||||
async-tungstenite = { version = "*" }
|
async-tungstenite = { version = "0.32.0", features = ["futures-03-sink", "futures-util", "handshake", "__rustls-tls", "async-native-tls", "async-std", "async-std-runtime", "async-tls", "gio", "gio-runtime", "glib", "openssl", "real-async-native-tls", "real-async-tls", "real-native-tls", "real-tokio-native-tls", "real-tokio-openssl", "real-tokio-rustls", "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-native-tls", "tokio-openssl", "tokio-runtime", "tokio-rustls-manual-roots", "tokio-rustls-native-certs", "tokio-rustls-webpki-roots", "url", "verbose-logging", "webpki-roots" ] }
|
||||||
axum = "*"
|
axum = "*"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
bytes = "*"
|
bytes = "*"
|
||||||
|
|
@ -18,7 +18,7 @@ futures = "*"
|
||||||
futures-util = "*"
|
futures-util = "*"
|
||||||
hex = "*"
|
hex = "*"
|
||||||
http = "*"
|
http = "*"
|
||||||
hyper = { version = "*", features = ["full"] }
|
hyper = { version = "1.8.1", features = ["full"] }
|
||||||
json = "*"
|
json = "*"
|
||||||
loom = "0.7.2"
|
loom = "0.7.2"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
|
|
@ -36,6 +36,7 @@ tokio-rustls = { version = "*" }
|
||||||
tokio-stream = "*"
|
tokio-stream = "*"
|
||||||
tokio-util = { version = "*", features = ["full"] }
|
tokio-util = { version = "*", features = ["full"] }
|
||||||
tokio_websocket_server = "0.1.0"
|
tokio_websocket_server = "0.1.0"
|
||||||
|
tower = "0.5.2"
|
||||||
tracing = "*"
|
tracing = "*"
|
||||||
tracing-subscriber = "*"
|
tracing-subscriber = "*"
|
||||||
tungstenite = "*"
|
tungstenite = "*"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use async_tungstenite::{WebSocketReceiver, WebSocketSender, tungstenite::Message};
|
use async_tungstenite::{WebSocketReceiver, WebSocketSender, tungstenite::Message};
|
||||||
use std::sync::Arc;
|
use std::{any::Any, sync::Arc};
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
RwLock,
|
RwLock,
|
||||||
mpsc::{UnboundedSender, unbounded_channel},
|
mpsc::{UnboundedSender, unbounded_channel},
|
||||||
|
|
@ -56,7 +56,21 @@ impl CallConnection {
|
||||||
if let Some(sender) = *self.user_id.read().await {
|
if let Some(sender) = *self.user_id.read().await {
|
||||||
cv = cv.with_sender(sender);
|
cv = cv.with_sender(sender);
|
||||||
}
|
}
|
||||||
if !cv.is_type(CommunicationType::ping) {
|
|
||||||
|
if cv.is_type(CommunicationType::webrtc_sdp)
|
||||||
|
| cv.is_type(CommunicationType::webrtc_ice)
|
||||||
|
| cv.is_type(CommunicationType::watch_stream)
|
||||||
|
{
|
||||||
|
line(
|
||||||
|
PrintType::CallIn,
|
||||||
|
&format!(
|
||||||
|
"{:?} : {} -> {} ",
|
||||||
|
&cv.type_id(),
|
||||||
|
&cv.get_sender(),
|
||||||
|
&cv.get_data(DataTypes::receiver_id).unwrap()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if !cv.is_type(CommunicationType::ping) {
|
||||||
line(PrintType::CallIn, &cv.to_json().to_string());
|
line(PrintType::CallIn, &cv.to_json().to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +130,8 @@ impl CallConnection {
|
||||||
group
|
group
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.broadcast(&broadcast.to_json().to_string());
|
.broadcast(&broadcast.to_json().to_string())
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -167,7 +182,7 @@ impl CallConnection {
|
||||||
|
|
||||||
let mut bc = cv.clone();
|
let mut bc = cv.clone();
|
||||||
bc = bc.add_data(DataTypes::sender_id, JsonValue::String(uid.to_string()));
|
bc = bc.add_data(DataTypes::sender_id, JsonValue::String(uid.to_string()));
|
||||||
group.broadcast(&bc.to_json().to_string());
|
group.broadcast(&bc.to_json().to_string()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,7 +202,7 @@ impl CallConnection {
|
||||||
let _streaming = cv.comm_type == CommunicationType::start_stream;
|
let _streaming = cv.comm_type == CommunicationType::start_stream;
|
||||||
let mut bc = cv.clone();
|
let mut bc = cv.clone();
|
||||||
bc = bc.add_data(DataTypes::sender_id, JsonValue::String(uid.to_string()));
|
bc = bc.add_data(DataTypes::sender_id, JsonValue::String(uid.to_string()));
|
||||||
group.broadcast(&bc.to_json().to_string());
|
group.broadcast(&bc.to_json().to_string()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,7 +223,8 @@ impl CallConnection {
|
||||||
group
|
group
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.send_to(&receiver_id, &bc.to_json().to_string());
|
.send_to(&receiver_id, &bc.to_json().to_string())
|
||||||
|
.await;
|
||||||
line(
|
line(
|
||||||
PrintType::CallOut,
|
PrintType::CallOut,
|
||||||
&format!("Forwarded WebRTC message to receiver: {}", receiver_str),
|
&format!("Forwarded WebRTC message to receiver: {}", receiver_str),
|
||||||
|
|
@ -243,12 +259,16 @@ impl CallConnection {
|
||||||
let (uid, cid) = { (*self.user_id.read().await, *self.call_id.read().await) };
|
let (uid, cid) = { (*self.user_id.read().await, *self.call_id.read().await) };
|
||||||
if let (Some(uid), Some(cid)) = (uid, cid) {
|
if let (Some(uid), Some(cid)) = (uid, cid) {
|
||||||
if let Some(group) = call_manager::get_group(cid).await {
|
if let Some(group) = call_manager::get_group(cid).await {
|
||||||
group.lock().await.broadcast(
|
group
|
||||||
&CommunicationValue::new(CommunicationType::client_disconnected)
|
.lock()
|
||||||
.add_data_str(DataTypes::user_id, uid.to_string())
|
.await
|
||||||
.to_json()
|
.broadcast(
|
||||||
.to_string(),
|
&CommunicationValue::new(CommunicationType::client_disconnected)
|
||||||
);
|
.add_data_str(DataTypes::user_id, uid.to_string())
|
||||||
|
.to_json()
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
group.lock().await.disconnect_member(uid).await;
|
group.lock().await.disconnect_member(uid).await;
|
||||||
}
|
}
|
||||||
call_manager::remove_inactive().await;
|
call_manager::remove_inactive().await;
|
||||||
|
|
|
||||||
|
|
@ -35,22 +35,22 @@ impl CallGroup {
|
||||||
|
|
||||||
pub async fn is_empty(&self) -> bool {
|
pub async fn is_empty(&self) -> bool {
|
||||||
for caller in self.callers.values() {
|
for caller in self.callers.values() {
|
||||||
if !caller.is_connected().await {
|
if caller.is_connected().await {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_to(&self, user_id: &Uuid, message: &str) {
|
pub async fn send_to(&self, user_id: &Uuid, message: &str) {
|
||||||
if let Some(caller) = self.callers.get(user_id) {
|
if let Some(caller) = self.callers.get(user_id) {
|
||||||
let _ = caller.send(Utf8Bytes::from(message.to_string()));
|
caller.send(Utf8Bytes::from(message.to_string())).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn broadcast(&self, message: &str) {
|
pub async fn broadcast(&self, message: &str) {
|
||||||
for caller in self.callers.values() {
|
for caller in self.callers.values() {
|
||||||
let _ = caller.send(Utf8Bytes::from(message.to_string()));
|
caller.send(Utf8Bytes::from(message.to_string())).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,87 @@
|
||||||
use crate::calls::call_group::CallGroup;
|
use crate::calls::call_group::CallGroup;
|
||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex; // Used only to protect CallGroup contents
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock; // Used to protect the global HashMap
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub static CALL_GROUPS: Lazy<RwLock<Mutex<HashMap<Uuid, Arc<Mutex<CallGroup>>>>>> =
|
// FIX 1: Simplify the global state. Only use RwLock to protect the HashMap.
|
||||||
Lazy::new(|| RwLock::new(Mutex::new(HashMap::new())));
|
// The inner Arc<Mutex<CallGroup>> protects the contents of each group.
|
||||||
|
pub static CALL_GROUPS: Lazy<RwLock<HashMap<Uuid, Arc<Mutex<CallGroup>>>>> =
|
||||||
|
Lazy::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
|
/// Retrieves a CallGroup by ID, if it exists.
|
||||||
pub async fn get_group(call_id: Uuid) -> Option<Arc<Mutex<CallGroup>>> {
|
pub async fn get_group(call_id: Uuid) -> Option<Arc<Mutex<CallGroup>>> {
|
||||||
|
// Acquire read lock (very fast, non-blocking for other readers)
|
||||||
CALL_GROUPS
|
CALL_GROUPS
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.lock()
|
.get(&call_id) // Use get(), as we only need to clone the Arc
|
||||||
.await
|
|
||||||
.get_mut(&call_id)
|
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves an existing group or creates a new one, performing a secret check.
|
||||||
pub async fn get_or_create_group(call_id: Uuid, secret: &str) -> Option<Arc<Mutex<CallGroup>>> {
|
pub async fn get_or_create_group(call_id: Uuid, secret: &str) -> Option<Arc<Mutex<CallGroup>>> {
|
||||||
let g = CALL_GROUPS.write().await;
|
// Phase 1: Check existence under a READ lock.
|
||||||
if let Some(group) = g.lock().await.get_mut(&call_id) {
|
{
|
||||||
if group.lock().await.secret_hash.eq(secret) {
|
let groups = CALL_GROUPS.read().await;
|
||||||
Some(group.clone())
|
if let Some(group_arc) = groups.get(&call_id) {
|
||||||
|
// Found it. Release the global READ lock and check the secret.
|
||||||
|
// This await on the group's lock only blocks access to that *specific* group.
|
||||||
|
let group_lock = group_arc.lock().await;
|
||||||
|
if group_lock.secret_hash.eq(secret) {
|
||||||
|
return Some(group_arc.clone());
|
||||||
|
} else {
|
||||||
|
return None; // Secret mismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // READ lock automatically released here.
|
||||||
|
|
||||||
|
// Phase 2: Not found, acquire a WRITE lock to create (only held for insertion).
|
||||||
|
let mut groups = CALL_GROUPS.write().await;
|
||||||
|
|
||||||
|
// FIX 2: Double-check (race condition prevention) - A group might have been created
|
||||||
|
// between the read lock release and the write lock acquisition.
|
||||||
|
if let Some(group_arc) = groups.get(&call_id) {
|
||||||
|
// Already created by another task, check secret again.
|
||||||
|
let group_lock = group_arc.lock().await;
|
||||||
|
if group_lock.secret_hash.eq(secret) {
|
||||||
|
return Some(group_arc.clone());
|
||||||
} else {
|
} else {
|
||||||
None
|
return None;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let cg = Arc::new(Mutex::new(CallGroup::new(secret.to_string())));
|
|
||||||
g.lock().await.insert(call_id, cg.clone());
|
|
||||||
Some(cg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Still not found, proceed with creation.
|
||||||
|
let new_group = Arc::new(Mutex::new(CallGroup::new(secret.to_string())));
|
||||||
|
groups.insert(call_id, new_group.clone());
|
||||||
|
|
||||||
|
// WRITE lock automatically released here.
|
||||||
|
Some(new_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes inactive groups.
|
||||||
pub async fn remove_inactive() {
|
pub async fn remove_inactive() {
|
||||||
|
// Collect keys to remove under a read lock first.
|
||||||
|
let groups_to_check: Vec<Uuid> = CALL_GROUPS.read().await.keys().cloned().collect();
|
||||||
|
|
||||||
let mut rem = Vec::new();
|
let mut rem = Vec::new();
|
||||||
for cg in CALL_GROUPS.read().await.lock().await.keys() {
|
for call_id in groups_to_check {
|
||||||
if CALL_GROUPS
|
// Retrieve the group Arc outside the global lock
|
||||||
.write()
|
if let Some(group_arc) = get_group(call_id).await {
|
||||||
.await
|
// FIX 3: Check for emptiness outside the global lock
|
||||||
.lock()
|
if group_arc.lock().await.is_empty().await {
|
||||||
.await
|
rem.push(call_id);
|
||||||
.get(cg)
|
}
|
||||||
.unwrap()
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.is_empty()
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
rem.push(cg.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for cg in rem {
|
|
||||||
CALL_GROUPS.write().await.lock().await.remove(&cg);
|
// Acquire write lock only to perform the removals.
|
||||||
|
if !rem.is_empty() {
|
||||||
|
let mut groups = CALL_GROUPS.write().await;
|
||||||
|
for call_id in rem {
|
||||||
|
groups.remove(&call_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ pub enum DataTypes {
|
||||||
error_type,
|
error_type,
|
||||||
accepted_ids,
|
accepted_ids,
|
||||||
uuid,
|
uuid,
|
||||||
|
settings,
|
||||||
|
settings_name,
|
||||||
chat_partner_id,
|
chat_partner_id,
|
||||||
iota_id,
|
iota_id,
|
||||||
user_id,
|
user_id,
|
||||||
|
|
@ -85,6 +87,9 @@ impl DataTypes {
|
||||||
match normalized.as_str() {
|
match normalized.as_str() {
|
||||||
"errortype" => DataTypes::error_type,
|
"errortype" => DataTypes::error_type,
|
||||||
"chatpartnerid" => DataTypes::chat_partner_id,
|
"chatpartnerid" => DataTypes::chat_partner_id,
|
||||||
|
"uuid" => DataTypes::uuid,
|
||||||
|
"settings" => DataTypes::settings,
|
||||||
|
"settingsname" => DataTypes::settings_name,
|
||||||
"iotaid" => DataTypes::iota_id,
|
"iotaid" => DataTypes::iota_id,
|
||||||
"userid" => DataTypes::user_id,
|
"userid" => DataTypes::user_id,
|
||||||
"userids" => DataTypes::user_ids,
|
"userids" => DataTypes::user_ids,
|
||||||
|
|
@ -164,6 +169,9 @@ pub enum CommunicationType {
|
||||||
error_invalid_secret,
|
error_invalid_secret,
|
||||||
error_invalid_private_key,
|
error_invalid_private_key,
|
||||||
success,
|
success,
|
||||||
|
settings_save,
|
||||||
|
settings_load,
|
||||||
|
settings_list,
|
||||||
message,
|
message,
|
||||||
message_send,
|
message_send,
|
||||||
message_live,
|
message_live,
|
||||||
|
|
@ -214,6 +222,9 @@ impl CommunicationType {
|
||||||
|
|
||||||
match normalized.as_str() {
|
match normalized.as_str() {
|
||||||
"error" => CommunicationType::error,
|
"error" => CommunicationType::error,
|
||||||
|
"settingssave" => CommunicationType::settings_save,
|
||||||
|
"settingsload" => CommunicationType::settings_load,
|
||||||
|
"settingslist" => CommunicationType::settings_list,
|
||||||
"success" => CommunicationType::success,
|
"success" => CommunicationType::success,
|
||||||
"message" => CommunicationType::message,
|
"message" => CommunicationType::message,
|
||||||
"messagelive" => CommunicationType::message_live,
|
"messagelive" => CommunicationType::message_live,
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ impl ClientConnection {
|
||||||
.with_sender(sender_id)
|
.with_sender(sender_id)
|
||||||
.add_data_str(DataTypes::call_id, call_id.to_string())
|
.add_data_str(DataTypes::call_id, call_id.to_string())
|
||||||
.add_data_str(DataTypes::receiver_id, receiver_id.to_string())
|
.add_data_str(DataTypes::receiver_id, receiver_id.to_string())
|
||||||
.add_data_str(DataTypes::sender_id, sender_id.to_string())
|
.add_data_str(DataTypes::sender_id, sender_id.to_string());
|
||||||
|
|
||||||
target_rho.message_to_client(distribute).await;
|
target_rho.message_to_client(distribute).await;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -281,24 +281,19 @@ impl IotaConnection {
|
||||||
let interested_ids: Vec<Uuid> = Vec::new();
|
let interested_ids: Vec<Uuid> = Vec::new();
|
||||||
|
|
||||||
// Process contacts and add call information
|
// Process contacts and add call information
|
||||||
if let Some(_contacts_data) = cv.get_data(DataTypes::user_ids) {
|
if let Some(contacts_data) = cv.get_data(DataTypes::user_ids) {
|
||||||
// Parse contacts JSON array and enrich with call data
|
/*let contacts: Vec<(Uuid, String)> = parse_contacts(contacts_data);
|
||||||
// This would need proper JSON parsing implementation
|
for contact in &contacts {
|
||||||
// For now, placeholder logic:
|
interested_ids.push(contact.user_id);
|
||||||
|
}
|
||||||
// Extract user IDs from contacts
|
|
||||||
// let contacts: Vec<ContactInfo> = parse_contacts(contacts_data);
|
|
||||||
// for contact in &contacts {
|
|
||||||
// interested_ids.push(contact.user_id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Get call invites for receiver
|
// Get call invites for receiver
|
||||||
// let invites = CallManager::get_call_invites(receiver_id).await;
|
let invites = CallManager::get_call_invites(receiver_id).await;
|
||||||
|
|
||||||
// Enrich contacts with call information
|
// Enrich contacts with call information
|
||||||
// let enriched_contacts = enrich_with_calls(contacts, invites);
|
let enriched_contacts = enrich_with_calls(contacts, invites);
|
||||||
|
|
||||||
// cv = cv.add_data(DataTypes::user_ids, enriched_contacts.into());
|
cv = cv.add_data(DataTypes::user_ids, enriched_contacts.into());*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify OmegaConnection about user states
|
// Notify OmegaConnection about user states
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue