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