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