1use crate::accept::{SipAccept, SipAcceptError};
8use crate::accept_encoding::{SipAcceptEncoding, SipAcceptEncodingError};
9use crate::accept_language::{SipAcceptLanguage, SipAcceptLanguageError};
10use crate::auth::{SipAuthError, SipAuthValue};
11use crate::contact::ContactValue;
12use crate::geolocation::SipGeolocation;
13use crate::header_addr::{ParseSipHeaderAddrError, SipHeaderAddr};
14use crate::history_info::{HistoryInfo, HistoryInfoError};
15use crate::replaces::{SipReplaces, SipReplacesError};
16use crate::security::{SipSecurity, SipSecurityError};
17use crate::target_dialog::{SipTargetDialog, SipTargetDialogError};
18use crate::uri_info::{UriInfo, UriInfoError};
19use crate::via::{SipVia, SipViaError};
20use crate::warning::{SipWarning, SipWarningError};
21
22define_header_enum! {
23 tests_mod: sip_header_generated_tests,
24 error_type: ParseSipHeaderError => "unknown SIP header",
25 pub enum SipHeader {
31 Accept => "Accept",
33 AcceptContact => "Accept-Contact",
35 AcceptEncoding => "Accept-Encoding",
37 AcceptLanguage => "Accept-Language",
39 AcceptResourcePriority => "Accept-Resource-Priority",
41 AdditionalIdentity => "Additional-Identity",
43 AlertInfo => "Alert-Info",
45 AlertmsgError => "AlertMsg-Error",
47 Allow => "Allow",
49 AllowEvents => "Allow-Events",
51 AnswerMode => "Answer-Mode",
53 AttestationInfo => "Attestation-Info",
55 AuthenticationInfo => "Authentication-Info",
57 Authorization => "Authorization",
59 CallId => "Call-ID",
61 CallInfo => "Call-Info",
63 CellularNetworkInfo => "Cellular-Network-Info",
65 Contact => "Contact",
67 ContentDisposition => "Content-Disposition",
69 ContentEncoding => "Content-Encoding",
71 ContentId => "Content-ID",
73 ContentLanguage => "Content-Language",
75 ContentLength => "Content-Length",
77 ContentType => "Content-Type",
79 Cseq => "CSeq",
81 Date => "Date",
83 DcInfo => "DC-Info",
85 Encryption => "Encryption",
87 ErrorInfo => "Error-Info",
89 Event => "Event",
91 Expires => "Expires",
93 FeatureCaps => "Feature-Caps",
95 FlowTimer => "Flow-Timer",
97 From => "From",
99 Geolocation => "Geolocation",
101 GeolocationError => "Geolocation-Error",
103 GeolocationRouting => "Geolocation-Routing",
105 Hide => "Hide",
107 HistoryInfo => "History-Info",
109 Identity => "Identity",
111 IdentityInfo => "Identity-Info",
113 InfoPackage => "Info-Package",
115 InReplyTo => "In-Reply-To",
117 Join => "Join",
119 MaxBreadth => "Max-Breadth",
121 MaxForwards => "Max-Forwards",
123 MimeVersion => "MIME-Version",
125 MinExpires => "Min-Expires",
127 MinSe => "Min-SE",
129 Organization => "Organization",
131 OriginationId => "Origination-Id",
133 PAccessNetworkInfo => "P-Access-Network-Info",
135 PAnswerState => "P-Answer-State",
137 PAssertedIdentity => "P-Asserted-Identity",
139 PAssertedService => "P-Asserted-Service",
141 PAssociatedUri => "P-Associated-URI",
143 PCalledPartyId => "P-Called-Party-ID",
145 PChargeInfo => "P-Charge-Info",
147 PChargingFunctionAddresses => "P-Charging-Function-Addresses",
149 PChargingVector => "P-Charging-Vector",
151 PDcsTracePartyId => "P-DCS-Trace-Party-ID",
153 PDcsOsps => "P-DCS-OSPS",
155 PDcsBillingInfo => "P-DCS-Billing-Info",
157 PDcsLaes => "P-DCS-LAES",
159 PDcsRedirect => "P-DCS-Redirect",
161 PEarlyMedia => "P-Early-Media",
163 PMediaAuthorization => "P-Media-Authorization",
165 PPreferredIdentity => "P-Preferred-Identity",
167 PPreferredService => "P-Preferred-Service",
169 PPrivateNetworkIndication => "P-Private-Network-Indication",
171 PProfileKey => "P-Profile-Key",
173 PRefusedUriList => "P-Refused-URI-List",
175 PServedUser => "P-Served-User",
177 PUserDatabase => "P-User-Database",
179 PVisitedNetworkId => "P-Visited-Network-ID",
181 Path => "Path",
183 PermissionMissing => "Permission-Missing",
185 PolicyContact => "Policy-Contact",
187 PolicyId => "Policy-ID",
189 Priority => "Priority",
191 PriorityShare => "Priority-Share",
193 PriorityVerstat => "Priority-Verstat",
195 PrivAnswerMode => "Priv-Answer-Mode",
197 Privacy => "Privacy",
199 ProxyAuthenticate => "Proxy-Authenticate",
201 ProxyAuthorization => "Proxy-Authorization",
203 ProxyRequire => "Proxy-Require",
205 Rack => "RAck",
207 Reason => "Reason",
209 ReasonPhrase => "Reason-Phrase",
211 RecordRoute => "Record-Route",
213 RecvInfo => "Recv-Info",
215 ReferEventsAt => "Refer-Events-At",
217 ReferSub => "Refer-Sub",
219 ReferTo => "Refer-To",
221 ReferredBy => "Referred-By",
223 RejectContact => "Reject-Contact",
225 RelayedCharge => "Relayed-Charge",
227 Replaces => "Replaces",
229 ReplyTo => "Reply-To",
231 RequestDisposition => "Request-Disposition",
233 Require => "Require",
235 ResourcePriority => "Resource-Priority",
237 ResourceShare => "Resource-Share",
239 ResponseKey => "Response-Key",
241 ResponseSource => "Response-Source",
243 RestorationInfo => "Restoration-Info",
245 RetryAfter => "Retry-After",
247 Route => "Route",
249 Rseq => "RSeq",
251 SecurityClient => "Security-Client",
253 SecurityServer => "Security-Server",
255 SecurityVerify => "Security-Verify",
257 Server => "Server",
259 ServiceInteractInfo => "Service-Interact-Info",
261 ServiceRoute => "Service-Route",
263 SessionExpires => "Session-Expires",
265 SessionId => "Session-ID",
267 SipEtag => "SIP-ETag",
269 SipIfMatch => "SIP-If-Match",
271 Subject => "Subject",
273 SubscriptionState => "Subscription-State",
275 Supported => "Supported",
277 SuppressIfMatch => "Suppress-If-Match",
279 TargetDialog => "Target-Dialog",
281 Timestamp => "Timestamp",
283 To => "To",
285 TriggerConsent => "Trigger-Consent",
287 Unsupported => "Unsupported",
289 UserAgent => "User-Agent",
291 UserToUser => "User-to-User",
293 Via => "Via",
295 Warning => "Warning",
297 WwwAuthenticate => "WWW-Authenticate",
299 #[cfg(feature = "draft")]
302 Diversion => "Diversion",
303 #[cfg(feature = "draft")]
305 RemotePartyId => "Remote-Party-ID",
306 }
307}
308
309const COMPACT_FORMS: &[(u8, SipHeader)] = &[
314 (b'a', SipHeader::AcceptContact),
315 (b'b', SipHeader::ReferredBy),
316 (b'c', SipHeader::ContentType),
317 (b'd', SipHeader::RequestDisposition),
318 (b'e', SipHeader::ContentEncoding),
319 (b'f', SipHeader::From),
320 (b'i', SipHeader::CallId),
321 (b'j', SipHeader::RejectContact),
322 (b'k', SipHeader::Supported),
323 (b'l', SipHeader::ContentLength),
324 (b'm', SipHeader::Contact),
325 (b'n', SipHeader::IdentityInfo),
326 (b'o', SipHeader::Event),
327 (b'r', SipHeader::ReferTo),
328 (b's', SipHeader::Subject),
329 (b't', SipHeader::To),
330 (b'u', SipHeader::AllowEvents),
331 (b'v', SipHeader::Via),
332 (b'x', SipHeader::SessionExpires),
333 (b'y', SipHeader::Identity),
334];
335
336impl SipHeader {
337 pub fn from_compact(ch: u8) -> Option<Self> {
341 let lower = ch.to_ascii_lowercase();
342 COMPACT_FORMS
343 .iter()
344 .find(|(c, _)| *c == lower)
345 .map(|(_, h)| *h)
346 }
347
348 pub fn compact_form(&self) -> Option<char> {
350 COMPACT_FORMS
351 .iter()
352 .find(|(_, h)| h == self)
353 .map(|(c, _)| *c as char)
354 }
355
356 pub fn is_multi_valued(&self) -> bool {
361 if matches!(
362 self,
363 Self::Via
365 | Self::Route
366 | Self::RecordRoute
367 | Self::Contact
368 | Self::Allow
369 | Self::Supported
370 | Self::Require
371 | Self::ProxyRequire
372 | Self::Unsupported
373 | Self::Authorization
374 | Self::ProxyAuthorization
375 | Self::WwwAuthenticate
376 | Self::ProxyAuthenticate
377 | Self::Warning
378 | Self::ErrorInfo
379 | Self::CallInfo
380 | Self::AlertInfo
381 | Self::Accept
382 | Self::AcceptEncoding
383 | Self::AcceptLanguage
384 | Self::ContentEncoding
385 | Self::ContentLanguage
386 | Self::InReplyTo
387 | Self::PAssertedIdentity
389 | Self::PPreferredIdentity
390 | Self::AllowEvents
392 | Self::SecurityClient
394 | Self::SecurityServer
395 | Self::SecurityVerify
396 | Self::Path
398 | Self::ServiceRoute
400 | Self::HistoryInfo
402 | Self::Reason
404 | Self::AcceptContact
406 | Self::RejectContact
407 | Self::RequestDisposition
408 | Self::ResourcePriority
410 | Self::AcceptResourcePriority
411 | Self::PAssociatedUri
413 | Self::Geolocation
415 ) {
416 return true;
417 }
418
419 #[cfg(feature = "draft")]
420 if matches!(
421 self,
422 Self::Diversion
424 | Self::RemotePartyId
426 ) {
427 return true;
428 }
429
430 false
431 }
432
433 pub fn parse_name(name: &str) -> Result<Self, ParseSipHeaderError> {
438 if name.len() == 1 {
439 if let Some(h) = Self::from_compact(name.as_bytes()[0]) {
440 return Ok(h);
441 }
442 }
443 name.parse()
444 }
445}
446
447pub trait SipHeaderLookup {
473 fn sip_header_str(&self, name: &str) -> Option<&str>;
475
476 fn sip_header(&self, name: SipHeader) -> Option<&str> {
478 self.sip_header_str(name.as_str())
479 }
480
481 fn sip_header_all_str<'a>(&'a self, name: &str) -> Vec<&'a str> {
489 self.sip_header_str(name)
490 .into_iter()
491 .collect()
492 }
493
494 fn sip_header_all(&self, name: SipHeader) -> Vec<&str> {
496 self.sip_header_all_str(name.as_str())
497 }
498
499 fn call_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
503 let rows = self.sip_header_all(SipHeader::CallInfo);
504 if rows.is_empty() {
505 return Ok(None);
506 }
507 UriInfo::from_entries(split_all(rows)).map(Some)
508 }
509
510 fn history_info(&self) -> Result<Option<HistoryInfo>, HistoryInfoError> {
514 let rows = self.sip_header_all(SipHeader::HistoryInfo);
515 if rows.is_empty() {
516 return Ok(None);
517 }
518 HistoryInfo::from_entries(split_all(rows)).map(Some)
519 }
520
521 fn p_asserted_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
526 parse_addr_list(self.sip_header_all(SipHeader::PAssertedIdentity))
527 }
528
529 fn p_preferred_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
531 parse_addr_list(self.sip_header_all(SipHeader::PPreferredIdentity))
532 }
533
534 fn route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
536 parse_addr_list(self.sip_header_all(SipHeader::Route))
537 }
538
539 fn record_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
541 parse_addr_list(self.sip_header_all(SipHeader::RecordRoute))
542 }
543
544 fn path(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
546 parse_addr_list(self.sip_header_all(SipHeader::Path))
547 }
548
549 fn service_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
551 parse_addr_list(self.sip_header_all(SipHeader::ServiceRoute))
552 }
553
554 fn contact(&self) -> Result<Vec<ContactValue>, ParseSipHeaderAddrError> {
559 crate::contact::parse_contact_entries(split_all(self.sip_header_all(SipHeader::Contact)))
560 }
561
562 fn alert_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
564 let rows = self.sip_header_all(SipHeader::AlertInfo);
565 if rows.is_empty() {
566 return Ok(None);
567 }
568 UriInfo::from_entries(split_all(rows)).map(Some)
569 }
570
571 fn error_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
573 let rows = self.sip_header_all(SipHeader::ErrorInfo);
574 if rows.is_empty() {
575 return Ok(None);
576 }
577 UriInfo::from_entries(split_all(rows)).map(Some)
578 }
579
580 fn allow(&self) -> Vec<&str> {
582 split_trim(self.sip_header_all(SipHeader::Allow))
583 }
584
585 fn supported(&self) -> Vec<&str> {
587 split_trim(self.sip_header_all(SipHeader::Supported))
588 }
589
590 fn require_header(&self) -> Vec<&str> {
592 split_trim(self.sip_header_all(SipHeader::Require))
593 }
594
595 fn proxy_require(&self) -> Vec<&str> {
597 split_trim(self.sip_header_all(SipHeader::ProxyRequire))
598 }
599
600 fn unsupported(&self) -> Vec<&str> {
602 split_trim(self.sip_header_all(SipHeader::Unsupported))
603 }
604
605 fn allow_events(&self) -> Vec<&str> {
607 split_trim(self.sip_header_all(SipHeader::AllowEvents))
608 }
609
610 fn content_encoding(&self) -> Vec<&str> {
612 split_trim(self.sip_header_all(SipHeader::ContentEncoding))
613 }
614
615 fn content_language(&self) -> Vec<&str> {
617 split_trim(self.sip_header_all(SipHeader::ContentLanguage))
618 }
619
620 fn in_reply_to(&self) -> Vec<&str> {
622 split_trim(self.sip_header_all(SipHeader::InReplyTo))
623 }
624
625 fn via(&self) -> Result<Option<SipVia>, SipViaError> {
627 let rows = self.sip_header_all(SipHeader::Via);
628 if rows.is_empty() {
629 return Ok(None);
630 }
631 SipVia::from_entries(split_all(rows)).map(Some)
632 }
633
634 fn replaces(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
639 single_row(self, SipHeader::Replaces, SipReplacesError::InvalidFormat)?
640 .map(SipReplaces::parse)
641 .transpose()
642 }
643
644 fn join(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
649 single_row(self, SipHeader::Join, SipReplacesError::InvalidFormat)?
650 .map(SipReplaces::parse)
651 .transpose()
652 }
653
654 fn target_dialog(&self) -> Result<Option<SipTargetDialog>, SipTargetDialogError> {
659 single_row(
660 self,
661 SipHeader::TargetDialog,
662 SipTargetDialogError::InvalidFormat,
663 )?
664 .map(SipTargetDialog::parse)
665 .transpose()
666 }
667
668 fn authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
673 self.sip_header_all(SipHeader::Authorization)
674 .into_iter()
675 .map(|s| s.parse::<SipAuthValue>())
676 .collect()
677 }
678
679 fn proxy_authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
681 self.sip_header_all(SipHeader::ProxyAuthorization)
682 .into_iter()
683 .map(|s| s.parse::<SipAuthValue>())
684 .collect()
685 }
686
687 fn www_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
689 self.sip_header_all(SipHeader::WwwAuthenticate)
690 .into_iter()
691 .map(|s| s.parse::<SipAuthValue>())
692 .collect()
693 }
694
695 fn proxy_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
697 self.sip_header_all(SipHeader::ProxyAuthenticate)
698 .into_iter()
699 .map(|s| s.parse::<SipAuthValue>())
700 .collect()
701 }
702
703 fn warning(&self) -> Result<Option<SipWarning>, SipWarningError> {
705 let rows = self.sip_header_all(SipHeader::Warning);
706 if rows.is_empty() {
707 return Ok(None);
708 }
709 SipWarning::from_entries(split_all(rows)).map(Some)
710 }
711
712 fn security_client(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
714 let rows = self.sip_header_all(SipHeader::SecurityClient);
715 if rows.is_empty() {
716 return Ok(None);
717 }
718 SipSecurity::from_entries(split_all(rows)).map(Some)
719 }
720
721 fn security_server(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
723 let rows = self.sip_header_all(SipHeader::SecurityServer);
724 if rows.is_empty() {
725 return Ok(None);
726 }
727 SipSecurity::from_entries(split_all(rows)).map(Some)
728 }
729
730 fn security_verify(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
732 let rows = self.sip_header_all(SipHeader::SecurityVerify);
733 if rows.is_empty() {
734 return Ok(None);
735 }
736 SipSecurity::from_entries(split_all(rows)).map(Some)
737 }
738
739 fn accept(&self) -> Result<Option<SipAccept>, SipAcceptError> {
741 let rows = self.sip_header_all(SipHeader::Accept);
742 if rows.is_empty() {
743 return Ok(None);
744 }
745 SipAccept::from_entries(split_all(rows)).map(Some)
746 }
747
748 fn accept_encoding(&self) -> Result<Option<SipAcceptEncoding>, SipAcceptEncodingError> {
750 let rows = self.sip_header_all(SipHeader::AcceptEncoding);
751 if rows.is_empty() {
752 return Ok(None);
753 }
754 SipAcceptEncoding::from_entries(split_all(rows)).map(Some)
755 }
756
757 fn accept_language(&self) -> Result<Option<SipAcceptLanguage>, SipAcceptLanguageError> {
759 let rows = self.sip_header_all(SipHeader::AcceptLanguage);
760 if rows.is_empty() {
761 return Ok(None);
762 }
763 SipAcceptLanguage::from_entries(split_all(rows)).map(Some)
764 }
765
766 fn geolocation(&self) -> Option<SipGeolocation> {
771 let rows = self.sip_header_all(SipHeader::Geolocation);
772 if rows.is_empty() {
773 return None;
774 }
775 Some(SipGeolocation::from_entries(split_all(rows)))
776 }
777
778 #[cfg(feature = "draft")]
780 fn diversion(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
781 parse_addr_list(self.sip_header_all(SipHeader::Diversion))
782 }
783
784 #[cfg(feature = "draft")]
786 fn remote_party_id(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
787 parse_addr_list(self.sip_header_all(SipHeader::RemotePartyId))
788 }
789}
790
791fn split_all(rows: Vec<&str>) -> impl Iterator<Item = &str> {
792 rows.into_iter()
793 .flat_map(crate::split_comma_entries)
794}
795
796fn parse_addr_list(rows: Vec<&str>) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
797 split_all(rows)
798 .map(|s| {
799 s.trim()
800 .parse::<SipHeaderAddr>()
801 })
802 .collect()
803}
804
805fn split_trim(rows: Vec<&str>) -> Vec<&str> {
806 split_all(rows)
807 .map(str::trim)
808 .filter(|s| !s.is_empty())
809 .collect()
810}
811
812fn single_row<L, E>(
814 lookup: &L,
815 name: SipHeader,
816 invalid: impl FnOnce(String) -> E,
817) -> Result<Option<&str>, E>
818where
819 L: SipHeaderLookup + ?Sized,
820{
821 match lookup
822 .sip_header_all(name)
823 .as_slice()
824 {
825 [] => Ok(None),
826 [row] => Ok(Some(*row)),
827 _ => Err(invalid(format!("more than one {name} header"))),
828 }
829}
830
831impl SipHeaderLookup for std::collections::HashMap<String, String> {
833 fn sip_header_str(&self, name: &str) -> Option<&str> {
834 self.get(name)
835 .map(|s| s.as_str())
836 }
837}
838
839impl SipHeaderLookup for std::collections::HashMap<String, Vec<String>> {
841 fn sip_header_str(&self, name: &str) -> Option<&str> {
842 self.get(name)
843 .and_then(|v| v.first())
844 .map(|s| s.as_str())
845 }
846
847 fn sip_header_all_str(&self, name: &str) -> Vec<&str> {
848 self.get(name)
849 .map(|v| {
850 v.iter()
851 .map(|s| s.as_str())
852 .collect()
853 })
854 .unwrap_or_default()
855 }
856}
857
858#[cfg(test)]
859mod tests {
860 use super::*;
861 use std::collections::HashMap;
862
863 #[test]
864 fn display_round_trip() {
865 assert_eq!(SipHeader::CallInfo.to_string(), "Call-Info");
866 assert_eq!(SipHeader::HistoryInfo.to_string(), "History-Info");
867 assert_eq!(
868 SipHeader::PAssertedIdentity.to_string(),
869 "P-Asserted-Identity"
870 );
871 }
872
873 #[test]
874 fn as_ref_str() {
875 let h: &str = SipHeader::CallInfo.as_ref();
876 assert_eq!(h, "Call-Info");
877 }
878
879 #[test]
880 fn from_str_case_insensitive() {
881 assert_eq!("call-info".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
882 assert_eq!("CALL-INFO".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
883 assert_eq!(
884 "history-info".parse::<SipHeader>(),
885 Ok(SipHeader::HistoryInfo)
886 );
887 assert_eq!(
888 "p-asserted-identity".parse::<SipHeader>(),
889 Ok(SipHeader::PAssertedIdentity)
890 );
891 assert_eq!(
892 "P-ASSERTED-IDENTITY".parse::<SipHeader>(),
893 Ok(SipHeader::PAssertedIdentity)
894 );
895 }
896
897 #[test]
898 fn from_str_unknown() {
899 assert!("X-Custom"
900 .parse::<SipHeader>()
901 .is_err());
902 }
903
904 fn headers_with(pairs: &[(&str, &str)]) -> HashMap<String, String> {
905 pairs
906 .iter()
907 .map(|(k, v)| (k.to_string(), v.to_string()))
908 .collect()
909 }
910
911 #[test]
912 fn sip_header_by_enum() {
913 let h = headers_with(&[("Call-Info", "<urn:x>;purpose=icon")]);
914 assert_eq!(
915 h.sip_header(SipHeader::CallInfo),
916 Some("<urn:x>;purpose=icon")
917 );
918 }
919
920 #[test]
921 fn call_info_raw_lookup() {
922 let h = headers_with(&[(
923 "Call-Info",
924 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
925 )]);
926 assert_eq!(
927 h.sip_header(SipHeader::CallInfo),
928 Some("<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId")
929 );
930 }
931
932 #[test]
933 fn call_info_typed() {
934 let h = headers_with(&[(
935 "Call-Info",
936 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
937 )]);
938 let ci = h
939 .call_info()
940 .unwrap()
941 .unwrap();
942 assert_eq!(ci.len(), 1);
943 assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
944 }
945
946 #[test]
947 fn call_info_absent() {
948 let h = headers_with(&[]);
949 assert_eq!(
950 h.call_info()
951 .unwrap(),
952 None
953 );
954 }
955
956 #[test]
957 fn p_asserted_identity_typed() {
958 let h = headers_with(&[(
959 "P-Asserted-Identity",
960 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
961 )]);
962 let pais = h
963 .p_asserted_identity()
964 .unwrap();
965 assert_eq!(pais.len(), 1);
966 assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
967 }
968
969 #[test]
970 fn p_asserted_identity_multi_value() {
971 let h = headers_with(&[(
972 "P-Asserted-Identity",
973 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>, <tel:+15551234567>"#,
974 )]);
975 let pais = h
976 .p_asserted_identity()
977 .unwrap();
978 assert_eq!(pais.len(), 2);
979 assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
980 assert!(pais[1]
981 .uri()
982 .to_string()
983 .contains("+15551234567"));
984 }
985
986 #[test]
987 fn p_asserted_identity_absent() {
988 let h = headers_with(&[]);
989 assert!(h
990 .p_asserted_identity()
991 .unwrap()
992 .is_empty());
993 }
994
995 #[test]
996 fn history_info_raw_lookup() {
997 let h = headers_with(&[(
998 "History-Info",
999 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
1000 )]);
1001 assert!(h
1002 .sip_header(SipHeader::HistoryInfo)
1003 .unwrap()
1004 .contains("esrp.example.com"));
1005 }
1006
1007 #[test]
1008 fn history_info_typed() {
1009 let h = headers_with(&[(
1010 "History-Info",
1011 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
1012 )]);
1013 let hi = h
1014 .history_info()
1015 .unwrap()
1016 .unwrap();
1017 assert_eq!(hi.len(), 2);
1018 assert_eq!(hi.entries()[0].index(), Some("1"));
1019 assert_eq!(hi.entries()[1].index(), Some("1.1"));
1020 }
1021
1022 #[test]
1023 fn history_info_absent() {
1024 let h = headers_with(&[]);
1025 assert_eq!(
1026 h.history_info()
1027 .unwrap(),
1028 None
1029 );
1030 }
1031
1032 #[test]
1033 fn sip_header_all_str_default() {
1034 let h = headers_with(&[("Via", "SIP/2.0/UDP host1")]);
1035 let all = h.sip_header_all(SipHeader::Via);
1036 assert_eq!(all.len(), 1);
1037 assert_eq!(all[0], "SIP/2.0/UDP host1");
1038 }
1039
1040 #[test]
1041 fn sip_header_all_str_absent() {
1042 let h = headers_with(&[]);
1043 assert!(h
1044 .sip_header_all(SipHeader::Via)
1045 .is_empty());
1046 }
1047
1048 #[test]
1049 fn hashmap_vec_impl() {
1050 let mut h: HashMap<String, Vec<String>> = HashMap::new();
1051 h.insert(
1052 "Via".into(),
1053 vec!["SIP/2.0/UDP host1".into(), "SIP/2.0/UDP host2".into()],
1054 );
1055 assert_eq!(h.sip_header_str("Via"), Some("SIP/2.0/UDP host1"));
1056 let all = h.sip_header_all_str("Via");
1057 assert_eq!(all.len(), 2);
1058 assert_eq!(all[0], "SIP/2.0/UDP host1");
1059 assert_eq!(all[1], "SIP/2.0/UDP host2");
1060 }
1061
1062 #[test]
1063 fn extract_from_sip_message() {
1064 let msg = concat!(
1065 "INVITE sip:bob@host SIP/2.0\r\n",
1066 "Call-Info: <urn:emergency:uid:callid:abc>;purpose=emergency-CallId\r\n",
1067 "History-Info: <sip:esrp@example.com>;index=1\r\n",
1068 "P-Asserted-Identity: \"Corp\" <sip:+15551234567@198.51.100.1>\r\n",
1069 "\r\n",
1070 );
1071 let ci = SipHeader::CallInfo.extract_from(msg);
1072 assert_eq!(ci.len(), 1);
1073 assert_eq!(
1074 ci[0],
1075 "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"
1076 );
1077
1078 let hi = SipHeader::HistoryInfo.extract_from(msg);
1079 assert_eq!(hi.len(), 1);
1080 assert_eq!(hi[0], "<sip:esrp@example.com>;index=1");
1081
1082 let pai = SipHeader::PAssertedIdentity.extract_from(msg);
1083 assert_eq!(pai.len(), 1);
1084 assert_eq!(pai[0], "\"Corp\" <sip:+15551234567@198.51.100.1>");
1085 }
1086
1087 #[test]
1088 fn extract_from_missing() {
1089 let msg = concat!(
1090 "INVITE sip:bob@host SIP/2.0\r\n",
1091 "From: Alice <sip:alice@host>\r\n",
1092 "\r\n",
1093 );
1094 assert!(SipHeader::CallInfo
1095 .extract_from(msg)
1096 .is_empty());
1097 assert!(SipHeader::PAssertedIdentity
1098 .extract_from(msg)
1099 .is_empty());
1100 }
1101
1102 #[test]
1103 fn missing_headers_return_none() {
1104 let h = headers_with(&[]);
1105 assert_eq!(h.sip_header(SipHeader::CallInfo), None);
1106 assert_eq!(
1107 h.call_info()
1108 .unwrap(),
1109 None
1110 );
1111 assert_eq!(h.sip_header(SipHeader::HistoryInfo), None);
1112 assert_eq!(
1113 h.history_info()
1114 .unwrap(),
1115 None
1116 );
1117 assert_eq!(h.sip_header(SipHeader::PAssertedIdentity), None);
1118 assert!(h
1119 .p_asserted_identity()
1120 .unwrap()
1121 .is_empty());
1122 }
1123
1124 #[test]
1125 fn route_accessor() {
1126 let h = headers_with(&[(
1127 "Route",
1128 "<sip:proxy1.example.com;lr>, <sip:proxy2.example.com;lr>",
1129 )]);
1130 let routes = h
1131 .route()
1132 .unwrap();
1133 assert_eq!(routes.len(), 2);
1134 assert!(routes[0]
1135 .uri()
1136 .to_string()
1137 .contains("proxy1"));
1138 assert!(routes[1]
1139 .uri()
1140 .to_string()
1141 .contains("proxy2"));
1142 }
1143
1144 #[test]
1145 fn route_absent() {
1146 let h = headers_with(&[]);
1147 assert!(h
1148 .route()
1149 .unwrap()
1150 .is_empty());
1151 }
1152
1153 #[test]
1154 fn record_route_accessor() {
1155 let h = headers_with(&[("Record-Route", "<sip:ss1.example.com;lr>")]);
1156 let rr = h
1157 .record_route()
1158 .unwrap();
1159 assert_eq!(rr.len(), 1);
1160 }
1161
1162 #[test]
1163 fn allow_accessor() {
1164 let h = headers_with(&[("Allow", "INVITE, ACK, OPTIONS, BYE")]);
1165 let methods = h.allow();
1166 assert_eq!(methods, vec!["INVITE", "ACK", "OPTIONS", "BYE"]);
1167 }
1168
1169 #[test]
1170 fn allow_absent() {
1171 let h = headers_with(&[]);
1172 assert!(h
1173 .allow()
1174 .is_empty());
1175 }
1176
1177 #[test]
1178 fn supported_accessor() {
1179 let h = headers_with(&[("Supported", "100rel, timer")]);
1180 let opts = h.supported();
1181 assert_eq!(opts, vec!["100rel", "timer"]);
1182 }
1183
1184 #[test]
1185 fn require_header_accessor() {
1186 let h = headers_with(&[("Require", "100rel")]);
1187 assert_eq!(h.require_header(), vec!["100rel"]);
1188 }
1189
1190 #[test]
1191 fn alert_info_accessor() {
1192 let h = headers_with(&[("Alert-Info", "<http://www.example.com/sounds/moo.wav>")]);
1193 let ai = h
1194 .alert_info()
1195 .unwrap()
1196 .unwrap();
1197 assert_eq!(ai.len(), 1);
1198 assert!(ai.entries()[0]
1199 .data
1200 .contains("moo.wav"));
1201 }
1202
1203 #[test]
1204 fn error_info_accessor() {
1205 let h = headers_with(&[("Error-Info", "<sip:not-in-service@example.com>")]);
1206 let ei = h
1207 .error_info()
1208 .unwrap()
1209 .unwrap();
1210 assert_eq!(ei.len(), 1);
1211 }
1212
1213 #[test]
1214 fn p_preferred_identity_accessor() {
1215 let h = headers_with(&[(
1216 "P-Preferred-Identity",
1217 r#""User" <sip:+15551234567@198.51.100.1>"#,
1218 )]);
1219 let ppi = h
1220 .p_preferred_identity()
1221 .unwrap();
1222 assert_eq!(ppi.len(), 1);
1223 assert_eq!(ppi[0].display_name(), Some("User"));
1224 }
1225
1226 #[test]
1227 fn content_encoding_accessor() {
1228 let h = headers_with(&[("Content-Encoding", "gzip")]);
1229 assert_eq!(h.content_encoding(), vec!["gzip"]);
1230 }
1231
1232 #[test]
1233 fn contact_accessor() {
1234 let h = headers_with(&[("Contact", "<sip:alice@198.51.100.1>")]);
1235 let contacts = h
1236 .contact()
1237 .unwrap();
1238 assert_eq!(contacts.len(), 1);
1239 assert!(matches!(&contacts[0], ContactValue::Addr(_)));
1240 }
1241
1242 #[test]
1243 fn contact_wildcard() {
1244 let h = headers_with(&[("Contact", "*")]);
1245 let contacts = h
1246 .contact()
1247 .unwrap();
1248 assert_eq!(contacts.len(), 1);
1249 assert!(matches!(contacts[0], ContactValue::Wildcard));
1250 }
1251
1252 #[test]
1253 fn contact_absent() {
1254 let h = headers_with(&[]);
1255 assert!(h
1256 .contact()
1257 .unwrap()
1258 .is_empty());
1259 }
1260
1261 #[test]
1262 fn in_reply_to_accessor() {
1263 let h = headers_with(&[("In-Reply-To", "call1@example.com, call2@example.com")]);
1264 let calls = h.in_reply_to();
1265 assert_eq!(calls.len(), 2);
1266 assert_eq!(calls[0], "call1@example.com");
1267 }
1268
1269 #[test]
1270 fn via_accessor() {
1271 let h = headers_with(&[("Via", "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bK776")]);
1272 let via = h
1273 .via()
1274 .unwrap()
1275 .unwrap();
1276 assert_eq!(via.len(), 1);
1277 assert_eq!(via.entries()[0].transport(), "UDP");
1278 assert_eq!(via.entries()[0].host(), "198.51.100.1");
1279 }
1280
1281 #[test]
1282 fn replaces_accessor() {
1283 let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1284 let r = h
1285 .replaces()
1286 .unwrap()
1287 .unwrap();
1288 assert_eq!(r.call_id(), "abc123@203.0.113.5");
1289 assert_eq!(r.to_tag(), "t1");
1290 assert_eq!(r.from_tag(), "f1");
1291 }
1292
1293 #[test]
1294 fn replaces_absent() {
1295 let h = headers_with(&[]);
1296 assert!(h
1297 .replaces()
1298 .unwrap()
1299 .is_none());
1300 }
1301
1302 #[test]
1303 fn replaces_malformed_err() {
1304 let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1")]);
1305 assert!(h
1306 .replaces()
1307 .is_err());
1308 }
1309
1310 #[test]
1311 fn join_accessor() {
1312 let h = headers_with(&[("Join", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1313 let j = h
1314 .join()
1315 .unwrap()
1316 .unwrap();
1317 assert_eq!(j.call_id(), "abc123@203.0.113.5");
1318 }
1319
1320 #[test]
1321 fn target_dialog_accessor() {
1322 let h = headers_with(&[(
1323 "Target-Dialog",
1324 "abc123@203.0.113.5;local-tag=l1;remote-tag=r1",
1325 )]);
1326 let t = h
1327 .target_dialog()
1328 .unwrap()
1329 .unwrap();
1330 assert_eq!(t.local_tag(), "l1");
1331 assert_eq!(t.remote_tag(), "r1");
1332 }
1333
1334 #[test]
1335 fn authorization_accessor() {
1336 let h = headers_with(&[(
1337 "Authorization",
1338 "Digest username=\"alice\", realm=\"example.com\", nonce=\"abc123\"",
1339 )]);
1340 let auth = h
1341 .authorization()
1342 .unwrap();
1343 assert_eq!(auth.len(), 1);
1344 assert_eq!(auth[0].scheme(), "Digest");
1345 assert_eq!(auth[0].username(), Some("alice"));
1346 assert_eq!(auth[0].realm(), Some("example.com"));
1347 }
1348
1349 #[test]
1350 fn www_authenticate_accessor() {
1351 let h = headers_with(&[(
1352 "WWW-Authenticate",
1353 "Digest realm=\"example.com\", nonce=\"xyz789\"",
1354 )]);
1355 let challenges = h
1356 .www_authenticate()
1357 .unwrap();
1358 assert_eq!(challenges.len(), 1);
1359 assert_eq!(challenges[0].realm(), Some("example.com"));
1360 }
1361
1362 #[test]
1363 fn warning_accessor() {
1364 let h = headers_with(&[(
1365 "Warning",
1366 "301 198.51.100.1 \"Incompatible network protocol\"",
1367 )]);
1368 let w = h
1369 .warning()
1370 .unwrap()
1371 .unwrap();
1372 assert_eq!(w.len(), 1);
1373 assert_eq!(w.entries()[0].code(), 301);
1374 }
1375
1376 #[test]
1377 fn security_client_accessor() {
1378 let h = headers_with(&[("Security-Client", "tls;q=0.2, digest;d-qop=auth;q=0.1")]);
1379 let sec = h
1380 .security_client()
1381 .unwrap()
1382 .unwrap();
1383 assert_eq!(sec.len(), 2);
1384 assert_eq!(sec.entries()[0].mechanism(), "tls");
1385 }
1386
1387 #[test]
1388 fn accept_accessor() {
1389 let h = headers_with(&[("Accept", "application/sdp, application/pidf+xml;q=0.5")]);
1390 let accept = h
1391 .accept()
1392 .unwrap()
1393 .unwrap();
1394 assert_eq!(accept.len(), 2);
1395 assert_eq!(accept.entries()[0].media_range(), "application/sdp");
1396 }
1397
1398 #[test]
1399 fn accept_encoding_accessor() {
1400 let h = headers_with(&[("Accept-Encoding", "gzip;q=1.0, identity;q=0.5")]);
1401 let ae = h
1402 .accept_encoding()
1403 .unwrap()
1404 .unwrap();
1405 assert_eq!(ae.len(), 2);
1406 assert_eq!(ae.entries()[0].encoding(), "gzip");
1407 }
1408
1409 #[test]
1410 fn accept_language_accessor() {
1411 let h = headers_with(&[("Accept-Language", "en;q=0.9, fr;q=0.8")]);
1412 let al = h
1413 .accept_language()
1414 .unwrap()
1415 .unwrap();
1416 assert_eq!(al.len(), 2);
1417 assert_eq!(al.entries()[0].language(), "en");
1418 }
1419
1420 #[test]
1421 fn geolocation_accessor_reads_every_row() {
1422 let mut h: HashMap<String, Vec<String>> = HashMap::new();
1423 h.insert(
1424 "Geolocation".to_string(),
1425 vec![
1426 "<cid:loc@example.com>".to_string(),
1427 "<https://lis.example.com/held/a>;inserted-by=example.org".to_string(),
1428 ],
1429 );
1430 let geo = h
1431 .geolocation()
1432 .unwrap();
1433 assert_eq!(geo.len(), 2);
1434 assert_eq!(geo.cid(), Some("loc@example.com"));
1435 assert_eq!(geo.url(), Some("https://lis.example.com/held/a"));
1436 }
1437
1438 #[test]
1439 fn geolocation_absent() {
1440 let h = headers_with(&[]);
1441 assert!(h
1442 .geolocation()
1443 .is_none());
1444 }
1445}
1446
1447#[cfg(test)]
1448mod compact_form_tests {
1449 use super::*;
1450
1451 #[test]
1452 fn from_compact_known() {
1453 assert_eq!(SipHeader::from_compact(b'f'), Some(SipHeader::From));
1454 assert_eq!(SipHeader::from_compact(b'F'), Some(SipHeader::From));
1455 assert_eq!(SipHeader::from_compact(b'v'), Some(SipHeader::Via));
1456 assert_eq!(SipHeader::from_compact(b'i'), Some(SipHeader::CallId));
1457 assert_eq!(SipHeader::from_compact(b'm'), Some(SipHeader::Contact));
1458 assert_eq!(SipHeader::from_compact(b't'), Some(SipHeader::To));
1459 assert_eq!(SipHeader::from_compact(b'c'), Some(SipHeader::ContentType));
1460 }
1461
1462 #[test]
1463 fn from_compact_unknown() {
1464 assert_eq!(SipHeader::from_compact(b'z'), None);
1465 assert_eq!(SipHeader::from_compact(b'g'), None);
1466 }
1467
1468 #[test]
1469 fn compact_form_roundtrip() {
1470 assert_eq!(SipHeader::From.compact_form(), Some('f'));
1471 assert_eq!(SipHeader::Via.compact_form(), Some('v'));
1472 assert_eq!(SipHeader::CallId.compact_form(), Some('i'));
1473 assert_eq!(SipHeader::Contact.compact_form(), Some('m'));
1474 }
1475
1476 #[test]
1477 fn compact_form_absent() {
1478 assert_eq!(SipHeader::HistoryInfo.compact_form(), None);
1479 assert_eq!(SipHeader::PAssertedIdentity.compact_form(), None);
1480 }
1481
1482 #[test]
1483 fn parse_name_compact() {
1484 assert_eq!(SipHeader::parse_name("f"), Ok(SipHeader::From));
1485 assert_eq!(SipHeader::parse_name("F"), Ok(SipHeader::From));
1486 assert_eq!(SipHeader::parse_name("v"), Ok(SipHeader::Via));
1487 }
1488
1489 #[test]
1490 fn parse_name_full() {
1491 assert_eq!(SipHeader::parse_name("From"), Ok(SipHeader::From));
1492 assert_eq!(SipHeader::parse_name("Via"), Ok(SipHeader::Via));
1493 }
1494
1495 #[test]
1496 fn parse_name_unknown() {
1497 assert!(SipHeader::parse_name("X-Custom").is_err());
1498 }
1499
1500 #[test]
1501 fn all_compact_forms_resolve() {
1502 let expected = [
1503 ('a', SipHeader::AcceptContact),
1504 ('b', SipHeader::ReferredBy),
1505 ('c', SipHeader::ContentType),
1506 ('d', SipHeader::RequestDisposition),
1507 ('e', SipHeader::ContentEncoding),
1508 ('f', SipHeader::From),
1509 ('i', SipHeader::CallId),
1510 ('j', SipHeader::RejectContact),
1511 ('k', SipHeader::Supported),
1512 ('l', SipHeader::ContentLength),
1513 ('m', SipHeader::Contact),
1514 ('n', SipHeader::IdentityInfo),
1515 ('o', SipHeader::Event),
1516 ('r', SipHeader::ReferTo),
1517 ('s', SipHeader::Subject),
1518 ('t', SipHeader::To),
1519 ('u', SipHeader::AllowEvents),
1520 ('v', SipHeader::Via),
1521 ('x', SipHeader::SessionExpires),
1522 ('y', SipHeader::Identity),
1523 ];
1524 for (ch, header) in expected {
1525 assert_eq!(
1526 SipHeader::from_compact(ch as u8),
1527 Some(header),
1528 "compact form '{ch}' failed"
1529 );
1530 assert_eq!(
1531 header.compact_form(),
1532 Some(ch),
1533 "compact_form() for {} failed",
1534 header
1535 );
1536 }
1537 }
1538}
1539
1540#[cfg(test)]
1541mod multi_valued_tests {
1542 use super::*;
1543
1544 #[test]
1545 fn rfc3261_multi_valued_headers() {
1546 assert!(SipHeader::Via.is_multi_valued());
1547 assert!(SipHeader::Route.is_multi_valued());
1548 assert!(SipHeader::RecordRoute.is_multi_valued());
1549 assert!(SipHeader::Contact.is_multi_valued());
1550 assert!(SipHeader::Allow.is_multi_valued());
1551 assert!(SipHeader::Supported.is_multi_valued());
1552 assert!(SipHeader::Require.is_multi_valued());
1553 assert!(SipHeader::ProxyRequire.is_multi_valued());
1554 assert!(SipHeader::Unsupported.is_multi_valued());
1555 assert!(SipHeader::Authorization.is_multi_valued());
1556 assert!(SipHeader::ProxyAuthorization.is_multi_valued());
1557 assert!(SipHeader::WwwAuthenticate.is_multi_valued());
1558 assert!(SipHeader::ProxyAuthenticate.is_multi_valued());
1559 assert!(SipHeader::Warning.is_multi_valued());
1560 assert!(SipHeader::ErrorInfo.is_multi_valued());
1561 assert!(SipHeader::CallInfo.is_multi_valued());
1562 assert!(SipHeader::AlertInfo.is_multi_valued());
1563 assert!(SipHeader::Accept.is_multi_valued());
1564 assert!(SipHeader::AcceptEncoding.is_multi_valued());
1565 assert!(SipHeader::AcceptLanguage.is_multi_valued());
1566 assert!(SipHeader::ContentEncoding.is_multi_valued());
1567 assert!(SipHeader::ContentLanguage.is_multi_valued());
1568 assert!(SipHeader::InReplyTo.is_multi_valued());
1569 }
1570
1571 #[test]
1572 fn extension_multi_valued_headers() {
1573 assert!(SipHeader::PAssertedIdentity.is_multi_valued());
1574 assert!(SipHeader::PPreferredIdentity.is_multi_valued());
1575 assert!(SipHeader::AllowEvents.is_multi_valued());
1576 assert!(SipHeader::SecurityClient.is_multi_valued());
1577 assert!(SipHeader::SecurityServer.is_multi_valued());
1578 assert!(SipHeader::SecurityVerify.is_multi_valued());
1579 assert!(SipHeader::Path.is_multi_valued());
1580 assert!(SipHeader::ServiceRoute.is_multi_valued());
1581 assert!(SipHeader::HistoryInfo.is_multi_valued());
1582 assert!(SipHeader::Reason.is_multi_valued());
1583 assert!(SipHeader::AcceptContact.is_multi_valued());
1584 assert!(SipHeader::RejectContact.is_multi_valued());
1585 assert!(SipHeader::RequestDisposition.is_multi_valued());
1586 assert!(SipHeader::ResourcePriority.is_multi_valued());
1587 assert!(SipHeader::AcceptResourcePriority.is_multi_valued());
1588 assert!(SipHeader::PAssociatedUri.is_multi_valued());
1589 assert!(SipHeader::Geolocation.is_multi_valued());
1590 }
1591
1592 #[test]
1593 fn single_valued_headers() {
1594 assert!(!SipHeader::From.is_multi_valued());
1595 assert!(!SipHeader::To.is_multi_valued());
1596 assert!(!SipHeader::CallId.is_multi_valued());
1597 assert!(!SipHeader::Cseq.is_multi_valued());
1598 assert!(!SipHeader::MaxForwards.is_multi_valued());
1599 assert!(!SipHeader::ContentType.is_multi_valued());
1600 assert!(!SipHeader::ContentLength.is_multi_valued());
1601 assert!(!SipHeader::Expires.is_multi_valued());
1602 assert!(!SipHeader::Date.is_multi_valued());
1603 assert!(!SipHeader::Subject.is_multi_valued());
1604 assert!(!SipHeader::ReplyTo.is_multi_valued());
1605 assert!(!SipHeader::Server.is_multi_valued());
1606 assert!(!SipHeader::UserAgent.is_multi_valued());
1607 }
1608
1609 #[test]
1610 #[cfg(feature = "draft")]
1611 fn draft_multi_valued_headers() {
1612 assert!(SipHeader::Diversion.is_multi_valued());
1613 assert!(SipHeader::RemotePartyId.is_multi_valued());
1614 }
1615
1616 #[test]
1617 #[cfg(feature = "draft")]
1618 fn draft_parse_roundtrip() {
1619 let d: SipHeader = "Diversion"
1620 .parse()
1621 .unwrap();
1622 assert_eq!(d, SipHeader::Diversion);
1623 assert_eq!(d.to_string(), "Diversion");
1624
1625 let r: SipHeader = "remote-party-id"
1626 .parse()
1627 .unwrap();
1628 assert_eq!(r, SipHeader::RemotePartyId);
1629 assert_eq!(r.to_string(), "Remote-Party-ID");
1630 }
1631}
1632
1633#[cfg(test)]
1634mod special_case_tests {
1635 use super::*;
1636
1637 #[test]
1638 fn cseq_variants() {
1639 assert_eq!("CSeq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1640 assert_eq!("cseq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1641 assert_eq!("CSEQ".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1642 assert_eq!(SipHeader::Cseq.to_string(), "CSeq");
1643 }
1644
1645 #[test]
1646 fn www_authenticate_variants() {
1647 assert_eq!(
1648 "WWW-Authenticate".parse::<SipHeader>(),
1649 Ok(SipHeader::WwwAuthenticate)
1650 );
1651 assert_eq!(
1652 "www-authenticate".parse::<SipHeader>(),
1653 Ok(SipHeader::WwwAuthenticate)
1654 );
1655 assert_eq!(SipHeader::WwwAuthenticate.to_string(), "WWW-Authenticate");
1656 }
1657
1658 #[test]
1659 fn rack_rseq_variants() {
1660 assert_eq!("RAck".parse::<SipHeader>(), Ok(SipHeader::Rack));
1661 assert_eq!("rack".parse::<SipHeader>(), Ok(SipHeader::Rack));
1662 assert_eq!(SipHeader::Rack.to_string(), "RAck");
1663
1664 assert_eq!("RSeq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1665 assert_eq!("rseq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1666 assert_eq!(SipHeader::Rseq.to_string(), "RSeq");
1667 }
1668
1669 #[test]
1670 fn user_to_user_variants() {
1671 assert_eq!(
1672 "User-to-User".parse::<SipHeader>(),
1673 Ok(SipHeader::UserToUser)
1674 );
1675 assert_eq!(
1676 "user-to-user".parse::<SipHeader>(),
1677 Ok(SipHeader::UserToUser)
1678 );
1679 assert_eq!(SipHeader::UserToUser.to_string(), "User-to-User");
1680 }
1681
1682 #[test]
1683 fn p_header_variants() {
1684 assert_eq!(
1685 "P-DCS-Trace-Party-ID".parse::<SipHeader>(),
1686 Ok(SipHeader::PDcsTracePartyId)
1687 );
1688 assert_eq!(
1689 "p-dcs-trace-party-id".parse::<SipHeader>(),
1690 Ok(SipHeader::PDcsTracePartyId)
1691 );
1692 assert_eq!(
1693 SipHeader::PDcsTracePartyId.to_string(),
1694 "P-DCS-Trace-Party-ID"
1695 );
1696 }
1697}
1698
1699#[cfg(test)]
1700mod multi_row_tests {
1701 use super::*;
1702 use std::collections::HashMap;
1703
1704 fn rows(pairs: &[(&str, &[&str])]) -> HashMap<String, Vec<String>> {
1705 pairs
1706 .iter()
1707 .map(|(k, vs)| {
1708 (
1709 k.to_string(),
1710 vs.iter()
1711 .map(|v| v.to_string())
1712 .collect(),
1713 )
1714 })
1715 .collect()
1716 }
1717
1718 #[test]
1719 fn via_reads_every_row() {
1720 let h = rows(&[(
1721 "Via",
1722 &[
1723 "SIP/2.0/UDP 198.51.100.1;branch=z9hG4bK1",
1724 "SIP/2.0/TCP 203.0.113.5",
1725 ],
1726 )]);
1727 let via = h
1728 .via()
1729 .unwrap()
1730 .unwrap();
1731 assert_eq!(via.len(), 2);
1732 assert_eq!(via.entries()[1].transport(), "TCP");
1733 }
1734
1735 #[test]
1736 fn via_row_with_list_plus_row() {
1737 let h = rows(&[(
1738 "Via",
1739 &[
1740 "SIP/2.0/UDP 198.51.100.1, SIP/2.0/UDP 198.51.100.2",
1741 "SIP/2.0/TCP 203.0.113.5",
1742 ],
1743 )]);
1744 assert_eq!(
1745 h.via()
1746 .unwrap()
1747 .unwrap()
1748 .len(),
1749 3
1750 );
1751 }
1752
1753 #[test]
1754 fn via_blank_rows() {
1755 let h = rows(&[("Via", &[""])]);
1756 assert!(matches!(h.via(), Err(SipViaError::Empty)));
1757 let h = rows(&[("Via", &[" "])]);
1758 assert!(matches!(h.via(), Err(SipViaError::InvalidFormat(_))));
1759 }
1760
1761 #[test]
1762 fn typed_lists_read_every_row() {
1763 let h = rows(&[
1764 (
1765 "Warning",
1766 &[r#"301 example.com "a""#, r#"399 example.org "b""#],
1767 ),
1768 ("Accept", &["application/sdp", "text/plain"]),
1769 ("Accept-Encoding", &["gzip", "identity"]),
1770 ("Accept-Language", &["en", "fr"]),
1771 ("Security-Client", &["tls", "digest"]),
1772 ("Security-Server", &["tls", "digest"]),
1773 ("Security-Verify", &["tls", "digest"]),
1774 (
1775 "Call-Info",
1776 &["<http://example.com/a>", "<http://example.com/b>"],
1777 ),
1778 (
1779 "Alert-Info",
1780 &["<http://example.com/a>", "<http://example.com/b>"],
1781 ),
1782 (
1783 "Error-Info",
1784 &["<http://example.com/a>", "<http://example.com/b>"],
1785 ),
1786 (
1787 "History-Info",
1788 &[
1789 "<sip:a@example.com>;index=1",
1790 "<sip:b@example.com>;index=1.1",
1791 ],
1792 ),
1793 ]);
1794 assert_eq!(
1795 h.warning()
1796 .unwrap()
1797 .unwrap()
1798 .len(),
1799 2
1800 );
1801 assert_eq!(
1802 h.accept()
1803 .unwrap()
1804 .unwrap()
1805 .len(),
1806 2
1807 );
1808 assert_eq!(
1809 h.accept_encoding()
1810 .unwrap()
1811 .unwrap()
1812 .len(),
1813 2
1814 );
1815 assert_eq!(
1816 h.accept_language()
1817 .unwrap()
1818 .unwrap()
1819 .len(),
1820 2
1821 );
1822 assert_eq!(
1823 h.security_client()
1824 .unwrap()
1825 .unwrap()
1826 .len(),
1827 2
1828 );
1829 assert_eq!(
1830 h.security_server()
1831 .unwrap()
1832 .unwrap()
1833 .len(),
1834 2
1835 );
1836 assert_eq!(
1837 h.security_verify()
1838 .unwrap()
1839 .unwrap()
1840 .len(),
1841 2
1842 );
1843 assert_eq!(
1844 h.call_info()
1845 .unwrap()
1846 .unwrap()
1847 .len(),
1848 2
1849 );
1850 assert_eq!(
1851 h.alert_info()
1852 .unwrap()
1853 .unwrap()
1854 .len(),
1855 2
1856 );
1857 assert_eq!(
1858 h.error_info()
1859 .unwrap()
1860 .unwrap()
1861 .len(),
1862 2
1863 );
1864 assert_eq!(
1865 h.history_info()
1866 .unwrap()
1867 .unwrap()
1868 .len(),
1869 2
1870 );
1871 }
1872
1873 #[test]
1874 fn contact_reads_every_row() {
1875 let h = rows(&[(
1876 "Contact",
1877 &[
1878 "<sip:a@example.com>",
1879 "<sip:b@example.com>, <sip:c@example.com>",
1880 ],
1881 )]);
1882 assert_eq!(
1883 h.contact()
1884 .unwrap()
1885 .len(),
1886 3
1887 );
1888 let h = rows(&[("Contact", &["*", "<sip:a@example.com>"])]);
1889 assert!(h
1890 .contact()
1891 .is_err());
1892 let h = rows(&[("Contact", &[""])]);
1893 assert!(h
1894 .contact()
1895 .unwrap()
1896 .is_empty());
1897 }
1898
1899 #[test]
1900 fn token_lists_read_every_row() {
1901 let h = rows(&[
1902 ("Allow", &["INVITE, ACK", "BYE"]),
1903 ("Supported", &["100rel", "timer"]),
1904 ("Require", &["100rel", "timer"]),
1905 ("Proxy-Require", &["100rel", "timer"]),
1906 ("Unsupported", &["100rel", "timer"]),
1907 ("Allow-Events", &["dialog", "presence"]),
1908 ("Content-Encoding", &["gzip", "identity"]),
1909 ("Content-Language", &["en", "fr"]),
1910 ("In-Reply-To", &["a@example.com", "b@example.com"]),
1911 ]);
1912 assert_eq!(h.allow(), vec!["INVITE", "ACK", "BYE"]);
1913 assert_eq!(
1914 h.supported()
1915 .len(),
1916 2
1917 );
1918 assert_eq!(
1919 h.require_header()
1920 .len(),
1921 2
1922 );
1923 assert_eq!(
1924 h.proxy_require()
1925 .len(),
1926 2
1927 );
1928 assert_eq!(
1929 h.unsupported()
1930 .len(),
1931 2
1932 );
1933 assert_eq!(
1934 h.allow_events()
1935 .len(),
1936 2
1937 );
1938 assert_eq!(
1939 h.content_encoding()
1940 .len(),
1941 2
1942 );
1943 assert_eq!(
1944 h.content_language()
1945 .len(),
1946 2
1947 );
1948 assert_eq!(
1949 h.in_reply_to()
1950 .len(),
1951 2
1952 );
1953 }
1954
1955 #[test]
1956 fn token_list_blank_rows() {
1957 let h = rows(&[("Allow", &[""])]);
1958 assert!(h
1959 .allow()
1960 .is_empty());
1961 let h = rows(&[("Allow", &[" "])]);
1962 assert!(h
1963 .allow()
1964 .is_empty());
1965 }
1966
1967 #[test]
1968 fn token_list_drops_empty_entries() {
1969 let h = rows(&[("Allow", &["INVITE, , ACK,", ",BYE"])]);
1970 assert_eq!(h.allow(), vec!["INVITE", "ACK", "BYE"]);
1971 }
1972
1973 #[test]
1974 fn dialog_id_headers_reject_multiple_rows() {
1975 let r = "abc@example.com;to-tag=t1;from-tag=f1";
1976 let h = rows(&[("Replaces", &[r, r]), ("Join", &[r, r])]);
1977 assert!(matches!(
1978 h.replaces(),
1979 Err(SipReplacesError::InvalidFormat(_))
1980 ));
1981 assert!(matches!(h.join(), Err(SipReplacesError::InvalidFormat(_))));
1982 let t = "abc@example.com;local-tag=l1;remote-tag=r1";
1983 let h = rows(&[("Target-Dialog", &[t, t])]);
1984 assert!(matches!(
1985 h.target_dialog(),
1986 Err(SipTargetDialogError::InvalidFormat(_))
1987 ));
1988 }
1989
1990 #[test]
1991 fn dialog_id_headers_single_row_and_absent() {
1992 let h = rows(&[
1993 ("Replaces", &["abc@example.com;to-tag=t1;from-tag=f1"]),
1994 (
1995 "Target-Dialog",
1996 &["abc@example.com;local-tag=l1;remote-tag=r1"],
1997 ),
1998 ]);
1999 assert_eq!(
2000 h.replaces()
2001 .unwrap()
2002 .unwrap()
2003 .to_tag(),
2004 "t1"
2005 );
2006 assert_eq!(
2007 h.target_dialog()
2008 .unwrap()
2009 .unwrap()
2010 .local_tag(),
2011 "l1"
2012 );
2013 assert!(h
2014 .join()
2015 .unwrap()
2016 .is_none());
2017 }
2018
2019 #[test]
2020 fn addr_list_blank_row_is_error() {
2021 let h = rows(&[("Route", &[" "])]);
2022 assert!(h
2023 .route()
2024 .is_err());
2025 }
2026}