Skip to main content

rvoip_sip/api/
callback_peer.rs

1//! Trait-based reactive peer API for servers, IVR, and routing apps.
2//!
3//! Implement [`CallHandler`] and pass it to [`CallbackPeer::new()`]. Call
4//! [`run()`][CallbackPeer::run] to start the event loop; it returns when the peer
5//! is shut down.
6//!
7//! `CallbackPeer` is built for applications where the library should drive the
8//! event loop and call user code when something happens: incoming calls,
9//! established calls, DTMF, hold/resume, transfer, NOTIFY, registration, and
10//! auth retry notifications. The peer also exposes [`CallbackPeerControl`] so
11//! supervisors and handler-owned tasks can place outbound calls, register,
12//! inspect registration metadata through the coordinator, and shut down the
13//! running peer. Outbound calls flow through `control.invite(to)` and chain
14//! `.with_extra_headers(...)` to attach caller-supplied typed headers to the
15//! very first INVITE for PBX/SBC integrations that require non-standard or
16//! vendor headers.
17//!
18//! # Use cases
19//!
20//! - **Proxy server**: `on_incoming_call` makes a fast routing decision and returns
21//!   `Accept`, `Reject`, or `Redirect`.
22//! - **IVR / call center**: `on_incoming_call` returns `Defer`, storing the
23//!   [`IncomingCallGuard`] in a queue until an agent is available. Resolving
24//!   the guard cancels the deferred-call watchdog.
25//! - **B2BUA leg**: `on_call_established` bridges the accepted session to a second
26//!   outgoing leg managed in the higher-layer b2bua crate.
27//!
28//! # Minimal server
29//!
30//! ```rust,no_run
31//! use async_trait::async_trait;
32//! use rvoip_sip::{
33//!     CallHandler, CallHandlerDecision, CallbackPeer, Config, IncomingCall, Result,
34//! };
35//!
36//! struct Server;
37//!
38//! #[async_trait]
39//! impl CallHandler for Server {
40//!     async fn on_incoming_call(&self, _call: IncomingCall) -> CallHandlerDecision {
41//!         CallHandlerDecision::Accept
42//!     }
43//! }
44//!
45//! # async fn example() -> Result<()> {
46//! let peer = CallbackPeer::new(Server, Config::default()).await?;
47//! peer.run().await?;
48//! # Ok(())
49//! # }
50//! ```
51
52#![deny(missing_docs)]
53
54use async_trait::async_trait;
55use std::collections::{HashMap, HashSet, VecDeque};
56use std::future::Future;
57use std::pin::Pin;
58use std::sync::Arc;
59use std::time::Duration;
60
61use crate::api::dialog_package::{DialogInfo, DialogInfoDocument};
62use crate::api::endpoint::SipAccount;
63use crate::api::events::{
64    Event, MediaSecurityState, SipTrace, SubscriptionState, TransferTargetEvidence,
65};
66use crate::api::handle::{CallId, SessionHandle};
67use crate::api::incoming::{IncomingCall, IncomingCallGuard};
68use crate::api::performance::PerformanceConfig;
69use crate::api::unified::{
70    Config, MediaMode, MediaSessionControllerConfig, RegistrationHandle, RtpSessionBufferConfig,
71    RtpTransportBufferConfig, UnifiedCoordinator,
72};
73use crate::auth::SipClientAuth;
74use crate::cleanup_diag::{self, CleanupStage};
75use crate::errors::{Result, SessionError};
76
77// ===== ShutdownHandle =====
78
79/// Cloneable handle for stopping a [`CallbackPeer`] from another task.
80///
81/// Obtained via [`CallbackPeer::shutdown_handle()`] **before** calling
82/// [`run()`](CallbackPeer::run).
83#[derive(Clone)]
84pub struct ShutdownHandle {
85    tx: tokio::sync::watch::Sender<bool>,
86}
87
88impl ShutdownHandle {
89    /// Signal the peer to stop its event loop.
90    ///
91    /// # Examples
92    ///
93    /// ```rust,no_run
94    /// # async fn example(peer: rvoip_sip::CallbackPeer<rvoip_sip::AutoAnswerHandler>) {
95    /// let stop = peer.shutdown_handle();
96    /// stop.shutdown();
97    /// # }
98    /// ```
99    pub fn shutdown(&self) {
100        let _ = self.tx.send(true);
101    }
102
103    /// Internal constructor for peers that want to mint a shutdown handle
104    /// from an existing watch channel. Scoped to the crate so external
105    /// callers go through [`CallbackPeer::shutdown_handle`] /
106    /// [`StreamPeer::shutdown_handle`].
107    pub(crate) fn from_sender(tx: tokio::sync::watch::Sender<bool>) -> Self {
108        Self { tx }
109    }
110}
111
112// ===== CallHandlerDecision =====
113
114/// How the [`CallHandler`] wants to resolve an incoming call.
115pub enum CallHandlerDecision {
116    /// Accept immediately, using auto-negotiated SDP.
117    Accept,
118    /// Accept with a custom SDP answer.
119    AcceptWithSdp(String),
120    /// Reject immediately with a SIP status code and reason phrase.
121    Reject {
122        /// SIP final response status code, usually in the 4xx, 5xx, or 6xx range.
123        status: u16,
124        /// SIP reason phrase to send with the final response.
125        reason: String,
126    },
127    /// Redirect the caller to another URI by sending SIP 302 with a Contact header.
128    Redirect(String),
129    /// Hold the call in `Ringing` state; the framework waits for the guard to resolve.
130    Defer(IncomingCallGuard),
131}
132
133// ===== EndReason =====
134
135/// Why a call ended.
136#[derive(Debug, Clone)]
137pub enum EndReason {
138    /// Clean BYE exchange.
139    Normal,
140    /// Remote party rejected or the call was never answered.
141    Rejected,
142    /// No response within the configured timeout.
143    Timeout,
144    /// Transport or protocol error.
145    NetworkError,
146    /// Other reason (check the string for details).
147    Other(String),
148}
149
150impl From<String> for EndReason {
151    fn from(s: String) -> Self {
152        match s.to_lowercase().as_str() {
153            r if r.contains("timeout") => EndReason::Timeout,
154            r if r.contains("reject") || r.contains("decline") || r.contains("busy") => {
155                EndReason::Rejected
156            }
157            r if r.contains("network") || r.contains("transport") => EndReason::NetworkError,
158            _ => EndReason::Other(s),
159        }
160    }
161}
162
163type CallbackFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
164type EventHook = Arc<dyn Fn(Event) -> CallbackFuture<Result<()>> + Send + Sync>;
165type IncomingHook = Arc<dyn Fn(IncomingCall) -> CallbackFuture<CallHandlerDecision> + Send + Sync>;
166type EstablishedHook = Arc<dyn Fn(SessionHandle) -> CallbackFuture<Result<()>> + Send + Sync>;
167type ProgressHook = Arc<
168    dyn Fn(SessionHandle, u16, String, Option<String>) -> CallbackFuture<Result<()>> + Send + Sync,
169>;
170type DtmfHook = Arc<dyn Fn(SessionHandle, char) -> CallbackFuture<Result<()>> + Send + Sync>;
171type EndedHook = Arc<dyn Fn(CallId, EndReason) -> CallbackFuture<Result<()>> + Send + Sync>;
172type FailedHook = Arc<dyn Fn(CallId, u16, String) -> CallbackFuture<Result<()>> + Send + Sync>;
173type CancelledHook = Arc<dyn Fn(CallId) -> CallbackFuture<Result<()>> + Send + Sync>;
174type MediaSecurityHook =
175    Arc<dyn Fn(SessionHandle, MediaSecurityState) -> CallbackFuture<Result<()>> + Send + Sync>;
176type HoldHook = Arc<dyn Fn(SessionHandle) -> CallbackFuture<Result<()>> + Send + Sync>;
177type ReferReceivedHook = Arc<
178    dyn Fn(SessionHandle, crate::api::incoming::IncomingRequest) -> CallbackFuture<Result<bool>>
179        + Send
180        + Sync,
181>;
182type TransferAcceptedHook =
183    Arc<dyn Fn(SessionHandle, String) -> CallbackFuture<Result<()>> + Send + Sync>;
184type ReferProgressHook =
185    Arc<dyn Fn(SessionHandle, u16, String) -> CallbackFuture<Result<()>> + Send + Sync>;
186type ReferCompletedHook =
187    Arc<dyn Fn(SessionHandle, String, u16, String) -> CallbackFuture<Result<()>> + Send + Sync>;
188type TransferFailedHook =
189    Arc<dyn Fn(SessionHandle, u16, String) -> CallbackFuture<Result<()>> + Send + Sync>;
190type RegistrationSuccessHook =
191    Arc<dyn Fn(String, u32, String) -> CallbackFuture<Result<()>> + Send + Sync>;
192type RegistrationFailedHook =
193    Arc<dyn Fn(String, u16, String) -> CallbackFuture<Result<()>> + Send + Sync>;
194type UnregistrationSuccessHook = Arc<dyn Fn(String) -> CallbackFuture<Result<()>> + Send + Sync>;
195type UnregistrationFailedHook =
196    Arc<dyn Fn(String, String) -> CallbackFuture<Result<()>> + Send + Sync>;
197type SipTraceHook = Arc<dyn Fn(SipTrace) -> CallbackFuture<Result<()>> + Send + Sync>;
198
199/// Builder for closure-based [`CallbackPeer`] applications.
200///
201/// Use this when a full [`CallHandler`] implementation would be noisy but the
202/// application still wants typed hooks for common lifecycle events.
203pub struct CallbackPeerBuilder {
204    config: Config,
205    event: Option<EventHook>,
206    incoming: Option<IncomingHook>,
207    established: Option<EstablishedHook>,
208    progress: Option<ProgressHook>,
209    dtmf: Option<DtmfHook>,
210    ended: Option<EndedHook>,
211    failed: Option<FailedHook>,
212    cancelled: Option<CancelledHook>,
213    media_security: Option<MediaSecurityHook>,
214    local_hold: Option<HoldHook>,
215    local_resume: Option<HoldHook>,
216    remote_hold: Option<HoldHook>,
217    remote_resume: Option<HoldHook>,
218    refer_received: Option<ReferReceivedHook>,
219    transfer_accepted: Option<TransferAcceptedHook>,
220    refer_progress: Option<ReferProgressHook>,
221    refer_completed: Option<ReferCompletedHook>,
222    transfer_failed: Option<TransferFailedHook>,
223    registration_success: Option<RegistrationSuccessHook>,
224    registration_failed: Option<RegistrationFailedHook>,
225    unregistration_success: Option<UnregistrationSuccessHook>,
226    unregistration_failed: Option<UnregistrationFailedHook>,
227    sip_trace: Option<SipTraceHook>,
228}
229
230impl CallbackPeerBuilder {
231    /// Create a closure builder for the supplied config.
232    pub fn new(config: Config) -> Self {
233        Self {
234            config,
235            event: None,
236            incoming: None,
237            established: None,
238            progress: None,
239            dtmf: None,
240            ended: None,
241            failed: None,
242            cancelled: None,
243            media_security: None,
244            local_hold: None,
245            local_resume: None,
246            remote_hold: None,
247            remote_resume: None,
248            refer_received: None,
249            transfer_accepted: None,
250            refer_progress: None,
251            refer_completed: None,
252            transfer_failed: None,
253            registration_success: None,
254            registration_failed: None,
255            unregistration_success: None,
256            unregistration_failed: None,
257            sip_trace: None,
258        }
259    }
260
261    /// Set peer-level UAC SIP auth used for outbound 401/407 retry.
262    ///
263    /// Use [`SipClientAuth::any`] when the peer may offer multiple schemes and
264    /// the UAC should negotiate among Digest, Bearer, Basic, and AKA options.
265    pub fn with_auth(mut self, auth: SipClientAuth) -> Self {
266        self.config.auth = Some(auth);
267        self
268    }
269
270    /// Set peer-level Digest credentials used for UAC outbound 401/407 retry.
271    ///
272    /// This is the Digest shorthand. Use [`Self::with_auth`] for Bearer,
273    /// Basic, AKA, or multi-challenge negotiation.
274    pub fn with_credentials(
275        mut self,
276        username: impl Into<String>,
277        password: impl Into<String>,
278    ) -> Self {
279        self.config.credentials = Some(crate::types::Credentials::new(username, password));
280        self
281    }
282
283    /// Set peer-level Bearer auth used for UAC outbound 401/407 retry.
284    pub fn with_bearer_token(mut self, token: impl Into<String>) -> Self {
285        self.config.auth = Some(SipClientAuth::bearer_token(token));
286        self
287    }
288
289    /// Set peer-level Basic auth used for UAC outbound 401/407 retry.
290    ///
291    /// Basic remains cleartext-disabled unless the auth value explicitly opts
292    /// in via [`SipClientAuth::allow_basic_over_cleartext`].
293    pub fn with_basic_credentials(
294        mut self,
295        username: impl Into<String>,
296        password: impl Into<String>,
297    ) -> Self {
298        self.config.auth = Some(SipClientAuth::basic(username, password));
299        self
300    }
301
302    /// Handle every public event before more specific typed hooks run.
303    pub fn on_event<F, Fut>(mut self, f: F) -> Self
304    where
305        F: Fn(Event) -> Fut + Send + Sync + 'static,
306        Fut: Future<Output = Result<()>> + Send + 'static,
307    {
308        self.event = Some(Arc::new(move |event| Box::pin(f(event))));
309        self
310    }
311
312    /// Enable or disable automatic `180 Ringing` on inbound INVITEs.
313    pub fn auto_180_ringing(mut self, enabled: bool) -> Self {
314        self.config = self.config.with_auto_180_ringing(enabled);
315        self
316    }
317
318    /// Enable or disable automatic `100 Trying` timer tasks on inbound INVITEs.
319    pub fn auto_100_trying(mut self, enabled: bool) -> Self {
320        self.config = self.config.with_auto_100_trying(enabled);
321        self
322    }
323
324    /// Enable or disable immediate session-path accept for inbound INVITEs.
325    pub fn fast_auto_accept_incoming_calls(mut self, enabled: bool) -> Self {
326        self.config = self.config.with_fast_auto_accept_incoming_calls(enabled);
327        self
328    }
329
330    /// Enable or disable real media-core RTP allocation.
331    pub fn media_enabled(mut self, enabled: bool) -> Self {
332        self.config = self.config.with_media_enabled(enabled);
333        self
334    }
335
336    /// Skip media-core RTP allocation while still generating SDP.
337    pub fn signaling_only_media(mut self, sdp_rtp_port: u16) -> Self {
338        self.config = self
339            .config
340            .with_media_mode(MediaMode::SignalingOnly { sdp_rtp_port });
341        self
342    }
343
344    /// Set the RTP media port range by start port and requested capacity.
345    pub fn media_port_capacity(mut self, start: u16, capacity: usize) -> Self {
346        self.config = self.config.with_media_port_capacity(start, capacity);
347        self
348    }
349
350    /// Set the media-core session and RTP allocator capacity hint.
351    pub fn media_session_capacity(mut self, capacity: usize) -> Self {
352        self.config = self.config.with_media_session_capacity(capacity);
353        self
354    }
355
356    /// Set RTP session queue sizing for SIP media calls.
357    pub fn rtp_session_buffer_config(mut self, config: RtpSessionBufferConfig) -> Self {
358        self.config = self.config.with_rtp_session_buffer_config(config);
359        self
360    }
361
362    /// Set RTP transport event and receive buffer sizing for SIP media calls.
363    pub fn rtp_transport_buffer_config(mut self, config: RtpTransportBufferConfig) -> Self {
364        self.config = self.config.with_rtp_transport_buffer_config(config);
365        self
366    }
367
368    /// Set media-core controller pool and capacity tuning for SIP media calls.
369    pub fn media_session_controller_config(mut self, config: MediaSessionControllerConfig) -> Self {
370        self.config = self.config.with_media_session_controller_config(config);
371        self
372    }
373
374    /// Apply the high-CPS UDP auto-answer profile.
375    pub fn high_cps_udp_auto_answer(mut self, capacity: usize) -> Self {
376        self.config = self.config.with_high_cps_udp_auto_answer(capacity);
377        self
378    }
379
380    /// Apply a YAML-backed performance recipe.
381    pub fn performance_config(mut self, performance: PerformanceConfig) -> Result<Self> {
382        self.config = self.config.try_with_performance_config(performance)?;
383        Ok(self)
384    }
385
386    /// Apply the PBX media server performance recipe.
387    pub fn pbx_media_server_performance(mut self, capacity: usize) -> Self {
388        self.config = self.config.with_pbx_media_server_performance(capacity);
389        self
390    }
391
392    /// Apply the signaling-only high-performance server recipe.
393    pub fn signaling_only_server_high_performance(
394        mut self,
395        capacity: usize,
396        sdp_rtp_port: u16,
397    ) -> Self {
398        self.config = self
399            .config
400            .with_signaling_only_server_high_performance(capacity, sdp_rtp_port);
401        self
402    }
403
404    /// Set app-facing event buffer capacity.
405    pub fn app_event_channel_capacity(mut self, capacity: usize) -> Self {
406        self.config = self.config.with_app_event_channel_capacity(capacity);
407        self
408    }
409
410    /// Set the UDP parse worker count.
411    pub fn sip_udp_parse_workers(mut self, workers: usize) -> Self {
412        self.config = self.config.with_sip_udp_parse_workers(workers);
413        self
414    }
415
416    /// Set the per-worker UDP parse queue capacity.
417    pub fn sip_udp_parse_queue_capacity(mut self, capacity: usize) -> Self {
418        self.config = self.config.with_sip_udp_parse_queue_capacity(capacity);
419        self
420    }
421
422    /// Set the per-transaction command channel capacity.
423    pub fn sip_transaction_command_channel_capacity(mut self, capacity: usize) -> Self {
424        self.config = self
425            .config
426            .with_sip_transaction_command_channel_capacity(capacity);
427        self
428    }
429
430    /// Set the server-side inbound call admission limit.
431    pub fn server_call_admission_limit(mut self, limit: usize) -> Self {
432        self.config = self.config.with_server_call_admission_limit(limit);
433        self
434    }
435
436    /// Set the soft threshold where server-side admission starts pacing.
437    pub fn server_call_admission_soft_limit(mut self, limit: usize) -> Self {
438        self.config = self.config.with_server_call_admission_soft_limit(limit);
439        self
440    }
441
442    /// Set the delay in milliseconds while above the soft admission threshold.
443    pub fn server_call_admission_pacing_delay_ms(mut self, delay_ms: u64) -> Self {
444        self.config = self
445            .config
446            .with_server_call_admission_pacing_delay_ms(delay_ms);
447        self
448    }
449
450    /// Set the `Retry-After` value used for server overload rejections.
451    pub fn server_overload_retry_after_secs(mut self, seconds: u32) -> Self {
452        self.config = self.config.with_server_overload_retry_after_secs(seconds);
453        self
454    }
455
456    /// Enable or disable SIP UDP transport and duplicate-recovery diagnostics.
457    pub fn sip_udp_diagnostics(mut self, enabled: bool) -> Self {
458        self.config = self.config.with_sip_udp_diagnostics(enabled);
459        self
460    }
461
462    /// Enable or disable media setup/teardown timing diagnostics.
463    pub fn media_setup_diagnostics(mut self, enabled: bool) -> Self {
464        self.config = self.config.with_media_setup_diagnostics(enabled);
465        self
466    }
467
468    /// Enable or disable cleanup-stage timing diagnostics.
469    pub fn cleanup_diagnostics(mut self, enabled: bool) -> Self {
470        self.config = self.config.with_cleanup_diagnostics(enabled);
471        self
472    }
473
474    /// Enable or disable per-operation cleanup diagnostic event logs.
475    pub fn cleanup_diagnostic_events(mut self, enabled: bool) -> Self {
476        self.config = self.config.with_cleanup_diagnostic_events(enabled);
477        self
478    }
479
480    /// Set the RSS growth threshold used by perf soak release gates.
481    #[cfg(feature = "perf-tests")]
482    pub fn perf_max_rss_growth_mb_per_hr(mut self, limit: f64) -> Self {
483        self.config = self.config.with_perf_max_rss_growth_mb_per_hr(limit);
484        self
485    }
486
487    /// Enable or disable SRTP negotiation diagnostic log lines.
488    pub fn srtp_diagnostics(mut self, enabled: bool) -> Self {
489        self.config = self.config.with_srtp_diagnostics(enabled);
490        self
491    }
492
493    /// Enable or disable RTP packet diagnostic log lines.
494    pub fn rtp_diagnostics(mut self, enabled: bool) -> Self {
495        self.config = self.config.with_rtp_diagnostics(enabled);
496        self
497    }
498
499    /// Enable or disable SDP media diagnostic log lines.
500    pub fn media_sdp_diagnostics(mut self, enabled: bool) -> Self {
501        self.config = self.config.with_media_sdp_diagnostics(enabled);
502        self
503    }
504
505    /// Handle incoming calls.
506    ///
507    /// This hook is required because every inbound INVITE needs an explicit
508    /// accept, reject, redirect, or defer decision.
509    pub fn on_incoming<F, Fut>(mut self, f: F) -> Self
510    where
511        F: Fn(IncomingCall) -> Fut + Send + Sync + 'static,
512        Fut: Future<Output = CallHandlerDecision> + Send + 'static,
513    {
514        self.incoming = Some(Arc::new(move |call| Box::pin(f(call))));
515        self
516    }
517
518    /// Handle established calls.
519    pub fn on_established<F, Fut>(mut self, f: F) -> Self
520    where
521        F: Fn(SessionHandle) -> Fut + Send + Sync + 'static,
522        Fut: Future<Output = Result<()>> + Send + 'static,
523    {
524        self.established = Some(Arc::new(move |handle| Box::pin(f(handle))));
525        self
526    }
527
528    /// Handle provisional call progress such as 180 Ringing or 183 Session Progress.
529    pub fn on_progress<F, Fut>(mut self, f: F) -> Self
530    where
531        F: Fn(SessionHandle, u16, String, Option<String>) -> Fut + Send + Sync + 'static,
532        Fut: Future<Output = Result<()>> + Send + 'static,
533    {
534        self.progress = Some(Arc::new(move |handle, status, reason, sdp| {
535            Box::pin(f(handle, status, reason, sdp))
536        }));
537        self
538    }
539
540    /// Handle inbound DTMF digits on active calls.
541    pub fn on_dtmf<F, Fut>(mut self, f: F) -> Self
542    where
543        F: Fn(SessionHandle, char) -> Fut + Send + Sync + 'static,
544        Fut: Future<Output = Result<()>> + Send + 'static,
545    {
546        self.dtmf = Some(Arc::new(move |handle, digit| Box::pin(f(handle, digit))));
547        self
548    }
549
550    /// Handle failed calls.
551    pub fn on_failed<F, Fut>(mut self, f: F) -> Self
552    where
553        F: Fn(CallId, u16, String) -> Fut + Send + Sync + 'static,
554        Fut: Future<Output = Result<()>> + Send + 'static,
555    {
556        self.failed = Some(Arc::new(move |call_id, status, reason| {
557            Box::pin(f(call_id, status, reason))
558        }));
559        self
560    }
561
562    /// Handle cancelled ringing calls.
563    pub fn on_cancelled<F, Fut>(mut self, f: F) -> Self
564    where
565        F: Fn(CallId) -> Fut + Send + Sync + 'static,
566        Fut: Future<Output = Result<()>> + Send + 'static,
567    {
568        self.cancelled = Some(Arc::new(move |call_id| Box::pin(f(call_id))));
569        self
570    }
571
572    /// Handle negotiated media security state.
573    pub fn on_media_security<F, Fut>(mut self, f: F) -> Self
574    where
575        F: Fn(SessionHandle, MediaSecurityState) -> Fut + Send + Sync + 'static,
576        Fut: Future<Output = Result<()>> + Send + 'static,
577    {
578        self.media_security = Some(Arc::new(move |handle, state| Box::pin(f(handle, state))));
579        self
580    }
581
582    /// Handle local hold confirmation.
583    pub fn on_local_hold<F, Fut>(mut self, f: F) -> Self
584    where
585        F: Fn(SessionHandle) -> Fut + Send + Sync + 'static,
586        Fut: Future<Output = Result<()>> + Send + 'static,
587    {
588        self.local_hold = Some(Arc::new(move |handle| Box::pin(f(handle))));
589        self
590    }
591
592    /// Handle local resume confirmation.
593    pub fn on_local_resume<F, Fut>(mut self, f: F) -> Self
594    where
595        F: Fn(SessionHandle) -> Fut + Send + Sync + 'static,
596        Fut: Future<Output = Result<()>> + Send + 'static,
597    {
598        self.local_resume = Some(Arc::new(move |handle| Box::pin(f(handle))));
599        self
600    }
601
602    /// Handle remote hold.
603    pub fn on_remote_hold<F, Fut>(mut self, f: F) -> Self
604    where
605        F: Fn(SessionHandle) -> Fut + Send + Sync + 'static,
606        Fut: Future<Output = Result<()>> + Send + 'static,
607    {
608        self.remote_hold = Some(Arc::new(move |handle| Box::pin(f(handle))));
609        self
610    }
611
612    /// Handle remote resume.
613    pub fn on_remote_resume<F, Fut>(mut self, f: F) -> Self
614    where
615        F: Fn(SessionHandle) -> Fut + Send + Sync + 'static,
616        Fut: Future<Output = Result<()>> + Send + 'static,
617    {
618        self.remote_resume = Some(Arc::new(move |handle| Box::pin(f(handle))));
619        self
620    }
621
622    /// Handle call termination.
623    pub fn on_ended<F, Fut>(mut self, f: F) -> Self
624    where
625        F: Fn(CallId, EndReason) -> Fut + Send + Sync + 'static,
626        Fut: Future<Output = Result<()>> + Send + 'static,
627    {
628        self.ended = Some(Arc::new(move |call_id, reason| {
629            Box::pin(f(call_id, reason))
630        }));
631        self
632    }
633
634    /// Decide whether to accept an inbound REFER (transfer request).
635    ///
636    /// SIP_API_DESIGN_2 Phase E. The closure receives a
637    /// [`SessionHandle`] for the original call and an
638    /// [`IncomingRequest`](crate::api::incoming::IncomingRequest) carrying
639    /// every REFER header (Referred-By, Replaces, Target-Dialog, custom
640    /// X-*). Return `Ok(true)` to send `202 Accepted` and have the
641    /// transferee follow the Refer-To URI; return `Ok(false)` (or an
642    /// `Err`) to send `603 Decline`.
643    pub fn on_refer_received<F, Fut>(mut self, f: F) -> Self
644    where
645        F: Fn(SessionHandle, crate::api::incoming::IncomingRequest) -> Fut + Send + Sync + 'static,
646        Fut: Future<Output = Result<bool>> + Send + 'static,
647    {
648        self.refer_received = Some(Arc::new(move |handle, req| Box::pin(f(handle, req))));
649        self
650    }
651
652    /// Handle an accepted outbound REFER.
653    pub fn on_transfer_accepted<F, Fut>(mut self, f: F) -> Self
654    where
655        F: Fn(SessionHandle, String) -> Fut + Send + Sync + 'static,
656        Fut: Future<Output = Result<()>> + Send + 'static,
657    {
658        self.transfer_accepted = Some(Arc::new(move |handle, refer_to| {
659            Box::pin(f(handle, refer_to))
660        }));
661        self
662    }
663
664    /// Handle provisional REFER progress.
665    pub fn on_refer_progress<F, Fut>(mut self, f: F) -> Self
666    where
667        F: Fn(SessionHandle, u16, String) -> Fut + Send + Sync + 'static,
668        Fut: Future<Output = Result<()>> + Send + 'static,
669    {
670        self.refer_progress = Some(Arc::new(move |handle, status, reason| {
671            Box::pin(f(handle, status, reason))
672        }));
673        self
674    }
675
676    /// Handle successful terminal REFER completion.
677    pub fn on_refer_completed<F, Fut>(mut self, f: F) -> Self
678    where
679        F: Fn(SessionHandle, String, u16, String) -> Fut + Send + Sync + 'static,
680        Fut: Future<Output = Result<()>> + Send + 'static,
681    {
682        self.refer_completed = Some(Arc::new(move |handle, target, status, reason| {
683            Box::pin(f(handle, target, status, reason))
684        }));
685        self
686    }
687
688    /// Handle REFER failure.
689    pub fn on_transfer_failed<F, Fut>(mut self, f: F) -> Self
690    where
691        F: Fn(SessionHandle, u16, String) -> Fut + Send + Sync + 'static,
692        Fut: Future<Output = Result<()>> + Send + 'static,
693    {
694        self.transfer_failed = Some(Arc::new(move |handle, status, reason| {
695            Box::pin(f(handle, status, reason))
696        }));
697        self
698    }
699
700    /// Handle successful registration.
701    pub fn on_registration_success<F, Fut>(mut self, f: F) -> Self
702    where
703        F: Fn(String, u32, String) -> Fut + Send + Sync + 'static,
704        Fut: Future<Output = Result<()>> + Send + 'static,
705    {
706        self.registration_success = Some(Arc::new(move |registrar, expires, contact| {
707            Box::pin(f(registrar, expires, contact))
708        }));
709        self
710    }
711
712    /// Handle failed registration.
713    pub fn on_registration_failed<F, Fut>(mut self, f: F) -> Self
714    where
715        F: Fn(String, u16, String) -> Fut + Send + Sync + 'static,
716        Fut: Future<Output = Result<()>> + Send + 'static,
717    {
718        self.registration_failed = Some(Arc::new(move |registrar, status, reason| {
719            Box::pin(f(registrar, status, reason))
720        }));
721        self
722    }
723
724    /// Handle successful unregistration.
725    pub fn on_unregistration_success<F, Fut>(mut self, f: F) -> Self
726    where
727        F: Fn(String) -> Fut + Send + Sync + 'static,
728        Fut: Future<Output = Result<()>> + Send + 'static,
729    {
730        self.unregistration_success = Some(Arc::new(move |registrar| Box::pin(f(registrar))));
731        self
732    }
733
734    /// Handle failed unregistration.
735    pub fn on_unregistration_failed<F, Fut>(mut self, f: F) -> Self
736    where
737        F: Fn(String, String) -> Fut + Send + Sync + 'static,
738        Fut: Future<Output = Result<()>> + Send + 'static,
739    {
740        self.unregistration_failed = Some(Arc::new(move |registrar, reason| {
741            Box::pin(f(registrar, reason))
742        }));
743        self
744    }
745
746    /// Handle SIP transport-boundary trace events when tracing is enabled.
747    pub fn on_sip_trace<F, Fut>(mut self, f: F) -> Self
748    where
749        F: Fn(SipTrace) -> Fut + Send + Sync + 'static,
750        Fut: Future<Output = Result<()>> + Send + 'static,
751    {
752        self.sip_trace = Some(Arc::new(move |trace| Box::pin(f(trace))));
753        self
754    }
755
756    /// Build the [`CallbackPeer`].
757    pub async fn build(self) -> Result<CallbackPeer<CallbackBuilderHandler>> {
758        let incoming = self.incoming.ok_or_else(|| {
759            SessionError::ConfigError(
760                "CallbackPeer::builder requires an on_incoming hook".to_string(),
761            )
762        })?;
763        CallbackPeer::new(
764            CallbackBuilderHandler {
765                event: self.event,
766                incoming,
767                established: self.established,
768                progress: self.progress,
769                dtmf: self.dtmf,
770                ended: self.ended,
771                failed: self.failed,
772                cancelled: self.cancelled,
773                media_security: self.media_security,
774                local_hold: self.local_hold,
775                local_resume: self.local_resume,
776                remote_hold: self.remote_hold,
777                remote_resume: self.remote_resume,
778                refer_received: self.refer_received,
779                transfer_accepted: self.transfer_accepted,
780                refer_progress: self.refer_progress,
781                refer_completed: self.refer_completed,
782                transfer_failed: self.transfer_failed,
783                registration_success: self.registration_success,
784                registration_failed: self.registration_failed,
785                unregistration_success: self.unregistration_success,
786                unregistration_failed: self.unregistration_failed,
787                sip_trace: self.sip_trace,
788            },
789            self.config,
790        )
791        .await
792    }
793}
794
795/// Internal [`CallHandler`] adapter used by [`CallbackPeerBuilder`].
796#[doc(hidden)]
797pub struct CallbackBuilderHandler {
798    event: Option<EventHook>,
799    incoming: IncomingHook,
800    established: Option<EstablishedHook>,
801    progress: Option<ProgressHook>,
802    dtmf: Option<DtmfHook>,
803    ended: Option<EndedHook>,
804    failed: Option<FailedHook>,
805    cancelled: Option<CancelledHook>,
806    media_security: Option<MediaSecurityHook>,
807    local_hold: Option<HoldHook>,
808    local_resume: Option<HoldHook>,
809    remote_hold: Option<HoldHook>,
810    remote_resume: Option<HoldHook>,
811    refer_received: Option<ReferReceivedHook>,
812    transfer_accepted: Option<TransferAcceptedHook>,
813    refer_progress: Option<ReferProgressHook>,
814    refer_completed: Option<ReferCompletedHook>,
815    transfer_failed: Option<TransferFailedHook>,
816    registration_success: Option<RegistrationSuccessHook>,
817    registration_failed: Option<RegistrationFailedHook>,
818    unregistration_success: Option<UnregistrationSuccessHook>,
819    unregistration_failed: Option<UnregistrationFailedHook>,
820    sip_trace: Option<SipTraceHook>,
821}
822
823#[async_trait]
824impl CallHandler for CallbackBuilderHandler {
825    async fn on_event(&self, event: Event) {
826        if let Some(hook) = &self.event {
827            if let Err(err) = hook(event).await {
828                tracing::warn!("[CallbackPeerBuilder] on_event failed: {}", err);
829            }
830        }
831    }
832
833    async fn on_incoming_call(&self, call: IncomingCall) -> CallHandlerDecision {
834        (self.incoming)(call).await
835    }
836
837    async fn on_call_established(&self, handle: SessionHandle) {
838        if let Some(hook) = &self.established {
839            if let Err(err) = hook(handle).await {
840                tracing::warn!("[CallbackPeerBuilder] on_established failed: {}", err);
841            }
842        }
843    }
844
845    async fn on_call_progress(
846        &self,
847        handle: SessionHandle,
848        status_code: u16,
849        reason: String,
850        sdp: Option<String>,
851    ) {
852        if let Some(hook) = &self.progress {
853            if let Err(err) = hook(handle, status_code, reason, sdp).await {
854                tracing::warn!("[CallbackPeerBuilder] on_progress failed: {}", err);
855            }
856        }
857    }
858
859    async fn on_call_ended(&self, call_id: CallId, reason: EndReason) {
860        if let Some(hook) = &self.ended {
861            if let Err(err) = hook(call_id, reason).await {
862                tracing::warn!("[CallbackPeerBuilder] on_ended failed: {}", err);
863            }
864        }
865    }
866
867    async fn on_call_failed(&self, call_id: CallId, status_code: u16, reason: String) {
868        if let Some(hook) = &self.failed {
869            if let Err(err) = hook(call_id, status_code, reason).await {
870                tracing::warn!("[CallbackPeerBuilder] on_failed failed: {}", err);
871            }
872        }
873    }
874
875    async fn on_call_cancelled(&self, call_id: CallId) {
876        if let Some(hook) = &self.cancelled {
877            if let Err(err) = hook(call_id).await {
878                tracing::warn!("[CallbackPeerBuilder] on_cancelled failed: {}", err);
879            }
880        }
881    }
882
883    async fn on_dtmf(&self, handle: SessionHandle, digit: char) {
884        if let Some(hook) = &self.dtmf {
885            if let Err(err) = hook(handle, digit).await {
886                tracing::warn!("[CallbackPeerBuilder] on_dtmf failed: {}", err);
887            }
888        }
889    }
890
891    async fn on_media_security_negotiated(&self, handle: SessionHandle, state: MediaSecurityState) {
892        if let Some(hook) = &self.media_security {
893            if let Err(err) = hook(handle, state).await {
894                tracing::warn!("[CallbackPeerBuilder] on_media_security failed: {}", err);
895            }
896        }
897    }
898
899    async fn on_call_on_hold(&self, handle: SessionHandle) {
900        if let Some(hook) = &self.local_hold {
901            if let Err(err) = hook(handle).await {
902                tracing::warn!("[CallbackPeerBuilder] on_local_hold failed: {}", err);
903            }
904        }
905    }
906
907    async fn on_call_resumed(&self, handle: SessionHandle) {
908        if let Some(hook) = &self.local_resume {
909            if let Err(err) = hook(handle).await {
910                tracing::warn!("[CallbackPeerBuilder] on_local_resume failed: {}", err);
911            }
912        }
913    }
914
915    async fn on_remote_call_on_hold(&self, handle: SessionHandle) {
916        if let Some(hook) = &self.remote_hold {
917            if let Err(err) = hook(handle).await {
918                tracing::warn!("[CallbackPeerBuilder] on_remote_hold failed: {}", err);
919            }
920        }
921    }
922
923    async fn on_remote_call_resumed(&self, handle: SessionHandle) {
924        if let Some(hook) = &self.remote_resume {
925            if let Err(err) = hook(handle).await {
926                tracing::warn!("[CallbackPeerBuilder] on_remote_resume failed: {}", err);
927            }
928        }
929    }
930
931    async fn on_refer_received(&self, request: crate::api::incoming::IncomingRequest) {
932        let Some(hook) = &self.refer_received else {
933            return;
934        };
935        let Some(coord) = request.coordinator.clone() else {
936            tracing::warn!(
937                "[CallbackPeerBuilder] on_refer_received fired without a coordinator hook; \
938                 dropping REFER for call {}",
939                request.call_id
940            );
941            return;
942        };
943        let handle = SessionHandle::new(request.call_id.clone(), coord);
944        let accepted = match hook(handle.clone(), request).await {
945            Ok(b) => b,
946            Err(err) => {
947                tracing::warn!(
948                    "[CallbackPeerBuilder] on_refer_received failed; rejecting REFER: {}",
949                    err
950                );
951                false
952            }
953        };
954        let result = if accepted {
955            handle.accept_refer().await
956        } else {
957            handle.reject_refer(603, "Decline").await
958        };
959        if let Err(err) = result {
960            tracing::warn!(
961                "[CallbackPeerBuilder] applying REFER decision failed: {}",
962                err
963            );
964        }
965    }
966
967    async fn on_transfer_accepted(&self, handle: SessionHandle, refer_to: String) {
968        if let Some(hook) = &self.transfer_accepted {
969            if let Err(err) = hook(handle, refer_to).await {
970                tracing::warn!("[CallbackPeerBuilder] on_transfer_accepted failed: {}", err);
971            }
972        }
973    }
974
975    async fn on_refer_progress(&self, handle: SessionHandle, status_code: u16, reason: String) {
976        if let Some(hook) = &self.refer_progress {
977            if let Err(err) = hook(handle, status_code, reason).await {
978                tracing::warn!("[CallbackPeerBuilder] on_refer_progress failed: {}", err);
979            }
980        }
981    }
982
983    async fn on_refer_completed(
984        &self,
985        handle: SessionHandle,
986        target: String,
987        status_code: u16,
988        reason: String,
989    ) {
990        if let Some(hook) = &self.refer_completed {
991            if let Err(err) = hook(handle, target, status_code, reason).await {
992                tracing::warn!("[CallbackPeerBuilder] on_refer_completed failed: {}", err);
993            }
994        }
995    }
996
997    async fn on_transfer_failed(&self, handle: SessionHandle, status_code: u16, reason: String) {
998        if let Some(hook) = &self.transfer_failed {
999            if let Err(err) = hook(handle, status_code, reason).await {
1000                tracing::warn!("[CallbackPeerBuilder] on_transfer_failed failed: {}", err);
1001            }
1002        }
1003    }
1004
1005    async fn on_registration_success(&self, registrar: String, expires: u32, contact: String) {
1006        if let Some(hook) = &self.registration_success {
1007            if let Err(err) = hook(registrar, expires, contact).await {
1008                tracing::warn!(
1009                    "[CallbackPeerBuilder] on_registration_success failed: {}",
1010                    err
1011                );
1012            }
1013        }
1014    }
1015
1016    async fn on_registration_failed(&self, registrar: String, status_code: u16, reason: String) {
1017        if let Some(hook) = &self.registration_failed {
1018            if let Err(err) = hook(registrar, status_code, reason).await {
1019                tracing::warn!(
1020                    "[CallbackPeerBuilder] on_registration_failed failed: {}",
1021                    err
1022                );
1023            }
1024        }
1025    }
1026
1027    async fn on_unregistration_success(&self, registrar: String) {
1028        if let Some(hook) = &self.unregistration_success {
1029            if let Err(err) = hook(registrar).await {
1030                tracing::warn!(
1031                    "[CallbackPeerBuilder] on_unregistration_success failed: {}",
1032                    err
1033                );
1034            }
1035        }
1036    }
1037
1038    async fn on_unregistration_failed(&self, registrar: String, reason: String) {
1039        if let Some(hook) = &self.unregistration_failed {
1040            if let Err(err) = hook(registrar, reason).await {
1041                tracing::warn!(
1042                    "[CallbackPeerBuilder] on_unregistration_failed failed: {}",
1043                    err
1044                );
1045            }
1046        }
1047    }
1048
1049    async fn on_sip_trace(&self, trace: SipTrace) {
1050        if let Some(hook) = &self.sip_trace {
1051            if let Err(err) = hook(trace).await {
1052                tracing::warn!("[CallbackPeerBuilder] on_sip_trace failed: {}", err);
1053            }
1054        }
1055    }
1056}
1057
1058// ===== CallHandler trait =====
1059
1060/// Implement this trait to handle SIP call events reactively.
1061///
1062/// All methods are `async`. The library spawns a task for each handler invocation,
1063/// so you can freely await without blocking other calls.
1064///
1065/// # Example — simple accept-all UAS
1066///
1067/// ```rust,no_run
1068/// use rvoip_sip::api::callback_peer::{CallHandler, CallHandlerDecision};
1069/// use rvoip_sip::{SessionHandle, CallId, IncomingCall};
1070/// use rvoip_sip::api::callback_peer::EndReason;
1071/// use async_trait::async_trait;
1072///
1073/// struct AcceptAll;
1074///
1075/// #[async_trait]
1076/// impl CallHandler for AcceptAll {
1077///     async fn on_incoming_call(&self, call: IncomingCall) -> CallHandlerDecision {
1078///         println!("Incoming call from {}", call.from);
1079///         CallHandlerDecision::Accept
1080///     }
1081///
1082///     async fn on_call_ended(&self, call_id: CallId, reason: EndReason) {
1083///         println!("Call {} ended: {:?}", call_id, reason);
1084///     }
1085/// }
1086/// ```
1087#[async_trait]
1088pub trait CallHandler: Send + Sync + 'static {
1089    /// Called for every event before the more specific callback hook.
1090    #[allow(unused_variables)]
1091    async fn on_event(&self, event: Event) {}
1092
1093    /// Decide what to do with an incoming call.
1094    ///
1095    /// This is the only required method. The call waits in `Ringing` state until
1096    /// this future returns or the session ringing timeout expires.
1097    ///
1098    /// Either:
1099    /// - Consume the [`IncomingCall`] directly by calling `accept().await`,
1100    ///   `reject(...)`, `defer(...)`, or `redirect(...)` on it, or
1101    /// - Return a [`CallHandlerDecision`] and the dispatch will apply it.
1102    ///
1103    /// Both paths converge to the same state machine transitions.
1104    async fn on_incoming_call(&self, call: IncomingCall) -> CallHandlerDecision;
1105
1106    /// Called when an outgoing or accepted incoming call is fully established.
1107    #[allow(unused_variables)]
1108    async fn on_call_established(&self, handle: SessionHandle) {}
1109
1110    /// Called when an outgoing call receives a provisional 1xx response.
1111    ///
1112    /// This surfaces SIP progress such as `180 Ringing` and
1113    /// `183 Session Progress` without requiring callback applications to
1114    /// inspect the catch-all [`on_event`](Self::on_event) hook.
1115    #[allow(unused_variables)]
1116    async fn on_call_progress(
1117        &self,
1118        handle: SessionHandle,
1119        status_code: u16,
1120        reason: String,
1121        sdp: Option<String>,
1122    ) {
1123    }
1124
1125    /// Called when any call (incoming or outgoing) ends.
1126    #[allow(unused_variables)]
1127    async fn on_call_ended(&self, call_id: CallId, reason: EndReason) {}
1128
1129    /// Called when a call fails before normal BYE teardown.
1130    #[allow(unused_variables)]
1131    async fn on_call_failed(&self, call_id: CallId, status_code: u16, reason: String) {}
1132
1133    /// Called when a ringing call is cancelled before answer.
1134    #[allow(unused_variables)]
1135    async fn on_call_cancelled(&self, call_id: CallId) {}
1136
1137    /// Called when a DTMF digit is received on an active call.
1138    #[allow(unused_variables)]
1139    async fn on_dtmf(&self, handle: SessionHandle, digit: char) {}
1140
1141    /// Called when SRTP media security has been negotiated and contexts are installed.
1142    ///
1143    /// The state is typed and intentionally omits key material.
1144    #[allow(unused_variables)]
1145    async fn on_media_security_negotiated(&self, handle: SessionHandle, state: MediaSecurityState) {
1146    }
1147
1148    /// Called when a locally requested hold is accepted.
1149    #[allow(unused_variables)]
1150    async fn on_call_on_hold(&self, handle: SessionHandle) {}
1151
1152    /// Called when a locally requested resume is accepted.
1153    #[allow(unused_variables)]
1154    async fn on_call_resumed(&self, handle: SessionHandle) {}
1155
1156    /// Called when the remote peer places this call on hold.
1157    #[allow(unused_variables)]
1158    async fn on_remote_call_on_hold(&self, handle: SessionHandle) {}
1159
1160    /// Called when the remote peer resumes this call.
1161    #[allow(unused_variables)]
1162    async fn on_remote_call_resumed(&self, handle: SessionHandle) {}
1163
1164    /// Called when an outbound REFER is accepted by the peer.
1165    #[allow(unused_variables)]
1166    async fn on_transfer_accepted(&self, handle: SessionHandle, refer_to: String) {}
1167
1168    /// Called when raw REFER NOTIFY status is received.
1169    #[allow(unused_variables)]
1170    async fn on_refer_notify(
1171        &self,
1172        handle: SessionHandle,
1173        status_code: u16,
1174        reason: String,
1175        subscription_state: Option<SubscriptionState>,
1176        body: Option<String>,
1177    ) {
1178    }
1179
1180    /// Called when provisional REFER progress NOTIFY is received.
1181    #[allow(unused_variables)]
1182    async fn on_refer_progress(&self, handle: SessionHandle, status_code: u16, reason: String) {}
1183
1184    /// Called when successful terminal REFER completion is received.
1185    #[allow(unused_variables)]
1186    async fn on_refer_completed(
1187        &self,
1188        handle: SessionHandle,
1189        target: String,
1190        status_code: u16,
1191        reason: String,
1192    ) {
1193    }
1194
1195    /// Called when REFER failure is received.
1196    #[allow(unused_variables)]
1197    async fn on_transfer_failed(&self, handle: SessionHandle, status_code: u16, reason: String) {}
1198
1199    /// Called when rvoip-sip has target-answer evidence for a transfer.
1200    #[allow(unused_variables)]
1201    async fn on_transfer_target_answered(
1202        &self,
1203        handle: SessionHandle,
1204        target_uri: String,
1205        evidence: TransferTargetEvidence,
1206    ) {
1207    }
1208
1209    /// Called when RFC 4235 observes a candidate replacement dialog.
1210    #[allow(unused_variables)]
1211    async fn on_transfer_replacement_dialog_observed(
1212        &self,
1213        handle: SessionHandle,
1214        dialog: DialogInfo,
1215    ) {
1216    }
1217
1218    /// Called when RFC 4235 or local evidence observes replacement dialog teardown.
1219    #[allow(unused_variables)]
1220    async fn on_transfer_replacement_dialog_terminated(
1221        &self,
1222        handle: SessionHandle,
1223        dialog: DialogInfo,
1224        reason: Option<String>,
1225    ) {
1226    }
1227
1228    /// Called for every valid RFC 4235 dialog-package NOTIFY.
1229    #[allow(unused_variables)]
1230    async fn on_dialog_package_notify(
1231        &self,
1232        subscription_id: CallId,
1233        entity: Option<String>,
1234        version: Option<u32>,
1235        dialogs: Vec<DialogInfo>,
1236        document: DialogInfoDocument,
1237    ) {
1238    }
1239
1240    /// Called for each parsed RFC 4235 dialog entry transition.
1241    #[allow(unused_variables)]
1242    async fn on_dialog_state_changed(&self, subscription_id: CallId, dialog: DialogInfo) {}
1243
1244    /// SIP_API_DESIGN_2 Phase E — typed inbound NOTIFY hook. Carries
1245    /// the full `IncomingRequest` so applications can inspect every
1246    /// header on the NOTIFY (Server, Allow-Events, custom routing
1247    /// hints) in addition to the legacy pre-decoded fields. Default
1248    /// impl is a no-op.
1249    #[allow(unused_variables)]
1250    async fn on_notify_received(&self, request: crate::api::incoming::IncomingRequest) {}
1251
1252    /// SIP_API_DESIGN_2 Phase E — typed inbound REFER hook. Apps drive
1253    /// accept/reject via `req.accept_refer()` / `req.reject_refer(...)`
1254    /// inside this callback. Use for header-level access (Referred-By,
1255    /// Replaces, Target-Dialog, custom X-*).
1256    #[allow(unused_variables)]
1257    async fn on_refer_received(&self, request: crate::api::incoming::IncomingRequest) {}
1258
1259    /// SIP_API_DESIGN_2 Phase E — typed inbound INFO hook. Today's
1260    /// stack drops INFO at the dialog layer; this hook surfaces it so
1261    /// SIP-INFO DTMF (`application/dtmf-relay`), fax flow control,
1262    /// and other application-layer signalling can be observed.
1263    #[allow(unused_variables)]
1264    async fn on_info_received(&self, request: crate::api::incoming::IncomingRequest) {}
1265
1266    /// SIP_API_DESIGN_2 Phase E — typed inbound MESSAGE hook (RFC 3428).
1267    #[allow(unused_variables)]
1268    async fn on_message_received(&self, request: crate::api::incoming::IncomingRequest) {}
1269
1270    /// SIP_API_DESIGN_2 Phase E — typed inbound OPTIONS hook
1271    /// (RFC 3261 §11). Both in-dialog and out-of-dialog OPTIONS reach
1272    /// here; `request.call_id` discriminates.
1273    #[allow(unused_variables)]
1274    async fn on_options_received(&self, request: crate::api::incoming::IncomingRequest) {}
1275
1276    /// SIP_API_DESIGN_2 Phase E — typed inbound UPDATE hook
1277    /// (RFC 3311). Fires in addition to the legacy hold/resume state
1278    /// transitions; subscribe here for Session-Expires / X-*
1279    /// header inspection.
1280    #[allow(unused_variables)]
1281    async fn on_update_received(&self, request: crate::api::incoming::IncomingRequest) {}
1282
1283    /// SIP_API_DESIGN_2 Phase D — typed inbound REGISTER hook
1284    /// (RFC 3261 §10). Registrar surfaces author the response via
1285    /// `register.accept_builder()` / `register.challenge_builder(..)` /
1286    /// `register.reject_builder(status)`; if the handler returns
1287    /// without authoring a response, the dialog stack falls back to
1288    /// the auto-response path.
1289    #[allow(unused_variables)]
1290    async fn on_register_received(&self, register: crate::api::incoming::IncomingRegister) {}
1291
1292    /// Called when registration succeeds.
1293    #[allow(unused_variables)]
1294    async fn on_registration_success(&self, registrar: String, expires: u32, contact: String) {}
1295
1296    /// Called when registration fails.
1297    #[allow(unused_variables)]
1298    async fn on_registration_failed(&self, registrar: String, status_code: u16, reason: String) {}
1299
1300    /// Called when unregistration succeeds.
1301    #[allow(unused_variables)]
1302    async fn on_unregistration_success(&self, registrar: String) {}
1303
1304    /// Called when unregistration fails.
1305    #[allow(unused_variables)]
1306    async fn on_unregistration_failed(&self, registrar: String, reason: String) {}
1307
1308    /// Called for each SIP trace event when tracing is enabled.
1309    #[allow(unused_variables)]
1310    async fn on_sip_trace(&self, trace: SipTrace) {}
1311
1312    /// Called when an outgoing call receives a 401/407 and the coordinator is
1313    /// about to retry with `Authorization` / `Proxy-Authorization` (RFC 3261
1314    /// §22.2). Informational — the retry proceeds automatically if credentials
1315    /// are on file via [`Config.credentials`] or
1316    /// `coord.invite(...).with_credentials(...)`; this hook does not alter
1317    /// flow. Useful for logging or surfacing auth activity in a UI.
1318    ///
1319    /// [`Config.credentials`]: crate::api::unified::Config::credentials
1320    #[allow(unused_variables)]
1321    async fn on_auth_retrying(&self, call_id: CallId, status_code: u16, realm: String) {}
1322}
1323
1324/// Cloneable command handle for a running [`CallbackPeer`].
1325#[derive(Clone)]
1326pub struct CallbackPeerControl {
1327    coordinator: Arc<UnifiedCoordinator>,
1328    local_uri: String,
1329    shutdown_tx: tokio::sync::watch::Sender<bool>,
1330}
1331
1332impl CallbackPeerControl {
1333    /// Begin building an outbound REGISTER from this peer.
1334    ///
1335    /// Returns a [`RegisterBuilder`](crate::api::send::RegisterBuilder)
1336    /// — chain `.with_expires(..)`, `.with_from_uri(..)`,
1337    /// `.with_contact_uri(..)`, `.with_credentials(..)` etc. and finish
1338    /// with `.send().await`.
1339    pub fn register(
1340        &self,
1341        registrar: impl Into<String>,
1342        username: impl Into<String>,
1343        password: impl Into<String>,
1344    ) -> crate::api::send::RegisterBuilder {
1345        self.coordinator.register(registrar, username, password)
1346    }
1347
1348    /// Begin building an outbound REGISTER from a shared SIP account.
1349    pub fn register_account(&self, account: &SipAccount) -> crate::api::send::RegisterBuilder {
1350        let mut builder = self
1351            .register(
1352                account.registrar.clone(),
1353                account.effective_auth_username().to_string(),
1354                account.password.clone(),
1355            )
1356            .with_expires(account.expires);
1357        if let Some(from_uri) = &account.from_uri {
1358            builder = builder.with_from_uri(from_uri.clone());
1359        }
1360        if let Some(contact_uri) = &account.contact_uri {
1361            builder = builder.with_contact_uri(contact_uri.clone());
1362        }
1363        builder
1364    }
1365
1366    /// Query whether a registration handle is currently registered.
1367    ///
1368    /// This is a coarse boolean. Use
1369    /// [`coordinator`](Self::coordinator) and
1370    /// [`UnifiedCoordinator::registration_info`] for lifecycle metadata.
1371    ///
1372    /// # Examples
1373    ///
1374    /// ```rust,no_run
1375    /// # async fn example(control: rvoip_sip::CallbackPeerControl, handle: rvoip_sip::RegistrationHandle) -> rvoip_sip::Result<()> {
1376    /// let active = control.is_registered(&handle).await?;
1377    /// # let _ = active;
1378    /// # Ok(())
1379    /// # }
1380    /// ```
1381    pub async fn is_registered(&self, handle: &RegistrationHandle) -> Result<bool> {
1382        self.coordinator.is_registered(handle).await
1383    }
1384
1385    /// Unregister.
1386    ///
1387    /// Returns after the state machine accepts the unregister request. Use
1388    /// [`UnifiedCoordinator::unregister_and_wait`] through
1389    /// [`coordinator`](Self::coordinator) for deterministic registrar
1390    /// confirmation.
1391    ///
1392    /// # Examples
1393    ///
1394    /// ```rust,no_run
1395    /// # async fn example(control: rvoip_sip::CallbackPeerControl, handle: rvoip_sip::RegistrationHandle) -> rvoip_sip::Result<()> {
1396    /// control.unregister(&handle).await?;
1397    /// # Ok(())
1398    /// # }
1399    /// ```
1400    pub async fn unregister(&self, handle: &RegistrationHandle) -> Result<()> {
1401        self.coordinator.unregister(handle).await
1402    }
1403
1404    /// Hang up or cancel a call and wait until rvoip-sip has accepted the
1405    /// request.
1406    ///
1407    /// This uses the same SIP teardown semantics as [`SessionHandle::hangup`]:
1408    /// BYE for established calls, CANCEL for ringing/early outbound calls, and
1409    /// delayed CANCEL intent before the first provisional response. Callback
1410    /// handlers observe terminal completion through `on_call_ended`,
1411    /// `on_call_failed`, or `on_call_cancelled`.
1412    ///
1413    /// # Examples
1414    ///
1415    /// ```rust,no_run
1416    /// # async fn example(control: rvoip_sip::CallbackPeerControl, call: rvoip_sip::SessionHandle) -> rvoip_sip::Result<()> {
1417    /// control.hangup(&call).await?;
1418    /// # Ok(())
1419    /// # }
1420    /// ```
1421    pub async fn hangup(&self, handle: &SessionHandle) -> Result<()> {
1422        self.coordinator.hangup(handle.id()).await
1423    }
1424
1425    /// Signal the owning [`CallbackPeer`] event loop to stop.
1426    ///
1427    /// This is a stop signal for the event loop. For deterministic graceful
1428    /// unregister, call [`UnifiedCoordinator::shutdown_gracefully`] through
1429    /// [`coordinator`](Self::coordinator).
1430    ///
1431    /// # Examples
1432    ///
1433    /// ```rust,no_run
1434    /// # fn example(control: rvoip_sip::CallbackPeerControl) {
1435    /// control.shutdown();
1436    /// # }
1437    /// ```
1438    pub fn shutdown(&self) {
1439        let _ = self.shutdown_tx.send(true);
1440    }
1441
1442    /// Access the underlying coordinator for advanced operations.
1443    ///
1444    /// This accessor is intentionally trivial and returns the shared
1445    /// [`UnifiedCoordinator`] handle.
1446    pub fn coordinator(&self) -> &Arc<UnifiedCoordinator> {
1447        &self.coordinator
1448    }
1449
1450    /// Begin building an outbound INVITE from this peer's configured
1451    /// `local_uri`. Returns an
1452    /// [`OutboundCallBuilder`](crate::api::send::OutboundCallBuilder).
1453    pub fn invite(&self, target: impl Into<String>) -> crate::api::send::OutboundCallBuilder {
1454        self.coordinator
1455            .invite(Some(self.local_uri.clone()), target)
1456    }
1457}
1458
1459// ===== CallbackPeer =====
1460
1461/// A SIP peer driven by a [`CallHandler`] implementation.
1462///
1463/// `CallbackPeer` subscribes to the coordinator event stream, dispatches each
1464/// event to the relevant handler hook, and tracks in-flight hook tasks during
1465/// shutdown. Incoming-call decisions can either be returned as
1466/// [`CallHandlerDecision`] values or performed directly on the supplied
1467/// [`IncomingCall`].
1468///
1469/// The peer consumes itself in [`run`](Self::run). Obtain
1470/// [`shutdown_handle`](Self::shutdown_handle) or [`control`](Self::control)
1471/// before calling `run` if another task must stop it or place outbound calls.
1472///
1473/// # Example
1474///
1475/// ```rust,no_run
1476/// # async fn example() -> anyhow::Result<()> {
1477/// use rvoip_sip::api::callback_peer::{CallbackPeer, CallHandler, CallHandlerDecision};
1478/// use rvoip_sip::{IncomingCall, Config};
1479/// use async_trait::async_trait;
1480///
1481/// struct Router;
1482///
1483/// #[async_trait]
1484/// impl CallHandler for Router {
1485///     async fn on_incoming_call(&self, call: IncomingCall) -> CallHandlerDecision {
1486///         if call.from.contains("blocked") {
1487///             CallHandlerDecision::Reject { status: 403, reason: "Forbidden".into() }
1488///         } else {
1489///             CallHandlerDecision::Accept
1490///         }
1491///     }
1492/// }
1493///
1494/// let config = Config { sip_port: 5060, ..Default::default() };
1495/// let peer = CallbackPeer::new(Router, config).await?;
1496/// peer.run().await?;
1497/// # Ok(())
1498/// # }
1499/// ```
1500pub struct CallbackPeer<H: CallHandler> {
1501    handler: Arc<H>,
1502    coordinator: Arc<UnifiedCoordinator>,
1503    local_uri: String,
1504    shutdown_tx: tokio::sync::watch::Sender<bool>,
1505    shutdown_rx: tokio::sync::watch::Receiver<bool>,
1506    established_callbacks: Arc<tokio::sync::Mutex<HashSet<CallId>>>,
1507    terminal_callbacks: Arc<tokio::sync::Mutex<BoundedCallDedupe>>,
1508    deferred_calls: Arc<tokio::sync::Mutex<HashMap<CallId, IncomingCallGuard>>>,
1509}
1510
1511const TERMINAL_CALLBACK_DEDUPE_CAPACITY: usize = 8192;
1512
1513struct BoundedCallDedupe {
1514    set: HashSet<CallId>,
1515    order: VecDeque<CallId>,
1516    capacity: usize,
1517}
1518
1519impl BoundedCallDedupe {
1520    fn with_capacity(capacity: usize) -> Self {
1521        Self {
1522            set: HashSet::with_capacity(capacity),
1523            order: VecDeque::with_capacity(capacity),
1524            capacity: capacity.max(1),
1525        }
1526    }
1527
1528    fn insert(&mut self, call_id: CallId) -> bool {
1529        if self.set.contains(&call_id) {
1530            return false;
1531        }
1532
1533        self.set.insert(call_id.clone());
1534        self.order.push_back(call_id);
1535
1536        while self.order.len() > self.capacity {
1537            if let Some(oldest) = self.order.pop_front() {
1538                self.set.remove(&oldest);
1539            }
1540        }
1541
1542        true
1543    }
1544
1545    #[cfg(test)]
1546    fn len(&self) -> usize {
1547        self.set.len()
1548    }
1549}
1550
1551impl CallbackPeer<CallbackBuilderHandler> {
1552    /// Create a closure-based [`CallbackPeerBuilder`].
1553    ///
1554    /// # Examples
1555    ///
1556    /// ```rust,no_run
1557    /// # async fn example() -> rvoip_sip::Result<()> {
1558    /// use rvoip_sip::{CallHandlerDecision, CallbackPeer, Config};
1559    ///
1560    /// let peer = CallbackPeer::builder(Config::default())
1561    ///     .on_incoming(|_call| async move { CallHandlerDecision::Accept })
1562    ///     .build()
1563    ///     .await?;
1564    /// # let _ = peer;
1565    /// # Ok(())
1566    /// # }
1567    /// ```
1568    pub fn builder(config: Config) -> CallbackPeerBuilder {
1569        CallbackPeerBuilder::new(config)
1570    }
1571}
1572
1573impl<H: CallHandler> CallbackPeer<H> {
1574    /// Create a new `CallbackPeer`.
1575    ///
1576    /// Set `config.auth` for general UAC 401/407 retry across supported SIP
1577    /// auth schemes, or `config.credentials` as the Digest shorthand:
1578    ///
1579    /// # Examples
1580    ///
1581    /// ```rust,no_run
1582    /// # async fn example() -> rvoip_sip::Result<()> {
1583    /// use rvoip_sip::{CallbackPeer, Config, SipClientAuth};
1584    /// use rvoip_sip::api::handlers::AutoAnswerHandler;
1585    ///
1586    /// let config = Config {
1587    ///     auth: Some(SipClientAuth::digest("alice", "secret")),
1588    ///     ..Config::default()
1589    /// };
1590    /// let peer = CallbackPeer::new(AutoAnswerHandler, config).await?;
1591    /// # Ok(())
1592    /// # }
1593    /// ```
1594    ///
1595    /// For per-call overrides, use `coordinator().invite(...).with_auth(...)`
1596    /// or the Digest shorthand `.with_credentials(...)` via
1597    /// [`coordinator()`](Self::coordinator).
1598    pub async fn new(handler: H, config: Config) -> Result<Self> {
1599        let local_uri = config.local_uri.clone();
1600        let coordinator = UnifiedCoordinator::new(config).await?;
1601        let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false);
1602        Ok(Self {
1603            handler: Arc::new(handler),
1604            coordinator,
1605            local_uri,
1606            shutdown_tx,
1607            shutdown_rx,
1608            established_callbacks: Arc::new(tokio::sync::Mutex::new(HashSet::new())),
1609            terminal_callbacks: Arc::new(tokio::sync::Mutex::new(
1610                BoundedCallDedupe::with_capacity(TERMINAL_CALLBACK_DEDUPE_CAPACITY),
1611            )),
1612            deferred_calls: Arc::new(tokio::sync::Mutex::new(HashMap::new())),
1613        })
1614    }
1615
1616    /// Access the underlying coordinator for advanced operations.
1617    ///
1618    /// Prefer [`control`](Self::control) for normal outbound calls and
1619    /// registration from outside the event loop. Use the coordinator directly
1620    /// when you need lower-level methods such as media bridging,
1621    /// per-session event receivers, or custom transfer orchestration.
1622    ///
1623    /// # Examples
1624    ///
1625    /// ```rust,no_run
1626    /// # async fn example(peer: rvoip_sip::CallbackPeer<rvoip_sip::AutoAnswerHandler>) {
1627    /// let coordinator = peer.coordinator().clone();
1628    /// let mut events = coordinator.events().await.unwrap();
1629    /// # let _ = events.next().await;
1630    /// # }
1631    /// ```
1632    pub fn coordinator(&self) -> &Arc<UnifiedCoordinator> {
1633        &self.coordinator
1634    }
1635
1636    /// Begin building an outbound INVITE from this peer's configured
1637    /// `local_uri`. Equivalent to
1638    /// `peer.coordinator().invite(Some(local_uri), target)`.
1639    pub fn invite(&self, target: impl Into<String>) -> crate::api::send::OutboundCallBuilder {
1640        self.coordinator
1641            .invite(Some(self.local_uri.clone()), target)
1642    }
1643
1644    /// Return a cloneable control handle for calls, registration, and shutdown.
1645    ///
1646    /// This is the ergonomic way to place outbound calls or unregister while
1647    /// the peer is running in another task.
1648    ///
1649    /// # Examples
1650    ///
1651    /// ```rust,no_run
1652    /// # async fn example(peer: rvoip_sip::CallbackPeer<rvoip_sip::AutoAnswerHandler>) -> rvoip_sip::Result<()> {
1653    /// let control = peer.control();
1654    /// tokio::spawn(async move { peer.run().await });
1655    /// let call = control.invite("sip:bob@example.com").send().await?;
1656    /// # let _ = call;
1657    /// # Ok(())
1658    /// # }
1659    /// ```
1660    pub fn control(&self) -> CallbackPeerControl {
1661        CallbackPeerControl {
1662            coordinator: self.coordinator.clone(),
1663            local_uri: self.local_uri.clone(),
1664            shutdown_tx: self.shutdown_tx.clone(),
1665        }
1666    }
1667
1668    // ===== Registration (symmetric with StreamPeer) =====
1669    //
1670    // A CallbackPeer acting as a B2BUA leg may need to register itself
1671    // upstream (with a carrier / SBC) before or while answering inbound
1672    // calls. These are thin wrappers over the coordinator; use
1673    // `coordinator()` for metadata and deterministic wait helpers.
1674
1675    /// Begin building an outbound REGISTER. Delegates to
1676    /// [`CallbackPeerControl::register`].
1677    pub fn register(
1678        &self,
1679        registrar: impl Into<String>,
1680        username: impl Into<String>,
1681        password: impl Into<String>,
1682    ) -> crate::api::send::RegisterBuilder {
1683        self.coordinator.register(registrar, username, password)
1684    }
1685
1686    /// Begin building an outbound REGISTER from a shared SIP account.
1687    pub fn register_account(&self, account: &SipAccount) -> crate::api::send::RegisterBuilder {
1688        self.control().register_account(account)
1689    }
1690
1691    /// Query whether a registration handle is currently registered.
1692    ///
1693    /// Returns `true` once the registrar has replied 200 OK to the REGISTER
1694    /// (including after a 423 Interval Too Brief retry or 401 auth retry),
1695    /// and `false` if the registration was rejected, unregistered, or has
1696    /// not yet completed. Use [`coordinator`](Self::coordinator) and
1697    /// [`UnifiedCoordinator::registration_info`] for richer lifecycle details.
1698    ///
1699    /// # Examples
1700    ///
1701    /// ```rust,no_run
1702    /// # async fn example(peer: rvoip_sip::CallbackPeer<rvoip_sip::AutoAnswerHandler>, handle: rvoip_sip::RegistrationHandle) -> rvoip_sip::Result<()> {
1703    /// let active = peer.is_registered(&handle).await?;
1704    /// # let _ = active;
1705    /// # Ok(())
1706    /// # }
1707    /// ```
1708    pub async fn is_registered(
1709        &self,
1710        handle: &crate::api::unified::RegistrationHandle,
1711    ) -> Result<bool> {
1712        self.coordinator.is_registered(handle).await
1713    }
1714
1715    /// Unregister (sends REGISTER with `Expires: 0`).
1716    ///
1717    /// Returns after the state machine accepts the request. Use
1718    /// [`UnifiedCoordinator::unregister_and_wait`] through
1719    /// [`coordinator`](Self::coordinator) when the caller needs to wait for
1720    /// the registrar response.
1721    ///
1722    /// # Examples
1723    ///
1724    /// ```rust,no_run
1725    /// # async fn example(peer: rvoip_sip::CallbackPeer<rvoip_sip::AutoAnswerHandler>, handle: rvoip_sip::RegistrationHandle) -> rvoip_sip::Result<()> {
1726    /// peer.unregister(&handle).await?;
1727    /// # Ok(())
1728    /// # }
1729    /// ```
1730    pub async fn unregister(&self, handle: &crate::api::unified::RegistrationHandle) -> Result<()> {
1731        self.coordinator.unregister(handle).await
1732    }
1733
1734    /// Signal shutdown. The `run()` future will return after the current event
1735    /// is processed.
1736    ///
1737    /// This does not wait for unregister. For deterministic unregister before
1738    /// stopping, call [`UnifiedCoordinator::shutdown_gracefully`] through
1739    /// [`coordinator`](Self::coordinator).
1740    ///
1741    /// # Examples
1742    ///
1743    /// ```rust,no_run
1744    /// # fn example(peer: rvoip_sip::CallbackPeer<rvoip_sip::AutoAnswerHandler>) {
1745    /// peer.shutdown();
1746    /// # }
1747    /// ```
1748    pub fn shutdown(&self) {
1749        let _ = self.shutdown_tx.send(true);
1750    }
1751
1752    /// Return a handle that can signal shutdown from another task.
1753    ///
1754    /// Obtain this **before** calling [`run()`], which consumes `self`.
1755    ///
1756    /// # Examples
1757    ///
1758    /// ```rust,no_run
1759    /// # async fn demo() -> rvoip_sip::Result<()> {
1760    /// # use rvoip_sip::*;
1761    /// let peer = CallbackPeer::with_auto_answer(Config::default()).await?;
1762    /// let stop = peer.shutdown_handle();
1763    /// tokio::spawn(async move { peer.run().await });
1764    /// // … later …
1765    /// stop.shutdown();
1766    /// # Ok(())
1767    /// # }
1768    /// ```
1769    ///
1770    /// [`run()`]: Self::run
1771    pub fn shutdown_handle(&self) -> ShutdownHandle {
1772        ShutdownHandle {
1773            tx: self.shutdown_tx.clone(),
1774        }
1775    }
1776
1777    /// Start the event loop.
1778    ///
1779    /// Processes events until [`shutdown()`] is called or the coordinator is dropped.
1780    /// In-flight handler invocations are tracked in a `JoinSet`; on shutdown,
1781    /// `run()` awaits all pending handlers before returning, so `Ok(())` means
1782    /// "all user callbacks have observed their final events and returned." This
1783    /// matters for tests (and b2bua) that tear down and re-create peers and
1784    /// need to guarantee no user-code is still mutating shared state.
1785    ///
1786    /// After draining handlers, `run()` signals coordinator shutdown. That
1787    /// shutdown path performs best-effort unregister when configured. Callers
1788    /// that need deterministic unregister completion should invoke
1789    /// [`UnifiedCoordinator::shutdown_gracefully`] before or instead of
1790    /// signalling this event loop.
1791    ///
1792    /// Deferred incoming calls that are still unresolved when shutdown begins
1793    /// are marked resolved before the coordinator is stopped. This prevents the
1794    /// deferred guard's drop safety net from attempting a late rejection over a
1795    /// closing transport. Applications that need to reject queued calls should
1796    /// resolve them before calling shutdown.
1797    ///
1798    /// [`shutdown()`]: Self::shutdown
1799    ///
1800    /// # Examples
1801    ///
1802    /// ```rust,no_run
1803    /// # async fn example(peer: rvoip_sip::CallbackPeer<rvoip_sip::AutoAnswerHandler>) -> rvoip_sip::Result<()> {
1804    /// let stop = peer.shutdown_handle();
1805    /// tokio::spawn(async move { peer.run().await });
1806    /// stop.shutdown();
1807    /// # Ok(())
1808    /// # }
1809    /// ```
1810    pub async fn run(self) -> Result<()> {
1811        let mut event_rx = self.coordinator.subscribe_events().await?;
1812        let mut shutdown_rx = self.shutdown_rx.clone();
1813        let mut handlers: tokio::task::JoinSet<()> = tokio::task::JoinSet::new();
1814
1815        loop {
1816            reap_ready_handlers(&mut handlers, "completed");
1817            tokio::select! {
1818                // Check for shutdown signal
1819                _ = shutdown_rx.changed() => {
1820                    if *shutdown_rx.borrow() {
1821                        tracing::info!("[CallbackPeer] Shutdown signal received");
1822                        break;
1823                    }
1824                }
1825                // Reap completed handlers so the JoinSet doesn't grow
1826                // unboundedly on a long-lived peer. This branch is only
1827                // selected when there's at least one pending handler.
1828                Some(join_result) = handlers.join_next(), if !handlers.is_empty() => {
1829                    if let Err(e) = join_result {
1830                        if !e.is_cancelled() {
1831                            tracing::warn!("[CallbackPeer] Handler task panicked or errored: {}", e);
1832                        }
1833                    }
1834                }
1835                // Process next event
1836                raw = event_rx.recv() => {
1837                    let Some(raw_event) = raw else {
1838                        tracing::info!("[CallbackPeer] Event channel closed, stopping");
1839                        break;
1840                    };
1841
1842                    // Downcast from cross-crate event wrapper to our Event type
1843                    let Some(session_event) = raw_event
1844                        .as_any()
1845                        .downcast_ref::<crate::adapters::SessionApiCrossCrateEvent>()
1846                    else {
1847                        continue;
1848                    };
1849
1850                    let event = session_event.event.clone();
1851                    self.dispatch(event, &mut handlers).await;
1852                    reap_ready_handlers(&mut handlers, "post-dispatch");
1853                }
1854            }
1855        }
1856
1857        // Wait for all in-flight handler invocations to return before we tear
1858        // down the coordinator. This is the whole point of the JoinSet: user
1859        // code should never be interrupted mid-handler by a shutdown.
1860        while let Some(join_result) = handlers.join_next().await {
1861            if let Err(e) = join_result {
1862                if !e.is_cancelled() {
1863                    tracing::warn!(
1864                        "[CallbackPeer] Handler task panicked or errored on drain: {}",
1865                        e
1866                    );
1867                }
1868            }
1869        }
1870
1871        {
1872            let mut deferred = self.deferred_calls.lock().await;
1873            for guard in deferred.values() {
1874                guard.resolve_without_response();
1875            }
1876            deferred.clear();
1877        }
1878
1879        // Shut down the coordinator and wait for dialog/transaction
1880        // transports to close before `run()` returns. Tests and services may
1881        // immediately restart a peer on the same port after this future
1882        // resolves.
1883        if let Err(e) = self
1884            .coordinator
1885            .shutdown_gracefully(Some(Duration::ZERO))
1886            .await
1887        {
1888            tracing::warn!("[CallbackPeer] Coordinator shutdown failed: {}", e);
1889        }
1890        Ok(())
1891    }
1892
1893    /// Dispatch a single event to the appropriate handler method. Each spawn
1894    /// is tracked in `handlers` so `run()` can drain them on shutdown.
1895    async fn dispatch(&self, event: Event, handlers: &mut tokio::task::JoinSet<()>) {
1896        let handler = self.handler.clone();
1897        let coordinator = self.coordinator.clone();
1898        let established_callbacks = self.established_callbacks.clone();
1899        let terminal_callbacks = self.terminal_callbacks.clone();
1900        let deferred_calls = self.deferred_calls.clone();
1901        let fast_auto_accept_incoming_calls = coordinator.fast_auto_accept_incoming_calls();
1902
1903        handlers.spawn(async move {
1904            let dispatch_guard = cleanup_diag::stage_guard(
1905                callback_stage_for_event(&event),
1906                callback_label_for_event(&event),
1907            );
1908            handler.on_event(event.clone()).await;
1909
1910            match event {
1911                Event::IncomingCall {
1912                    call_id,
1913                    from,
1914                    to,
1915                    sdp,
1916                } => {
1917                    // The handler may resolve the call itself (accept/reject/defer)
1918                    // by consuming IncomingCall. If it only returns a decision
1919                    // without consuming the call, the dispatch applies it via the
1920                    // coordinator below. Handler Drop still acts as a safety net.
1921                    //
1922                    // SIP_API_DESIGN_2 Phase A: prefer the parsed
1923                    // `Arc<Request>` view when the bus enriched it.
1924                    let parsed = coordinator
1925                        .session_registry
1926                        .peek_pending_incoming_request()
1927                        .await;
1928                    let transport = coordinator
1929                        .session_registry
1930                        .peek_pending_incoming_transport()
1931                        .await;
1932                    let incoming = match parsed {
1933                        Some(req) => IncomingCall::with_request(
1934                            call_id.clone(),
1935                            from,
1936                            to,
1937                            sdp,
1938                            coordinator.clone(),
1939                            req,
1940                        ),
1941                        None => IncomingCall::new(
1942                            call_id.clone(),
1943                            from,
1944                            to,
1945                            sdp,
1946                            coordinator.clone(),
1947                        ),
1948                    }
1949                    .with_transport_context(
1950                        transport
1951                            .as_deref()
1952                            .cloned()
1953                            .unwrap_or_else(crate::auth::SipTransportSecurityContext::unknown),
1954                    );
1955                    let decision = handler.on_incoming_call(incoming).await;
1956                    // These coordinator calls are idempotent — if the handler
1957                    // already resolved the call, the session has transitioned
1958                    // out of Ringing and the call becomes a no-op error we ignore.
1959                    match decision {
1960                        CallHandlerDecision::Accept => {
1961                            if fast_auto_accept_incoming_calls {
1962                                tracing::debug!(
1963                                    "Callback accept decision for {} already handled by fast auto-accept",
1964                                    call_id
1965                                );
1966                                return;
1967                            }
1968
1969                            let accept_guard = cleanup_diag::stage_guard(
1970                                CleanupStage::CallbackAcceptCall,
1971                                call_id.to_string(),
1972                            );
1973                            match coordinator.accept_call(&call_id).await {
1974                                Ok(()) => {
1975                                    accept_guard.finish_success();
1976                                    let should_notify = {
1977                                        let mut callbacks = established_callbacks.lock().await;
1978                                        callbacks.insert(call_id.clone())
1979                                    };
1980                                    if should_notify {
1981                                        let handle =
1982                                            SessionHandle::new(call_id.clone(), coordinator.clone());
1983                                        handler.on_call_established(handle).await;
1984                                    }
1985                                }
1986                                Err(e) => {
1987                                    tracing::debug!(
1988                                        "Callback accept decision for {} was not applied: {}",
1989                                        call_id,
1990                                        e
1991                                    );
1992                                    accept_guard.finish_failure();
1993                                }
1994                            }
1995                        }
1996                        CallHandlerDecision::AcceptWithSdp(sdp) => {
1997                            if fast_auto_accept_incoming_calls {
1998                                tracing::debug!(
1999                                    "Callback accept-with-SDP decision for {} already handled by fast auto-accept",
2000                                    call_id
2001                                );
2002                                return;
2003                            }
2004
2005                            let accept_guard = cleanup_diag::stage_guard(
2006                                CleanupStage::CallbackAcceptCall,
2007                                call_id.to_string(),
2008                            );
2009                            match coordinator.accept_call_with_sdp(&call_id, sdp).await {
2010                                Ok(()) => {
2011                                    accept_guard.finish_success();
2012                                    let should_notify = {
2013                                        let mut callbacks = established_callbacks.lock().await;
2014                                        callbacks.insert(call_id.clone())
2015                                    };
2016                                    if should_notify {
2017                                        let handle =
2018                                            SessionHandle::new(call_id.clone(), coordinator.clone());
2019                                        handler.on_call_established(handle).await;
2020                                    }
2021                                }
2022                                Err(e) => {
2023                                    tracing::debug!(
2024                                        "Callback accept-with-SDP decision for {} was not applied: {}",
2025                                        call_id,
2026                                        e
2027                                    );
2028                                    accept_guard.finish_failure();
2029                                }
2030                            }
2031                        }
2032                        CallHandlerDecision::Reject { status, reason } => {
2033                            if fast_auto_accept_incoming_calls {
2034                                tracing::debug!(
2035                                    "Callback reject decision for {} ignored because fast auto-accept already answered the call",
2036                                    call_id
2037                                );
2038                                return;
2039                            }
2040
2041                            let _ = coordinator
2042                                .reject(&call_id)
2043                                .with_status(status)
2044                                .with_reason(reason)
2045                                .send()
2046                                .await;
2047                        }
2048                        CallHandlerDecision::Redirect(target) => {
2049                            if fast_auto_accept_incoming_calls {
2050                                tracing::debug!(
2051                                    "Callback redirect decision for {} ignored because fast auto-accept already answered the call",
2052                                    call_id
2053                                );
2054                                return;
2055                            }
2056
2057                            let _ = coordinator
2058                                .redirect(&call_id)
2059                                .with_status(302)
2060                                .with_contacts(vec![target])
2061                                .send()
2062                                .await;
2063                        }
2064                        CallHandlerDecision::Defer(guard) => {
2065                            if fast_auto_accept_incoming_calls {
2066                                guard.resolve_without_response();
2067                                tracing::debug!(
2068                                    "Callback defer decision for {} ignored because fast auto-accept already answered the call",
2069                                    call_id
2070                                );
2071                                return;
2072                            }
2073
2074                            deferred_calls.lock().await.insert(call_id, guard);
2075                        }
2076                    }
2077                }
2078
2079                Event::CallAnswered { call_id, .. } => {
2080                    if let Some(guard) = deferred_calls.lock().await.remove(&call_id) {
2081                        guard.resolve_without_response();
2082                    }
2083                    let should_notify = {
2084                        let mut callbacks = established_callbacks.lock().await;
2085                        callbacks.insert(call_id.clone())
2086                    };
2087                    if should_notify {
2088                        let handle = SessionHandle::new(call_id, coordinator);
2089                        handler.on_call_established(handle).await;
2090                    }
2091                }
2092
2093                Event::CallProgress {
2094                    call_id,
2095                    status_code,
2096                    reason,
2097                    sdp,
2098                } => {
2099                    let handle = SessionHandle::new(call_id, coordinator);
2100                    handler
2101                        .on_call_progress(handle, status_code, reason, sdp)
2102                        .await;
2103                }
2104
2105                Event::CallEnded { call_id, reason } => {
2106                    if let Some(guard) = deferred_calls.lock().await.remove(&call_id) {
2107                        guard.resolve_without_response();
2108                    }
2109                    established_callbacks.lock().await.remove(&call_id);
2110                    let should_notify = {
2111                        let mut callbacks = terminal_callbacks.lock().await;
2112                        callbacks.insert(call_id.clone())
2113                    };
2114                    if should_notify {
2115                        let end_reason = EndReason::from(reason);
2116                        handler.on_call_ended(call_id, end_reason).await;
2117                    }
2118                }
2119
2120                Event::CallFailed {
2121                    call_id,
2122                    status_code,
2123                    reason,
2124                } => {
2125                    if let Some(guard) = deferred_calls.lock().await.remove(&call_id) {
2126                        guard.resolve_without_response();
2127                    }
2128                    established_callbacks.lock().await.remove(&call_id);
2129                    let should_notify = {
2130                        let mut callbacks = terminal_callbacks.lock().await;
2131                        callbacks.insert(call_id.clone())
2132                    };
2133                    if should_notify {
2134                        handler.on_call_failed(call_id, status_code, reason).await;
2135                    }
2136                }
2137
2138                Event::CallCancelled { call_id } => {
2139                    if let Some(guard) = deferred_calls.lock().await.remove(&call_id) {
2140                        guard.resolve_without_response();
2141                    }
2142                    established_callbacks.lock().await.remove(&call_id);
2143                    let should_notify = {
2144                        let mut callbacks = terminal_callbacks.lock().await;
2145                        callbacks.insert(call_id.clone())
2146                    };
2147                    if should_notify {
2148                        handler.on_call_cancelled(call_id).await;
2149                    }
2150                }
2151
2152                Event::CallOnHold { call_id } => {
2153                    let handle = SessionHandle::new(call_id, coordinator);
2154                    handler.on_call_on_hold(handle).await;
2155                }
2156
2157                Event::CallResumed { call_id } => {
2158                    let handle = SessionHandle::new(call_id, coordinator);
2159                    handler.on_call_resumed(handle).await;
2160                }
2161
2162                Event::RemoteCallOnHold { call_id } => {
2163                    let handle = SessionHandle::new(call_id, coordinator);
2164                    handler.on_remote_call_on_hold(handle).await;
2165                }
2166
2167                Event::RemoteCallResumed { call_id } => {
2168                    let handle = SessionHandle::new(call_id, coordinator);
2169                    handler.on_remote_call_resumed(handle).await;
2170                }
2171
2172                Event::DtmfReceived { call_id, digit } => {
2173                    let handle = SessionHandle::new(call_id, coordinator);
2174                    handler.on_dtmf(handle, digit).await;
2175                }
2176
2177                Event::MediaSecurityNegotiated {
2178                    call_id,
2179                    keying,
2180                    suite,
2181                    profile,
2182                    contexts_installed,
2183                } => {
2184                    let handle = SessionHandle::new(call_id, coordinator);
2185                    handler
2186                        .on_media_security_negotiated(
2187                            handle,
2188                            MediaSecurityState {
2189                                keying,
2190                                suite,
2191                                profile,
2192                                contexts_installed,
2193                            },
2194                        )
2195                        .await;
2196                }
2197
2198                Event::ReferReceived {
2199                    call_id: _, refer_to: _, request, ..
2200                } => {
2201                    // SIP_API_DESIGN_2 Phase E §9.5 — typed
2202                    // `on_refer_received(IncomingRequest)` hook only.
2203                    // Applications drive accept/reject via
2204                    // `req.accept_refer()` / `req.reject_refer(...)`
2205                    // inside the callback.
2206                    if let Some(mut req) = request {
2207                        req.set_coordinator(coordinator.clone());
2208                        handler.on_refer_received(req).await;
2209                    }
2210                }
2211
2212                Event::TransferAccepted { call_id, refer_to } => {
2213                    let handle = SessionHandle::new(call_id, coordinator);
2214                    handler.on_transfer_accepted(handle, refer_to).await;
2215                }
2216
2217                Event::ReferNotify {
2218                    call_id,
2219                    status_code,
2220                    reason,
2221                    subscription_state,
2222                    body,
2223                } => {
2224                    let handle = SessionHandle::new(call_id, coordinator);
2225                    handler
2226                        .on_refer_notify(handle, status_code, reason, subscription_state, body)
2227                        .await;
2228                }
2229
2230                Event::ReferProgress {
2231                    call_id,
2232                    status_code,
2233                    reason,
2234                } => {
2235                    let handle = SessionHandle::new(call_id, coordinator);
2236                    handler.on_refer_progress(handle, status_code, reason).await;
2237                }
2238
2239                Event::ReferCompleted {
2240                    call_id,
2241                    target,
2242                    status_code,
2243                    reason,
2244                } => {
2245                    let handle = SessionHandle::new(call_id, coordinator);
2246                    handler
2247                        .on_refer_completed(handle, target, status_code, reason)
2248                        .await;
2249                }
2250
2251                Event::TransferFailed {
2252                    call_id,
2253                    status_code,
2254                    reason,
2255                } => {
2256                    let handle = SessionHandle::new(call_id, coordinator);
2257                    handler
2258                        .on_transfer_failed(handle, status_code, reason)
2259                        .await;
2260                }
2261
2262                Event::TransferTargetAnswered {
2263                    transfer_call_id,
2264                    target_uri,
2265                    evidence,
2266                } => {
2267                    let handle = SessionHandle::new(transfer_call_id, coordinator);
2268                    handler
2269                        .on_transfer_target_answered(handle, target_uri, evidence)
2270                        .await;
2271                }
2272
2273                Event::TransferReplacementDialogObserved {
2274                    transfer_call_id,
2275                    dialog,
2276                } => {
2277                    let handle = SessionHandle::new(transfer_call_id, coordinator);
2278                    handler
2279                        .on_transfer_replacement_dialog_observed(handle, dialog)
2280                        .await;
2281                }
2282
2283                Event::TransferReplacementDialogTerminated {
2284                    transfer_call_id,
2285                    dialog,
2286                    reason,
2287                } => {
2288                    let handle = SessionHandle::new(transfer_call_id, coordinator);
2289                    handler
2290                        .on_transfer_replacement_dialog_terminated(handle, dialog, reason)
2291                        .await;
2292                }
2293
2294                Event::DialogPackageNotify {
2295                    subscription_id,
2296                    entity,
2297                    version,
2298                    dialogs,
2299                    document,
2300                } => {
2301                    handler
2302                        .on_dialog_package_notify(subscription_id, entity, version, dialogs, document)
2303                        .await;
2304                }
2305
2306                Event::DialogStateChanged {
2307                    subscription_id,
2308                    dialog,
2309                } => {
2310                    handler
2311                        .on_dialog_state_changed(subscription_id, dialog)
2312                        .await;
2313                }
2314
2315                Event::NotifyReceived {
2316                    call_id: _,
2317                    event_package: _,
2318                    subscription_state: _,
2319                    content_type: _,
2320                    body: _,
2321                    request,
2322                } => {
2323                    // SIP_API_DESIGN_2 Phase E §9.5 — typed
2324                    // `on_notify_received(IncomingRequest)` hook only.
2325                    // Applications inspect headers/body via the
2326                    // `IncomingRequest` directly.
2327                    if let Some(mut req) = request {
2328                        req.set_coordinator(coordinator.clone());
2329                        handler.on_notify_received(req).await;
2330                    }
2331                }
2332
2333                Event::RegistrationSuccess {
2334                    registrar,
2335                    expires,
2336                    contact,
2337                } => {
2338                    handler
2339                        .on_registration_success(registrar, expires, contact)
2340                        .await;
2341                }
2342
2343                Event::RegistrationFailed {
2344                    registrar,
2345                    status_code,
2346                    reason,
2347                } => {
2348                    handler
2349                        .on_registration_failed(registrar, status_code, reason)
2350                        .await;
2351                }
2352
2353                Event::UnregistrationSuccess { registrar } => {
2354                    handler.on_unregistration_success(registrar).await;
2355                }
2356
2357                Event::UnregistrationFailed { registrar, reason } => {
2358                    handler.on_unregistration_failed(registrar, reason).await;
2359                }
2360
2361                Event::SipTrace(trace) => {
2362                    handler.on_sip_trace(trace).await;
2363                }
2364
2365                Event::CallAuthRetrying {
2366                    call_id,
2367                    status_code,
2368                    realm,
2369                } => {
2370                    handler.on_auth_retrying(call_id, status_code, realm).await;
2371                }
2372
2373                Event::SessionRefreshed { .. }
2374                | Event::SessionRefreshFailed { .. }
2375                | Event::CallMuted { .. }
2376                | Event::CallUnmuted { .. }
2377                | Event::MediaQualityChanged { .. }
2378                | Event::NetworkError { .. }
2379                | Event::AuthenticationRequired { .. }
2380                // SIP_API_DESIGN_2 Phase A: detailed-response events are
2381                // an additive surface alongside the legacy `CallProgress`
2382                // / `CallEnded` / `CallFailed` variants. The callback
2383                // surface routes the existing variants today; consumers
2384                // can also pattern-match on the detailed variant via
2385                // `handler.on_event(...)`.
2386                | Event::CallProgressDetailed(_)
2387                | Event::CallEstablishedDetailed(_)
2388                | Event::CallFailedDetailed(_) => {}
2389                // SIP_API_DESIGN_2 Phase E: typed mid-dialog inbound
2390                // events. Rehydrate the coordinator hook on the
2391                // IncomingRequest before forwarding to the handler so
2392                // any *_builder() it calls can dispatch.
2393                Event::InfoReceived { call_id: _, mut request } => {
2394                    request.set_coordinator(coordinator.clone());
2395                    handler.on_info_received(request).await;
2396                }
2397                Event::MessageReceived { call_id: _, mut request } => {
2398                    request.set_coordinator(coordinator.clone());
2399                    handler.on_message_received(request).await;
2400                }
2401                Event::OptionsReceived { call_id: _, mut request } => {
2402                    request.set_coordinator(coordinator.clone());
2403                    handler.on_options_received(request).await;
2404                }
2405                Event::UpdateReceived { call_id: _, mut request } => {
2406                    request.set_coordinator(coordinator.clone());
2407                    handler.on_update_received(request).await;
2408                }
2409                Event::IncomingRegister { mut register } => {
2410                    register.set_coordinator(coordinator.clone());
2411                    handler.on_register_received(register).await;
2412                }
2413            }
2414            dispatch_guard.finish_success();
2415        });
2416    }
2417}
2418
2419fn reap_ready_handlers(handlers: &mut tokio::task::JoinSet<()>, context: &str) {
2420    while let Some(join_result) = handlers.try_join_next() {
2421        if let Err(e) = join_result {
2422            if !e.is_cancelled() {
2423                tracing::warn!("[CallbackPeer] Handler task panicked or errored ({context}): {e}");
2424            }
2425        }
2426    }
2427}
2428
2429fn callback_stage_for_event(event: &Event) -> CleanupStage {
2430    match event {
2431        Event::IncomingCall { .. } => CleanupStage::CallbackIncomingDispatch,
2432        _ => CleanupStage::CallbackEventDispatch,
2433    }
2434}
2435
2436fn callback_label_for_event(event: &Event) -> String {
2437    event
2438        .call_id()
2439        .map(|call_id| call_id.to_string())
2440        .unwrap_or_else(|| "-".to_string())
2441}
2442
2443// ===== Convenience constructors using built-in handlers =====
2444
2445use crate::api::handlers::{AutoAnswerHandler, RejectAllHandler};
2446
2447impl CallbackPeer<AutoAnswerHandler> {
2448    /// Create a peer that auto-answers all incoming calls and allows transfers.
2449    ///
2450    /// # Examples
2451    ///
2452    /// ```rust,no_run
2453    /// # async fn example() -> rvoip_sip::Result<()> {
2454    /// let peer = rvoip_sip::CallbackPeer::with_auto_answer(
2455    ///     rvoip_sip::Config::default(),
2456    /// ).await?;
2457    /// let stop = peer.shutdown_handle();
2458    /// tokio::spawn(async move { peer.run().await });
2459    /// stop.shutdown();
2460    /// # Ok(())
2461    /// # }
2462    /// ```
2463    pub async fn with_auto_answer(config: Config) -> Result<Self> {
2464        Self::new(AutoAnswerHandler, config).await
2465    }
2466}
2467
2468// ===== ClosureHandler — use a closure instead of a full trait impl =====
2469
2470/// A [`CallHandler`] that delegates `on_incoming_call` to a closure.
2471///
2472/// Created by [`CallbackPeer::from_fn()`]. The closure receives a borrowed
2473/// [`IncomingCall`] for inspection and returns a [`CallHandlerDecision`].
2474/// The peer applies that decision after the closure returns. Use
2475/// [`CallbackPeer::builder`] for async closure hooks, DTMF, ended, transfer,
2476/// or defer flows that need to own the [`IncomingCall`].
2477pub struct ClosureHandler {
2478    f: Box<dyn Fn(&IncomingCall) -> CallHandlerDecision + Send + Sync>,
2479}
2480
2481#[async_trait]
2482impl CallHandler for ClosureHandler {
2483    async fn on_incoming_call(&self, call: IncomingCall) -> CallHandlerDecision {
2484        match (self.f)(&call) {
2485            CallHandlerDecision::Defer(_) => {
2486                // Defer is not supported for closure handlers because a
2487                // captured IncomingCallGuard can't escape the &IncomingCall
2488                // closure signature. Use a trait impl for queue patterns.
2489                tracing::warn!("[ClosureHandler] Defer decision not supported; rejecting");
2490                call.reject(503, "Service Unavailable");
2491                CallHandlerDecision::Reject {
2492                    status: 503,
2493                    reason: "Service Unavailable".to_string(),
2494                }
2495            }
2496            decision => decision,
2497        }
2498    }
2499}
2500
2501impl CallbackPeer<ClosureHandler> {
2502    /// Create a peer with a closure for handling incoming calls.
2503    ///
2504    /// The closure receives a `&IncomingCall` (borrowed) and returns a
2505    /// [`CallHandlerDecision`]. The peer applies the decision automatically.
2506    ///
2507    /// # Example
2508    ///
2509    /// ```rust,no_run
2510    /// # async fn example() -> anyhow::Result<()> {
2511    /// use rvoip_sip::{CallbackPeer, CallHandlerDecision, Config};
2512    ///
2513    /// let peer = CallbackPeer::from_fn(Config::default(), |call| {
2514    ///     println!("Call from {}", call.from);
2515    ///     CallHandlerDecision::Accept
2516    /// }).await?;
2517    ///
2518    /// peer.run().await?;
2519    /// # Ok(())
2520    /// # }
2521    /// ```
2522    pub async fn from_fn(
2523        config: Config,
2524        handler: impl Fn(&IncomingCall) -> CallHandlerDecision + Send + Sync + 'static,
2525    ) -> Result<Self> {
2526        Self::new(
2527            ClosureHandler {
2528                f: Box::new(handler),
2529            },
2530            config,
2531        )
2532        .await
2533    }
2534}
2535
2536impl CallbackPeer<RejectAllHandler> {
2537    /// Create a peer that rejects all incoming calls with `486 Busy Here`.
2538    ///
2539    /// # Examples
2540    ///
2541    /// ```rust,no_run
2542    /// # async fn example() -> rvoip_sip::Result<()> {
2543    /// let peer = rvoip_sip::CallbackPeer::with_reject_all(
2544    ///     rvoip_sip::Config::default(),
2545    /// ).await?;
2546    /// let stop = peer.shutdown_handle();
2547    /// tokio::spawn(async move { peer.run().await });
2548    /// stop.shutdown();
2549    /// # Ok(())
2550    /// # }
2551    /// ```
2552    pub async fn with_reject_all(config: Config) -> Result<Self> {
2553        Self::new(RejectAllHandler::default(), config).await
2554    }
2555
2556    /// Create a peer that rejects all calls with a custom status and reason.
2557    ///
2558    /// # Examples
2559    ///
2560    /// ```rust,no_run
2561    /// # async fn example() -> rvoip_sip::Result<()> {
2562    /// let peer = rvoip_sip::CallbackPeer::with_reject(
2563    ///     rvoip_sip::Config::default(),
2564    ///     603,
2565    ///     "Decline",
2566    /// ).await?;
2567    /// # let _ = peer;
2568    /// # Ok(())
2569    /// # }
2570    /// ```
2571    pub async fn with_reject(
2572        config: Config,
2573        status: u16,
2574        reason: impl Into<String>,
2575    ) -> Result<Self> {
2576        Self::new(RejectAllHandler::new(status, reason), config).await
2577    }
2578}
2579
2580#[cfg(test)]
2581mod tests {
2582    use super::*;
2583    use crate::api::events::{MediaSecurityKeying, MediaSecurityProfile};
2584    use crate::state_table::types::SessionId;
2585    use rvoip_sip_core::types::sdp::CryptoSuite;
2586    use std::sync::Mutex;
2587    use tokio::task::JoinSet;
2588
2589    #[test]
2590    fn callback_peer_builder_exposes_rtp_media_buffer_tuning() {
2591        let session_buffers = RtpSessionBufferConfig {
2592            sender_channel_capacity: 9,
2593            receiver_channel_capacity: 6,
2594            event_channel_capacity: 18,
2595        };
2596        let transport_buffers = RtpTransportBufferConfig {
2597            event_channel_capacity: 14,
2598            recv_buffer_size: 2048,
2599            rtcp_recv_buffer_size: 1024,
2600        };
2601        let mut media_config = MediaSessionControllerConfig::default();
2602        media_config.rtp_buffer_size = 960;
2603        media_config.rtp_buffer_initial_count = 5;
2604        media_config.rtp_buffer_max_count = 20;
2605
2606        let builder = CallbackPeer::builder(Config::local("callback-builder", 15444))
2607            .media_session_controller_config(media_config)
2608            .rtp_session_buffer_config(session_buffers)
2609            .rtp_transport_buffer_config(transport_buffers);
2610
2611        assert_eq!(builder.config.rtp_session_buffer_config, session_buffers);
2612        assert_eq!(
2613            builder.config.rtp_transport_buffer_config,
2614            transport_buffers
2615        );
2616        assert_eq!(
2617            builder
2618                .config
2619                .media_session_controller_config
2620                .rtp_buffer_size,
2621            960
2622        );
2623        assert_eq!(
2624            builder
2625                .config
2626                .media_session_controller_config
2627                .rtp_buffer_initial_count,
2628            5
2629        );
2630        assert_eq!(
2631            builder
2632                .config
2633                .media_session_controller_config
2634                .rtp_buffer_max_count,
2635            20
2636        );
2637    }
2638
2639    #[test]
2640    fn bounded_terminal_callback_dedupe_evicts_oldest_entries() {
2641        let mut dedupe = BoundedCallDedupe::with_capacity(2);
2642        assert!(dedupe.insert(SessionId("call-1".to_string())));
2643        assert!(!dedupe.insert(SessionId("call-1".to_string())));
2644        assert!(dedupe.insert(SessionId("call-2".to_string())));
2645        assert_eq!(dedupe.len(), 2);
2646        assert!(dedupe.insert(SessionId("call-3".to_string())));
2647        assert_eq!(dedupe.len(), 2);
2648        assert!(dedupe.insert(SessionId("call-1".to_string())));
2649    }
2650
2651    #[derive(Default)]
2652    struct RecordingHandler {
2653        events: Arc<Mutex<Vec<String>>>,
2654    }
2655
2656    impl RecordingHandler {
2657        fn push(&self, value: impl Into<String>) {
2658            self.events.lock().unwrap().push(value.into());
2659        }
2660    }
2661
2662    #[async_trait]
2663    impl CallHandler for RecordingHandler {
2664        async fn on_event(&self, _event: Event) {
2665            self.push("event");
2666        }
2667
2668        async fn on_incoming_call(&self, call: IncomingCall) -> CallHandlerDecision {
2669            self.push("incoming");
2670            CallHandlerDecision::Defer(call.defer(std::time::Duration::from_millis(10)))
2671        }
2672
2673        async fn on_call_established(&self, _handle: SessionHandle) {
2674            self.push("answered");
2675        }
2676
2677        async fn on_call_progress(
2678            &self,
2679            _handle: SessionHandle,
2680            status_code: u16,
2681            _reason: String,
2682            _sdp: Option<String>,
2683        ) {
2684            self.push(format!("progress:{status_code}"));
2685        }
2686
2687        async fn on_call_ended(&self, _call_id: CallId, _reason: EndReason) {
2688            self.push("ended");
2689        }
2690
2691        async fn on_call_failed(&self, _call_id: CallId, status_code: u16, _reason: String) {
2692            self.push(format!("failed:{status_code}"));
2693        }
2694
2695        async fn on_call_cancelled(&self, _call_id: CallId) {
2696            self.push("cancelled");
2697        }
2698
2699        async fn on_dtmf(&self, _handle: SessionHandle, digit: char) {
2700            self.push(format!("dtmf:{digit}"));
2701        }
2702
2703        async fn on_media_security_negotiated(
2704            &self,
2705            _handle: SessionHandle,
2706            state: MediaSecurityState,
2707        ) {
2708            self.push(format!("media-security:{}", state.contexts_installed));
2709        }
2710
2711        async fn on_call_on_hold(&self, _handle: SessionHandle) {
2712            self.push("hold");
2713        }
2714
2715        async fn on_call_resumed(&self, _handle: SessionHandle) {
2716            self.push("resume");
2717        }
2718
2719        async fn on_remote_call_on_hold(&self, _handle: SessionHandle) {
2720            self.push("remote-hold");
2721        }
2722
2723        async fn on_remote_call_resumed(&self, _handle: SessionHandle) {
2724            self.push("remote-resume");
2725        }
2726
2727        async fn on_transfer_accepted(&self, _handle: SessionHandle, _refer_to: String) {
2728            self.push("transfer-accepted");
2729        }
2730
2731        async fn on_refer_notify(
2732            &self,
2733            _handle: SessionHandle,
2734            status_code: u16,
2735            _reason: String,
2736            _subscription_state: Option<SubscriptionState>,
2737            _body: Option<String>,
2738        ) {
2739            self.push(format!("refer-notify:{status_code}"));
2740        }
2741
2742        async fn on_refer_progress(
2743            &self,
2744            _handle: SessionHandle,
2745            status_code: u16,
2746            _reason: String,
2747        ) {
2748            self.push(format!("refer-progress:{status_code}"));
2749        }
2750
2751        async fn on_refer_completed(
2752            &self,
2753            _handle: SessionHandle,
2754            _target: String,
2755            status_code: u16,
2756            _reason: String,
2757        ) {
2758            self.push(format!("refer-completed:{status_code}"));
2759        }
2760
2761        async fn on_transfer_failed(
2762            &self,
2763            _handle: SessionHandle,
2764            status_code: u16,
2765            _reason: String,
2766        ) {
2767            self.push(format!("transfer-failed:{status_code}"));
2768        }
2769
2770        async fn on_registration_success(
2771            &self,
2772            _registrar: String,
2773            _expires: u32,
2774            _contact: String,
2775        ) {
2776            self.push("registration-success");
2777        }
2778
2779        async fn on_registration_failed(
2780            &self,
2781            _registrar: String,
2782            status_code: u16,
2783            _reason: String,
2784        ) {
2785            self.push(format!("registration-failed:{status_code}"));
2786        }
2787
2788        async fn on_unregistration_success(&self, _registrar: String) {
2789            self.push("unregistration-success");
2790        }
2791
2792        async fn on_unregistration_failed(&self, _registrar: String, _reason: String) {
2793            self.push("unregistration-failed");
2794        }
2795    }
2796
2797    async fn drain(mut handlers: JoinSet<()>) {
2798        while let Some(result) = handlers.join_next().await {
2799            result.unwrap();
2800        }
2801    }
2802
2803    #[tokio::test]
2804    async fn callback_dispatch_invokes_typed_hooks_for_public_events() {
2805        let seen = Arc::new(Mutex::new(Vec::new()));
2806        let handler = RecordingHandler {
2807            events: seen.clone(),
2808        };
2809        let peer = CallbackPeer::new(handler, Config::local("callback-test", 15440))
2810            .await
2811            .unwrap();
2812        let mut handlers = JoinSet::new();
2813        let call_id = SessionId::new();
2814        let failed_call_id = SessionId::new();
2815        let cancelled_call_id = SessionId::new();
2816
2817        let events = vec![
2818            Event::IncomingCall {
2819                call_id: call_id.clone(),
2820                from: "sip:a@example.test".into(),
2821                to: "sip:b@example.test".into(),
2822                sdp: None,
2823            },
2824            Event::CallAnswered {
2825                call_id: call_id.clone(),
2826                sdp: None,
2827            },
2828            Event::CallAnswered {
2829                call_id: call_id.clone(),
2830                sdp: None,
2831            },
2832            Event::CallProgress {
2833                call_id: call_id.clone(),
2834                status_code: 180,
2835                reason: "Ringing".into(),
2836                sdp: None,
2837            },
2838            Event::CallEnded {
2839                call_id: call_id.clone(),
2840                reason: "normal".into(),
2841            },
2842            Event::CallFailed {
2843                call_id: failed_call_id.clone(),
2844                status_code: 486,
2845                reason: "Busy Here".into(),
2846            },
2847            Event::CallCancelled {
2848                call_id: cancelled_call_id.clone(),
2849            },
2850            Event::CallCancelled {
2851                call_id: cancelled_call_id.clone(),
2852            },
2853            Event::CallOnHold {
2854                call_id: call_id.clone(),
2855            },
2856            Event::CallResumed {
2857                call_id: call_id.clone(),
2858            },
2859            Event::RemoteCallOnHold {
2860                call_id: call_id.clone(),
2861            },
2862            Event::RemoteCallResumed {
2863                call_id: call_id.clone(),
2864            },
2865            Event::DtmfReceived {
2866                call_id: call_id.clone(),
2867                digit: '5',
2868            },
2869            Event::MediaSecurityNegotiated {
2870                call_id: call_id.clone(),
2871                keying: MediaSecurityKeying::Sdes,
2872                suite: CryptoSuite::AesCm128HmacSha1_80,
2873                profile: MediaSecurityProfile::RtpSavp,
2874                contexts_installed: true,
2875            },
2876            Event::ReferReceived {
2877                call_id: call_id.clone(),
2878                refer_to: "sip:c@example.test".into(),
2879                referred_by: None,
2880                replaces: None,
2881                transaction_id: "tx-1".into(),
2882                transfer_type: "blind".into(),
2883                request: None,
2884            },
2885            Event::TransferAccepted {
2886                call_id: call_id.clone(),
2887                refer_to: "sip:c@example.test".into(),
2888            },
2889            Event::ReferNotify {
2890                call_id: call_id.clone(),
2891                status_code: 100,
2892                reason: "Trying".into(),
2893                subscription_state: Some(SubscriptionState::parse("active;expires=60")),
2894                body: Some("SIP/2.0 100 Trying".into()),
2895            },
2896            Event::ReferProgress {
2897                call_id: call_id.clone(),
2898                status_code: 180,
2899                reason: "Ringing".into(),
2900            },
2901            Event::ReferCompleted {
2902                call_id: call_id.clone(),
2903                target: "sip:c@example.test".into(),
2904                status_code: 200,
2905                reason: "OK".into(),
2906            },
2907            Event::TransferFailed {
2908                call_id: call_id.clone(),
2909                status_code: 503,
2910                reason: "Service Unavailable".into(),
2911            },
2912            Event::NotifyReceived {
2913                call_id: call_id.clone(),
2914                event_package: "refer".into(),
2915                subscription_state: Some("active".into()),
2916                content_type: Some("message/sipfrag".into()),
2917                body: Some("SIP/2.0 100 Trying".into()),
2918                request: None,
2919            },
2920            Event::RegistrationSuccess {
2921                registrar: "sip:registrar.example.test".into(),
2922                expires: 300,
2923                contact: "sip:callback-test@example.test".into(),
2924            },
2925            Event::RegistrationFailed {
2926                registrar: "sip:registrar.example.test".into(),
2927                status_code: 403,
2928                reason: "Forbidden".into(),
2929            },
2930            Event::UnregistrationSuccess {
2931                registrar: "sip:registrar.example.test".into(),
2932            },
2933            Event::UnregistrationFailed {
2934                registrar: "sip:registrar.example.test".into(),
2935                reason: "timeout".into(),
2936            },
2937        ];
2938
2939        for event in events {
2940            peer.dispatch(event, &mut handlers).await;
2941        }
2942        drain(handlers).await;
2943
2944        let seen = seen.lock().unwrap().clone();
2945        for expected in [
2946            "incoming",
2947            "answered",
2948            "progress:180",
2949            "ended",
2950            "failed:486",
2951            "cancelled",
2952            "hold",
2953            "resume",
2954            "remote-hold",
2955            "remote-resume",
2956            "dtmf:5",
2957            "media-security:true",
2958            "transfer-accepted",
2959            "refer-notify:100",
2960            "refer-progress:180",
2961            "refer-completed:200",
2962            "transfer-failed:503",
2963            "registration-success",
2964            "registration-failed:403",
2965            "unregistration-success",
2966            "unregistration-failed",
2967        ] {
2968            assert!(
2969                seen.iter().any(|value| value == expected),
2970                "missing {expected}"
2971            );
2972        }
2973        assert_eq!(
2974            seen.iter()
2975                .filter(|value| value.as_str() == "event")
2976                .count(),
2977            25
2978        );
2979        assert_eq!(
2980            seen.iter()
2981                .filter(|value| value.as_str() == "answered")
2982                .count(),
2983            1
2984        );
2985        assert_eq!(
2986            seen.iter()
2987                .filter(|value| value.as_str() == "cancelled")
2988                .count(),
2989            1
2990        );
2991    }
2992
2993    #[tokio::test]
2994    async fn callback_builder_invokes_common_closure_hooks() {
2995        let seen = Arc::new(Mutex::new(Vec::new()));
2996        let peer = CallbackPeer::builder(Config::local("callback-builder", 15441))
2997            .on_incoming({
2998                let seen = seen.clone();
2999                move |_call| {
3000                    let seen = seen.clone();
3001                    async move {
3002                        seen.lock().unwrap().push("incoming".to_string());
3003                        CallHandlerDecision::Reject {
3004                            status: 486,
3005                            reason: "Busy Here".into(),
3006                        }
3007                    }
3008                }
3009            })
3010            .on_established({
3011                let seen = seen.clone();
3012                move |_handle| {
3013                    let seen = seen.clone();
3014                    async move {
3015                        seen.lock().unwrap().push("established".to_string());
3016                        Ok(())
3017                    }
3018                }
3019            })
3020            .on_dtmf({
3021                let seen = seen.clone();
3022                move |_handle, digit| {
3023                    let seen = seen.clone();
3024                    async move {
3025                        seen.lock().unwrap().push(format!("dtmf:{digit}"));
3026                        Ok(())
3027                    }
3028                }
3029            })
3030            .on_ended({
3031                let seen = seen.clone();
3032                move |_call_id, _reason| {
3033                    let seen = seen.clone();
3034                    async move {
3035                        seen.lock().unwrap().push("ended".to_string());
3036                        Ok(())
3037                    }
3038                }
3039            })
3040            .build()
3041            .await
3042            .unwrap();
3043
3044        let mut handlers = JoinSet::new();
3045        let call_id = SessionId::new();
3046        for event in [
3047            Event::IncomingCall {
3048                call_id: call_id.clone(),
3049                from: "sip:a@example.test".into(),
3050                to: "sip:b@example.test".into(),
3051                sdp: None,
3052            },
3053            Event::CallAnswered {
3054                call_id: call_id.clone(),
3055                sdp: None,
3056            },
3057            Event::DtmfReceived {
3058                call_id: call_id.clone(),
3059                digit: '7',
3060            },
3061            Event::ReferReceived {
3062                call_id: call_id.clone(),
3063                refer_to: "sip:c@example.test".into(),
3064                referred_by: None,
3065                replaces: None,
3066                transaction_id: "tx-builder".into(),
3067                transfer_type: "blind".into(),
3068                request: None,
3069            },
3070            Event::CallEnded {
3071                call_id,
3072                reason: "normal".into(),
3073            },
3074        ] {
3075            peer.dispatch(event, &mut handlers).await;
3076        }
3077        drain(handlers).await;
3078
3079        let seen = seen.lock().unwrap().clone();
3080        for expected in ["incoming", "established", "dtmf:7", "ended"] {
3081            assert!(
3082                seen.iter().any(|value| value == expected),
3083                "missing {expected}; saw {seen:?}"
3084            );
3085        }
3086    }
3087
3088    #[tokio::test]
3089    async fn callback_builder_requires_incoming_hook() {
3090        let result = CallbackPeer::builder(Config::local("callback-builder-missing", 15443))
3091            .build()
3092            .await;
3093        let err = match result {
3094            Ok(_) => panic!("builder without on_incoming should fail"),
3095            Err(err) => err,
3096        };
3097        assert!(err.to_string().contains("on_incoming"));
3098    }
3099
3100    #[tokio::test]
3101    async fn callback_control_can_initiate_calls_while_peer_can_be_moved_to_run() {
3102        struct NoopHandler;
3103        #[async_trait]
3104        impl CallHandler for NoopHandler {
3105            async fn on_incoming_call(&self, _call: IncomingCall) -> CallHandlerDecision {
3106                CallHandlerDecision::Reject {
3107                    status: 486,
3108                    reason: "Busy Here".into(),
3109                }
3110            }
3111        }
3112
3113        let peer = CallbackPeer::new(NoopHandler, Config::local("callback-control", 15442))
3114            .await
3115            .unwrap();
3116        let control = peer.control();
3117        let stop = peer.shutdown_handle();
3118        let run_task = tokio::spawn(async move { peer.run().await });
3119
3120        let call_id = control
3121            .invite("sip:unreachable@127.0.0.1:15443")
3122            .send()
3123            .await
3124            .unwrap();
3125        assert!(!call_id.to_string().is_empty());
3126
3127        control.shutdown();
3128        stop.shutdown();
3129        tokio::time::timeout(std::time::Duration::from_secs(2), run_task)
3130            .await
3131            .unwrap()
3132            .unwrap()
3133            .unwrap();
3134    }
3135}