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 let rows = self.sip_header_all(SipHeader::CallInfo);
490 if rows.is_empty() {
491 return Ok(None);
492 }
493 UriInfo::from_entries(split_all(rows)).map(Some)
494 }
495
496 fn history_info(&self) -> Result<Option<HistoryInfo>, HistoryInfoError> {
500 let rows = self.sip_header_all(SipHeader::HistoryInfo);
501 if rows.is_empty() {
502 return Ok(None);
503 }
504 HistoryInfo::from_entries(split_all(rows)).map(Some)
505 }
506
507 fn p_asserted_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
512 parse_addr_list(self.sip_header_all(SipHeader::PAssertedIdentity))
513 }
514
515 fn p_preferred_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
517 parse_addr_list(self.sip_header_all(SipHeader::PPreferredIdentity))
518 }
519
520 fn route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
522 parse_addr_list(self.sip_header_all(SipHeader::Route))
523 }
524
525 fn record_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
527 parse_addr_list(self.sip_header_all(SipHeader::RecordRoute))
528 }
529
530 fn path(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
532 parse_addr_list(self.sip_header_all(SipHeader::Path))
533 }
534
535 fn service_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
537 parse_addr_list(self.sip_header_all(SipHeader::ServiceRoute))
538 }
539
540 fn contact(&self) -> Result<Vec<ContactValue>, ParseSipHeaderAddrError> {
545 crate::contact::parse_contact_entries(split_all(self.sip_header_all(SipHeader::Contact)))
546 }
547
548 fn alert_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
550 let rows = self.sip_header_all(SipHeader::AlertInfo);
551 if rows.is_empty() {
552 return Ok(None);
553 }
554 UriInfo::from_entries(split_all(rows)).map(Some)
555 }
556
557 fn error_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
559 let rows = self.sip_header_all(SipHeader::ErrorInfo);
560 if rows.is_empty() {
561 return Ok(None);
562 }
563 UriInfo::from_entries(split_all(rows)).map(Some)
564 }
565
566 fn allow(&self) -> Vec<&str> {
568 split_trim(self.sip_header_all(SipHeader::Allow))
569 }
570
571 fn supported(&self) -> Vec<&str> {
573 split_trim(self.sip_header_all(SipHeader::Supported))
574 }
575
576 fn require_header(&self) -> Vec<&str> {
578 split_trim(self.sip_header_all(SipHeader::Require))
579 }
580
581 fn proxy_require(&self) -> Vec<&str> {
583 split_trim(self.sip_header_all(SipHeader::ProxyRequire))
584 }
585
586 fn unsupported(&self) -> Vec<&str> {
588 split_trim(self.sip_header_all(SipHeader::Unsupported))
589 }
590
591 fn allow_events(&self) -> Vec<&str> {
593 split_trim(self.sip_header_all(SipHeader::AllowEvents))
594 }
595
596 fn content_encoding(&self) -> Vec<&str> {
598 split_trim(self.sip_header_all(SipHeader::ContentEncoding))
599 }
600
601 fn content_language(&self) -> Vec<&str> {
603 split_trim(self.sip_header_all(SipHeader::ContentLanguage))
604 }
605
606 fn in_reply_to(&self) -> Vec<&str> {
608 split_trim(self.sip_header_all(SipHeader::InReplyTo))
609 }
610
611 fn via(&self) -> Result<Option<SipVia>, SipViaError> {
613 let rows = self.sip_header_all(SipHeader::Via);
614 if rows.is_empty() {
615 return Ok(None);
616 }
617 SipVia::from_entries(split_all(rows)).map(Some)
618 }
619
620 fn replaces(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
622 match self.sip_header(SipHeader::Replaces) {
623 Some(s) => SipReplaces::parse(s).map(Some),
624 None => Ok(None),
625 }
626 }
627
628 fn join(&self) -> Result<Option<SipReplaces>, SipReplacesError> {
633 match self.sip_header(SipHeader::Join) {
634 Some(s) => SipReplaces::parse(s).map(Some),
635 None => Ok(None),
636 }
637 }
638
639 fn target_dialog(&self) -> Result<Option<SipTargetDialog>, SipTargetDialogError> {
641 match self.sip_header(SipHeader::TargetDialog) {
642 Some(s) => SipTargetDialog::parse(s).map(Some),
643 None => Ok(None),
644 }
645 }
646
647 fn authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
652 self.sip_header_all(SipHeader::Authorization)
653 .into_iter()
654 .map(|s| s.parse::<SipAuthValue>())
655 .collect()
656 }
657
658 fn proxy_authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
660 self.sip_header_all(SipHeader::ProxyAuthorization)
661 .into_iter()
662 .map(|s| s.parse::<SipAuthValue>())
663 .collect()
664 }
665
666 fn www_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
668 self.sip_header_all(SipHeader::WwwAuthenticate)
669 .into_iter()
670 .map(|s| s.parse::<SipAuthValue>())
671 .collect()
672 }
673
674 fn proxy_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
676 self.sip_header_all(SipHeader::ProxyAuthenticate)
677 .into_iter()
678 .map(|s| s.parse::<SipAuthValue>())
679 .collect()
680 }
681
682 fn warning(&self) -> Result<Option<SipWarning>, SipWarningError> {
684 let rows = self.sip_header_all(SipHeader::Warning);
685 if rows.is_empty() {
686 return Ok(None);
687 }
688 SipWarning::from_entries(split_all(rows)).map(Some)
689 }
690
691 fn security_client(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
693 let rows = self.sip_header_all(SipHeader::SecurityClient);
694 if rows.is_empty() {
695 return Ok(None);
696 }
697 SipSecurity::from_entries(split_all(rows)).map(Some)
698 }
699
700 fn security_server(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
702 let rows = self.sip_header_all(SipHeader::SecurityServer);
703 if rows.is_empty() {
704 return Ok(None);
705 }
706 SipSecurity::from_entries(split_all(rows)).map(Some)
707 }
708
709 fn security_verify(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
711 let rows = self.sip_header_all(SipHeader::SecurityVerify);
712 if rows.is_empty() {
713 return Ok(None);
714 }
715 SipSecurity::from_entries(split_all(rows)).map(Some)
716 }
717
718 fn accept(&self) -> Result<Option<SipAccept>, SipAcceptError> {
720 let rows = self.sip_header_all(SipHeader::Accept);
721 if rows.is_empty() {
722 return Ok(None);
723 }
724 SipAccept::from_entries(split_all(rows)).map(Some)
725 }
726
727 fn accept_encoding(&self) -> Result<Option<SipAcceptEncoding>, SipAcceptEncodingError> {
729 let rows = self.sip_header_all(SipHeader::AcceptEncoding);
730 if rows.is_empty() {
731 return Ok(None);
732 }
733 SipAcceptEncoding::from_entries(split_all(rows)).map(Some)
734 }
735
736 fn accept_language(&self) -> Result<Option<SipAcceptLanguage>, SipAcceptLanguageError> {
738 let rows = self.sip_header_all(SipHeader::AcceptLanguage);
739 if rows.is_empty() {
740 return Ok(None);
741 }
742 SipAcceptLanguage::from_entries(split_all(rows)).map(Some)
743 }
744
745 #[cfg(feature = "draft")]
747 fn diversion(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
748 parse_addr_list(self.sip_header_all(SipHeader::Diversion))
749 }
750
751 #[cfg(feature = "draft")]
753 fn remote_party_id(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
754 parse_addr_list(self.sip_header_all(SipHeader::RemotePartyId))
755 }
756}
757
758fn split_all(rows: Vec<&str>) -> impl Iterator<Item = &str> {
759 rows.into_iter()
760 .flat_map(crate::split_comma_entries)
761}
762
763fn parse_addr_list(rows: Vec<&str>) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
764 split_all(rows)
765 .map(|s| {
766 s.trim()
767 .parse::<SipHeaderAddr>()
768 })
769 .collect()
770}
771
772fn split_trim(rows: Vec<&str>) -> Vec<&str> {
773 split_all(rows)
774 .map(str::trim)
775 .collect()
776}
777
778impl SipHeaderLookup for std::collections::HashMap<String, String> {
779 fn sip_header_str(&self, name: &str) -> Option<&str> {
780 self.get(name)
781 .map(|s| s.as_str())
782 }
783}
784
785impl SipHeaderLookup for std::collections::HashMap<String, Vec<String>> {
786 fn sip_header_str(&self, name: &str) -> Option<&str> {
787 self.get(name)
788 .and_then(|v| v.first())
789 .map(|s| s.as_str())
790 }
791
792 fn sip_header_all_str(&self, name: &str) -> Vec<&str> {
793 self.get(name)
794 .map(|v| {
795 v.iter()
796 .map(|s| s.as_str())
797 .collect()
798 })
799 .unwrap_or_default()
800 }
801}
802
803#[cfg(test)]
804mod tests {
805 use super::*;
806 use std::collections::HashMap;
807
808 #[test]
809 fn display_round_trip() {
810 assert_eq!(SipHeader::CallInfo.to_string(), "Call-Info");
811 assert_eq!(SipHeader::HistoryInfo.to_string(), "History-Info");
812 assert_eq!(
813 SipHeader::PAssertedIdentity.to_string(),
814 "P-Asserted-Identity"
815 );
816 }
817
818 #[test]
819 fn as_ref_str() {
820 let h: &str = SipHeader::CallInfo.as_ref();
821 assert_eq!(h, "Call-Info");
822 }
823
824 #[test]
825 fn from_str_case_insensitive() {
826 assert_eq!("call-info".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
827 assert_eq!("CALL-INFO".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
828 assert_eq!(
829 "history-info".parse::<SipHeader>(),
830 Ok(SipHeader::HistoryInfo)
831 );
832 assert_eq!(
833 "p-asserted-identity".parse::<SipHeader>(),
834 Ok(SipHeader::PAssertedIdentity)
835 );
836 assert_eq!(
837 "P-ASSERTED-IDENTITY".parse::<SipHeader>(),
838 Ok(SipHeader::PAssertedIdentity)
839 );
840 }
841
842 #[test]
843 fn from_str_unknown() {
844 assert!("X-Custom"
845 .parse::<SipHeader>()
846 .is_err());
847 }
848
849 fn headers_with(pairs: &[(&str, &str)]) -> HashMap<String, String> {
850 pairs
851 .iter()
852 .map(|(k, v)| (k.to_string(), v.to_string()))
853 .collect()
854 }
855
856 #[test]
857 fn sip_header_by_enum() {
858 let h = headers_with(&[("Call-Info", "<urn:x>;purpose=icon")]);
859 assert_eq!(
860 h.sip_header(SipHeader::CallInfo),
861 Some("<urn:x>;purpose=icon")
862 );
863 }
864
865 #[test]
866 fn call_info_raw_lookup() {
867 let h = headers_with(&[(
868 "Call-Info",
869 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
870 )]);
871 assert_eq!(
872 h.sip_header(SipHeader::CallInfo),
873 Some("<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId")
874 );
875 }
876
877 #[test]
878 fn call_info_typed() {
879 let h = headers_with(&[(
880 "Call-Info",
881 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
882 )]);
883 let ci = h
884 .call_info()
885 .unwrap()
886 .unwrap();
887 assert_eq!(ci.len(), 1);
888 assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
889 }
890
891 #[test]
892 fn call_info_absent() {
893 let h = headers_with(&[]);
894 assert_eq!(
895 h.call_info()
896 .unwrap(),
897 None
898 );
899 }
900
901 #[test]
902 fn p_asserted_identity_typed() {
903 let h = headers_with(&[(
904 "P-Asserted-Identity",
905 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
906 )]);
907 let pais = h
908 .p_asserted_identity()
909 .unwrap();
910 assert_eq!(pais.len(), 1);
911 assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
912 }
913
914 #[test]
915 fn p_asserted_identity_multi_value() {
916 let h = headers_with(&[(
917 "P-Asserted-Identity",
918 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>, <tel:+15551234567>"#,
919 )]);
920 let pais = h
921 .p_asserted_identity()
922 .unwrap();
923 assert_eq!(pais.len(), 2);
924 assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
925 assert!(pais[1]
926 .uri()
927 .to_string()
928 .contains("+15551234567"));
929 }
930
931 #[test]
932 fn p_asserted_identity_absent() {
933 let h = headers_with(&[]);
934 assert!(h
935 .p_asserted_identity()
936 .unwrap()
937 .is_empty());
938 }
939
940 #[test]
941 fn history_info_raw_lookup() {
942 let h = headers_with(&[(
943 "History-Info",
944 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
945 )]);
946 assert!(h
947 .sip_header(SipHeader::HistoryInfo)
948 .unwrap()
949 .contains("esrp.example.com"));
950 }
951
952 #[test]
953 fn history_info_typed() {
954 let h = headers_with(&[(
955 "History-Info",
956 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
957 )]);
958 let hi = h
959 .history_info()
960 .unwrap()
961 .unwrap();
962 assert_eq!(hi.len(), 2);
963 assert_eq!(hi.entries()[0].index(), Some("1"));
964 assert_eq!(hi.entries()[1].index(), Some("1.1"));
965 }
966
967 #[test]
968 fn history_info_absent() {
969 let h = headers_with(&[]);
970 assert_eq!(
971 h.history_info()
972 .unwrap(),
973 None
974 );
975 }
976
977 #[test]
978 fn sip_header_all_str_default() {
979 let h = headers_with(&[("Via", "SIP/2.0/UDP host1")]);
980 let all = h.sip_header_all(SipHeader::Via);
981 assert_eq!(all.len(), 1);
982 assert_eq!(all[0], "SIP/2.0/UDP host1");
983 }
984
985 #[test]
986 fn sip_header_all_str_absent() {
987 let h = headers_with(&[]);
988 assert!(h
989 .sip_header_all(SipHeader::Via)
990 .is_empty());
991 }
992
993 #[test]
994 fn hashmap_vec_impl() {
995 let mut h: HashMap<String, Vec<String>> = HashMap::new();
996 h.insert(
997 "Via".into(),
998 vec!["SIP/2.0/UDP host1".into(), "SIP/2.0/UDP host2".into()],
999 );
1000 assert_eq!(h.sip_header_str("Via"), Some("SIP/2.0/UDP host1"));
1001 let all = h.sip_header_all_str("Via");
1002 assert_eq!(all.len(), 2);
1003 assert_eq!(all[0], "SIP/2.0/UDP host1");
1004 assert_eq!(all[1], "SIP/2.0/UDP host2");
1005 }
1006
1007 #[test]
1008 fn extract_from_sip_message() {
1009 let msg = concat!(
1010 "INVITE sip:bob@host SIP/2.0\r\n",
1011 "Call-Info: <urn:emergency:uid:callid:abc>;purpose=emergency-CallId\r\n",
1012 "History-Info: <sip:esrp@example.com>;index=1\r\n",
1013 "P-Asserted-Identity: \"Corp\" <sip:+15551234567@198.51.100.1>\r\n",
1014 "\r\n",
1015 );
1016 let ci = SipHeader::CallInfo.extract_from(msg);
1017 assert_eq!(ci.len(), 1);
1018 assert_eq!(
1019 ci[0],
1020 "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"
1021 );
1022
1023 let hi = SipHeader::HistoryInfo.extract_from(msg);
1024 assert_eq!(hi.len(), 1);
1025 assert_eq!(hi[0], "<sip:esrp@example.com>;index=1");
1026
1027 let pai = SipHeader::PAssertedIdentity.extract_from(msg);
1028 assert_eq!(pai.len(), 1);
1029 assert_eq!(pai[0], "\"Corp\" <sip:+15551234567@198.51.100.1>");
1030 }
1031
1032 #[test]
1033 fn extract_from_missing() {
1034 let msg = concat!(
1035 "INVITE sip:bob@host SIP/2.0\r\n",
1036 "From: Alice <sip:alice@host>\r\n",
1037 "\r\n",
1038 );
1039 assert!(SipHeader::CallInfo
1040 .extract_from(msg)
1041 .is_empty());
1042 assert!(SipHeader::PAssertedIdentity
1043 .extract_from(msg)
1044 .is_empty());
1045 }
1046
1047 #[test]
1048 fn missing_headers_return_none() {
1049 let h = headers_with(&[]);
1050 assert_eq!(h.sip_header(SipHeader::CallInfo), None);
1051 assert_eq!(
1052 h.call_info()
1053 .unwrap(),
1054 None
1055 );
1056 assert_eq!(h.sip_header(SipHeader::HistoryInfo), None);
1057 assert_eq!(
1058 h.history_info()
1059 .unwrap(),
1060 None
1061 );
1062 assert_eq!(h.sip_header(SipHeader::PAssertedIdentity), None);
1063 assert!(h
1064 .p_asserted_identity()
1065 .unwrap()
1066 .is_empty());
1067 }
1068
1069 #[test]
1070 fn route_accessor() {
1071 let h = headers_with(&[(
1072 "Route",
1073 "<sip:proxy1.example.com;lr>, <sip:proxy2.example.com;lr>",
1074 )]);
1075 let routes = h
1076 .route()
1077 .unwrap();
1078 assert_eq!(routes.len(), 2);
1079 assert!(routes[0]
1080 .uri()
1081 .to_string()
1082 .contains("proxy1"));
1083 assert!(routes[1]
1084 .uri()
1085 .to_string()
1086 .contains("proxy2"));
1087 }
1088
1089 #[test]
1090 fn route_absent() {
1091 let h = headers_with(&[]);
1092 assert!(h
1093 .route()
1094 .unwrap()
1095 .is_empty());
1096 }
1097
1098 #[test]
1099 fn record_route_accessor() {
1100 let h = headers_with(&[("Record-Route", "<sip:ss1.example.com;lr>")]);
1101 let rr = h
1102 .record_route()
1103 .unwrap();
1104 assert_eq!(rr.len(), 1);
1105 }
1106
1107 #[test]
1108 fn allow_accessor() {
1109 let h = headers_with(&[("Allow", "INVITE, ACK, OPTIONS, BYE")]);
1110 let methods = h.allow();
1111 assert_eq!(methods, vec!["INVITE", "ACK", "OPTIONS", "BYE"]);
1112 }
1113
1114 #[test]
1115 fn allow_absent() {
1116 let h = headers_with(&[]);
1117 assert!(h
1118 .allow()
1119 .is_empty());
1120 }
1121
1122 #[test]
1123 fn supported_accessor() {
1124 let h = headers_with(&[("Supported", "100rel, timer")]);
1125 let opts = h.supported();
1126 assert_eq!(opts, vec!["100rel", "timer"]);
1127 }
1128
1129 #[test]
1130 fn require_header_accessor() {
1131 let h = headers_with(&[("Require", "100rel")]);
1132 assert_eq!(h.require_header(), vec!["100rel"]);
1133 }
1134
1135 #[test]
1136 fn alert_info_accessor() {
1137 let h = headers_with(&[("Alert-Info", "<http://www.example.com/sounds/moo.wav>")]);
1138 let ai = h
1139 .alert_info()
1140 .unwrap()
1141 .unwrap();
1142 assert_eq!(ai.len(), 1);
1143 assert!(ai.entries()[0]
1144 .data
1145 .contains("moo.wav"));
1146 }
1147
1148 #[test]
1149 fn error_info_accessor() {
1150 let h = headers_with(&[("Error-Info", "<sip:not-in-service@example.com>")]);
1151 let ei = h
1152 .error_info()
1153 .unwrap()
1154 .unwrap();
1155 assert_eq!(ei.len(), 1);
1156 }
1157
1158 #[test]
1159 fn p_preferred_identity_accessor() {
1160 let h = headers_with(&[(
1161 "P-Preferred-Identity",
1162 r#""User" <sip:+15551234567@198.51.100.1>"#,
1163 )]);
1164 let ppi = h
1165 .p_preferred_identity()
1166 .unwrap();
1167 assert_eq!(ppi.len(), 1);
1168 assert_eq!(ppi[0].display_name(), Some("User"));
1169 }
1170
1171 #[test]
1172 fn content_encoding_accessor() {
1173 let h = headers_with(&[("Content-Encoding", "gzip")]);
1174 assert_eq!(h.content_encoding(), vec!["gzip"]);
1175 }
1176
1177 #[test]
1178 fn contact_accessor() {
1179 let h = headers_with(&[("Contact", "<sip:alice@198.51.100.1>")]);
1180 let contacts = h
1181 .contact()
1182 .unwrap();
1183 assert_eq!(contacts.len(), 1);
1184 assert!(matches!(&contacts[0], ContactValue::Addr(_)));
1185 }
1186
1187 #[test]
1188 fn contact_wildcard() {
1189 let h = headers_with(&[("Contact", "*")]);
1190 let contacts = h
1191 .contact()
1192 .unwrap();
1193 assert_eq!(contacts.len(), 1);
1194 assert!(matches!(contacts[0], ContactValue::Wildcard));
1195 }
1196
1197 #[test]
1198 fn contact_absent() {
1199 let h = headers_with(&[]);
1200 assert!(h
1201 .contact()
1202 .unwrap()
1203 .is_empty());
1204 }
1205
1206 #[test]
1207 fn in_reply_to_accessor() {
1208 let h = headers_with(&[("In-Reply-To", "call1@example.com, call2@example.com")]);
1209 let calls = h.in_reply_to();
1210 assert_eq!(calls.len(), 2);
1211 assert_eq!(calls[0], "call1@example.com");
1212 }
1213
1214 #[test]
1215 fn via_accessor() {
1216 let h = headers_with(&[("Via", "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bK776")]);
1217 let via = h
1218 .via()
1219 .unwrap()
1220 .unwrap();
1221 assert_eq!(via.len(), 1);
1222 assert_eq!(via.entries()[0].transport(), "UDP");
1223 assert_eq!(via.entries()[0].host(), "198.51.100.1");
1224 }
1225
1226 #[test]
1227 fn replaces_accessor() {
1228 let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1229 let r = h
1230 .replaces()
1231 .unwrap()
1232 .unwrap();
1233 assert_eq!(r.call_id(), "abc123@203.0.113.5");
1234 assert_eq!(r.to_tag(), "t1");
1235 assert_eq!(r.from_tag(), "f1");
1236 }
1237
1238 #[test]
1239 fn replaces_absent() {
1240 let h = headers_with(&[]);
1241 assert!(h
1242 .replaces()
1243 .unwrap()
1244 .is_none());
1245 }
1246
1247 #[test]
1248 fn replaces_malformed_err() {
1249 let h = headers_with(&[("Replaces", "abc123@203.0.113.5;to-tag=t1")]);
1250 assert!(h
1251 .replaces()
1252 .is_err());
1253 }
1254
1255 #[test]
1256 fn join_accessor() {
1257 let h = headers_with(&[("Join", "abc123@203.0.113.5;to-tag=t1;from-tag=f1")]);
1258 let j = h
1259 .join()
1260 .unwrap()
1261 .unwrap();
1262 assert_eq!(j.call_id(), "abc123@203.0.113.5");
1263 }
1264
1265 #[test]
1266 fn target_dialog_accessor() {
1267 let h = headers_with(&[(
1268 "Target-Dialog",
1269 "abc123@203.0.113.5;local-tag=l1;remote-tag=r1",
1270 )]);
1271 let t = h
1272 .target_dialog()
1273 .unwrap()
1274 .unwrap();
1275 assert_eq!(t.local_tag(), "l1");
1276 assert_eq!(t.remote_tag(), "r1");
1277 }
1278
1279 #[test]
1280 fn authorization_accessor() {
1281 let h = headers_with(&[(
1282 "Authorization",
1283 "Digest username=\"alice\", realm=\"example.com\", nonce=\"abc123\"",
1284 )]);
1285 let auth = h
1286 .authorization()
1287 .unwrap();
1288 assert_eq!(auth.len(), 1);
1289 assert_eq!(auth[0].scheme(), "Digest");
1290 assert_eq!(auth[0].username(), Some("alice"));
1291 assert_eq!(auth[0].realm(), Some("example.com"));
1292 }
1293
1294 #[test]
1295 fn www_authenticate_accessor() {
1296 let h = headers_with(&[(
1297 "WWW-Authenticate",
1298 "Digest realm=\"example.com\", nonce=\"xyz789\"",
1299 )]);
1300 let challenges = h
1301 .www_authenticate()
1302 .unwrap();
1303 assert_eq!(challenges.len(), 1);
1304 assert_eq!(challenges[0].realm(), Some("example.com"));
1305 }
1306
1307 #[test]
1308 fn warning_accessor() {
1309 let h = headers_with(&[(
1310 "Warning",
1311 "301 198.51.100.1 \"Incompatible network protocol\"",
1312 )]);
1313 let w = h
1314 .warning()
1315 .unwrap()
1316 .unwrap();
1317 assert_eq!(w.len(), 1);
1318 assert_eq!(w.entries()[0].code(), 301);
1319 }
1320
1321 #[test]
1322 fn security_client_accessor() {
1323 let h = headers_with(&[("Security-Client", "tls;q=0.2, digest;d-qop=auth;q=0.1")]);
1324 let sec = h
1325 .security_client()
1326 .unwrap()
1327 .unwrap();
1328 assert_eq!(sec.len(), 2);
1329 assert_eq!(sec.entries()[0].mechanism(), "tls");
1330 }
1331
1332 #[test]
1333 fn accept_accessor() {
1334 let h = headers_with(&[("Accept", "application/sdp, application/pidf+xml;q=0.5")]);
1335 let accept = h
1336 .accept()
1337 .unwrap()
1338 .unwrap();
1339 assert_eq!(accept.len(), 2);
1340 assert_eq!(accept.entries()[0].media_range(), "application/sdp");
1341 }
1342
1343 #[test]
1344 fn accept_encoding_accessor() {
1345 let h = headers_with(&[("Accept-Encoding", "gzip;q=1.0, identity;q=0.5")]);
1346 let ae = h
1347 .accept_encoding()
1348 .unwrap()
1349 .unwrap();
1350 assert_eq!(ae.len(), 2);
1351 assert_eq!(ae.entries()[0].encoding(), "gzip");
1352 }
1353
1354 #[test]
1355 fn accept_language_accessor() {
1356 let h = headers_with(&[("Accept-Language", "en;q=0.9, fr;q=0.8")]);
1357 let al = h
1358 .accept_language()
1359 .unwrap()
1360 .unwrap();
1361 assert_eq!(al.len(), 2);
1362 assert_eq!(al.entries()[0].language(), "en");
1363 }
1364}
1365
1366#[cfg(test)]
1367mod compact_form_tests {
1368 use super::*;
1369
1370 #[test]
1371 fn from_compact_known() {
1372 assert_eq!(SipHeader::from_compact(b'f'), Some(SipHeader::From));
1373 assert_eq!(SipHeader::from_compact(b'F'), Some(SipHeader::From));
1374 assert_eq!(SipHeader::from_compact(b'v'), Some(SipHeader::Via));
1375 assert_eq!(SipHeader::from_compact(b'i'), Some(SipHeader::CallId));
1376 assert_eq!(SipHeader::from_compact(b'm'), Some(SipHeader::Contact));
1377 assert_eq!(SipHeader::from_compact(b't'), Some(SipHeader::To));
1378 assert_eq!(SipHeader::from_compact(b'c'), Some(SipHeader::ContentType));
1379 }
1380
1381 #[test]
1382 fn from_compact_unknown() {
1383 assert_eq!(SipHeader::from_compact(b'z'), None);
1384 assert_eq!(SipHeader::from_compact(b'g'), None);
1385 }
1386
1387 #[test]
1388 fn compact_form_roundtrip() {
1389 assert_eq!(SipHeader::From.compact_form(), Some('f'));
1390 assert_eq!(SipHeader::Via.compact_form(), Some('v'));
1391 assert_eq!(SipHeader::CallId.compact_form(), Some('i'));
1392 assert_eq!(SipHeader::Contact.compact_form(), Some('m'));
1393 }
1394
1395 #[test]
1396 fn compact_form_absent() {
1397 assert_eq!(SipHeader::HistoryInfo.compact_form(), None);
1398 assert_eq!(SipHeader::PAssertedIdentity.compact_form(), None);
1399 }
1400
1401 #[test]
1402 fn parse_name_compact() {
1403 assert_eq!(SipHeader::parse_name("f"), Ok(SipHeader::From));
1404 assert_eq!(SipHeader::parse_name("F"), Ok(SipHeader::From));
1405 assert_eq!(SipHeader::parse_name("v"), Ok(SipHeader::Via));
1406 }
1407
1408 #[test]
1409 fn parse_name_full() {
1410 assert_eq!(SipHeader::parse_name("From"), Ok(SipHeader::From));
1411 assert_eq!(SipHeader::parse_name("Via"), Ok(SipHeader::Via));
1412 }
1413
1414 #[test]
1415 fn parse_name_unknown() {
1416 assert!(SipHeader::parse_name("X-Custom").is_err());
1417 }
1418
1419 #[test]
1420 fn all_compact_forms_resolve() {
1421 let expected = [
1422 ('a', SipHeader::AcceptContact),
1423 ('b', SipHeader::ReferredBy),
1424 ('c', SipHeader::ContentType),
1425 ('d', SipHeader::RequestDisposition),
1426 ('e', SipHeader::ContentEncoding),
1427 ('f', SipHeader::From),
1428 ('i', SipHeader::CallId),
1429 ('j', SipHeader::RejectContact),
1430 ('k', SipHeader::Supported),
1431 ('l', SipHeader::ContentLength),
1432 ('m', SipHeader::Contact),
1433 ('n', SipHeader::IdentityInfo),
1434 ('o', SipHeader::Event),
1435 ('r', SipHeader::ReferTo),
1436 ('s', SipHeader::Subject),
1437 ('t', SipHeader::To),
1438 ('u', SipHeader::AllowEvents),
1439 ('v', SipHeader::Via),
1440 ('x', SipHeader::SessionExpires),
1441 ('y', SipHeader::Identity),
1442 ];
1443 for (ch, header) in expected {
1444 assert_eq!(
1445 SipHeader::from_compact(ch as u8),
1446 Some(header),
1447 "compact form '{ch}' failed"
1448 );
1449 assert_eq!(
1450 header.compact_form(),
1451 Some(ch),
1452 "compact_form() for {} failed",
1453 header
1454 );
1455 }
1456 }
1457}
1458
1459#[cfg(test)]
1460mod multi_valued_tests {
1461 use super::*;
1462
1463 #[test]
1464 fn rfc3261_multi_valued_headers() {
1465 assert!(SipHeader::Via.is_multi_valued());
1466 assert!(SipHeader::Route.is_multi_valued());
1467 assert!(SipHeader::RecordRoute.is_multi_valued());
1468 assert!(SipHeader::Contact.is_multi_valued());
1469 assert!(SipHeader::Allow.is_multi_valued());
1470 assert!(SipHeader::Supported.is_multi_valued());
1471 assert!(SipHeader::Require.is_multi_valued());
1472 assert!(SipHeader::ProxyRequire.is_multi_valued());
1473 assert!(SipHeader::Unsupported.is_multi_valued());
1474 assert!(SipHeader::Authorization.is_multi_valued());
1475 assert!(SipHeader::ProxyAuthorization.is_multi_valued());
1476 assert!(SipHeader::WwwAuthenticate.is_multi_valued());
1477 assert!(SipHeader::ProxyAuthenticate.is_multi_valued());
1478 assert!(SipHeader::Warning.is_multi_valued());
1479 assert!(SipHeader::ErrorInfo.is_multi_valued());
1480 assert!(SipHeader::CallInfo.is_multi_valued());
1481 assert!(SipHeader::AlertInfo.is_multi_valued());
1482 assert!(SipHeader::Accept.is_multi_valued());
1483 assert!(SipHeader::AcceptEncoding.is_multi_valued());
1484 assert!(SipHeader::AcceptLanguage.is_multi_valued());
1485 assert!(SipHeader::ContentEncoding.is_multi_valued());
1486 assert!(SipHeader::ContentLanguage.is_multi_valued());
1487 assert!(SipHeader::InReplyTo.is_multi_valued());
1488 }
1489
1490 #[test]
1491 fn extension_multi_valued_headers() {
1492 assert!(SipHeader::PAssertedIdentity.is_multi_valued());
1493 assert!(SipHeader::PPreferredIdentity.is_multi_valued());
1494 assert!(SipHeader::AllowEvents.is_multi_valued());
1495 assert!(SipHeader::SecurityClient.is_multi_valued());
1496 assert!(SipHeader::SecurityServer.is_multi_valued());
1497 assert!(SipHeader::SecurityVerify.is_multi_valued());
1498 assert!(SipHeader::Path.is_multi_valued());
1499 assert!(SipHeader::ServiceRoute.is_multi_valued());
1500 assert!(SipHeader::HistoryInfo.is_multi_valued());
1501 }
1502
1503 #[test]
1504 fn single_valued_headers() {
1505 assert!(!SipHeader::From.is_multi_valued());
1506 assert!(!SipHeader::To.is_multi_valued());
1507 assert!(!SipHeader::CallId.is_multi_valued());
1508 assert!(!SipHeader::Cseq.is_multi_valued());
1509 assert!(!SipHeader::MaxForwards.is_multi_valued());
1510 assert!(!SipHeader::ContentType.is_multi_valued());
1511 assert!(!SipHeader::ContentLength.is_multi_valued());
1512 assert!(!SipHeader::Expires.is_multi_valued());
1513 assert!(!SipHeader::Date.is_multi_valued());
1514 assert!(!SipHeader::Subject.is_multi_valued());
1515 assert!(!SipHeader::ReplyTo.is_multi_valued());
1516 assert!(!SipHeader::Server.is_multi_valued());
1517 assert!(!SipHeader::UserAgent.is_multi_valued());
1518 }
1519
1520 #[test]
1521 #[cfg(feature = "draft")]
1522 fn draft_multi_valued_headers() {
1523 assert!(SipHeader::Diversion.is_multi_valued());
1524 assert!(SipHeader::RemotePartyId.is_multi_valued());
1525 }
1526
1527 #[test]
1528 #[cfg(feature = "draft")]
1529 fn draft_parse_roundtrip() {
1530 let d: SipHeader = "Diversion"
1531 .parse()
1532 .unwrap();
1533 assert_eq!(d, SipHeader::Diversion);
1534 assert_eq!(d.to_string(), "Diversion");
1535
1536 let r: SipHeader = "remote-party-id"
1537 .parse()
1538 .unwrap();
1539 assert_eq!(r, SipHeader::RemotePartyId);
1540 assert_eq!(r.to_string(), "Remote-Party-ID");
1541 }
1542}
1543
1544#[cfg(test)]
1545mod special_case_tests {
1546 use super::*;
1547
1548 #[test]
1549 fn cseq_variants() {
1550 assert_eq!("CSeq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1551 assert_eq!("cseq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1552 assert_eq!("CSEQ".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1553 assert_eq!(SipHeader::Cseq.to_string(), "CSeq");
1554 }
1555
1556 #[test]
1557 fn www_authenticate_variants() {
1558 assert_eq!(
1559 "WWW-Authenticate".parse::<SipHeader>(),
1560 Ok(SipHeader::WwwAuthenticate)
1561 );
1562 assert_eq!(
1563 "www-authenticate".parse::<SipHeader>(),
1564 Ok(SipHeader::WwwAuthenticate)
1565 );
1566 assert_eq!(SipHeader::WwwAuthenticate.to_string(), "WWW-Authenticate");
1567 }
1568
1569 #[test]
1570 fn rack_rseq_variants() {
1571 assert_eq!("RAck".parse::<SipHeader>(), Ok(SipHeader::Rack));
1572 assert_eq!("rack".parse::<SipHeader>(), Ok(SipHeader::Rack));
1573 assert_eq!(SipHeader::Rack.to_string(), "RAck");
1574
1575 assert_eq!("RSeq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1576 assert_eq!("rseq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1577 assert_eq!(SipHeader::Rseq.to_string(), "RSeq");
1578 }
1579
1580 #[test]
1581 fn user_to_user_variants() {
1582 assert_eq!(
1583 "User-to-User".parse::<SipHeader>(),
1584 Ok(SipHeader::UserToUser)
1585 );
1586 assert_eq!(
1587 "user-to-user".parse::<SipHeader>(),
1588 Ok(SipHeader::UserToUser)
1589 );
1590 assert_eq!(SipHeader::UserToUser.to_string(), "User-to-User");
1591 }
1592
1593 #[test]
1594 fn p_header_variants() {
1595 assert_eq!(
1596 "P-DCS-Trace-Party-ID".parse::<SipHeader>(),
1597 Ok(SipHeader::PDcsTracePartyId)
1598 );
1599 assert_eq!(
1600 "p-dcs-trace-party-id".parse::<SipHeader>(),
1601 Ok(SipHeader::PDcsTracePartyId)
1602 );
1603 assert_eq!(
1604 SipHeader::PDcsTracePartyId.to_string(),
1605 "P-DCS-Trace-Party-ID"
1606 );
1607 }
1608}
1609
1610#[cfg(test)]
1611mod multi_row_tests {
1612 use super::*;
1613 use std::collections::HashMap;
1614
1615 fn rows(pairs: &[(&str, &[&str])]) -> HashMap<String, Vec<String>> {
1616 pairs
1617 .iter()
1618 .map(|(k, vs)| {
1619 (
1620 k.to_string(),
1621 vs.iter()
1622 .map(|v| v.to_string())
1623 .collect(),
1624 )
1625 })
1626 .collect()
1627 }
1628
1629 #[test]
1630 fn via_reads_every_row() {
1631 let h = rows(&[(
1632 "Via",
1633 &[
1634 "SIP/2.0/UDP 198.51.100.1;branch=z9hG4bK1",
1635 "SIP/2.0/TCP 203.0.113.5",
1636 ],
1637 )]);
1638 let via = h
1639 .via()
1640 .unwrap()
1641 .unwrap();
1642 assert_eq!(via.len(), 2);
1643 assert_eq!(via.entries()[1].transport(), "TCP");
1644 }
1645
1646 #[test]
1647 fn via_row_with_list_plus_row() {
1648 let h = rows(&[(
1649 "Via",
1650 &[
1651 "SIP/2.0/UDP 198.51.100.1, SIP/2.0/UDP 198.51.100.2",
1652 "SIP/2.0/TCP 203.0.113.5",
1653 ],
1654 )]);
1655 assert_eq!(
1656 h.via()
1657 .unwrap()
1658 .unwrap()
1659 .len(),
1660 3
1661 );
1662 }
1663
1664 #[test]
1665 fn via_blank_rows() {
1666 let h = rows(&[("Via", &[""])]);
1667 assert!(matches!(h.via(), Err(SipViaError::Empty)));
1668 let h = rows(&[("Via", &[" "])]);
1669 assert!(matches!(h.via(), Err(SipViaError::InvalidFormat(_))));
1670 }
1671
1672 #[test]
1673 fn typed_lists_read_every_row() {
1674 let h = rows(&[
1675 (
1676 "Warning",
1677 &[r#"301 example.com "a""#, r#"399 example.org "b""#],
1678 ),
1679 ("Accept", &["application/sdp", "text/plain"]),
1680 ("Accept-Encoding", &["gzip", "identity"]),
1681 ("Accept-Language", &["en", "fr"]),
1682 ("Security-Client", &["tls", "digest"]),
1683 ("Security-Server", &["tls", "digest"]),
1684 ("Security-Verify", &["tls", "digest"]),
1685 (
1686 "Call-Info",
1687 &["<http://example.com/a>", "<http://example.com/b>"],
1688 ),
1689 (
1690 "Alert-Info",
1691 &["<http://example.com/a>", "<http://example.com/b>"],
1692 ),
1693 (
1694 "Error-Info",
1695 &["<http://example.com/a>", "<http://example.com/b>"],
1696 ),
1697 (
1698 "History-Info",
1699 &[
1700 "<sip:a@example.com>;index=1",
1701 "<sip:b@example.com>;index=1.1",
1702 ],
1703 ),
1704 ]);
1705 assert_eq!(
1706 h.warning()
1707 .unwrap()
1708 .unwrap()
1709 .len(),
1710 2
1711 );
1712 assert_eq!(
1713 h.accept()
1714 .unwrap()
1715 .unwrap()
1716 .len(),
1717 2
1718 );
1719 assert_eq!(
1720 h.accept_encoding()
1721 .unwrap()
1722 .unwrap()
1723 .len(),
1724 2
1725 );
1726 assert_eq!(
1727 h.accept_language()
1728 .unwrap()
1729 .unwrap()
1730 .len(),
1731 2
1732 );
1733 assert_eq!(
1734 h.security_client()
1735 .unwrap()
1736 .unwrap()
1737 .len(),
1738 2
1739 );
1740 assert_eq!(
1741 h.security_server()
1742 .unwrap()
1743 .unwrap()
1744 .len(),
1745 2
1746 );
1747 assert_eq!(
1748 h.security_verify()
1749 .unwrap()
1750 .unwrap()
1751 .len(),
1752 2
1753 );
1754 assert_eq!(
1755 h.call_info()
1756 .unwrap()
1757 .unwrap()
1758 .len(),
1759 2
1760 );
1761 assert_eq!(
1762 h.alert_info()
1763 .unwrap()
1764 .unwrap()
1765 .len(),
1766 2
1767 );
1768 assert_eq!(
1769 h.error_info()
1770 .unwrap()
1771 .unwrap()
1772 .len(),
1773 2
1774 );
1775 assert_eq!(
1776 h.history_info()
1777 .unwrap()
1778 .unwrap()
1779 .len(),
1780 2
1781 );
1782 }
1783
1784 #[test]
1785 fn contact_reads_every_row() {
1786 let h = rows(&[(
1787 "Contact",
1788 &[
1789 "<sip:a@example.com>",
1790 "<sip:b@example.com>, <sip:c@example.com>",
1791 ],
1792 )]);
1793 assert_eq!(
1794 h.contact()
1795 .unwrap()
1796 .len(),
1797 3
1798 );
1799 let h = rows(&[("Contact", &["*", "<sip:a@example.com>"])]);
1800 assert!(h
1801 .contact()
1802 .is_err());
1803 let h = rows(&[("Contact", &[""])]);
1804 assert!(h
1805 .contact()
1806 .unwrap()
1807 .is_empty());
1808 }
1809
1810 #[test]
1811 fn token_lists_read_every_row() {
1812 let h = rows(&[
1813 ("Allow", &["INVITE, ACK", "BYE"]),
1814 ("Supported", &["100rel", "timer"]),
1815 ("Require", &["100rel", "timer"]),
1816 ("Proxy-Require", &["100rel", "timer"]),
1817 ("Unsupported", &["100rel", "timer"]),
1818 ("Allow-Events", &["dialog", "presence"]),
1819 ("Content-Encoding", &["gzip", "identity"]),
1820 ("Content-Language", &["en", "fr"]),
1821 ("In-Reply-To", &["a@example.com", "b@example.com"]),
1822 ]);
1823 assert_eq!(h.allow(), vec!["INVITE", "ACK", "BYE"]);
1824 assert_eq!(
1825 h.supported()
1826 .len(),
1827 2
1828 );
1829 assert_eq!(
1830 h.require_header()
1831 .len(),
1832 2
1833 );
1834 assert_eq!(
1835 h.proxy_require()
1836 .len(),
1837 2
1838 );
1839 assert_eq!(
1840 h.unsupported()
1841 .len(),
1842 2
1843 );
1844 assert_eq!(
1845 h.allow_events()
1846 .len(),
1847 2
1848 );
1849 assert_eq!(
1850 h.content_encoding()
1851 .len(),
1852 2
1853 );
1854 assert_eq!(
1855 h.content_language()
1856 .len(),
1857 2
1858 );
1859 assert_eq!(
1860 h.in_reply_to()
1861 .len(),
1862 2
1863 );
1864 }
1865
1866 #[test]
1867 fn token_list_blank_rows() {
1868 let h = rows(&[("Allow", &[""])]);
1869 assert!(h
1870 .allow()
1871 .is_empty());
1872 let h = rows(&[("Allow", &[" "])]);
1873 assert_eq!(h.allow(), vec![""]);
1874 }
1875
1876 #[test]
1877 fn addr_list_blank_row_is_error() {
1878 let h = rows(&[("Route", &[" "])]);
1879 assert!(h
1880 .route()
1881 .is_err());
1882 }
1883}