1use std::collections::HashMap;
7use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
8use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
9use std::time::Duration;
10
11use bytes::Bytes;
12use sipx_sip::build::ResponseBuilder;
13use sipx_sip::{HeaderName, Response, StatusCode};
14use sipx_transport::{Handle, Incoming, Target, TransportKind};
15use sipx_ua::event_client::{
16 Config, EventClient, NotificationMeta, Output, PackageConsumer, Peer, Start, StartError,
17 StateChange, SubscriptionId, Timer, Transport,
18};
19use thiserror::Error;
20use tokio::sync::mpsc;
21use tokio::task::JoinHandle;
22use tokio_util::sync::CancellationToken;
23
24const DRIVER_QUEUE: usize = 64;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct EventSubscriptionCounts {
29 pub active_tasks: usize,
31 pub active_timers: usize,
33 pub active_transactions: usize,
35 pub started_tasks: u64,
37 pub finished_tasks: u64,
39 pub shed: u64,
41}
42
43#[derive(Debug, Default)]
44struct Counters {
45 tasks: AtomicUsize,
46 timers: AtomicUsize,
47 transactions: AtomicUsize,
48 started: AtomicU64,
49 finished: AtomicU64,
50 shed: AtomicU64,
51}
52
53#[derive(Debug, Error)]
55#[non_exhaustive]
56pub enum EventSubscriptionError {
57 #[error("event subscriptions are not attached to a dispatcher")]
59 NotAttached,
60 #[error("event subscription shutdown has closed admission")]
62 ShuttingDown,
63 #[error("event subscription capacity exceeded")]
65 CapacityExceeded,
66 #[error("an event subscription already owns this Call-ID")]
68 DuplicateIdentity,
69 #[error(transparent)]
71 Start(#[from] StartError),
72}
73
74#[derive(Debug)]
76pub struct EventNotification<V> {
77 pub received_at: tokio::time::Instant,
79 pub metadata: Option<NotificationMeta>,
81 pub value: V,
83}
84
85#[derive(Debug)]
87pub enum EventSubscriptionEvent<V> {
88 Notification(EventNotification<V>),
90 State(StateChange),
92}
93
94#[derive(Debug)]
96pub struct EventSubscription<V> {
97 id: SubscriptionId,
98 deliveries: mpsc::Receiver<EventNotification<V>>,
99 states: mpsc::Receiver<StateChange>,
100 commands: mpsc::Sender<Command>,
101}
102
103impl<V> EventSubscription<V> {
104 #[must_use]
106 pub fn id(&self) -> SubscriptionId {
107 self.id
108 }
109
110 pub async fn recv(&mut self) -> Option<EventNotification<V>> {
112 let delivery = self.deliveries.recv().await?;
113 let _ = self.commands.try_send(Command::Drained(1));
115 Some(delivery)
116 }
117
118 pub async fn next_state(&mut self) -> Option<StateChange> {
120 self.states.recv().await
121 }
122
123 pub async fn next_event(&mut self) -> Option<EventSubscriptionEvent<V>> {
129 tokio::select! {
130 Some(delivery) = self.deliveries.recv() => {
131 let _ = self.commands.try_send(Command::Drained(1));
133 Some(EventSubscriptionEvent::Notification(delivery))
134 }
135 Some(change) = self.states.recv() => Some(EventSubscriptionEvent::State(change)),
136 else => None,
137 }
138 }
139
140 pub async fn unsubscribe(&self) -> Result<(), EventSubscriptionError> {
143 self.commands
144 .send(Command::Unsubscribe)
145 .await
146 .map_err(|_| EventSubscriptionError::NotAttached)
147 }
148}
149
150impl<V> Drop for EventSubscription<V> {
151 fn drop(&mut self) {
152 let _ = self.commands.try_send(Command::Unsubscribe);
154 }
155}
156
157#[derive(Debug)]
158struct Shared {
159 endpoint: Mutex<Option<Handle>>,
160 routes: Mutex<HashMap<Vec<u8>, mpsc::Sender<Incoming>>>,
161 drivers: Mutex<HashMap<Vec<u8>, JoinHandle<()>>>,
162 config: Config,
163 counters: Arc<Counters>,
164 shutdown: CancellationToken,
165}
166
167#[derive(Debug, Clone)]
169pub struct EventSubscriptionsHandle {
170 shared: Arc<Shared>,
171}
172
173impl EventSubscriptionsHandle {
174 pub fn subscribe<C: PackageConsumer>(
176 &self,
177 start: Start<C>,
178 ) -> Result<EventSubscription<C::Value>, EventSubscriptionError> {
179 let call_id = start.call_id.as_bytes().to_vec();
180 let mut drivers = lock(&self.shared.drivers);
181 drivers.retain(|_, task| !task.is_finished());
182 if self.shared.shutdown.is_cancelled() {
183 return Err(EventSubscriptionError::ShuttingDown);
184 }
185 if drivers.contains_key(&call_id) {
186 return Err(EventSubscriptionError::DuplicateIdentity);
187 }
188 let endpoint = lock(&self.shared.endpoint)
189 .clone()
190 .ok_or(EventSubscriptionError::NotAttached)?;
191 reserve(&self.shared)?;
192 let mut core = match EventClient::new(self.shared.config.clone()) {
193 Ok(core) => core,
194 Err(error) => {
195 release(&self.shared.counters);
196 return Err(error.into());
197 }
198 };
199 let (id, initial) = match core.start(start) {
200 Ok(started) => started,
201 Err(error) => {
202 release(&self.shared.counters);
203 return Err(error.into());
204 }
205 };
206 let (incoming_tx, incoming_rx) = mpsc::channel(DRIVER_QUEUE);
207 let mut routes = lock(&self.shared.routes);
208 if routes.contains_key(&call_id) {
209 release(&self.shared.counters);
210 return Err(EventSubscriptionError::DuplicateIdentity);
211 }
212 routes.insert(call_id.clone(), incoming_tx);
213 drop(routes);
214 let (delivery_tx, deliveries) = mpsc::channel(self.shared.config.delivery_capacity);
215 let (state_tx, states) = mpsc::channel(DRIVER_QUEUE);
216 let (command_tx, command_rx) = mpsc::channel(DRIVER_QUEUE);
217 let driver = Driver {
218 id,
219 call_id: call_id.clone(),
220 endpoint,
221 core,
222 incoming: incoming_rx,
223 commands: command_rx,
224 delivery: delivery_tx,
225 states: state_tx,
226 events: None,
227 response: None,
228 timers: HashMap::new(),
229 shared: Arc::clone(&self.shared),
230 };
231 self.shared.counters.started.fetch_add(1, Ordering::Relaxed);
232 let task = tokio::spawn(driver.run(initial));
233 drivers.insert(call_id, task);
234 drop(drivers);
235 Ok(EventSubscription {
236 id,
237 deliveries,
238 states,
239 commands: command_tx,
240 })
241 }
242
243 #[must_use]
245 pub fn counts(&self) -> EventSubscriptionCounts {
246 EventSubscriptionCounts {
247 active_tasks: self.shared.counters.tasks.load(Ordering::Relaxed),
248 active_timers: self.shared.counters.timers.load(Ordering::Relaxed),
249 active_transactions: self.shared.counters.transactions.load(Ordering::Relaxed),
250 started_tasks: self.shared.counters.started.load(Ordering::Relaxed),
251 finished_tasks: self.shared.counters.finished.load(Ordering::Relaxed),
252 shed: self.shared.counters.shed.load(Ordering::Relaxed),
253 }
254 }
255}
256
257#[derive(Debug)]
259pub struct EventSubscriptions {
260 shared: Arc<Shared>,
261}
262
263impl EventSubscriptions {
264 pub fn new(config: Config) -> Result<Self, EventSubscriptionError> {
267 config.validate()?;
268 Ok(Self {
269 shared: Arc::new(Shared {
270 endpoint: Mutex::new(None),
271 routes: Mutex::new(HashMap::new()),
272 drivers: Mutex::new(HashMap::new()),
273 config,
274 counters: Arc::new(Counters::default()),
275 shutdown: CancellationToken::new(),
276 }),
277 })
278 }
279
280 #[must_use]
282 pub fn handle(&self) -> EventSubscriptionsHandle {
283 EventSubscriptionsHandle {
284 shared: Arc::clone(&self.shared),
285 }
286 }
287
288 pub(crate) fn attach(&self, endpoint: Handle) {
289 *lock(&self.shared.endpoint) = Some(endpoint);
290 }
291
292 pub(crate) async fn receive(&self, incoming: &Incoming) -> bool {
294 let Some(call_id) = incoming.request.headers.value(&HeaderName::CallId) else {
295 return false;
296 };
297 let sender = lock(&self.shared.routes).get(call_id.as_ref()).cloned();
298 let Some(sender) = sender else {
299 return false;
300 };
301 let cloned = Incoming {
302 key: incoming.key.clone(),
303 request: incoming.request.clone(),
304 source: incoming.source,
305 transport: incoming.transport,
306 connection_generation: incoming.connection_generation,
307 };
308 match sender.try_send(cloned) {
309 Ok(()) => true,
310 Err(mpsc::error::TrySendError::Full(_)) => {
311 self.shared.counters.shed.fetch_add(1, Ordering::Relaxed);
312 let endpoint = lock(&self.shared.endpoint).clone();
313 answer_notify(
314 endpoint.as_ref(),
315 incoming,
316 503,
317 Some(Duration::from_secs(1)),
318 )
319 .await;
320 true
321 }
322 Err(mpsc::error::TrySendError::Closed(_)) => false,
323 }
324 }
325
326 pub(crate) async fn shutdown(&mut self) {
327 let drivers: Vec<_> = {
328 let mut drivers = lock(&self.shared.drivers);
329 self.shared.shutdown.cancel();
330 drivers.drain().map(|(_, task)| task).collect()
331 };
332 for task in drivers {
333 if let Err(error) = task.await {
334 tracing::warn!(%error, "event subscription driver did not join cleanly");
335 }
336 }
337 }
338}
339
340impl Drop for EventSubscriptions {
341 fn drop(&mut self) {
342 let _drivers = lock(&self.shared.drivers);
343 self.shared.shutdown.cancel();
344 }
345}
346
347#[derive(Debug)]
348enum Command {
349 Drained(usize),
350 Unsubscribe,
351}
352
353#[derive(Debug)]
354enum RuntimeEvent {
355 Response(Option<Response>),
356 Timer(Timer, u64),
357}
358
359struct Driver<C: PackageConsumer> {
360 id: SubscriptionId,
361 call_id: Vec<u8>,
362 endpoint: Handle,
363 core: EventClient<C>,
364 incoming: mpsc::Receiver<Incoming>,
365 commands: mpsc::Receiver<Command>,
366 delivery: mpsc::Sender<EventNotification<C::Value>>,
367 states: mpsc::Sender<StateChange>,
368 events: Option<(mpsc::Sender<RuntimeEvent>, mpsc::Receiver<RuntimeEvent>)>,
369 response: Option<JoinHandle<()>>,
370 timers: HashMap<Timer, JoinHandle<()>>,
371 shared: Arc<Shared>,
372}
373
374impl<C: PackageConsumer> Driver<C> {
375 async fn run(mut self, initial: Vec<Output<C::Value>>) {
376 let guard = WorkGuard::task(Arc::clone(&self.shared.counters));
377 let (event_tx, event_rx) = mpsc::channel(DRIVER_QUEUE);
378 self.events = Some((event_tx, event_rx));
379 self.apply(initial, None).await;
380 loop {
381 if !self.core.contains(self.id) {
382 break;
383 }
384 let event = {
385 let Some((_, events)) = self.events.as_mut() else {
386 break;
387 };
388 tokio::select! {
389 () = self.shared.shutdown.cancelled() => DriverInput::Shutdown,
390 incoming = self.incoming.recv() => incoming.map_or(DriverInput::Shutdown, |incoming| DriverInput::Incoming(Box::new(incoming))),
391 command = self.commands.recv() => DriverInput::Command(command),
392 event = events.recv() => DriverInput::Event(event),
393 () = self.delivery.closed() => DriverInput::Shutdown,
394 }
395 };
396 let (outputs, incoming) = match event {
397 DriverInput::Incoming(incoming) => {
398 let incoming = *incoming;
399 let source = peer_from_incoming(&incoming);
400 let outputs = self.core.notify(1, &incoming.request, source);
401 (outputs, Some(incoming))
402 }
403 DriverInput::Command(Some(Command::Drained(count))) => {
404 self.core.consumer_drained(self.id, count);
405 (Vec::new(), None)
406 }
407 DriverInput::Command(Some(Command::Unsubscribe)) => {
408 (self.core.unsubscribe(self.id), None)
409 }
410 DriverInput::Event(Some(RuntimeEvent::Response(response))) => (
411 self.core
412 .response(self.id, response.as_ref(), &sipx_ua::auth::new_cnonce()),
413 None,
414 ),
415 DriverInput::Event(Some(RuntimeEvent::Timer(timer, generation))) => {
416 (self.core.timer_fired(self.id, timer, generation), None)
417 }
418 DriverInput::Shutdown | DriverInput::Command(None) | DriverInput::Event(None) => {
419 (self.core.shutdown_deadline(), None)
420 }
421 };
422 self.apply(outputs, incoming.as_ref()).await;
423 }
424 if self.core.contains(self.id) {
425 let outputs = self.core.shutdown_deadline();
426 self.apply(outputs, None).await;
427 }
428 if let Some(response) = self.response.take() {
429 abort_and_join(response).await;
430 }
431 for (_, timer) in self.timers.drain() {
432 abort_and_join(timer).await;
433 }
434 lock(&self.shared.routes).remove(&self.call_id);
435 drop(guard);
436 }
437
438 async fn apply(&mut self, outputs: Vec<Output<C::Value>>, incoming: Option<&Incoming>) {
439 for output in outputs {
440 match output {
441 Output::SendSubscribe {
442 request, target, ..
443 } => self.send(*request, target).await,
444 Output::RespondNotify {
445 status,
446 retry_after,
447 ..
448 } => {
449 if let Some(incoming) = incoming {
450 answer_notify(Some(&self.endpoint), incoming, status, retry_after).await;
451 }
452 }
453 Output::Deliver {
454 metadata, value, ..
455 } => {
456 let _ = self.delivery.try_send(EventNotification {
458 received_at: tokio::time::Instant::now(),
459 metadata,
460 value,
461 });
462 }
463 Output::ArmTimer {
464 timer,
465 generation,
466 after,
467 ..
468 } => self.arm(timer, generation, after).await,
469 Output::CancelTimer { timer, .. } => {
470 if let Some(task) = self.timers.remove(&timer) {
471 abort_and_join(task).await;
472 }
473 }
474 Output::StateChanged { change, .. } => {
475 let _ = self.states.try_send(change);
477 }
478 Output::Stopped => {}
479 }
480 }
481 }
482
483 async fn send(&mut self, request: sipx_sip::Request, peer: Peer) {
484 if let Some(response) = self.response.take() {
485 abort_and_join(response).await;
486 }
487 let Some((events, _)) = self.events.as_ref() else {
488 return;
489 };
490 match self.endpoint.send(request, target(peer)).await {
491 Ok(mut responses) => {
492 self.core
493 .connection_selected(self.id, responses.connection_generation());
494 let events = events.clone();
495 let counters = Arc::clone(&self.shared.counters);
496 self.response = Some(tokio::spawn(async move {
497 let _guard = WorkGuard::transaction(counters);
498 let response = responses.final_response().await;
499 let _ = events.send(RuntimeEvent::Response(response)).await;
501 }));
502 }
503 Err(error) => {
504 tracing::warn!(%error, "could not send event SUBSCRIBE");
505 let _ = events.try_send(RuntimeEvent::Response(None));
507 }
508 }
509 }
510
511 async fn arm(&mut self, timer: Timer, generation: u64, after: Duration) {
512 if let Some(previous) = self.timers.remove(&timer) {
513 abort_and_join(previous).await;
514 }
515 let Some((events, _)) = self.events.as_ref() else {
516 return;
517 };
518 let events = events.clone();
519 let counters = Arc::clone(&self.shared.counters);
520 self.timers.insert(
521 timer,
522 tokio::spawn(async move {
523 let _guard = WorkGuard::timer(counters);
524 tokio::time::sleep(after).await;
526 let _ = events.send(RuntimeEvent::Timer(timer, generation)).await;
528 }),
529 );
530 }
531}
532
533async fn abort_and_join(task: JoinHandle<()>) {
534 task.abort();
535 let _ = task.await;
537}
538
539enum DriverInput {
540 Incoming(Box<Incoming>),
541 Command(Option<Command>),
542 Event(Option<RuntimeEvent>),
543 Shutdown,
544}
545
546enum WorkKind {
547 Task,
548 Timer,
549 Transaction,
550}
551
552struct WorkGuard {
553 counters: Arc<Counters>,
554 kind: WorkKind,
555}
556
557impl WorkGuard {
558 fn task(counters: Arc<Counters>) -> Self {
559 Self {
560 counters,
561 kind: WorkKind::Task,
562 }
563 }
564
565 fn timer(counters: Arc<Counters>) -> Self {
566 counters.timers.fetch_add(1, Ordering::Relaxed);
567 Self {
568 counters,
569 kind: WorkKind::Timer,
570 }
571 }
572
573 fn transaction(counters: Arc<Counters>) -> Self {
574 counters.transactions.fetch_add(1, Ordering::Relaxed);
575 Self {
576 counters,
577 kind: WorkKind::Transaction,
578 }
579 }
580}
581
582impl Drop for WorkGuard {
583 fn drop(&mut self) {
584 match self.kind {
585 WorkKind::Task => {
586 self.counters.tasks.fetch_sub(1, Ordering::Relaxed);
587 self.counters.finished.fetch_add(1, Ordering::Relaxed);
588 }
589 WorkKind::Timer => {
590 self.counters.timers.fetch_sub(1, Ordering::Relaxed);
591 }
592 WorkKind::Transaction => {
593 self.counters.transactions.fetch_sub(1, Ordering::Relaxed);
594 }
595 }
596 }
597}
598
599#[allow(deprecated)]
602fn reserve(shared: &Shared) -> Result<(), EventSubscriptionError> {
603 shared
604 .counters
605 .tasks
606 .fetch_update(Ordering::AcqRel, Ordering::Relaxed, |current| {
607 (current < shared.config.capacity).then_some(current.saturating_add(1))
608 })
609 .map_err(|_| EventSubscriptionError::CapacityExceeded)?;
610 Ok(())
611}
612
613fn release(counters: &Counters) {
614 counters.tasks.fetch_sub(1, Ordering::Relaxed);
615}
616
617fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
618 mutex.lock().unwrap_or_else(PoisonError::into_inner)
619}
620
621fn peer_from_incoming(incoming: &Incoming) -> Peer {
622 let mut peer = Peer::new(incoming.source, from_transport(incoming.transport));
623 peer.connection = incoming.connection_generation;
624 peer
625}
626
627fn target(peer: Peer) -> Target {
628 let mut target = Target::new(peer.address, to_transport(peer.transport));
629 if let Some(identity) = peer.identity {
630 target = target.verifying(identity);
631 }
632 if let Some(path) = peer.path {
633 target = target.at_path(path);
634 }
635 target
636}
637
638fn from_transport(transport: TransportKind) -> Transport {
639 match transport {
640 TransportKind::Udp => Transport::Udp,
641 TransportKind::Tcp => Transport::Tcp,
642 TransportKind::Tls => Transport::Tls,
643 TransportKind::Ws => Transport::Ws,
644 TransportKind::Wss => Transport::Wss,
645 TransportKind::Quic => Transport::Quic,
646 }
647}
648
649fn to_transport(transport: Transport) -> TransportKind {
650 match transport {
651 Transport::Udp => TransportKind::Udp,
652 Transport::Tcp => TransportKind::Tcp,
653 Transport::Tls => TransportKind::Tls,
654 Transport::Ws => TransportKind::Ws,
655 Transport::Wss => TransportKind::Wss,
656 Transport::Quic => TransportKind::Quic,
657 }
658}
659
660async fn answer_notify(
661 endpoint: Option<&Handle>,
662 incoming: &Incoming,
663 status: u16,
664 retry_after: Option<Duration>,
665) {
666 let (Some(endpoint), Some(status)) = (endpoint, StatusCode::new(status)) else {
667 return;
668 };
669 let built = ResponseBuilder::to_request(&incoming.request, status, "Event notification")
670 .and_then(|builder| match retry_after {
671 Some(value) => builder.header(
672 HeaderName::RetryAfter,
673 Bytes::from(value.as_secs().to_string()),
674 ),
675 None => Ok(builder),
676 });
677 let Ok(builder) = built else {
678 return;
679 };
680 if let Err(error) = endpoint.respond(&incoming.key, builder.build()).await {
681 tracing::warn!(%error, "could not answer event NOTIFY");
682 }
683}
684
685#[cfg(test)]
686#[allow(
687 clippy::unwrap_used,
688 clippy::expect_used,
689 clippy::panic,
690 clippy::indexing_slicing
691)]
692mod admission_tests {
693 use std::sync::{Arc, Barrier};
694 use std::time::Duration;
695
696 use bytes::Bytes;
697 use sipx_transport::{Config as TransportConfig, bind};
698 use sipx_ua::event_client::{PackageRejection, SamePeer, Start, Transport};
699
700 use super::*;
701
702 #[derive(Debug)]
703 struct Package;
704
705 impl PackageConsumer for Package {
706 type Value = ();
707 fn event(&self) -> &'static str {
708 "admission"
709 }
710 fn accept(&self) -> &[String] {
711 &[]
712 }
713 fn neutral(&mut self) -> Option<()> {
714 None
715 }
716 fn consume(&mut self, _: Option<&[u8]>, _: &[u8]) -> Result<(), PackageRejection> {
717 Ok(())
718 }
719 }
720
721 fn start(target: std::net::SocketAddr) -> Start<Package> {
722 Start {
723 resource: sipx_sip::Uri::parse(Bytes::from_static(b"sip:resource@example.test"))
724 .expect("URI"),
725 local_identity: "<sip:client@example.test>".to_owned(),
726 contact: "<sip:client@127.0.0.1>".to_owned(),
727 target: Peer::new(target, Transport::Udp),
728 expires: Duration::from_secs(60),
729 body: Bytes::new(),
730 content_type: None,
731 credentials: None,
732 call_id: "admission@example.test".to_owned(),
733 from_tag: "admission".to_owned(),
734 initial_cseq: 1,
735 consumer: Package,
736 trust: Arc::new(SamePeer),
737 }
738 }
739
740 #[tokio::test]
741 async fn racing_shutdown_closes_admission_before_any_spawn() {
742 let (endpoint, _) = bind(TransportConfig::new(
743 "127.0.0.1:0".parse().expect("address"),
744 ))
745 .await
746 .expect("endpoint");
747 let runtime = EventSubscriptions::new(Config::default()).expect("runtime");
748 runtime.attach(endpoint.clone());
749 let handle = runtime.handle();
750 let post_shutdown = handle.clone();
751 let shared = Arc::clone(&runtime.shared);
752 let drivers = lock(&shared.drivers);
753 let barrier = Arc::new(Barrier::new(2));
754 let contender = Arc::clone(&barrier);
755 let target = endpoint.local_addr();
756 let attempt = std::thread::spawn(move || {
757 contender.wait();
758 handle.subscribe(start(target))
759 });
760 barrier.wait();
761 shared.shutdown.cancel();
762 drop(drivers);
763 assert!(matches!(
764 attempt.join().expect("thread"),
765 Err(EventSubscriptionError::ShuttingDown)
766 ));
767 assert!(matches!(
768 post_shutdown.subscribe(start(target)),
769 Err(EventSubscriptionError::ShuttingDown)
770 ));
771 assert!(lock(&shared.drivers).is_empty());
772 endpoint.shutdown().await;
773 }
774}