cookie/
lib.rs

1//! HTTP cookie parsing and cookie jar management.
2//!
3//! This crates provides the [`Cookie`] type, representing an HTTP cookie, and
4//! the [`CookieJar`] type, which manages a collection of cookies for session
5//! management, recording changes as they are made, and optional automatic
6//! cookie encryption and signing.
7//!
8//! # Usage
9//!
10//! Add the following to the `[dependencies]` section of your `Cargo.toml`:
11//!
12//! ```toml
13//! cookie = "0.18"
14//! ```
15//!
16//! # Features
17//!
18//! This crate exposes several features, all of which are disabled by default:
19//!
20//! * **`percent-encode`**
21//!
22//!   Enables _percent encoding and decoding_ of names and values in cookies.
23//!
24//!   When this feature is enabled, the [`Cookie::encoded()`] and
25//!   [`Cookie::parse_encoded()`] methods are available. The `encoded` method
26//!   returns a wrapper around a `Cookie` whose `Display` implementation
27//!   percent-encodes the name and value of the cookie. The `parse_encoded`
28//!   method percent-decodes the name and value of a `Cookie` during parsing.
29//!
30//! * **`signed`**
31//!
32//!   Enables _signed_ cookies via [`CookieJar::signed()`].
33//!
34//!   When this feature is enabled, the [`CookieJar::signed()`] method,
35//!   [`SignedJar`] type, and [`Key`] type are available. The jar acts as "child
36//!   jar"; operations on the jar automatically sign and verify cookies as they
37//!   are added and retrieved from the parent jar.
38//!
39//! * **`private`**
40//!
41//!   Enables _private_ (authenticated, encrypted) cookies via
42//!   [`CookieJar::private()`].
43//!
44//!   When this feature is enabled, the [`CookieJar::private()`] method,
45//!   [`PrivateJar`] type, and [`Key`] type are available. The jar acts as "child
46//!   jar"; operations on the jar automatically encrypt and decrypt/authenticate
47//!   cookies as they are added and retrieved from the parent jar.
48//!
49//! * **`key-expansion`**
50//!
51//!   Enables _key expansion_ or _key derivation_ via [`Key::derive_from()`].
52//!
53//!   When this feature is enabled, and either `signed` or `private` are _also_
54//!   enabled, the [`Key::derive_from()`] method is available. The method can be
55//!   used to derive a `Key` structure appropriate for use with signed and
56//!   private jars from cryptographically valid key material that is shorter in
57//!   length than the full key.
58//!
59//! * **`secure`**
60//!
61//!   A meta-feature that simultaneously enables `signed`, `private`, and
62//!   `key-expansion`.
63//!
64//! You can enable features via `Cargo.toml`:
65//!
66//! ```toml
67//! [dependencies.cookie]
68//! features = ["secure", "percent-encode"]
69//! ```
70
71#![cfg_attr(all(nightly, doc), feature(doc_cfg))]
72
73#![deny(missing_docs)]
74
75pub use time;
76
77mod builder;
78mod parse;
79mod jar;
80mod delta;
81mod same_site;
82mod expiration;
83
84/// Implementation of [HTTP RFC6265 draft] cookie prefixes.
85///
86/// [HTTP RFC6265 draft]:
87/// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#name-cookie-name-prefixes
88pub mod prefix;
89
90#[cfg(any(feature = "private", feature = "signed"))] #[macro_use] mod secure;
91#[cfg(any(feature = "private", feature = "signed"))] pub use secure::*;
92
93use std::borrow::Cow;
94use std::fmt;
95use std::str::FromStr;
96
97#[allow(unused_imports, deprecated)]
98use std::ascii::AsciiExt;
99
100use time::{Duration, OffsetDateTime, UtcOffset, macros::datetime};
101
102use crate::parse::parse_cookie;
103pub use crate::parse::ParseError;
104pub use crate::builder::CookieBuilder;
105pub use crate::jar::{CookieJar, Delta, Iter};
106pub use crate::same_site::*;
107pub use crate::expiration::*;
108
109#[derive(Debug, Clone)]
110enum CookieStr<'c> {
111    /// An string derived from indexes (start, end).
112    Indexed(usize, usize),
113    /// A string derived from a concrete string.
114    Concrete(Cow<'c, str>),
115}
116
117impl<'c> CookieStr<'c> {
118    /// Creates an indexed `CookieStr` that holds the start and end indices of
119    /// `needle` inside of `haystack`, if `needle` is a substring of `haystack`.
120    /// Otherwise returns `None`.
121    ///
122    /// The `needle` can later be retrieved via `to_str()`.
123    fn indexed(needle: &str, haystack: &str) -> Option<CookieStr<'static>> {
124        let haystack_start = haystack.as_ptr() as usize;
125        let needle_start = needle.as_ptr() as usize;
126
127        if needle_start < haystack_start {
128            return None;
129        }
130
131        if (needle_start + needle.len()) > (haystack_start + haystack.len()) {
132            return None;
133        }
134
135        let start = needle_start - haystack_start;
136        let end = start + needle.len();
137        Some(CookieStr::Indexed(start, end))
138    }
139
140    /// Retrieves the string `self` corresponds to. If `self` is derived from
141    /// indices, the corresponding subslice of `string` is returned. Otherwise,
142    /// the concrete string is returned.
143    ///
144    /// # Panics
145    ///
146    /// Panics if `self` is an indexed string and `string` is None.
147    fn to_str<'s>(&'s self, string: Option<&'s Cow<str>>) -> &'s str {
148        match *self {
149            CookieStr::Indexed(i, j) => {
150                let s = string.expect("`Some` base string must exist when \
151                    converting indexed str to str! (This is a module invariant.)");
152                &s[i..j]
153            },
154            CookieStr::Concrete(ref cstr) => &*cstr,
155        }
156    }
157
158    #[allow(clippy::ptr_arg)]
159    fn to_raw_str<'s, 'b: 's>(&'s self, string: &'s Cow<'b, str>) -> Option<&'b str> {
160        match *self {
161            CookieStr::Indexed(i, j) => {
162                match *string {
163                    Cow::Borrowed(s) => Some(&s[i..j]),
164                    Cow::Owned(_) => None,
165                }
166            },
167            CookieStr::Concrete(_) => None,
168        }
169    }
170
171    fn into_owned(self) -> CookieStr<'static> {
172        use crate::CookieStr::*;
173
174        match self {
175            Indexed(a, b) => Indexed(a, b),
176            Concrete(Cow::Owned(c)) => Concrete(Cow::Owned(c)),
177            Concrete(Cow::Borrowed(c)) => Concrete(Cow::Owned(c.into())),
178        }
179    }
180}
181
182/// Representation of an HTTP cookie.
183///
184/// ## Constructing a `Cookie`
185///
186/// To construct a cookie with only a name/value, use [`Cookie::new()`]:
187///
188/// ```rust
189/// use cookie::Cookie;
190///
191/// let cookie = Cookie::new("name", "value");
192/// assert_eq!(cookie.to_string(), "name=value");
193/// ```
194///
195/// ## Building a `Cookie`
196///
197/// To construct more elaborate cookies, use [`Cookie::build()`] and
198/// [`CookieBuilder`] methods. `Cookie::build()` accepts any type that
199/// implements `T: Into<Cookie>`. See [`Cookie::build()`] for details.
200///
201/// ```rust
202/// use cookie::Cookie;
203///
204/// let cookie = Cookie::build(("name", "value"))
205///     .domain("www.rust-lang.org")
206///     .path("/")
207///     .secure(true)
208///     .http_only(true);
209///
210/// # let mut jar = cookie::CookieJar::new();
211/// jar.add(cookie);
212/// jar.remove(Cookie::build("name").path("/"));
213/// ```
214#[derive(Debug, Clone)]
215pub struct Cookie<'c> {
216    /// Storage for the cookie string. Only used if this structure was derived
217    /// from a string that was subsequently parsed.
218    cookie_string: Option<Cow<'c, str>>,
219    /// The cookie's name.
220    name: CookieStr<'c>,
221    /// The cookie's value.
222    value: CookieStr<'c>,
223    /// The cookie's expiration, if any.
224    expires: Option<Expiration>,
225    /// The cookie's maximum age, if any.
226    max_age: Option<Duration>,
227    /// The cookie's domain, if any.
228    domain: Option<CookieStr<'c>>,
229    /// The cookie's path domain, if any.
230    path: Option<CookieStr<'c>>,
231    /// Whether this cookie was marked Secure.
232    secure: Option<bool>,
233    /// Whether this cookie was marked HttpOnly.
234    http_only: Option<bool>,
235    /// The draft `SameSite` attribute.
236    same_site: Option<SameSite>,
237    /// The draft `Partitioned` attribute.
238    partitioned: Option<bool>,
239}
240
241impl<'c> Cookie<'c> {
242    /// Creates a new `Cookie` with the given name and value.
243    ///
244    /// # Example
245    ///
246    /// ```rust
247    /// use cookie::Cookie;
248    ///
249    /// let cookie = Cookie::new("name", "value");
250    /// assert_eq!(cookie.name_value(), ("name", "value"));
251    ///
252    /// // This is equivalent to `from` with a `(name, value)` tuple:
253    /// let cookie = Cookie::from(("name", "value"));
254    /// assert_eq!(cookie.name_value(), ("name", "value"));
255    /// ```
256    pub fn new<N, V>(name: N, value: V) -> Self
257        where N: Into<Cow<'c, str>>,
258              V: Into<Cow<'c, str>>
259    {
260        Cookie {
261            cookie_string: None,
262            name: CookieStr::Concrete(name.into()),
263            value: CookieStr::Concrete(value.into()),
264            expires: None,
265            max_age: None,
266            domain: None,
267            path: None,
268            secure: None,
269            http_only: None,
270            same_site: None,
271            partitioned: None,
272        }
273    }
274
275    /// Creates a new `Cookie` with the given name and an empty value.
276    ///
277    /// # Example
278    ///
279    /// ```rust
280    /// use cookie::Cookie;
281    ///
282    /// let cookie = Cookie::named("name");
283    /// assert_eq!(cookie.name(), "name");
284    /// assert!(cookie.value().is_empty());
285    ///
286    /// // This is equivalent to `from` with `"name`:
287    /// let cookie = Cookie::from("name");
288    /// assert_eq!(cookie.name(), "name");
289    /// assert!(cookie.value().is_empty());
290    /// ```
291    #[deprecated(since = "0.18.0", note = "use `Cookie::build(name)` or `Cookie::from(name)`")]
292    pub fn named<N>(name: N) -> Cookie<'c>
293        where N: Into<Cow<'c, str>>
294    {
295        Cookie::new(name, "")
296    }
297
298    /// Creates a new [`CookieBuilder`] starting from a `base` cookie.
299    ///
300    /// Any type that implements `T: Into<Cookie>` can be used as a `base`:
301    ///
302    /// | `Into<Cookie>` Type              | Example                | Equivalent To              |
303    /// |----------------------------------|------------------------|----------------------------|
304    /// | `(K, V)`, `K, V: Into<Cow<str>>` | `("name", "value")`    | `Cookie::new(name, value)` |
305    /// | `&str`, `String`, `Cow<str>`     | `"name"`               | `Cookie::new(name, "")`    |
306    /// | [`CookieBuilder`]                | `Cookie::build("foo")` | [`CookieBuilder::build()`] |
307    ///
308    /// # Example
309    ///
310    /// ```
311    /// use cookie::Cookie;
312    ///
313    /// // Use `(K, V)` as the base, setting a name and value.
314    /// let b1 = Cookie::build(("name", "value")).path("/");
315    /// assert_eq!(b1.inner().name_value(), ("name", "value"));
316    /// assert_eq!(b1.inner().path(), Some("/"));
317    ///
318    /// // Use `&str` as the base, setting a name and empty value.
319    /// let b2 = Cookie::build(("name"));
320    /// assert_eq!(b2.inner().name_value(), ("name", ""));
321    ///
322    /// // Use `CookieBuilder` as the base, inheriting all properties.
323    /// let b3 = Cookie::build(b1);
324    /// assert_eq!(b3.inner().name_value(), ("name", "value"));
325    /// assert_eq!(b3.inner().path(), Some("/"));
326    /// ```
327    pub fn build<C: Into<Cookie<'c>>>(base: C) -> CookieBuilder<'c> {
328        CookieBuilder::from(base.into())
329    }
330
331    /// Parses a `Cookie` from the given HTTP cookie header value string. Does
332    /// not perform any percent-decoding.
333    ///
334    /// # Example
335    ///
336    /// ```
337    /// use cookie::Cookie;
338    ///
339    /// let c = Cookie::parse("foo=bar%20baz; HttpOnly").unwrap();
340    /// assert_eq!(c.name_value(), ("foo", "bar%20baz"));
341    /// assert_eq!(c.http_only(), Some(true));
342    /// assert_eq!(c.secure(), None);
343    /// ```
344    pub fn parse<S>(s: S) -> Result<Cookie<'c>, ParseError>
345        where S: Into<Cow<'c, str>>
346    {
347        parse_cookie(s.into(), false)
348    }
349
350    /// Parses a `Cookie` from the given HTTP cookie header value string where
351    /// the name and value fields are percent-encoded. Percent-decodes the
352    /// name/value fields.
353    ///
354    /// # Example
355    ///
356    /// ```
357    /// use cookie::Cookie;
358    ///
359    /// let c = Cookie::parse_encoded("foo=bar%20baz; HttpOnly").unwrap();
360    /// assert_eq!(c.name_value(), ("foo", "bar baz"));
361    /// assert_eq!(c.http_only(), Some(true));
362    /// assert_eq!(c.secure(), None);
363    /// ```
364    #[cfg(feature = "percent-encode")]
365    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "percent-encode")))]
366    pub fn parse_encoded<S>(s: S) -> Result<Cookie<'c>, ParseError>
367        where S: Into<Cow<'c, str>>
368    {
369        parse_cookie(s.into(), true)
370    }
371
372    /// Parses the HTTP `Cookie` header, a series of cookie names and value
373    /// separated by `;`, returning an iterator over the parse results. Each
374    /// item returned by the iterator is a `Result<Cookie, ParseError>` of
375    /// parsing one name/value pair. Empty cookie values (i.e, in `a=1;;b=2`)
376    /// and any excess surrounding whitespace are ignored.
377    ///
378    /// Unlike [`Cookie::split_parse_encoded()`], this method _does **not**_
379    /// percent-decode keys and values.
380    ///
381    /// # Example
382    ///
383    /// ```rust
384    /// use cookie::Cookie;
385    ///
386    /// let string = "name=value; other=key%20value";
387    /// # let values: Vec<_> = Cookie::split_parse(string).collect();
388    /// # assert_eq!(values.len(), 2);
389    /// # assert_eq!(values[0].as_ref().unwrap().name(), "name");
390    /// # assert_eq!(values[1].as_ref().unwrap().name(), "other");
391    /// for cookie in Cookie::split_parse(string) {
392    ///     let cookie = cookie.unwrap();
393    ///     match cookie.name() {
394    ///         "name" => assert_eq!(cookie.value(), "value"),
395    ///         "other" => assert_eq!(cookie.value(), "key%20value"),
396    ///         _ => unreachable!()
397    ///     }
398    /// }
399    /// ```
400    #[inline(always)]
401    pub fn split_parse<S>(string: S) -> SplitCookies<'c>
402        where S: Into<Cow<'c, str>>
403    {
404        SplitCookies {
405            string: string.into(),
406            last: 0,
407            decode: false,
408        }
409    }
410
411    /// Parses the HTTP `Cookie` header, a series of cookie names and value
412    /// separated by `;`, returning an iterator over the parse results. Each
413    /// item returned by the iterator is a `Result<Cookie, ParseError>` of
414    /// parsing one name/value pair. Empty cookie values (i.e, in `a=1;;b=2`)
415    /// and any excess surrounding whitespace are ignored.
416    ///
417    /// Unlike [`Cookie::split_parse()`], this method _does_ percent-decode keys
418    /// and values.
419    ///
420    /// # Example
421    ///
422    /// ```rust
423    /// use cookie::Cookie;
424    ///
425    /// let string = "name=value; other=key%20value";
426    /// # let v: Vec<_> = Cookie::split_parse_encoded(string).collect();
427    /// # assert_eq!(v.len(), 2);
428    /// # assert_eq!(v[0].as_ref().unwrap().name_value(), ("name", "value"));
429    /// # assert_eq!(v[1].as_ref().unwrap().name_value(), ("other", "key value"));
430    /// for cookie in Cookie::split_parse_encoded(string) {
431    ///     let cookie = cookie.unwrap();
432    ///     match cookie.name() {
433    ///         "name" => assert_eq!(cookie.value(), "value"),
434    ///         "other" => assert_eq!(cookie.value(), "key value"),
435    ///         _ => unreachable!()
436    ///     }
437    /// }
438    /// ```
439    #[cfg(feature = "percent-encode")]
440    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "percent-encode")))]
441    #[inline(always)]
442    pub fn split_parse_encoded<S>(string: S) -> SplitCookies<'c>
443        where S: Into<Cow<'c, str>>
444    {
445        SplitCookies {
446            string: string.into(),
447            last: 0,
448            decode: true,
449        }
450    }
451
452    /// Converts `self` into a `Cookie` with a static lifetime with as few
453    /// allocations as possible.
454    ///
455    /// # Example
456    ///
457    /// ```
458    /// use cookie::Cookie;
459    ///
460    /// let c = Cookie::new("a", "b");
461    /// let owned_cookie = c.into_owned();
462    /// assert_eq!(owned_cookie.name_value(), ("a", "b"));
463    /// ```
464    pub fn into_owned(self) -> Cookie<'static> {
465        Cookie {
466            cookie_string: self.cookie_string.map(|s| s.into_owned().into()),
467            name: self.name.into_owned(),
468            value: self.value.into_owned(),
469            expires: self.expires,
470            max_age: self.max_age,
471            domain: self.domain.map(|s| s.into_owned()),
472            path: self.path.map(|s| s.into_owned()),
473            secure: self.secure,
474            http_only: self.http_only,
475            same_site: self.same_site,
476            partitioned: self.partitioned,
477        }
478    }
479
480    /// Returns the name of `self`.
481    ///
482    /// # Example
483    ///
484    /// ```
485    /// use cookie::Cookie;
486    ///
487    /// let c = Cookie::new("name", "value");
488    /// assert_eq!(c.name(), "name");
489    /// ```
490    #[inline]
491    pub fn name(&self) -> &str {
492        self.name.to_str(self.cookie_string.as_ref())
493    }
494
495    /// Returns the value of `self`.
496    ///
497    /// Does not strip surrounding quotes. See [`Cookie::value_trimmed()`] for a
498    /// version that does.
499    ///
500    /// # Example
501    ///
502    /// ```
503    /// use cookie::Cookie;
504    ///
505    /// let c = Cookie::new("name", "value");
506    /// assert_eq!(c.value(), "value");
507    ///
508    /// let c = Cookie::new("name", "\"value\"");
509    /// assert_eq!(c.value(), "\"value\"");
510    /// ```
511    #[inline]
512    pub fn value(&self) -> &str {
513        self.value.to_str(self.cookie_string.as_ref())
514    }
515
516    /// Returns the value of `self` with surrounding double-quotes trimmed.
517    ///
518    /// This is _not_ the value of the cookie (_that_ is [`Cookie::value()`]).
519    /// Instead, this is the value with a surrounding pair of double-quotes, if
520    /// any, trimmed away. Quotes are only trimmed when they form a pair and
521    /// never otherwise. The trimmed value is never used for other operations,
522    /// such as equality checking, on `self`.
523    ///
524    /// # Example
525    ///
526    /// ```
527    /// use cookie::Cookie;
528    /// let c0 = Cookie::new("name", "value");
529    /// assert_eq!(c0.value_trimmed(), "value");
530    ///
531    /// let c = Cookie::new("name", "\"value\"");
532    /// assert_eq!(c.value_trimmed(), "value");
533    /// assert!(c != c0);
534    ///
535    /// let c = Cookie::new("name", "\"value");
536    /// assert_eq!(c.value(), "\"value");
537    /// assert_eq!(c.value_trimmed(), "\"value");
538    /// assert!(c != c0);
539    ///
540    /// let c = Cookie::new("name", "\"value\"\"");
541    /// assert_eq!(c.value(), "\"value\"\"");
542    /// assert_eq!(c.value_trimmed(), "value\"");
543    /// assert!(c != c0);
544    /// ```
545    #[inline]
546    pub fn value_trimmed(&self) -> &str {
547        #[inline(always)]
548        fn trim_quotes(s: &str) -> &str {
549            if s.len() < 2 {
550                return s;
551            }
552
553            let bytes = s.as_bytes();
554            match (bytes.first(), bytes.last()) {
555                (Some(b'"'), Some(b'"')) => &s[1..(s.len() - 1)],
556                _ => s
557            }
558        }
559
560        trim_quotes(self.value())
561    }
562
563    /// Returns the name and value of `self` as a tuple of `(name, value)`.
564    ///
565    /// # Example
566    ///
567    /// ```
568    /// use cookie::Cookie;
569    ///
570    /// let c = Cookie::new("name", "value");
571    /// assert_eq!(c.name_value(), ("name", "value"));
572    /// ```
573    #[inline]
574    pub fn name_value(&self) -> (&str, &str) {
575        (self.name(), self.value())
576    }
577
578    /// Returns the name and [trimmed value](Cookie::value_trimmed()) of `self`
579    /// as a tuple of `(name, trimmed_value)`.
580    ///
581    /// # Example
582    ///
583    /// ```
584    /// use cookie::Cookie;
585    ///
586    /// let c = Cookie::new("name", "\"value\"");
587    /// assert_eq!(c.name_value_trimmed(), ("name", "value"));
588    /// ```
589    #[inline]
590    pub fn name_value_trimmed(&self) -> (&str, &str) {
591        (self.name(), self.value_trimmed())
592    }
593
594    /// Returns whether this cookie was marked `HttpOnly` or not. Returns
595    /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
596    /// `HttpOnly`, `Some(false)` when `http_only` was manually set to `false`,
597    /// and `None` otherwise.
598    ///
599    /// # Example
600    ///
601    /// ```
602    /// use cookie::Cookie;
603    ///
604    /// let c = Cookie::parse("name=value; httponly").unwrap();
605    /// assert_eq!(c.http_only(), Some(true));
606    ///
607    /// let mut c = Cookie::new("name", "value");
608    /// assert_eq!(c.http_only(), None);
609    ///
610    /// let mut c = Cookie::new("name", "value");
611    /// assert_eq!(c.http_only(), None);
612    ///
613    /// // An explicitly set "false" value.
614    /// c.set_http_only(false);
615    /// assert_eq!(c.http_only(), Some(false));
616    ///
617    /// // An explicitly set "true" value.
618    /// c.set_http_only(true);
619    /// assert_eq!(c.http_only(), Some(true));
620    /// ```
621    #[inline]
622    pub fn http_only(&self) -> Option<bool> {
623        self.http_only
624    }
625
626    /// Returns whether this cookie was marked `Secure` or not. Returns
627    /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
628    /// `Secure`, `Some(false)` when `secure` was manually set to `false`, and
629    /// `None` otherwise.
630    ///
631    /// # Example
632    ///
633    /// ```
634    /// use cookie::Cookie;
635    ///
636    /// let c = Cookie::parse("name=value; Secure").unwrap();
637    /// assert_eq!(c.secure(), Some(true));
638    ///
639    /// let mut c = Cookie::parse("name=value").unwrap();
640    /// assert_eq!(c.secure(), None);
641    ///
642    /// let mut c = Cookie::new("name", "value");
643    /// assert_eq!(c.secure(), None);
644    ///
645    /// // An explicitly set "false" value.
646    /// c.set_secure(false);
647    /// assert_eq!(c.secure(), Some(false));
648    ///
649    /// // An explicitly set "true" value.
650    /// c.set_secure(true);
651    /// assert_eq!(c.secure(), Some(true));
652    /// ```
653    #[inline]
654    pub fn secure(&self) -> Option<bool> {
655        self.secure
656    }
657
658    /// Returns the `SameSite` attribute of this cookie if one was specified.
659    ///
660    /// # Example
661    ///
662    /// ```
663    /// use cookie::{Cookie, SameSite};
664    ///
665    /// let c = Cookie::parse("name=value; SameSite=Lax").unwrap();
666    /// assert_eq!(c.same_site(), Some(SameSite::Lax));
667    /// ```
668    #[inline]
669    pub fn same_site(&self) -> Option<SameSite> {
670        self.same_site
671    }
672
673    /// Returns whether this cookie was marked `Partitioned` or not. Returns
674    /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
675    /// `Partitioned`, `Some(false)` when `partitioned` was manually set to `false`,
676    /// and `None` otherwise.
677    ///
678    /// **Note:** This cookie attribute is an [HTTP draft]! Its meaning and
679    /// definition are not standardized and therefore subject to change.
680    ///
681    /// [HTTP draft]: https://www.ietf.org/id/draft-cutler-httpbis-partitioned-cookies-01.html
682    ///
683    /// # Example
684    ///
685    /// ```
686    /// use cookie::Cookie;
687    ///
688    /// let c = Cookie::parse("name=value; Partitioned").unwrap();
689    /// assert_eq!(c.partitioned(), Some(true));
690    ///
691    /// let mut c = Cookie::parse("name=value").unwrap();
692    /// assert_eq!(c.partitioned(), None);
693    ///
694    /// let mut c = Cookie::new("name", "value");
695    /// assert_eq!(c.partitioned(), None);
696    ///
697    /// // An explicitly set "false" value.
698    /// c.set_partitioned(false);
699    /// assert_eq!(c.partitioned(), Some(false));
700    ///
701    /// // An explicitly set "true" value.
702    /// c.set_partitioned(true);
703    /// assert_eq!(c.partitioned(), Some(true));
704    /// ```
705    #[inline]
706    pub fn partitioned(&self) -> Option<bool> {
707        self.partitioned
708    }
709
710    /// Returns the specified max-age of the cookie if one was specified.
711    ///
712    /// # Example
713    ///
714    /// ```
715    /// use cookie::Cookie;
716    ///
717    /// let c = Cookie::parse("name=value").unwrap();
718    /// assert_eq!(c.max_age(), None);
719    ///
720    /// let c = Cookie::parse("name=value; Max-Age=3600").unwrap();
721    /// assert_eq!(c.max_age().map(|age| age.whole_hours()), Some(1));
722    /// ```
723    #[inline]
724    pub fn max_age(&self) -> Option<Duration> {
725        self.max_age
726    }
727
728    /// Returns the `Path` of the cookie if one was specified.
729    ///
730    /// # Example
731    ///
732    /// ```
733    /// use cookie::Cookie;
734    ///
735    /// let c = Cookie::parse("name=value").unwrap();
736    /// assert_eq!(c.path(), None);
737    ///
738    /// let c = Cookie::parse("name=value; Path=/").unwrap();
739    /// assert_eq!(c.path(), Some("/"));
740    ///
741    /// let c = Cookie::parse("name=value; path=/sub").unwrap();
742    /// assert_eq!(c.path(), Some("/sub"));
743    /// ```
744    #[inline]
745    pub fn path(&self) -> Option<&str> {
746        match self.path {
747            Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
748            None => None,
749        }
750    }
751
752    /// Returns the `Domain` of the cookie if one was specified.
753    ///
754    /// This does not consider whether the `Domain` is valid; validation is left
755    /// to higher-level libraries, as needed. However, if the `Domain` starts
756    /// with a leading `.`, the leading `.` is stripped.
757    ///
758    /// # Example
759    ///
760    /// ```
761    /// use cookie::Cookie;
762    ///
763    /// let c = Cookie::parse("name=value").unwrap();
764    /// assert_eq!(c.domain(), None);
765    ///
766    /// let c = Cookie::parse("name=value; Domain=crates.io").unwrap();
767    /// assert_eq!(c.domain(), Some("crates.io"));
768    ///
769    /// let c = Cookie::parse("name=value; Domain=.crates.io").unwrap();
770    /// assert_eq!(c.domain(), Some("crates.io"));
771    ///
772    /// // Note that `..crates.io` is not a valid domain.
773    /// let c = Cookie::parse("name=value; Domain=..crates.io").unwrap();
774    /// assert_eq!(c.domain(), Some(".crates.io"));
775    /// ```
776    #[inline]
777    pub fn domain(&self) -> Option<&str> {
778        match self.domain {
779            Some(ref c) => {
780                let domain = c.to_str(self.cookie_string.as_ref());
781                domain.strip_prefix(".").or(Some(domain))
782            },
783            None => None,
784        }
785    }
786
787    /// Returns the [`Expiration`] of the cookie if one was specified.
788    ///
789    /// # Example
790    ///
791    /// ```
792    /// use cookie::{Cookie, Expiration};
793    ///
794    /// let c = Cookie::parse("name=value").unwrap();
795    /// assert_eq!(c.expires(), None);
796    ///
797    /// // Here, `cookie.expires_datetime()` returns `None`.
798    /// let c = Cookie::build(("name", "value")).expires(None).build();
799    /// assert_eq!(c.expires(), Some(Expiration::Session));
800    ///
801    /// let expire_time = "Wed, 21 Oct 2017 07:28:00 GMT";
802    /// let cookie_str = format!("name=value; Expires={}", expire_time);
803    /// let c = Cookie::parse(cookie_str).unwrap();
804    /// assert_eq!(c.expires().and_then(|e| e.datetime()).map(|t| t.year()), Some(2017));
805    /// ```
806    #[inline]
807    pub fn expires(&self) -> Option<Expiration> {
808        self.expires
809    }
810
811    /// Returns the expiration date-time of the cookie if one was specified.
812    ///
813    /// # Example
814    ///
815    /// ```
816    /// use cookie::Cookie;
817    ///
818    /// let c = Cookie::parse("name=value").unwrap();
819    /// assert_eq!(c.expires_datetime(), None);
820    ///
821    /// // Here, `cookie.expires()` returns `Some`.
822    /// let c = Cookie::build(("name", "value")).expires(None).build();
823    /// assert_eq!(c.expires_datetime(), None);
824    ///
825    /// let expire_time = "Wed, 21 Oct 2017 07:28:00 GMT";
826    /// let cookie_str = format!("name=value; Expires={}", expire_time);
827    /// let c = Cookie::parse(cookie_str).unwrap();
828    /// assert_eq!(c.expires_datetime().map(|t| t.year()), Some(2017));
829    /// ```
830    #[inline]
831    pub fn expires_datetime(&self) -> Option<OffsetDateTime> {
832        self.expires.and_then(|e| e.datetime())
833    }
834
835    /// Sets the name of `self` to `name`.
836    ///
837    /// # Example
838    ///
839    /// ```
840    /// use cookie::Cookie;
841    ///
842    /// let mut c = Cookie::new("name", "value");
843    /// assert_eq!(c.name(), "name");
844    ///
845    /// c.set_name("foo");
846    /// assert_eq!(c.name(), "foo");
847    /// ```
848    pub fn set_name<N: Into<Cow<'c, str>>>(&mut self, name: N) {
849        self.name = CookieStr::Concrete(name.into())
850    }
851
852    /// Sets the value of `self` to `value`.
853    ///
854    /// # Example
855    ///
856    /// ```
857    /// use cookie::Cookie;
858    ///
859    /// let mut c = Cookie::new("name", "value");
860    /// assert_eq!(c.value(), "value");
861    ///
862    /// c.set_value("bar");
863    /// assert_eq!(c.value(), "bar");
864    /// ```
865    pub fn set_value<V: Into<Cow<'c, str>>>(&mut self, value: V) {
866        self.value = CookieStr::Concrete(value.into())
867    }
868
869    /// Sets the value of `http_only` in `self` to `value`.  If `value` is
870    /// `None`, the field is unset.
871    ///
872    /// # Example
873    ///
874    /// ```
875    /// use cookie::Cookie;
876    ///
877    /// let mut c = Cookie::new("name", "value");
878    /// assert_eq!(c.http_only(), None);
879    ///
880    /// c.set_http_only(true);
881    /// assert_eq!(c.http_only(), Some(true));
882    ///
883    /// c.set_http_only(false);
884    /// assert_eq!(c.http_only(), Some(false));
885    ///
886    /// c.set_http_only(None);
887    /// assert_eq!(c.http_only(), None);
888    /// ```
889    #[inline]
890    pub fn set_http_only<T: Into<Option<bool>>>(&mut self, value: T) {
891        self.http_only = value.into();
892    }
893
894    /// Sets the value of `secure` in `self` to `value`. If `value` is `None`,
895    /// the field is unset.
896    ///
897    /// # Example
898    ///
899    /// ```
900    /// use cookie::Cookie;
901    ///
902    /// let mut c = Cookie::new("name", "value");
903    /// assert_eq!(c.secure(), None);
904    ///
905    /// c.set_secure(true);
906    /// assert_eq!(c.secure(), Some(true));
907    ///
908    /// c.set_secure(false);
909    /// assert_eq!(c.secure(), Some(false));
910    ///
911    /// c.set_secure(None);
912    /// assert_eq!(c.secure(), None);
913    /// ```
914    #[inline]
915    pub fn set_secure<T: Into<Option<bool>>>(&mut self, value: T) {
916        self.secure = value.into();
917    }
918
919    /// Sets the value of `same_site` in `self` to `value`. If `value` is
920    /// `None`, the field is unset. If `value` is `SameSite::None`, the "Secure"
921    /// flag will be set when the cookie is written out unless `secure` is
922    /// explicitly set to `false` via [`Cookie::set_secure()`] or the equivalent
923    /// builder method.
924    ///
925    /// [HTTP draft]: https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
926    ///
927    /// # Example
928    ///
929    /// ```
930    /// use cookie::{Cookie, SameSite};
931    ///
932    /// let mut c = Cookie::new("name", "value");
933    /// assert_eq!(c.same_site(), None);
934    ///
935    /// c.set_same_site(SameSite::None);
936    /// assert_eq!(c.same_site(), Some(SameSite::None));
937    /// assert_eq!(c.to_string(), "name=value; SameSite=None; Secure");
938    ///
939    /// c.set_secure(false);
940    /// assert_eq!(c.to_string(), "name=value; SameSite=None");
941    ///
942    /// let mut c = Cookie::new("name", "value");
943    /// assert_eq!(c.same_site(), None);
944    ///
945    /// c.set_same_site(SameSite::Strict);
946    /// assert_eq!(c.same_site(), Some(SameSite::Strict));
947    /// assert_eq!(c.to_string(), "name=value; SameSite=Strict");
948    ///
949    /// c.set_same_site(None);
950    /// assert_eq!(c.same_site(), None);
951    /// assert_eq!(c.to_string(), "name=value");
952    /// ```
953    #[inline]
954    pub fn set_same_site<T: Into<Option<SameSite>>>(&mut self, value: T) {
955        self.same_site = value.into();
956    }
957
958    /// Sets the value of `partitioned` in `self` to `value`. If `value` is
959    /// `None`, the field is unset.
960    ///
961    /// **Note:** _Partitioned_ cookies require the `Secure` attribute to be
962    /// set. As such, `Partitioned` cookies are always rendered with the
963    /// `Secure` attribute, irrespective of the `Secure` attribute's setting.
964    ///
965    /// **Note:** This cookie attribute is an [HTTP draft]! Its meaning and
966    /// definition are not standardized and therefore subject to change.
967    ///
968    /// [HTTP draft]: https://www.ietf.org/id/draft-cutler-httpbis-partitioned-cookies-01.html
969    ///
970    /// # Example
971    ///
972    /// ```
973    /// use cookie::Cookie;
974    ///
975    /// let mut c = Cookie::new("name", "value");
976    /// assert_eq!(c.partitioned(), None);
977    ///
978    /// c.set_partitioned(true);
979    /// assert_eq!(c.partitioned(), Some(true));
980    /// assert!(c.to_string().contains("Secure"));
981    ///
982    /// c.set_partitioned(false);
983    /// assert_eq!(c.partitioned(), Some(false));
984    /// assert!(!c.to_string().contains("Secure"));
985    ///
986    /// c.set_partitioned(None);
987    /// assert_eq!(c.partitioned(), None);
988    /// assert!(!c.to_string().contains("Secure"));
989    /// ```
990    #[inline]
991    pub fn set_partitioned<T: Into<Option<bool>>>(&mut self, value: T) {
992        self.partitioned = value.into();
993    }
994
995    /// Sets the value of `max_age` in `self` to `value`. If `value` is `None`,
996    /// the field is unset.
997    ///
998    /// # Example
999    ///
1000    /// ```rust
1001    /// # extern crate cookie;
1002    /// use cookie::Cookie;
1003    /// use cookie::time::Duration;
1004    ///
1005    /// # fn main() {
1006    /// let mut c = Cookie::new("name", "value");
1007    /// assert_eq!(c.max_age(), None);
1008    ///
1009    /// c.set_max_age(Duration::hours(10));
1010    /// assert_eq!(c.max_age(), Some(Duration::hours(10)));
1011    ///
1012    /// c.set_max_age(None);
1013    /// assert!(c.max_age().is_none());
1014    /// # }
1015    /// ```
1016    #[inline]
1017    pub fn set_max_age<D: Into<Option<Duration>>>(&mut self, value: D) {
1018        self.max_age = value.into();
1019    }
1020
1021    /// Sets the `path` of `self` to `path`.
1022    ///
1023    /// # Example
1024    ///
1025    /// ```rust
1026    /// use cookie::Cookie;
1027    ///
1028    /// let mut c = Cookie::new("name", "value");
1029    /// assert_eq!(c.path(), None);
1030    ///
1031    /// c.set_path("/");
1032    /// assert_eq!(c.path(), Some("/"));
1033    /// ```
1034    pub fn set_path<P: Into<Cow<'c, str>>>(&mut self, path: P) {
1035        self.path = Some(CookieStr::Concrete(path.into()));
1036    }
1037
1038    /// Unsets the `path` of `self`.
1039    ///
1040    /// # Example
1041    ///
1042    /// ```
1043    /// use cookie::Cookie;
1044    ///
1045    /// let mut c = Cookie::new("name", "value");
1046    /// assert_eq!(c.path(), None);
1047    ///
1048    /// c.set_path("/");
1049    /// assert_eq!(c.path(), Some("/"));
1050    ///
1051    /// c.unset_path();
1052    /// assert_eq!(c.path(), None);
1053    /// ```
1054    pub fn unset_path(&mut self) {
1055        self.path = None;
1056    }
1057
1058    /// Sets the `domain` of `self` to `domain`.
1059    ///
1060    /// # Example
1061    ///
1062    /// ```
1063    /// use cookie::Cookie;
1064    ///
1065    /// let mut c = Cookie::new("name", "value");
1066    /// assert_eq!(c.domain(), None);
1067    ///
1068    /// c.set_domain("rust-lang.org");
1069    /// assert_eq!(c.domain(), Some("rust-lang.org"));
1070    /// ```
1071    pub fn set_domain<D: Into<Cow<'c, str>>>(&mut self, domain: D) {
1072        self.domain = Some(CookieStr::Concrete(domain.into()));
1073    }
1074
1075    /// Unsets the `domain` of `self`.
1076    ///
1077    /// # Example
1078    ///
1079    /// ```
1080    /// use cookie::Cookie;
1081    ///
1082    /// let mut c = Cookie::new("name", "value");
1083    /// assert_eq!(c.domain(), None);
1084    ///
1085    /// c.set_domain("rust-lang.org");
1086    /// assert_eq!(c.domain(), Some("rust-lang.org"));
1087    ///
1088    /// c.unset_domain();
1089    /// assert_eq!(c.domain(), None);
1090    /// ```
1091    pub fn unset_domain(&mut self) {
1092        self.domain = None;
1093    }
1094
1095    /// Sets the expires field of `self` to `time`. If `time` is `None`, an
1096    /// expiration of [`Session`](Expiration::Session) is set.
1097    ///
1098    /// # Example
1099    ///
1100    /// ```
1101    /// # extern crate cookie;
1102    /// use cookie::{Cookie, Expiration};
1103    /// use cookie::time::{Duration, OffsetDateTime};
1104    ///
1105    /// let mut c = Cookie::new("name", "value");
1106    /// assert_eq!(c.expires(), None);
1107    ///
1108    /// let mut now = OffsetDateTime::now_utc();
1109    /// now += Duration::weeks(52);
1110    ///
1111    /// c.set_expires(now);
1112    /// assert!(c.expires().is_some());
1113    ///
1114    /// c.set_expires(None);
1115    /// assert_eq!(c.expires(), Some(Expiration::Session));
1116    /// ```
1117    pub fn set_expires<T: Into<Expiration>>(&mut self, time: T) {
1118        static MAX_DATETIME: OffsetDateTime = datetime!(9999-12-31 23:59:59.999_999 UTC);
1119
1120        // RFC 6265 requires dates not to exceed 9999 years.
1121        self.expires = Some(time.into()
1122            .map(|time| std::cmp::min(time, MAX_DATETIME)));
1123    }
1124
1125    /// Unsets the `expires` of `self`.
1126    ///
1127    /// # Example
1128    ///
1129    /// ```
1130    /// use cookie::{Cookie, Expiration};
1131    ///
1132    /// let mut c = Cookie::new("name", "value");
1133    /// assert_eq!(c.expires(), None);
1134    ///
1135    /// c.set_expires(None);
1136    /// assert_eq!(c.expires(), Some(Expiration::Session));
1137    ///
1138    /// c.unset_expires();
1139    /// assert_eq!(c.expires(), None);
1140    /// ```
1141    pub fn unset_expires(&mut self) {
1142        self.expires = None;
1143    }
1144
1145    /// Makes `self` a "permanent" cookie by extending its expiration and max
1146    /// age 20 years into the future.
1147    ///
1148    /// # Example
1149    ///
1150    /// ```rust
1151    /// # extern crate cookie;
1152    /// use cookie::Cookie;
1153    /// use cookie::time::Duration;
1154    ///
1155    /// # fn main() {
1156    /// let mut c = Cookie::new("foo", "bar");
1157    /// assert!(c.expires().is_none());
1158    /// assert!(c.max_age().is_none());
1159    ///
1160    /// c.make_permanent();
1161    /// assert!(c.expires().is_some());
1162    /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
1163    /// # }
1164    /// ```
1165    pub fn make_permanent(&mut self) {
1166        let twenty_years = Duration::days(365 * 20);
1167        self.set_max_age(twenty_years);
1168        self.set_expires(OffsetDateTime::now_utc() + twenty_years);
1169    }
1170
1171    /// Make `self` a "removal" cookie by clearing its value, setting a max-age
1172    /// of `0`, and setting an expiration date far in the past.
1173    ///
1174    /// # Example
1175    ///
1176    /// ```rust
1177    /// # extern crate cookie;
1178    /// use cookie::Cookie;
1179    /// use cookie::time::Duration;
1180    ///
1181    /// # fn main() {
1182    /// let mut c = Cookie::new("foo", "bar");
1183    /// c.make_permanent();
1184    /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
1185    /// assert_eq!(c.value(), "bar");
1186    ///
1187    /// c.make_removal();
1188    /// assert_eq!(c.value(), "");
1189    /// assert_eq!(c.max_age(), Some(Duration::ZERO));
1190    /// # }
1191    /// ```
1192    pub fn make_removal(&mut self) {
1193        self.set_value("");
1194        self.set_max_age(Duration::seconds(0));
1195        self.set_expires(OffsetDateTime::now_utc() - Duration::days(365));
1196    }
1197
1198    fn fmt_parameters(&self, f: &mut fmt::Formatter) -> fmt::Result {
1199        if let Some(true) = self.http_only() {
1200            write!(f, "; HttpOnly")?;
1201        }
1202
1203        if let Some(same_site) = self.same_site() {
1204            write!(f, "; SameSite={}", same_site)?;
1205        }
1206
1207        if let Some(true) = self.partitioned() {
1208            write!(f, "; Partitioned")?;
1209        }
1210
1211        if self.secure() == Some(true)
1212            || self.partitioned() == Some(true)
1213            || self.secure().is_none() && self.same_site() == Some(SameSite::None)
1214        {
1215            write!(f, "; Secure")?;
1216        }
1217
1218        if let Some(path) = self.path() {
1219            write!(f, "; Path={}", path)?;
1220        }
1221
1222        if let Some(domain) = self.domain() {
1223            write!(f, "; Domain={}", domain)?;
1224        }
1225
1226        if let Some(max_age) = self.max_age() {
1227            write!(f, "; Max-Age={}", max_age.whole_seconds())?;
1228        }
1229
1230        if let Some(time) = self.expires_datetime() {
1231            let time = time.to_offset(UtcOffset::UTC);
1232            write!(f, "; Expires={}", time.format(&crate::parse::FMT1).map_err(|_| fmt::Error)?)?;
1233        }
1234
1235        Ok(())
1236    }
1237
1238    /// Returns the name of `self` as a string slice of the raw string `self`
1239    /// was originally parsed from. If `self` was not originally parsed from a
1240    /// raw string, returns `None`.
1241    ///
1242    /// This method differs from [`Cookie::name()`] in that it returns a string
1243    /// with the same lifetime as the originally parsed string. This lifetime
1244    /// may outlive `self`. If a longer lifetime is not required, or you're
1245    /// unsure if you need a longer lifetime, use [`Cookie::name()`].
1246    ///
1247    /// # Example
1248    ///
1249    /// ```
1250    /// use cookie::Cookie;
1251    ///
1252    /// let cookie_string = format!("{}={}", "foo", "bar");
1253    ///
1254    /// // `c` will be dropped at the end of the scope, but `name` will live on
1255    /// let name = {
1256    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
1257    ///     c.name_raw()
1258    /// };
1259    ///
1260    /// assert_eq!(name, Some("foo"));
1261    /// ```
1262    #[inline]
1263    pub fn name_raw(&self) -> Option<&'c str> {
1264        self.cookie_string.as_ref()
1265            .and_then(|s| self.name.to_raw_str(s))
1266    }
1267
1268    /// Returns the value of `self` as a string slice of the raw string `self`
1269    /// was originally parsed from. If `self` was not originally parsed from a
1270    /// raw string, returns `None`.
1271    ///
1272    /// This method differs from [`Cookie::value()`] in that it returns a
1273    /// string with the same lifetime as the originally parsed string. This
1274    /// lifetime may outlive `self`. If a longer lifetime is not required, or
1275    /// you're unsure if you need a longer lifetime, use [`Cookie::value()`].
1276    ///
1277    /// # Example
1278    ///
1279    /// ```
1280    /// use cookie::Cookie;
1281    ///
1282    /// let cookie_string = format!("{}={}", "foo", "bar");
1283    ///
1284    /// // `c` will be dropped at the end of the scope, but `value` will live on
1285    /// let value = {
1286    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
1287    ///     c.value_raw()
1288    /// };
1289    ///
1290    /// assert_eq!(value, Some("bar"));
1291    /// ```
1292    #[inline]
1293    pub fn value_raw(&self) -> Option<&'c str> {
1294        self.cookie_string.as_ref()
1295            .and_then(|s| self.value.to_raw_str(s))
1296    }
1297
1298    /// Returns the `Path` of `self` as a string slice of the raw string `self`
1299    /// was originally parsed from. If `self` was not originally parsed from a
1300    /// raw string, or if `self` doesn't contain a `Path`, or if the `Path` has
1301    /// changed since parsing, returns `None`.
1302    ///
1303    /// This method differs from [`Cookie::path()`] in that it returns a
1304    /// string with the same lifetime as the originally parsed string. This
1305    /// lifetime may outlive `self`. If a longer lifetime is not required, or
1306    /// you're unsure if you need a longer lifetime, use [`Cookie::path()`].
1307    ///
1308    /// # Example
1309    ///
1310    /// ```
1311    /// use cookie::Cookie;
1312    ///
1313    /// let cookie_string = format!("{}={}; Path=/", "foo", "bar");
1314    ///
1315    /// // `c` will be dropped at the end of the scope, but `path` will live on
1316    /// let path = {
1317    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
1318    ///     c.path_raw()
1319    /// };
1320    ///
1321    /// assert_eq!(path, Some("/"));
1322    /// ```
1323    #[inline]
1324    pub fn path_raw(&self) -> Option<&'c str> {
1325        match (self.path.as_ref(), self.cookie_string.as_ref()) {
1326            (Some(path), Some(string)) => path.to_raw_str(string),
1327            _ => None,
1328        }
1329    }
1330
1331    /// Returns the `Domain` of `self` as a string slice of the raw string
1332    /// `self` was originally parsed from. If `self` was not originally parsed
1333    /// from a raw string, or if `self` doesn't contain a `Domain`, or if the
1334    /// `Domain` has changed since parsing, returns `None`.
1335    ///
1336    /// Like [`Cookie::domain()`], this does not consider whether `Domain` is
1337    /// valid; validation is left to higher-level libraries, as needed. However,
1338    /// if `Domain` starts with a leading `.`, the leading `.` is stripped.
1339    ///
1340    /// This method differs from [`Cookie::domain()`] in that it returns a
1341    /// string with the same lifetime as the originally parsed string. This
1342    /// lifetime may outlive `self` struct. If a longer lifetime is not
1343    /// required, or you're unsure if you need a longer lifetime, use
1344    /// [`Cookie::domain()`].
1345    ///
1346    /// # Example
1347    ///
1348    /// ```
1349    /// use cookie::Cookie;
1350    ///
1351    /// let cookie_string = format!("{}={}; Domain=.crates.io", "foo", "bar");
1352    ///
1353    /// //`c` will be dropped at the end of the scope, but `domain` will live on
1354    /// let domain = {
1355    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
1356    ///     c.domain_raw()
1357    /// };
1358    ///
1359    /// assert_eq!(domain, Some("crates.io"));
1360    /// ```
1361    #[inline]
1362    pub fn domain_raw(&self) -> Option<&'c str> {
1363        match (self.domain.as_ref(), self.cookie_string.as_ref()) {
1364            (Some(domain), Some(string)) => match domain.to_raw_str(string) {
1365                Some(s) => s.strip_prefix(".").or(Some(s)),
1366                None => None,
1367            }
1368            _ => None,
1369        }
1370    }
1371
1372    /// Wraps `self` in an encoded [`Display`]: a cost-free wrapper around
1373    /// `Cookie` whose [`fmt::Display`] implementation percent-encodes the name
1374    /// and value of the wrapped `Cookie`.
1375    ///
1376    /// The returned structure can be chained with [`Display::stripped()`] to
1377    /// display only the name and value.
1378    ///
1379    /// # Example
1380    ///
1381    /// ```rust
1382    /// use cookie::Cookie;
1383    ///
1384    /// let mut c = Cookie::build(("my name", "this; value?")).secure(true).build();
1385    /// assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%3F; Secure");
1386    /// assert_eq!(&c.encoded().stripped().to_string(), "my%20name=this%3B%20value%3F");
1387    /// ```
1388    #[cfg(feature = "percent-encode")]
1389    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "percent-encode")))]
1390    #[inline(always)]
1391    pub fn encoded<'a>(&'a self) -> Display<'a, 'c> {
1392        Display::new_encoded(self)
1393    }
1394
1395    /// Wraps `self` in a stripped `Display`]: a cost-free wrapper around
1396    /// `Cookie` whose [`fmt::Display`] implementation prints only the `name`
1397    /// and `value` of the wrapped `Cookie`.
1398    ///
1399    /// The returned structure can be chained with [`Display::encoded()`] to
1400    /// encode the name and value.
1401    ///
1402    /// # Example
1403    ///
1404    /// ```rust
1405    /// use cookie::Cookie;
1406    ///
1407    /// let mut c = Cookie::build(("key?", "value")).secure(true).path("/").build();
1408    /// assert_eq!(&c.stripped().to_string(), "key?=value");
1409    #[cfg_attr(feature = "percent-encode", doc = r##"
1410// Note: `encoded()` is only available when `percent-encode` is enabled.
1411assert_eq!(&c.stripped().encoded().to_string(), "key%3F=value");
1412    #"##)]
1413    /// ```
1414    #[inline(always)]
1415    pub fn stripped<'a>(&'a self) -> Display<'a, 'c> {
1416        Display::new_stripped(self)
1417    }
1418}
1419
1420/// An iterator over cookie parse `Result`s: `Result<Cookie, ParseError>`.
1421///
1422/// Returned by [`Cookie::split_parse()`] and [`Cookie::split_parse_encoded()`].
1423pub struct SplitCookies<'c> {
1424    // The source string, which we split and parse.
1425    string: Cow<'c, str>,
1426    // The index where we last split off.
1427    last: usize,
1428    // Whether we should percent-decode when parsing.
1429    decode: bool,
1430}
1431
1432impl<'c> Iterator for SplitCookies<'c> {
1433    type Item = Result<Cookie<'c>, ParseError>;
1434
1435    fn next(&mut self) -> Option<Self::Item> {
1436        while self.last < self.string.len() {
1437            let i = self.last;
1438            let j = self.string[i..]
1439                .find(';')
1440                .map(|k| i + k)
1441                .unwrap_or(self.string.len());
1442
1443            self.last = j + 1;
1444            if self.string[i..j].chars().all(|c| c.is_whitespace()) {
1445                continue;
1446            }
1447
1448            return Some(match self.string {
1449                Cow::Borrowed(s) => parse_cookie(s[i..j].trim(), self.decode),
1450                Cow::Owned(ref s) => parse_cookie(s[i..j].trim().to_owned(), self.decode),
1451            })
1452        }
1453
1454        None
1455    }
1456}
1457
1458#[cfg(feature = "percent-encode")]
1459mod encoding {
1460    use percent_encoding::{AsciiSet, CONTROLS};
1461
1462    /// https://url.spec.whatwg.org/#fragment-percent-encode-set
1463    const FRAGMENT: &AsciiSet = &CONTROLS
1464        .add(b' ')
1465        .add(b'"')
1466        .add(b'<')
1467        .add(b'>')
1468        .add(b'`');
1469
1470    /// https://url.spec.whatwg.org/#path-percent-encode-set
1471    const PATH: &AsciiSet = &FRAGMENT
1472        .add(b'#')
1473        .add(b'?')
1474        .add(b'{')
1475        .add(b'}');
1476
1477    /// https://url.spec.whatwg.org/#userinfo-percent-encode-set
1478    const USERINFO: &AsciiSet = &PATH
1479        .add(b'/')
1480        .add(b':')
1481        .add(b';')
1482        .add(b'=')
1483        .add(b'@')
1484        .add(b'[')
1485        .add(b'\\')
1486        .add(b']')
1487        .add(b'^')
1488        .add(b'|')
1489        .add(b'%');
1490
1491    /// https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1 + '(', ')'
1492    const COOKIE: &AsciiSet = &USERINFO
1493        .add(b'(')
1494        .add(b')')
1495        .add(b',');
1496
1497    /// Percent-encode a cookie name or value with the proper encoding set.
1498    pub fn encode(string: &str) -> impl std::fmt::Display + '_ {
1499        percent_encoding::percent_encode(string.as_bytes(), COOKIE)
1500    }
1501}
1502
1503/// Wrapper around `Cookie` whose `Display` implementation either
1504/// percent-encodes the cookie's name and value, skips displaying the cookie's
1505/// parameters (only displaying it's name and value), or both.
1506///
1507/// A value of this type can be obtained via [`Cookie::encoded()`] and
1508/// [`Cookie::stripped()`], or an arbitrary chaining of the two methods. This
1509/// type should only be used for its `Display` implementation.
1510///
1511/// # Example
1512///
1513/// ```rust
1514/// use cookie::Cookie;
1515///
1516/// let c = Cookie::build(("my name", "this; value%?")).secure(true).build();
1517/// assert_eq!(&c.stripped().to_string(), "my name=this; value%?");
1518#[cfg_attr(feature = "percent-encode", doc = r##"
1519// Note: `encoded()` is only available when `percent-encode` is enabled.
1520assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%25%3F; Secure");
1521assert_eq!(&c.stripped().encoded().to_string(), "my%20name=this%3B%20value%25%3F");
1522assert_eq!(&c.encoded().stripped().to_string(), "my%20name=this%3B%20value%25%3F");
1523"##)]
1524/// ```
1525pub struct Display<'a, 'c: 'a> {
1526    cookie: &'a Cookie<'c>,
1527    #[cfg(feature = "percent-encode")]
1528    encode: bool,
1529    strip: bool,
1530}
1531
1532impl<'a, 'c: 'a> fmt::Display for Display<'a, 'c> {
1533    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1534        #[cfg(feature = "percent-encode")] {
1535            if self.encode {
1536                let name = encoding::encode(self.cookie.name());
1537                let value = encoding::encode(self.cookie.value());
1538                write!(f, "{}={}", name, value)?;
1539            } else {
1540                write!(f, "{}={}", self.cookie.name(), self.cookie.value())?;
1541            }
1542        }
1543
1544        #[cfg(not(feature = "percent-encode"))] {
1545            write!(f, "{}={}", self.cookie.name(), self.cookie.value())?;
1546        }
1547
1548        match self.strip {
1549            true => Ok(()),
1550            false => self.cookie.fmt_parameters(f)
1551        }
1552    }
1553}
1554
1555impl<'a, 'c> Display<'a, 'c> {
1556    #[cfg(feature = "percent-encode")]
1557    fn new_encoded(cookie: &'a Cookie<'c>) -> Self {
1558        Display { cookie, strip: false, encode: true }
1559    }
1560
1561    fn new_stripped(cookie: &'a Cookie<'c>) -> Self {
1562        Display { cookie, strip: true, #[cfg(feature = "percent-encode")] encode: false }
1563    }
1564
1565    /// Percent-encode the name and value pair.
1566    #[inline]
1567    #[cfg(feature = "percent-encode")]
1568    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "percent-encode")))]
1569    pub fn encoded(mut self) -> Self {
1570        self.encode = true;
1571        self
1572    }
1573
1574    /// Only display the name and value.
1575    #[inline]
1576    pub fn stripped(mut self) -> Self {
1577        self.strip = true;
1578        self
1579    }
1580}
1581
1582impl<'c> fmt::Display for Cookie<'c> {
1583    /// Formats the cookie `self` as a `Set-Cookie` header value.
1584    ///
1585    /// Does _not_ percent-encode any values. To percent-encode, use
1586    /// [`Cookie::encoded()`].
1587    ///
1588    /// # Example
1589    ///
1590    /// ```rust
1591    /// use cookie::Cookie;
1592    ///
1593    /// let mut cookie = Cookie::build(("foo", "bar")).path("/");
1594    /// assert_eq!(cookie.to_string(), "foo=bar; Path=/");
1595    /// ```
1596    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1597        write!(f, "{}={}", self.name(), self.value())?;
1598        self.fmt_parameters(f)
1599    }
1600}
1601
1602impl FromStr for Cookie<'static> {
1603    type Err = ParseError;
1604
1605    fn from_str(s: &str) -> Result<Cookie<'static>, ParseError> {
1606        Cookie::parse(s).map(|c| c.into_owned())
1607    }
1608}
1609
1610impl<'a, 'b> PartialEq<Cookie<'b>> for Cookie<'a> {
1611    fn eq(&self, other: &Cookie<'b>) -> bool {
1612        let so_far_so_good = self.name() == other.name()
1613            && self.value() == other.value()
1614            && self.http_only() == other.http_only()
1615            && self.secure() == other.secure()
1616            && self.partitioned() == other.partitioned()
1617            && self.max_age() == other.max_age()
1618            && self.expires() == other.expires();
1619
1620        if !so_far_so_good {
1621            return false;
1622        }
1623
1624        match (self.path(), other.path()) {
1625            (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
1626            (None, None) => {}
1627            _ => return false,
1628        };
1629
1630        match (self.domain(), other.domain()) {
1631            (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
1632            (None, None) => {}
1633            _ => return false,
1634        };
1635
1636        true
1637    }
1638}
1639
1640impl<'a> From<&'a str> for Cookie<'a> {
1641    fn from(name: &'a str) -> Self {
1642        Cookie::new(name, "")
1643    }
1644}
1645
1646impl From<String> for Cookie<'static> {
1647    fn from(name: String) -> Self {
1648        Cookie::new(name, "")
1649    }
1650}
1651
1652impl<'a> From<Cow<'a, str>> for Cookie<'a> {
1653    fn from(name: Cow<'a, str>) -> Self {
1654        Cookie::new(name, "")
1655    }
1656}
1657
1658impl<'a, N, V> From<(N, V)> for Cookie<'a>
1659    where N: Into<Cow<'a, str>>,
1660          V: Into<Cow<'a, str>>
1661{
1662    fn from((name, value): (N, V)) -> Self {
1663        Cookie::new(name, value)
1664    }
1665}
1666
1667impl<'a> From<CookieBuilder<'a>> for Cookie<'a> {
1668    fn from(builder: CookieBuilder<'a>) -> Self {
1669        builder.build()
1670    }
1671}
1672
1673impl<'a> AsRef<Cookie<'a>> for Cookie<'a> {
1674    fn as_ref(&self) -> &Cookie<'a> {
1675        self
1676    }
1677}
1678
1679impl<'a> AsMut<Cookie<'a>> for Cookie<'a> {
1680    fn as_mut(&mut self) -> &mut Cookie<'a> {
1681        self
1682    }
1683}
1684
1685#[cfg(test)]
1686mod tests {
1687    use crate::{Cookie, SameSite, parse::parse_date};
1688    use time::{Duration, OffsetDateTime};
1689
1690    #[test]
1691    fn format() {
1692        let cookie = Cookie::new("foo", "bar");
1693        assert_eq!(&cookie.to_string(), "foo=bar");
1694
1695        let cookie = Cookie::build(("foo", "bar")).http_only(true);
1696        assert_eq!(&cookie.to_string(), "foo=bar; HttpOnly");
1697
1698        let cookie = Cookie::build(("foo", "bar")).max_age(Duration::seconds(10));
1699        assert_eq!(&cookie.to_string(), "foo=bar; Max-Age=10");
1700
1701        let cookie = Cookie::build(("foo", "bar")).secure(true);
1702        assert_eq!(&cookie.to_string(), "foo=bar; Secure");
1703
1704        let cookie = Cookie::build(("foo", "bar")).path("/");
1705        assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
1706
1707        let cookie = Cookie::build(("foo", "bar")).domain("www.rust-lang.org");
1708        assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");
1709
1710        let cookie = Cookie::build(("foo", "bar")).domain(".rust-lang.org");
1711        assert_eq!(&cookie.to_string(), "foo=bar; Domain=rust-lang.org");
1712
1713        let cookie = Cookie::build(("foo", "bar")).domain("rust-lang.org");
1714        assert_eq!(&cookie.to_string(), "foo=bar; Domain=rust-lang.org");
1715
1716        let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
1717        let expires = parse_date(time_str, &crate::parse::FMT1).unwrap();
1718        let cookie = Cookie::build(("foo", "bar")).expires(expires);
1719        assert_eq!(&cookie.to_string(),
1720                   "foo=bar; Expires=Wed, 21 Oct 2015 07:28:00 GMT");
1721
1722        let cookie = Cookie::build(("foo", "bar")).same_site(SameSite::Strict);
1723        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Strict");
1724
1725        let cookie = Cookie::build(("foo", "bar")).same_site(SameSite::Lax);
1726        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Lax");
1727
1728        let mut cookie = Cookie::build(("foo", "bar")).same_site(SameSite::None).build();
1729        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=None; Secure");
1730
1731        cookie.set_partitioned(true);
1732        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=None; Partitioned; Secure");
1733
1734        cookie.set_same_site(None);
1735        assert_eq!(&cookie.to_string(), "foo=bar; Partitioned; Secure");
1736
1737        cookie.set_secure(false);
1738        assert_eq!(&cookie.to_string(), "foo=bar; Partitioned; Secure");
1739
1740        cookie.set_secure(None);
1741        assert_eq!(&cookie.to_string(), "foo=bar; Partitioned; Secure");
1742
1743        cookie.set_partitioned(None);
1744        assert_eq!(&cookie.to_string(), "foo=bar");
1745
1746        let mut c = Cookie::build(("foo", "bar")).same_site(SameSite::None).secure(false).build();
1747        assert_eq!(&c.to_string(), "foo=bar; SameSite=None");
1748        c.set_secure(true);
1749        assert_eq!(&c.to_string(), "foo=bar; SameSite=None; Secure");
1750    }
1751
1752    #[test]
1753    #[ignore]
1754    fn format_date_wraps() {
1755        let expires = OffsetDateTime::UNIX_EPOCH + Duration::MAX;
1756        let cookie = Cookie::build(("foo", "bar")).expires(expires);
1757        assert_eq!(&cookie.to_string(), "foo=bar; Expires=Fri, 31 Dec 9999 23:59:59 GMT");
1758
1759        let expires = time::macros::datetime!(9999-01-01 0:00 UTC) + Duration::days(1000);
1760        let cookie = Cookie::build(("foo", "bar")).expires(expires);
1761        assert_eq!(&cookie.to_string(), "foo=bar; Expires=Fri, 31 Dec 9999 23:59:59 GMT");
1762    }
1763
1764    #[test]
1765    fn cookie_string_long_lifetimes() {
1766        let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
1767        let (name, value, path, domain) = {
1768            // Create a cookie passing a slice
1769            let c = Cookie::parse(cookie_string.as_str()).unwrap();
1770            (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1771        };
1772
1773        assert_eq!(name, Some("bar"));
1774        assert_eq!(value, Some("baz"));
1775        assert_eq!(path, Some("/subdir"));
1776        assert_eq!(domain, Some("crates.io"));
1777    }
1778
1779    #[test]
1780    fn owned_cookie_string() {
1781        let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
1782        let (name, value, path, domain) = {
1783            // Create a cookie passing an owned string
1784            let c = Cookie::parse(cookie_string).unwrap();
1785            (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1786        };
1787
1788        assert_eq!(name, None);
1789        assert_eq!(value, None);
1790        assert_eq!(path, None);
1791        assert_eq!(domain, None);
1792    }
1793
1794    #[test]
1795    fn owned_cookie_struct() {
1796        let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io";
1797        let (name, value, path, domain) = {
1798            // Create an owned cookie
1799            let c = Cookie::parse(cookie_string).unwrap().into_owned();
1800
1801            (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1802        };
1803
1804        assert_eq!(name, None);
1805        assert_eq!(value, None);
1806        assert_eq!(path, None);
1807        assert_eq!(domain, None);
1808    }
1809
1810    #[test]
1811    #[cfg(feature = "percent-encode")]
1812    fn format_encoded() {
1813        let cookie = Cookie::new("foo !%?=", "bar;;, a");
1814        let cookie_str = cookie.encoded().to_string();
1815        assert_eq!(&cookie_str, "foo%20!%25%3F%3D=bar%3B%3B%2C%20a");
1816
1817        let cookie = Cookie::parse_encoded(cookie_str).unwrap();
1818        assert_eq!(cookie.name_value(), ("foo !%?=", "bar;;, a"));
1819    }
1820
1821    #[test]
1822    fn split_parse() {
1823        let cases = [
1824            ("", vec![]),
1825            (";;", vec![]),
1826            ("name=value", vec![("name", "value")]),
1827            ("a=%20", vec![("a", "%20")]),
1828            ("a=d#$%^&*()_", vec![("a", "d#$%^&*()_")]),
1829            ("  name=value  ", vec![("name", "value")]),
1830            ("name=value  ", vec![("name", "value")]),
1831            ("name=value;;other=key", vec![("name", "value"), ("other", "key")]),
1832            ("name=value;  ;other=key", vec![("name", "value"), ("other", "key")]),
1833            ("name=value ;  ;other=key", vec![("name", "value"), ("other", "key")]),
1834            ("name=value ;  ; other=key", vec![("name", "value"), ("other", "key")]),
1835            ("name=value ;  ; other=key ", vec![("name", "value"), ("other", "key")]),
1836            ("name=value ;  ; other=key;; ", vec![("name", "value"), ("other", "key")]),
1837            (";name=value ;  ; other=key ", vec![("name", "value"), ("other", "key")]),
1838            (";a=1 ;  ; b=2 ", vec![("a", "1"), ("b", "2")]),
1839            (";a=1 ;  ; b= ", vec![("a", "1"), ("b", "")]),
1840            (";a=1 ;  ; =v ; c=", vec![("a", "1"), ("c", "")]),
1841            (" ;   a=1 ;  ; =v ; ;;c=", vec![("a", "1"), ("c", "")]),
1842            (" ;   a=1 ;  ; =v ; ;;c===  ", vec![("a", "1"), ("c", "==")]),
1843        ];
1844
1845        for (string, expected) in cases {
1846            let actual: Vec<_> = Cookie::split_parse(string)
1847                .filter_map(|parse| parse.ok())
1848                .map(|c| (c.name_raw().unwrap(), c.value_raw().unwrap()))
1849                .collect();
1850
1851            assert_eq!(expected, actual);
1852        }
1853    }
1854
1855    #[test]
1856    #[cfg(feature = "percent-encode")]
1857    fn split_parse_encoded() {
1858        let cases = [
1859            ("", vec![]),
1860            (";;", vec![]),
1861            ("name=val%20ue", vec![("name", "val ue")]),
1862            ("foo%20!%25%3F%3D=bar%3B%3B%2C%20a", vec![("foo !%?=", "bar;;, a")]),
1863            (
1864                "name=val%20ue ; ; foo%20!%25%3F%3D=bar%3B%3B%2C%20a",
1865                vec![("name", "val ue"), ("foo !%?=", "bar;;, a")]
1866            ),
1867        ];
1868
1869        for (string, expected) in cases {
1870            let cookies: Vec<_> = Cookie::split_parse_encoded(string)
1871                .filter_map(|parse| parse.ok())
1872                .collect();
1873
1874            let actual: Vec<_> = cookies.iter()
1875                .map(|c| c.name_value())
1876                .collect();
1877
1878            assert_eq!(expected, actual);
1879        }
1880    }
1881}