Skip to main content

sip_header/
header.rs

1//! Standard SIP header names and typed lookup trait (RFC 3261 and extensions).
2//!
3//! Protocol-agnostic catalog of SIP header names with canonical wire casing,
4//! plus a [`SipHeaderLookup`] trait providing typed convenience accessors for
5//! any key-value store that can look up headers by name.
6
7use 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    /// Standard SIP header names with canonical wire casing.
23    ///
24    /// Each variant maps to the header's canonical form as defined in the
25    /// relevant RFC. `FromStr` is case-insensitive; `Display` always emits
26    /// the canonical form.
27    pub enum SipHeader {
28        /// `Accept` (RFC 3261).
29        Accept => "Accept",
30        /// `Accept-Contact`.
31        AcceptContact => "Accept-Contact",
32        /// `Accept-Encoding` (RFC 3261).
33        AcceptEncoding => "Accept-Encoding",
34        /// `Accept-Language` (RFC 3261).
35        AcceptLanguage => "Accept-Language",
36        /// `Accept-Resource-Priority`.
37        AcceptResourcePriority => "Accept-Resource-Priority",
38        /// `Additional-Identity`.
39        AdditionalIdentity => "Additional-Identity",
40        /// `Alert-Info` (RFC 3261).
41        AlertInfo => "Alert-Info",
42        /// `AlertMsg-Error`.
43        AlertmsgError => "AlertMsg-Error",
44        /// `Allow` (RFC 3261).
45        Allow => "Allow",
46        /// `Allow-Events` (RFC 6665).
47        AllowEvents => "Allow-Events",
48        /// `Answer-Mode`.
49        AnswerMode => "Answer-Mode",
50        /// `Attestation-Info`.
51        AttestationInfo => "Attestation-Info",
52        /// `Authentication-Info`.
53        AuthenticationInfo => "Authentication-Info",
54        /// `Authorization` (RFC 3261).
55        Authorization => "Authorization",
56        /// `Call-ID` (RFC 3261).
57        CallId => "Call-ID",
58        /// `Call-Info` (RFC 3261).
59        CallInfo => "Call-Info",
60        /// `Cellular-Network-Info`.
61        CellularNetworkInfo => "Cellular-Network-Info",
62        /// `Contact` (RFC 3261).
63        Contact => "Contact",
64        /// `Content-Disposition` (RFC 3261).
65        ContentDisposition => "Content-Disposition",
66        /// `Content-Encoding` (RFC 3261).
67        ContentEncoding => "Content-Encoding",
68        /// `Content-ID`.
69        ContentId => "Content-ID",
70        /// `Content-Language` (RFC 3261).
71        ContentLanguage => "Content-Language",
72        /// `Content-Length` (RFC 3261).
73        ContentLength => "Content-Length",
74        /// `Content-Type` (RFC 3261).
75        ContentType => "Content-Type",
76        /// `CSeq` (RFC 3261).
77        Cseq => "CSeq",
78        /// `Date` (RFC 3261).
79        Date => "Date",
80        /// `DC-Info`.
81        DcInfo => "DC-Info",
82        /// `Encryption` (deprecated in RFC 3261).
83        Encryption => "Encryption",
84        /// `Error-Info` (RFC 3261).
85        ErrorInfo => "Error-Info",
86        /// `Event` (RFC 6665).
87        Event => "Event",
88        /// `Expires` (RFC 3261).
89        Expires => "Expires",
90        /// `Feature-Caps`.
91        FeatureCaps => "Feature-Caps",
92        /// `Flow-Timer`.
93        FlowTimer => "Flow-Timer",
94        /// `From` (RFC 3261).
95        From => "From",
96        /// `Geolocation` (RFC 6442).
97        Geolocation => "Geolocation",
98        /// `Geolocation-Error` (RFC 6442).
99        GeolocationError => "Geolocation-Error",
100        /// `Geolocation-Routing` (RFC 6442).
101        GeolocationRouting => "Geolocation-Routing",
102        /// `Hide` (deprecated in RFC 3261).
103        Hide => "Hide",
104        /// `History-Info` (RFC 7044).
105        HistoryInfo => "History-Info",
106        /// `Identity` (RFC 8224).
107        Identity => "Identity",
108        /// `Identity-Info`.
109        IdentityInfo => "Identity-Info",
110        /// `Info-Package`.
111        InfoPackage => "Info-Package",
112        /// `In-Reply-To` (RFC 3261).
113        InReplyTo => "In-Reply-To",
114        /// `Join` (RFC 3911).
115        Join => "Join",
116        /// `Max-Breadth`.
117        MaxBreadth => "Max-Breadth",
118        /// `Max-Forwards` (RFC 3261).
119        MaxForwards => "Max-Forwards",
120        /// `MIME-Version` (RFC 3261).
121        MimeVersion => "MIME-Version",
122        /// `Min-Expires` (RFC 3261).
123        MinExpires => "Min-Expires",
124        /// `Min-SE` (RFC 4028).
125        MinSe => "Min-SE",
126        /// `Organization` (RFC 3261).
127        Organization => "Organization",
128        /// `Origination-Id`.
129        OriginationId => "Origination-Id",
130        /// `P-Access-Network-Info`.
131        PAccessNetworkInfo => "P-Access-Network-Info",
132        /// `P-Answer-State`.
133        PAnswerState => "P-Answer-State",
134        /// `P-Asserted-Identity` (RFC 3325).
135        PAssertedIdentity => "P-Asserted-Identity",
136        /// `P-Asserted-Service`.
137        PAssertedService => "P-Asserted-Service",
138        /// `P-Associated-URI`.
139        PAssociatedUri => "P-Associated-URI",
140        /// `P-Called-Party-ID`.
141        PCalledPartyId => "P-Called-Party-ID",
142        /// `P-Charge-Info`.
143        PChargeInfo => "P-Charge-Info",
144        /// `P-Charging-Function-Addresses`.
145        PChargingFunctionAddresses => "P-Charging-Function-Addresses",
146        /// `P-Charging-Vector`.
147        PChargingVector => "P-Charging-Vector",
148        /// `P-DCS-Trace-Party-ID`.
149        PDcsTracePartyId => "P-DCS-Trace-Party-ID",
150        /// `P-DCS-OSPS`.
151        PDcsOsps => "P-DCS-OSPS",
152        /// `P-DCS-Billing-Info`.
153        PDcsBillingInfo => "P-DCS-Billing-Info",
154        /// `P-DCS-LAES`.
155        PDcsLaes => "P-DCS-LAES",
156        /// `P-DCS-Redirect`.
157        PDcsRedirect => "P-DCS-Redirect",
158        /// `P-Early-Media`.
159        PEarlyMedia => "P-Early-Media",
160        /// `P-Media-Authorization`.
161        PMediaAuthorization => "P-Media-Authorization",
162        /// `P-Preferred-Identity` (RFC 3325).
163        PPreferredIdentity => "P-Preferred-Identity",
164        /// `P-Preferred-Service`.
165        PPreferredService => "P-Preferred-Service",
166        /// `P-Private-Network-Indication`.
167        PPrivateNetworkIndication => "P-Private-Network-Indication",
168        /// `P-Profile-Key`.
169        PProfileKey => "P-Profile-Key",
170        /// `P-Refused-URI-List`.
171        PRefusedUriList => "P-Refused-URI-List",
172        /// `P-Served-User`.
173        PServedUser => "P-Served-User",
174        /// `P-User-Database`.
175        PUserDatabase => "P-User-Database",
176        /// `P-Visited-Network-ID`.
177        PVisitedNetworkId => "P-Visited-Network-ID",
178        /// `Path` (RFC 3327).
179        Path => "Path",
180        /// `Permission-Missing`.
181        PermissionMissing => "Permission-Missing",
182        /// `Policy-Contact`.
183        PolicyContact => "Policy-Contact",
184        /// `Policy-ID`.
185        PolicyId => "Policy-ID",
186        /// `Priority` (RFC 3261).
187        Priority => "Priority",
188        /// `Priority-Share`.
189        PriorityShare => "Priority-Share",
190        /// `Priority-Verstat`.
191        PriorityVerstat => "Priority-Verstat",
192        /// `Priv-Answer-Mode`.
193        PrivAnswerMode => "Priv-Answer-Mode",
194        /// `Privacy` (RFC 3323).
195        Privacy => "Privacy",
196        /// `Proxy-Authenticate` (RFC 3261).
197        ProxyAuthenticate => "Proxy-Authenticate",
198        /// `Proxy-Authorization` (RFC 3261).
199        ProxyAuthorization => "Proxy-Authorization",
200        /// `Proxy-Require` (RFC 3261).
201        ProxyRequire => "Proxy-Require",
202        /// `RAck`.
203        Rack => "RAck",
204        /// `Reason` (RFC 3326).
205        Reason => "Reason",
206        /// `Reason-Phrase`.
207        ReasonPhrase => "Reason-Phrase",
208        /// `Record-Route` (RFC 3261).
209        RecordRoute => "Record-Route",
210        /// `Recv-Info`.
211        RecvInfo => "Recv-Info",
212        /// `Refer-Events-At`.
213        ReferEventsAt => "Refer-Events-At",
214        /// `Refer-Sub`.
215        ReferSub => "Refer-Sub",
216        /// `Refer-To` (RFC 3515).
217        ReferTo => "Refer-To",
218        /// `Referred-By` (RFC 3892).
219        ReferredBy => "Referred-By",
220        /// `Reject-Contact`.
221        RejectContact => "Reject-Contact",
222        /// `Relayed-Charge`.
223        RelayedCharge => "Relayed-Charge",
224        /// `Replaces` (RFC 3891).
225        Replaces => "Replaces",
226        /// `Reply-To` (RFC 3261).
227        ReplyTo => "Reply-To",
228        /// `Request-Disposition`.
229        RequestDisposition => "Request-Disposition",
230        /// `Require` (RFC 3261).
231        Require => "Require",
232        /// `Resource-Priority`.
233        ResourcePriority => "Resource-Priority",
234        /// `Resource-Share`.
235        ResourceShare => "Resource-Share",
236        /// `Response-Key` (deprecated in RFC 3261).
237        ResponseKey => "Response-Key",
238        /// `Response-Source`.
239        ResponseSource => "Response-Source",
240        /// `Restoration-Info`.
241        RestorationInfo => "Restoration-Info",
242        /// `Retry-After` (RFC 3261).
243        RetryAfter => "Retry-After",
244        /// `Route` (RFC 3261).
245        Route => "Route",
246        /// `RSeq`.
247        Rseq => "RSeq",
248        /// `Security-Client` (RFC 3329).
249        SecurityClient => "Security-Client",
250        /// `Security-Server` (RFC 3329).
251        SecurityServer => "Security-Server",
252        /// `Security-Verify` (RFC 3329).
253        SecurityVerify => "Security-Verify",
254        /// `Server` (RFC 3261).
255        Server => "Server",
256        /// `Service-Interact-Info`.
257        ServiceInteractInfo => "Service-Interact-Info",
258        /// `Service-Route` (RFC 3608).
259        ServiceRoute => "Service-Route",
260        /// `Session-Expires` (RFC 4028).
261        SessionExpires => "Session-Expires",
262        /// `Session-ID`.
263        SessionId => "Session-ID",
264        /// `SIP-ETag`.
265        SipEtag => "SIP-ETag",
266        /// `SIP-If-Match`.
267        SipIfMatch => "SIP-If-Match",
268        /// `Subject` (RFC 3261).
269        Subject => "Subject",
270        /// `Subscription-State` (RFC 6665).
271        SubscriptionState => "Subscription-State",
272        /// `Supported` (RFC 3261).
273        Supported => "Supported",
274        /// `Suppress-If-Match`.
275        SuppressIfMatch => "Suppress-If-Match",
276        /// `Target-Dialog` (RFC 4538).
277        TargetDialog => "Target-Dialog",
278        /// `Timestamp` (RFC 3261).
279        Timestamp => "Timestamp",
280        /// `To` (RFC 3261).
281        To => "To",
282        /// `Trigger-Consent`.
283        TriggerConsent => "Trigger-Consent",
284        /// `Unsupported` (RFC 3261).
285        Unsupported => "Unsupported",
286        /// `User-Agent` (RFC 3261).
287        UserAgent => "User-Agent",
288        /// `User-to-User` (RFC 7433).
289        UserToUser => "User-to-User",
290        /// `Via` (RFC 3261).
291        Via => "Via",
292        /// `Warning` (RFC 3261).
293        Warning => "Warning",
294        /// `WWW-Authenticate` (RFC 3261).
295        WwwAuthenticate => "WWW-Authenticate",
296        // Draft headers — appended after IANA variants to preserve discriminants.
297        /// `Diversion` (draft-levy-sip-diversion-08, superseded by RFC 7044).
298        #[cfg(feature = "draft")]
299        Diversion => "Diversion",
300        /// `Remote-Party-ID` (draft-ietf-sip-privacy-01, superseded by RFC 3325).
301        #[cfg(feature = "draft")]
302        RemotePartyId => "Remote-Party-ID",
303    }
304}
305
306/// RFC 3261 §7.3.3 compact header form mappings.
307///
308/// Includes forms from RFC 3261, RFC 3515, RFC 3841, RFC 3892, RFC 4028,
309/// RFC 4474, and RFC 6665.
310const 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    /// Resolve a compact form letter to the corresponding header (RFC 3261 §7.3.3).
335    ///
336    /// Case-insensitive: both `'f'` and `'F'` resolve to [`SipHeader::From`].
337    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    /// Return the compact form letter for this header, if one exists.
346    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    /// Whether this header may appear multiple times in a SIP message.
354    ///
355    /// Headers listed here use comma-separated or repeated-header semantics
356    /// per RFC 3261 §7.3.1 and their defining RFCs.
357    pub fn is_multi_valued(&self) -> bool {
358        if matches!(
359            self,
360            // RFC 3261 core
361            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                // RFC 3325
385                | Self::PAssertedIdentity
386                | Self::PPreferredIdentity
387                // RFC 6665
388                | Self::AllowEvents
389                // RFC 3329
390                | Self::SecurityClient
391                | Self::SecurityServer
392                | Self::SecurityVerify
393                // RFC 3327
394                | Self::Path
395                // RFC 3608
396                | Self::ServiceRoute
397                // RFC 7044
398                | Self::HistoryInfo
399        ) {
400            return true;
401        }
402
403        #[cfg(feature = "draft")]
404        if matches!(
405            self,
406            // draft-levy-sip-diversion-08
407            Self::Diversion
408                // draft-ietf-sip-privacy-01
409                | Self::RemotePartyId
410        ) {
411            return true;
412        }
413
414        false
415    }
416
417    /// Parse a header name, including RFC 3261 §7.3.3 compact forms.
418    ///
419    /// Tries compact form resolution for single-character input, then
420    /// falls back to case-insensitive canonical name matching.
421    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
431/// Trait for looking up standard SIP headers from any key-value store.
432///
433/// Implementors provide `sip_header_str()` and get all typed accessors as
434/// default implementations.
435///
436/// # Example
437///
438/// ```
439/// use std::collections::HashMap;
440/// use sip_header::{SipHeaderLookup, SipHeader};
441///
442/// let mut headers = HashMap::new();
443/// headers.insert(
444///     "Call-Info".to_string(),
445///     "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId".to_string(),
446/// );
447///
448/// assert_eq!(
449///     headers.sip_header(SipHeader::CallInfo),
450///     Some("<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"),
451/// );
452///
453/// let ci = headers.call_info().unwrap().unwrap();
454/// assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
455/// ```
456pub trait SipHeaderLookup {
457    /// Look up a SIP header by its canonical name (e.g. `"Call-Info"`).
458    fn sip_header_str(&self, name: &str) -> Option<&str>;
459
460    /// Look up a SIP header by its [`SipHeader`] enum variant.
461    fn sip_header(&self, name: SipHeader) -> Option<&str> {
462        self.sip_header_str(name.as_str())
463    }
464
465    /// Return all occurrences of a header by canonical name.
466    ///
467    /// Unlike [`sip_header_str`](SipHeaderLookup::sip_header_str) which returns
468    /// at most one value, this method returns every occurrence. The default
469    /// implementation wraps `sip_header_str` in a single-element `Vec`; storage
470    /// backends that preserve per-occurrence values (e.g. `HashMap<String,
471    /// Vec<String>>`) should override this.
472    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    /// Return all occurrences of a header by [`SipHeader`] variant.
479    fn sip_header_all(&self, name: SipHeader) -> Vec<&str> {
480        self.sip_header_all_str(name.as_str())
481    }
482
483    /// Parse the `Call-Info` header into a [`UriInfo`].
484    ///
485    /// Returns `Ok(None)` if the header is absent, `Err` if present but unparseable.
486    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    /// Parse the `History-Info` header into a [`HistoryInfo`].
494    ///
495    /// Returns `Ok(None)` if the header is absent, `Err` if present but unparseable.
496    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    /// Parse `P-Asserted-Identity` into a list of [`SipHeaderAddr`].
504    ///
505    /// PAI is multi-valued per RFC 3325 — a message may assert up to two
506    /// identities. Returns an empty `Vec` if the header is absent.
507    fn p_asserted_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
508        parse_addr_list(self.sip_header_all(SipHeader::PAssertedIdentity))
509    }
510
511    /// Parse `P-Preferred-Identity` into a list of [`SipHeaderAddr`] (RFC 3325).
512    fn p_preferred_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
513        parse_addr_list(self.sip_header_all(SipHeader::PPreferredIdentity))
514    }
515
516    /// Parse `Route` into a list of [`SipHeaderAddr`] (RFC 3261 §20.34).
517    fn route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
518        parse_addr_list(self.sip_header_all(SipHeader::Route))
519    }
520
521    /// Parse `Record-Route` into a list of [`SipHeaderAddr`] (RFC 3261 §20.30).
522    fn record_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
523        parse_addr_list(self.sip_header_all(SipHeader::RecordRoute))
524    }
525
526    /// Parse `Path` into a list of [`SipHeaderAddr`] (RFC 3327).
527    fn path(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
528        parse_addr_list(self.sip_header_all(SipHeader::Path))
529    }
530
531    /// Parse `Service-Route` into a list of [`SipHeaderAddr`] (RFC 3608).
532    fn service_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
533        parse_addr_list(self.sip_header_all(SipHeader::ServiceRoute))
534    }
535
536    /// Parse `Contact` into a list of [`ContactValue`] (RFC 3261 §20.10).
537    ///
538    /// The Contact header may contain `*` (wildcard, used in REGISTER) or
539    /// a comma-separated list of name-addr/addr-spec entries.
540    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    /// Parse `Alert-Info` into a [`UriInfo`] (RFC 3261 §20.4).
548    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    /// Parse `Error-Info` into a [`UriInfo`] (RFC 3261 §20.18).
556    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    /// `Allow` header values as individual method tokens (RFC 3261 §20.5).
564    fn allow(&self) -> Vec<&str> {
565        split_trim(self.sip_header(SipHeader::Allow))
566    }
567
568    /// `Supported` header values as individual option-tag tokens (RFC 3261 §20.37).
569    fn supported(&self) -> Vec<&str> {
570        split_trim(self.sip_header(SipHeader::Supported))
571    }
572
573    /// `Require` header values as individual option-tag tokens (RFC 3261 §20.32).
574    fn require_header(&self) -> Vec<&str> {
575        split_trim(self.sip_header(SipHeader::Require))
576    }
577
578    /// `Proxy-Require` values as individual option-tag tokens (RFC 3261 §20.29).
579    fn proxy_require(&self) -> Vec<&str> {
580        split_trim(self.sip_header(SipHeader::ProxyRequire))
581    }
582
583    /// `Unsupported` values as individual option-tag tokens (RFC 3261 §20.40).
584    fn unsupported(&self) -> Vec<&str> {
585        split_trim(self.sip_header(SipHeader::Unsupported))
586    }
587
588    /// `Allow-Events` values as individual event-type tokens (RFC 6665).
589    fn allow_events(&self) -> Vec<&str> {
590        split_trim(self.sip_header(SipHeader::AllowEvents))
591    }
592
593    /// `Content-Encoding` values as individual tokens (RFC 3261 §20.12).
594    fn content_encoding(&self) -> Vec<&str> {
595        split_trim(self.sip_header(SipHeader::ContentEncoding))
596    }
597
598    /// `Content-Language` values as individual language tags (RFC 3261 §20.13).
599    fn content_language(&self) -> Vec<&str> {
600        split_trim(self.sip_header(SipHeader::ContentLanguage))
601    }
602
603    /// `In-Reply-To` values as individual Call-ID tokens (RFC 3261 §20.21).
604    fn in_reply_to(&self) -> Vec<&str> {
605        split_trim(self.sip_header(SipHeader::InReplyTo))
606    }
607
608    /// Parse `Via` into a [`SipVia`] (RFC 3261 §20.42).
609    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    /// Parse `Authorization` into a list of [`SipAuthValue`] (RFC 3261 §20.7).
617    ///
618    /// Auth headers MUST NOT be comma-combined (RFC 3261 §7.3.1), so each
619    /// occurrence is parsed separately via [`sip_header_all`](SipHeaderLookup::sip_header_all).
620    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    /// Parse `Proxy-Authorization` into a list of [`SipAuthValue`] (RFC 3261 §20.28).
628    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    /// Parse `WWW-Authenticate` into a list of [`SipAuthValue`] (RFC 3261 §20.44).
636    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    /// Parse `Proxy-Authenticate` into a list of [`SipAuthValue`] (RFC 3261 §20.27).
644    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    /// Parse `Warning` into a [`SipWarning`] (RFC 3261 §20.43).
652    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    /// Parse `Security-Client` into a [`SipSecurity`] (RFC 3329).
660    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    /// Parse `Security-Server` into a [`SipSecurity`] (RFC 3329).
668    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    /// Parse `Security-Verify` into a [`SipSecurity`] (RFC 3329).
676    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    /// Parse `Accept` into a [`SipAccept`] (RFC 3261 §20.1).
684    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    /// Parse `Accept-Encoding` into a [`SipAcceptEncoding`] (RFC 3261 §20.2).
692    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    /// Parse `Accept-Language` into a [`SipAcceptLanguage`] (RFC 3261 §20.3).
700    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    /// Parse `Diversion` into a list of [`SipHeaderAddr`] (draft-levy-sip-diversion-08).
708    #[cfg(feature = "draft")]
709    fn diversion(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
710        parse_addr_list(self.sip_header_all(SipHeader::Diversion))
711    }
712
713    /// Parse `Remote-Party-ID` into a list of [`SipHeaderAddr`] (draft-ietf-sip-privacy-01).
714    #[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}