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
19/// Error returned when parsing an unrecognized SIP header name.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct ParseSipHeaderError(pub String);
22
23impl std::fmt::Display for ParseSipHeaderError {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        write!(f, "unknown SIP header: {}", self.0)
26    }
27}
28
29impl std::error::Error for ParseSipHeaderError {}
30
31define_header_enum! {
32    error_type: ParseSipHeaderError,
33    /// Standard SIP header names with canonical wire casing.
34    ///
35    /// Each variant maps to the header's canonical form as defined in the
36    /// relevant RFC. `FromStr` is case-insensitive; `Display` always emits
37    /// the canonical form.
38    pub enum SipHeader {
39        /// `Accept` (RFC 3261).
40        Accept => "Accept",
41        /// `Accept-Contact`.
42        AcceptContact => "Accept-Contact",
43        /// `Accept-Encoding` (RFC 3261).
44        AcceptEncoding => "Accept-Encoding",
45        /// `Accept-Language` (RFC 3261).
46        AcceptLanguage => "Accept-Language",
47        /// `Accept-Resource-Priority`.
48        AcceptResourcePriority => "Accept-Resource-Priority",
49        /// `Additional-Identity`.
50        AdditionalIdentity => "Additional-Identity",
51        /// `Alert-Info` (RFC 3261).
52        AlertInfo => "Alert-Info",
53        /// `AlertMsg-Error`.
54        AlertmsgError => "AlertMsg-Error",
55        /// `Allow` (RFC 3261).
56        Allow => "Allow",
57        /// `Allow-Events` (RFC 6665).
58        AllowEvents => "Allow-Events",
59        /// `Answer-Mode`.
60        AnswerMode => "Answer-Mode",
61        /// `Attestation-Info`.
62        AttestationInfo => "Attestation-Info",
63        /// `Authentication-Info`.
64        AuthenticationInfo => "Authentication-Info",
65        /// `Authorization` (RFC 3261).
66        Authorization => "Authorization",
67        /// `Call-ID` (RFC 3261).
68        CallId => "Call-ID",
69        /// `Call-Info` (RFC 3261).
70        CallInfo => "Call-Info",
71        /// `Cellular-Network-Info`.
72        CellularNetworkInfo => "Cellular-Network-Info",
73        /// `Contact` (RFC 3261).
74        Contact => "Contact",
75        /// `Content-Disposition` (RFC 3261).
76        ContentDisposition => "Content-Disposition",
77        /// `Content-Encoding` (RFC 3261).
78        ContentEncoding => "Content-Encoding",
79        /// `Content-ID`.
80        ContentId => "Content-ID",
81        /// `Content-Language` (RFC 3261).
82        ContentLanguage => "Content-Language",
83        /// `Content-Length` (RFC 3261).
84        ContentLength => "Content-Length",
85        /// `Content-Type` (RFC 3261).
86        ContentType => "Content-Type",
87        /// `CSeq` (RFC 3261).
88        Cseq => "CSeq",
89        /// `Date` (RFC 3261).
90        Date => "Date",
91        /// `DC-Info`.
92        DcInfo => "DC-Info",
93        /// `Encryption` (deprecated in RFC 3261).
94        Encryption => "Encryption",
95        /// `Error-Info` (RFC 3261).
96        ErrorInfo => "Error-Info",
97        /// `Event` (RFC 6665).
98        Event => "Event",
99        /// `Expires` (RFC 3261).
100        Expires => "Expires",
101        /// `Feature-Caps`.
102        FeatureCaps => "Feature-Caps",
103        /// `Flow-Timer`.
104        FlowTimer => "Flow-Timer",
105        /// `From` (RFC 3261).
106        From => "From",
107        /// `Geolocation` (RFC 6442).
108        Geolocation => "Geolocation",
109        /// `Geolocation-Error` (RFC 6442).
110        GeolocationError => "Geolocation-Error",
111        /// `Geolocation-Routing` (RFC 6442).
112        GeolocationRouting => "Geolocation-Routing",
113        /// `Hide` (deprecated in RFC 3261).
114        Hide => "Hide",
115        /// `History-Info` (RFC 7044).
116        HistoryInfo => "History-Info",
117        /// `Identity` (RFC 8224).
118        Identity => "Identity",
119        /// `Identity-Info`.
120        IdentityInfo => "Identity-Info",
121        /// `Info-Package`.
122        InfoPackage => "Info-Package",
123        /// `In-Reply-To` (RFC 3261).
124        InReplyTo => "In-Reply-To",
125        /// `Join` (RFC 3911).
126        Join => "Join",
127        /// `Max-Breadth`.
128        MaxBreadth => "Max-Breadth",
129        /// `Max-Forwards` (RFC 3261).
130        MaxForwards => "Max-Forwards",
131        /// `MIME-Version` (RFC 3261).
132        MimeVersion => "MIME-Version",
133        /// `Min-Expires` (RFC 3261).
134        MinExpires => "Min-Expires",
135        /// `Min-SE` (RFC 4028).
136        MinSe => "Min-SE",
137        /// `Organization` (RFC 3261).
138        Organization => "Organization",
139        /// `Origination-Id`.
140        OriginationId => "Origination-Id",
141        /// `P-Access-Network-Info`.
142        PAccessNetworkInfo => "P-Access-Network-Info",
143        /// `P-Answer-State`.
144        PAnswerState => "P-Answer-State",
145        /// `P-Asserted-Identity` (RFC 3325).
146        PAssertedIdentity => "P-Asserted-Identity",
147        /// `P-Asserted-Service`.
148        PAssertedService => "P-Asserted-Service",
149        /// `P-Associated-URI`.
150        PAssociatedUri => "P-Associated-URI",
151        /// `P-Called-Party-ID`.
152        PCalledPartyId => "P-Called-Party-ID",
153        /// `P-Charge-Info`.
154        PChargeInfo => "P-Charge-Info",
155        /// `P-Charging-Function-Addresses`.
156        PChargingFunctionAddresses => "P-Charging-Function-Addresses",
157        /// `P-Charging-Vector`.
158        PChargingVector => "P-Charging-Vector",
159        /// `P-DCS-Trace-Party-ID`.
160        PDcsTracePartyId => "P-DCS-Trace-Party-ID",
161        /// `P-DCS-OSPS`.
162        PDcsOsps => "P-DCS-OSPS",
163        /// `P-DCS-Billing-Info`.
164        PDcsBillingInfo => "P-DCS-Billing-Info",
165        /// `P-DCS-LAES`.
166        PDcsLaes => "P-DCS-LAES",
167        /// `P-DCS-Redirect`.
168        PDcsRedirect => "P-DCS-Redirect",
169        /// `P-Early-Media`.
170        PEarlyMedia => "P-Early-Media",
171        /// `P-Media-Authorization`.
172        PMediaAuthorization => "P-Media-Authorization",
173        /// `P-Preferred-Identity` (RFC 3325).
174        PPreferredIdentity => "P-Preferred-Identity",
175        /// `P-Preferred-Service`.
176        PPreferredService => "P-Preferred-Service",
177        /// `P-Private-Network-Indication`.
178        PPrivateNetworkIndication => "P-Private-Network-Indication",
179        /// `P-Profile-Key`.
180        PProfileKey => "P-Profile-Key",
181        /// `P-Refused-URI-List`.
182        PRefusedUriList => "P-Refused-URI-List",
183        /// `P-Served-User`.
184        PServedUser => "P-Served-User",
185        /// `P-User-Database`.
186        PUserDatabase => "P-User-Database",
187        /// `P-Visited-Network-ID`.
188        PVisitedNetworkId => "P-Visited-Network-ID",
189        /// `Path` (RFC 3327).
190        Path => "Path",
191        /// `Permission-Missing`.
192        PermissionMissing => "Permission-Missing",
193        /// `Policy-Contact`.
194        PolicyContact => "Policy-Contact",
195        /// `Policy-ID`.
196        PolicyId => "Policy-ID",
197        /// `Priority` (RFC 3261).
198        Priority => "Priority",
199        /// `Priority-Share`.
200        PriorityShare => "Priority-Share",
201        /// `Priority-Verstat`.
202        PriorityVerstat => "Priority-Verstat",
203        /// `Priv-Answer-Mode`.
204        PrivAnswerMode => "Priv-Answer-Mode",
205        /// `Privacy` (RFC 3323).
206        Privacy => "Privacy",
207        /// `Proxy-Authenticate` (RFC 3261).
208        ProxyAuthenticate => "Proxy-Authenticate",
209        /// `Proxy-Authorization` (RFC 3261).
210        ProxyAuthorization => "Proxy-Authorization",
211        /// `Proxy-Require` (RFC 3261).
212        ProxyRequire => "Proxy-Require",
213        /// `RAck`.
214        Rack => "RAck",
215        /// `Reason` (RFC 3326).
216        Reason => "Reason",
217        /// `Reason-Phrase`.
218        ReasonPhrase => "Reason-Phrase",
219        /// `Record-Route` (RFC 3261).
220        RecordRoute => "Record-Route",
221        /// `Recv-Info`.
222        RecvInfo => "Recv-Info",
223        /// `Refer-Events-At`.
224        ReferEventsAt => "Refer-Events-At",
225        /// `Refer-Sub`.
226        ReferSub => "Refer-Sub",
227        /// `Refer-To` (RFC 3515).
228        ReferTo => "Refer-To",
229        /// `Referred-By` (RFC 3892).
230        ReferredBy => "Referred-By",
231        /// `Reject-Contact`.
232        RejectContact => "Reject-Contact",
233        /// `Relayed-Charge`.
234        RelayedCharge => "Relayed-Charge",
235        /// `Replaces` (RFC 3891).
236        Replaces => "Replaces",
237        /// `Reply-To` (RFC 3261).
238        ReplyTo => "Reply-To",
239        /// `Request-Disposition`.
240        RequestDisposition => "Request-Disposition",
241        /// `Require` (RFC 3261).
242        Require => "Require",
243        /// `Resource-Priority`.
244        ResourcePriority => "Resource-Priority",
245        /// `Resource-Share`.
246        ResourceShare => "Resource-Share",
247        /// `Response-Key` (deprecated in RFC 3261).
248        ResponseKey => "Response-Key",
249        /// `Response-Source`.
250        ResponseSource => "Response-Source",
251        /// `Restoration-Info`.
252        RestorationInfo => "Restoration-Info",
253        /// `Retry-After` (RFC 3261).
254        RetryAfter => "Retry-After",
255        /// `Route` (RFC 3261).
256        Route => "Route",
257        /// `RSeq`.
258        Rseq => "RSeq",
259        /// `Security-Client` (RFC 3329).
260        SecurityClient => "Security-Client",
261        /// `Security-Server` (RFC 3329).
262        SecurityServer => "Security-Server",
263        /// `Security-Verify` (RFC 3329).
264        SecurityVerify => "Security-Verify",
265        /// `Server` (RFC 3261).
266        Server => "Server",
267        /// `Service-Interact-Info`.
268        ServiceInteractInfo => "Service-Interact-Info",
269        /// `Service-Route` (RFC 3608).
270        ServiceRoute => "Service-Route",
271        /// `Session-Expires` (RFC 4028).
272        SessionExpires => "Session-Expires",
273        /// `Session-ID`.
274        SessionId => "Session-ID",
275        /// `SIP-ETag`.
276        SipEtag => "SIP-ETag",
277        /// `SIP-If-Match`.
278        SipIfMatch => "SIP-If-Match",
279        /// `Subject` (RFC 3261).
280        Subject => "Subject",
281        /// `Subscription-State` (RFC 6665).
282        SubscriptionState => "Subscription-State",
283        /// `Supported` (RFC 3261).
284        Supported => "Supported",
285        /// `Suppress-If-Match`.
286        SuppressIfMatch => "Suppress-If-Match",
287        /// `Target-Dialog` (RFC 4538).
288        TargetDialog => "Target-Dialog",
289        /// `Timestamp` (RFC 3261).
290        Timestamp => "Timestamp",
291        /// `To` (RFC 3261).
292        To => "To",
293        /// `Trigger-Consent`.
294        TriggerConsent => "Trigger-Consent",
295        /// `Unsupported` (RFC 3261).
296        Unsupported => "Unsupported",
297        /// `User-Agent` (RFC 3261).
298        UserAgent => "User-Agent",
299        /// `User-to-User` (RFC 7433).
300        UserToUser => "User-to-User",
301        /// `Via` (RFC 3261).
302        Via => "Via",
303        /// `Warning` (RFC 3261).
304        Warning => "Warning",
305        /// `WWW-Authenticate` (RFC 3261).
306        WwwAuthenticate => "WWW-Authenticate",
307        // Draft headers — appended after IANA variants to preserve discriminants.
308        /// `Diversion` (draft-levy-sip-diversion-08, superseded by RFC 7044).
309        #[cfg(feature = "draft")]
310        Diversion => "Diversion",
311        /// `Remote-Party-ID` (draft-ietf-sip-privacy-01, superseded by RFC 3325).
312        #[cfg(feature = "draft")]
313        RemotePartyId => "Remote-Party-ID",
314    }
315}
316
317/// RFC 3261 §7.3.3 compact header form mappings.
318///
319/// Includes forms from RFC 3261, RFC 3515, RFC 3841, RFC 3892, RFC 4028,
320/// RFC 4474, and RFC 6665.
321const COMPACT_FORMS: &[(u8, SipHeader)] = &[
322    (b'a', SipHeader::AcceptContact),
323    (b'b', SipHeader::ReferredBy),
324    (b'c', SipHeader::ContentType),
325    (b'd', SipHeader::RequestDisposition),
326    (b'e', SipHeader::ContentEncoding),
327    (b'f', SipHeader::From),
328    (b'i', SipHeader::CallId),
329    (b'j', SipHeader::RejectContact),
330    (b'k', SipHeader::Supported),
331    (b'l', SipHeader::ContentLength),
332    (b'm', SipHeader::Contact),
333    (b'n', SipHeader::IdentityInfo),
334    (b'o', SipHeader::Event),
335    (b'r', SipHeader::ReferTo),
336    (b's', SipHeader::Subject),
337    (b't', SipHeader::To),
338    (b'u', SipHeader::AllowEvents),
339    (b'v', SipHeader::Via),
340    (b'x', SipHeader::SessionExpires),
341    (b'y', SipHeader::Identity),
342];
343
344impl SipHeader {
345    /// Resolve a compact form letter to the corresponding header (RFC 3261 §7.3.3).
346    ///
347    /// Case-insensitive: both `'f'` and `'F'` resolve to [`SipHeader::From`].
348    pub fn from_compact(ch: u8) -> Option<Self> {
349        let lower = ch.to_ascii_lowercase();
350        COMPACT_FORMS
351            .iter()
352            .find(|(c, _)| *c == lower)
353            .map(|(_, h)| *h)
354    }
355
356    /// Return the compact form letter for this header, if one exists.
357    pub fn compact_form(&self) -> Option<char> {
358        COMPACT_FORMS
359            .iter()
360            .find(|(_, h)| h == self)
361            .map(|(c, _)| *c as char)
362    }
363
364    /// Whether this header may appear multiple times in a SIP message.
365    ///
366    /// Headers listed here use comma-separated or repeated-header semantics
367    /// per RFC 3261 §7.3.1 and their defining RFCs.
368    pub fn is_multi_valued(&self) -> bool {
369        if matches!(
370            self,
371            // RFC 3261 core
372            Self::Via
373                | Self::Route
374                | Self::RecordRoute
375                | Self::Contact
376                | Self::Allow
377                | Self::Supported
378                | Self::Require
379                | Self::ProxyRequire
380                | Self::Unsupported
381                | Self::Authorization
382                | Self::ProxyAuthorization
383                | Self::WwwAuthenticate
384                | Self::ProxyAuthenticate
385                | Self::Warning
386                | Self::ErrorInfo
387                | Self::CallInfo
388                | Self::AlertInfo
389                | Self::Accept
390                | Self::AcceptEncoding
391                | Self::AcceptLanguage
392                | Self::ContentEncoding
393                | Self::ContentLanguage
394                | Self::InReplyTo
395                // RFC 3325
396                | Self::PAssertedIdentity
397                | Self::PPreferredIdentity
398                // RFC 6665
399                | Self::AllowEvents
400                // RFC 3329
401                | Self::SecurityClient
402                | Self::SecurityServer
403                | Self::SecurityVerify
404                // RFC 3327
405                | Self::Path
406                // RFC 3608
407                | Self::ServiceRoute
408                // RFC 7044
409                | Self::HistoryInfo
410        ) {
411            return true;
412        }
413
414        #[cfg(feature = "draft")]
415        if matches!(
416            self,
417            // draft-levy-sip-diversion-08
418            Self::Diversion
419                // draft-ietf-sip-privacy-01
420                | Self::RemotePartyId
421        ) {
422            return true;
423        }
424
425        false
426    }
427
428    /// Parse a header name, including RFC 3261 §7.3.3 compact forms.
429    ///
430    /// Tries compact form resolution for single-character input, then
431    /// falls back to case-insensitive canonical name matching.
432    pub fn parse_name(name: &str) -> Result<Self, ParseSipHeaderError> {
433        if name.len() == 1 {
434            if let Some(h) = Self::from_compact(name.as_bytes()[0]) {
435                return Ok(h);
436            }
437        }
438        name.parse()
439    }
440}
441
442/// Trait for looking up standard SIP headers from any key-value store.
443///
444/// Implementors provide `sip_header_str()` and get all typed accessors as
445/// default implementations.
446///
447/// # Example
448///
449/// ```
450/// use std::collections::HashMap;
451/// use sip_header::{SipHeaderLookup, SipHeader};
452///
453/// let mut headers = HashMap::new();
454/// headers.insert(
455///     "Call-Info".to_string(),
456///     "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId".to_string(),
457/// );
458///
459/// assert_eq!(
460///     headers.sip_header(SipHeader::CallInfo),
461///     Some("<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"),
462/// );
463///
464/// let ci = headers.call_info().unwrap().unwrap();
465/// assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
466/// ```
467pub trait SipHeaderLookup {
468    /// Look up a SIP header by its canonical name (e.g. `"Call-Info"`).
469    fn sip_header_str(&self, name: &str) -> Option<&str>;
470
471    /// Look up a SIP header by its [`SipHeader`] enum variant.
472    fn sip_header(&self, name: SipHeader) -> Option<&str> {
473        self.sip_header_str(name.as_str())
474    }
475
476    /// Return all occurrences of a header by canonical name.
477    ///
478    /// Unlike [`sip_header_str`](SipHeaderLookup::sip_header_str) which returns
479    /// at most one value, this method returns every occurrence. The default
480    /// implementation wraps `sip_header_str` in a single-element `Vec`; storage
481    /// backends that preserve per-occurrence values (e.g. `HashMap<String,
482    /// Vec<String>>`) should override this.
483    fn sip_header_all_str<'a>(&'a self, name: &str) -> Vec<&'a str> {
484        self.sip_header_str(name)
485            .into_iter()
486            .collect()
487    }
488
489    /// Return all occurrences of a header by [`SipHeader`] variant.
490    fn sip_header_all(&self, name: SipHeader) -> Vec<&str> {
491        self.sip_header_all_str(name.as_str())
492    }
493
494    /// Parse the `Call-Info` header into a [`UriInfo`].
495    ///
496    /// Returns `Ok(None)` if the header is absent, `Err` if present but unparseable.
497    fn call_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
498        match self.sip_header(SipHeader::CallInfo) {
499            Some(s) => UriInfo::parse(s).map(Some),
500            None => Ok(None),
501        }
502    }
503
504    /// Parse the `History-Info` header into a [`HistoryInfo`].
505    ///
506    /// Returns `Ok(None)` if the header is absent, `Err` if present but unparseable.
507    fn history_info(&self) -> Result<Option<HistoryInfo>, HistoryInfoError> {
508        match self.sip_header(SipHeader::HistoryInfo) {
509            Some(s) => HistoryInfo::parse(s).map(Some),
510            None => Ok(None),
511        }
512    }
513
514    /// Parse `P-Asserted-Identity` into a list of [`SipHeaderAddr`].
515    ///
516    /// PAI is multi-valued per RFC 3325 — a message may assert up to two
517    /// identities. Returns an empty `Vec` if the header is absent.
518    fn p_asserted_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
519        parse_addr_list(self.sip_header_all(SipHeader::PAssertedIdentity))
520    }
521
522    /// Parse `P-Preferred-Identity` into a list of [`SipHeaderAddr`] (RFC 3325).
523    fn p_preferred_identity(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
524        parse_addr_list(self.sip_header_all(SipHeader::PPreferredIdentity))
525    }
526
527    /// Parse `Route` into a list of [`SipHeaderAddr`] (RFC 3261 §20.34).
528    fn route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
529        parse_addr_list(self.sip_header_all(SipHeader::Route))
530    }
531
532    /// Parse `Record-Route` into a list of [`SipHeaderAddr`] (RFC 3261 §20.30).
533    fn record_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
534        parse_addr_list(self.sip_header_all(SipHeader::RecordRoute))
535    }
536
537    /// Parse `Path` into a list of [`SipHeaderAddr`] (RFC 3327).
538    fn path(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
539        parse_addr_list(self.sip_header_all(SipHeader::Path))
540    }
541
542    /// Parse `Service-Route` into a list of [`SipHeaderAddr`] (RFC 3608).
543    fn service_route(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
544        parse_addr_list(self.sip_header_all(SipHeader::ServiceRoute))
545    }
546
547    /// Parse `Contact` into a list of [`ContactValue`] (RFC 3261 §20.10).
548    ///
549    /// The Contact header may contain `*` (wildcard, used in REGISTER) or
550    /// a comma-separated list of name-addr/addr-spec entries.
551    fn contact(&self) -> Result<Vec<ContactValue>, ParseSipHeaderAddrError> {
552        match self.sip_header(SipHeader::Contact) {
553            Some(s) => crate::contact::parse_contact_list(s),
554            None => Ok(Vec::new()),
555        }
556    }
557
558    /// Parse `Alert-Info` into a [`UriInfo`] (RFC 3261 §20.4).
559    fn alert_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
560        match self.sip_header(SipHeader::AlertInfo) {
561            Some(s) => UriInfo::parse(s).map(Some),
562            None => Ok(None),
563        }
564    }
565
566    /// Parse `Error-Info` into a [`UriInfo`] (RFC 3261 §20.18).
567    fn error_info(&self) -> Result<Option<UriInfo>, UriInfoError> {
568        match self.sip_header(SipHeader::ErrorInfo) {
569            Some(s) => UriInfo::parse(s).map(Some),
570            None => Ok(None),
571        }
572    }
573
574    /// `Allow` header values as individual method tokens (RFC 3261 §20.5).
575    fn allow(&self) -> Vec<&str> {
576        split_trim(self.sip_header(SipHeader::Allow))
577    }
578
579    /// `Supported` header values as individual option-tag tokens (RFC 3261 §20.37).
580    fn supported(&self) -> Vec<&str> {
581        split_trim(self.sip_header(SipHeader::Supported))
582    }
583
584    /// `Require` header values as individual option-tag tokens (RFC 3261 §20.32).
585    fn require_header(&self) -> Vec<&str> {
586        split_trim(self.sip_header(SipHeader::Require))
587    }
588
589    /// `Proxy-Require` values as individual option-tag tokens (RFC 3261 §20.29).
590    fn proxy_require(&self) -> Vec<&str> {
591        split_trim(self.sip_header(SipHeader::ProxyRequire))
592    }
593
594    /// `Unsupported` values as individual option-tag tokens (RFC 3261 §20.40).
595    fn unsupported(&self) -> Vec<&str> {
596        split_trim(self.sip_header(SipHeader::Unsupported))
597    }
598
599    /// `Allow-Events` values as individual event-type tokens (RFC 6665).
600    fn allow_events(&self) -> Vec<&str> {
601        split_trim(self.sip_header(SipHeader::AllowEvents))
602    }
603
604    /// `Content-Encoding` values as individual tokens (RFC 3261 §20.12).
605    fn content_encoding(&self) -> Vec<&str> {
606        split_trim(self.sip_header(SipHeader::ContentEncoding))
607    }
608
609    /// `Content-Language` values as individual language tags (RFC 3261 §20.13).
610    fn content_language(&self) -> Vec<&str> {
611        split_trim(self.sip_header(SipHeader::ContentLanguage))
612    }
613
614    /// `In-Reply-To` values as individual Call-ID tokens (RFC 3261 §20.21).
615    fn in_reply_to(&self) -> Vec<&str> {
616        split_trim(self.sip_header(SipHeader::InReplyTo))
617    }
618
619    /// Parse `Via` into a [`SipVia`] (RFC 3261 §20.42).
620    fn via(&self) -> Result<Option<SipVia>, SipViaError> {
621        match self.sip_header(SipHeader::Via) {
622            Some(s) => SipVia::parse(s).map(Some),
623            None => Ok(None),
624        }
625    }
626
627    /// Parse `Authorization` into a list of [`SipAuthValue`] (RFC 3261 §20.7).
628    ///
629    /// Auth headers MUST NOT be comma-combined (RFC 3261 §7.3.1), so each
630    /// occurrence is parsed separately via [`sip_header_all`](SipHeaderLookup::sip_header_all).
631    fn authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
632        self.sip_header_all(SipHeader::Authorization)
633            .into_iter()
634            .map(|s| s.parse::<SipAuthValue>())
635            .collect()
636    }
637
638    /// Parse `Proxy-Authorization` into a list of [`SipAuthValue`] (RFC 3261 §20.28).
639    fn proxy_authorization(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
640        self.sip_header_all(SipHeader::ProxyAuthorization)
641            .into_iter()
642            .map(|s| s.parse::<SipAuthValue>())
643            .collect()
644    }
645
646    /// Parse `WWW-Authenticate` into a list of [`SipAuthValue`] (RFC 3261 §20.44).
647    fn www_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
648        self.sip_header_all(SipHeader::WwwAuthenticate)
649            .into_iter()
650            .map(|s| s.parse::<SipAuthValue>())
651            .collect()
652    }
653
654    /// Parse `Proxy-Authenticate` into a list of [`SipAuthValue`] (RFC 3261 §20.27).
655    fn proxy_authenticate(&self) -> Result<Vec<SipAuthValue>, SipAuthError> {
656        self.sip_header_all(SipHeader::ProxyAuthenticate)
657            .into_iter()
658            .map(|s| s.parse::<SipAuthValue>())
659            .collect()
660    }
661
662    /// Parse `Warning` into a [`SipWarning`] (RFC 3261 §20.43).
663    fn warning(&self) -> Result<Option<SipWarning>, SipWarningError> {
664        match self.sip_header(SipHeader::Warning) {
665            Some(s) => SipWarning::parse(s).map(Some),
666            None => Ok(None),
667        }
668    }
669
670    /// Parse `Security-Client` into a [`SipSecurity`] (RFC 3329).
671    fn security_client(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
672        match self.sip_header(SipHeader::SecurityClient) {
673            Some(s) => SipSecurity::parse(s).map(Some),
674            None => Ok(None),
675        }
676    }
677
678    /// Parse `Security-Server` into a [`SipSecurity`] (RFC 3329).
679    fn security_server(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
680        match self.sip_header(SipHeader::SecurityServer) {
681            Some(s) => SipSecurity::parse(s).map(Some),
682            None => Ok(None),
683        }
684    }
685
686    /// Parse `Security-Verify` into a [`SipSecurity`] (RFC 3329).
687    fn security_verify(&self) -> Result<Option<SipSecurity>, SipSecurityError> {
688        match self.sip_header(SipHeader::SecurityVerify) {
689            Some(s) => SipSecurity::parse(s).map(Some),
690            None => Ok(None),
691        }
692    }
693
694    /// Parse `Accept` into a [`SipAccept`] (RFC 3261 §20.1).
695    fn accept(&self) -> Result<Option<SipAccept>, SipAcceptError> {
696        match self.sip_header(SipHeader::Accept) {
697            Some(s) => SipAccept::parse(s).map(Some),
698            None => Ok(None),
699        }
700    }
701
702    /// Parse `Accept-Encoding` into a [`SipAcceptEncoding`] (RFC 3261 §20.2).
703    fn accept_encoding(&self) -> Result<Option<SipAcceptEncoding>, SipAcceptEncodingError> {
704        match self.sip_header(SipHeader::AcceptEncoding) {
705            Some(s) => SipAcceptEncoding::parse(s).map(Some),
706            None => Ok(None),
707        }
708    }
709
710    /// Parse `Accept-Language` into a [`SipAcceptLanguage`] (RFC 3261 §20.3).
711    fn accept_language(&self) -> Result<Option<SipAcceptLanguage>, SipAcceptLanguageError> {
712        match self.sip_header(SipHeader::AcceptLanguage) {
713            Some(s) => SipAcceptLanguage::parse(s).map(Some),
714            None => Ok(None),
715        }
716    }
717
718    /// Parse `Diversion` into a list of [`SipHeaderAddr`] (draft-levy-sip-diversion-08).
719    #[cfg(feature = "draft")]
720    fn diversion(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
721        parse_addr_list(self.sip_header_all(SipHeader::Diversion))
722    }
723
724    /// Parse `Remote-Party-ID` into a list of [`SipHeaderAddr`] (draft-ietf-sip-privacy-01).
725    #[cfg(feature = "draft")]
726    fn remote_party_id(&self) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
727        parse_addr_list(self.sip_header_all(SipHeader::RemotePartyId))
728    }
729}
730
731fn parse_addr_list(raw: Vec<&str>) -> Result<Vec<SipHeaderAddr>, ParseSipHeaderAddrError> {
732    if raw.is_empty() {
733        return Ok(Vec::new());
734    }
735    raw.into_iter()
736        .flat_map(|s| crate::split_comma_entries(s))
737        .map(|s| {
738            s.trim()
739                .parse::<SipHeaderAddr>()
740        })
741        .collect()
742}
743
744fn split_trim(raw: Option<&str>) -> Vec<&str> {
745    match raw {
746        Some(s) => crate::split_comma_entries(s)
747            .into_iter()
748            .map(str::trim)
749            .collect(),
750        None => Vec::new(),
751    }
752}
753
754impl SipHeaderLookup for std::collections::HashMap<String, String> {
755    fn sip_header_str(&self, name: &str) -> Option<&str> {
756        self.get(name)
757            .map(|s| s.as_str())
758    }
759}
760
761impl SipHeaderLookup for std::collections::HashMap<String, Vec<String>> {
762    fn sip_header_str(&self, name: &str) -> Option<&str> {
763        self.get(name)
764            .and_then(|v| v.first())
765            .map(|s| s.as_str())
766    }
767
768    fn sip_header_all_str(&self, name: &str) -> Vec<&str> {
769        self.get(name)
770            .map(|v| {
771                v.iter()
772                    .map(|s| s.as_str())
773                    .collect()
774            })
775            .unwrap_or_default()
776    }
777}
778
779#[cfg(test)]
780mod tests {
781    use super::*;
782    use std::collections::HashMap;
783
784    #[test]
785    fn display_round_trip() {
786        assert_eq!(SipHeader::CallInfo.to_string(), "Call-Info");
787        assert_eq!(SipHeader::HistoryInfo.to_string(), "History-Info");
788        assert_eq!(
789            SipHeader::PAssertedIdentity.to_string(),
790            "P-Asserted-Identity"
791        );
792    }
793
794    #[test]
795    fn as_ref_str() {
796        let h: &str = SipHeader::CallInfo.as_ref();
797        assert_eq!(h, "Call-Info");
798    }
799
800    #[test]
801    fn from_str_case_insensitive() {
802        assert_eq!("call-info".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
803        assert_eq!("CALL-INFO".parse::<SipHeader>(), Ok(SipHeader::CallInfo));
804        assert_eq!(
805            "history-info".parse::<SipHeader>(),
806            Ok(SipHeader::HistoryInfo)
807        );
808        assert_eq!(
809            "p-asserted-identity".parse::<SipHeader>(),
810            Ok(SipHeader::PAssertedIdentity)
811        );
812        assert_eq!(
813            "P-ASSERTED-IDENTITY".parse::<SipHeader>(),
814            Ok(SipHeader::PAssertedIdentity)
815        );
816    }
817
818    #[test]
819    fn from_str_unknown() {
820        assert!("X-Custom"
821            .parse::<SipHeader>()
822            .is_err());
823    }
824
825    #[test]
826    fn from_str_round_trip_all() {
827        let variants = [
828            SipHeader::Accept,
829            SipHeader::AcceptContact,
830            SipHeader::AcceptEncoding,
831            SipHeader::AcceptLanguage,
832            SipHeader::AcceptResourcePriority,
833            SipHeader::AdditionalIdentity,
834            SipHeader::AlertInfo,
835            SipHeader::AlertmsgError,
836            SipHeader::Allow,
837            SipHeader::AllowEvents,
838            SipHeader::AnswerMode,
839            SipHeader::AttestationInfo,
840            SipHeader::AuthenticationInfo,
841            SipHeader::Authorization,
842            SipHeader::CallId,
843            SipHeader::CallInfo,
844            SipHeader::CellularNetworkInfo,
845            SipHeader::Contact,
846            SipHeader::ContentDisposition,
847            SipHeader::ContentEncoding,
848            SipHeader::ContentId,
849            SipHeader::ContentLanguage,
850            SipHeader::ContentLength,
851            SipHeader::ContentType,
852            SipHeader::Cseq,
853            SipHeader::Date,
854            SipHeader::DcInfo,
855            SipHeader::Encryption,
856            SipHeader::ErrorInfo,
857            SipHeader::Event,
858            SipHeader::Expires,
859            SipHeader::FeatureCaps,
860            SipHeader::FlowTimer,
861            SipHeader::From,
862            SipHeader::Geolocation,
863            SipHeader::GeolocationError,
864            SipHeader::GeolocationRouting,
865            SipHeader::Hide,
866            SipHeader::HistoryInfo,
867            SipHeader::Identity,
868            SipHeader::IdentityInfo,
869            SipHeader::InfoPackage,
870            SipHeader::InReplyTo,
871            SipHeader::Join,
872            SipHeader::MaxBreadth,
873            SipHeader::MaxForwards,
874            SipHeader::MimeVersion,
875            SipHeader::MinExpires,
876            SipHeader::MinSe,
877            SipHeader::Organization,
878            SipHeader::OriginationId,
879            SipHeader::PAccessNetworkInfo,
880            SipHeader::PAnswerState,
881            SipHeader::PAssertedIdentity,
882            SipHeader::PAssertedService,
883            SipHeader::PAssociatedUri,
884            SipHeader::PCalledPartyId,
885            SipHeader::PChargeInfo,
886            SipHeader::PChargingFunctionAddresses,
887            SipHeader::PChargingVector,
888            SipHeader::PDcsTracePartyId,
889            SipHeader::PDcsOsps,
890            SipHeader::PDcsBillingInfo,
891            SipHeader::PDcsLaes,
892            SipHeader::PDcsRedirect,
893            SipHeader::PEarlyMedia,
894            SipHeader::PMediaAuthorization,
895            SipHeader::PPreferredIdentity,
896            SipHeader::PPreferredService,
897            SipHeader::PPrivateNetworkIndication,
898            SipHeader::PProfileKey,
899            SipHeader::PRefusedUriList,
900            SipHeader::PServedUser,
901            SipHeader::PUserDatabase,
902            SipHeader::PVisitedNetworkId,
903            SipHeader::Path,
904            SipHeader::PermissionMissing,
905            SipHeader::PolicyContact,
906            SipHeader::PolicyId,
907            SipHeader::Priority,
908            SipHeader::PriorityShare,
909            SipHeader::PriorityVerstat,
910            SipHeader::PrivAnswerMode,
911            SipHeader::Privacy,
912            SipHeader::ProxyAuthenticate,
913            SipHeader::ProxyAuthorization,
914            SipHeader::ProxyRequire,
915            SipHeader::Rack,
916            SipHeader::Reason,
917            SipHeader::ReasonPhrase,
918            SipHeader::RecordRoute,
919            SipHeader::RecvInfo,
920            SipHeader::ReferEventsAt,
921            SipHeader::ReferSub,
922            SipHeader::ReferTo,
923            SipHeader::ReferredBy,
924            SipHeader::RejectContact,
925            SipHeader::RelayedCharge,
926            SipHeader::Replaces,
927            SipHeader::ReplyTo,
928            SipHeader::RequestDisposition,
929            SipHeader::Require,
930            SipHeader::ResourcePriority,
931            SipHeader::ResourceShare,
932            SipHeader::ResponseKey,
933            SipHeader::ResponseSource,
934            SipHeader::RestorationInfo,
935            SipHeader::RetryAfter,
936            SipHeader::Route,
937            SipHeader::Rseq,
938            SipHeader::SecurityClient,
939            SipHeader::SecurityServer,
940            SipHeader::SecurityVerify,
941            SipHeader::Server,
942            SipHeader::ServiceInteractInfo,
943            SipHeader::ServiceRoute,
944            SipHeader::SessionExpires,
945            SipHeader::SessionId,
946            SipHeader::SipEtag,
947            SipHeader::SipIfMatch,
948            SipHeader::Subject,
949            SipHeader::SubscriptionState,
950            SipHeader::Supported,
951            SipHeader::SuppressIfMatch,
952            SipHeader::TargetDialog,
953            SipHeader::Timestamp,
954            SipHeader::To,
955            SipHeader::TriggerConsent,
956            SipHeader::Unsupported,
957            SipHeader::UserAgent,
958            SipHeader::UserToUser,
959            SipHeader::Via,
960            SipHeader::Warning,
961            SipHeader::WwwAuthenticate,
962        ];
963        for v in variants {
964            let wire = v.to_string();
965            let parsed: SipHeader = wire
966                .parse()
967                .unwrap();
968            assert_eq!(parsed, v, "round-trip failed for {wire}");
969        }
970    }
971
972    fn headers_with(pairs: &[(&str, &str)]) -> HashMap<String, String> {
973        pairs
974            .iter()
975            .map(|(k, v)| (k.to_string(), v.to_string()))
976            .collect()
977    }
978
979    #[test]
980    fn sip_header_by_enum() {
981        let h = headers_with(&[("Call-Info", "<urn:x>;purpose=icon")]);
982        assert_eq!(
983            h.sip_header(SipHeader::CallInfo),
984            Some("<urn:x>;purpose=icon")
985        );
986    }
987
988    #[test]
989    fn call_info_raw_lookup() {
990        let h = headers_with(&[(
991            "Call-Info",
992            "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
993        )]);
994        assert_eq!(
995            h.sip_header(SipHeader::CallInfo),
996            Some("<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId")
997        );
998    }
999
1000    #[test]
1001    fn call_info_typed() {
1002        let h = headers_with(&[(
1003            "Call-Info",
1004            "<urn:emergency:uid:callid:test:bcf.example.com>;purpose=emergency-CallId",
1005        )]);
1006        let ci = h
1007            .call_info()
1008            .unwrap()
1009            .unwrap();
1010        assert_eq!(ci.len(), 1);
1011        assert_eq!(ci.entries()[0].purpose(), Some("emergency-CallId"));
1012    }
1013
1014    #[test]
1015    fn call_info_absent() {
1016        let h = headers_with(&[]);
1017        assert_eq!(
1018            h.call_info()
1019                .unwrap(),
1020            None
1021        );
1022    }
1023
1024    #[test]
1025    fn p_asserted_identity_typed() {
1026        let h = headers_with(&[(
1027            "P-Asserted-Identity",
1028            r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>"#,
1029        )]);
1030        let pais = h
1031            .p_asserted_identity()
1032            .unwrap();
1033        assert_eq!(pais.len(), 1);
1034        assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
1035    }
1036
1037    #[test]
1038    fn p_asserted_identity_multi_value() {
1039        let h = headers_with(&[(
1040            "P-Asserted-Identity",
1041            r#""EXAMPLE CO" <sip:+15551234567@198.51.100.1>, <tel:+15551234567>"#,
1042        )]);
1043        let pais = h
1044            .p_asserted_identity()
1045            .unwrap();
1046        assert_eq!(pais.len(), 2);
1047        assert_eq!(pais[0].display_name(), Some("EXAMPLE CO"));
1048        assert!(pais[1]
1049            .uri()
1050            .to_string()
1051            .contains("+15551234567"));
1052    }
1053
1054    #[test]
1055    fn p_asserted_identity_absent() {
1056        let h = headers_with(&[]);
1057        assert!(h
1058            .p_asserted_identity()
1059            .unwrap()
1060            .is_empty());
1061    }
1062
1063    #[test]
1064    fn history_info_raw_lookup() {
1065        let h = headers_with(&[(
1066            "History-Info",
1067            "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
1068        )]);
1069        assert!(h
1070            .sip_header(SipHeader::HistoryInfo)
1071            .unwrap()
1072            .contains("esrp.example.com"));
1073    }
1074
1075    #[test]
1076    fn history_info_typed() {
1077        let h = headers_with(&[(
1078            "History-Info",
1079            "<sip:alice@esrp.example.com>;index=1,<sip:sos@psap.example.com>;index=1.1",
1080        )]);
1081        let hi = h
1082            .history_info()
1083            .unwrap()
1084            .unwrap();
1085        assert_eq!(hi.len(), 2);
1086        assert_eq!(hi.entries()[0].index(), Some("1"));
1087        assert_eq!(hi.entries()[1].index(), Some("1.1"));
1088    }
1089
1090    #[test]
1091    fn history_info_absent() {
1092        let h = headers_with(&[]);
1093        assert_eq!(
1094            h.history_info()
1095                .unwrap(),
1096            None
1097        );
1098    }
1099
1100    #[test]
1101    fn sip_header_all_str_default() {
1102        let h = headers_with(&[("Via", "SIP/2.0/UDP host1")]);
1103        let all = h.sip_header_all(SipHeader::Via);
1104        assert_eq!(all.len(), 1);
1105        assert_eq!(all[0], "SIP/2.0/UDP host1");
1106    }
1107
1108    #[test]
1109    fn sip_header_all_str_absent() {
1110        let h = headers_with(&[]);
1111        assert!(h
1112            .sip_header_all(SipHeader::Via)
1113            .is_empty());
1114    }
1115
1116    #[test]
1117    fn hashmap_vec_impl() {
1118        let mut h: HashMap<String, Vec<String>> = HashMap::new();
1119        h.insert(
1120            "Via".into(),
1121            vec!["SIP/2.0/UDP host1".into(), "SIP/2.0/UDP host2".into()],
1122        );
1123        assert_eq!(h.sip_header_str("Via"), Some("SIP/2.0/UDP host1"));
1124        let all = h.sip_header_all_str("Via");
1125        assert_eq!(all.len(), 2);
1126        assert_eq!(all[0], "SIP/2.0/UDP host1");
1127        assert_eq!(all[1], "SIP/2.0/UDP host2");
1128    }
1129
1130    #[test]
1131    fn extract_from_sip_message() {
1132        let msg = concat!(
1133            "INVITE sip:bob@host SIP/2.0\r\n",
1134            "Call-Info: <urn:emergency:uid:callid:abc>;purpose=emergency-CallId\r\n",
1135            "History-Info: <sip:esrp@example.com>;index=1\r\n",
1136            "P-Asserted-Identity: \"Corp\" <sip:+15551234567@198.51.100.1>\r\n",
1137            "\r\n",
1138        );
1139        let ci = SipHeader::CallInfo.extract_from(msg);
1140        assert_eq!(ci.len(), 1);
1141        assert_eq!(
1142            ci[0],
1143            "<urn:emergency:uid:callid:abc>;purpose=emergency-CallId"
1144        );
1145
1146        let hi = SipHeader::HistoryInfo.extract_from(msg);
1147        assert_eq!(hi.len(), 1);
1148        assert_eq!(hi[0], "<sip:esrp@example.com>;index=1");
1149
1150        let pai = SipHeader::PAssertedIdentity.extract_from(msg);
1151        assert_eq!(pai.len(), 1);
1152        assert_eq!(pai[0], "\"Corp\" <sip:+15551234567@198.51.100.1>");
1153    }
1154
1155    #[test]
1156    fn extract_from_missing() {
1157        let msg = concat!(
1158            "INVITE sip:bob@host SIP/2.0\r\n",
1159            "From: Alice <sip:alice@host>\r\n",
1160            "\r\n",
1161        );
1162        assert!(SipHeader::CallInfo
1163            .extract_from(msg)
1164            .is_empty());
1165        assert!(SipHeader::PAssertedIdentity
1166            .extract_from(msg)
1167            .is_empty());
1168    }
1169
1170    #[test]
1171    fn missing_headers_return_none() {
1172        let h = headers_with(&[]);
1173        assert_eq!(h.sip_header(SipHeader::CallInfo), None);
1174        assert_eq!(
1175            h.call_info()
1176                .unwrap(),
1177            None
1178        );
1179        assert_eq!(h.sip_header(SipHeader::HistoryInfo), None);
1180        assert_eq!(
1181            h.history_info()
1182                .unwrap(),
1183            None
1184        );
1185        assert_eq!(h.sip_header(SipHeader::PAssertedIdentity), None);
1186        assert!(h
1187            .p_asserted_identity()
1188            .unwrap()
1189            .is_empty());
1190    }
1191
1192    #[test]
1193    fn route_accessor() {
1194        let h = headers_with(&[(
1195            "Route",
1196            "<sip:proxy1.example.com;lr>, <sip:proxy2.example.com;lr>",
1197        )]);
1198        let routes = h
1199            .route()
1200            .unwrap();
1201        assert_eq!(routes.len(), 2);
1202        assert!(routes[0]
1203            .uri()
1204            .to_string()
1205            .contains("proxy1"));
1206        assert!(routes[1]
1207            .uri()
1208            .to_string()
1209            .contains("proxy2"));
1210    }
1211
1212    #[test]
1213    fn route_absent() {
1214        let h = headers_with(&[]);
1215        assert!(h
1216            .route()
1217            .unwrap()
1218            .is_empty());
1219    }
1220
1221    #[test]
1222    fn record_route_accessor() {
1223        let h = headers_with(&[("Record-Route", "<sip:ss1.example.com;lr>")]);
1224        let rr = h
1225            .record_route()
1226            .unwrap();
1227        assert_eq!(rr.len(), 1);
1228    }
1229
1230    #[test]
1231    fn allow_accessor() {
1232        let h = headers_with(&[("Allow", "INVITE, ACK, OPTIONS, BYE")]);
1233        let methods = h.allow();
1234        assert_eq!(methods, vec!["INVITE", "ACK", "OPTIONS", "BYE"]);
1235    }
1236
1237    #[test]
1238    fn allow_absent() {
1239        let h = headers_with(&[]);
1240        assert!(h
1241            .allow()
1242            .is_empty());
1243    }
1244
1245    #[test]
1246    fn supported_accessor() {
1247        let h = headers_with(&[("Supported", "100rel, timer")]);
1248        let opts = h.supported();
1249        assert_eq!(opts, vec!["100rel", "timer"]);
1250    }
1251
1252    #[test]
1253    fn require_header_accessor() {
1254        let h = headers_with(&[("Require", "100rel")]);
1255        assert_eq!(h.require_header(), vec!["100rel"]);
1256    }
1257
1258    #[test]
1259    fn alert_info_accessor() {
1260        let h = headers_with(&[("Alert-Info", "<http://www.example.com/sounds/moo.wav>")]);
1261        let ai = h
1262            .alert_info()
1263            .unwrap()
1264            .unwrap();
1265        assert_eq!(ai.len(), 1);
1266        assert!(ai.entries()[0]
1267            .data
1268            .contains("moo.wav"));
1269    }
1270
1271    #[test]
1272    fn error_info_accessor() {
1273        let h = headers_with(&[("Error-Info", "<sip:not-in-service@example.com>")]);
1274        let ei = h
1275            .error_info()
1276            .unwrap()
1277            .unwrap();
1278        assert_eq!(ei.len(), 1);
1279    }
1280
1281    #[test]
1282    fn p_preferred_identity_accessor() {
1283        let h = headers_with(&[(
1284            "P-Preferred-Identity",
1285            r#""User" <sip:+15551234567@198.51.100.1>"#,
1286        )]);
1287        let ppi = h
1288            .p_preferred_identity()
1289            .unwrap();
1290        assert_eq!(ppi.len(), 1);
1291        assert_eq!(ppi[0].display_name(), Some("User"));
1292    }
1293
1294    #[test]
1295    fn content_encoding_accessor() {
1296        let h = headers_with(&[("Content-Encoding", "gzip")]);
1297        assert_eq!(h.content_encoding(), vec!["gzip"]);
1298    }
1299
1300    #[test]
1301    fn contact_accessor() {
1302        let h = headers_with(&[("Contact", "<sip:alice@198.51.100.1>")]);
1303        let contacts = h
1304            .contact()
1305            .unwrap();
1306        assert_eq!(contacts.len(), 1);
1307        assert!(matches!(&contacts[0], ContactValue::Addr(_)));
1308    }
1309
1310    #[test]
1311    fn contact_wildcard() {
1312        let h = headers_with(&[("Contact", "*")]);
1313        let contacts = h
1314            .contact()
1315            .unwrap();
1316        assert_eq!(contacts.len(), 1);
1317        assert!(matches!(contacts[0], ContactValue::Wildcard));
1318    }
1319
1320    #[test]
1321    fn contact_absent() {
1322        let h = headers_with(&[]);
1323        assert!(h
1324            .contact()
1325            .unwrap()
1326            .is_empty());
1327    }
1328
1329    #[test]
1330    fn in_reply_to_accessor() {
1331        let h = headers_with(&[("In-Reply-To", "call1@example.com, call2@example.com")]);
1332        let calls = h.in_reply_to();
1333        assert_eq!(calls.len(), 2);
1334        assert_eq!(calls[0], "call1@example.com");
1335    }
1336
1337    #[test]
1338    fn via_accessor() {
1339        let h = headers_with(&[("Via", "SIP/2.0/UDP 198.51.100.1:5060;branch=z9hG4bK776")]);
1340        let via = h
1341            .via()
1342            .unwrap()
1343            .unwrap();
1344        assert_eq!(via.len(), 1);
1345        assert_eq!(via.entries()[0].transport(), "UDP");
1346        assert_eq!(via.entries()[0].host(), "198.51.100.1");
1347    }
1348
1349    #[test]
1350    fn authorization_accessor() {
1351        let h = headers_with(&[(
1352            "Authorization",
1353            "Digest username=\"alice\", realm=\"example.com\", nonce=\"abc123\"",
1354        )]);
1355        let auth = h
1356            .authorization()
1357            .unwrap();
1358        assert_eq!(auth.len(), 1);
1359        assert_eq!(auth[0].scheme(), "Digest");
1360        assert_eq!(auth[0].username(), Some("alice"));
1361        assert_eq!(auth[0].realm(), Some("example.com"));
1362    }
1363
1364    #[test]
1365    fn www_authenticate_accessor() {
1366        let h = headers_with(&[(
1367            "WWW-Authenticate",
1368            "Digest realm=\"example.com\", nonce=\"xyz789\"",
1369        )]);
1370        let challenges = h
1371            .www_authenticate()
1372            .unwrap();
1373        assert_eq!(challenges.len(), 1);
1374        assert_eq!(challenges[0].realm(), Some("example.com"));
1375    }
1376
1377    #[test]
1378    fn warning_accessor() {
1379        let h = headers_with(&[(
1380            "Warning",
1381            "301 198.51.100.1 \"Incompatible network protocol\"",
1382        )]);
1383        let w = h
1384            .warning()
1385            .unwrap()
1386            .unwrap();
1387        assert_eq!(w.len(), 1);
1388        assert_eq!(w.entries()[0].code(), 301);
1389    }
1390
1391    #[test]
1392    fn security_client_accessor() {
1393        let h = headers_with(&[("Security-Client", "tls;q=0.2, digest;d-qop=auth;q=0.1")]);
1394        let sec = h
1395            .security_client()
1396            .unwrap()
1397            .unwrap();
1398        assert_eq!(sec.len(), 2);
1399        assert_eq!(sec.entries()[0].mechanism(), "tls");
1400    }
1401
1402    #[test]
1403    fn accept_accessor() {
1404        let h = headers_with(&[("Accept", "application/sdp, application/pidf+xml;q=0.5")]);
1405        let accept = h
1406            .accept()
1407            .unwrap()
1408            .unwrap();
1409        assert_eq!(accept.len(), 2);
1410        assert_eq!(accept.entries()[0].media_range(), "application/sdp");
1411    }
1412
1413    #[test]
1414    fn accept_encoding_accessor() {
1415        let h = headers_with(&[("Accept-Encoding", "gzip;q=1.0, identity;q=0.5")]);
1416        let ae = h
1417            .accept_encoding()
1418            .unwrap()
1419            .unwrap();
1420        assert_eq!(ae.len(), 2);
1421        assert_eq!(ae.entries()[0].encoding(), "gzip");
1422    }
1423
1424    #[test]
1425    fn accept_language_accessor() {
1426        let h = headers_with(&[("Accept-Language", "en;q=0.9, fr;q=0.8")]);
1427        let al = h
1428            .accept_language()
1429            .unwrap()
1430            .unwrap();
1431        assert_eq!(al.len(), 2);
1432        assert_eq!(al.entries()[0].language(), "en");
1433    }
1434}
1435
1436#[cfg(test)]
1437mod compact_form_tests {
1438    use super::*;
1439
1440    #[test]
1441    fn from_compact_known() {
1442        assert_eq!(SipHeader::from_compact(b'f'), Some(SipHeader::From));
1443        assert_eq!(SipHeader::from_compact(b'F'), Some(SipHeader::From));
1444        assert_eq!(SipHeader::from_compact(b'v'), Some(SipHeader::Via));
1445        assert_eq!(SipHeader::from_compact(b'i'), Some(SipHeader::CallId));
1446        assert_eq!(SipHeader::from_compact(b'm'), Some(SipHeader::Contact));
1447        assert_eq!(SipHeader::from_compact(b't'), Some(SipHeader::To));
1448        assert_eq!(SipHeader::from_compact(b'c'), Some(SipHeader::ContentType));
1449    }
1450
1451    #[test]
1452    fn from_compact_unknown() {
1453        assert_eq!(SipHeader::from_compact(b'z'), None);
1454        assert_eq!(SipHeader::from_compact(b'g'), None);
1455    }
1456
1457    #[test]
1458    fn compact_form_roundtrip() {
1459        assert_eq!(SipHeader::From.compact_form(), Some('f'));
1460        assert_eq!(SipHeader::Via.compact_form(), Some('v'));
1461        assert_eq!(SipHeader::CallId.compact_form(), Some('i'));
1462        assert_eq!(SipHeader::Contact.compact_form(), Some('m'));
1463    }
1464
1465    #[test]
1466    fn compact_form_absent() {
1467        assert_eq!(SipHeader::HistoryInfo.compact_form(), None);
1468        assert_eq!(SipHeader::PAssertedIdentity.compact_form(), None);
1469    }
1470
1471    #[test]
1472    fn parse_name_compact() {
1473        assert_eq!(SipHeader::parse_name("f"), Ok(SipHeader::From));
1474        assert_eq!(SipHeader::parse_name("F"), Ok(SipHeader::From));
1475        assert_eq!(SipHeader::parse_name("v"), Ok(SipHeader::Via));
1476    }
1477
1478    #[test]
1479    fn parse_name_full() {
1480        assert_eq!(SipHeader::parse_name("From"), Ok(SipHeader::From));
1481        assert_eq!(SipHeader::parse_name("Via"), Ok(SipHeader::Via));
1482    }
1483
1484    #[test]
1485    fn parse_name_unknown() {
1486        assert!(SipHeader::parse_name("X-Custom").is_err());
1487    }
1488
1489    #[test]
1490    fn all_compact_forms_resolve() {
1491        let expected = [
1492            ('a', SipHeader::AcceptContact),
1493            ('b', SipHeader::ReferredBy),
1494            ('c', SipHeader::ContentType),
1495            ('d', SipHeader::RequestDisposition),
1496            ('e', SipHeader::ContentEncoding),
1497            ('f', SipHeader::From),
1498            ('i', SipHeader::CallId),
1499            ('j', SipHeader::RejectContact),
1500            ('k', SipHeader::Supported),
1501            ('l', SipHeader::ContentLength),
1502            ('m', SipHeader::Contact),
1503            ('n', SipHeader::IdentityInfo),
1504            ('o', SipHeader::Event),
1505            ('r', SipHeader::ReferTo),
1506            ('s', SipHeader::Subject),
1507            ('t', SipHeader::To),
1508            ('u', SipHeader::AllowEvents),
1509            ('v', SipHeader::Via),
1510            ('x', SipHeader::SessionExpires),
1511            ('y', SipHeader::Identity),
1512        ];
1513        for (ch, header) in expected {
1514            assert_eq!(
1515                SipHeader::from_compact(ch as u8),
1516                Some(header),
1517                "compact form '{ch}' failed"
1518            );
1519            assert_eq!(
1520                header.compact_form(),
1521                Some(ch),
1522                "compact_form() for {} failed",
1523                header
1524            );
1525        }
1526    }
1527}
1528
1529#[cfg(test)]
1530mod multi_valued_tests {
1531    use super::*;
1532
1533    #[test]
1534    fn rfc3261_multi_valued_headers() {
1535        assert!(SipHeader::Via.is_multi_valued());
1536        assert!(SipHeader::Route.is_multi_valued());
1537        assert!(SipHeader::RecordRoute.is_multi_valued());
1538        assert!(SipHeader::Contact.is_multi_valued());
1539        assert!(SipHeader::Allow.is_multi_valued());
1540        assert!(SipHeader::Supported.is_multi_valued());
1541        assert!(SipHeader::Require.is_multi_valued());
1542        assert!(SipHeader::ProxyRequire.is_multi_valued());
1543        assert!(SipHeader::Unsupported.is_multi_valued());
1544        assert!(SipHeader::Authorization.is_multi_valued());
1545        assert!(SipHeader::ProxyAuthorization.is_multi_valued());
1546        assert!(SipHeader::WwwAuthenticate.is_multi_valued());
1547        assert!(SipHeader::ProxyAuthenticate.is_multi_valued());
1548        assert!(SipHeader::Warning.is_multi_valued());
1549        assert!(SipHeader::ErrorInfo.is_multi_valued());
1550        assert!(SipHeader::CallInfo.is_multi_valued());
1551        assert!(SipHeader::AlertInfo.is_multi_valued());
1552        assert!(SipHeader::Accept.is_multi_valued());
1553        assert!(SipHeader::AcceptEncoding.is_multi_valued());
1554        assert!(SipHeader::AcceptLanguage.is_multi_valued());
1555        assert!(SipHeader::ContentEncoding.is_multi_valued());
1556        assert!(SipHeader::ContentLanguage.is_multi_valued());
1557        assert!(SipHeader::InReplyTo.is_multi_valued());
1558    }
1559
1560    #[test]
1561    fn extension_multi_valued_headers() {
1562        assert!(SipHeader::PAssertedIdentity.is_multi_valued());
1563        assert!(SipHeader::PPreferredIdentity.is_multi_valued());
1564        assert!(SipHeader::AllowEvents.is_multi_valued());
1565        assert!(SipHeader::SecurityClient.is_multi_valued());
1566        assert!(SipHeader::SecurityServer.is_multi_valued());
1567        assert!(SipHeader::SecurityVerify.is_multi_valued());
1568        assert!(SipHeader::Path.is_multi_valued());
1569        assert!(SipHeader::ServiceRoute.is_multi_valued());
1570        assert!(SipHeader::HistoryInfo.is_multi_valued());
1571    }
1572
1573    #[test]
1574    fn single_valued_headers() {
1575        assert!(!SipHeader::From.is_multi_valued());
1576        assert!(!SipHeader::To.is_multi_valued());
1577        assert!(!SipHeader::CallId.is_multi_valued());
1578        assert!(!SipHeader::Cseq.is_multi_valued());
1579        assert!(!SipHeader::MaxForwards.is_multi_valued());
1580        assert!(!SipHeader::ContentType.is_multi_valued());
1581        assert!(!SipHeader::ContentLength.is_multi_valued());
1582        assert!(!SipHeader::Expires.is_multi_valued());
1583        assert!(!SipHeader::Date.is_multi_valued());
1584        assert!(!SipHeader::Subject.is_multi_valued());
1585        assert!(!SipHeader::ReplyTo.is_multi_valued());
1586        assert!(!SipHeader::Server.is_multi_valued());
1587        assert!(!SipHeader::UserAgent.is_multi_valued());
1588    }
1589
1590    #[test]
1591    #[cfg(feature = "draft")]
1592    fn draft_multi_valued_headers() {
1593        assert!(SipHeader::Diversion.is_multi_valued());
1594        assert!(SipHeader::RemotePartyId.is_multi_valued());
1595    }
1596
1597    #[test]
1598    #[cfg(feature = "draft")]
1599    fn draft_parse_roundtrip() {
1600        let d: SipHeader = "Diversion"
1601            .parse()
1602            .unwrap();
1603        assert_eq!(d, SipHeader::Diversion);
1604        assert_eq!(d.to_string(), "Diversion");
1605
1606        let r: SipHeader = "remote-party-id"
1607            .parse()
1608            .unwrap();
1609        assert_eq!(r, SipHeader::RemotePartyId);
1610        assert_eq!(r.to_string(), "Remote-Party-ID");
1611    }
1612}
1613
1614#[cfg(test)]
1615mod special_case_tests {
1616    use super::*;
1617
1618    #[test]
1619    fn cseq_variants() {
1620        assert_eq!("CSeq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1621        assert_eq!("cseq".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1622        assert_eq!("CSEQ".parse::<SipHeader>(), Ok(SipHeader::Cseq));
1623        assert_eq!(SipHeader::Cseq.to_string(), "CSeq");
1624    }
1625
1626    #[test]
1627    fn www_authenticate_variants() {
1628        assert_eq!(
1629            "WWW-Authenticate".parse::<SipHeader>(),
1630            Ok(SipHeader::WwwAuthenticate)
1631        );
1632        assert_eq!(
1633            "www-authenticate".parse::<SipHeader>(),
1634            Ok(SipHeader::WwwAuthenticate)
1635        );
1636        assert_eq!(SipHeader::WwwAuthenticate.to_string(), "WWW-Authenticate");
1637    }
1638
1639    #[test]
1640    fn rack_rseq_variants() {
1641        assert_eq!("RAck".parse::<SipHeader>(), Ok(SipHeader::Rack));
1642        assert_eq!("rack".parse::<SipHeader>(), Ok(SipHeader::Rack));
1643        assert_eq!(SipHeader::Rack.to_string(), "RAck");
1644
1645        assert_eq!("RSeq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1646        assert_eq!("rseq".parse::<SipHeader>(), Ok(SipHeader::Rseq));
1647        assert_eq!(SipHeader::Rseq.to_string(), "RSeq");
1648    }
1649
1650    #[test]
1651    fn user_to_user_variants() {
1652        assert_eq!(
1653            "User-to-User".parse::<SipHeader>(),
1654            Ok(SipHeader::UserToUser)
1655        );
1656        assert_eq!(
1657            "user-to-user".parse::<SipHeader>(),
1658            Ok(SipHeader::UserToUser)
1659        );
1660        assert_eq!(SipHeader::UserToUser.to_string(), "User-to-User");
1661    }
1662
1663    #[test]
1664    fn p_header_variants() {
1665        assert_eq!(
1666            "P-DCS-Trace-Party-ID".parse::<SipHeader>(),
1667            Ok(SipHeader::PDcsTracePartyId)
1668        );
1669        assert_eq!(
1670            "p-dcs-trace-party-id".parse::<SipHeader>(),
1671            Ok(SipHeader::PDcsTracePartyId)
1672        );
1673        assert_eq!(
1674            SipHeader::PDcsTracePartyId.to_string(),
1675            "P-DCS-Trace-Party-ID"
1676        );
1677    }
1678}