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 #[cfg(feature = "draft")]
302 Diversion => "Diversion",
303 #[cfg(feature = "draft")]
305 RemotePartyId => "Remote-Party-ID",
306 }
307}
308
309const COMPACT_FORMS: &[(u8, SipHeader)] = &[
314 (b'a', SipHeader::AcceptContact),
315 (b'b', SipHeader::ReferredBy),
316 (b'c', SipHeader::ContentType),
317 (b'd', SipHeader::RequestDisposition),
318 (b'e', SipHeader::ContentEncoding),
319 (b'f', SipHeader::From),
320 (b'i', SipHeader::CallId),
321 (b'j', SipHeader::RejectContact),
322 (b'k', SipHeader::Supported),
323 (b'l', SipHeader::ContentLength),
324 (b'm', SipHeader::Contact),
325 (b'n', SipHeader::IdentityInfo),
326 (b'o', SipHeader::Event),
327 (b'r', SipHeader::ReferTo),
328 (b's', SipHeader::Subject),
329 (b't', SipHeader::To),
330 (b'u', SipHeader::AllowEvents),
331 (b'v', SipHeader::Via),
332 (b'x', SipHeader::SessionExpires),
333 (b'y', SipHeader::Identity),
334];
335
336impl SipHeader {
337 pub fn from_compact(ch: u8) -> Option<Self> {
341 let lower = ch.to_ascii_lowercase();
342 COMPACT_FORMS
343 .iter()
344 .find(|(c, _)| *c == lower)
345 .map(|(_, h)| *h)
346 }
347
348 pub fn compact_form(&self) -> Option<char> {
350 COMPACT_FORMS
351 .iter()
352 .find(|(_, h)| h == self)
353 .map(|(c, _)| *c as char)
354 }
355
356 pub fn is_multi_valued(&self) -> bool {
361 if matches!(
362 self,
363 Self::Via
365 | Self::Route
366 | Self::RecordRoute
367 | Self::Contact
368 | Self::Allow
369 | Self::Supported
370 | Self::Require
371 | Self::ProxyRequire
372 | Self::Unsupported
373 | Self::Authorization
374 | Self::ProxyAuthorization
375 | Self::WwwAuthenticate
376 | Self::ProxyAuthenticate
377 | Self::Warning
378 | Self::ErrorInfo
379 | Self::CallInfo
380 | Self::AlertInfo
381 | Self::Accept
382 | Self::AcceptEncoding
383 | Self::AcceptLanguage
384 | Self::ContentEncoding
385 | Self::ContentLanguage
386 | Self::InReplyTo
387 | Self::PAssertedIdentity
389 | Self::PPreferredIdentity
390 | Self::AllowEvents
392 | Self::SecurityClient
394 | Self::SecurityServer
395 | Self::SecurityVerify
396 | Self::Path
398 | Self::ServiceRoute
400 | Self::HistoryInfo
402 ) {
403 return true;
404 }
405
406 #[cfg(feature = "draft")]
407 if matches!(
408 self,
409 Self::Diversion
411 | Self::RemotePartyId
413 ) {
414 return true;
415 }
416
417 false
418 }
419
420 pub fn parse_name(name: &str) -> Result<Self, ParseSipHeaderError> {
425 if name.len() == 1 {
426 if let Some(h) = Self::from_compact(name.as_bytes()[0]) {
427 return Ok(h);
428 }
429 }
430 name.parse()
431 }
432}
433
434pub trait SipHeaderLookup {
460 fn sip_header_str(&self, name: &str) -> Option<&str>;
462
463 fn sip_header(&self, name: SipHeader) -> Option<&str> {
465 self.sip_header_str(name.as_str())
466 }
467
468 fn call_info_raw(&self) -> Option<&str> {
470 self.sip_header(SipHeader::CallInfo)
471 }
472
473 fn call_info(&self) -> Result<Option<SipCallInfo>, SipCallInfoError> {
477 match self.call_info_raw() {
478 Some(s) => SipCallInfo::parse(s).map(Some),
479 None => Ok(None),
480 }
481 }
482
483 fn history_info_raw(&self) -> Option<&str> {
485 self.sip_header(SipHeader::HistoryInfo)
486 }
487
488 fn history_info(&self) -> Result<Option<HistoryInfo>, HistoryInfoError> {
492 match self.history_info_raw() {
493 Some(s) => HistoryInfo::parse(s).map(Some),
494 None => Ok(None),
495 }
496 }
497
498 fn p_asserted_identity_raw(&self) -> Option<&str> {
500 self.sip_header(SipHeader::PAssertedIdentity)
501 }
502
503 fn p_asserted_identity(&self) -> Result<Option<SipHeaderAddr>, ParseSipHeaderAddrError> {
507 match self.p_asserted_identity_raw() {
508 Some(s) => s
509 .parse::<SipHeaderAddr>()
510 .map(Some),
511 None => Ok(None),
512 }
513 }
514}
515
516impl SipHeaderLookup for std::collections::HashMap<String, String> {
517 fn sip_header_str(&self, name: &str) -> Option<&str> {
518 self.get(name)
519 .map(|s| s.as_str())
520 }
521}
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526 use std::collections::HashMap;
527
528 #[test]
529 fn display_round_trip() {
530 assert_eq!(SipHeader::CallInfo.to_string(), "Call-Info");
531 assert_eq!(SipHeader::HistoryInfo.to_string(), "History-Info");
532 assert_eq!(
533 SipHeader::PAssertedIdentity.to_string(),
534 "P-Asserted-Identity"
535 );
536 }
537
538 #[test]
539 fn as_ref_str() {
540 let h: &str = SipHeader::CallInfo.as_ref();
541 assert_eq!(h, "Call-Info");
542 }
543
544 #[test]
545 fn from_str_case_insensitive() {
546 assert_eq!("call-info".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
547 assert_eq!("CALL-INFO".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
548 assert_eq!(
549 "history-info".parse::<SipHeader>(),
550 Ok(SipHeader::HistoryInfo)
551 );
552 assert_eq!(
553 "p-asserted-identity".parse::<SipHeader>(),
554 Ok(SipHeader::PAssertedIdentity)
555 );
556 assert_eq!(
557 "P-ASSERTED-IDENTITY".parse::<SipHeader>(),
558 Ok(SipHeader::PAssertedIdentity)
559 );
560 }
561
562 #[test]
563 fn from_str_unknown() {
564 assert!("X-Custom"
565 .parse::<SipHeader>()
566 .is_err());
567 }
568
569 #[test]
570 fn from_str_round_trip_all() {
571 let variants = [
572 SipHeader::Accept,
573 SipHeader::AcceptContact,
574 SipHeader::AcceptEncoding,
575 SipHeader::AcceptLanguage,
576 SipHeader::AcceptResourcePriority,
577 SipHeader::AdditionalIdentity,
578 SipHeader::AlertInfo,
579 SipHeader::AlertmsgError,
580 SipHeader::Allow,
581 SipHeader::AllowEvents,
582 SipHeader::AnswerMode,
583 SipHeader::AttestationInfo,
584 SipHeader::AuthenticationInfo,
585 SipHeader::Authorization,
586 SipHeader::CallId,
587 SipHeader::CallInfo,
588 SipHeader::CellularNetworkInfo,
589 SipHeader::Contact,
590 SipHeader::ContentDisposition,
591 SipHeader::ContentEncoding,
592 SipHeader::ContentId,
593 SipHeader::ContentLanguage,
594 SipHeader::ContentLength,
595 SipHeader::ContentType,
596 SipHeader::Cseq,
597 SipHeader::Date,
598 SipHeader::DcInfo,
599 SipHeader::Encryption,
600 SipHeader::ErrorInfo,
601 SipHeader::Event,
602 SipHeader::Expires,
603 SipHeader::FeatureCaps,
604 SipHeader::FlowTimer,
605 SipHeader::From,
606 SipHeader::Geolocation,
607 SipHeader::GeolocationError,
608 SipHeader::GeolocationRouting,
609 SipHeader::Hide,
610 SipHeader::HistoryInfo,
611 SipHeader::Identity,
612 SipHeader::IdentityInfo,
613 SipHeader::InfoPackage,
614 SipHeader::InReplyTo,
615 SipHeader::Join,
616 SipHeader::MaxBreadth,
617 SipHeader::MaxForwards,
618 SipHeader::MimeVersion,
619 SipHeader::MinExpires,
620 SipHeader::MinSe,
621 SipHeader::Organization,
622 SipHeader::OriginationId,
623 SipHeader::PAccessNetworkInfo,
624 SipHeader::PAnswerState,
625 SipHeader::PAssertedIdentity,
626 SipHeader::PAssertedService,
627 SipHeader::PAssociatedUri,
628 SipHeader::PCalledPartyId,
629 SipHeader::PChargeInfo,
630 SipHeader::PChargingFunctionAddresses,
631 SipHeader::PChargingVector,
632 SipHeader::PDcsTracePartyId,
633 SipHeader::PDcsOsps,
634 SipHeader::PDcsBillingInfo,
635 SipHeader::PDcsLaes,
636 SipHeader::PDcsRedirect,
637 SipHeader::PEarlyMedia,
638 SipHeader::PMediaAuthorization,
639 SipHeader::PPreferredIdentity,
640 SipHeader::PPreferredService,
641 SipHeader::PPrivateNetworkIndication,
642 SipHeader::PProfileKey,
643 SipHeader::PRefusedUriList,
644 SipHeader::PServedUser,
645 SipHeader::PUserDatabase,
646 SipHeader::PVisitedNetworkId,
647 SipHeader::Path,
648 SipHeader::PermissionMissing,
649 SipHeader::PolicyContact,
650 SipHeader::PolicyId,
651 SipHeader::Priority,
652 SipHeader::PriorityShare,
653 SipHeader::PriorityVerstat,
654 SipHeader::PrivAnswerMode,
655 SipHeader::Privacy,
656 SipHeader::ProxyAuthenticate,
657 SipHeader::ProxyAuthorization,
658 SipHeader::ProxyRequire,
659 SipHeader::Rack,
660 SipHeader::Reason,
661 SipHeader::ReasonPhrase,
662 SipHeader::RecordRoute,
663 SipHeader::RecvInfo,
664 SipHeader::ReferEventsAt,
665 SipHeader::ReferSub,
666 SipHeader::ReferTo,
667 SipHeader::ReferredBy,
668 SipHeader::RejectContact,
669 SipHeader::RelayedCharge,
670 SipHeader::Replaces,
671 SipHeader::ReplyTo,
672 SipHeader::RequestDisposition,
673 SipHeader::Require,
674 SipHeader::ResourcePriority,
675 SipHeader::ResourceShare,
676 SipHeader::ResponseKey,
677 SipHeader::ResponseSource,
678 SipHeader::RestorationInfo,
679 SipHeader::RetryAfter,
680 SipHeader::Route,
681 SipHeader::Rseq,
682 SipHeader::SecurityClient,
683 SipHeader::SecurityServer,
684 SipHeader::SecurityVerify,
685 SipHeader::Server,
686 SipHeader::ServiceInteractInfo,
687 SipHeader::ServiceRoute,
688 SipHeader::SessionExpires,
689 SipHeader::SessionId,
690 SipHeader::SipEtag,
691 SipHeader::SipIfMatch,
692 SipHeader::Subject,
693 SipHeader::SubscriptionState,
694 SipHeader::Supported,
695 SipHeader::SuppressIfMatch,
696 SipHeader::TargetDialog,
697 SipHeader::Timestamp,
698 SipHeader::To,
699 SipHeader::TriggerConsent,
700 SipHeader::Unsupported,
701 SipHeader::UserAgent,
702 SipHeader::UserToUser,
703 SipHeader::Via,
704 SipHeader::Warning,
705 SipHeader::WwwAuthenticate,
706 ];
707 for v in variants {
708 let wire = v.to_string();
709 let parsed: SipHeader = wire
710 .parse()
711 .unwrap();
712 assert_eq!(parsed, v, "round-trip failed for {wire}");
713 }
714 }
715
716 fn headers_with(pairs: &[(&str, &str)]) -> HashMap<String, String> {
717 pairs
718 .iter()
719 .map(|(k, v)| (k.to_string(), v.to_string()))
720 .collect()
721 }
722
723 #[test]
724 fn sip_header_by_enum() {
725 let h = headers_with(&[("Call-Info", "<urn:x>;purpose=icon")]);
726 assert_eq!(
727 h.sip_header(SipHeader::CallInfo),
728 Some("<urn:x>;purpose=icon")
729 );
730 }
731
732 #[test]
733 fn call_info_raw_lookup() {
734 let h = headers_with(&[(
735 "Call-Info",
736 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
737 )]);
738 assert_eq!(
739 h.call_info_raw(),
740 Some("<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId")
741 );
742 }
743
744 #[test]
745 fn call_info_typed() {
746 let h = headers_with(&[(
747 "Call-Info",
748 "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
749 )]);
750 let ci = h
751 .call_info()
752 .unwrap()
753 .unwrap();
754 assert_eq!(ci.len(), 1);
755 assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
756 }
757
758 #[test]
759 fn call_info_absent() {
760 let h = headers_with(&[]);
761 assert_eq!(
762 h.call_info()
763 .unwrap(),
764 None
765 );
766 }
767
768 #[test]
769 fn p_asserted_identity_raw_lookup() {
770 let h = headers_with(&[(
771 "P-Asserted-Identity",
772 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
773 )]);
774 assert_eq!(
775 h.p_asserted_identity_raw(),
776 Some(r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#)
777 );
778 }
779
780 #[test]
781 fn p_asserted_identity_typed() {
782 let h = headers_with(&[(
783 "P-Asserted-Identity",
784 r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
785 )]);
786 let pai = h
787 .p_asserted_identity()
788 .unwrap()
789 .unwrap();
790 assert_eq!(pai.display_name(), Some("EXAMPLE CO"));
791 }
792
793 #[test]
794 fn p_asserted_identity_absent() {
795 let h = headers_with(&[]);
796 assert_eq!(
797 h.p_asserted_identity()
798 .unwrap(),
799 None
800 );
801 }
802
803 #[test]
804 fn history_info_raw_lookup() {
805 let h = headers_with(&[(
806 "History-Info",
807 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
808 )]);
809 assert!(h
810 .history_info_raw()
811 .unwrap()
812 .contains("esrp.example.com"));
813 }
814
815 #[test]
816 fn history_info_typed() {
817 let h = headers_with(&[(
818 "History-Info",
819 "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
820 )]);
821 let hi = h
822 .history_info()
823 .unwrap()
824 .unwrap();
825 assert_eq!(hi.len(), 2);
826 assert_eq!(hi.entries()[0].index(), Some("1"));
827 assert_eq!(hi.entries()[1].index(), Some("1.1"));
828 }
829
830 #[test]
831 fn history_info_absent() {
832 let h = headers_with(&[]);
833 assert_eq!(
834 h.history_info()
835 .unwrap(),
836 None
837 );
838 }
839
840 #[test]
841 fn extract_from_sip_message() {
842 let msg = concat!(
843 "INVITE sip:bob@host SIP/2.0\r\n",
844 "Call-Info: <urn:emergency:uid:callid:abc>;purpose=emergency-CallId\r\n",
845 "History-Info: <sip:esrp@example.com>;index=1\r\n",
846 "P-Asserted-Identity: \"Corp\" <sip:+15551234567@198.51.100.1>\r\n",
847 "\r\n",
848 );
849 assert_eq!(
850 SipHeader::CallInfo.extract_from(msg),
851 Some("<urn:emergency:uid:callid:abc>;purpose=emergency-CallId".into())
852 );
853 assert_eq!(
854 SipHeader::HistoryInfo.extract_from(msg),
855 Some("<sip:esrp@example.com>;index=1".into())
856 );
857 assert_eq!(
858 SipHeader::PAssertedIdentity.extract_from(msg),
859 Some("\"Corp\" <sip:+15551234567@198.51.100.1>".into())
860 );
861 }
862
863 #[test]
864 fn extract_from_missing() {
865 let msg = concat!(
866 "INVITE sip:bob@host SIP/2.0\r\n",
867 "From: Alice <sip:alice@host>\r\n",
868 "\r\n",
869 );
870 assert_eq!(SipHeader::CallInfo.extract_from(msg), None);
871 assert_eq!(SipHeader::PAssertedIdentity.extract_from(msg), None);
872 }
873
874 #[test]
875 fn missing_headers_return_none() {
876 let h = headers_with(&[]);
877 assert_eq!(h.call_info_raw(), None);
878 assert_eq!(
879 h.call_info()
880 .unwrap(),
881 None
882 );
883 assert_eq!(h.history_info_raw(), None);
884 assert_eq!(
885 h.history_info()
886 .unwrap(),
887 None
888 );
889 assert_eq!(h.p_asserted_identity_raw(), None);
890 assert_eq!(
891 h.p_asserted_identity()
892 .unwrap(),
893 None
894 );
895 }
896}
897
898#[cfg(test)]
899mod compact_form_tests {
900 use super::*;
901
902 #[test]
903 fn from_compact_known() {
904 assert_eq!(SipHeader::from_compact(b'f'), Some(SipHeader::From));
905 assert_eq!(SipHeader::from_compact(b'F'), Some(SipHeader::From));
906 assert_eq!(SipHeader::from_compact(b'v'), Some(SipHeader::Via));
907 assert_eq!(SipHeader::from_compact(b'i'), Some(SipHeader::CallId));
908 assert_eq!(SipHeader::from_compact(b'm'), Some(SipHeader::Contact));
909 assert_eq!(SipHeader::from_compact(b't'), Some(SipHeader::To));
910 assert_eq!(SipHeader::from_compact(b'c'), Some(SipHeader::ContentType));
911 }
912
913 #[test]
914 fn from_compact_unknown() {
915 assert_eq!(SipHeader::from_compact(b'z'), None);
916 assert_eq!(SipHeader::from_compact(b'g'), None);
917 }
918
919 #[test]
920 fn compact_form_roundtrip() {
921 assert_eq!(SipHeader::From.compact_form(), Some('f'));
922 assert_eq!(SipHeader::Via.compact_form(), Some('v'));
923 assert_eq!(SipHeader::CallId.compact_form(), Some('i'));
924 assert_eq!(SipHeader::Contact.compact_form(), Some('m'));
925 }
926
927 #[test]
928 fn compact_form_absent() {
929 assert_eq!(SipHeader::HistoryInfo.compact_form(), None);
930 assert_eq!(SipHeader::PAssertedIdentity.compact_form(), None);
931 }
932
933 #[test]
934 fn parse_name_compact() {
935 assert_eq!(SipHeader::parse_name("f"), Ok(SipHeader::From));
936 assert_eq!(SipHeader::parse_name("F"), Ok(SipHeader::From));
937 assert_eq!(SipHeader::parse_name("v"), Ok(SipHeader::Via));
938 }
939
940 #[test]
941 fn parse_name_full() {
942 assert_eq!(SipHeader::parse_name("From"), Ok(SipHeader::From));
943 assert_eq!(SipHeader::parse_name("Via"), Ok(SipHeader::Via));
944 }
945
946 #[test]
947 fn parse_name_unknown() {
948 assert!(SipHeader::parse_name("X-Custom").is_err());
949 }
950
951 #[test]
952 fn all_compact_forms_resolve() {
953 let expected = [
954 ('a', SipHeader::AcceptContact),
955 ('b', SipHeader::ReferredBy),
956 ('c', SipHeader::ContentType),
957 ('d', SipHeader::RequestDisposition),
958 ('e', SipHeader::ContentEncoding),
959 ('f', SipHeader::From),
960 ('i', SipHeader::CallId),
961 ('j', SipHeader::RejectContact),
962 ('k', SipHeader::Supported),
963 ('l', SipHeader::ContentLength),
964 ('m', SipHeader::Contact),
965 ('n', SipHeader::IdentityInfo),
966 ('o', SipHeader::Event),
967 ('r', SipHeader::ReferTo),
968 ('s', SipHeader::Subject),
969 ('t', SipHeader::To),
970 ('u', SipHeader::AllowEvents),
971 ('v', SipHeader::Via),
972 ('x', SipHeader::SessionExpires),
973 ('y', SipHeader::Identity),
974 ];
975 for (ch, header) in expected {
976 assert_eq!(
977 SipHeader::from_compact(ch as u8),
978 Some(header),
979 "compact form '{ch}' failed"
980 );
981 assert_eq!(
982 header.compact_form(),
983 Some(ch),
984 "compact_form() for {} failed",
985 header
986 );
987 }
988 }
989}
990
991#[cfg(test)]
992mod multi_valued_tests {
993 use super::*;
994
995 #[test]
996 fn rfc3261_multi_valued_headers() {
997 assert!(SipHeader::Via.is_multi_valued());
998 assert!(SipHeader::Route.is_multi_valued());
999 assert!(SipHeader::RecordRoute.is_multi_valued());
1000 assert!(SipHeader::Contact.is_multi_valued());
1001 assert!(SipHeader::Allow.is_multi_valued());
1002 assert!(SipHeader::Supported.is_multi_valued());
1003 assert!(SipHeader::Require.is_multi_valued());
1004 assert!(SipHeader::ProxyRequire.is_multi_valued());
1005 assert!(SipHeader::Unsupported.is_multi_valued());
1006 assert!(SipHeader::Authorization.is_multi_valued());
1007 assert!(SipHeader::ProxyAuthorization.is_multi_valued());
1008 assert!(SipHeader::WwwAuthenticate.is_multi_valued());
1009 assert!(SipHeader::ProxyAuthenticate.is_multi_valued());
1010 assert!(SipHeader::Warning.is_multi_valued());
1011 assert!(SipHeader::ErrorInfo.is_multi_valued());
1012 assert!(SipHeader::CallInfo.is_multi_valued());
1013 assert!(SipHeader::AlertInfo.is_multi_valued());
1014 assert!(SipHeader::Accept.is_multi_valued());
1015 assert!(SipHeader::AcceptEncoding.is_multi_valued());
1016 assert!(SipHeader::AcceptLanguage.is_multi_valued());
1017 assert!(SipHeader::ContentEncoding.is_multi_valued());
1018 assert!(SipHeader::ContentLanguage.is_multi_valued());
1019 assert!(SipHeader::InReplyTo.is_multi_valued());
1020 }
1021
1022 #[test]
1023 fn extension_multi_valued_headers() {
1024 assert!(SipHeader::PAssertedIdentity.is_multi_valued());
1025 assert!(SipHeader::PPreferredIdentity.is_multi_valued());
1026 assert!(SipHeader::AllowEvents.is_multi_valued());
1027 assert!(SipHeader::SecurityClient.is_multi_valued());
1028 assert!(SipHeader::SecurityServer.is_multi_valued());
1029 assert!(SipHeader::SecurityVerify.is_multi_valued());
1030 assert!(SipHeader::Path.is_multi_valued());
1031 assert!(SipHeader::ServiceRoute.is_multi_valued());
1032 assert!(SipHeader::HistoryInfo.is_multi_valued());
1033 }
1034
1035 #[test]
1036 fn single_valued_headers() {
1037 assert!(!SipHeader::From.is_multi_valued());
1038 assert!(!SipHeader::To.is_multi_valued());
1039 assert!(!SipHeader::CallId.is_multi_valued());
1040 assert!(!SipHeader::Cseq.is_multi_valued());
1041 assert!(!SipHeader::MaxForwards.is_multi_valued());
1042 assert!(!SipHeader::ContentType.is_multi_valued());
1043 assert!(!SipHeader::ContentLength.is_multi_valued());
1044 assert!(!SipHeader::Expires.is_multi_valued());
1045 assert!(!SipHeader::Date.is_multi_valued());
1046 assert!(!SipHeader::Subject.is_multi_valued());
1047 assert!(!SipHeader::ReplyTo.is_multi_valued());
1048 assert!(!SipHeader::Server.is_multi_valued());
1049 assert!(!SipHeader::UserAgent.is_multi_valued());
1050 }
1051
1052 #[test]
1053 #[cfg(feature = "draft")]
1054 fn draft_multi_valued_headers() {
1055 assert!(SipHeader::Diversion.is_multi_valued());
1056 assert!(SipHeader::RemotePartyId.is_multi_valued());
1057 }
1058
1059 #[test]
1060 #[cfg(feature = "draft")]
1061 fn draft_parse_roundtrip() {
1062 let d: SipHeader = "Diversion"
1063 .parse()
1064 .unwrap();
1065 assert_eq!(d, SipHeader::Diversion);
1066 assert_eq!(d.to_string(), "Diversion");
1067
1068 let r: SipHeader = "remote-party-id"
1069 .parse()
1070 .unwrap();
1071 assert_eq!(r, SipHeader::RemotePartyId);
1072 assert_eq!(r.to_string(), "Remote-Party-ID");
1073 }
1074}
1075
1076#[cfg(test)]
1077mod special_case_tests {
1078 use super::*;
1079
1080 #[test]
1081 fn cseq_variants() {
1082 assert_eq!("CSeq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1083 assert_eq!("cseq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1084 assert_eq!("CSEQ".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1085 assert_eq!(SipHeader::Cseq.to_string(), "CSeq");
1086 }
1087
1088 #[test]
1089 fn www_authenticate_variants() {
1090 assert_eq!(
1091 "WWW-Authenticate".parse::<SipHeader>(),
1092 Ok(SipHeader::WwwAuthenticate)
1093 );
1094 assert_eq!(
1095 "www-authenticate".parse::<SipHeader>(),
1096 Ok(SipHeader::WwwAuthenticate)
1097 );
1098 assert_eq!(SipHeader::WwwAuthenticate.to_string(), "WWW-Authenticate");
1099 }
1100
1101 #[test]
1102 fn rack_rseq_variants() {
1103 assert_eq!("RAck".parse::<SipHeader>(), Ok(SipHeader::Rack));
1104 assert_eq!("rack".parse::<SipHeader>(), Ok(SipHeader::Rack));
1105 assert_eq!(SipHeader::Rack.to_string(), "RAck");
1106
1107 assert_eq!("RSeq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1108 assert_eq!("rseq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1109 assert_eq!(SipHeader::Rseq.to_string(), "RSeq");
1110 }
1111
1112 #[test]
1113 fn user_to_user_variants() {
1114 assert_eq!(
1115 "User-to-User".parse::<SipHeader>(),
1116 Ok(SipHeader::UserToUser)
1117 );
1118 assert_eq!(
1119 "user-to-user".parse::<SipHeader>(),
1120 Ok(SipHeader::UserToUser)
1121 );
1122 assert_eq!(SipHeader::UserToUser.to_string(), "User-to-User");
1123 }
1124
1125 #[test]
1126 fn p_header_variants() {
1127 assert_eq!(
1128 "P-DCS-Trace-Party-ID".parse::<SipHeader>(),
1129 Ok(SipHeader::PDcsTracePartyId)
1130 );
1131 assert_eq!(
1132 "p-dcs-trace-party-id".parse::<SipHeader>(),
1133 Ok(SipHeader::PDcsTracePartyId)
1134 );
1135 assert_eq!(
1136 SipHeader::PDcsTracePartyId.to_string(),
1137 "P-DCS-Trace-Party-ID"
1138 );
1139 }
1140}