1use std::collections::HashMap;
8use std::net::SocketAddr;
9use std::sync::Arc;
10use std::time::Duration;
11
12use bytes::Bytes;
13use sipx_sip::auth::{Challenge, Credentials, respond, strongest};
14use sipx_sip::build::RequestBuilder;
15use sipx_sip::event::{Reason, State, Subscription};
16use sipx_sip::headers::{CSeq, Contact, Expires, From as FromHeader, RecordRoute, To};
17use sipx_sip::{Address, Header, HeaderName, Host, Method, Request, Response, Uri, UriTransport};
18use thiserror::Error;
19
20pub const DEFAULT_TIMER_N: Duration = Duration::from_secs(32);
22pub const DEFAULT_CAPACITY: usize = 1_024;
24pub const DEFAULT_DELIVERY_CAPACITY: usize = 32;
26pub const DEFAULT_BODY_LIMIT: usize = 65_536;
28pub const DEFAULT_PROBATION_BACKOFF: Duration = Duration::from_secs(60);
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub enum Transport {
34 Udp,
36 Tcp,
38 Tls,
40 Ws,
42 Wss,
44 Quic,
46}
47
48impl Transport {
49 fn default_port(self) -> u16 {
50 match self {
51 Self::Udp | Self::Tcp => 5060,
52 Self::Tls | Self::Quic => 5061,
53 Self::Ws => 80,
54 Self::Wss => 443,
55 }
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Hash)]
61pub struct Peer {
62 pub address: SocketAddr,
64 pub transport: Transport,
66 pub connection: Option<u64>,
68 pub identity: Option<Arc<str>>,
70 pub path: Option<Arc<str>>,
72}
73
74impl Peer {
75 #[must_use]
77 pub fn new(address: SocketAddr, transport: Transport) -> Self {
78 Self {
79 address,
80 transport,
81 connection: None,
82 identity: None,
83 path: None,
84 }
85 }
86
87 #[must_use]
89 pub fn verifying(mut self, identity: impl AsRef<str>) -> Self {
90 self.identity = Some(Arc::from(identity.as_ref()));
91 self
92 }
93
94 #[must_use]
96 pub fn at_path(mut self, path: impl AsRef<str>) -> Self {
97 let path = path.as_ref();
98 self.path = Some(if path.starts_with('/') {
99 Arc::from(path)
100 } else {
101 Arc::from(format!("/{path}"))
102 });
103 self
104 }
105}
106
107#[derive(Debug, Default)]
109pub struct SamePeer;
110
111pub trait NotifyTrustPolicy: Send + Sync + 'static {
113 fn accepts(&self, selected_target: &Peer, received_from: &Peer, request: &Request) -> bool;
115}
116
117impl NotifyTrustPolicy for SamePeer {
118 fn accepts(&self, selected_target: &Peer, received_from: &Peer, _request: &Request) -> bool {
119 selected_target.address == received_from.address
120 && selected_target.transport == received_from.transport
121 && selected_target.connection == received_from.connection
122 }
123}
124
125pub trait PackageConsumer: Send + 'static {
127 type Value: Send + 'static;
129
130 fn event(&self) -> &str;
132
133 fn event_id(&self) -> Option<&str> {
135 None
136 }
137
138 fn accept(&self) -> &[String];
140
141 fn neutral(&mut self) -> Option<Self::Value>;
143
144 fn empty_terminal_is_valid(&self) -> bool {
146 true
147 }
148
149 fn consume(
151 &mut self,
152 content_type: Option<&[u8]>,
153 body: &[u8],
154 ) -> Result<Self::Value, PackageRejection>;
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub struct PackageRejection {
160 pub status: u16,
162}
163
164impl PackageRejection {
165 #[must_use]
167 pub const fn malformed() -> Self {
168 Self { status: 400 }
169 }
170
171 #[must_use]
173 pub const fn unsupported_media() -> Self {
174 Self { status: 415 }
175 }
176}
177
178#[derive(Debug, Clone)]
180pub struct Config {
181 pub capacity: usize,
183 pub delivery_capacity: usize,
185 pub notify_body_limit: usize,
187 pub subscribe_body_limit: usize,
189 pub authentication_retries: u8,
191 pub interval_retries: u8,
193 pub timer_n: Duration,
195 pub maximum_expiry: Duration,
197 pub probation_backoff: Duration,
199}
200
201impl Default for Config {
202 fn default() -> Self {
203 Self {
204 capacity: DEFAULT_CAPACITY,
205 delivery_capacity: DEFAULT_DELIVERY_CAPACITY,
206 notify_body_limit: DEFAULT_BODY_LIMIT,
207 subscribe_body_limit: DEFAULT_BODY_LIMIT,
208 authentication_retries: 2,
209 interval_retries: 1,
210 timer_n: DEFAULT_TIMER_N,
211 maximum_expiry: Duration::from_secs(u64::from(u32::MAX)),
212 probation_backoff: DEFAULT_PROBATION_BACKOFF,
213 }
214 }
215}
216
217impl Config {
218 pub fn validate(&self) -> Result<(), StartError> {
220 if self.capacity == 0
221 || self.delivery_capacity == 0
222 || self.notify_body_limit == 0
223 || self.subscribe_body_limit == 0
224 || self.authentication_retries == 0
225 || self.interval_retries == 0
226 || self.timer_n.is_zero()
227 || self.maximum_expiry.is_zero()
228 || self.probation_backoff.is_zero()
229 || self.maximum_expiry.as_secs() > u64::from(u32::MAX)
230 {
231 return Err(StartError::InvalidConfiguration);
232 }
233 Ok(())
234 }
235}
236
237pub struct Start<C> {
239 pub resource: Uri,
241 pub local_identity: String,
243 pub contact: String,
245 pub target: Peer,
247 pub expires: Duration,
249 pub body: Bytes,
251 pub content_type: Option<String>,
253 pub credentials: Option<Credentials>,
255 pub call_id: String,
257 pub from_tag: String,
259 pub initial_cseq: u32,
261 pub consumer: C,
263 pub trust: Arc<dyn NotifyTrustPolicy>,
265}
266
267impl<C> std::fmt::Debug for Start<C> {
268 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 formatter
270 .debug_struct("Start")
271 .field("resource", &self.resource)
272 .field("local_identity", &self.local_identity)
273 .field("contact", &self.contact)
274 .field("target", &self.target)
275 .field("expires", &self.expires)
276 .field("body_len", &self.body.len())
277 .field("content_type", &self.content_type)
278 .field("has_credentials", &self.credentials.is_some())
279 .field("call_id", &self.call_id)
280 .field("from_tag", &self.from_tag)
281 .field("initial_cseq", &self.initial_cseq)
282 .finish_non_exhaustive()
283 }
284}
285
286#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
288pub struct SubscriptionId(u64);
289
290impl SubscriptionId {
291 #[must_use]
293 pub fn get(self) -> u64 {
294 self.0
295 }
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
300pub enum Timer {
301 N,
303 Expiry,
305 Refresh,
307 Retry,
309}
310
311#[derive(Debug, Clone, Copy, PartialEq, Eq)]
313pub enum Lifecycle {
314 NotifyWait,
316 Active,
318 Pending,
320 Unsubscribing,
322 RetryWait,
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
328pub enum Termination {
329 NoInitialNotify,
331 LocalExpiry,
333 LocalCSeqExhausted,
335 InvalidExpiry,
337 MalformedResponse,
339 IntervalRejected,
341 AuthenticationExhausted,
343 Rejected(u16),
345 TransactionFailed,
347 UnsupportedRouteTransport,
349 Remote(Option<Reason>),
351 UnsubscribeUnconfirmed(Box<Termination>),
353 Shutdown,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq)]
359pub enum StateChange {
360 State(Lifecycle),
362 ConflictingSubscribeResponse,
364 RefreshUnconfirmed,
366 MayRetryNow,
368 Terminated(Termination),
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
374pub struct NotificationMeta {
375 pub subscription: Subscription,
377 pub remote_cseq: u32,
379}
380
381#[derive(Debug)]
383pub enum Output<V> {
384 SendSubscribe {
386 id: SubscriptionId,
388 request: Box<Request>,
390 target: Peer,
392 },
393 RespondNotify {
395 transaction: u64,
397 status: u16,
399 retry_after: Option<Duration>,
401 },
402 Deliver {
404 id: SubscriptionId,
406 metadata: Option<NotificationMeta>,
408 value: V,
410 },
411 ArmTimer {
413 id: SubscriptionId,
415 timer: Timer,
417 generation: u64,
419 after: Duration,
421 },
422 CancelTimer {
424 id: SubscriptionId,
426 timer: Timer,
428 generation: u64,
430 },
431 StateChanged {
433 id: SubscriptionId,
435 change: StateChange,
437 },
438 Stopped,
440}
441
442#[derive(Debug, Error, Clone, PartialEq, Eq)]
444#[non_exhaustive]
445pub enum StartError {
446 #[error("invalid event-client configuration")]
448 InvalidConfiguration,
449 #[error("event-client capacity exceeded")]
451 CapacityExceeded,
452 #[error("SUBSCRIBE body exceeds configured maximum")]
454 BodyTooLarge,
455 #[error("invalid subscription start")]
457 InvalidStart,
458 #[error("could not build SUBSCRIBE")]
460 Build,
461}
462
463#[derive(Debug, Clone, Copy, PartialEq, Eq)]
464enum OperationKind {
465 Initial,
466 Refresh,
467 Unsubscribe,
468}
469
470struct Operation {
471 kind: OperationKind,
472 attempted: Duration,
473 request: Request,
474 auth_retries: u8,
475 interval_retries: u8,
476 notify_expiry: bool,
477}
478
479struct Dialog {
480 remote_tag: Vec<u8>,
481 local: String,
482 remote: String,
483 remote_target: Uri,
484 route_set: Vec<RouteHop>,
485 peer: Peer,
486 route_peer: Option<Peer>,
487 selected_peer: Option<Peer>,
488 remote_cseq: u32,
489}
490
491struct RouteHop {
492 uri: Uri,
493 wire: String,
494}
495
496#[derive(Default)]
497struct Timers {
498 n: Option<u64>,
499 expiry: Option<u64>,
500 refresh: Option<u64>,
501 retry: Option<u64>,
502 next: u64,
503}
504
505struct Entry<C> {
506 lifecycle: Lifecycle,
507 contact: String,
508 target: Peer,
509 desired: Duration,
510 body: Bytes,
511 credentials: Option<Credentials>,
512 call_id: String,
513 from_tag: String,
514 local_cseq: u32,
515 event: String,
516 event_id: Option<String>,
517 accepts: Vec<String>,
518 consumer: C,
519 trust: Arc<dyn NotifyTrustPolicy>,
520 response_tag: Option<Vec<u8>>,
521 response_expiry: Option<Duration>,
522 dialog: Option<Dialog>,
523 operation: Option<Operation>,
524 pending_unsubscribe: bool,
525 timers: Timers,
526 queued: usize,
527 retry_termination: Option<Termination>,
528}
529
530pub struct EventClient<C: PackageConsumer> {
532 config: Config,
533 entries: HashMap<SubscriptionId, Entry<C>>,
534 next_id: u64,
535 shutting_down: bool,
536}
537
538pub type Started<V> = (SubscriptionId, Vec<Output<V>>);
540
541impl<C: PackageConsumer> std::fmt::Debug for EventClient<C> {
542 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543 formatter
544 .debug_struct("EventClient")
545 .field("config", &self.config)
546 .field("active", &self.entries.len())
547 .field("next_id", &self.next_id)
548 .field("shutting_down", &self.shutting_down)
549 .finish()
550 }
551}
552
553impl<C: PackageConsumer> EventClient<C> {
554 pub fn new(config: Config) -> Result<Self, StartError> {
556 config.validate()?;
557 Ok(Self {
558 config,
559 entries: HashMap::new(),
560 next_id: 1,
561 shutting_down: false,
562 })
563 }
564
565 #[must_use]
567 pub fn active(&self) -> usize {
568 self.entries.len()
569 }
570
571 #[must_use]
573 pub fn contains(&self, id: SubscriptionId) -> bool {
574 self.entries.contains_key(&id)
575 }
576
577 pub fn connection_selected(&mut self, id: SubscriptionId, generation: Option<u64>) {
579 let Some(entry) = self.entries.get_mut(&id) else {
580 return;
581 };
582 if entry.dialog.is_some() {
583 let mut selected = request_peer(entry);
584 selected.connection = generation;
585 if let Some(dialog) = entry.dialog.as_mut() {
586 dialog.selected_peer = Some(selected);
587 }
588 } else {
589 entry.target.connection = generation;
590 }
591 }
592
593 pub fn start(&mut self, start: Start<C>) -> Result<Started<C::Value>, StartError> {
595 if self.shutting_down || self.entries.len() >= self.config.capacity {
596 return Err(StartError::CapacityExceeded);
597 }
598 if start.body.len() > self.config.subscribe_body_limit
599 || start.expires.is_zero()
600 || start.expires > self.config.maximum_expiry
601 || start.expires.as_secs() > u64::from(u32::MAX)
602 || start.initial_cseq == 0
603 || start.call_id.trim().is_empty()
604 || start.from_tag.trim().is_empty()
605 || start.consumer.event().trim().is_empty()
606 {
607 return Err(if start.body.len() > self.config.subscribe_body_limit {
608 StartError::BodyTooLarge
609 } else {
610 StartError::InvalidStart
611 });
612 }
613 let id = SubscriptionId(self.next_id);
614 self.next_id = self
615 .next_id
616 .checked_add(1)
617 .ok_or(StartError::CapacityExceeded)?;
618 let event = start.consumer.event().to_owned();
619 let event_id = start.consumer.event_id().map(str::to_owned);
620 let accepts = start.consumer.accept().to_vec();
621 let request = build_initial(&start, &event, event_id.as_deref())?;
622 let mut entry = Entry {
623 lifecycle: Lifecycle::NotifyWait,
624 contact: start.contact,
625 target: start.target,
626 desired: start.expires,
627 body: start.body,
628 credentials: start.credentials,
629 call_id: start.call_id,
630 from_tag: start.from_tag,
631 local_cseq: start.initial_cseq,
632 event,
633 event_id,
634 accepts,
635 consumer: start.consumer,
636 trust: start.trust,
637 response_tag: None,
638 response_expiry: None,
639 dialog: None,
640 operation: Some(Operation {
641 kind: OperationKind::Initial,
642 attempted: start.expires,
643 request: request.clone(),
644 auth_retries: 0,
645 interval_retries: 0,
646 notify_expiry: false,
647 }),
648 pending_unsubscribe: false,
649 timers: Timers::default(),
650 queued: 0,
651 retry_termination: None,
652 };
653 let mut outputs = Vec::new();
654 if let Some(value) = entry.consumer.neutral() {
655 entry.queued = 1;
656 outputs.push(Output::Deliver {
657 id,
658 metadata: None,
659 value,
660 });
661 }
662 outputs.push(Output::SendSubscribe {
663 id,
664 request: Box::new(request),
665 target: entry.target.clone(),
666 });
667 arm(&mut entry, id, Timer::N, self.config.timer_n, &mut outputs);
668 arm(&mut entry, id, Timer::Expiry, start.expires, &mut outputs);
669 outputs.push(Output::StateChanged {
670 id,
671 change: StateChange::State(Lifecycle::NotifyWait),
672 });
673 self.entries.insert(id, entry);
674 Ok((id, outputs))
675 }
676
677 #[allow(
680 clippy::too_many_lines,
681 reason = "the response table is kept in protocol order beside the normative state table"
682 )]
683 pub fn response(
684 &mut self,
685 id: SubscriptionId,
686 response: Option<&Response>,
687 cnonce: &str,
688 ) -> Vec<Output<C::Value>> {
689 let mut outputs = Vec::new();
690 let Some(entry) = self.entries.get_mut(&id) else {
691 return outputs;
692 };
693 let Some(mut operation) = entry.operation.take() else {
694 return outputs;
695 };
696
697 let Some(response) = response else {
698 operation_failure(
699 entry,
700 id,
701 operation.kind,
702 Termination::TransactionFailed,
703 self.config.timer_n,
704 &mut outputs,
705 );
706 finish_if_terminal(&mut self.entries, id, &outputs);
707 return outputs;
708 };
709
710 if matches!(response.status.code(), 401 | 407) {
711 let from_proxy = response.status.code() == 407;
712 let name = if from_proxy {
713 HeaderName::ProxyAuthenticate
714 } else {
715 HeaderName::WwwAuthenticate
716 };
717 let challenge = strongest(
718 response
719 .headers
720 .get_all(&name)
721 .filter_map(|header| Challenge::parse(&header.value(), from_proxy))
722 .collect(),
723 );
724 if operation.auth_retries >= self.config.authentication_retries
725 || challenge.is_none()
726 || entry.credentials.is_none()
727 || !increment_cseq(entry, id, operation.kind, &mut outputs)
728 {
729 if !outputs.iter().any(is_terminal::<C::Value>) {
730 operation_failure(
731 entry,
732 id,
733 operation.kind,
734 Termination::AuthenticationExhausted,
735 self.config.timer_n,
736 &mut outputs,
737 );
738 }
739 } else if let (Some(challenge), Some(credentials)) =
740 (challenge, entry.credentials.as_ref())
741 {
742 operation.auth_retries = operation.auth_retries.saturating_add(1);
743 let mut retry = operation.request.clone();
744 replace_cseq(&mut retry, entry.local_cseq);
745 let uri = String::from_utf8_lossy(&retry.uri.to_bytes()).into_owned();
746 let value = respond(
747 &challenge,
748 credentials,
749 "SUBSCRIBE",
750 &uri,
751 u32::from(operation.auth_retries),
752 cnonce,
753 );
754 retry.headers.remove_all(&challenge.response_header());
755 if let Ok(header) = Header::build(challenge.response_header(), Bytes::from(value)) {
756 retry.headers.push(header);
757 operation.request = retry.clone();
758 entry.operation = Some(operation);
759 outputs.push(Output::SendSubscribe {
760 id,
761 request: Box::new(retry),
762 target: request_peer(entry),
763 });
764 arm(entry, id, Timer::N, self.config.timer_n, &mut outputs);
765 return outputs;
766 }
767 operation_failure(
768 entry,
769 id,
770 operation.kind,
771 Termination::AuthenticationExhausted,
772 self.config.timer_n,
773 &mut outputs,
774 );
775 }
776 finish_if_terminal(&mut self.entries, id, &outputs);
777 return outputs;
778 }
779
780 if response.status.code() == 423 {
781 let minimum = strict_scalar(response, &HeaderName::MinExpires);
782 let valid = operation.kind != OperationKind::Unsubscribe
783 && operation.interval_retries < self.config.interval_retries
784 && minimum.is_some_and(|value| {
785 value > operation.attempted
786 && value <= self.config.maximum_expiry
787 && u32::try_from(value.as_secs()).is_ok()
788 });
789 if valid
790 && increment_cseq(entry, id, operation.kind, &mut outputs)
791 && let Some(minimum) = minimum
792 {
793 operation.interval_retries = operation.interval_retries.saturating_add(1);
794 operation.attempted = minimum;
795 let mut retry = operation.request.clone();
796 replace_cseq(&mut retry, entry.local_cseq);
797 replace_duration_header(&mut retry, HeaderName::Expires, minimum);
798 operation.request = retry.clone();
799 entry.operation = Some(operation);
800 outputs.push(Output::SendSubscribe {
801 id,
802 request: Box::new(retry),
803 target: request_peer(entry),
804 });
805 arm(entry, id, Timer::N, self.config.timer_n, &mut outputs);
806 arm(entry, id, Timer::Expiry, minimum, &mut outputs);
807 return outputs;
808 }
809 if !outputs.iter().any(is_terminal::<C::Value>) {
810 operation_failure(
811 entry,
812 id,
813 operation.kind,
814 Termination::IntervalRejected,
815 self.config.timer_n,
816 &mut outputs,
817 );
818 }
819 finish_if_terminal(&mut self.entries, id, &outputs);
820 return outputs;
821 }
822
823 if response.status.is_success() {
824 let granted = strict_expires(response);
825 let valid_interval = match operation.kind {
826 OperationKind::Initial | OperationKind::Refresh => {
827 granted.is_some_and(|value| !value.is_zero() && value <= operation.attempted)
828 }
829 OperationKind::Unsubscribe => granted == Some(Duration::ZERO),
830 };
831 let tag = response_tag(response);
832 let valid_dialog = match operation.kind {
833 OperationKind::Initial => entry.dialog.is_some() || tag.is_some(),
834 OperationKind::Refresh | OperationKind::Unsubscribe => {
835 entry.dialog.as_ref().is_some_and(|dialog| {
836 tag.as_ref()
837 .is_some_and(|tag| tag == dialog.remote_tag.as_slice())
838 })
839 }
840 };
841 if !valid_interval {
842 operation_failure(
843 entry,
844 id,
845 operation.kind,
846 Termination::InvalidExpiry,
847 self.config.timer_n,
848 &mut outputs,
849 );
850 } else if !valid_dialog {
851 operation_failure(
852 entry,
853 id,
854 operation.kind,
855 Termination::MalformedResponse,
856 self.config.timer_n,
857 &mut outputs,
858 );
859 } else if let Some(granted) = granted {
860 if operation.kind == OperationKind::Unsubscribe {
861 entry.operation = None;
862 } else {
863 if operation.kind == OperationKind::Initial && entry.dialog.is_none() {
864 entry.response_tag = tag;
865 } else if operation.kind == OperationKind::Refresh {
866 refresh_dialog_from_response(entry, response);
867 }
868 if !operation.notify_expiry {
869 entry.response_expiry = Some(granted);
870 arm(entry, id, Timer::Expiry, granted, &mut outputs);
871 if matches!(entry.lifecycle, Lifecycle::Active | Lifecycle::Pending) {
872 arm_refresh(entry, id, granted, &mut outputs);
873 }
874 }
875 entry.operation = None;
876 maybe_begin_pending_unsubscribe(entry, id, self.config.timer_n, &mut outputs);
877 }
878 }
879 finish_if_terminal(&mut self.entries, id, &outputs);
880 return outputs;
881 }
882
883 if operation.kind == OperationKind::Refresh && fatal_refresh_status(response.status.code())
884 {
885 terminate(
886 entry,
887 id,
888 Termination::Rejected(response.status.code()),
889 &mut outputs,
890 );
891 } else {
892 operation_failure(
893 entry,
894 id,
895 operation.kind,
896 Termination::Rejected(response.status.code()),
897 self.config.timer_n,
898 &mut outputs,
899 );
900 }
901 finish_if_terminal(&mut self.entries, id, &outputs);
902 outputs
903 }
904
905 #[allow(
908 clippy::too_many_lines,
909 clippy::needless_pass_by_value,
910 reason = "the fail-closed NOTIFY validation order mirrors the normative decision table"
911 )]
912 pub fn notify(
913 &mut self,
914 transaction: u64,
915 request: &Request,
916 source: Peer,
917 ) -> Vec<Output<C::Value>> {
918 let mut outputs = Vec::new();
919 let Some((id, entry)) = self.entries.iter_mut().find(|(_, entry)| {
920 call_id(request).is_some_and(|call_id| call_id == entry.call_id.as_bytes())
921 && tag::<To>(request).is_some_and(|tag| tag == entry.from_tag.as_bytes())
922 }) else {
923 outputs.push(respond_notify(transaction, 481, None));
924 return outputs;
925 };
926 let id = *id;
927
928 if request.method != Method::Notify
929 || request.headers.count(&HeaderName::CallId) != 1
930 || request.headers.count(&HeaderName::From) != 1
931 || request.headers.count(&HeaderName::To) != 1
932 {
933 outputs.push(respond_notify(transaction, 400, None));
934 return outputs;
935 }
936 let Some((event, event_id)) = parse_event(request) else {
937 outputs.push(respond_notify(transaction, 489, None));
938 return outputs;
939 };
940 if !event.eq_ignore_ascii_case(&entry.event)
941 || event_id.as_deref() != entry.event_id.as_deref()
942 {
943 outputs.push(respond_notify(transaction, 489, None));
944 return outputs;
945 }
946 let Some(remote_tag) = tag::<FromHeader>(request) else {
947 outputs.push(respond_notify(transaction, 400, None));
948 return outputs;
949 };
950 if entry
951 .dialog
952 .as_ref()
953 .is_some_and(|dialog| dialog.remote_tag.as_slice() != remote_tag.as_slice())
954 || (entry.dialog.is_none()
955 && entry
956 .response_tag
957 .as_ref()
958 .is_some_and(|candidate| candidate.as_slice() != remote_tag.as_slice()))
959 {
960 outputs.push(respond_notify(transaction, 481, None));
961 return outputs;
962 }
963 let selected_target = request_peer(entry);
964 if !entry.trust.accepts(&selected_target, &source, request) {
965 outputs.push(respond_notify(transaction, 403, None));
966 return outputs;
967 }
968 let cseq = if request.headers.count(&HeaderName::CSeq) == 1 {
969 if let Some(Ok(CSeq {
970 sequence,
971 method: Method::Notify,
972 })) = request.headers.typed::<CSeq>()
973 {
974 sequence
975 } else {
976 outputs.push(respond_notify(transaction, 400, None));
977 return outputs;
978 }
979 } else {
980 outputs.push(respond_notify(transaction, 400, None));
981 return outputs;
982 };
983 if entry
984 .dialog
985 .as_ref()
986 .is_some_and(|dialog| cseq <= dialog.remote_cseq)
987 {
988 outputs.push(respond_notify(transaction, 500, None));
989 return outputs;
990 }
991 let contacts: Vec<_> = request.headers.typed_all::<Contact>().collect();
992 let [Ok(contact)] = contacts.as_slice() else {
993 outputs.push(respond_notify(transaction, 400, None));
994 return outputs;
995 };
996 let state = if request.headers.count(&HeaderName::SubscriptionState) == 1 {
997 request
998 .headers
999 .value(&HeaderName::SubscriptionState)
1000 .and_then(|value| Subscription::parse(&value))
1001 } else {
1002 None
1003 };
1004 let Some(state) = state else {
1005 outputs.push(respond_notify(transaction, 400, None));
1006 return outputs;
1007 };
1008 if request.body().len() > self.config.notify_body_limit {
1009 outputs.push(respond_notify(transaction, 413, None));
1010 return outputs;
1011 }
1012 let should_consume = !request.body().is_empty()
1013 || state.state != State::Terminated
1014 || !entry.consumer.empty_terminal_is_valid();
1015 if should_consume && entry.queued >= self.config.delivery_capacity {
1016 outputs.push(respond_notify(
1017 transaction,
1018 503,
1019 Some(Duration::from_secs(1)),
1020 ));
1021 return outputs;
1022 }
1023 let value = if should_consume {
1024 let content_type = request.headers.value(&HeaderName::ContentType);
1025 match entry
1026 .consumer
1027 .consume(content_type.as_deref(), request.body())
1028 {
1029 Ok(value) => Some(value),
1030 Err(rejection) => {
1031 outputs.push(respond_notify(transaction, rejection.status, None));
1032 return outputs;
1033 }
1034 }
1035 } else {
1036 None
1037 };
1038
1039 let remote = request
1040 .headers
1041 .value(&HeaderName::From)
1042 .map(|value| String::from_utf8_lossy(&value).into_owned())
1043 .unwrap_or_default();
1044 let local = request
1045 .headers
1046 .value(&HeaderName::To)
1047 .map(|value| String::from_utf8_lossy(&value).into_owned())
1048 .unwrap_or_default();
1049 let routes: Result<Vec<_>, _> = request
1050 .headers
1051 .typed_all::<RecordRoute>()
1052 .map(|route| route.map(|route| route_hop(&route)))
1053 .collect();
1054 let Ok(routes) = routes else {
1055 outputs.push(respond_notify(transaction, 400, None));
1056 return outputs;
1057 };
1058 let source = Peer {
1059 identity: selected_target.identity.clone(),
1060 path: selected_target.path.clone(),
1061 ..source
1062 };
1063 let refreshed_peer = contact_peer(&contact.uri, &source);
1064 let route_peer = if entry.dialog.is_none() {
1065 if let Ok(peer) = routes
1066 .first()
1067 .map(|route| route_peer(&route.uri, &source))
1068 .transpose()
1069 {
1070 peer
1071 } else {
1072 outputs.push(respond_notify(transaction, 400, None));
1073 cancel_all(entry, id, &mut outputs);
1074 outputs.push(Output::StateChanged {
1075 id,
1076 change: StateChange::Terminated(Termination::UnsupportedRouteTransport),
1077 });
1078 finish_if_terminal(&mut self.entries, id, &outputs);
1079 return outputs;
1080 }
1081 } else {
1082 None
1083 };
1084 match entry.dialog.as_mut() {
1085 Some(dialog) => {
1086 dialog.remote_target = contact.uri.clone();
1087 dialog.peer = refreshed_peer;
1088 dialog.remote_cseq = cseq;
1089 }
1090 None => {
1091 entry.dialog = Some(Dialog {
1092 remote_tag,
1093 local,
1094 remote,
1095 remote_target: contact.uri.clone(),
1096 route_set: routes,
1097 peer: refreshed_peer,
1098 route_peer,
1099 selected_peer: Some(selected_target),
1100 remote_cseq: cseq,
1101 });
1102 }
1103 }
1104 outputs.push(respond_notify(transaction, 200, None));
1105 cancel(entry, id, Timer::N, &mut outputs);
1106
1107 if let Some(operation) = entry.operation.as_mut()
1108 && state.expires.is_some()
1109 {
1110 operation.notify_expiry = true;
1111 }
1112 match state.state {
1113 State::Active | State::Pending => {
1114 entry.lifecycle = if state.state == State::Active {
1115 Lifecycle::Active
1116 } else {
1117 Lifecycle::Pending
1118 };
1119 if let Some(expires) = state.expires {
1120 arm(entry, id, Timer::Expiry, expires, &mut outputs);
1121 arm_refresh(entry, id, expires, &mut outputs);
1122 } else if let Some(expires) = entry.response_expiry {
1123 arm_refresh(entry, id, expires, &mut outputs);
1124 }
1125 outputs.push(Output::StateChanged {
1126 id,
1127 change: StateChange::State(entry.lifecycle),
1128 });
1129 if let Some(value) = value {
1130 entry.queued = entry.queued.saturating_add(1);
1131 outputs.push(Output::Deliver {
1132 id,
1133 metadata: Some(NotificationMeta {
1134 subscription: state,
1135 remote_cseq: cseq,
1136 }),
1137 value,
1138 });
1139 }
1140 maybe_begin_pending_unsubscribe(entry, id, self.config.timer_n, &mut outputs);
1141 }
1142 State::Terminated => {
1143 cancel_all(entry, id, &mut outputs);
1144 if let Some(value) = value {
1145 outputs.push(Output::Deliver {
1146 id,
1147 metadata: Some(NotificationMeta {
1148 subscription: state.clone(),
1149 remote_cseq: cseq,
1150 }),
1151 value,
1152 });
1153 }
1154 let termination = Termination::Remote(state.reason);
1155 let retry = if entry.lifecycle == Lifecycle::Unsubscribing
1156 || entry.pending_unsubscribe
1157 || self.shutting_down
1158 {
1159 RetryPolicy::Never
1160 } else {
1161 retry_policy(&state, self.config.probation_backoff)
1162 };
1163 match retry {
1164 RetryPolicy::Never => outputs.push(Output::StateChanged {
1165 id,
1166 change: StateChange::Terminated(termination),
1167 }),
1168 RetryPolicy::Immediate => {
1169 outputs.push(Output::StateChanged {
1170 id,
1171 change: StateChange::MayRetryNow,
1172 });
1173 outputs.push(Output::StateChanged {
1174 id,
1175 change: StateChange::Terminated(termination),
1176 });
1177 }
1178 RetryPolicy::After(after) => {
1179 entry.lifecycle = Lifecycle::RetryWait;
1180 entry.retry_termination = Some(termination);
1181 arm(entry, id, Timer::Retry, after, &mut outputs);
1182 outputs.push(Output::StateChanged {
1183 id,
1184 change: StateChange::State(Lifecycle::RetryWait),
1185 });
1186 }
1187 }
1188 }
1189 }
1190 finish_if_terminal(&mut self.entries, id, &outputs);
1191 outputs
1192 }
1193
1194 pub fn consumer_drained(&mut self, id: SubscriptionId, count: usize) {
1196 if let Some(entry) = self.entries.get_mut(&id) {
1197 entry.queued = entry.queued.saturating_sub(count);
1198 }
1199 }
1200
1201 pub fn timer_fired(
1203 &mut self,
1204 id: SubscriptionId,
1205 timer: Timer,
1206 generation: u64,
1207 ) -> Vec<Output<C::Value>> {
1208 let mut outputs = Vec::new();
1209 let Some(entry) = self.entries.get_mut(&id) else {
1210 return outputs;
1211 };
1212 if timer_generation(&entry.timers, timer) != Some(generation) {
1213 return outputs;
1214 }
1215 set_timer(&mut entry.timers, timer, None);
1216 match timer {
1217 Timer::N => match entry.lifecycle {
1218 Lifecycle::NotifyWait => {
1219 terminate(entry, id, Termination::NoInitialNotify, &mut outputs);
1220 }
1221 Lifecycle::Unsubscribing => terminate(
1222 entry,
1223 id,
1224 Termination::UnsubscribeUnconfirmed(Box::new(Termination::NoInitialNotify)),
1225 &mut outputs,
1226 ),
1227 Lifecycle::Active | Lifecycle::Pending => {
1228 entry.operation = None;
1229 outputs.push(Output::StateChanged {
1230 id,
1231 change: StateChange::RefreshUnconfirmed,
1232 });
1233 maybe_begin_pending_unsubscribe(entry, id, self.config.timer_n, &mut outputs);
1234 }
1235 Lifecycle::RetryWait => {
1236 let reason = entry
1237 .retry_termination
1238 .take()
1239 .unwrap_or(Termination::TransactionFailed);
1240 terminate(entry, id, reason, &mut outputs);
1241 }
1242 },
1243 Timer::Expiry => terminate(entry, id, Termination::LocalExpiry, &mut outputs),
1244 Timer::Refresh => {
1245 if !self.shutting_down
1246 && entry.operation.is_none()
1247 && increment_cseq(entry, id, OperationKind::Refresh, &mut outputs)
1248 {
1249 match build_in_dialog(entry, entry.desired) {
1250 Ok(request) => {
1251 entry.operation = Some(Operation {
1252 kind: OperationKind::Refresh,
1253 attempted: entry.desired,
1254 request: request.clone(),
1255 auth_retries: 0,
1256 interval_retries: 0,
1257 notify_expiry: false,
1258 });
1259 outputs.push(Output::SendSubscribe {
1260 id,
1261 request: Box::new(request),
1262 target: request_peer(entry),
1263 });
1264 arm(entry, id, Timer::N, self.config.timer_n, &mut outputs);
1265 }
1266 Err(()) => {
1267 terminate(entry, id, Termination::TransactionFailed, &mut outputs);
1268 }
1269 }
1270 }
1271 }
1272 Timer::Retry => {
1273 outputs.push(Output::StateChanged {
1274 id,
1275 change: StateChange::MayRetryNow,
1276 });
1277 let reason = entry
1278 .retry_termination
1279 .take()
1280 .unwrap_or(Termination::TransactionFailed);
1281 terminate(entry, id, reason, &mut outputs);
1282 }
1283 }
1284 finish_if_terminal(&mut self.entries, id, &outputs);
1285 outputs
1286 }
1287
1288 pub fn unsubscribe(&mut self, id: SubscriptionId) -> Vec<Output<C::Value>> {
1290 let mut outputs = Vec::new();
1291 let Some(entry) = self.entries.get_mut(&id) else {
1292 return outputs;
1293 };
1294 cancel(entry, id, Timer::Refresh, &mut outputs);
1295 cancel(entry, id, Timer::Retry, &mut outputs);
1296 if entry.lifecycle == Lifecycle::RetryWait {
1297 let reason = entry
1298 .retry_termination
1299 .take()
1300 .unwrap_or(Termination::TransactionFailed);
1301 terminate(entry, id, reason, &mut outputs);
1302 } else if entry.operation.is_some() {
1303 entry.pending_unsubscribe = true;
1304 } else if entry.dialog.is_none() {
1305 terminate(
1306 entry,
1307 id,
1308 Termination::UnsubscribeUnconfirmed(Box::new(Termination::TransactionFailed)),
1309 &mut outputs,
1310 );
1311 } else {
1312 entry.pending_unsubscribe = true;
1313 maybe_begin_pending_unsubscribe(entry, id, self.config.timer_n, &mut outputs);
1314 }
1315 finish_if_terminal(&mut self.entries, id, &outputs);
1316 outputs
1317 }
1318
1319 pub fn shutdown(&mut self) -> Vec<Output<C::Value>> {
1321 self.shutting_down = true;
1322 let ids: Vec<_> = self.entries.keys().copied().collect();
1323 let mut outputs = Vec::new();
1324 for id in ids {
1325 outputs.extend(self.unsubscribe(id));
1326 }
1327 if self.entries.is_empty() {
1328 outputs.push(Output::Stopped);
1329 }
1330 outputs
1331 }
1332
1333 pub fn shutdown_deadline(&mut self) -> Vec<Output<C::Value>> {
1335 let mut outputs = Vec::new();
1336 for (id, mut entry) in self.entries.drain() {
1337 cancel_all(&mut entry, id, &mut outputs);
1338 outputs.push(Output::StateChanged {
1339 id,
1340 change: StateChange::Terminated(Termination::Shutdown),
1341 });
1342 }
1343 outputs.push(Output::Stopped);
1344 outputs
1345 }
1346}
1347
1348fn build_initial<C: PackageConsumer>(
1349 start: &Start<C>,
1350 event: &str,
1351 event_id: Option<&str>,
1352) -> Result<Request, StartError> {
1353 let event = event_value(event, event_id);
1354 let mut builder = RequestBuilder::new(Method::Subscribe, start.resource.clone())
1355 .header(HeaderName::To, Bytes::from(format!("<{}>", start.resource)))
1356 .map_err(|_| StartError::Build)?
1357 .header(
1358 HeaderName::From,
1359 Bytes::from(format!("{};tag={}", start.local_identity, start.from_tag)),
1360 )
1361 .map_err(|_| StartError::Build)?
1362 .header(HeaderName::CallId, Bytes::from(start.call_id.clone()))
1363 .map_err(|_| StartError::Build)?
1364 .cseq(start.initial_cseq, &Method::Subscribe)
1365 .map_err(|_| StartError::Build)?
1366 .header(HeaderName::Contact, Bytes::from(start.contact.clone()))
1367 .map_err(|_| StartError::Build)?
1368 .header(HeaderName::Event, Bytes::from(event))
1369 .map_err(|_| StartError::Build)?
1370 .header(
1371 HeaderName::Expires,
1372 Bytes::from(start.expires.as_secs().to_string()),
1373 )
1374 .map_err(|_| StartError::Build)?
1375 .max_forwards(70);
1376 if !start.consumer.accept().is_empty() {
1377 builder = builder
1378 .header(
1379 HeaderName::Accept,
1380 Bytes::from(start.consumer.accept().join(", ")),
1381 )
1382 .map_err(|_| StartError::Build)?;
1383 }
1384 if let Some(content_type) = &start.content_type {
1385 builder = builder
1386 .header(HeaderName::ContentType, Bytes::from(content_type.clone()))
1387 .map_err(|_| StartError::Build)?;
1388 }
1389 Ok(builder.body(start.body.clone()).build())
1390}
1391
1392fn build_in_dialog<C: PackageConsumer>(entry: &Entry<C>, expires: Duration) -> Result<Request, ()> {
1393 let dialog = entry.dialog.as_ref().ok_or(())?;
1394 let event = event_value(&entry.event, entry.event_id.as_deref());
1395 let (request_uri, routes) = dialog_request_target(dialog);
1396 let mut builder = RequestBuilder::new(Method::Subscribe, request_uri)
1397 .header(HeaderName::To, Bytes::from(dialog.remote.clone()))
1398 .map_err(|_| ())?
1399 .header(HeaderName::From, Bytes::from(dialog.local.clone()))
1400 .map_err(|_| ())?
1401 .header(HeaderName::CallId, Bytes::from(entry.call_id.clone()))
1402 .map_err(|_| ())?
1403 .cseq(entry.local_cseq, &Method::Subscribe)
1404 .map_err(|_| ())?
1405 .header(HeaderName::Contact, Bytes::from(entry.contact.clone()))
1406 .map_err(|_| ())?
1407 .header(HeaderName::Event, Bytes::from(event))
1408 .map_err(|_| ())?
1409 .header(
1410 HeaderName::Expires,
1411 Bytes::from(expires.as_secs().to_string()),
1412 )
1413 .map_err(|_| ())?
1414 .max_forwards(70);
1415 if !entry.accepts.is_empty() {
1416 builder = builder
1417 .header(HeaderName::Accept, Bytes::from(entry.accepts.join(", ")))
1418 .map_err(|_| ())?;
1419 }
1420 for route in routes {
1421 builder = builder
1422 .header(HeaderName::Route, Bytes::from(route))
1423 .map_err(|_| ())?;
1424 }
1425 Ok(builder.body(entry.body.clone()).build())
1426}
1427
1428fn route_hop(address: &Address) -> RouteHop {
1429 let mut wire = format!("<{}>", address.uri);
1430 for parameter in &address.params {
1431 wire.push(';');
1432 wire.push_str(&String::from_utf8_lossy(¶meter.name));
1433 if let Some(value) = ¶meter.value {
1434 wire.push('=');
1435 wire.push_str(&String::from_utf8_lossy(value));
1436 }
1437 }
1438 RouteHop {
1439 uri: address.uri.clone(),
1440 wire,
1441 }
1442}
1443
1444fn dialog_request_target(dialog: &Dialog) -> (Uri, Vec<String>) {
1445 let Some(first) = dialog.route_set.first() else {
1446 return (dialog.remote_target.clone(), Vec::new());
1447 };
1448 if first
1449 .uri
1450 .params()
1451 .is_some_and(|parameters| parameters.contains("lr"))
1452 {
1453 return (
1454 dialog.remote_target.clone(),
1455 dialog
1456 .route_set
1457 .iter()
1458 .map(|route| route.wire.clone())
1459 .collect(),
1460 );
1461 }
1462 let mut routes: Vec<_> = dialog
1463 .route_set
1464 .iter()
1465 .skip(1)
1466 .map(|route| route.wire.clone())
1467 .collect();
1468 routes.push(format!("<{}>", dialog.remote_target));
1469 (first.uri.clone(), routes)
1470}
1471
1472fn fatal_refresh_status(status: u16) -> bool {
1473 matches!(
1474 status,
1475 404 | 405 | 410 | 416 | 480 | 481 | 482 | 483 | 484 | 485 | 489 | 501 | 604
1476 )
1477}
1478
1479fn event_value(event: &str, id: Option<&str>) -> String {
1480 id.map_or_else(|| event.to_owned(), |id| format!("{event};id={id}"))
1481}
1482
1483fn replace_cseq(request: &mut Request, sequence: u32) {
1484 request.headers.remove_all(&HeaderName::CSeq);
1485 if let Ok(header) = Header::build(
1486 HeaderName::CSeq,
1487 Bytes::from(format!("{sequence} SUBSCRIBE")),
1488 ) {
1489 request.headers.push(header);
1490 }
1491}
1492
1493fn replace_duration_header(request: &mut Request, name: HeaderName, value: Duration) {
1494 request.headers.remove_all(&name);
1495 if let Ok(header) = Header::build(name, Bytes::from(value.as_secs().to_string())) {
1496 request.headers.push(header);
1497 }
1498}
1499
1500fn parse_event(request: &Request) -> Option<(String, Option<String>)> {
1501 if request.headers.count(&HeaderName::Event) != 1 {
1502 return None;
1503 }
1504 let value = request.headers.value(&HeaderName::Event)?;
1505 let text = std::str::from_utf8(&value).ok()?;
1506 let mut parts = text.split(';');
1507 let event = parts.next()?.trim();
1508 if event.is_empty() {
1509 return None;
1510 }
1511 let mut id = None;
1512 for parameter in parts {
1513 let Some((name, value)) = parameter.split_once('=') else {
1514 continue;
1515 };
1516 if name.trim().eq_ignore_ascii_case("id") {
1517 if id.is_some() {
1518 return None;
1519 }
1520 id = Some(value.trim().trim_matches('"').to_owned());
1521 }
1522 }
1523 Some((event.to_owned(), id))
1524}
1525
1526fn call_id(request: &Request) -> Option<std::borrow::Cow<'_, [u8]>> {
1527 (request.headers.count(&HeaderName::CallId) == 1)
1528 .then(|| request.headers.value(&HeaderName::CallId))
1529 .flatten()
1530}
1531
1532fn tag<H>(request: &Request) -> Option<Vec<u8>>
1533where
1534 H: sipx_sip::TypedHeader + std::ops::Deref<Target = sipx_sip::Address>,
1535{
1536 if request.headers.count(&H::NAME) != 1 {
1537 return None;
1538 }
1539 request
1540 .headers
1541 .typed::<H>()?
1542 .ok()?
1543 .tag()
1544 .map(<[u8]>::to_vec)
1545}
1546
1547fn response_tag(response: &Response) -> Option<Vec<u8>> {
1548 if response.headers.count(&HeaderName::To) != 1 {
1549 return None;
1550 }
1551 response
1552 .headers
1553 .typed::<To>()?
1554 .ok()?
1555 .tag()
1556 .map(<[u8]>::to_vec)
1557}
1558
1559fn strict_expires(response: &Response) -> Option<Duration> {
1560 if response.headers.count(&HeaderName::Expires) != 1 {
1561 return None;
1562 }
1563 response
1564 .headers
1565 .typed::<Expires>()?
1566 .ok()
1567 .map(|value| Duration::from_secs(u64::from(value.0)))
1568}
1569
1570fn strict_scalar(response: &Response, name: &HeaderName) -> Option<Duration> {
1571 if response.headers.count(name) != 1 {
1572 return None;
1573 }
1574 let value = response.headers.value(name)?;
1575 let seconds = std::str::from_utf8(&value)
1576 .ok()?
1577 .trim()
1578 .parse::<u32>()
1579 .ok()?;
1580 Some(Duration::from_secs(u64::from(seconds)))
1581}
1582
1583fn request_peer<C>(entry: &Entry<C>) -> Peer {
1584 let Some(dialog) = entry.dialog.as_ref() else {
1585 return entry.target.clone();
1586 };
1587 let mut peer = dialog.route_peer.as_ref().unwrap_or(&dialog.peer).clone();
1588 peer.connection = dialog
1589 .selected_peer
1590 .as_ref()
1591 .filter(|selected| same_target_selectors(selected, &peer))
1592 .and_then(|selected| selected.connection);
1593 peer
1594}
1595
1596fn refresh_dialog_from_response<C>(entry: &mut Entry<C>, response: &Response) {
1597 let contacts: Vec<_> = response.headers.typed_all::<Contact>().collect();
1598 let [Ok(contact)] = contacts.as_slice() else {
1599 return;
1600 };
1601 if let Some(dialog) = entry.dialog.as_mut() {
1602 dialog.remote_target = contact.uri.clone();
1603 dialog.peer = contact_peer(&contact.uri, &dialog.peer);
1604 }
1605}
1606
1607fn contact_peer(uri: &Uri, fallback: &Peer) -> Peer {
1608 let ip = match uri.host() {
1609 Some(Host::Ip(ip)) => *ip,
1610 _ => fallback.address.ip(),
1611 };
1612 let address = SocketAddr::new(
1613 ip,
1614 uri.port()
1615 .unwrap_or_else(|| fallback.transport.default_port()),
1616 );
1617 let mut peer = fallback.clone();
1618 if peer.address != address {
1619 peer.connection = None;
1620 }
1621 peer.address = address;
1622 peer
1623}
1624
1625fn route_peer(uri: &Uri, fallback: &Peer) -> Result<Peer, ()> {
1626 let selected = uri.selected_transport().map_err(|_| ())?;
1627 let transport = match selected {
1628 UriTransport::Udp => Transport::Udp,
1629 UriTransport::Tcp => Transport::Tcp,
1630 UriTransport::Tls => Transport::Tls,
1631 UriTransport::Ws => Transport::Ws,
1632 UriTransport::Wss => Transport::Wss,
1633 UriTransport::Quic => Transport::Quic,
1634 };
1635 let ip = match uri.host() {
1636 Some(Host::Ip(ip)) => *ip,
1637 Some(Host::Name(_)) => fallback.address.ip(),
1638 None => return Err(()),
1639 };
1640 let carries_authority = matches!(transport, Transport::Tls | Transport::Wss | Transport::Quic)
1641 || matches!(
1642 (transport, uri.host()),
1643 (Transport::Ws, Some(Host::Name(_)))
1644 );
1645 let identity = if carries_authority {
1646 uri.host()
1647 .map(|host| Arc::from(String::from_utf8_lossy(&host.to_bytes()).into_owned()))
1648 } else {
1649 None
1650 };
1651 let path = if matches!(transport, Transport::Ws | Transport::Wss) {
1652 fallback.path.clone()
1653 } else {
1654 None
1655 };
1656 let mut peer = Peer {
1657 address: SocketAddr::new(ip, uri.port().unwrap_or(selected.default_port())),
1658 transport,
1659 connection: None,
1660 identity,
1661 path,
1662 };
1663 if same_target_selectors(&peer, fallback) {
1664 peer.connection = fallback.connection;
1665 }
1666 Ok(peer)
1667}
1668
1669fn same_target_selectors(left: &Peer, right: &Peer) -> bool {
1670 left.address == right.address
1671 && left.transport == right.transport
1672 && left.identity == right.identity
1673 && left.path == right.path
1674}
1675
1676fn respond_notify<V>(transaction: u64, status: u16, retry_after: Option<Duration>) -> Output<V> {
1677 Output::RespondNotify {
1678 transaction,
1679 status,
1680 retry_after,
1681 }
1682}
1683
1684enum RetryPolicy {
1685 Never,
1686 Immediate,
1687 After(Duration),
1688}
1689
1690fn retry_policy(state: &Subscription, probation_backoff: Duration) -> RetryPolicy {
1691 match state.reason {
1692 Some(Reason::Deactivated) => RetryPolicy::After(Duration::ZERO),
1693 Some(Reason::Timeout) => RetryPolicy::Immediate,
1694 Some(Reason::Probation) => {
1695 RetryPolicy::After(state.retry_after.unwrap_or(probation_backoff))
1696 }
1697 Some(Reason::GiveUp) | None => state
1698 .retry_after
1699 .map_or(RetryPolicy::Immediate, RetryPolicy::After),
1700 Some(Reason::Rejected | Reason::NoResource | Reason::Invariant | Reason::BadFilter) => {
1701 RetryPolicy::Never
1702 }
1703 }
1704}
1705
1706fn timer_slot(timers: &mut Timers, timer: Timer) -> &mut Option<u64> {
1707 match timer {
1708 Timer::N => &mut timers.n,
1709 Timer::Expiry => &mut timers.expiry,
1710 Timer::Refresh => &mut timers.refresh,
1711 Timer::Retry => &mut timers.retry,
1712 }
1713}
1714
1715fn timer_generation(timers: &Timers, timer: Timer) -> Option<u64> {
1716 match timer {
1717 Timer::N => timers.n,
1718 Timer::Expiry => timers.expiry,
1719 Timer::Refresh => timers.refresh,
1720 Timer::Retry => timers.retry,
1721 }
1722}
1723
1724fn set_timer(timers: &mut Timers, timer: Timer, generation: Option<u64>) {
1725 *timer_slot(timers, timer) = generation;
1726}
1727
1728fn arm<C, V>(
1729 entry: &mut Entry<C>,
1730 id: SubscriptionId,
1731 timer: Timer,
1732 after: Duration,
1733 outputs: &mut Vec<Output<V>>,
1734) {
1735 entry.timers.next = entry.timers.next.saturating_add(1);
1736 let generation = entry.timers.next;
1737 set_timer(&mut entry.timers, timer, Some(generation));
1738 outputs.push(Output::ArmTimer {
1739 id,
1740 timer,
1741 generation,
1742 after,
1743 });
1744}
1745
1746fn cancel<C, V>(
1747 entry: &mut Entry<C>,
1748 id: SubscriptionId,
1749 timer: Timer,
1750 outputs: &mut Vec<Output<V>>,
1751) {
1752 if let Some(generation) = timer_generation(&entry.timers, timer) {
1753 set_timer(&mut entry.timers, timer, None);
1754 entry.timers.next = entry.timers.next.saturating_add(1);
1755 outputs.push(Output::CancelTimer {
1756 id,
1757 timer,
1758 generation,
1759 });
1760 }
1761}
1762
1763fn cancel_all<C, V>(entry: &mut Entry<C>, id: SubscriptionId, outputs: &mut Vec<Output<V>>) {
1764 for timer in [Timer::N, Timer::Expiry, Timer::Refresh, Timer::Retry] {
1765 cancel(entry, id, timer, outputs);
1766 }
1767}
1768
1769fn arm_refresh<C, V>(
1770 entry: &mut Entry<C>,
1771 id: SubscriptionId,
1772 expires: Duration,
1773 outputs: &mut Vec<Output<V>>,
1774) {
1775 let seconds = expires.as_secs();
1776 let refresh = if seconds <= 1 {
1777 Duration::ZERO
1778 } else {
1779 Duration::from_secs((seconds.saturating_mul(4) / 5).clamp(1, seconds - 1))
1780 };
1781 arm(entry, id, Timer::Refresh, refresh, outputs);
1782}
1783
1784fn increment_cseq<C, V>(
1785 entry: &mut Entry<C>,
1786 id: SubscriptionId,
1787 kind: OperationKind,
1788 outputs: &mut Vec<Output<V>>,
1789) -> bool {
1790 let Some(next) = entry.local_cseq.checked_add(1) else {
1791 let reason = if kind == OperationKind::Unsubscribe {
1792 Termination::UnsubscribeUnconfirmed(Box::new(Termination::LocalCSeqExhausted))
1793 } else {
1794 Termination::LocalCSeqExhausted
1795 };
1796 terminate(entry, id, reason, outputs);
1797 return false;
1798 };
1799 entry.local_cseq = next;
1800 true
1801}
1802
1803fn operation_failure<C: PackageConsumer, V>(
1804 entry: &mut Entry<C>,
1805 id: SubscriptionId,
1806 kind: OperationKind,
1807 reason: Termination,
1808 timer_n: Duration,
1809 outputs: &mut Vec<Output<V>>,
1810) {
1811 match entry.lifecycle {
1812 Lifecycle::NotifyWait | Lifecycle::RetryWait => terminate(entry, id, reason, outputs),
1813 Lifecycle::Active | Lifecycle::Pending => {
1814 cancel(entry, id, Timer::N, outputs);
1815 outputs.push(Output::StateChanged {
1816 id,
1817 change: if kind == OperationKind::Refresh {
1818 StateChange::RefreshUnconfirmed
1819 } else {
1820 StateChange::ConflictingSubscribeResponse
1821 },
1822 });
1823 entry.operation = None;
1824 maybe_begin_pending_unsubscribe(entry, id, timer_n, outputs);
1825 }
1826 Lifecycle::Unsubscribing => terminate(
1827 entry,
1828 id,
1829 Termination::UnsubscribeUnconfirmed(Box::new(reason)),
1830 outputs,
1831 ),
1832 }
1833}
1834
1835fn maybe_begin_pending_unsubscribe<C: PackageConsumer, V>(
1836 entry: &mut Entry<C>,
1837 id: SubscriptionId,
1838 timer_n: Duration,
1839 outputs: &mut Vec<Output<V>>,
1840) {
1841 if !entry.pending_unsubscribe || entry.operation.is_some() || entry.dialog.is_none() {
1842 return;
1843 }
1844 entry.pending_unsubscribe = false;
1845 if !increment_cseq(entry, id, OperationKind::Unsubscribe, outputs) {
1846 return;
1847 }
1848 match build_in_dialog(entry, Duration::ZERO) {
1849 Ok(request) => {
1850 entry.lifecycle = Lifecycle::Unsubscribing;
1851 entry.operation = Some(Operation {
1852 kind: OperationKind::Unsubscribe,
1853 attempted: Duration::ZERO,
1854 request: request.clone(),
1855 auth_retries: 0,
1856 interval_retries: 0,
1857 notify_expiry: false,
1858 });
1859 outputs.push(Output::SendSubscribe {
1860 id,
1861 request: Box::new(request),
1862 target: request_peer(entry),
1863 });
1864 arm(entry, id, Timer::N, timer_n, outputs);
1865 outputs.push(Output::StateChanged {
1866 id,
1867 change: StateChange::State(Lifecycle::Unsubscribing),
1868 });
1869 }
1870 Err(()) => terminate(
1871 entry,
1872 id,
1873 Termination::UnsubscribeUnconfirmed(Box::new(Termination::TransactionFailed)),
1874 outputs,
1875 ),
1876 }
1877}
1878
1879fn terminate<C, V>(
1880 entry: &mut Entry<C>,
1881 id: SubscriptionId,
1882 reason: Termination,
1883 outputs: &mut Vec<Output<V>>,
1884) {
1885 cancel_all(entry, id, outputs);
1886 entry.operation = None;
1887 entry.pending_unsubscribe = false;
1888 outputs.push(Output::StateChanged {
1889 id,
1890 change: StateChange::Terminated(reason),
1891 });
1892}
1893
1894fn is_terminal<V>(output: &Output<V>) -> bool {
1895 matches!(
1896 output,
1897 Output::StateChanged {
1898 change: StateChange::Terminated(_),
1899 ..
1900 }
1901 )
1902}
1903
1904fn finish_if_terminal<C, V>(
1905 entries: &mut HashMap<SubscriptionId, Entry<C>>,
1906 id: SubscriptionId,
1907 outputs: &[Output<V>],
1908) {
1909 if outputs.iter().any(is_terminal) {
1910 entries.remove(&id);
1911 }
1912}