1use crate::call_info::{SipCallInfo, SipCallInfoError};
8use crate::header_addr::{ParseSipHeaderAddrError, SipHeaderAddr};
9use crate::history_info::{HistoryInfo, HistoryInfoError};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct ParseSipHeaderError(pub String);
14
15impl std::fmt::Display for ParseSipHeaderError {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 write!(f, "unknown SIP header: {}", self.0)
18 }
19}
20
21impl std::error::Error for ParseSipHeaderError {}
22
23define_header_enum! {
24 error_type: ParseSipHeaderError,
25 pub enum SipHeader {
31 Accept => "Accept",
33 AcceptContact => "Accept-Contact",
35 AcceptEncoding => "Accept-Encoding",
37 AcceptLanguage => "Accept-Language",
39 AcceptResourcePriority => "Accept-Resource-Priority",
41 AdditionalIdentity => "Additional-Identity",
43 AlertInfo => "Alert-Info",
45 AlertmsgError => "AlertMsg-Error",
47 Allow => "Allow",
49 AllowEvents => "Allow-Events",
51 AnswerMode => "Answer-Mode",
53 AttestationInfo => "Attestation-Info",
55 AuthenticationInfo => "Authentication-Info",
57 Authorization => "Authorization",
59 CallId => "Call-ID",
61 CallInfo => "Call-Info",
63 CellularNetworkInfo => "Cellular-Network-Info",
65 Contact => "Contact",
67 ContentDisposition => "Content-Disposition",
69 ContentEncoding => "Content-Encoding",
71 ContentId => "Content-ID",
73 ContentLanguage => "Content-Language",
75 ContentLength => "Content-Length",
77 ContentType => "Content-Type",
79 Cseq => "CSeq",
81 Date => "Date",
83 DcInfo => "DC-Info",
85 Encryption => "Encryption",
87 ErrorInfo => "Error-Info",
89 Event => "Event",
91 Expires => "Expires",
93 FeatureCaps => "Feature-Caps",
95 FlowTimer => "Flow-Timer",
97 From => "From",
99 Geolocation => "Geolocation",
101 GeolocationError => "Geolocation-Error",
103 GeolocationRouting => "Geolocation-Routing",
105 Hide => "Hide",
107 HistoryInfo => "History-Info",
109 Identity => "Identity",
111 IdentityInfo => "Identity-Info",
113 InfoPackage => "Info-Package",
115 InReplyTo => "In-Reply-To",
117 Join => "Join",
119 MaxBreadth => "Max-Breadth",
121 MaxForwards => "Max-Forwards",
123 MimeVersion => "MIME-Version",
125 MinExpires => "Min-Expires",
127 MinSe => "Min-SE",
129 Organization => "Organization",
131 OriginationId => "Origination-Id",
133 PAccessNetworkInfo => "P-Access-Network-Info",
135 PAnswerState => "P-Answer-State",
137 PAssertedIdentity => "P-Asserted-Identity",
139 PAssertedService => "P-Asserted-Service",
141 PAssociatedUri => "P-Associated-URI",
143 PCalledPartyId => "P-Called-Party-ID",
145 PChargeInfo => "P-Charge-Info",
147 PChargingFunctionAddresses => "P-Charging-Function-Addresses",
149 PChargingVector => "P-Charging-Vector",
151 PDcsTracePartyId => "P-DCS-Trace-Party-ID",
153 PDcsOsps => "P-DCS-OSPS",
155 PDcsBillingInfo => "P-DCS-Billing-Info",
157 PDcsLaes => "P-DCS-LAES",
159 PDcsRedirect => "P-DCS-Redirect",
161 PEarlyMedia => "P-Early-Media",
163 PMediaAuthorization => "P-Media-Authorization",
165 PPreferredIdentity => "P-Preferred-Identity",
167 PPreferredService => "P-Preferred-Service",
169 PPrivateNetworkIndication => "P-Private-Network-Indication",
171 PProfileKey => "P-Profile-Key",
173 PRefusedUriList => "P-Refused-URI-List",
175 PServedUser => "P-Served-User",
177 PUserDatabase => "P-User-Database",
179 PVisitedNetworkId => "P-Visited-Network-ID",
181 Path => "Path",
183 PermissionMissing => "Permission-Missing",
185 PolicyContact => "Policy-Contact",
187 PolicyId => "Policy-ID",
189 Priority => "Priority",
191 PriorityShare => "Priority-Share",
193 PriorityVerstat => "Priority-Verstat",
195 PrivAnswerMode => "Priv-Answer-Mode",
197 Privacy => "Privacy",
199 ProxyAuthenticate => "Proxy-Authenticate",
201 ProxyAuthorization => "Proxy-Authorization",
203 ProxyRequire => "Proxy-Require",
205 Rack => "RAck",
207 Reason => "Reason",
209 ReasonPhrase => "Reason-Phrase",
211 RecordRoute => "Record-Route",
213 RecvInfo => "Recv-Info",
215 ReferEventsAt => "Refer-Events-At",
217 ReferSub => "Refer-Sub",
219 ReferTo => "Refer-To",
221 ReferredBy => "Referred-By",
223 RejectContact => "Reject-Contact",
225 RelayedCharge => "Relayed-Charge",
227 Replaces => "Replaces",
229 ReplyTo => "Reply-To",
231 RequestDisposition => "Request-Disposition",
233 Require => "Require",
235 ResourcePriority => "Resource-Priority",
237 ResourceShare => "Resource-Share",
239 ResponseKey => "Response-Key",
241 ResponseSource => "Response-Source",
243 RestorationInfo => "Restoration-Info",
245 RetryAfter => "Retry-After",
247 Route => "Route",
249 Rseq => "RSeq",
251 SecurityClient => "Security-Client",
253 SecurityServer => "Security-Server",
255 SecurityVerify => "Security-Verify",
257 Server => "Server",
259 ServiceInteractInfo => "Service-Interact-Info",
261 ServiceRoute => "Service-Route",
263 SessionExpires => "Session-Expires",
265 SessionId => "Session-ID",
267 SipEtag => "SIP-ETag",
269 SipIfMatch => "SIP-If-Match",
271 Subject => "Subject",
273 SubscriptionState => "Subscription-State",
275 Supported => "Supported",
277 SuppressIfMatch => "Suppress-If-Match",
279 TargetDialog => "Target-Dialog",
281 Timestamp => "Timestamp",
283 To => "To",
285 TriggerConsent => "Trigger-Consent",
287 Unsupported => "Unsupported",
289 UserAgent => "User-Agent",
291 UserToUser => "User-to-User",
293 Via => "Via",
295 Warning => "Warning",
297 WwwAuthenticate => "WWW-Authenticate",
299 }
300}
301
302const COMPACT_FORMS: &[(u8, SipHeader)] = &[
307 (b'a', SipHeader::AcceptContact),
308 (b'b', SipHeader::ReferredBy),
309 (b'c', SipHeader::ContentType),
310 (b'd', SipHeader::RequestDisposition),
311 (b'e', SipHeader::ContentEncoding),
312 (b'f', SipHeader::From),
313 (b'i', SipHeader::CallId),
314 (b'j', SipHeader::RejectContact),
315 (b'k', SipHeader::Supported),
316 (b'l', SipHeader::ContentLength),
317 (b'm', SipHeader::Contact),
318 (b'n', SipHeader::IdentityInfo),
319 (b'o', SipHeader::Event),
320 (b'r', SipHeader::ReferTo),
321 (b's', SipHeader::Subject),
322 (b't', SipHeader::To),
323 (b'u', SipHeader::AllowEvents),
324 (b'v', SipHeader::Via),
325 (b'x', SipHeader::SessionExpires),
326 (b'y', SipHeader::Identity),
327];
328
329impl SipHeader {
330 pub fn from_compact(ch: u8) -> Option<Self> {
334 let lower = ch.to_ascii_lowercase();
335 COMPACT_FORMS
336 .iter()
337 .find(|(c, _)| *c == lower)
338 .map(|(_, h)| *h)
339 }
340
341 pub fn compact_form(&self) -> Option<char> {
343 COMPACT_FORMS
344 .iter()
345 .find(|(_, h)| h == self)
346 .map(|(c, _)| *c as char)
347 }
348
349 pub fn is_multi_valued(&self) -> bool {
354 matches!(
355 self,
356 Self::Via
358 | Self::Route
359 | Self::RecordRoute
360 | Self::Contact
361 | Self::Allow
362 | Self::Supported
363 | Self::Require
364 | Self::ProxyRequire
365 | Self::Unsupported
366 | Self::Authorization
367 | Self::ProxyAuthorization
368 | Self::WwwAuthenticate
369 | Self::ProxyAuthenticate
370 | Self::Warning
371 | Self::ErrorInfo
372 | Self::CallInfo
373 | Self::AlertInfo
374 | Self::Accept
375 | Self::AcceptEncoding
376 | Self::AcceptLanguage
377 | Self::ContentEncoding
378 | Self::ContentLanguage
379 | Self::InReplyTo
380 | Self::PAssertedIdentity
382 | Self::PPreferredIdentity
383 | Self::AllowEvents
385 | Self::SecurityClient
387 | Self::SecurityServer
388 | Self::SecurityVerify
389 | Self::Path
391 | Self::ServiceRoute
393 | Self::HistoryInfo
395 )
396 }
397
398 pub fn parse_name(name: &str) -> Result<Self, ParseSipHeaderError> {
403 if name.len() == 1 {
404 if let Some(h) = Self::from_compact(name.as_bytes()[0]) {
405 return Ok(h);
406 }
407 }
408 name.parse()
409 }
410}
411
412pub trait SipHeaderLookup {
438 fn sip_header_str(&self, name: &str) -> Option<&str>;
440
441 fn sip_header(&self, name: SipHeader) -> Option<&str> {
443 self.sip_header_str(name.as_str())
444 }
445
446 fn call_info_raw(&self) -> Option<&str> {
448 self.sip_header(SipHeader::CallInfo)
449 }
450
451 fn call_info(&self) -> Result<Option<SipCallInfo>, SipCallInfoError> {
455 match self.call_info_raw() {
456 Some(s) => SipCallInfo::parse(s).map(Some),
457 None => Ok(None),
458 }
459 }
460
461 fn history_info_raw(&self) -> Option<&str> {
463 self.sip_header(SipHeader::HistoryInfo)
464 }
465
466 fn history_info(&self) -> Result<Option<HistoryInfo>, HistoryInfoError> {
470 match self.history_info_raw() {
471 Some(s) => HistoryInfo::parse(s).map(Some),
472 None => Ok(None),
473 }
474 }
475
476 fn p_asserted_identity_raw(&self) -> Option<&str> {
478 self.sip_header(SipHeader::PAssertedIdentity)
479 }
480
481 fn p_asserted_identity(&self) -> Result<Option<SipHeaderAddr>, ParseSipHeaderAddrError> {
485 match self.p_asserted_identity_raw() {
486 Some(s) => s
487 .parse::<SipHeaderAddr>()
488 .map(Some),
489 None => Ok(None),
490 }
491 }
492}
493
494impl SipHeaderLookup for std::collections::HashMap<String, String> {
495 fn sip_header_str(&self, name: &str) -> Option<&str> {
496 self.get(name)
497 .map(|s| s.as_str())
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use super::*;
504 use std::collections::HashMap;
505
506 #[test]
507 fn display_round_trip() {
508 assert_eq!(SipHeader::CallInfo.to_string(), "Call-Info");
509 assert_eq!(SipHeader::HistoryInfo.to_string(), "History-Info");
510 assert_eq!(
511 SipHeader::PAssertedIdentity.to_string(),
512 "P-Asserted-Identity"
513 );
514 }
515
516 #[test]
517 fn as_ref_str() {
518 let h: &str = SipHeader::CallInfo.as_ref();
519 assert_eq!(h, "Call-Info");
520 }
521
522 #[test]
523 fn from_str_case_insensitive() {
524 assert_eq!("call-info".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
525 assert_eq!("CALL-INFO".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
526 assert_eq!(
527 "history-info".parse::<SipHeader>(),
528 Ok(SipHeader::HistoryInfo)
529 );
530 assert_eq!(
531 "p-asserted-identity".parse::<SipHeader>(),
532 Ok(SipHeader::PAssertedIdentity)
533 );
534 assert_eq!(
535 "P-ASSERTED-IDENTITY".parse::<SipHeader>(),
536 Ok(SipHeader::PAssertedIdentity)
537 );
538 }
539
540 #[test]
541 fn from_str_unknown() {
542 assert!("X-Custom"
543 .parse::<SipHeader>()
544 .is_err());
545 }
546
547 #[test]
548 fn from_str_round_trip_all() {
549 let variants = [
550 SipHeader::Accept,
551 SipHeader::AcceptContact,
552 SipHeader::AcceptEncoding,
553 SipHeader::AcceptLanguage,
554 SipHeader::AcceptResourcePriority,
555 SipHeader::AdditionalIdentity,
556 SipHeader::AlertInfo,
557 SipHeader::AlertmsgError,
558 SipHeader::Allow,
559 SipHeader::AllowEvents,
560 SipHeader::AnswerMode,
561 SipHeader::AttestationInfo,
562 SipHeader::AuthenticationInfo,
563 SipHeader::Authorization,
564 SipHeader::CallId,
565 SipHeader::CallInfo,
566 SipHeader::CellularNetworkInfo,
567 SipHeader::Contact,
568 SipHeader::ContentDisposition,
569 SipHeader::ContentEncoding,
570 SipHeader::ContentId,
571 SipHeader::ContentLanguage,
572 SipHeader::ContentLength,
573 SipHeader::ContentType,
574 SipHeader::Cseq,
575 SipHeader::Date,
576 SipHeader::DcInfo,
577 SipHeader::Encryption,
578 SipHeader::ErrorInfo,
579 SipHeader::Event,
580 SipHeader::Expires,
581 SipHeader::FeatureCaps,
582 SipHeader::FlowTimer,
583 SipHeader::From,
584 SipHeader::Geolocation,
585 SipHeader::GeolocationError,
586 SipHeader::GeolocationRouting,
587 SipHeader::Hide,
588 SipHeader::HistoryInfo,
589 SipHeader::Identity,
590 SipHeader::IdentityInfo,
591 SipHeader::InfoPackage,
592 SipHeader::InReplyTo,
593 SipHeader::Join,
594 SipHeader::MaxBreadth,
595 SipHeader::MaxForwards,
596 SipHeader::MimeVersion,
597 SipHeader::MinExpires,
598 SipHeader::MinSe,
599 SipHeader::Organization,
600 SipHeader::OriginationId,
601 SipHeader::PAccessNetworkInfo,
602 SipHeader::PAnswerState,
603 SipHeader::PAssertedIdentity,
604 SipHeader::PAssertedService,
605 SipHeader::PAssociatedUri,
606 SipHeader::PCalledPartyId,
607 SipHeader::PChargeInfo,
608 SipHeader::PChargingFunctionAddresses,
609 SipHeader::PChargingVector,
610 SipHeader::PDcsTracePartyId,
611 SipHeader::PDcsOsps,
612 SipHeader::PDcsBillingInfo,
613 SipHeader::PDcsLaes,
614 SipHeader::PDcsRedirect,
615 SipHeader::PEarlyMedia,
616 SipHeader::PMediaAuthorization,
617 SipHeader::PPreferredIdentity,
618 SipHeader::PPreferredService,
619 SipHeader::PPrivateNetworkIndication,
620 SipHeader::PProfileKey,
621 SipHeader::PRefusedUriList,
622 SipHeader::PServedUser,
623 SipHeader::PUserDatabase,
624 SipHeader::PVisitedNetworkId,
625 SipHeader::Path,
626 SipHeader::PermissionMissing,
627 SipHeader::PolicyContact,
628 SipHeader::PolicyId,
629 SipHeader::Priority,
630 SipHeader::PriorityShare,
631 SipHeader::PriorityVerstat,
632 SipHeader::PrivAnswerMode,
633 SipHeader::Privacy,
634 SipHeader::ProxyAuthenticate,
635 SipHeader::ProxyAuthorization,
636 SipHeader::ProxyRequire,
637 SipHeader::Rack,
638 SipHeader::Reason,
639 SipHeader::ReasonPhrase,
640 SipHeader::RecordRoute,
641 SipHeader::RecvInfo,
642 SipHeader::ReferEventsAt,
643 SipHeader::ReferSub,
644 SipHeader::ReferTo,
645 SipHeader::ReferredBy,
646 SipHeader::RejectContact,
647 SipHeader::RelayedCharge,
648 SipHeader::Replaces,
649 SipHeader::ReplyTo,
650 SipHeader::RequestDisposition,
651 SipHeader::Require,
652 SipHeader::ResourcePriority,
653 SipHeader::ResourceShare,
654 SipHeader::ResponseKey,
655 SipHeader::ResponseSource,
656 SipHeader::RestorationInfo,
657 SipHeader::RetryAfter,
658 SipHeader::Route,
659 SipHeader::Rseq,
660 SipHeader::SecurityClient,
661 SipHeader::SecurityServer,
662 SipHeader::SecurityVerify,
663 SipHeader::Server,
664 SipHeader::ServiceInteractInfo,
665 SipHeader::ServiceRoute,
666 SipHeader::SessionExpires,
667 SipHeader::SessionId,
668 SipHeader::SipEtag,
669 SipHeader::SipIfMatch,
670 SipHeader::Subject,
671 SipHeader::SubscriptionState,
672 SipHeader::Supported,
673 SipHeader::SuppressIfMatch,
674 SipHeader::TargetDialog,
675 SipHeader::Timestamp,
676 SipHeader::To,
677 SipHeader::TriggerConsent,
678 SipHeader::Unsupported,
679 SipHeader::UserAgent,
680 SipHeader::UserToUser,
681 SipHeader::Via,
682 SipHeader::Warning,
683 SipHeader::WwwAuthenticate,
684 ];
685 for v in variants {
686 let wire = v.to_string();
687 let parsed: SipHeader = wire
688 .parse()
689 .unwrap();
690 assert_eq!(parsed, v, "round-trip failed for {wire}");
691 }
692 }
693
694 fn headers_with(pairs: &[(&str, &str)]) -> HashMap<String, String> {
695 pairs
696 .iter()
697 .map(|(k, v)| (k.to_string(), v.to_string()))
698 .collect()
699 }
700
701 #[test]
702 fn sip_header_by_enum() {
703 let h = headers_with(&[("Call-Info", "<urn:x>;purpose=icon")]);
704 assert_eq!(
705 h.sip_header(SipHeader::CallInfo),
706 Some("<urn:x>;purpose=icon")
707 );
708 }
709
710 #[test]
711 fn call_info_raw_lookup() {
712 let h = headers_with(&[(
713 "Call-Info",
714 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
715 )]);
716 assert_eq!(
717 h.call_info_raw(),
718 Some("<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId")
719 );
720 }
721
722 #[test]
723 fn call_info_typed() {
724 let h = headers_with(&[(
725 "Call-Info",
726 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
727 )]);
728 let ci = h
729 .call_info()
730 .unwrap()
731 .unwrap();
732 assert_eq!(ci.len(), 1);
733 assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
734 }
735
736 #[test]
737 fn call_info_absent() {
738 let h = headers_with(&[]);
739 assert_eq!(
740 h.call_info()
741 .unwrap(),
742 None
743 );
744 }
745
746 #[test]
747 fn p_asserted_identity_raw_lookup() {
748 let h = headers_with(&[(
749 "P-Asserted-Identity",
750 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
751 )]);
752 assert_eq!(
753 h.p_asserted_identity_raw(),
754 Some(r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#)
755 );
756 }
757
758 #[test]
759 fn p_asserted_identity_typed() {
760 let h = headers_with(&[(
761 "P-Asserted-Identity",
762 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
763 )]);
764 let pai = h
765 .p_asserted_identity()
766 .unwrap()
767 .unwrap();
768 assert_eq!(pai.display_name(), Some("EXAMPLE CO"));
769 }
770
771 #[test]
772 fn p_asserted_identity_absent() {
773 let h = headers_with(&[]);
774 assert_eq!(
775 h.p_asserted_identity()
776 .unwrap(),
777 None
778 );
779 }
780
781 #[test]
782 fn history_info_raw_lookup() {
783 let h = headers_with(&[(
784 "History-Info",
785 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
786 )]);
787 assert!(h
788 .history_info_raw()
789 .unwrap()
790 .contains("esrp.example.com"));
791 }
792
793 #[test]
794 fn history_info_typed() {
795 let h = headers_with(&[(
796 "History-Info",
797 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
798 )]);
799 let hi = h
800 .history_info()
801 .unwrap()
802 .unwrap();
803 assert_eq!(hi.len(), 2);
804 assert_eq!(hi.entries()[0].index(), Some("1"));
805 assert_eq!(hi.entries()[1].index(), Some("1.1"));
806 }
807
808 #[test]
809 fn history_info_absent() {
810 let h = headers_with(&[]);
811 assert_eq!(
812 h.history_info()
813 .unwrap(),
814 None
815 );
816 }
817
818 #[test]
819 fn extract_from_sip_message() {
820 let msg = concat!(
821 "INVITE sip:bob@host SIP/2.0\r\n",
822 "Call-Info: <urn:emergency:uid:callid:abc>;purpose=emergency-CallId\r\n",
823 "History-Info: <sip:esrp@example.com>;index=1\r\n",
824 "P-Asserted-Identity: \"Corp\" <sip:+15551234567@198.51.100.1>\r\n",
825 "\r\n",
826 );
827 assert_eq!(
828 SipHeader::CallInfo.extract_from(msg),
829 Some("<urn:emergency:uid:callid:abc>;purpose=emergency-CallId".into())
830 );
831 assert_eq!(
832 SipHeader::HistoryInfo.extract_from(msg),
833 Some("<sip:esrp@example.com>;index=1".into())
834 );
835 assert_eq!(
836 SipHeader::PAssertedIdentity.extract_from(msg),
837 Some("\"Corp\" <sip:+15551234567@198.51.100.1>".into())
838 );
839 }
840
841 #[test]
842 fn extract_from_missing() {
843 let msg = concat!(
844 "INVITE sip:bob@host SIP/2.0\r\n",
845 "From: Alice <sip:alice@host>\r\n",
846 "\r\n",
847 );
848 assert_eq!(SipHeader::CallInfo.extract_from(msg), None);
849 assert_eq!(SipHeader::PAssertedIdentity.extract_from(msg), None);
850 }
851
852 #[test]
853 fn missing_headers_return_none() {
854 let h = headers_with(&[]);
855 assert_eq!(h.call_info_raw(), None);
856 assert_eq!(
857 h.call_info()
858 .unwrap(),
859 None
860 );
861 assert_eq!(h.history_info_raw(), None);
862 assert_eq!(
863 h.history_info()
864 .unwrap(),
865 None
866 );
867 assert_eq!(h.p_asserted_identity_raw(), None);
868 assert_eq!(
869 h.p_asserted_identity()
870 .unwrap(),
871 None
872 );
873 }
874}
875
876#[cfg(test)]
877mod compact_form_tests {
878 use super::*;
879
880 #[test]
881 fn from_compact_known() {
882 assert_eq!(SipHeader::from_compact(b'f'), Some(SipHeader::From));
883 assert_eq!(SipHeader::from_compact(b'F'), Some(SipHeader::From));
884 assert_eq!(SipHeader::from_compact(b'v'), Some(SipHeader::Via));
885 assert_eq!(SipHeader::from_compact(b'i'), Some(SipHeader::CallId));
886 assert_eq!(SipHeader::from_compact(b'm'), Some(SipHeader::Contact));
887 assert_eq!(SipHeader::from_compact(b't'), Some(SipHeader::To));
888 assert_eq!(SipHeader::from_compact(b'c'), Some(SipHeader::ContentType));
889 }
890
891 #[test]
892 fn from_compact_unknown() {
893 assert_eq!(SipHeader::from_compact(b'z'), None);
894 assert_eq!(SipHeader::from_compact(b'g'), None);
895 }
896
897 #[test]
898 fn compact_form_roundtrip() {
899 assert_eq!(SipHeader::From.compact_form(), Some('f'));
900 assert_eq!(SipHeader::Via.compact_form(), Some('v'));
901 assert_eq!(SipHeader::CallId.compact_form(), Some('i'));
902 assert_eq!(SipHeader::Contact.compact_form(), Some('m'));
903 }
904
905 #[test]
906 fn compact_form_absent() {
907 assert_eq!(SipHeader::HistoryInfo.compact_form(), None);
908 assert_eq!(SipHeader::PAssertedIdentity.compact_form(), None);
909 }
910
911 #[test]
912 fn parse_name_compact() {
913 assert_eq!(SipHeader::parse_name("f"), Ok(SipHeader::From));
914 assert_eq!(SipHeader::parse_name("F"), Ok(SipHeader::From));
915 assert_eq!(SipHeader::parse_name("v"), Ok(SipHeader::Via));
916 }
917
918 #[test]
919 fn parse_name_full() {
920 assert_eq!(SipHeader::parse_name("From"), Ok(SipHeader::From));
921 assert_eq!(SipHeader::parse_name("Via"), Ok(SipHeader::Via));
922 }
923
924 #[test]
925 fn parse_name_unknown() {
926 assert!(SipHeader::parse_name("X-Custom").is_err());
927 }
928
929 #[test]
930 fn all_compact_forms_resolve() {
931 let expected = [
932 ('a', SipHeader::AcceptContact),
933 ('b', SipHeader::ReferredBy),
934 ('c', SipHeader::ContentType),
935 ('d', SipHeader::RequestDisposition),
936 ('e', SipHeader::ContentEncoding),
937 ('f', SipHeader::From),
938 ('i', SipHeader::CallId),
939 ('j', SipHeader::RejectContact),
940 ('k', SipHeader::Supported),
941 ('l', SipHeader::ContentLength),
942 ('m', SipHeader::Contact),
943 ('n', SipHeader::IdentityInfo),
944 ('o', SipHeader::Event),
945 ('r', SipHeader::ReferTo),
946 ('s', SipHeader::Subject),
947 ('t', SipHeader::To),
948 ('u', SipHeader::AllowEvents),
949 ('v', SipHeader::Via),
950 ('x', SipHeader::SessionExpires),
951 ('y', SipHeader::Identity),
952 ];
953 for (ch, header) in expected {
954 assert_eq!(
955 SipHeader::from_compact(ch as u8),
956 Some(header),
957 "compact form '{ch}' failed"
958 );
959 assert_eq!(
960 header.compact_form(),
961 Some(ch),
962 "compact_form() for {} failed",
963 header
964 );
965 }
966 }
967}
968
969#[cfg(test)]
970mod multi_valued_tests {
971 use super::*;
972
973 #[test]
974 fn rfc3261_multi_valued_headers() {
975 assert!(SipHeader::Via.is_multi_valued());
976 assert!(SipHeader::Route.is_multi_valued());
977 assert!(SipHeader::RecordRoute.is_multi_valued());
978 assert!(SipHeader::Contact.is_multi_valued());
979 assert!(SipHeader::Allow.is_multi_valued());
980 assert!(SipHeader::Supported.is_multi_valued());
981 assert!(SipHeader::Require.is_multi_valued());
982 assert!(SipHeader::ProxyRequire.is_multi_valued());
983 assert!(SipHeader::Unsupported.is_multi_valued());
984 assert!(SipHeader::Authorization.is_multi_valued());
985 assert!(SipHeader::ProxyAuthorization.is_multi_valued());
986 assert!(SipHeader::WwwAuthenticate.is_multi_valued());
987 assert!(SipHeader::ProxyAuthenticate.is_multi_valued());
988 assert!(SipHeader::Warning.is_multi_valued());
989 assert!(SipHeader::ErrorInfo.is_multi_valued());
990 assert!(SipHeader::CallInfo.is_multi_valued());
991 assert!(SipHeader::AlertInfo.is_multi_valued());
992 assert!(SipHeader::Accept.is_multi_valued());
993 assert!(SipHeader::AcceptEncoding.is_multi_valued());
994 assert!(SipHeader::AcceptLanguage.is_multi_valued());
995 assert!(SipHeader::ContentEncoding.is_multi_valued());
996 assert!(SipHeader::ContentLanguage.is_multi_valued());
997 assert!(SipHeader::InReplyTo.is_multi_valued());
998 }
999
1000 #[test]
1001 fn extension_multi_valued_headers() {
1002 assert!(SipHeader::PAssertedIdentity.is_multi_valued());
1003 assert!(SipHeader::PPreferredIdentity.is_multi_valued());
1004 assert!(SipHeader::AllowEvents.is_multi_valued());
1005 assert!(SipHeader::SecurityClient.is_multi_valued());
1006 assert!(SipHeader::SecurityServer.is_multi_valued());
1007 assert!(SipHeader::SecurityVerify.is_multi_valued());
1008 assert!(SipHeader::Path.is_multi_valued());
1009 assert!(SipHeader::ServiceRoute.is_multi_valued());
1010 assert!(SipHeader::HistoryInfo.is_multi_valued());
1011 }
1012
1013 #[test]
1014 fn single_valued_headers() {
1015 assert!(!SipHeader::From.is_multi_valued());
1016 assert!(!SipHeader::To.is_multi_valued());
1017 assert!(!SipHeader::CallId.is_multi_valued());
1018 assert!(!SipHeader::Cseq.is_multi_valued());
1019 assert!(!SipHeader::MaxForwards.is_multi_valued());
1020 assert!(!SipHeader::ContentType.is_multi_valued());
1021 assert!(!SipHeader::ContentLength.is_multi_valued());
1022 assert!(!SipHeader::Expires.is_multi_valued());
1023 assert!(!SipHeader::Date.is_multi_valued());
1024 assert!(!SipHeader::Subject.is_multi_valued());
1025 assert!(!SipHeader::ReplyTo.is_multi_valued());
1026 assert!(!SipHeader::Server.is_multi_valued());
1027 assert!(!SipHeader::UserAgent.is_multi_valued());
1028 }
1029}
1030
1031#[cfg(test)]
1032mod special_case_tests {
1033 use super::*;
1034
1035 #[test]
1036 fn cseq_variants() {
1037 assert_eq!("CSeq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1038 assert_eq!("cseq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1039 assert_eq!("CSEQ".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1040 assert_eq!(SipHeader::Cseq.to_string(), "CSeq");
1041 }
1042
1043 #[test]
1044 fn www_authenticate_variants() {
1045 assert_eq!(
1046 "WWW-Authenticate".parse::<SipHeader>(),
1047 Ok(SipHeader::WwwAuthenticate)
1048 );
1049 assert_eq!(
1050 "www-authenticate".parse::<SipHeader>(),
1051 Ok(SipHeader::WwwAuthenticate)
1052 );
1053 assert_eq!(SipHeader::WwwAuthenticate.to_string(), "WWW-Authenticate");
1054 }
1055
1056 #[test]
1057 fn rack_rseq_variants() {
1058 assert_eq!("RAck".parse::<SipHeader>(), Ok(SipHeader::Rack));
1059 assert_eq!("rack".parse::<SipHeader>(), Ok(SipHeader::Rack));
1060 assert_eq!(SipHeader::Rack.to_string(), "RAck");
1061
1062 assert_eq!("RSeq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1063 assert_eq!("rseq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1064 assert_eq!(SipHeader::Rseq.to_string(), "RSeq");
1065 }
1066
1067 #[test]
1068 fn user_to_user_variants() {
1069 assert_eq!(
1070 "User-to-User".parse::<SipHeader>(),
1071 Ok(SipHeader::UserToUser)
1072 );
1073 assert_eq!(
1074 "user-to-user".parse::<SipHeader>(),
1075 Ok(SipHeader::UserToUser)
1076 );
1077 assert_eq!(SipHeader::UserToUser.to_string(), "User-to-User");
1078 }
1079
1080 #[test]
1081 fn p_header_variants() {
1082 assert_eq!(
1083 "P-DCS-Trace-Party-ID".parse::<SipHeader>(),
1084 Ok(SipHeader::PDcsTracePartyId)
1085 );
1086 assert_eq!(
1087 "p-dcs-trace-party-id".parse::<SipHeader>(),
1088 Ok(SipHeader::PDcsTracePartyId)
1089 );
1090 assert_eq!(
1091 SipHeader::PDcsTracePartyId.to_string(),
1092 "P-DCS-Trace-Party-ID"
1093 );
1094 }
1095}