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::header_addr::{ParseSipHeaderAddrError, SipHeaderAddr};
13use crate::history_info::{HistoryInfo, HistoryInfoError};
14use crate::replaces::{SipReplaces, SipReplacesError};
15use crate::security::{SipSecurity, SipSecurityError};
16use crate::target_dialog::{SipTargetDialog, SipTargetDialogError};
17use crate::uri_info::{UriInfo, UriInfoError};
18use crate::via::{SipVia, SipViaError};
19use crate::warning::{SipWarning, SipWarningError};
20
21define_header_enum! {
22 tests_mod: sip_header_generated_tests,
23 error_type: ParseSipHeaderError => "unknown SIP header",
24 pub enum SipHeader {
30 Accept => "Accept",
32 AcceptContact => "Accept-Contact",
34 AcceptEncoding => "Accept-Encoding",
36 AcceptLanguage => "Accept-Language",
38 AcceptResourcePriority => "Accept-Resource-Priority",
40 AdditionalIdentity => "Additional-Identity",
42 AlertInfo => "Alert-Info",
44 AlertmsgError => "AlertMsg-Error",
46 Allow => "Allow",
48 AllowEvents => "Allow-Events",
50 AnswerMode => "Answer-Mode",
52 AttestationInfo => "Attestation-Info",
54 AuthenticationInfo => "Authentication-Info",
56 Authorization => "Authorization",
58 CallId => "Call-ID",
60 CallInfo => "Call-Info",
62 CellularNetworkInfo => "Cellular-Network-Info",
64 Contact => "Contact",
66 ContentDisposition => "Content-Disposition",
68 ContentEncoding => "Content-Encoding",
70 ContentId => "Content-ID",
72 ContentLanguage => "Content-Language",
74 ContentLength => "Content-Length",
76 ContentType => "Content-Type",
78 Cseq => "CSeq",
80 Date => "Date",
82 DcInfo => "DC-Info",
84 Encryption => "Encryption",
86 ErrorInfo => "Error-Info",
88 Event => "Event",
90 Expires => "Expires",
92 FeatureCaps => "Feature-Caps",
94 FlowTimer => "Flow-Timer",
96 From => "From",
98 Geolocation => "Geolocation",
100 GeolocationError => "Geolocation-Error",
102 GeolocationRouting => "Geolocation-Routing",
104 Hide => "Hide",
106 HistoryInfo => "History-Info",
108 Identity => "Identity",
110 IdentityInfo => "Identity-Info",
112 InfoPackage => "Info-Package",
114 InReplyTo => "In-Reply-To",
116 Join => "Join",
118 MaxBreadth => "Max-Breadth",
120 MaxForwards => "Max-Forwards",
122 MimeVersion => "MIME-Version",
124 MinExpires => "Min-Expires",
126 MinSe => "Min-SE",
128 Organization => "Organization",
130 OriginationId => "Origination-Id",
132 PAccessNetworkInfo => "P-Access-Network-Info",
134 PAnswerState => "P-Answer-State",
136 PAssertedIdentity => "P-Asserted-Identity",
138 PAssertedService => "P-Asserted-Service",
140 PAssociatedUri => "P-Associated-URI",
142 PCalledPartyId => "P-Called-Party-ID",
144 PChargeInfo => "P-Charge-Info",
146 PChargingFunctionAddresses => "P-Charging-Function-Addresses",
148 PChargingVector => "P-Charging-Vector",
150 PDcsTracePartyId => "P-DCS-Trace-Party-ID",
152 PDcsOsps => "P-DCS-OSPS",
154 PDcsBillingInfo => "P-DCS-Billing-Info",
156 PDcsLaes => "P-DCS-LAES",
158 PDcsRedirect => "P-DCS-Redirect",
160 PEarlyMedia => "P-Early-Media",
162 PMediaAuthorization => "P-Media-Authorization",
164 PPreferredIdentity => "P-Preferred-Identity",
166 PPreferredService => "P-Preferred-Service",
168 PPrivateNetworkIndication => "P-Private-Network-Indication",
170 PProfileKey => "P-Profile-Key",
172 PRefusedUriList => "P-Refused-URI-List",
174 PServedUser => "P-Served-User",
176 PUserDatabase => "P-User-Database",
178 PVisitedNetworkId => "P-Visited-Network-ID",
180 Path => "Path",
182 PermissionMissing => "Permission-Missing",
184 PolicyContact => "Policy-Contact",
186 PolicyId => "Policy-ID",
188 Priority => "Priority",
190 PriorityShare => "Priority-Share",
192 PriorityVerstat => "Priority-Verstat",
194 PrivAnswerMode => "Priv-Answer-Mode",
196 Privacy => "Privacy",
198 ProxyAuthenticate => "Proxy-Authenticate",
200 ProxyAuthorization => "Proxy-Authorization",
202 ProxyRequire => "Proxy-Require",
204 Rack => "RAck",
206 Reason => "Reason",
208 ReasonPhrase => "Reason-Phrase",
210 RecordRoute => "Record-Route",
212 RecvInfo => "Recv-Info",
214 ReferEventsAt => "Refer-Events-At",
216 ReferSub => "Refer-Sub",
218 ReferTo => "Refer-To",
220 ReferredBy => "Referred-By",
222 RejectContact => "Reject-Contact",
224 RelayedCharge => "Relayed-Charge",
226 Replaces => "Replaces",
228 ReplyTo => "Reply-To",
230 RequestDisposition => "Request-Disposition",
232 Require => "Require",
234 ResourcePriority => "Resource-Priority",
236 ResourceShare => "Resource-Share",
238 ResponseKey => "Response-Key",
240 ResponseSource => "Response-Source",
242 RestorationInfo => "Restoration-Info",
244 RetryAfter => "Retry-After",
246 Route => "Route",
248 Rseq => "RSeq",
250 SecurityClient => "Security-Client",
252 SecurityServer => "Security-Server",
254 SecurityVerify => "Security-Verify",
256 Server => "Server",
258 ServiceInteractInfo => "Service-Interact-Info",
260 ServiceRoute => "Service-Route",
262 SessionExpires => "Session-Expires",
264 SessionId => "Session-ID",
266 SipEtag => "SIP-ETag",
268 SipIfMatch => "SIP-If-Match",
270 Subject => "Subject",
272 SubscriptionState => "Subscription-State",
274 Supported => "Supported",
276 SuppressIfMatch => "Suppress-If-Match",
278 TargetDialog => "Target-Dialog",
280 Timestamp => "Timestamp",
282 To => "To",
284 TriggerConsent => "Trigger-Consent",
286 Unsupported => "Unsupported",
288 UserAgent => "User-Agent",
290 UserToUser => "User-to-User",
292 Via => "Via",
294 Warning => "Warning",
296 WwwAuthenticate => "WWW-Authenticate",
298 #[cfg(feature = "draft")]
301 Diversion => "Diversion",
302 #[cfg(feature = "draft")]
304 RemotePartyId => "Remote-Party-ID",
305 }
306}
307
308const COMPACT_FORMS: &[(u8, SipHeader)] = &[
313 (b'a', SipHeader::AcceptContact),
314 (b'b', SipHeader::ReferredBy),
315 (b'c', SipHeader::ContentType),
316 (b'd', SipHeader::RequestDisposition),
317 (b'e', SipHeader::ContentEncoding),
318 (b'f', SipHeader::From),
319 (b'i', SipHeader::CallId),
320 (b'j', SipHeader::RejectContact),
321 (b'k', SipHeader::Supported),
322 (b'l', SipHeader::ContentLength),
323 (b'm', SipHeader::Contact),
324 (b'n', SipHeader::IdentityInfo),
325 (b'o', SipHeader::Event),
326 (b'r', SipHeader::ReferTo),
327 (b's', SipHeader::Subject),
328 (b't', SipHeader::To),
329 (b'u', SipHeader::AllowEvents),
330 (b'v', SipHeader::Via),
331 (b'x', SipHeader::SessionExpires),
332 (b'y', SipHeader::Identity),
333];
334
335impl SipHeader {
336 pub fn from_compact(ch: u8) -> Option<Self> {
340 let lower = ch.to_ascii_lowercase();
341 COMPACT_FORMS
342 .iter()
343 .find(|(c, _)| *c == lower)
344 .map(|(_, h)| *h)
345 }
346
347 pub fn compact_form(&self) -> Option<char> {
349 COMPACT_FORMS
350 .iter()
351 .find(|(_, h)| h == self)
352 .map(|(c, _)| *c as char)
353 }
354
355 pub fn is_multi_valued(&self) -> bool {
360 if matches!(
361 self,
362 Self::Via
364 | Self::Route
365 | Self::RecordRoute
366 | Self::Contact
367 | Self::Allow
368 | Self::Supported
369 | Self::Require
370 | Self::ProxyRequire
371 | Self::Unsupported
372 | Self::Authorization
373 | Self::ProxyAuthorization
374 | Self::WwwAuthenticate
375 | Self::ProxyAuthenticate
376 | Self::Warning
377 | Self::ErrorInfo
378 | Self::CallInfo
379 | Self::AlertInfo
380 | Self::Accept
381 | Self::AcceptEncoding
382 | Self::AcceptLanguage
383 | Self::ContentEncoding
384 | Self::ContentLanguage
385 | Self::InReplyTo
386 | Self::PAssertedIdentity
388 | Self::PPreferredIdentity
389 | Self::AllowEvents
391 | Self::SecurityClient
393 | Self::SecurityServer
394 | Self::SecurityVerify
395 | Self::Path
397 | Self::ServiceRoute
399 | Self::HistoryInfo
401 ) {
402 return true;
403 }
404
405 #[cfg(feature = "draft")]
406 if matches!(
407 self,
408 Self::Diversion
410 | Self::RemotePartyId
412 ) {
413 return true;
414 }
415
416 false
417 }
418
419 pub fn parse_name(name: &str) -> Result<Self, ParseSipHeaderError> {
424 if name.len() == 1 {
425 if let Some(h) = Self::from_compact(name.as_bytes()[0]) {
426 return Ok(h);
427 }
428 }
429 name.parse()
430 }
431}
432
433pub trait SipHeaderLookup {
459 fn sip_header_str(&self, name: &str) -> Option<&str>;
461
462 fn sip_header(&self, name: SipHeader) -> Option<&str> {
464 self.sip_header_str(name.as_str())
465 }
466
467 fn sip_header_all_str<'a>(&'a self, name: &str) -> Vec<&'a str> {
475 self.sip_header_str(name)
476 .into_iter()
477 .collect()
478 }
479
480 fn sip_header_all(&self, name: SipHeader) -> Vec<&str> {
482 self.sip_header_all_str(name.as_str())
483 }
484
485 fn call_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
489 match self.sip_header(SipHeader::CallInfo) {
490 Some(s) => UriInfo::parse(s).map(Some),
491 None => Ok(None),
492 }
493 }
494
495 fn history_info(&self) -> Result<Option<HistoryInfo>, HistoryInfoError> {
499 match self.sip_header(SipHeader::HistoryInfo) {
500 Some(s) => HistoryInfo::parse(s).map(Some),
501 None => Ok(None),
502 }
503 }
504
505 fn p_asserted_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
510 parse_addr_list(self.sip_header_all(SipHeader::PAssertedIdentity))
511 }
512
513 fn p_preferred_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
515 parse_addr_list(self.sip_header_all(SipHeader::PPreferredIdentity))
516 }
517
518 fn route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
520 parse_addr_list(self.sip_header_all(SipHeader::Route))
521 }
522
523 fn record_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
525 parse_addr_list(self.sip_header_all(SipHeader::RecordRoute))
526 }
527
528 fn path(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
530 parse_addr_list(self.sip_header_all(SipHeader::Path))
531 }
532
533 fn service_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
535 parse_addr_list(self.sip_header_all(SipHeader::ServiceRoute))
536 }
537
538 fn contact(&self) -> Result<Vec<ContactValue>, ParseSipHeaderAddrError> {
543 match self.sip_header(SipHeader::Contact) {
544 Some(s) => crate::contact::parse_contact_list(s),
545 None => Ok(Vec::new()),
546 }
547 }
548
549 fn alert_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
551 match self.sip_header(SipHeader::AlertInfo) {
552 Some(s) => UriInfo::parse(s).map(Some),
553 None => Ok(None),
554 }
555 }
556
557 fn error_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
559 match self.sip_header(SipHeader::ErrorInfo) {
560 Some(s) => UriInfo::parse(s).map(Some),
561 None => Ok(None),
562 }
563 }
564
565 fn allow(&self) -> Vec<&str> {
567 split_trim(self.sip_header(SipHeader::Allow))
568 }
569
570 fn supported(&self) -> Vec<&str> {
572 split_trim(self.sip_header(SipHeader::Supported))
573 }
574
575 fn require_header(&self) -> Vec<&str> {
577 split_trim(self.sip_header(SipHeader::Require))
578 }
579
580 fn proxy_require(&self) -> Vec<&str> {
582 split_trim(self.sip_header(SipHeader::ProxyRequire))
583 }
584
585 fn unsupported(&self) -> Vec<&str> {
587 split_trim(self.sip_header(SipHeader::Unsupported))
588 }
589
590 fn allow_events(&self) -> Vec<&str> {
592 split_trim(self.sip_header(SipHeader::AllowEvents))
593 }
594
595 fn content_encoding(&self) -> Vec<&str> {
597 split_trim(self.sip_header(SipHeader::ContentEncoding))
598 }
599
600 fn content_language(&self) -> Vec<&str> {
602 split_trim(self.sip_header(SipHeader::ContentLanguage))
603 }
604
605 fn in_reply_to(&self) -> Vec<&str> {
607 split_trim(self.sip_header(SipHeader::InReplyTo))
608 }
609
610 fn via(&self) -> Result<Option<SipVia>, SipViaError> {
612 match self.sip_header(SipHeader::Via) {
613 Some(s) => SipVia::parse(s).map(Some),
614 None => Ok(None),
615 }
616 }
617
618 fn replaces(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
620 match self.sip_header(SipHeader::Replaces) {
621 Some(s) => SipReplaces::parse(s).map(Some),
622 None => Ok(None),
623 }
624 }
625
626 fn join(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
631 match self.sip_header(SipHeader::Join) {
632 Some(s) => SipReplaces::parse(s).map(Some),
633 None => Ok(None),
634 }
635 }
636
637 fn target_dialog(&self) -> Result<Option<SipTargetDialog>, SipTargetDialogError> {
639 match self.sip_header(SipHeader::TargetDialog) {
640 Some(s) => SipTargetDialog::parse(s).map(Some),
641 None => Ok(None),
642 }
643 }
644
645 fn authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
650 self.sip_header_all(SipHeader::Authorization)
651 .into_iter()
652 .map(|s| s.parse::<SipAuthValue>())
653 .collect()
654 }
655
656 fn proxy_authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
658 self.sip_header_all(SipHeader::ProxyAuthorization)
659 .into_iter()
660 .map(|s| s.parse::<SipAuthValue>())
661 .collect()
662 }
663
664 fn www_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
666 self.sip_header_all(SipHeader::WwwAuthenticate)
667 .into_iter()
668 .map(|s| s.parse::<SipAuthValue>())
669 .collect()
670 }
671
672 fn proxy_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
674 self.sip_header_all(SipHeader::ProxyAuthenticate)
675 .into_iter()
676 .map(|s| s.parse::<SipAuthValue>())
677 .collect()
678 }
679
680 fn warning(&self) -> Result<Option<SipWarning>, SipWarningError> {
682 match self.sip_header(SipHeader::Warning) {
683 Some(s) => SipWarning::parse(s).map(Some),
684 None => Ok(None),
685 }
686 }
687
688 fn security_client(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
690 match self.sip_header(SipHeader::SecurityClient) {
691 Some(s) => SipSecurity::parse(s).map(Some),
692 None => Ok(None),
693 }
694 }
695
696 fn security_server(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
698 match self.sip_header(SipHeader::SecurityServer) {
699 Some(s) => SipSecurity::parse(s).map(Some),
700 None => Ok(None),
701 }
702 }
703
704 fn security_verify(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
706 match self.sip_header(SipHeader::SecurityVerify) {
707 Some(s) => SipSecurity::parse(s).map(Some),
708 None => Ok(None),
709 }
710 }
711
712 fn accept(&self) -> Result<Option<SipAccept>, SipAcceptError> {
714 match self.sip_header(SipHeader::Accept) {
715 Some(s) => SipAccept::parse(s).map(Some),
716 None => Ok(None),
717 }
718 }
719
720 fn accept_encoding(&self) -> Result<Option<SipAcceptEncoding>, SipAcceptEncodingError> {
722 match self.sip_header(SipHeader::AcceptEncoding) {
723 Some(s) => SipAcceptEncoding::parse(s).map(Some),
724 None => Ok(None),
725 }
726 }
727
728 fn accept_language(&self) -> Result<Option<SipAcceptLanguage>, SipAcceptLanguageError> {
730 match self.sip_header(SipHeader::AcceptLanguage) {
731 Some(s) => SipAcceptLanguage::parse(s).map(Some),
732 None => Ok(None),
733 }
734 }
735
736 #[cfg(feature = "draft")]
738 fn diversion(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
739 parse_addr_list(self.sip_header_all(SipHeader::Diversion))
740 }
741
742 #[cfg(feature = "draft")]
744 fn remote_party_id(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
745 parse_addr_list(self.sip_header_all(SipHeader::RemotePartyId))
746 }
747}
748
749fn parse_addr_list(raw: Vec<&str>) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
750 if raw.is_empty() {
751 return Ok(Vec::new());
752 }
753 raw.into_iter()
754 .flat_map(|s| crate::split_comma_entries(s))
755 .map(|s| {
756 s.trim()
757 .parse::<SipHeaderAddr>()
758 })
759 .collect()
760}
761
762fn split_trim(raw: Option<&str>) -> Vec<&str> {
763 match raw {
764 Some(s) => crate::split_comma_entries(s)
765 .into_iter()
766 .map(str::trim)
767 .collect(),
768 None => Vec::new(),
769 }
770}
771
772impl SipHeaderLookup for std::collections::HashMap<String, String> {
773 fn sip_header_str(&self, name: &str) -> Option<&str> {
774 self.get(name)
775 .map(|s| s.as_str())
776 }
777}
778
779impl SipHeaderLookup for std::collections::HashMap<String, Vec<String>> {
780 fn sip_header_str(&self, name: &str) -> Option<&str> {
781 self.get(name)
782 .and_then(|v| v.first())
783 .map(|s| s.as_str())
784 }
785
786 fn sip_header_all_str(&self, name: &str) -> Vec<&str> {
787 self.get(name)
788 .map(|v| {
789 v.iter()
790 .map(|s| s.as_str())
791 .collect()
792 })
793 .unwrap_or_default()
794 }
795}
796
797#[cfg(test)]
798mod tests {
799 use super::*;
800 use std::collections::HashMap;
801
802 #[test]
803 fn display_round_trip() {
804 assert_eq!(SipHeader::CallInfo.to_string(), "Call-Info");
805 assert_eq!(SipHeader::HistoryInfo.to_string(), "History-Info");
806 assert_eq!(
807 SipHeader::PAssertedIdentity.to_string(),
808 "P-Asserted-Identity"
809 );
810 }
811
812 #[test]
813 fn as_ref_str() {
814 let h: &str = SipHeader::CallInfo.as_ref();
815 assert_eq!(h, "Call-Info");
816 }
817
818 #[test]
819 fn from_str_case_insensitive() {
820 assert_eq!("call-info".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
821 assert_eq!("CALL-INFO".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
822 assert_eq!(
823 "history-info".parse::<SipHeader>(),
824 Ok(SipHeader::HistoryInfo)
825 );
826 assert_eq!(
827 "p-asserted-identity".parse::<SipHeader>(),
828 Ok(SipHeader::PAssertedIdentity)
829 );
830 assert_eq!(
831 "P-ASSERTED-IDENTITY".parse::<SipHeader>(),
832 Ok(SipHeader::PAssertedIdentity)
833 );
834 }
835
836 #[test]
837 fn from_str_unknown() {
838 assert!("X-Custom"
839 .parse::<SipHeader>()
840 .is_err());
841 }
842
843 fn headers_with(pairs: &[(&str, &str)]) -> HashMap<String, String> {
844 pairs
845 .iter()
846 .map(|(k, v)| (k.to_string(), v.to_string()))
847 .collect()
848 }
849
850 #[test]
851 fn sip_header_by_enum() {
852 let h = headers_with(&[("Call-Info", "<urn:x>;purpose=icon")]);
853 assert_eq!(
854 h.sip_header(SipHeader::CallInfo),
855 Some("<urn:x>;purpose=icon")
856 );
857 }
858
859 #[test]
860 fn call_info_raw_lookup() {
861 let h = headers_with(&[(
862 "Call-Info",
863 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
864 )]);
865 assert_eq!(
866 h.sip_header(SipHeader::CallInfo),
867 Some("<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId")
868 );
869 }
870
871 #[test]
872 fn call_info_typed() {
873 let h = headers_with(&[(
874 "Call-Info",
875 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
876 )]);
877 let ci = h
878 .call_info()
879 .unwrap()
880 .unwrap();
881 assert_eq!(ci.len(), 1);
882 assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
883 }
884
885 #[test]
886 fn call_info_absent() {
887 let h = headers_with(&[]);
888 assert_eq!(
889 h.call_info()
890 .unwrap(),
891 None
892 );
893 }
894
895 #[test]
896 fn p_asserted_identity_typed() {
897 let h = headers_with(&[(
898 "P-Asserted-Identity",
899 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
900 )]);
901 let pais = h
902 .p_asserted_identity()
903 .unwrap();
904 assert_eq!(pais.len(), 1);
905 assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
906 }
907
908 #[test]
909 fn p_asserted_identity_multi_value() {
910 let h = headers_with(&[(
911 "P-Asserted-Identity",
912 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>, <tel:+15551234567>"#,
913 )]);
914 let pais = h
915 .p_asserted_identity()
916 .unwrap();
917 assert_eq!(pais.len(), 2);
918 assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
919 assert!(pais[1]
920 .uri()
921 .to_string()
922 .contains("+15551234567"));
923 }
924
925 #[test]
926 fn p_asserted_identity_absent() {
927 let h = headers_with(&[]);
928 assert!(h
929 .p_asserted_identity()
930 .unwrap()
931 .is_empty());
932 }
933
934 #[test]
935 fn history_info_raw_lookup() {
936 let h = headers_with(&[(
937 "History-Info",
938 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
939 )]);
940 assert!(h
941 .sip_header(SipHeader::HistoryInfo)
942 .unwrap()
943 .contains("esrp.example.com"));
944 }
945
946 #[test]
947 fn history_info_typed() {
948 let h = headers_with(&[(
949 "History-Info",
950 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
951 )]);
952 let hi = h
953 .history_info()
954 .unwrap()
955 .unwrap();
956 assert_eq!(hi.len(), 2);
957 assert_eq!(hi.entries()[0].index(), Some("1"));
958 assert_eq!(hi.entries()[1].index(), Some("1.1"));
959 }
960
961 #[test]
962 fn history_info_absent() {
963 let h = headers_with(&[]);
964 assert_eq!(
965 h.history_info()
966 .unwrap(),
967 None
968 );
969 }
970
971 #[test]
972 fn sip_header_all_str_default() {
973 let h = headers_with(&[("Via", "SIP/2.0/UDP host1")]);
974 let all = h.sip_header_all(SipHeader::Via);
975 assert_eq!(all.len(), 1);
976 assert_eq!(all[0], "SIP/2.0/UDP host1");
977 }
978
979 #[test]
980 fn sip_header_all_str_absent() {
981 let h = headers_with(&[]);
982 assert!(h
983 .sip_header_all(SipHeader::Via)
984 .is_empty());
985 }
986
987 #[test]
988 fn hashmap_vec_impl() {
989 let mut h: HashMap<String, Vec<String>> = HashMap::new();
990 h.insert(
991 "Via".into(),
992 vec!["SIP/2.0/UDP host1".into(), "SIP/2.0/UDP host2".into()],
993 );
994 assert_eq!(h.sip_header_str("Via"), Some("SIP/2.0/UDP host1"));
995 let all = h.sip_header_all_str("Via");
996 assert_eq!(all.len(), 2);
997 assert_eq!(all[0], "SIP/2.0/UDP host1");
998 assert_eq!(all[1], "SIP/2.0/UDP host2");
999 }
1000
1001 #[test]
1002 fn extract_from_sip_message() {
1003 let msg = concat!(
1004 "INVITE sip:bob@host SIP/2.0\r\n",
1005 "Call-Info: <urn:emergency:uid:callid:abc>;purpose=emergency-CallId\r\n",
1006 "History-Info: <sip:esrp@example.com>;index=1\r\n",
1007 "P-Asserted-Identity: \"Corp\" <sip:+15551234567@198.51.100.1>\r\n",
1008 "\r\n",
1009 );
1010 let ci = SipHeader::CallInfo.extract_from(msg);
1011 assert_eq!(ci.len(), 1);
1012 assert_eq!(
1013 ci[0],
1014 "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"
1015 );
1016
1017 let hi = SipHeader::HistoryInfo.extract_from(msg);
1018 assert_eq!(hi.len(), 1);
1019 assert_eq!(hi[0], "<sip:esrp@example.com>;index=1");
1020
1021 let pai = SipHeader::PAssertedIdentity.extract_from(msg);
1022 assert_eq!(pai.len(), 1);
1023 assert_eq!(pai[0], "\"Corp\" <sip:+15551234567@198.51.100.1>");
1024 }
1025
1026 #[test]
1027 fn extract_from_missing() {
1028 let msg = concat!(
1029 "INVITE sip:bob@host SIP/2.0\r\n",
1030 "From: Alice <sip:alice@host>\r\n",
1031 "\r\n",
1032 );
1033 assert!(SipHeader::CallInfo
1034 .extract_from(msg)
1035 .is_empty());
1036 assert!(SipHeader::PAssertedIdentity
1037 .extract_from(msg)
1038 .is_empty());
1039 }
1040
1041 #[test]
1042 fn missing_headers_return_none() {
1043 let h = headers_with(&[]);
1044 assert_eq!(h.sip_header(SipHeader::CallInfo), None);
1045 assert_eq!(
1046 h.call_info()
1047 .unwrap(),
1048 None
1049 );
1050 assert_eq!(h.sip_header(SipHeader::HistoryInfo), None);
1051 assert_eq!(
1052 h.history_info()
1053 .unwrap(),
1054 None
1055 );
1056 assert_eq!(h.sip_header(SipHeader::PAssertedIdentity), None);
1057 assert!(h
1058 .p_asserted_identity()
1059 .unwrap()
1060 .is_empty());
1061 }
1062
1063 #[test]
1064 fn route_accessor() {
1065 let h = headers_with(&[(
1066 "Route",
1067 "<sip:proxy1.example.com;lr>, <sip:proxy2.example.com;lr>",
1068 )]);
1069 let routes = h
1070 .route()
1071 .unwrap();
1072 assert_eq!(routes.len(), 2);
1073 assert!(routes[0]
1074 .uri()
1075 .to_string()
1076 .contains("proxy1"));
1077 assert!(routes[1]
1078 .uri()
1079 .to_string()
1080 .contains("proxy2"));
1081 }
1082
1083 #[test]
1084 fn route_absent() {
1085 let h = headers_with(&[]);
1086 assert!(h
1087 .route()
1088 .unwrap()
1089 .is_empty());
1090 }
1091
1092 #[test]
1093 fn record_route_accessor() {
1094 let h = headers_with(&[("Record-Route", "<sip:ss1.example.com;lr>")]);
1095 let rr = h
1096 .record_route()
1097 .unwrap();
1098 assert_eq!(rr.len(), 1);
1099 }
1100
1101 #[test]
1102 fn allow_accessor() {
1103 let h = headers_with(&[("Allow", "INVITE, ACK, OPTIONS, BYE")]);
1104 let methods = h.allow();
1105 assert_eq!(methods, vec!["INVITE", "ACK", "OPTIONS", "BYE"]);
1106 }
1107
1108 #[test]
1109 fn allow_absent() {
1110 let h = headers_with(&[]);
1111 assert!(h
1112 .allow()
1113 .is_empty());
1114 }
1115
1116 #[test]
1117 fn supported_accessor() {
1118 let h = headers_with(&[("Supported", "100rel, timer")]);
1119 let opts = h.supported();
1120 assert_eq!(opts, vec!["100rel", "timer"]);
1121 }
1122
1123 #[test]
1124 fn require_header_accessor() {
1125 let h = headers_with(&[("Require", "100rel")]);
1126 assert_eq!(h.require_header(), vec!["100rel"]);
1127 }
1128
1129 #[test]
1130 fn alert_info_accessor() {
1131 let h = headers_with(&[("Alert-Info", "<http://www.example.com/sounds/moo.wav>")]);
1132 let ai = h
1133 .alert_info()
1134 .unwrap()
1135 .unwrap();
1136 assert_eq!(ai.len(), 1);
1137 assert!(ai.entries()[0]
1138 .data
1139 .contains("moo.wav"));
1140 }
1141
1142 #[test]
1143 fn error_info_accessor() {
1144 let h = headers_with(&[("Error-Info", "<sip:not-in-service@example.com>")]);
1145 let ei = h
1146 .error_info()
1147 .unwrap()
1148 .unwrap();
1149 assert_eq!(ei.len(), 1);
1150 }
1151
1152 #[test]
1153 fn p_preferred_identity_accessor() {
1154 let h = headers_with(&[(
1155 "P-Preferred-Identity",
1156 r#""User" <sip:+15551234567@198.51.100.1>"#,
1157 )]);
1158 let ppi = h
1159 .p_preferred_identity()
1160 .unwrap();
1161 assert_eq!(ppi.len(), 1);
1162 assert_eq!(ppi[0].display_name(), Some("User"));
1163 }
1164
1165 #[test]
1166 fn content_encoding_accessor() {
1167 let h = headers_with(&[("Content-Encoding", "gzip")]);
1168 assert_eq!(h.content_encoding(), vec!["gzip"]);
1169 }
1170
1171 #[test]
1172 fn contact_accessor() {
1173 let h = headers_with(&[("Contact", "<sip:alice@198.51.100.1>")]);
1174 let contacts = h
1175 .contact()
1176 .unwrap();
1177 assert_eq!(contacts.len(), 1);
1178 assert!(matches!(&contacts[0], ContactValue::Addr(_)));
1179 }
1180
1181 #[test]
1182 fn contact_wildcard() {
1183 let h = headers_with(&[("Contact", "*")]);
1184 let contacts = h
1185 .contact()
1186 .unwrap();
1187 assert_eq!(contacts.len(), 1);
1188 assert!(matches!(contacts[0], ContactValue::Wildcard));
1189 }
1190
1191 #[test]
1192 fn contact_absent() {
1193 let h = headers_with(&[]);
1194 assert!(h
1195 .contact()
1196 .unwrap()
1197 .is_empty());
1198 }
1199
1200 #[test]
1201 fn in_reply_to_accessor() {
1202 let h = headers_with(&[("In-Reply-To", "call1@example.com, call2@example.com")]);
1203 let calls = h.in_reply_to();
1204 assert_eq!(calls.len(), 2);
1205 assert_eq!(calls[0], "call1@example.com");
1206 }
1207
1208 #[test]
1209 fn via_accessor() {
1210 let h = headers_with(&[("Via", "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bK776")]);
1211 let via = h
1212 .via()
1213 .unwrap()
1214 .unwrap();
1215 assert_eq!(via.len(), 1);
1216 assert_eq!(via.entries()[0].transport(), "UDP");
1217 assert_eq!(via.entries()[0].host(), "198.51.100.1");
1218 }
1219
1220 #[test]
1221 fn replaces_accessor() {
1222 let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1223 let r = h
1224 .replaces()
1225 .unwrap()
1226 .unwrap();
1227 assert_eq!(r.call_id(), "abc123@203.0.113.5");
1228 assert_eq!(r.to_tag(), "t1");
1229 assert_eq!(r.from_tag(), "f1");
1230 }
1231
1232 #[test]
1233 fn replaces_absent() {
1234 let h = headers_with(&[]);
1235 assert!(h
1236 .replaces()
1237 .unwrap()
1238 .is_none());
1239 }
1240
1241 #[test]
1242 fn replaces_malformed_err() {
1243 let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1")]);
1244 assert!(h
1245 .replaces()
1246 .is_err());
1247 }
1248
1249 #[test]
1250 fn join_accessor() {
1251 let h = headers_with(&[("Join", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1252 let j = h
1253 .join()
1254 .unwrap()
1255 .unwrap();
1256 assert_eq!(j.call_id(), "abc123@203.0.113.5");
1257 }
1258
1259 #[test]
1260 fn target_dialog_accessor() {
1261 let h = headers_with(&[(
1262 "Target-Dialog",
1263 "abc123@203.0.113.5;local-tag=l1;remote-tag=r1",
1264 )]);
1265 let t = h
1266 .target_dialog()
1267 .unwrap()
1268 .unwrap();
1269 assert_eq!(t.local_tag(), "l1");
1270 assert_eq!(t.remote_tag(), "r1");
1271 }
1272
1273 #[test]
1274 fn authorization_accessor() {
1275 let h = headers_with(&[(
1276 "Authorization",
1277 "Digest username=\"alice\", realm=\"example.com\", nonce=\"abc123\"",
1278 )]);
1279 let auth = h
1280 .authorization()
1281 .unwrap();
1282 assert_eq!(auth.len(), 1);
1283 assert_eq!(auth[0].scheme(), "Digest");
1284 assert_eq!(auth[0].username(), Some("alice"));
1285 assert_eq!(auth[0].realm(), Some("example.com"));
1286 }
1287
1288 #[test]
1289 fn www_authenticate_accessor() {
1290 let h = headers_with(&[(
1291 "WWW-Authenticate",
1292 "Digest realm=\"example.com\", nonce=\"xyz789\"",
1293 )]);
1294 let challenges = h
1295 .www_authenticate()
1296 .unwrap();
1297 assert_eq!(challenges.len(), 1);
1298 assert_eq!(challenges[0].realm(), Some("example.com"));
1299 }
1300
1301 #[test]
1302 fn warning_accessor() {
1303 let h = headers_with(&[(
1304 "Warning",
1305 "301 198.51.100.1 \"Incompatible network protocol\"",
1306 )]);
1307 let w = h
1308 .warning()
1309 .unwrap()
1310 .unwrap();
1311 assert_eq!(w.len(), 1);
1312 assert_eq!(w.entries()[0].code(), 301);
1313 }
1314
1315 #[test]
1316 fn security_client_accessor() {
1317 let h = headers_with(&[("Security-Client", "tls;q=0.2, digest;d-qop=auth;q=0.1")]);
1318 let sec = h
1319 .security_client()
1320 .unwrap()
1321 .unwrap();
1322 assert_eq!(sec.len(), 2);
1323 assert_eq!(sec.entries()[0].mechanism(), "tls");
1324 }
1325
1326 #[test]
1327 fn accept_accessor() {
1328 let h = headers_with(&[("Accept", "application/sdp, application/pidf+xml;q=0.5")]);
1329 let accept = h
1330 .accept()
1331 .unwrap()
1332 .unwrap();
1333 assert_eq!(accept.len(), 2);
1334 assert_eq!(accept.entries()[0].media_range(), "application/sdp");
1335 }
1336
1337 #[test]
1338 fn accept_encoding_accessor() {
1339 let h = headers_with(&[("Accept-Encoding", "gzip;q=1.0, identity;q=0.5")]);
1340 let ae = h
1341 .accept_encoding()
1342 .unwrap()
1343 .unwrap();
1344 assert_eq!(ae.len(), 2);
1345 assert_eq!(ae.entries()[0].encoding(), "gzip");
1346 }
1347
1348 #[test]
1349 fn accept_language_accessor() {
1350 let h = headers_with(&[("Accept-Language", "en;q=0.9, fr;q=0.8")]);
1351 let al = h
1352 .accept_language()
1353 .unwrap()
1354 .unwrap();
1355 assert_eq!(al.len(), 2);
1356 assert_eq!(al.entries()[0].language(), "en");
1357 }
1358}
1359
1360#[cfg(test)]
1361mod compact_form_tests {
1362 use super::*;
1363
1364 #[test]
1365 fn from_compact_known() {
1366 assert_eq!(SipHeader::from_compact(b'f'), Some(SipHeader::From));
1367 assert_eq!(SipHeader::from_compact(b'F'), Some(SipHeader::From));
1368 assert_eq!(SipHeader::from_compact(b'v'), Some(SipHeader::Via));
1369 assert_eq!(SipHeader::from_compact(b'i'), Some(SipHeader::CallId));
1370 assert_eq!(SipHeader::from_compact(b'm'), Some(SipHeader::Contact));
1371 assert_eq!(SipHeader::from_compact(b't'), Some(SipHeader::To));
1372 assert_eq!(SipHeader::from_compact(b'c'), Some(SipHeader::ContentType));
1373 }
1374
1375 #[test]
1376 fn from_compact_unknown() {
1377 assert_eq!(SipHeader::from_compact(b'z'), None);
1378 assert_eq!(SipHeader::from_compact(b'g'), None);
1379 }
1380
1381 #[test]
1382 fn compact_form_roundtrip() {
1383 assert_eq!(SipHeader::From.compact_form(), Some('f'));
1384 assert_eq!(SipHeader::Via.compact_form(), Some('v'));
1385 assert_eq!(SipHeader::CallId.compact_form(), Some('i'));
1386 assert_eq!(SipHeader::Contact.compact_form(), Some('m'));
1387 }
1388
1389 #[test]
1390 fn compact_form_absent() {
1391 assert_eq!(SipHeader::HistoryInfo.compact_form(), None);
1392 assert_eq!(SipHeader::PAssertedIdentity.compact_form(), None);
1393 }
1394
1395 #[test]
1396 fn parse_name_compact() {
1397 assert_eq!(SipHeader::parse_name("f"), Ok(SipHeader::From));
1398 assert_eq!(SipHeader::parse_name("F"), Ok(SipHeader::From));
1399 assert_eq!(SipHeader::parse_name("v"), Ok(SipHeader::Via));
1400 }
1401
1402 #[test]
1403 fn parse_name_full() {
1404 assert_eq!(SipHeader::parse_name("From"), Ok(SipHeader::From));
1405 assert_eq!(SipHeader::parse_name("Via"), Ok(SipHeader::Via));
1406 }
1407
1408 #[test]
1409 fn parse_name_unknown() {
1410 assert!(SipHeader::parse_name("X-Custom").is_err());
1411 }
1412
1413 #[test]
1414 fn all_compact_forms_resolve() {
1415 let expected = [
1416 ('a', SipHeader::AcceptContact),
1417 ('b', SipHeader::ReferredBy),
1418 ('c', SipHeader::ContentType),
1419 ('d', SipHeader::RequestDisposition),
1420 ('e', SipHeader::ContentEncoding),
1421 ('f', SipHeader::From),
1422 ('i', SipHeader::CallId),
1423 ('j', SipHeader::RejectContact),
1424 ('k', SipHeader::Supported),
1425 ('l', SipHeader::ContentLength),
1426 ('m', SipHeader::Contact),
1427 ('n', SipHeader::IdentityInfo),
1428 ('o', SipHeader::Event),
1429 ('r', SipHeader::ReferTo),
1430 ('s', SipHeader::Subject),
1431 ('t', SipHeader::To),
1432 ('u', SipHeader::AllowEvents),
1433 ('v', SipHeader::Via),
1434 ('x', SipHeader::SessionExpires),
1435 ('y', SipHeader::Identity),
1436 ];
1437 for (ch, header) in expected {
1438 assert_eq!(
1439 SipHeader::from_compact(ch as u8),
1440 Some(header),
1441 "compact form '{ch}' failed"
1442 );
1443 assert_eq!(
1444 header.compact_form(),
1445 Some(ch),
1446 "compact_form() for {} failed",
1447 header
1448 );
1449 }
1450 }
1451}
1452
1453#[cfg(test)]
1454mod multi_valued_tests {
1455 use super::*;
1456
1457 #[test]
1458 fn rfc3261_multi_valued_headers() {
1459 assert!(SipHeader::Via.is_multi_valued());
1460 assert!(SipHeader::Route.is_multi_valued());
1461 assert!(SipHeader::RecordRoute.is_multi_valued());
1462 assert!(SipHeader::Contact.is_multi_valued());
1463 assert!(SipHeader::Allow.is_multi_valued());
1464 assert!(SipHeader::Supported.is_multi_valued());
1465 assert!(SipHeader::Require.is_multi_valued());
1466 assert!(SipHeader::ProxyRequire.is_multi_valued());
1467 assert!(SipHeader::Unsupported.is_multi_valued());
1468 assert!(SipHeader::Authorization.is_multi_valued());
1469 assert!(SipHeader::ProxyAuthorization.is_multi_valued());
1470 assert!(SipHeader::WwwAuthenticate.is_multi_valued());
1471 assert!(SipHeader::ProxyAuthenticate.is_multi_valued());
1472 assert!(SipHeader::Warning.is_multi_valued());
1473 assert!(SipHeader::ErrorInfo.is_multi_valued());
1474 assert!(SipHeader::CallInfo.is_multi_valued());
1475 assert!(SipHeader::AlertInfo.is_multi_valued());
1476 assert!(SipHeader::Accept.is_multi_valued());
1477 assert!(SipHeader::AcceptEncoding.is_multi_valued());
1478 assert!(SipHeader::AcceptLanguage.is_multi_valued());
1479 assert!(SipHeader::ContentEncoding.is_multi_valued());
1480 assert!(SipHeader::ContentLanguage.is_multi_valued());
1481 assert!(SipHeader::InReplyTo.is_multi_valued());
1482 }
1483
1484 #[test]
1485 fn extension_multi_valued_headers() {
1486 assert!(SipHeader::PAssertedIdentity.is_multi_valued());
1487 assert!(SipHeader::PPreferredIdentity.is_multi_valued());
1488 assert!(SipHeader::AllowEvents.is_multi_valued());
1489 assert!(SipHeader::SecurityClient.is_multi_valued());
1490 assert!(SipHeader::SecurityServer.is_multi_valued());
1491 assert!(SipHeader::SecurityVerify.is_multi_valued());
1492 assert!(SipHeader::Path.is_multi_valued());
1493 assert!(SipHeader::ServiceRoute.is_multi_valued());
1494 assert!(SipHeader::HistoryInfo.is_multi_valued());
1495 }
1496
1497 #[test]
1498 fn single_valued_headers() {
1499 assert!(!SipHeader::From.is_multi_valued());
1500 assert!(!SipHeader::To.is_multi_valued());
1501 assert!(!SipHeader::CallId.is_multi_valued());
1502 assert!(!SipHeader::Cseq.is_multi_valued());
1503 assert!(!SipHeader::MaxForwards.is_multi_valued());
1504 assert!(!SipHeader::ContentType.is_multi_valued());
1505 assert!(!SipHeader::ContentLength.is_multi_valued());
1506 assert!(!SipHeader::Expires.is_multi_valued());
1507 assert!(!SipHeader::Date.is_multi_valued());
1508 assert!(!SipHeader::Subject.is_multi_valued());
1509 assert!(!SipHeader::ReplyTo.is_multi_valued());
1510 assert!(!SipHeader::Server.is_multi_valued());
1511 assert!(!SipHeader::UserAgent.is_multi_valued());
1512 }
1513
1514 #[test]
1515 #[cfg(feature = "draft")]
1516 fn draft_multi_valued_headers() {
1517 assert!(SipHeader::Diversion.is_multi_valued());
1518 assert!(SipHeader::RemotePartyId.is_multi_valued());
1519 }
1520
1521 #[test]
1522 #[cfg(feature = "draft")]
1523 fn draft_parse_roundtrip() {
1524 let d: SipHeader = "Diversion"
1525 .parse()
1526 .unwrap();
1527 assert_eq!(d, SipHeader::Diversion);
1528 assert_eq!(d.to_string(), "Diversion");
1529
1530 let r: SipHeader = "remote-party-id"
1531 .parse()
1532 .unwrap();
1533 assert_eq!(r, SipHeader::RemotePartyId);
1534 assert_eq!(r.to_string(), "Remote-Party-ID");
1535 }
1536}
1537
1538#[cfg(test)]
1539mod special_case_tests {
1540 use super::*;
1541
1542 #[test]
1543 fn cseq_variants() {
1544 assert_eq!("CSeq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1545 assert_eq!("cseq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1546 assert_eq!("CSEQ".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1547 assert_eq!(SipHeader::Cseq.to_string(), "CSeq");
1548 }
1549
1550 #[test]
1551 fn www_authenticate_variants() {
1552 assert_eq!(
1553 "WWW-Authenticate".parse::<SipHeader>(),
1554 Ok(SipHeader::WwwAuthenticate)
1555 );
1556 assert_eq!(
1557 "www-authenticate".parse::<SipHeader>(),
1558 Ok(SipHeader::WwwAuthenticate)
1559 );
1560 assert_eq!(SipHeader::WwwAuthenticate.to_string(), "WWW-Authenticate");
1561 }
1562
1563 #[test]
1564 fn rack_rseq_variants() {
1565 assert_eq!("RAck".parse::<SipHeader>(), Ok(SipHeader::Rack));
1566 assert_eq!("rack".parse::<SipHeader>(), Ok(SipHeader::Rack));
1567 assert_eq!(SipHeader::Rack.to_string(), "RAck");
1568
1569 assert_eq!("RSeq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1570 assert_eq!("rseq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1571 assert_eq!(SipHeader::Rseq.to_string(), "RSeq");
1572 }
1573
1574 #[test]
1575 fn user_to_user_variants() {
1576 assert_eq!(
1577 "User-to-User".parse::<SipHeader>(),
1578 Ok(SipHeader::UserToUser)
1579 );
1580 assert_eq!(
1581 "user-to-user".parse::<SipHeader>(),
1582 Ok(SipHeader::UserToUser)
1583 );
1584 assert_eq!(SipHeader::UserToUser.to_string(), "User-to-User");
1585 }
1586
1587 #[test]
1588 fn p_header_variants() {
1589 assert_eq!(
1590 "P-DCS-Trace-Party-ID".parse::<SipHeader>(),
1591 Ok(SipHeader::PDcsTracePartyId)
1592 );
1593 assert_eq!(
1594 "p-dcs-trace-party-id".parse::<SipHeader>(),
1595 Ok(SipHeader::PDcsTracePartyId)
1596 );
1597 assert_eq!(
1598 SipHeader::PDcsTracePartyId.to_string(),
1599 "P-DCS-Trace-Party-ID"
1600 );
1601 }
1602}