1#![deny(missing_docs)]
13
14use std::fmt;
15use std::fs;
16use std::net::{IpAddr, Ipv4Addr, SocketAddr};
17use std::path::{Path, PathBuf};
18use std::str::FromStr;
19use std::sync::Arc;
20use std::time::Duration;
21
22use serde::Deserialize;
23use tokio::sync::Mutex;
24
25use rvoip_sip_core::types::uri::{Scheme, Uri};
26
27use crate::api::audio::{AudioReceiver, AudioSender, AudioStream};
28use crate::api::events::Event;
29use crate::api::handle::{CallId, SessionHandle};
30use crate::api::incoming::{IncomingCall, IncomingCallGuard};
31use crate::api::performance::PerformanceConfig;
32use crate::api::stream_peer::{EventReceiver, PeerControl, StreamPeer};
33use crate::api::unified::{
34 Config, MediaMode, Registration, RegistrationHandle, RegistrationInfo, RegistrationStatus,
35 SipTlsMode,
36};
37use crate::auth::SipClientAuth;
38use crate::errors::{Result, SessionError};
39use crate::types::Credentials;
40
41pub struct Endpoint {
49 peer: StreamPeer,
50 registration: Option<Registration>,
51 registration_handle: SharedRegistrationHandle,
52 registrar: Option<String>,
53 transport: EndpointTransport,
54}
55
56impl Endpoint {
57 pub fn builder() -> EndpointBuilder {
72 EndpointBuilder::new()
73 }
74
75 pub async fn from_config(config: EndpointConfig) -> Result<Self> {
87 EndpointBuilder::from_config(config)?.build().await
88 }
89
90 pub async fn from_json_file(path: impl AsRef<Path>) -> Result<Self> {
102 let text = fs::read_to_string(path.as_ref()).map_err(|err| {
103 SessionError::ConfigError(format!(
104 "failed to read endpoint JSON config '{}': {err}",
105 path.as_ref().display()
106 ))
107 })?;
108 let config = serde_json::from_str::<EndpointConfig>(&text).map_err(|err| {
109 SessionError::ConfigError(format!(
110 "failed to parse endpoint JSON config '{}': {err}",
111 path.as_ref().display()
112 ))
113 })?;
114 Self::from_config(config).await
115 }
116
117 pub async fn register(&mut self) -> Result<RegistrationHandle> {
124 let mut stored = self.registration_handle.lock().await;
125 if let Some(handle) = stored.as_ref() {
126 return Ok(handle.clone());
127 }
128
129 let registration = self.registration.clone().ok_or_else(|| {
130 SessionError::ConfigError(
131 "Endpoint has no complete registration account; set account, password, and registrar"
132 .to_string(),
133 )
134 })?;
135 let mut b = self
136 .peer
137 .register(
138 registration.registrar.clone(),
139 registration.username.clone(),
140 registration.password.clone(),
141 )
142 .with_expires(registration.expires);
143 if let Some(from) = registration.from_uri.clone() {
144 b = b.with_from_uri(from);
145 }
146 if let Some(contact) = registration.contact_uri.clone() {
147 b = b.with_contact_uri(contact);
148 }
149 let handle = b.send().await?;
150 *stored = Some(handle.clone());
151 Ok(handle)
152 }
153
154 pub async fn register_and_wait(
156 &mut self,
157 timeout: Option<Duration>,
158 ) -> Result<EndpointRegistrationInfo> {
159 let mut events = self.events().await?;
160 let handle = self.register().await?;
161 wait_for_registration_result(&mut events, &handle, timeout).await
162 }
163
164 pub async fn unregister(&mut self) -> Result<()> {
168 let mut stored = self.registration_handle.lock().await;
169 if let Some(handle) = stored.take() {
170 self.peer.unregister(&handle).await?;
171 }
172 Ok(())
173 }
174
175 pub async fn call_and_wait(
177 &self,
178 target: &str,
179 timeout: Option<Duration>,
180 ) -> Result<EndpointCall> {
181 let call_id = self.invite(target)?.send().await?;
182 let call = self.wrap_call(call_id);
183 call.wait_for_answered(timeout).await
184 }
185
186 pub async fn wait_for_incoming(&mut self) -> Result<EndpointIncomingCall> {
188 let incoming = self.peer.wait_for_incoming().await?;
189 Ok(EndpointIncomingCall::new(
190 incoming,
191 self.registrar.clone(),
192 self.transport,
193 ))
194 }
195
196 pub async fn events(&self) -> Result<EndpointEvents> {
198 let events = self.peer.control().subscribe_events().await?;
199 Ok(EndpointEvents::new(
200 events,
201 self.peer.control().clone(),
202 self.registrar.clone(),
203 self.transport,
204 ))
205 }
206
207 pub fn split(self) -> (EndpointControl, EndpointEvents) {
209 let registration = self.registration;
210 let registration_handle = self.registration_handle;
211 let registrar = self.registrar;
212 let transport = self.transport;
213 let (control, events) = self.peer.split();
214 let endpoint_control = EndpointControl::new(
215 control.clone(),
216 registration,
217 registration_handle,
218 registrar.clone(),
219 transport,
220 );
221 let endpoint_events = EndpointEvents::new(events, control, registrar, transport);
222 (endpoint_control, endpoint_events)
223 }
224
225 pub fn control(&self) -> &PeerControl {
227 self.peer.control()
228 }
229
230 pub fn resolve_target(&self, target: &str) -> Result<String> {
235 normalize_target(self.registrar.as_deref(), target, self.transport)
236 }
237
238 pub fn invite(&self, target: &str) -> Result<crate::api::send::OutboundCallBuilder> {
246 let resolved = self.resolve_target(target)?;
247 Ok(self.peer.control().invite(resolved))
248 }
249
250 pub fn wrap_call(&self, call_id: crate::api::handle::CallId) -> EndpointCall {
256 let coord = self.peer.control().coordinator().clone();
257 EndpointCall::new(
258 crate::api::handle::SessionHandle::new(call_id, coord),
259 self.registrar.clone(),
260 self.transport,
261 )
262 }
263
264 pub fn into_stream_peer(self) -> StreamPeer {
266 self.peer
267 }
268
269 pub async fn shutdown(self) -> Result<()> {
271 self.peer.shutdown().await
272 }
273}
274
275type SharedRegistrationHandle = Arc<Mutex<Option<RegistrationHandle>>>;
276
277#[derive(Clone)]
279pub struct EndpointControl {
280 control: PeerControl,
281 registration: Option<Registration>,
282 registration_handle: SharedRegistrationHandle,
283 registrar: Option<String>,
284 transport: EndpointTransport,
285}
286
287impl EndpointControl {
288 fn new(
289 control: PeerControl,
290 registration: Option<Registration>,
291 registration_handle: SharedRegistrationHandle,
292 registrar: Option<String>,
293 transport: EndpointTransport,
294 ) -> Self {
295 Self {
296 control,
297 registration,
298 registration_handle,
299 registrar,
300 transport,
301 }
302 }
303
304 pub async fn register(&self) -> Result<()> {
306 let mut stored = self.registration_handle.lock().await;
307 if stored.is_some() {
308 return Ok(());
309 }
310 let registration = self.registration.clone().ok_or_else(|| {
311 SessionError::ConfigError(
312 "Endpoint has no complete registration account; set account, password, and registrar"
313 .to_string(),
314 )
315 })?;
316 let mut b = self
317 .control
318 .coordinator()
319 .register(
320 registration.registrar,
321 registration.username,
322 registration.password,
323 )
324 .with_expires(registration.expires);
325 if let Some(from) = registration.from_uri {
326 b = b.with_from_uri(from);
327 }
328 if let Some(contact) = registration.contact_uri {
329 b = b.with_contact_uri(contact);
330 }
331 let handle = b.send().await?;
332 *stored = Some(handle);
333 Ok(())
334 }
335
336 pub async fn register_and_wait(
338 &self,
339 timeout: Option<Duration>,
340 ) -> Result<EndpointRegistrationInfo> {
341 let mut events = self.events().await?;
342 self.register().await?;
343 let handle = self
344 .registration_handle
345 .lock()
346 .await
347 .clone()
348 .ok_or_else(|| SessionError::Other("registration handle missing".to_string()))?;
349 wait_for_registration_result(&mut events, &handle, timeout).await
350 }
351
352 pub async fn registration_info(&self) -> Result<Option<EndpointRegistrationInfo>> {
354 let handle = self.registration_handle.lock().await.clone();
355 match handle {
356 Some(handle) => self
357 .control
358 .coordinator()
359 .registration_info(&handle)
360 .await
361 .map(EndpointRegistrationInfo::from)
362 .map(Some),
363 None => Ok(None),
364 }
365 }
366
367 pub async fn unregister(&self) -> Result<()> {
369 if let Some(handle) = self.registration_handle.lock().await.take() {
370 self.control.coordinator().unregister(&handle).await?;
371 }
372 Ok(())
373 }
374
375 pub async fn unregister_and_wait(&self, timeout: Option<Duration>) -> Result<()> {
377 if let Some(handle) = self.registration_handle.lock().await.take() {
378 self.control
379 .coordinator()
380 .unregister_and_wait(&handle, timeout)
381 .await?;
382 }
383 Ok(())
384 }
385
386 pub async fn events(&self) -> Result<EndpointEvents> {
388 let events = self.control.subscribe_events().await?;
389 Ok(EndpointEvents::new(
390 events,
391 self.control.clone(),
392 self.registrar.clone(),
393 self.transport,
394 ))
395 }
396
397 pub fn resolve_target(&self, target: &str) -> Result<String> {
399 normalize_target(self.registrar.as_deref(), target, self.transport)
400 }
401
402 pub fn invite(&self, target: &str) -> Result<crate::api::send::OutboundCallBuilder> {
406 let resolved = self.resolve_target(target)?;
407 Ok(self.control.invite(resolved))
408 }
409
410 pub fn wrap_call(&self, call_id: crate::api::handle::CallId) -> EndpointCall {
413 let coord = self.control.coordinator().clone();
414 EndpointCall::new(
415 crate::api::handle::SessionHandle::new(call_id, coord),
416 self.registrar.clone(),
417 self.transport,
418 )
419 }
420
421 pub async fn shutdown(&self) -> Result<()> {
423 self.control.coordinator().shutdown_gracefully(None).await
424 }
425}
426
427pub struct EndpointEvents {
429 events: EventReceiver,
430 control: PeerControl,
431 registrar: Option<String>,
432 transport: EndpointTransport,
433}
434
435impl EndpointEvents {
436 fn new(
437 events: EventReceiver,
438 control: PeerControl,
439 registrar: Option<String>,
440 transport: EndpointTransport,
441 ) -> Self {
442 Self {
443 events,
444 control,
445 registrar,
446 transport,
447 }
448 }
449
450 pub async fn next(&mut self) -> Result<Option<EndpointEvent>> {
452 Ok(self.events.next().await.map(|event| self.map_event(event)))
453 }
454
455 pub fn try_next(&mut self) -> Option<EndpointEvent> {
457 self.events.try_next().map(|event| self.map_event(event))
458 }
459
460 fn map_event(&self, event: Event) -> EndpointEvent {
461 match event {
462 Event::IncomingCall {
463 call_id,
464 from,
465 to,
466 sdp,
467 } => {
468 let incoming =
469 IncomingCall::new(call_id, from, to, sdp, self.control.coordinator().clone());
470 EndpointEvent::IncomingCall(EndpointIncomingCall::new(
471 incoming,
472 self.registrar.clone(),
473 self.transport,
474 ))
475 }
476 Event::CallProgress {
477 call_id,
478 status_code,
479 reason,
480 sdp,
481 } => EndpointEvent::CallProgress {
482 call_id: EndpointCallId(call_id),
483 status_code,
484 reason,
485 has_sdp: sdp.is_some(),
486 },
487 Event::CallAnswered { call_id, sdp } => EndpointEvent::CallAnswered {
488 call: EndpointCall::new(
489 SessionHandle::new(call_id, self.control.coordinator().clone()),
490 self.registrar.clone(),
491 self.transport,
492 ),
493 has_sdp: sdp.is_some(),
494 },
495 Event::CallEnded { call_id, reason } => EndpointEvent::CallEnded {
496 call_id: EndpointCallId(call_id),
497 reason,
498 },
499 Event::CallFailed {
500 call_id,
501 status_code,
502 reason,
503 } => EndpointEvent::CallFailed {
504 call_id: EndpointCallId(call_id),
505 status_code,
506 reason,
507 },
508 Event::CallCancelled { call_id } => EndpointEvent::CallCancelled {
509 call_id: EndpointCallId(call_id),
510 },
511 Event::CallOnHold { call_id } => EndpointEvent::LocalHold {
512 call_id: EndpointCallId(call_id),
513 },
514 Event::CallResumed { call_id } => EndpointEvent::LocalResume {
515 call_id: EndpointCallId(call_id),
516 },
517 Event::RemoteCallOnHold { call_id } => EndpointEvent::RemoteHold {
518 call_id: EndpointCallId(call_id),
519 },
520 Event::RemoteCallResumed { call_id } => EndpointEvent::RemoteResume {
521 call_id: EndpointCallId(call_id),
522 },
523 Event::DtmfReceived { call_id, digit } => EndpointEvent::DtmfReceived {
524 call_id: EndpointCallId(call_id),
525 digit,
526 },
527 Event::RegistrationSuccess {
528 registrar,
529 expires,
530 contact,
531 } => EndpointEvent::RegistrationChanged(EndpointRegistrationInfo {
532 status: EndpointRegistrationStatus::Registered,
533 registrar: Some(registrar),
534 contact: Some(contact),
535 expires_secs: Some(expires),
536 accepted_expires_secs: Some(expires),
537 next_refresh_in: None,
538 retry_count: 0,
539 last_failure: None,
540 }),
541 Event::RegistrationFailed {
542 registrar,
543 status_code,
544 reason,
545 } => EndpointEvent::RegistrationChanged(EndpointRegistrationInfo {
546 status: EndpointRegistrationStatus::Failed,
547 registrar: Some(registrar),
548 contact: None,
549 expires_secs: None,
550 accepted_expires_secs: None,
551 next_refresh_in: None,
552 retry_count: 0,
553 last_failure: Some(format!("{status_code} {reason}")),
554 }),
555 Event::UnregistrationSuccess { registrar } => {
556 EndpointEvent::RegistrationChanged(EndpointRegistrationInfo {
557 status: EndpointRegistrationStatus::Unregistered,
558 registrar: Some(registrar),
559 contact: None,
560 expires_secs: None,
561 accepted_expires_secs: None,
562 next_refresh_in: None,
563 retry_count: 0,
564 last_failure: None,
565 })
566 }
567 Event::UnregistrationFailed { registrar, reason } => {
568 EndpointEvent::RegistrationChanged(EndpointRegistrationInfo {
569 status: EndpointRegistrationStatus::Failed,
570 registrar: Some(registrar),
571 contact: None,
572 expires_secs: None,
573 accepted_expires_secs: None,
574 next_refresh_in: None,
575 retry_count: 0,
576 last_failure: Some(reason),
577 })
578 }
579 Event::NetworkError { call_id, error } => EndpointEvent::NetworkError {
580 call_id: call_id.map(EndpointCallId),
581 error,
582 },
583 Event::SipTrace(trace) => EndpointEvent::SipTrace(EndpointSipTrace {
584 direction: trace.direction,
585 transport: trace.transport,
586 local_addr: trace.local_addr,
587 remote_addr: trace.remote_addr,
588 timestamp_unix_millis: trace.timestamp_unix_millis,
589 start_line: trace.start_line,
590 sip_call_id: trace.sip_call_id,
591 session_id: trace.session_id.map(EndpointCallId),
592 raw_message: trace.raw_message,
593 original_len: trace.original_len,
594 truncated: trace.truncated,
595 redacted: trace.redacted,
596 }),
597 other => EndpointEvent::Info {
598 call_id: other.call_id().cloned().map(EndpointCallId),
599 message: format!("{other:?}"),
600 },
601 }
602 }
603}
604
605pub enum EndpointEvent {
607 IncomingCall(EndpointIncomingCall),
609 CallProgress {
611 call_id: EndpointCallId,
613 status_code: u16,
615 reason: String,
617 has_sdp: bool,
619 },
620 CallAnswered {
622 call: EndpointCall,
624 has_sdp: bool,
626 },
627 CallEnded {
629 call_id: EndpointCallId,
631 reason: String,
633 },
634 CallFailed {
636 call_id: EndpointCallId,
638 status_code: u16,
640 reason: String,
642 },
643 CallCancelled {
645 call_id: EndpointCallId,
647 },
648 LocalHold {
650 call_id: EndpointCallId,
652 },
653 LocalResume {
655 call_id: EndpointCallId,
657 },
658 RemoteHold {
660 call_id: EndpointCallId,
662 },
663 RemoteResume {
665 call_id: EndpointCallId,
667 },
668 DtmfReceived {
670 call_id: EndpointCallId,
672 digit: char,
674 },
675 RegistrationChanged(EndpointRegistrationInfo),
677 SipTrace(EndpointSipTrace),
679 NetworkError {
681 call_id: Option<EndpointCallId>,
683 error: String,
685 },
686 Info {
688 call_id: Option<EndpointCallId>,
690 message: String,
692 },
693}
694
695#[derive(Debug, Clone, PartialEq, Eq)]
697pub struct EndpointSipTrace {
698 pub direction: crate::api::events::SipTraceDirection,
700 pub transport: String,
702 pub local_addr: String,
704 pub remote_addr: String,
706 pub timestamp_unix_millis: u64,
708 pub start_line: String,
710 pub sip_call_id: Option<String>,
712 pub session_id: Option<EndpointCallId>,
714 pub raw_message: String,
716 pub original_len: usize,
718 pub truncated: bool,
720 pub redacted: bool,
722}
723
724#[derive(Debug, Clone, PartialEq, Eq, Hash)]
726pub struct EndpointCallId(CallId);
727
728impl fmt::Display for EndpointCallId {
729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730 self.0.fmt(f)
731 }
732}
733
734#[derive(Clone)]
736pub struct EndpointCall {
737 handle: SessionHandle,
738 registrar: Option<String>,
739 transport: EndpointTransport,
740}
741
742impl EndpointCall {
743 fn new(handle: SessionHandle, registrar: Option<String>, transport: EndpointTransport) -> Self {
744 Self {
745 handle,
746 registrar,
747 transport,
748 }
749 }
750
751 pub fn id(&self) -> EndpointCallId {
753 EndpointCallId(self.handle.id().clone())
754 }
755
756 pub fn as_session_handle(&self) -> &SessionHandle {
759 &self.handle
760 }
761
762 pub async fn wait_for_answered(&self, timeout: Option<Duration>) -> Result<Self> {
764 let handle = self.handle.wait_for_answered(timeout).await?;
765 Ok(Self::new(handle, self.registrar.clone(), self.transport))
766 }
767
768 pub async fn wait_for_end(&self, timeout: Option<Duration>) -> Result<String> {
770 self.handle.wait_for_end(timeout).await
771 }
772
773 pub async fn hangup(&self) -> Result<()> {
775 self.handle.hangup().await
776 }
777
778 pub async fn hangup_and_wait(&self, timeout: Option<Duration>) -> Result<String> {
780 self.handle.hangup_and_wait(timeout).await
781 }
782
783 pub async fn hold(&self) -> Result<()> {
785 self.handle.hold().await
786 }
787
788 pub async fn resume(&self) -> Result<()> {
790 self.handle.resume().await
791 }
792
793 pub async fn mute(&self) -> Result<()> {
795 self.handle.mute().await
796 }
797
798 pub async fn unmute(&self) -> Result<()> {
800 self.handle.unmute().await
801 }
802
803 pub async fn send_dtmf(&self, digit: char) -> Result<()> {
805 self.handle.send_dtmf(digit).await
806 }
807
808 pub async fn transfer(&self, target: &str) -> Result<()> {
810 let target = normalize_target(self.registrar.as_deref(), target, self.transport)?;
811 self.handle.transfer_blind(&target).await
812 }
813
814 pub async fn audio(&self) -> Result<EndpointAudio> {
816 self.handle.audio().await.map(EndpointAudio::new)
817 }
818}
819
820impl fmt::Debug for EndpointCall {
821 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
822 f.debug_struct("EndpointCall")
823 .field("id", &self.id().to_string())
824 .finish()
825 }
826}
827
828pub struct EndpointIncomingCall {
830 incoming: IncomingCall,
831 registrar: Option<String>,
832 transport: EndpointTransport,
833}
834
835impl EndpointIncomingCall {
836 fn new(
837 incoming: IncomingCall,
838 registrar: Option<String>,
839 transport: EndpointTransport,
840 ) -> Self {
841 Self {
842 incoming,
843 registrar,
844 transport,
845 }
846 }
847
848 pub fn id(&self) -> EndpointCallId {
850 EndpointCallId(self.incoming.call_id.clone())
851 }
852
853 pub fn from(&self) -> &str {
855 &self.incoming.from
856 }
857
858 pub fn to(&self) -> &str {
860 &self.incoming.to
861 }
862
863 pub async fn answer(self) -> Result<EndpointCall> {
865 let handle = self.incoming.accept().await?;
866 Ok(EndpointCall::new(handle, self.registrar, self.transport))
867 }
868
869 pub async fn accept(self) -> Result<EndpointCall> {
871 self.answer().await
872 }
873
874 pub fn defer(self, watchdog: Duration) -> IncomingCallGuard {
876 self.incoming.defer(watchdog)
877 }
878
879 pub async fn decline(self) -> Result<()> {
881 self.reject(603, "Decline").await
882 }
883
884 pub async fn busy(self) -> Result<()> {
886 self.reject(486, "Busy Here").await
887 }
888
889 pub async fn reject(self, status: u16, reason: &str) -> Result<()> {
891 self.incoming.reject(status, reason);
892 Ok(())
893 }
894
895 pub async fn redirect_to(self, target: impl Into<String>) -> Result<()> {
897 self.incoming.redirect_to(target).await
898 }
899
900 pub async fn redirect_with_contacts<I, S>(self, status: u16, contacts: I) -> Result<()>
902 where
903 I: IntoIterator<Item = S>,
904 S: Into<String>,
905 {
906 self.incoming.redirect_with_contacts(status, contacts).await
907 }
908}
909
910impl fmt::Debug for EndpointIncomingCall {
911 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
912 f.debug_struct("EndpointIncomingCall")
913 .field("id", &self.id().to_string())
914 .field("from", &self.from())
915 .field("to", &self.to())
916 .finish()
917 }
918}
919
920pub struct EndpointAudio {
922 stream: AudioStream,
923}
924
925impl EndpointAudio {
926 fn new(stream: AudioStream) -> Self {
927 Self { stream }
928 }
929
930 pub fn split(self) -> (EndpointAudioSender, EndpointAudioReceiver) {
932 let (sender, receiver) = self.stream.split();
933 (
934 EndpointAudioSender { sender },
935 EndpointAudioReceiver { receiver },
936 )
937 }
938}
939
940#[derive(Clone)]
942pub struct EndpointAudioSender {
943 sender: AudioSender,
944}
945
946impl EndpointAudioSender {
947 pub async fn send(&self, frame: EndpointAudioFrame) -> Result<()> {
949 self.sender.send(frame.into()).await
950 }
951
952 pub fn is_open(&self) -> bool {
954 self.sender.is_open()
955 }
956}
957
958pub struct EndpointAudioReceiver {
960 receiver: AudioReceiver,
961}
962
963impl EndpointAudioReceiver {
964 pub async fn recv(&mut self) -> Option<EndpointAudioFrame> {
966 self.receiver.recv().await.map(EndpointAudioFrame::from)
967 }
968
969 pub fn try_recv(&mut self) -> Option<EndpointAudioFrame> {
971 self.receiver.try_recv().map(EndpointAudioFrame::from)
972 }
973}
974
975#[derive(Debug, Clone, Deserialize)]
977pub struct EndpointAudioFrame {
978 pub samples: Vec<i16>,
980 pub sample_rate: u32,
982 pub channels: u8,
984 pub timestamp: u32,
986}
987
988impl EndpointAudioFrame {
989 pub fn new(samples: Vec<i16>, sample_rate: u32, channels: u8, timestamp: u32) -> Self {
991 Self {
992 samples,
993 sample_rate,
994 channels,
995 timestamp,
996 }
997 }
998
999 pub fn pcmu_sized_mono_8khz(samples: Vec<i16>, timestamp: u32) -> Self {
1001 Self::new(samples, 8_000, 1, timestamp)
1002 }
1003
1004 pub fn samples_per_channel(&self) -> usize {
1006 self.samples.len() / self.channels.max(1) as usize
1007 }
1008}
1009
1010impl From<EndpointAudioFrame> for rvoip_media_core::types::AudioFrame {
1011 fn from(frame: EndpointAudioFrame) -> Self {
1012 rvoip_media_core::types::AudioFrame::new(
1013 frame.samples,
1014 frame.sample_rate,
1015 frame.channels,
1016 frame.timestamp,
1017 )
1018 }
1019}
1020
1021impl From<rvoip_media_core::types::AudioFrame> for EndpointAudioFrame {
1022 fn from(frame: rvoip_media_core::types::AudioFrame) -> Self {
1023 Self {
1024 samples: frame.samples,
1025 sample_rate: frame.sample_rate,
1026 channels: frame.channels,
1027 timestamp: frame.timestamp,
1028 }
1029 }
1030}
1031
1032#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1034pub enum EndpointRegistrationStatus {
1035 Registering,
1037 Registered,
1039 Unregistering,
1041 Unregistered,
1043 Failed,
1045}
1046
1047#[derive(Debug, Clone)]
1049pub struct EndpointRegistrationInfo {
1050 pub status: EndpointRegistrationStatus,
1052 pub registrar: Option<String>,
1054 pub contact: Option<String>,
1056 pub expires_secs: Option<u32>,
1058 pub accepted_expires_secs: Option<u32>,
1060 pub next_refresh_in: Option<Duration>,
1062 pub retry_count: u32,
1064 pub last_failure: Option<String>,
1066}
1067
1068impl From<RegistrationInfo> for EndpointRegistrationInfo {
1069 fn from(info: RegistrationInfo) -> Self {
1070 Self {
1071 status: match info.status {
1072 RegistrationStatus::Registering => EndpointRegistrationStatus::Registering,
1073 RegistrationStatus::Registered => EndpointRegistrationStatus::Registered,
1074 RegistrationStatus::Unregistering => EndpointRegistrationStatus::Unregistering,
1075 RegistrationStatus::Unregistered => EndpointRegistrationStatus::Unregistered,
1076 RegistrationStatus::Failed => EndpointRegistrationStatus::Failed,
1077 },
1078 registrar: info.registrar,
1079 contact: info.contact,
1080 expires_secs: info.expires_secs,
1081 accepted_expires_secs: info.accepted_expires_secs,
1082 next_refresh_in: info.next_refresh_in,
1083 retry_count: info.retry_count,
1084 last_failure: info.last_failure,
1085 }
1086 }
1087}
1088
1089#[derive(Debug, Clone)]
1099pub struct SipAccount {
1100 pub registrar: String,
1103 pub username: String,
1105 pub auth_username: Option<String>,
1107 pub password: String,
1109 pub expires: u32,
1111 pub from_uri: Option<String>,
1113 pub contact_uri: Option<String>,
1115}
1116
1117impl SipAccount {
1118 pub fn new(
1120 registrar: impl Into<String>,
1121 username: impl Into<String>,
1122 password: impl Into<String>,
1123 ) -> Self {
1124 Self {
1125 registrar: registrar.into(),
1126 username: username.into(),
1127 auth_username: None,
1128 password: password.into(),
1129 expires: 3600,
1130 from_uri: None,
1131 contact_uri: None,
1132 }
1133 }
1134
1135 pub fn effective_auth_username(&self) -> &str {
1137 self.auth_username
1138 .as_deref()
1139 .unwrap_or(self.username.as_str())
1140 }
1141
1142 pub fn credentials(&self) -> Credentials {
1144 Credentials::new(self.effective_auth_username(), self.password.clone())
1145 }
1146
1147 pub fn registration(&self) -> Registration {
1149 let mut registration = Registration::new(
1150 self.registrar.clone(),
1151 self.effective_auth_username().to_string(),
1152 self.password.clone(),
1153 )
1154 .expires(self.expires);
1155 if let Some(from_uri) = &self.from_uri {
1156 registration = registration.from_uri(from_uri.clone());
1157 }
1158 if let Some(contact_uri) = &self.contact_uri {
1159 registration = registration.contact_uri(contact_uri.clone());
1160 }
1161 registration
1162 }
1163
1164 pub fn endpoint_account(&self) -> EndpointAccount {
1166 EndpointAccount {
1167 registrar: self.registrar.clone(),
1168 username: self.username.clone(),
1169 auth_username: self.auth_username.clone(),
1170 password: self.password.clone(),
1171 expires: self.expires,
1172 from_uri: self.from_uri.clone(),
1173 contact_uri: self.contact_uri.clone(),
1174 }
1175 }
1176
1177 pub fn auth_username(mut self, username: impl Into<String>) -> Self {
1179 self.auth_username = Some(username.into());
1180 self
1181 }
1182
1183 pub fn expires(mut self, seconds: u32) -> Self {
1185 self.expires = seconds;
1186 self
1187 }
1188
1189 pub fn from_uri(mut self, uri: impl Into<String>) -> Self {
1191 self.from_uri = Some(uri.into());
1192 self
1193 }
1194
1195 pub fn contact_uri(mut self, uri: impl Into<String>) -> Self {
1197 self.contact_uri = Some(uri.into());
1198 self
1199 }
1200}
1201
1202#[derive(Debug, Clone)]
1211pub struct EndpointAccount {
1212 pub registrar: String,
1215 pub username: String,
1217 pub auth_username: Option<String>,
1219 pub password: String,
1221 pub expires: u32,
1223 pub from_uri: Option<String>,
1225 pub contact_uri: Option<String>,
1227}
1228
1229impl EndpointAccount {
1230 pub fn new(
1243 registrar: impl Into<String>,
1244 username: impl Into<String>,
1245 password: impl Into<String>,
1246 ) -> Self {
1247 Self {
1248 registrar: registrar.into(),
1249 username: username.into(),
1250 auth_username: None,
1251 password: password.into(),
1252 expires: 3600,
1253 from_uri: None,
1254 contact_uri: None,
1255 }
1256 }
1257
1258 pub fn auth_username(mut self, username: impl Into<String>) -> Self {
1260 self.auth_username = Some(username.into());
1261 self
1262 }
1263
1264 pub fn expires(mut self, seconds: u32) -> Self {
1266 self.expires = seconds;
1267 self
1268 }
1269
1270 pub fn from_uri(mut self, uri: impl Into<String>) -> Self {
1272 self.from_uri = Some(uri.into());
1273 self
1274 }
1275
1276 pub fn contact_uri(mut self, uri: impl Into<String>) -> Self {
1278 self.contact_uri = Some(uri.into());
1279 self
1280 }
1281}
1282
1283impl From<SipAccount> for EndpointAccount {
1284 fn from(account: SipAccount) -> Self {
1285 account.endpoint_account()
1286 }
1287}
1288
1289impl From<EndpointAccount> for SipAccount {
1290 fn from(account: EndpointAccount) -> Self {
1291 Self {
1292 registrar: account.registrar,
1293 username: account.username,
1294 auth_username: account.auth_username,
1295 password: account.password,
1296 expires: account.expires,
1297 from_uri: account.from_uri,
1298 contact_uri: account.contact_uri,
1299 }
1300 }
1301}
1302
1303#[derive(Debug, Clone, Default, Deserialize)]
1305#[serde(rename_all = "camelCase")]
1306pub struct EndpointConfig {
1307 pub name: Option<String>,
1309 pub profile: Option<EndpointProfileName>,
1311 pub bind: Option<SocketAddr>,
1313 pub advertise: Option<SocketAddr>,
1315 pub account: Option<EndpointAccountConfig>,
1317 pub network: Option<EndpointNetworkConfig>,
1319 pub media: Option<EndpointMediaConfig>,
1321 pub performance: Option<PerformanceConfig>,
1323 pub auto_180_ringing: Option<bool>,
1325 pub auto_100_trying: Option<bool>,
1327 pub fast_auto_accept_incoming_calls: Option<bool>,
1329 pub cleanup_diagnostics: Option<bool>,
1331 pub cleanup_diagnostic_events: Option<bool>,
1333 pub app_event_channel_capacity: Option<usize>,
1335 pub sip_transaction_command_channel_capacity: Option<usize>,
1337 pub server_call_admission_limit: Option<usize>,
1339 pub server_call_admission_soft_limit: Option<usize>,
1341 pub server_call_admission_pacing_delay_ms: Option<u64>,
1343 pub server_overload_retry_after_secs: Option<u32>,
1345 #[cfg(feature = "perf-tests")]
1347 pub perf_max_rss_growth_mb_per_hr: Option<f64>,
1348 pub srtp_diagnostics: Option<bool>,
1350 pub rtp_diagnostics: Option<bool>,
1352 pub media_sdp_diagnostics: Option<bool>,
1354 pub sip_trace: Option<crate::api::events::SipTraceConfig>,
1356 pub register_on_start: Option<bool>,
1358}
1359
1360#[derive(Debug, Clone, Deserialize)]
1362#[serde(rename_all = "camelCase")]
1363pub struct EndpointAccountConfig {
1364 pub registrar: String,
1366 pub username: String,
1368 pub auth_username: Option<String>,
1370 pub password: String,
1372 pub expires: Option<u32>,
1374 pub from_uri: Option<String>,
1376 pub contact_uri: Option<String>,
1378}
1379
1380impl TryFrom<EndpointAccountConfig> for EndpointAccount {
1381 type Error = SessionError;
1382
1383 fn try_from(config: EndpointAccountConfig) -> Result<Self> {
1384 let mut account = EndpointAccount::new(config.registrar, config.username, config.password);
1385 if let Some(auth_username) = config.auth_username {
1386 account = account.auth_username(auth_username);
1387 }
1388 if let Some(expires) = config.expires {
1389 account = account.expires(expires);
1390 }
1391 if let Some(from_uri) = config.from_uri {
1392 account = account.from_uri(from_uri);
1393 }
1394 if let Some(contact_uri) = config.contact_uri {
1395 account = account.contact_uri(contact_uri);
1396 }
1397 Ok(account)
1398 }
1399}
1400
1401#[derive(Debug, Clone, Default, Deserialize)]
1403#[serde(rename_all = "camelCase")]
1404pub struct EndpointNetworkConfig {
1405 pub bind: Option<SocketAddr>,
1407 pub advertise: Option<SocketAddr>,
1409 pub transport: Option<EndpointTransport>,
1411 pub stun: Option<String>,
1413 pub outbound_proxy: Option<String>,
1415 pub sip_instance: Option<String>,
1417 pub tls_bind: Option<SocketAddr>,
1419 pub tls_cert_path: Option<PathBuf>,
1421 pub tls_key_path: Option<PathBuf>,
1423 pub udp_parse_workers: Option<usize>,
1425 pub udp_parse_queue_capacity: Option<usize>,
1427}
1428
1429#[derive(Debug, Clone, Default, Deserialize)]
1431#[serde(rename_all = "camelCase")]
1432pub struct EndpointMediaConfig {
1433 pub public_address: Option<String>,
1435 pub port_start: Option<u16>,
1437 pub port_end: Option<u16>,
1439 pub enabled: Option<bool>,
1441 pub signaling_only_rtp_port: Option<u16>,
1443 pub srtp: Option<EndpointSrtpMode>,
1445}
1446
1447#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
1449#[serde(rename_all = "kebab-case")]
1450pub enum EndpointProfileName {
1451 Local,
1453 LanPbx,
1455 AsteriskUdp,
1457 AsteriskTlsSrtp,
1459 FreeswitchInternal,
1461 FreeswitchTlsSrtp,
1463 CarrierSbc,
1465}
1466
1467impl From<EndpointProfileName> for EndpointProfile {
1468 fn from(profile: EndpointProfileName) -> Self {
1469 match profile {
1470 EndpointProfileName::Local => EndpointProfile::Local,
1471 EndpointProfileName::LanPbx => EndpointProfile::LanPbx,
1472 EndpointProfileName::AsteriskUdp => EndpointProfile::AsteriskUdp,
1473 EndpointProfileName::AsteriskTlsSrtp => EndpointProfile::AsteriskTlsSrtpRegisteredFlow,
1474 EndpointProfileName::FreeswitchInternal => EndpointProfile::FreeSwitchInternal,
1475 EndpointProfileName::FreeswitchTlsSrtp => {
1476 EndpointProfile::FreeSwitchTlsSrtpReachableContact
1477 }
1478 EndpointProfileName::CarrierSbc => EndpointProfile::CarrierSbc,
1479 }
1480 }
1481}
1482
1483#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
1485#[serde(rename_all = "lowercase")]
1486pub enum EndpointTransport {
1487 Udp,
1489 Tcp,
1491 Tls,
1493}
1494
1495#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
1497#[serde(rename_all = "lowercase")]
1498pub enum EndpointSrtpMode {
1499 Off,
1501 Offer,
1503 Required,
1505}
1506
1507#[derive(Debug, Clone)]
1513pub enum EndpointProfile {
1514 Local,
1516 LanPbx,
1518 AsteriskUdp,
1520 AsteriskTlsSrtpRegisteredFlow,
1522 FreeSwitchInternal,
1524 FreeSwitchTlsSrtpReachableContact,
1526 CarrierSbc,
1528 Custom(Config),
1530}
1531
1532impl Default for EndpointProfile {
1533 fn default() -> Self {
1534 Self::Local
1535 }
1536}
1537
1538pub struct EndpointBuilder {
1544 name: Option<String>,
1545 profile: EndpointProfile,
1546 bind_addr: Option<SocketAddr>,
1547 advertised_addr: Option<SocketAddr>,
1548 tls_bind_addr: Option<SocketAddr>,
1549 tls_cert_path: Option<std::path::PathBuf>,
1550 tls_key_path: Option<std::path::PathBuf>,
1551 media_port_start: Option<u16>,
1552 media_port_end: Option<u16>,
1553 media_public_addr: Option<SocketAddr>,
1554 media_mode: Option<MediaMode>,
1555 stun_server: Option<String>,
1556 outbound_proxy_uri: Option<String>,
1557 sip_instance: Option<String>,
1558 transport: EndpointTransport,
1559 sip_udp_parse_workers: Option<usize>,
1560 sip_udp_parse_queue_capacity: Option<usize>,
1561 performance: Option<PerformanceConfig>,
1562 srtp_mode: Option<EndpointSrtpMode>,
1563 auto_180_ringing: Option<bool>,
1564 auto_100_trying: Option<bool>,
1565 fast_auto_accept_incoming_calls: Option<bool>,
1566 cleanup_diagnostics: Option<bool>,
1567 cleanup_diagnostic_events: Option<bool>,
1568 app_event_channel_capacity: Option<usize>,
1569 sip_transaction_command_channel_capacity: Option<usize>,
1570 server_call_admission_limit: Option<usize>,
1571 server_call_admission_soft_limit: Option<usize>,
1572 server_call_admission_pacing_delay_ms: Option<u64>,
1573 server_overload_retry_after_secs: Option<u32>,
1574 #[cfg(feature = "perf-tests")]
1575 perf_max_rss_growth_mb_per_hr: Option<f64>,
1576 srtp_diagnostics: Option<bool>,
1577 rtp_diagnostics: Option<bool>,
1578 media_sdp_diagnostics: Option<bool>,
1579 account_username: Option<String>,
1580 auth_username: Option<String>,
1581 password: Option<String>,
1582 auth: Option<SipClientAuth>,
1583 registrar: Option<String>,
1584 expires: u32,
1585 sip_trace: Option<crate::api::events::SipTraceConfig>,
1586 from_uri: Option<String>,
1587 contact_uri: Option<String>,
1588 configurators: Vec<Box<dyn FnOnce(&mut Config) + Send>>,
1589}
1590
1591impl EndpointBuilder {
1592 pub fn new() -> Self {
1594 Self {
1595 name: None,
1596 profile: EndpointProfile::Local,
1597 bind_addr: None,
1598 advertised_addr: None,
1599 tls_bind_addr: None,
1600 tls_cert_path: None,
1601 tls_key_path: None,
1602 media_port_start: None,
1603 media_port_end: None,
1604 media_public_addr: None,
1605 media_mode: None,
1606 stun_server: None,
1607 outbound_proxy_uri: None,
1608 sip_instance: None,
1609 transport: EndpointTransport::Udp,
1610 sip_udp_parse_workers: None,
1611 sip_udp_parse_queue_capacity: None,
1612 performance: None,
1613 srtp_mode: None,
1614 auto_180_ringing: None,
1615 auto_100_trying: None,
1616 fast_auto_accept_incoming_calls: None,
1617 cleanup_diagnostics: None,
1618 cleanup_diagnostic_events: None,
1619 app_event_channel_capacity: None,
1620 sip_transaction_command_channel_capacity: None,
1621 server_call_admission_limit: None,
1622 server_call_admission_soft_limit: None,
1623 server_call_admission_pacing_delay_ms: None,
1624 server_overload_retry_after_secs: None,
1625 #[cfg(feature = "perf-tests")]
1626 perf_max_rss_growth_mb_per_hr: None,
1627 srtp_diagnostics: None,
1628 rtp_diagnostics: None,
1629 media_sdp_diagnostics: None,
1630 account_username: None,
1631 auth_username: None,
1632 password: None,
1633 auth: None,
1634 registrar: None,
1635 expires: 3600,
1636 sip_trace: None,
1637 from_uri: None,
1638 contact_uri: None,
1639 configurators: Vec::new(),
1640 }
1641 }
1642
1643 pub fn from_config(config: EndpointConfig) -> Result<Self> {
1645 let mut builder = EndpointBuilder::new();
1646
1647 if let Some(name) = config.name {
1648 builder = builder.name(name);
1649 }
1650 if let Some(profile) = config.profile {
1651 builder = builder.profile(profile.into());
1652 }
1653 if let Some(performance) = config.performance {
1654 builder = builder.performance_config(performance);
1655 }
1656 if let Some(bind) = config.bind.or(config.network.as_ref().and_then(|n| n.bind)) {
1657 builder = builder.bind_addr(bind);
1658 }
1659 if let Some(advertise) = config
1660 .advertise
1661 .or(config.network.as_ref().and_then(|n| n.advertise))
1662 {
1663 builder = builder.advertised_addr(advertise);
1664 }
1665
1666 if let Some(account) = config.account {
1667 builder = builder.endpoint_account(account.try_into()?);
1668 }
1669 if let Some(auto_180_ringing) = config.auto_180_ringing {
1670 builder = builder.auto_180_ringing(auto_180_ringing);
1671 }
1672 if let Some(auto_100_trying) = config.auto_100_trying {
1673 builder = builder.auto_100_trying(auto_100_trying);
1674 }
1675 if let Some(fast_auto_accept) = config.fast_auto_accept_incoming_calls {
1676 builder = builder.fast_auto_accept_incoming_calls(fast_auto_accept);
1677 }
1678 if let Some(cleanup_diagnostics) = config.cleanup_diagnostics {
1679 builder = builder.cleanup_diagnostics(cleanup_diagnostics);
1680 }
1681 if let Some(cleanup_diagnostic_events) = config.cleanup_diagnostic_events {
1682 builder = builder.cleanup_diagnostic_events(cleanup_diagnostic_events);
1683 }
1684 if let Some(capacity) = config.app_event_channel_capacity {
1685 builder = builder.app_event_channel_capacity(capacity);
1686 }
1687 if let Some(capacity) = config.sip_transaction_command_channel_capacity {
1688 builder = builder.sip_transaction_command_channel_capacity(capacity);
1689 }
1690 if let Some(limit) = config.server_call_admission_limit {
1691 builder = builder.server_call_admission_limit(limit);
1692 }
1693 if let Some(limit) = config.server_call_admission_soft_limit {
1694 builder = builder.server_call_admission_soft_limit(limit);
1695 }
1696 if let Some(delay_ms) = config.server_call_admission_pacing_delay_ms {
1697 builder = builder.server_call_admission_pacing_delay_ms(delay_ms);
1698 }
1699 if let Some(seconds) = config.server_overload_retry_after_secs {
1700 builder = builder.server_overload_retry_after_secs(seconds);
1701 }
1702 #[cfg(feature = "perf-tests")]
1703 if let Some(limit) = config.perf_max_rss_growth_mb_per_hr {
1704 builder = builder.perf_max_rss_growth_mb_per_hr(limit);
1705 }
1706 if let Some(srtp_diagnostics) = config.srtp_diagnostics {
1707 builder = builder.srtp_diagnostics(srtp_diagnostics);
1708 }
1709 if let Some(rtp_diagnostics) = config.rtp_diagnostics {
1710 builder = builder.rtp_diagnostics(rtp_diagnostics);
1711 }
1712 if let Some(media_sdp_diagnostics) = config.media_sdp_diagnostics {
1713 builder = builder.media_sdp_diagnostics(media_sdp_diagnostics);
1714 }
1715
1716 if let Some(network) = config.network {
1717 if let Some(transport) = network.transport {
1718 builder = builder.transport(transport);
1719 }
1720 if let Some(stun) = network.stun {
1721 builder = builder.stun_server(stun);
1722 }
1723 if let Some(proxy) = network.outbound_proxy {
1724 builder = builder.outbound_proxy(proxy);
1725 }
1726 if let Some(instance) = network.sip_instance {
1727 builder = builder.sip_instance(instance);
1728 }
1729 if let Some(tls_bind) = network.tls_bind {
1730 builder = builder.tls_bind_addr(tls_bind);
1731 }
1732 if let Some(path) = network.tls_cert_path {
1733 builder = builder.tls_cert_path(path);
1734 }
1735 if let Some(path) = network.tls_key_path {
1736 builder = builder.tls_key_path(path);
1737 }
1738 if let Some(workers) = network.udp_parse_workers {
1739 builder = builder.sip_udp_parse_workers(workers);
1740 }
1741 if let Some(capacity) = network.udp_parse_queue_capacity {
1742 builder = builder.sip_udp_parse_queue_capacity(capacity);
1743 }
1744 }
1745
1746 if let Some(media) = config.media {
1747 if let Some(public) = media.public_address {
1748 builder = builder.media_public_addr(parse_media_public_address(&public)?);
1749 }
1750 if let Some(start) = media.port_start {
1751 let end = media.port_end.unwrap_or(start);
1752 builder = builder.media_ports(start, end);
1753 } else if let Some(end) = media.port_end {
1754 builder = builder.media_ports(Config::DEFAULT_MEDIA_PORT_START, end);
1755 }
1756 if let Some(srtp) = media.srtp {
1757 builder = builder.srtp(srtp);
1758 }
1759 if media.enabled == Some(false) || media.signaling_only_rtp_port.is_some() {
1760 builder = builder.signaling_only_media(media.signaling_only_rtp_port.unwrap_or(9));
1761 } else if media.enabled == Some(true) {
1762 builder = builder.media_enabled(true);
1763 }
1764 }
1765
1766 if let Some(sip_trace) = config.sip_trace {
1767 builder = builder.sip_trace(sip_trace);
1768 }
1769
1770 Ok(builder)
1771 }
1772
1773 pub fn name(mut self, name: impl Into<String>) -> Self {
1775 self.name = Some(name.into());
1776 self
1777 }
1778
1779 pub fn account(mut self, username: impl Into<String>) -> Self {
1781 self.account_username = Some(username.into());
1782 self
1783 }
1784
1785 pub fn endpoint_account(mut self, account: EndpointAccount) -> Self {
1787 self.registrar = Some(account.registrar);
1788 self.account_username = Some(account.username);
1789 self.auth_username = account.auth_username;
1790 self.password = Some(account.password);
1791 self.expires = account.expires;
1792 self.from_uri = account.from_uri;
1793 self.contact_uri = account.contact_uri;
1794 self
1795 }
1796
1797 pub fn sip_account(self, account: SipAccount) -> Self {
1799 self.endpoint_account(account.into())
1800 }
1801
1802 pub fn auth_username(mut self, username: impl Into<String>) -> Self {
1804 self.auth_username = Some(username.into());
1805 self
1806 }
1807
1808 pub fn password(mut self, password: impl Into<String>) -> Self {
1810 self.password = Some(password.into());
1811 self
1812 }
1813
1814 pub fn auth(mut self, auth: SipClientAuth) -> Self {
1819 self.auth = Some(auth);
1820 self
1821 }
1822
1823 pub fn bearer_token(mut self, token: impl Into<String>) -> Self {
1825 self.auth = Some(SipClientAuth::bearer_token(token));
1826 self
1827 }
1828
1829 pub fn basic_credentials(
1834 mut self,
1835 username: impl Into<String>,
1836 password: impl Into<String>,
1837 ) -> Self {
1838 self.auth = Some(SipClientAuth::basic(username, password));
1839 self
1840 }
1841
1842 pub fn registrar(mut self, registrar: impl Into<String>) -> Self {
1844 self.registrar = Some(registrar.into());
1845 self
1846 }
1847
1848 pub fn expires(mut self, seconds: u32) -> Self {
1850 self.expires = seconds;
1851 self
1852 }
1853
1854 pub fn profile(mut self, profile: EndpointProfile) -> Self {
1856 self.profile = profile;
1857 self
1858 }
1859
1860 pub fn config(self, config: EndpointConfig) -> Result<Self> {
1862 let mut configured = EndpointBuilder::from_config(config)?;
1863 if self.name.is_some() {
1864 configured.name = self.name;
1865 }
1866 if self.sip_trace.is_some() {
1867 configured.sip_trace = self.sip_trace;
1868 }
1869 Ok(configured)
1870 }
1871
1872 pub fn bind_addr(mut self, addr: SocketAddr) -> Self {
1874 self.bind_addr = Some(addr);
1875 self
1876 }
1877
1878 pub fn advertised_addr(mut self, addr: SocketAddr) -> Self {
1880 self.advertised_addr = Some(addr);
1881 self
1882 }
1883
1884 pub fn tls_bind_addr(mut self, addr: SocketAddr) -> Self {
1886 self.tls_bind_addr = Some(addr);
1887 self
1888 }
1889
1890 pub fn tls_cert_path(mut self, path: impl Into<std::path::PathBuf>) -> Self {
1892 self.tls_cert_path = Some(path.into());
1893 self
1894 }
1895
1896 pub fn tls_key_path(mut self, path: impl Into<std::path::PathBuf>) -> Self {
1898 self.tls_key_path = Some(path.into());
1899 self
1900 }
1901
1902 pub fn media_ports(mut self, start: u16, end: u16) -> Self {
1904 self.media_port_start = Some(start);
1905 self.media_port_end = Some(end);
1906 self
1907 }
1908
1909 pub fn media_enabled(mut self, enabled: bool) -> Self {
1911 self.media_mode = Some(if enabled {
1912 MediaMode::Enabled
1913 } else {
1914 MediaMode::SignalingOnly { sdp_rtp_port: 9 }
1915 });
1916 self
1917 }
1918
1919 pub fn signaling_only_media(mut self, sdp_rtp_port: u16) -> Self {
1921 self.media_mode = Some(MediaMode::SignalingOnly { sdp_rtp_port });
1922 self
1923 }
1924
1925 pub fn media_public_addr(mut self, addr: SocketAddr) -> Self {
1927 self.media_public_addr = Some(addr);
1928 self
1929 }
1930
1931 pub fn media_public_ip(mut self, addr: IpAddr) -> Self {
1933 self.media_public_addr = Some(SocketAddr::new(addr, 0));
1934 self
1935 }
1936
1937 pub fn stun_server(mut self, server: impl Into<String>) -> Self {
1939 self.stun_server = Some(server.into());
1940 self
1941 }
1942
1943 pub fn outbound_proxy(mut self, uri: impl Into<String>) -> Self {
1945 self.outbound_proxy_uri = Some(uri.into());
1946 self
1947 }
1948
1949 pub fn sip_instance(mut self, urn: impl Into<String>) -> Self {
1951 self.sip_instance = Some(urn.into());
1952 self
1953 }
1954
1955 pub fn transport(mut self, transport: EndpointTransport) -> Self {
1957 self.transport = transport;
1958 self
1959 }
1960
1961 pub fn sip_udp_parse_workers(mut self, workers: usize) -> Self {
1963 self.sip_udp_parse_workers = Some(workers);
1964 self
1965 }
1966
1967 pub fn sip_udp_parse_queue_capacity(mut self, capacity: usize) -> Self {
1969 self.sip_udp_parse_queue_capacity = Some(capacity);
1970 self
1971 }
1972
1973 pub fn performance_config(mut self, performance: PerformanceConfig) -> Self {
1975 self.performance = Some(performance);
1976 self
1977 }
1978
1979 pub fn pbx_media_server_performance(mut self, capacity: usize) -> Self {
1981 self.performance = Some(PerformanceConfig::pbx_media_server(capacity));
1982 self
1983 }
1984
1985 pub fn signaling_only_server_high_performance(mut self, capacity: usize) -> Self {
1987 self.performance = Some(PerformanceConfig::signaling_only_server_high_performance(
1988 capacity,
1989 ));
1990 self
1991 }
1992
1993 pub fn signaling_only_server_high_performance_with_port(
1995 mut self,
1996 capacity: usize,
1997 sdp_rtp_port: u16,
1998 ) -> Self {
1999 self.performance = Some(
2000 PerformanceConfig::signaling_only_server_high_performance(capacity)
2001 .with_signaling_only_rtp_port(sdp_rtp_port),
2002 );
2003 self
2004 }
2005
2006 pub fn auto_180_ringing(mut self, enabled: bool) -> Self {
2008 self.auto_180_ringing = Some(enabled);
2009 self
2010 }
2011
2012 pub fn auto_100_trying(mut self, enabled: bool) -> Self {
2014 self.auto_100_trying = Some(enabled);
2015 self
2016 }
2017
2018 pub fn fast_auto_accept_incoming_calls(mut self, enabled: bool) -> Self {
2020 self.fast_auto_accept_incoming_calls = Some(enabled);
2021 self
2022 }
2023
2024 pub fn cleanup_diagnostics(mut self, enabled: bool) -> Self {
2026 self.cleanup_diagnostics = Some(enabled);
2027 self
2028 }
2029
2030 pub fn cleanup_diagnostic_events(mut self, enabled: bool) -> Self {
2032 self.cleanup_diagnostic_events = Some(enabled);
2033 self
2034 }
2035
2036 pub fn app_event_channel_capacity(mut self, capacity: usize) -> Self {
2038 self.app_event_channel_capacity = Some(capacity);
2039 self
2040 }
2041
2042 pub fn sip_transaction_command_channel_capacity(mut self, capacity: usize) -> Self {
2044 self.sip_transaction_command_channel_capacity = Some(capacity);
2045 self
2046 }
2047
2048 pub fn server_call_admission_limit(mut self, limit: usize) -> Self {
2050 self.server_call_admission_limit = Some(limit);
2051 self
2052 }
2053
2054 pub fn server_call_admission_soft_limit(mut self, limit: usize) -> Self {
2056 self.server_call_admission_soft_limit = Some(limit);
2057 self
2058 }
2059
2060 pub fn server_call_admission_pacing_delay_ms(mut self, delay_ms: u64) -> Self {
2062 self.server_call_admission_pacing_delay_ms = Some(delay_ms);
2063 self
2064 }
2065
2066 pub fn server_overload_retry_after_secs(mut self, seconds: u32) -> Self {
2068 self.server_overload_retry_after_secs = Some(seconds);
2069 self
2070 }
2071
2072 #[cfg(feature = "perf-tests")]
2074 pub fn perf_max_rss_growth_mb_per_hr(mut self, limit: f64) -> Self {
2075 self.perf_max_rss_growth_mb_per_hr = Some(limit);
2076 self
2077 }
2078
2079 pub fn srtp_diagnostics(mut self, enabled: bool) -> Self {
2081 self.srtp_diagnostics = Some(enabled);
2082 self
2083 }
2084
2085 pub fn rtp_diagnostics(mut self, enabled: bool) -> Self {
2087 self.rtp_diagnostics = Some(enabled);
2088 self
2089 }
2090
2091 pub fn media_sdp_diagnostics(mut self, enabled: bool) -> Self {
2093 self.media_sdp_diagnostics = Some(enabled);
2094 self
2095 }
2096
2097 pub fn srtp(mut self, mode: EndpointSrtpMode) -> Self {
2099 self.srtp_mode = Some(mode);
2100 self
2101 }
2102
2103 pub fn enable_sip_trace(mut self) -> Self {
2105 self.sip_trace = Some(crate::api::events::SipTraceConfig::enabled());
2106 self
2107 }
2108
2109 pub fn sip_trace(mut self, config: crate::api::events::SipTraceConfig) -> Self {
2111 self.sip_trace = Some(config);
2112 self
2113 }
2114
2115 pub fn from_uri(mut self, uri: impl Into<String>) -> Self {
2117 self.from_uri = Some(uri.into());
2118 self
2119 }
2120
2121 pub fn contact_uri(mut self, uri: impl Into<String>) -> Self {
2123 self.contact_uri = Some(uri.into());
2124 self
2125 }
2126
2127 pub fn configure(mut self, f: impl FnOnce(&mut Config) + Send + 'static) -> Self {
2129 self.configurators.push(Box::new(f));
2130 self
2131 }
2132
2133 pub async fn build(self) -> Result<Endpoint> {
2135 let parts = self.build_parts()?;
2136 let peer = StreamPeer::with_config(parts.config).await?;
2137 Ok(Endpoint {
2138 peer,
2139 registration: parts.registration,
2140 registration_handle: Arc::new(Mutex::new(None)),
2141 registrar: parts.registrar,
2142 transport: parts.transport,
2143 })
2144 }
2145
2146 fn build_parts(self) -> Result<EndpointParts> {
2147 let mut config = self.profile_config()?;
2148 let registrar = self
2149 .registrar
2150 .clone()
2151 .map(|uri| apply_transport_to_uri(&uri, self.transport, true));
2152 let account_username = self.account_username.clone();
2153
2154 if let (Some(username), Some(password)) = (&account_username, &self.password) {
2155 let auth_username = self.auth_username.as_deref().unwrap_or(username);
2156 config.credentials = Some(Credentials::new(auth_username, password));
2157 }
2158 if let Some(auth) = self.auth {
2159 config.auth = Some(auth);
2160 }
2161
2162 if let Some(performance) = self.performance {
2163 config = config.try_with_performance_config(performance)?;
2164 }
2165
2166 if self.media_port_start.is_some() || self.media_port_end.is_some() {
2167 let media_port_start = self.media_port_start.unwrap_or(config.media_port_start);
2168 let media_port_end = self.media_port_end.unwrap_or(config.media_port_end);
2169 config = config.with_media_ports(media_port_start, media_port_end);
2170 }
2171 if let Some(addr) = self.media_public_addr {
2172 config.media_public_addr = Some(addr);
2173 }
2174 if let Some(mode) = self.media_mode {
2175 config.media_mode = mode;
2176 }
2177 if let Some(stun) = self.stun_server {
2178 config.stun_server = Some(stun);
2179 }
2180 if let Some(outbound_proxy) = self.outbound_proxy_uri.as_ref() {
2181 config.outbound_proxy_uri =
2182 Some(apply_transport_to_uri(outbound_proxy, self.transport, true));
2183 }
2184 if let Some(srtp_mode) = self.srtp_mode {
2185 match srtp_mode {
2186 EndpointSrtpMode::Off => {
2187 config.offer_srtp = false;
2188 config.srtp_required = false;
2189 }
2190 EndpointSrtpMode::Offer => {
2191 config.offer_srtp = true;
2192 config.srtp_required = false;
2193 }
2194 EndpointSrtpMode::Required => {
2195 config.offer_srtp = true;
2196 config.srtp_required = true;
2197 }
2198 }
2199 }
2200 if let Some(sip_trace) = self.sip_trace {
2201 config.sip_trace = sip_trace;
2202 }
2203 if let Some(workers) = self.sip_udp_parse_workers {
2204 config.sip_udp_parse_workers = Some(workers);
2205 }
2206 if let Some(capacity) = self.sip_udp_parse_queue_capacity {
2207 config.sip_udp_parse_queue_capacity = Some(capacity);
2208 }
2209 if let Some(auto_180_ringing) = self.auto_180_ringing {
2210 config.auto_180_ringing = auto_180_ringing;
2211 }
2212 if let Some(auto_100_trying) = self.auto_100_trying {
2213 config.auto_100_trying = auto_100_trying;
2214 }
2215 if let Some(fast_auto_accept) = self.fast_auto_accept_incoming_calls {
2216 config.fast_auto_accept_incoming_calls = fast_auto_accept;
2217 }
2218 if let Some(cleanup_diagnostics) = self.cleanup_diagnostics {
2219 config.cleanup_diagnostics = cleanup_diagnostics;
2220 }
2221 if let Some(cleanup_diagnostic_events) = self.cleanup_diagnostic_events {
2222 config.cleanup_diagnostic_events = cleanup_diagnostic_events;
2223 }
2224 if let Some(capacity) = self.app_event_channel_capacity {
2225 config = config.with_app_event_channel_capacity(capacity);
2226 }
2227 if let Some(capacity) = self.sip_transaction_command_channel_capacity {
2228 config = config.with_sip_transaction_command_channel_capacity(capacity);
2229 }
2230 if let Some(limit) = self.server_call_admission_limit {
2231 config = config.with_server_call_admission_limit(limit);
2232 }
2233 if let Some(limit) = self.server_call_admission_soft_limit {
2234 config = config.with_server_call_admission_soft_limit(limit);
2235 }
2236 if let Some(delay_ms) = self.server_call_admission_pacing_delay_ms {
2237 config = config.with_server_call_admission_pacing_delay_ms(delay_ms);
2238 }
2239 if let Some(seconds) = self.server_overload_retry_after_secs {
2240 config = config.with_server_overload_retry_after_secs(seconds);
2241 }
2242 #[cfg(feature = "perf-tests")]
2243 if let Some(limit) = self.perf_max_rss_growth_mb_per_hr {
2244 config.perf_max_rss_growth_mb_per_hr = Some(limit);
2245 }
2246 if let Some(srtp_diagnostics) = self.srtp_diagnostics {
2247 config.srtp_diagnostics = srtp_diagnostics;
2248 }
2249 if let Some(rtp_diagnostics) = self.rtp_diagnostics {
2250 config.rtp_diagnostics = rtp_diagnostics;
2251 }
2252 if let Some(media_sdp_diagnostics) = self.media_sdp_diagnostics {
2253 config.media_sdp_diagnostics = media_sdp_diagnostics;
2254 }
2255 if self.transport == EndpointTransport::Tls && config.sip_tls_mode == SipTlsMode::Disabled {
2256 config.sip_tls_mode = SipTlsMode::ClientOnly;
2257 }
2258
2259 let derived_from_uri = match (&self.from_uri, &account_username, ®istrar) {
2260 (Some(uri), _, _) => Some(uri.clone()),
2261 (None, Some(username), Some(registrar)) => Some(account_aor_uri(registrar, username)?),
2262 _ => None,
2263 };
2264 if let Some(from_uri) = &derived_from_uri {
2265 config.local_uri = from_uri.clone();
2266 }
2267
2268 if let Some(contact_uri) = &self.contact_uri {
2269 config.contact_uri = Some(contact_uri.clone());
2270 }
2271
2272 for configure in self.configurators {
2273 configure(&mut config);
2274 }
2275
2276 let registration = match (
2277 registrar.as_ref(),
2278 account_username.as_ref(),
2279 self.password.as_ref(),
2280 ) {
2281 (Some(registrar), Some(username), Some(password)) => {
2282 let auth_username = self.auth_username.as_deref().unwrap_or(username);
2283 let mut registration = Registration::new(
2284 registrar.clone(),
2285 auth_username.to_string(),
2286 password.clone(),
2287 )
2288 .expires(self.expires);
2289 if let Some(from_uri) = derived_from_uri {
2290 registration = registration.from_uri(from_uri);
2291 }
2292 if let Some(contact_uri) = self.contact_uri {
2293 registration = registration.contact_uri(contact_uri);
2294 }
2295 Some(registration)
2296 }
2297 _ => None,
2298 };
2299
2300 Ok(EndpointParts {
2301 config,
2302 registration,
2303 registrar,
2304 transport: self.transport,
2305 })
2306 }
2307
2308 fn profile_config(&self) -> Result<Config> {
2309 let name = self
2310 .name
2311 .as_deref()
2312 .or(self.account_username.as_deref())
2313 .unwrap_or("endpoint");
2314
2315 match &self.profile {
2316 EndpointProfile::Local => {
2317 let bind = self
2318 .bind_addr
2319 .unwrap_or_else(|| SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5060));
2320 if bind.ip().is_loopback() {
2321 Ok(Config::local(name, bind.port()))
2322 } else {
2323 let mut config = Config::on(name, bind.ip(), bind.port());
2324 config.bind_addr = bind;
2325 Ok(config)
2326 }
2327 }
2328 EndpointProfile::LanPbx => {
2329 let bind = self.bind_addr.unwrap_or_else(default_udp_bind);
2330 let advertised = self.advertised_addr.ok_or_else(|| {
2331 SessionError::ConfigError(
2332 "EndpointProfile::LanPbx requires advertised_addr".to_string(),
2333 )
2334 })?;
2335 Ok(Config::lan_pbx(name, bind, advertised))
2336 }
2337 EndpointProfile::AsteriskUdp => {
2338 let bind = self.bind_addr.unwrap_or_else(default_udp_bind);
2339 if let Some(advertised) = self.advertised_addr {
2340 Ok(Config::lan_pbx(name, bind, advertised))
2341 } else if bind.ip().is_loopback() {
2342 let mut config = Config::local(name, bind.port());
2343 config.bind_addr = bind;
2344 Ok(config)
2345 } else if bind.ip().is_unspecified() {
2346 Err(SessionError::ConfigError(
2347 "EndpointProfile::AsteriskUdp with an unspecified bind address requires advertised_addr"
2348 .to_string(),
2349 ))
2350 } else {
2351 let mut config = Config::on(name, bind.ip(), bind.port());
2352 config.bind_addr = bind;
2353 Ok(config)
2354 }
2355 }
2356 EndpointProfile::AsteriskTlsSrtpRegisteredFlow => {
2357 let bind = self.bind_addr.unwrap_or_else(default_tls_bind);
2358 Ok(Config::asterisk_tls_registered_flow(
2359 name,
2360 bind,
2361 self.sip_instance
2362 .clone()
2363 .unwrap_or_else(generate_sip_instance),
2364 ))
2365 }
2366 EndpointProfile::FreeSwitchInternal => {
2367 let bind = self.bind_addr.unwrap_or_else(default_udp_bind);
2368 Ok(Config::freeswitch_internal(name, bind))
2369 }
2370 EndpointProfile::FreeSwitchTlsSrtpReachableContact => {
2371 let bind = self.bind_addr.unwrap_or_else(default_udp_bind);
2372 let tls_bind = self.tls_bind_addr.unwrap_or_else(default_tls_bind);
2373 let cert = self.tls_cert_path.clone().ok_or_else(|| {
2374 SessionError::ConfigError(
2375 "EndpointProfile::FreeSwitchTlsSrtpReachableContact requires tls_cert_path"
2376 .to_string(),
2377 )
2378 })?;
2379 let key = self.tls_key_path.clone().ok_or_else(|| {
2380 SessionError::ConfigError(
2381 "EndpointProfile::FreeSwitchTlsSrtpReachableContact requires tls_key_path"
2382 .to_string(),
2383 )
2384 })?;
2385 Ok(Config::freeswitch_tls_srtp_reachable_contact(
2386 name, bind, tls_bind, cert, key,
2387 ))
2388 }
2389 EndpointProfile::CarrierSbc => {
2390 let bind = self.bind_addr.unwrap_or_else(default_tls_bind);
2391 let public = self.advertised_addr.ok_or_else(|| {
2392 SessionError::ConfigError(
2393 "EndpointProfile::CarrierSbc requires advertised_addr".to_string(),
2394 )
2395 })?;
2396 let outbound_proxy = self.outbound_proxy_uri.clone().ok_or_else(|| {
2397 SessionError::ConfigError(
2398 "EndpointProfile::CarrierSbc requires outbound_proxy".to_string(),
2399 )
2400 })?;
2401 Ok(Config::carrier_sbc(
2402 name,
2403 bind,
2404 public,
2405 outbound_proxy,
2406 self.sip_instance
2407 .clone()
2408 .unwrap_or_else(generate_sip_instance),
2409 ))
2410 }
2411 EndpointProfile::Custom(config) => Ok(config.clone()),
2412 }
2413 }
2414}
2415
2416impl Default for EndpointBuilder {
2417 fn default() -> Self {
2418 Self::new()
2419 }
2420}
2421
2422struct EndpointParts {
2423 config: Config,
2424 registration: Option<Registration>,
2425 registrar: Option<String>,
2426 transport: EndpointTransport,
2427}
2428
2429fn default_udp_bind() -> SocketAddr {
2430 SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 5060)
2431}
2432
2433fn default_tls_bind() -> SocketAddr {
2434 SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 5061)
2435}
2436
2437fn generate_sip_instance() -> String {
2438 format!("urn:uuid:{}", uuid::Uuid::new_v4())
2439}
2440
2441async fn wait_for_registration_result(
2442 events: &mut EndpointEvents,
2443 handle: &RegistrationHandle,
2444 timeout: Option<Duration>,
2445) -> Result<EndpointRegistrationInfo> {
2446 let coordinator = events.control.coordinator().clone();
2447 let registrar = coordinator
2448 .registration_info(handle)
2449 .await?
2450 .registrar
2451 .unwrap_or_default();
2452 let fut = async {
2453 loop {
2454 match events.next().await? {
2455 Some(EndpointEvent::RegistrationChanged(info))
2456 if registrar.is_empty()
2457 || info.registrar.as_deref() == Some(registrar.as_str()) =>
2458 {
2459 if info.status == EndpointRegistrationStatus::Registered {
2460 return coordinator
2461 .registration_info(handle)
2462 .await
2463 .map(EndpointRegistrationInfo::from);
2464 }
2465 if info.status == EndpointRegistrationStatus::Failed {
2466 return Err(SessionError::Other(format!(
2467 "registration failed for {}: {}",
2468 info.registrar.unwrap_or_default(),
2469 info.last_failure
2470 .unwrap_or_else(|| "unknown error".to_string())
2471 )));
2472 }
2473 }
2474 Some(_) => {}
2475 None => {
2476 return Err(SessionError::Other(
2477 "event stream closed while waiting for registration".to_string(),
2478 ));
2479 }
2480 }
2481 }
2482 };
2483
2484 match timeout {
2485 Some(duration) => tokio::time::timeout(duration, fut)
2486 .await
2487 .map_err(|_| SessionError::Timeout("register_and_wait timed out".to_string()))?,
2488 None => fut.await,
2489 }
2490}
2491
2492fn normalize_target(
2493 registrar: Option<&str>,
2494 target: &str,
2495 transport: EndpointTransport,
2496) -> Result<String> {
2497 let target = target.trim();
2498 if target.is_empty() {
2499 return Err(SessionError::InvalidInput(
2500 "call target must not be empty".to_string(),
2501 ));
2502 }
2503
2504 let lower = target.to_ascii_lowercase();
2505 if lower.starts_with("sip:") || lower.starts_with("sips:") || lower.starts_with("tel:") {
2506 return Ok(apply_transport_to_uri(target, transport, false));
2507 }
2508
2509 let registrar = registrar.ok_or_else(|| {
2510 SessionError::ConfigError(
2511 "bare call targets require EndpointBuilder::registrar".to_string(),
2512 )
2513 })?;
2514 let registrar = apply_transport_to_uri(registrar, transport, true);
2515 let mut registrar_uri = parse_uri(®istrar, "registrar")?;
2516
2517 if target.contains('@') {
2518 return Ok(format!("{}:{}", registrar_uri.scheme, target));
2519 }
2520
2521 registrar_uri.user = Some(target.to_string());
2522 registrar_uri.password = None;
2523 registrar_uri.headers.clear();
2524 Ok(registrar_uri.to_string())
2525}
2526
2527fn apply_transport_to_uri(
2528 uri: &str,
2529 transport: EndpointTransport,
2530 registrar_or_proxy: bool,
2531) -> String {
2532 match transport {
2533 EndpointTransport::Udp => uri.to_string(),
2534 EndpointTransport::Tcp => {
2535 if uri.contains(";transport=") {
2536 uri.to_string()
2537 } else {
2538 format!("{uri};transport=tcp")
2539 }
2540 }
2541 EndpointTransport::Tls => {
2542 let tls_uri = if uri.to_ascii_lowercase().starts_with("sip:") {
2543 format!("sips:{}", &uri[4..])
2544 } else {
2545 uri.to_string()
2546 };
2547 if registrar_or_proxy || tls_uri.contains(";transport=") {
2548 tls_uri
2549 } else {
2550 format!("{tls_uri};transport=tls")
2551 }
2552 }
2553 }
2554}
2555
2556fn parse_media_public_address(value: &str) -> Result<SocketAddr> {
2557 if let Ok(addr) = value.parse::<SocketAddr>() {
2558 return Ok(addr);
2559 }
2560 let ip = value.parse::<IpAddr>().map_err(|err| {
2561 SessionError::InvalidInput(format!("invalid media public address '{value}': {err}"))
2562 })?;
2563 Ok(SocketAddr::new(ip, 0))
2564}
2565
2566fn account_aor_uri(registrar: &str, username: &str) -> Result<String> {
2567 let mut uri = parse_uri(registrar, "registrar")?;
2568 uri.user = Some(username.to_string());
2569 uri.password = None;
2570 uri.port = None;
2571 uri.parameters.clear();
2572 uri.headers.clear();
2573 Ok(uri.to_string())
2574}
2575
2576fn parse_uri(value: &str, label: &str) -> Result<Uri> {
2577 let uri = Uri::from_str(value).map_err(|err| {
2578 SessionError::InvalidInput(format!("invalid {label} URI '{value}': {err}"))
2579 })?;
2580 match uri.scheme {
2581 Scheme::Sip | Scheme::Sips => Ok(uri),
2582 _ => Err(SessionError::InvalidInput(format!(
2583 "{label} URI must use sip: or sips:"
2584 ))),
2585 }
2586}
2587
2588#[cfg(test)]
2589mod tests {
2590 use super::*;
2591 use crate::api::unified::{SipContactMode, SipTlsMode};
2592
2593 #[test]
2594 fn endpoint_builder_maps_asterisk_tls_profile() {
2595 let parts = Endpoint::builder()
2596 .name("alice")
2597 .account("1001")
2598 .password("secret")
2599 .registrar("sips:pbx.example.test:5061;transport=tls")
2600 .profile(EndpointProfile::AsteriskTlsSrtpRegisteredFlow)
2601 .sip_instance("urn:uuid:00000000-0000-0000-0000-000000000001")
2602 .build_parts()
2603 .unwrap();
2604
2605 assert_eq!(parts.config.sip_tls_mode, SipTlsMode::ClientOnly);
2606 assert_eq!(
2607 parts.config.sip_contact_mode,
2608 SipContactMode::RegisteredFlowSymmetric
2609 );
2610 assert!(parts.config.offer_srtp);
2611 assert!(parts.config.srtp_required);
2612 assert_eq!(parts.config.local_uri, "sips:1001@pbx.example.test");
2613 assert!(parts.registration.is_some());
2614 }
2615
2616 #[test]
2617 fn endpoint_builder_creates_registration_defaults() {
2618 let parts = Endpoint::builder()
2619 .account("1001")
2620 .auth_username("auth1001")
2621 .password("secret")
2622 .registrar("sip:pbx.example.test")
2623 .contact_uri("sip:1001@192.0.2.10:5060")
2624 .expires(600)
2625 .build_parts()
2626 .unwrap();
2627
2628 let registration = parts.registration.unwrap();
2629 assert_eq!(registration.registrar, "sip:pbx.example.test");
2630 assert_eq!(registration.username, "auth1001");
2631 assert_eq!(registration.password, "secret");
2632 assert_eq!(registration.expires, 600);
2633 assert_eq!(
2634 registration.from_uri.as_deref(),
2635 Some("sip:1001@pbx.example.test")
2636 );
2637 assert_eq!(
2638 registration.contact_uri.as_deref(),
2639 Some("sip:1001@192.0.2.10:5060")
2640 );
2641 }
2642
2643 #[test]
2644 fn sip_account_derives_compatible_registration_endpoint_account_and_credentials() {
2645 let account = SipAccount::new("sip:pbx.example.test", "1001", "secret")
2646 .auth_username("auth1001")
2647 .expires(600)
2648 .from_uri("sip:1001@pbx.example.test")
2649 .contact_uri("sip:1001@192.0.2.10:5060");
2650
2651 let credentials = account.credentials();
2652 assert_eq!(credentials.username, "auth1001");
2653 assert_eq!(credentials.password, "secret");
2654
2655 let registration = account.registration();
2656 assert_eq!(registration.registrar, "sip:pbx.example.test");
2657 assert_eq!(registration.username, "auth1001");
2658 assert_eq!(registration.password, "secret");
2659 assert_eq!(registration.expires, 600);
2660 assert_eq!(
2661 registration.from_uri.as_deref(),
2662 Some("sip:1001@pbx.example.test")
2663 );
2664 assert_eq!(
2665 registration.contact_uri.as_deref(),
2666 Some("sip:1001@192.0.2.10:5060")
2667 );
2668
2669 let endpoint_account = account.endpoint_account();
2670 assert_eq!(endpoint_account.username, "1001");
2671 assert_eq!(endpoint_account.auth_username.as_deref(), Some("auth1001"));
2672
2673 let legacy: EndpointAccount = account.clone().into();
2674 let round_trip: SipAccount = legacy.into();
2675 assert_eq!(round_trip.effective_auth_username(), "auth1001");
2676 assert_eq!(round_trip.username, "1001");
2677 }
2678
2679 #[test]
2680 fn endpoint_normalizes_bare_extension_through_registrar() {
2681 let target = normalize_target(
2682 Some("sips:pbx.example.test:5061;transport=tls"),
2683 "1002",
2684 EndpointTransport::Udp,
2685 )
2686 .unwrap();
2687 assert_eq!(target, "sips:1002@pbx.example.test:5061;transport=tls");
2688 }
2689
2690 #[test]
2691 fn endpoint_leaves_full_sip_uri_unchanged() {
2692 let target = normalize_target(
2693 Some("sips:pbx.example.test:5061"),
2694 "sip:bob@example.test",
2695 EndpointTransport::Udp,
2696 )
2697 .unwrap();
2698 assert_eq!(target, "sip:bob@example.test");
2699 }
2700
2701 #[test]
2702 fn endpoint_requires_registrar_for_bare_target() {
2703 let err = normalize_target(None, "1002", EndpointTransport::Udp).unwrap_err();
2704 assert!(err.to_string().contains("registrar"));
2705 }
2706
2707 #[test]
2708 fn endpoint_transport_rewrites_tls_target() {
2709 let target = normalize_target(
2710 Some("sip:pbx.example.test:5060"),
2711 "1002",
2712 EndpointTransport::Tls,
2713 )
2714 .unwrap();
2715 assert_eq!(target, "sips:1002@pbx.example.test:5060");
2716 }
2717
2718 #[test]
2719 fn endpoint_json_config_maps_builder_fields() {
2720 let config = serde_json::from_str::<EndpointConfig>(
2721 r#"{
2722 "name": "alice",
2723 "profile": "asterisk-udp",
2724 "auto180Ringing": false,
2725 "auto100Trying": false,
2726 "fastAutoAcceptIncomingCalls": true,
2727 "cleanupDiagnostics": true,
2728 "cleanupDiagnosticEvents": true,
2729 "appEventChannelCapacity": 512,
2730 "srtpDiagnostics": true,
2731 "rtpDiagnostics": true,
2732 "mediaSdpDiagnostics": true,
2733 "account": {
2734 "username": "1001",
2735 "password": "secret",
2736 "registrar": "sip:pbx.example.test"
2737 },
2738 "network": {
2739 "bind": "127.0.0.1:5060",
2740 "transport": "tcp",
2741 "stun": "stun.example.test:3478",
2742 "udpParseWorkers": 4,
2743 "udpParseQueueCapacity": 8192
2744 },
2745 "media": {
2746 "publicAddress": "192.0.2.10",
2747 "enabled": false,
2748 "signalingOnlyRtpPort": 9,
2749 "srtp": "offer"
2750 }
2751 }"#,
2752 )
2753 .unwrap();
2754
2755 let parts = EndpointBuilder::from_config(config)
2756 .unwrap()
2757 .build_parts()
2758 .unwrap();
2759 assert_eq!(parts.transport, EndpointTransport::Tcp);
2760 assert_eq!(
2761 parts.config.stun_server.as_deref(),
2762 Some("stun.example.test:3478")
2763 );
2764 assert!(parts.config.offer_srtp);
2765 assert!(!parts.config.srtp_required);
2766 assert!(!parts.config.auto_180_ringing);
2767 assert!(!parts.config.auto_100_trying);
2768 assert!(parts.config.fast_auto_accept_incoming_calls);
2769 assert!(parts.config.cleanup_diagnostics);
2770 assert!(parts.config.cleanup_diagnostic_events);
2771 assert_eq!(parts.config.global_event_channel_capacity, 512);
2772 assert_eq!(parts.config.session_event_dispatcher_channel_capacity, 512);
2773 assert!(parts.config.srtp_diagnostics);
2774 assert!(parts.config.rtp_diagnostics);
2775 assert!(parts.config.media_sdp_diagnostics);
2776 assert_eq!(parts.config.sip_udp_parse_workers, Some(4));
2777 assert_eq!(parts.config.sip_udp_parse_queue_capacity, Some(8192));
2778 assert_eq!(
2779 parts.config.media_mode,
2780 MediaMode::SignalingOnly { sdp_rtp_port: 9 }
2781 );
2782 assert_eq!(
2783 parts.config.media_public_addr,
2784 Some("192.0.2.10:0".parse().unwrap())
2785 );
2786 assert_eq!(
2787 parts.registrar.as_deref(),
2788 Some("sip:pbx.example.test;transport=tcp")
2789 );
2790 }
2791
2792 #[test]
2793 fn endpoint_json_performance_profile_maps_into_config() {
2794 let config = serde_json::from_str::<EndpointConfig>(
2795 r#"{
2796 "name": "perf",
2797 "performance": {
2798 "profile": "pbx-media-server",
2799 "capacity": 2000
2800 },
2801 "network": {
2802 "udpParseWorkers": 2
2803 },
2804 "sipTransactionCommandChannelCapacity": 256,
2805 "serverCallAdmissionLimit": 3000,
2806 "serverCallAdmissionSoftLimit": 2500,
2807 "serverCallAdmissionPacingDelayMs": 3,
2808 "serverOverloadRetryAfterSecs": 2
2809 }"#,
2810 )
2811 .unwrap();
2812
2813 let parts = EndpointBuilder::from_config(config)
2814 .unwrap()
2815 .build_parts()
2816 .unwrap();
2817 assert!(parts.config.fast_auto_accept_incoming_calls);
2818 assert_eq!(parts.config.media_mode, MediaMode::Enabled);
2819 assert_eq!(parts.config.media_port_start, 16_384);
2820 assert_eq!(parts.config.media_port_capacity, Some(49_152));
2821 assert_eq!(parts.config.media_session_capacity, Some(2_000));
2822 assert_eq!(parts.config.sip_udp_parse_workers, Some(2));
2823 assert_eq!(
2824 parts.config.sip_udp_parse_dispatch,
2825 Some(rvoip_sip_transport::UdpParseDispatch::RoundRobin)
2826 );
2827 assert_eq!(
2828 parts.config.sip_transaction_command_channel_capacity,
2829 Some(256)
2830 );
2831 assert_eq!(parts.config.server_call_capacity, Some(2_000));
2832 assert_eq!(parts.config.server_call_admission_limit, Some(3_000));
2833 assert_eq!(parts.config.server_call_admission_soft_limit, Some(2_500));
2834 assert_eq!(parts.config.server_call_admission_pacing_delay_ms, Some(3));
2835 assert_eq!(parts.config.server_overload_retry_after_secs, Some(2));
2836 }
2837
2838 #[test]
2839 fn endpoint_json_endpoint_performance_recipe_is_default_shape() {
2840 let config = serde_json::from_str::<EndpointConfig>(
2841 r#"{
2842 "name": "softphone",
2843 "performance": {
2844 "profile": "endpoint"
2845 }
2846 }"#,
2847 )
2848 .unwrap();
2849
2850 let parts = EndpointBuilder::from_config(config)
2851 .unwrap()
2852 .build_parts()
2853 .unwrap();
2854 assert!(parts.config.auto_180_ringing);
2855 assert!(parts.config.auto_100_trying);
2856 assert!(!parts.config.fast_auto_accept_incoming_calls);
2857 assert_eq!(parts.config.media_mode, MediaMode::Enabled);
2858 assert_eq!(parts.config.sip_udp_parse_workers, None);
2859 assert_eq!(parts.config.sip_transaction_command_channel_capacity, None);
2860 }
2861
2862 #[test]
2863 fn endpoint_json_signaling_only_performance_profile_maps_into_config() {
2864 let config = serde_json::from_str::<EndpointConfig>(
2865 r#"{
2866 "name": "perf",
2867 "performance": {
2868 "profile": "signaling-only-server-high-performance",
2869 "capacity": 2000,
2870 "signalingOnlyRtpPort": 4000
2871 }
2872 }"#,
2873 )
2874 .unwrap();
2875
2876 let parts = EndpointBuilder::from_config(config)
2877 .unwrap()
2878 .build_parts()
2879 .unwrap();
2880 assert_eq!(
2881 parts.config.media_mode,
2882 MediaMode::SignalingOnly { sdp_rtp_port: 4000 }
2883 );
2884 assert_eq!(parts.config.sip_udp_parse_workers, Some(4));
2885 assert_eq!(
2886 parts.config.sip_transaction_command_channel_capacity,
2887 Some(128)
2888 );
2889 assert_eq!(parts.config.server_call_capacity, Some(2_000));
2890 assert_eq!(parts.config.server_call_admission_limit, Some(2_000));
2891 assert_eq!(parts.config.server_call_admission_soft_limit, Some(1_800));
2892 assert_eq!(parts.config.server_call_admission_pacing_delay_ms, Some(1));
2893 }
2894
2895 #[test]
2896 fn endpoint_json_config_accepts_partial_sip_trace_config() {
2897 let config = serde_json::from_str::<EndpointConfig>(
2898 r#"{
2899 "sipTrace": {
2900 "enabled": true,
2901 "redactSensitiveHeaders": false
2902 }
2903 }"#,
2904 )
2905 .unwrap();
2906
2907 let trace = config.sip_trace.unwrap();
2908 assert!(trace.enabled);
2909 assert_eq!(
2910 trace.capacity,
2911 crate::api::events::SipTraceConfig::DEFAULT_CAPACITY
2912 );
2913 assert!(!trace.redact_sensitive_headers);
2914 assert!(trace.include_body);
2915 }
2916
2917 #[test]
2918 fn sip_client_example_stays_on_endpoint_surface() {
2919 let source = [
2920 include_str!("../../examples/sip_client/main.rs"),
2921 include_str!("../../examples/sip_client/audio.rs"),
2922 include_str!("../../examples/sip_client/config.rs"),
2923 include_str!("../../examples/sip_client/runtime.rs"),
2924 include_str!("../../examples/sip_client/smoke.rs"),
2925 include_str!("../../examples/sip_client/ui.rs"),
2926 ]
2927 .join("\n");
2928 for banned in [
2929 "StreamPeer",
2930 "PeerControl",
2931 "UnifiedCoordinator",
2932 "RegistrationHandle",
2933 "SessionHandle",
2934 "SipTlsMode",
2935 "rvoip_media_core",
2936 ] {
2937 assert!(
2938 !source.contains(banned),
2939 "sip_client example must not reference lower-level API {banned}"
2940 );
2941 }
2942 }
2943
2944 #[test]
2945 fn endpoint_audio_roundtrip_stays_on_endpoint_surface() {
2946 let source = include_str!("../../examples/endpoint/04_audio_roundtrip/main.rs");
2947 for banned in [
2948 "StreamPeer",
2949 "PeerControl",
2950 "UnifiedCoordinator",
2951 "RegistrationHandle",
2952 "SessionHandle",
2953 "as_session_handle",
2954 "rvoip_media_core",
2955 ] {
2956 assert!(
2957 !source.contains(banned),
2958 "endpoint audio roundtrip example must not reference lower-level API {banned}"
2959 );
2960 }
2961 }
2962}