gst-plugins-rs/net/webrtc/signalling/src/handlers/mod.rs

1420 lines
45 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use anyhow::{anyhow, Error};
use anyhow::{bail, Context};
use futures::prelude::*;
use futures::ready;
use gst_plugin_webrtc_protocol as p;
use p::PeerStatus;
use pin_project_lite::pin_project;
use std::collections::{HashMap, HashSet, VecDeque};
use std::pin::Pin;
use std::task::{Context as TaskContext, Poll};
use tracing::log::error;
use tracing::{info, instrument, warn};
type PeerId = String;
#[derive(Clone)]
struct Session {
id: String,
producer: PeerId,
consumer: PeerId,
}
impl Session {
fn other_peer_id(&self, id: &str) -> Result<&str, Error> {
if self.producer == id {
Ok(&self.consumer)
} else if self.consumer == id {
Ok(&self.producer)
} else {
bail!("Peer {id} is not part of {}", self.id)
}
}
}
pin_project! {
#[must_use = "streams do nothing unless polled"]
pub struct Handler {
#[pin]
stream: Pin<Box<dyn Stream<Item=(String, Option<p::IncomingMessage>)> + Send>>,
items: VecDeque<(String, p::OutgoingMessage)>,
peers: HashMap<PeerId, PeerStatus>,
sessions: HashMap<String, Session>,
consumer_sessions: HashMap<String, HashSet<String>>,
producer_sessions: HashMap<String, HashSet<String>>,
}
}
impl Handler {
#[instrument(level = "debug", skip(stream))]
/// Create a handler
pub fn new(
stream: Pin<Box<dyn Stream<Item = (String, Option<p::IncomingMessage>)> + Send>>,
) -> Self {
Self {
stream,
items: VecDeque::new(),
peers: Default::default(),
sessions: Default::default(),
consumer_sessions: Default::default(),
producer_sessions: Default::default(),
}
}
#[instrument(level = "trace", skip(self))]
fn handle(
mut self: Pin<&mut Self>,
peer_id: &str,
msg: p::IncomingMessage,
) -> Result<(), Error> {
match msg {
p::IncomingMessage::NewPeer => {
self.peers.insert(peer_id.to_string(), Default::default());
self.items.push_back((
peer_id.into(),
p::OutgoingMessage::Welcome {
peer_id: peer_id.to_string(),
},
));
Ok(())
}
p::IncomingMessage::SetPeerStatus(status) => self.set_peer_status(peer_id, &status),
p::IncomingMessage::StartSession(message) => {
self.start_session(&message.peer_id, peer_id)
}
p::IncomingMessage::Peer(peermsg) => self.handle_peer_message(peer_id, peermsg),
p::IncomingMessage::List => self.list_producers(peer_id),
p::IncomingMessage::EndSession(msg) => self.end_session(peer_id, &msg.session_id),
}
}
fn handle_peer_message(&mut self, peer_id: &str, peermsg: p::PeerMessage) -> Result<(), Error> {
let session_id = &peermsg.session_id;
let session = self
.sessions
.get(session_id)
.context(format!("Session {session_id} doesn't exist"))?
.clone();
if matches!(
peermsg.peer_message,
p::PeerMessageInner::Sdp(p::SdpMessage::Offer { .. })
) && peer_id == session.consumer
{
bail!(
r#"cannot forward offer from "{peer_id}" to "{}" as "{peer_id}" is not the producer"#,
session.producer,
);
}
self.items.push_back((
session.other_peer_id(peer_id)?.to_owned(),
p::OutgoingMessage::Peer(p::PeerMessage {
session_id: session_id.to_string(),
peer_message: peermsg.peer_message.clone(),
}),
));
Ok(())
}
fn stop_producer(&mut self, peer_id: &str) {
if let Some(session_ids) = self.producer_sessions.remove(peer_id) {
for session_id in session_ids {
if let Err(e) = self.end_session(peer_id, &session_id) {
error!("Could not end session {session_id}: {e:?}");
}
}
}
}
fn stop_consumer(&mut self, peer_id: &str) {
if let Some(session_ids) = self.consumer_sessions.remove(peer_id) {
for session_id in session_ids {
if let Err(e) = self.end_session(peer_id, &session_id) {
error!("Could not end session {session_id}: {e:?}");
}
}
}
}
#[instrument(level = "debug", skip(self))]
/// Remove a peer, this can cause sessions to be ended
fn remove_peer(&mut self, peer_id: &str) {
info!(peer_id = %peer_id, "removing peer");
let peer_status = match self.peers.remove(peer_id) {
Some(peer_status) => peer_status,
_ => return,
};
self.stop_producer(peer_id);
self.stop_consumer(peer_id);
for (id, p) in self.peers.iter() {
if !p.listening() {
continue;
}
let message = p::OutgoingMessage::PeerStatusChanged(PeerStatus {
roles: Default::default(),
meta: peer_status.meta.clone(),
peer_id: Some(peer_id.to_string()),
});
self.items.push_back((id.to_string(), message));
}
}
#[instrument(level = "debug", skip(self))]
/// End a session between two peers
fn end_session(&mut self, peer_id: &str, session_id: &str) -> Result<(), Error> {
let session = self
.sessions
.remove(session_id)
.with_context(|| format!("Session {session_id} doesn't exist"))?;
self.consumer_sessions
.entry(session.consumer.clone())
.and_modify(|sessions| {
sessions.remove(session_id);
});
self.producer_sessions
.entry(session.producer.clone())
.and_modify(|sessions| {
sessions.remove(session_id);
});
self.items.push_back((
session.other_peer_id(peer_id)?.to_string(),
p::OutgoingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.to_string(),
}),
));
Ok(())
}
/// List producer peers
#[instrument(level = "debug", skip(self))]
fn list_producers(&mut self, peer_id: &str) -> Result<(), Error> {
self.items.push_back((
peer_id.to_string(),
p::OutgoingMessage::List {
producers: self
.peers
.iter()
.filter_map(|(peer_id, peer)| {
peer.producing().then_some(p::Peer {
id: peer_id.clone(),
meta: peer.meta.clone(),
})
})
.collect(),
},
));
Ok(())
}
/// Register peer as a producer
#[instrument(level = "debug", skip(self))]
fn set_peer_status(&mut self, peer_id: &str, status: &p::PeerStatus) -> Result<(), Error> {
let old_status = self
.peers
.get(peer_id)
.context(anyhow!("Peer '{peer_id}' hasn't been welcomed"))?;
if status == old_status {
info!("Status for '{}' hasn't changed", peer_id);
return Ok(());
}
if old_status.producing() && !status.producing() {
self.stop_producer(peer_id);
}
let mut status = status.clone();
status.peer_id = Some(peer_id.to_string());
self.peers.insert(peer_id.to_string(), status.clone());
for (id, peer) in &self.peers {
if !peer.listening() {
continue;
}
self.items.push_back((
id.to_string(),
p::OutgoingMessage::PeerStatusChanged(p::PeerStatus {
peer_id: Some(peer_id.to_string()),
roles: status.roles.clone(),
meta: status.meta.clone(),
}),
));
}
info!(peer_id = %peer_id, "registered as a producer");
Ok(())
}
/// Start a session between two peers
#[instrument(level = "debug", skip(self))]
fn start_session(&mut self, producer_id: &str, consumer_id: &str) -> Result<(), Error> {
self.peers.get(producer_id).map_or_else(
|| Err(anyhow!("Peer '{producer_id}' hasn't been welcomed")),
|peer| {
if !peer.producing() {
Err(anyhow!(
"Peer with id {} is not registered as a producer",
producer_id
))
} else {
Ok(peer)
}
},
)?;
self.peers.get(consumer_id).map_or_else(
|| Err(anyhow!("Peer '{consumer_id}' hasn't been welcomed")),
Ok,
)?;
let session_id = uuid::Uuid::new_v4().to_string();
self.sessions.insert(
session_id.clone(),
Session {
id: session_id.clone(),
consumer: consumer_id.to_string(),
producer: producer_id.to_string(),
},
);
self.consumer_sessions
.entry(consumer_id.to_string())
.or_insert_with(HashSet::new)
.insert(session_id.clone());
self.producer_sessions
.entry(producer_id.to_string())
.or_insert_with(HashSet::new)
.insert(session_id.clone());
self.items.push_back((
consumer_id.to_string(),
p::OutgoingMessage::SessionStarted {
peer_id: producer_id.to_string(),
session_id: session_id.clone(),
},
));
self.items.push_back((
producer_id.to_string(),
p::OutgoingMessage::StartSession {
peer_id: consumer_id.to_string(),
session_id: session_id.clone(),
},
));
info!(id = %session_id, producer_id = %producer_id, consumer_id = %consumer_id, "started a session");
Ok(())
}
}
impl Stream for Handler {
type Item = (String, p::OutgoingMessage);
fn poll_next(mut self: Pin<&mut Self>, cx: &mut TaskContext<'_>) -> Poll<Option<Self::Item>> {
loop {
let this = self.as_mut().project();
if let Some(item) = this.items.pop_front() {
break Poll::Ready(Some(item));
}
match ready!(this.stream.poll_next(cx)) {
Some((peer_id, msg)) => {
if let Some(msg) = msg {
if let Err(err) = self.as_mut().handle(&peer_id, msg) {
self.items.push_back((
peer_id.to_string(),
p::OutgoingMessage::Error {
details: err.to_string(),
},
));
}
} else {
self.remove_peer(&peer_id);
}
}
None => {
break Poll::Ready(None);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::channel::mpsc;
use serde_json::json;
async fn new_peer(
tx: &mut mpsc::UnboundedSender<(String, Option<p::IncomingMessage>)>,
handler: &mut Handler,
peer_id: &str,
) {
tx.send((peer_id.to_string(), Some(p::IncomingMessage::NewPeer)))
.await
.unwrap();
let res = handler.next().await.unwrap();
assert_eq!(
res,
(
peer_id.to_string(),
p::OutgoingMessage::Welcome {
peer_id: peer_id.to_string()
}
)
);
}
#[tokio::test]
async fn test_register_producer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
tx.send((
"producer".to_string(),
Some(p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
})),
))
.await
.unwrap();
}
#[tokio::test]
async fn test_list_producers() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
meta: Some(json!({"display-name":"foobar".to_string()})),
roles: vec![p::PeerRole::Producer],
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let message = p::IncomingMessage::List;
tx.send(("listener".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "listener");
assert_eq!(
sent_message,
p::OutgoingMessage::List {
producers: vec![p::Peer {
id: "producer".to_string(),
meta: Some(json!(
{"display-name": "foobar".to_string()
})),
}]
}
);
}
#[tokio::test]
async fn test_welcome() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "consumer").await;
}
#[tokio::test]
async fn test_listener() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
new_peer(&mut tx, &mut handler, "listener").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Listener],
meta: None,
peer_id: None,
});
tx.send(("listener".to_string(), Some(message)))
.await
.unwrap();
let _ = handler.next().await.unwrap();
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: Some(json!({
"display-name": "foobar".to_string(),
})),
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "listener");
assert_eq!(
sent_message,
p::OutgoingMessage::PeerStatusChanged(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
peer_id: Some("producer".to_string()),
meta: Some(json!({
"display-name": Some("foobar".to_string()),
}
))
})
);
}
#[tokio::test]
async fn test_start_session() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing {sent_message:?}"),
};
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer");
assert_eq!(
sent_message,
p::OutgoingMessage::StartSession {
peer_id: "consumer".to_string(),
session_id: session_id.to_string(),
}
);
}
#[tokio::test]
async fn test_remove_peer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
assert_eq!(
handler.next().await.unwrap(),
(
"producer".into(),
p::OutgoingMessage::StartSession {
peer_id: "consumer".into(),
session_id: session_id.clone()
}
)
);
new_peer(&mut tx, &mut handler, "listener").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Listener],
meta: None,
peer_id: None,
});
tx.send(("listener".to_string(), Some(message)))
.await
.unwrap();
let _ = handler.next().await.unwrap();
handler.remove_peer("producer");
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::EndSession(p::EndSessionMessage { session_id })
);
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "listener");
assert_eq!(
sent_message,
p::OutgoingMessage::PeerStatusChanged(PeerStatus {
roles: vec![],
peer_id: Some("producer".to_string()),
meta: Default::default()
})
);
}
#[tokio::test]
async fn test_end_session_consumer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
let message = p::IncomingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.clone(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer");
assert_eq!(
sent_message,
p::OutgoingMessage::EndSession(p::EndSessionMessage { session_id })
);
}
#[tokio::test]
async fn test_disconnect_consumer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
tx.send(("consumer".to_string(), None)).await.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer");
assert_eq!(
sent_message,
p::OutgoingMessage::EndSession(p::EndSessionMessage { session_id })
);
}
#[tokio::test]
async fn test_end_session_producer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
let message = p::IncomingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.clone(),
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::EndSession(p::EndSessionMessage { session_id })
);
}
#[tokio::test]
async fn test_end_session_twice() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
// The consumer ends the session
let message = p::IncomingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.clone(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer");
assert_eq!(
sent_message,
p::OutgoingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.clone()
})
);
let message = p::IncomingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.clone(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::Error {
details: format!("Session {session_id} doesn't exist")
}
);
}
#[tokio::test]
async fn test_sdp_exchange() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
let message = p::IncomingMessage::Peer(p::PeerMessage {
session_id: session_id.clone(),
peer_message: p::PeerMessageInner::Sdp(p::SdpMessage::Offer {
sdp: "offer".to_string(),
}),
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::Peer(p::PeerMessage {
session_id: session_id.clone(),
peer_message: p::PeerMessageInner::Sdp(p::SdpMessage::Offer {
sdp: "offer".to_string()
})
})
);
}
#[tokio::test]
async fn test_ice_exchange() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
let message = p::IncomingMessage::Peer(p::PeerMessage {
session_id: session_id.clone(),
peer_message: p::PeerMessageInner::Ice {
candidate: "candidate".to_string(),
sdp_m_line_index: 42,
},
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::Peer(p::PeerMessage {
session_id: session_id.clone(),
peer_message: p::PeerMessageInner::Ice {
candidate: "candidate".to_string(),
sdp_m_line_index: 42
}
})
);
let message = p::IncomingMessage::Peer(p::PeerMessage {
session_id: session_id.clone(),
peer_message: p::PeerMessageInner::Ice {
candidate: "candidate".to_string(),
sdp_m_line_index: 42,
},
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer");
assert_eq!(
sent_message,
p::OutgoingMessage::Peer(p::PeerMessage {
session_id: session_id.clone(),
peer_message: p::PeerMessageInner::Ice {
candidate: "candidate".to_string(),
sdp_m_line_index: 42
}
})
);
}
#[tokio::test]
async fn test_sdp_exchange_wrong_direction_offer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
let message = p::IncomingMessage::Peer(p::PeerMessage {
session_id,
peer_message: p::PeerMessageInner::Sdp(p::SdpMessage::Offer {
sdp: "offer".to_string(),
}),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let response = handler.next().await.unwrap();
assert_eq!(response,
(
"consumer".into(),
p::OutgoingMessage::Error {
details: r#"cannot forward offer from "consumer" to "producer" as "consumer" is not the producer"#.into()
}
)
);
}
#[tokio::test]
async fn test_start_session_no_producer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::Error {
details: "Peer 'producer' hasn't been welcomed".into()
}
);
}
#[tokio::test]
async fn test_stop_producing() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer");
assert_eq!(
sent_message,
p::OutgoingMessage::StartSession {
peer_id: "consumer".to_string(),
session_id: session_id.clone(),
}
);
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.clone(),
})
);
}
#[tokio::test]
async fn test_unregistering_with_listeners() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "listener").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Listener],
meta: None,
peer_id: None,
});
tx.send(("listener".to_string(), Some(message)))
.await
.unwrap();
let _ = handler.next().await.unwrap();
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "listener");
assert_eq!(
sent_message,
p::OutgoingMessage::PeerStatusChanged(PeerStatus {
roles: vec![p::PeerRole::Producer],
peer_id: Some("producer".to_string()),
meta: Default::default()
})
);
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing {sent_message:?}"),
};
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer");
assert_eq!(
sent_message,
p::OutgoingMessage::StartSession {
peer_id: "consumer".to_string(),
session_id: session_id.clone(),
}
);
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
assert_eq!(
handler.next().await.unwrap(),
(
"consumer".into(),
p::OutgoingMessage::EndSession(p::EndSessionMessage {
session_id: session_id.clone(),
})
)
);
assert_eq!(
handler.next().await.unwrap(),
(
"listener".into(),
p::OutgoingMessage::PeerStatusChanged(PeerStatus {
roles: vec![],
peer_id: Some("producer".to_string()),
meta: Default::default()
})
)
);
}
#[tokio::test]
async fn test_start_session_no_consumer() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
assert_eq!(
sent_message,
p::OutgoingMessage::Error {
details: "Peer 'consumer' hasn't been welcomed".into()
}
);
}
#[tokio::test]
async fn test_start_session_twice() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
meta: Some(json!( {"display-name": "foobar".to_string() })),
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
new_peer(&mut tx, &mut handler, "consumer").await;
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session0_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let _ = handler.next().await.unwrap();
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "consumer");
let session1_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
assert_ne!(session0_id, session1_id);
}
#[tokio::test]
async fn test_start_session_stop_producing() {
let (mut tx, rx) = mpsc::unbounded();
let mut handler = Handler::new(Box::pin(rx));
new_peer(&mut tx, &mut handler, "producer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer, p::PeerRole::Listener],
meta: None,
peer_id: None,
});
tx.send(("producer".to_string(), Some(message)))
.await
.unwrap();
let _ = handler.next().await.unwrap();
new_peer(&mut tx, &mut handler, "producer-consumer").await;
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Producer],
..Default::default()
});
tx.send(("producer-consumer".to_string(), Some(message)))
.await
.unwrap();
handler.next().await.unwrap();
let message = p::IncomingMessage::StartSession(p::StartSessionMessage {
peer_id: "producer".to_string(),
});
tx.send(("producer-consumer".to_string(), Some(message)))
.await
.unwrap();
let (peer_id, sent_message) = handler.next().await.unwrap();
assert_eq!(peer_id, "producer-consumer");
let session0_id = match sent_message {
p::OutgoingMessage::SessionStarted {
ref peer_id,
ref session_id,
} => {
assert_eq!(peer_id, "producer");
session_id.to_string()
}
_ => panic!("SessionStarted message missing"),
};
let message = p::IncomingMessage::SetPeerStatus(p::PeerStatus {
roles: vec![p::PeerRole::Listener],
..Default::default()
});
tx.send(("producer-consumer".to_string(), Some(message)))
.await
.unwrap();
handler.next().await.unwrap();
let message = p::IncomingMessage::List;
tx.send(("producer-consumer".to_string(), Some(message)))
.await
.unwrap();
handler.next().await.unwrap();
handler
.sessions
.get(&session0_id)
.expect("Session should remain");
}
}