#![deny(missing_docs)]
use std::net::IpAddr;
use std::sync::Arc;
use tokio::sync::mpsc;
use crate::adapters::SessionApiCrossCrateEvent;
use crate::api::events::{Event, MediaSecurityState, SipTrace};
use crate::api::handle::{CallId, SessionHandle};
use crate::api::incoming::IncomingCall;
use crate::api::performance::PerformanceConfig;
use crate::api::unified::{Config, MediaMode, UnifiedCoordinator};
use crate::errors::{Result, SessionError};
pub use crate::api::unified::Config as PeerConfig;
pub struct EventReceiver {
rx: mpsc::Receiver<Arc<dyn rvoip_infra_common::events::cross_crate::CrossCrateEvent>>,
filter: Option<CallId>,
primed: std::collections::VecDeque<Event>,
}
impl EventReceiver {
pub(crate) fn new(
rx: mpsc::Receiver<Arc<dyn rvoip_infra_common::events::cross_crate::CrossCrateEvent>>,
) -> Self {
Self {
rx,
filter: None,
primed: std::collections::VecDeque::new(),
}
}
pub(crate) fn filtered(
rx: mpsc::Receiver<Arc<dyn rvoip_infra_common::events::cross_crate::CrossCrateEvent>>,
call_id: CallId,
) -> Self {
Self {
rx,
filter: Some(call_id),
primed: std::collections::VecDeque::new(),
}
}
pub(crate) fn prime(&mut self, event: Event) {
self.primed.push_back(event);
}
pub async fn next(&mut self) -> Option<Event> {
if let Some(event) = self.primed.pop_front() {
return Some(event);
}
loop {
let raw = self.rx.recv().await?;
let session_event = raw.as_any().downcast_ref::<SessionApiCrossCrateEvent>()?;
let event = session_event.event.clone();
if let Some(ref filter) = self.filter {
if event.call_id() != Some(filter) {
continue;
}
}
return Some(event);
}
}
pub fn try_next(&mut self) -> Option<Event> {
if let Some(event) = self.primed.pop_front() {
return Some(event);
}
loop {
let raw = self.rx.try_recv().ok()?;
let session_event = raw.as_any().downcast_ref::<SessionApiCrossCrateEvent>()?;
let event = session_event.event.clone();
if let Some(ref filter) = self.filter {
if event.call_id() != Some(filter) {
continue;
}
}
return Some(event);
}
}
pub async fn next_incoming(&mut self) -> Option<(CallId, String, String, Option<String>)> {
loop {
match self.next().await? {
Event::IncomingCall {
call_id,
from,
to,
sdp,
} => {
return Some((call_id, from, to, sdp));
}
_ => continue,
}
}
}
pub async fn next_dtmf(&mut self) -> Option<(CallId, char)> {
loop {
match self.next().await? {
Event::DtmfReceived { call_id, digit } => {
return Some((call_id, digit));
}
_ => continue,
}
}
}
pub async fn next_progress(&mut self) -> Option<(CallId, u16, String, Option<String>)> {
loop {
match self.next().await? {
Event::CallProgress {
call_id,
status_code,
reason,
sdp,
} => return Some((call_id, status_code, reason, sdp)),
_ => continue,
}
}
}
pub async fn next_media_security_negotiated(&mut self) -> Option<(CallId, MediaSecurityState)> {
loop {
let event = self.next().await?;
if let Some(state) = media_security_state_from_event(event) {
return Some(state);
}
}
}
pub async fn next_sip_trace(&mut self) -> Option<SipTrace> {
loop {
match self.next().await? {
Event::SipTrace(trace) => return Some(trace),
_ => continue,
}
}
}
pub async fn next_transfer(&mut self) -> Option<Event> {
loop {
let event = self.next().await?;
if event.is_transfer_event() {
return Some(event);
}
}
}
pub async fn next_where<F: FnMut(&Event) -> bool>(
&mut self,
mut predicate: F,
) -> Option<Event> {
loop {
let event = self.next().await?;
if predicate(&event) {
return Some(event);
}
}
}
pub async fn next_for_call(&mut self, call_id: &CallId) -> Option<Event> {
loop {
let event = self.next().await?;
if event.call_id() == Some(call_id) {
return Some(event);
}
}
}
}
#[derive(Clone)]
pub struct PeerControl {
pub(crate) coordinator: Arc<UnifiedCoordinator>,
pub(crate) local_uri: String,
}
impl PeerControl {
pub async fn accept(&self, call_id: &CallId) -> Result<SessionHandle> {
self.coordinator.accept_call(call_id).await?;
Ok(SessionHandle::new(
call_id.clone(),
self.coordinator.clone(),
))
}
pub async fn reject(&self, call_id: &CallId, status: u16, reason: &str) -> Result<()> {
self.coordinator
.reject(call_id)
.with_status(status)
.with_reason(reason.to_string())
.send()
.await
}
pub async fn send_early_media(&self, call_id: &CallId, sdp: Option<String>) -> Result<()> {
self.coordinator.send_early_media(call_id, sdp).await
}
pub async fn subscribe_events(&self) -> Result<EventReceiver> {
let rx = self.coordinator.subscribe_events().await?;
Ok(EventReceiver::new(rx))
}
pub fn coordinator(&self) -> &Arc<UnifiedCoordinator> {
&self.coordinator
}
pub fn invite(&self, target: impl Into<String>) -> crate::api::send::OutboundCallBuilder {
self.coordinator
.invite(Some(self.local_uri.clone()), target)
}
pub fn register(
&self,
registrar: impl Into<String>,
username: impl Into<String>,
password: impl Into<String>,
) -> crate::api::send::RegisterBuilder {
self.coordinator.register(registrar, username, password)
}
}
pub struct StreamPeer {
control: PeerControl,
events: EventReceiver,
}
impl StreamPeer {
pub async fn new(name: &str) -> Result<Self> {
let mut config = Config::default();
config.local_uri = format!("sip:{}@{}:{}", name, config.local_ip, config.sip_port);
Self::with_config(config).await
}
pub async fn with_config(config: Config) -> Result<Self> {
let local_uri = config.local_uri.clone();
let coordinator = UnifiedCoordinator::new(config).await?;
let event_rx = coordinator.subscribe_events().await?;
Ok(Self {
control: PeerControl {
coordinator,
local_uri,
},
events: EventReceiver::new(event_rx),
})
}
pub fn split(self) -> (PeerControl, EventReceiver) {
(self.control, self.events)
}
pub fn control(&self) -> &PeerControl {
&self.control
}
pub fn coordinator(&self) -> &Arc<UnifiedCoordinator> {
self.control.coordinator()
}
pub fn invite(&self, target: impl Into<String>) -> crate::api::send::OutboundCallBuilder {
self.control.invite(target)
}
pub fn register(
&self,
registrar: impl Into<String>,
username: impl Into<String>,
password: impl Into<String>,
) -> crate::api::send::RegisterBuilder {
self.control.register(registrar, username, password)
}
pub async fn wait_for_incoming(&mut self) -> Result<IncomingCall> {
loop {
match self.events.next().await {
Some(Event::IncomingCall {
call_id,
from,
to,
sdp,
}) => {
let coord = self.control.coordinator.clone();
let parsed = coord.session_registry.peek_pending_incoming_request().await;
let incoming = match parsed {
Some(req) => IncomingCall::with_request(call_id, from, to, sdp, coord, req),
None => IncomingCall::new(call_id, from, to, sdp, coord),
};
return Ok(incoming);
}
None => return Err(SessionError::Other("Event channel closed".to_string())),
_ => {}
}
}
}
pub async fn wait_for_answered(&mut self, call_id: &CallId) -> Result<SessionHandle> {
loop {
match self.events.next().await {
Some(Event::CallAnswered {
call_id: answered_id,
..
}) if &answered_id == call_id => {
return Ok(SessionHandle::new(
answered_id,
self.control.coordinator.clone(),
));
}
Some(Event::CallFailed {
call_id: failed_id,
reason,
status_code,
}) if &failed_id == call_id => {
return Err(SessionError::Other(format!(
"Call failed with {}: {}",
status_code, reason
)));
}
None => return Err(SessionError::Other("Event channel closed".to_string())),
_ => {}
}
}
}
pub async fn wait_for_progress<F>(
&mut self,
call_id: &CallId,
mut predicate: F,
) -> Result<Event>
where
F: FnMut(&Event) -> bool,
{
loop {
match self.events.next().await {
Some(event @ Event::CallProgress { .. }) => {
if event.call_id() == Some(call_id) && predicate(&event) {
return Ok(event);
}
}
Some(Event::CallAnswered {
call_id: answered_id,
..
}) if &answered_id == call_id => {
return Err(SessionError::Other(
"call answered before matching provisional progress".to_string(),
));
}
Some(Event::CallFailed {
call_id: failed_id,
reason,
status_code,
}) if &failed_id == call_id => {
return Err(SessionError::Other(format!(
"Call failed with {}: {}",
status_code, reason
)));
}
Some(Event::CallCancelled { call_id: id }) if &id == call_id => {
return Err(SessionError::Other(
"call cancelled before matching provisional progress".to_string(),
));
}
None => return Err(SessionError::Other("Event channel closed".to_string())),
_ => {}
}
}
}
pub async fn wait_for_media_security(
&mut self,
call_id: &CallId,
) -> Result<MediaSecurityState> {
loop {
match self.events.next().await {
Some(event) if event.call_id() == Some(call_id) => {
if let Some((_, state)) = media_security_state_from_event(event) {
return Ok(state);
}
}
None => return Err(SessionError::Other("Event channel closed".to_string())),
_ => {}
}
}
}
pub async fn wait_for_ended(&mut self, call_id: &CallId) -> Result<String> {
loop {
match self.events.next().await {
Some(Event::CallEnded {
call_id: ended_id,
reason,
}) if &ended_id == call_id => {
return Ok(reason);
}
None => return Err(SessionError::Other("Event channel closed".to_string())),
_ => {}
}
}
}
pub async fn next_event(&mut self) -> Option<Event> {
self.events.next().await
}
pub async fn is_registered(
&self,
handle: &crate::api::unified::RegistrationHandle,
) -> Result<bool> {
self.control.coordinator.is_registered(handle).await
}
pub async fn unregister(&self, handle: &crate::api::unified::RegistrationHandle) -> Result<()> {
self.control.coordinator.unregister(handle).await
}
pub async fn shutdown(self) -> Result<()> {
self.control.coordinator.shutdown_gracefully(None).await?;
drop(self);
Ok(())
}
pub fn shutdown_handle(&self) -> crate::api::callback_peer::ShutdownHandle {
self.control.coordinator.shutdown_handle()
}
pub fn builder() -> StreamPeerBuilder {
StreamPeerBuilder::new()
}
}
fn media_security_state_from_event(event: Event) -> Option<(CallId, MediaSecurityState)> {
match event {
Event::MediaSecurityNegotiated {
call_id,
keying,
suite,
profile,
contexts_installed,
} => Some((
call_id,
MediaSecurityState {
keying,
suite,
profile,
contexts_installed,
},
)),
_ => None,
}
}
pub struct StreamPeerBuilder {
config: Config,
name: Option<String>,
}
impl StreamPeerBuilder {
pub fn new() -> Self {
Self {
config: Config::default(),
name: None,
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
pub fn sip_port(mut self, port: u16) -> Self {
self.config.sip_port = port;
self.config.bind_addr.set_port(port);
self
}
pub fn local_ip(mut self, ip: IpAddr) -> Self {
self.config.local_ip = ip;
self.config.bind_addr.set_ip(ip);
self
}
pub fn media_ports(mut self, start: u16, end: u16) -> Self {
self.config = self.config.with_media_ports(start, end);
self
}
pub fn media_port_capacity(mut self, start: u16, capacity: usize) -> Self {
self.config = self.config.with_media_port_capacity(start, capacity);
self
}
pub fn media_session_capacity(mut self, capacity: usize) -> Self {
self.config = self.config.with_media_session_capacity(capacity);
self
}
pub fn high_cps_udp_auto_answer(mut self, capacity: usize) -> Self {
self.config = self.config.with_high_cps_udp_auto_answer(capacity);
self
}
pub fn performance_config(mut self, performance: PerformanceConfig) -> Result<Self> {
self.config = self.config.try_with_performance_config(performance)?;
Ok(self)
}
pub fn pbx_media_server_performance(mut self, capacity: usize) -> Self {
self.config = self.config.with_pbx_media_server_performance(capacity);
self
}
pub fn signaling_only_server_high_performance(
mut self,
capacity: usize,
sdp_rtp_port: u16,
) -> Self {
self.config = self
.config
.with_signaling_only_server_high_performance(capacity, sdp_rtp_port);
self
}
pub fn app_event_channel_capacity(mut self, capacity: usize) -> Self {
self.config = self.config.with_app_event_channel_capacity(capacity);
self
}
pub fn auto_180_ringing(mut self, enabled: bool) -> Self {
self.config = self.config.with_auto_180_ringing(enabled);
self
}
pub fn auto_100_trying(mut self, enabled: bool) -> Self {
self.config = self.config.with_auto_100_trying(enabled);
self
}
pub fn fast_auto_accept_incoming_calls(mut self, enabled: bool) -> Self {
self.config = self.config.with_fast_auto_accept_incoming_calls(enabled);
self
}
pub fn media_enabled(mut self, enabled: bool) -> Self {
self.config = self.config.with_media_enabled(enabled);
self
}
pub fn signaling_only_media(mut self, sdp_rtp_port: u16) -> Self {
self.config = self
.config
.with_media_mode(MediaMode::SignalingOnly { sdp_rtp_port });
self
}
pub fn sip_udp_parse_workers(mut self, workers: usize) -> Self {
self.config = self.config.with_sip_udp_parse_workers(workers);
self
}
pub fn sip_udp_parse_queue_capacity(mut self, capacity: usize) -> Self {
self.config = self.config.with_sip_udp_parse_queue_capacity(capacity);
self
}
pub fn sip_transaction_command_channel_capacity(mut self, capacity: usize) -> Self {
self.config = self
.config
.with_sip_transaction_command_channel_capacity(capacity);
self
}
pub fn server_call_admission_limit(mut self, limit: usize) -> Self {
self.config = self.config.with_server_call_admission_limit(limit);
self
}
pub fn server_call_admission_soft_limit(mut self, limit: usize) -> Self {
self.config = self.config.with_server_call_admission_soft_limit(limit);
self
}
pub fn server_call_admission_pacing_delay_ms(mut self, delay_ms: u64) -> Self {
self.config = self
.config
.with_server_call_admission_pacing_delay_ms(delay_ms);
self
}
pub fn server_overload_retry_after_secs(mut self, seconds: u32) -> Self {
self.config = self.config.with_server_overload_retry_after_secs(seconds);
self
}
pub fn sip_udp_diagnostics(mut self, enabled: bool) -> Self {
self.config = self.config.with_sip_udp_diagnostics(enabled);
self
}
pub fn media_setup_diagnostics(mut self, enabled: bool) -> Self {
self.config = self.config.with_media_setup_diagnostics(enabled);
self
}
pub fn cleanup_diagnostics(mut self, enabled: bool) -> Self {
self.config = self.config.with_cleanup_diagnostics(enabled);
self
}
pub fn cleanup_diagnostic_events(mut self, enabled: bool) -> Self {
self.config = self.config.with_cleanup_diagnostic_events(enabled);
self
}
#[cfg(feature = "perf-tests")]
pub fn perf_max_rss_growth_mb_per_hr(mut self, limit: f64) -> Self {
self.config = self.config.with_perf_max_rss_growth_mb_per_hr(limit);
self
}
pub fn srtp_diagnostics(mut self, enabled: bool) -> Self {
self.config = self.config.with_srtp_diagnostics(enabled);
self
}
pub fn rtp_diagnostics(mut self, enabled: bool) -> Self {
self.config = self.config.with_rtp_diagnostics(enabled);
self
}
pub fn media_sdp_diagnostics(mut self, enabled: bool) -> Self {
self.config = self.config.with_media_sdp_diagnostics(enabled);
self
}
pub fn config(mut self, config: Config) -> Self {
self.config = config;
self
}
pub fn with_credentials(mut self, username: &str, password: &str) -> Self {
self.config.credentials = Some(crate::types::Credentials::new(username, password));
self
}
pub async fn build(mut self) -> Result<StreamPeer> {
if let Some(name) = self.name {
self.config.local_uri = format!(
"sip:{}@{}:{}",
name, self.config.local_ip, self.config.sip_port
);
}
StreamPeer::with_config(self.config).await
}
}
impl Default for StreamPeerBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::EventReceiver;
use crate::adapters::SessionApiCrossCrateEvent;
use crate::api::events::{Event, SipTrace, SipTraceDirection};
use crate::state_table::types::SessionId;
use rvoip_infra_common::events::cross_crate::CrossCrateEvent;
use std::sync::Arc;
use tokio::sync::mpsc;
#[tokio::test]
async fn event_receiver_returns_sip_trace_events() {
let (tx, rx) = mpsc::channel::<Arc<dyn CrossCrateEvent>>(4);
tx.send(SessionApiCrossCrateEvent::new(Event::CallEnded {
call_id: SessionId("other".into()),
reason: "done".into(),
}))
.await
.unwrap();
tx.send(SessionApiCrossCrateEvent::new(Event::SipTrace(
trace_event(),
)))
.await
.unwrap();
drop(tx);
let mut receiver = EventReceiver::new(rx);
let trace = receiver.next_sip_trace().await.unwrap();
assert_eq!(trace.sip_call_id.as_deref(), Some("wire-call"));
assert_eq!(trace.session_id, Some(SessionId("session-1".into())));
}
fn trace_event() -> SipTrace {
SipTrace {
direction: SipTraceDirection::Outbound,
transport: "UDP".into(),
local_addr: "127.0.0.1:5060".into(),
remote_addr: "127.0.0.1:5080".into(),
timestamp_unix_millis: 1,
start_line: "INVITE sip:bob@example.com SIP/2.0".into(),
sip_call_id: Some("wire-call".into()),
session_id: Some(SessionId("session-1".into())),
raw_message: "INVITE sip:bob@example.com SIP/2.0\n\n".into(),
original_len: 40,
truncated: false,
redacted: true,
}
}
}