use std::mem;
use derive_more::{AsRef, Display, From, Into};
use medea_client_api_proto::{IceServer, PeerId, RoomId};
use crate::{
log::prelude as log,
utils::{generate_token, MpscOneshotSender},
};
#[derive(AsRef, Clone, Debug, Display, Eq, From, Into, PartialEq)]
#[as_ref(forward)]
pub struct IceUsername(String);
impl IceUsername {
#[must_use]
fn new(room_id: &RoomId, peer_id: PeerId) -> Self {
Self(format!("{}_{}", room_id, peer_id))
}
}
#[derive(AsRef, Clone, Debug, Display)]
pub struct IcePassword(String);
impl IcePassword {
pub const LENGTH: usize = 16;
#[inline]
#[must_use]
pub fn generate() -> Self {
Self(generate_token(Self::LENGTH))
}
}
#[derive(Debug)]
pub struct IceUser {
address: String,
username: IceUsername,
pass: IcePassword,
on_drop: Option<MpscOneshotSender<IceUsername>>,
}
impl IceUser {
#[inline]
#[must_use]
pub fn new_non_static(
address: String,
room_id: &RoomId,
peer_id: PeerId,
pass: IcePassword,
on_drop: MpscOneshotSender<IceUsername>,
) -> Self {
Self {
address,
username: IceUsername::new(&room_id, peer_id),
pass,
on_drop: Some(on_drop),
}
}
#[inline]
#[must_use]
pub fn new_static(address: String, username: String, pass: String) -> Self {
Self {
address,
username: IceUsername(username),
pass: IcePassword(pass),
on_drop: None,
}
}
#[must_use]
pub fn servers_list(&self) -> Vec<IceServer> {
let stun_url = vec![format!("stun:{}", self.address)];
let stun = IceServer {
urls: stun_url,
username: None,
credential: None,
};
let turn_urls = vec![
format!("turn:{}", self.address),
format!("turn:{}?transport=tcp", self.address),
];
let turn = IceServer {
urls: turn_urls,
username: Some(self.username.to_string()),
credential: Some(self.pass.to_string()),
};
vec![stun, turn]
}
#[inline]
#[must_use]
pub fn user(&self) -> &IceUsername {
&self.username
}
#[inline]
#[must_use]
pub fn pass(&self) -> &IcePassword {
&self.pass
}
}
impl Drop for IceUser {
fn drop(&mut self) {
if let Some(tx) = self.on_drop.take() {
let name = mem::take(&mut self.username.0);
if let Err(user) = tx.send(IceUsername(name)) {
log::warn!("Failed to cleanup IceUser: {}", user);
}
}
}
}
#[cfg(test)]
mod spec {
use futures::{channel::mpsc, StreamExt as _};
use super::*;
#[actix_rt::test]
async fn removes_from_coturn_on_drop() {
let (tx, mut rx) = mpsc::unbounded();
let user = IceUser::new_non_static(
String::new(),
&RoomId::from("foobar"),
PeerId(0),
IcePassword::generate(),
MpscOneshotSender::from(tx),
);
let user_name = user.username.clone();
drop(user);
assert_eq!(rx.next().await.unwrap(), user_name);
assert!(rx.next().await.is_none());
}
}