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