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.16"
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#![doc(html_root_url = "https://docs.rs/cookie/0.16")]
74#![deny(missing_docs)]
75
76pub use time;
77
78mod builder;
79mod parse;
80mod jar;
81mod delta;
82mod draft;
83mod expiration;
84
85#[cfg(any(feature = "private", feature = "signed"))] #[macro_use] mod secure;
86#[cfg(any(feature = "private", feature = "signed"))] pub use secure::*;
87
88use std::borrow::Cow;
89use std::fmt;
90use std::str::FromStr;
91
92#[allow(unused_imports, deprecated)]
93use std::ascii::AsciiExt;
94
95use time::{Duration, OffsetDateTime, UtcOffset, macros::datetime};
96
97use crate::parse::parse_cookie;
98pub use crate::parse::ParseError;
99pub use crate::builder::CookieBuilder;
100pub use crate::jar::{CookieJar, Delta, Iter};
101pub use crate::draft::*;
102pub use crate::expiration::*;
103
104#[derive(Debug, Clone)]
105enum CookieStr<'c> {
106    /// An string derived from indexes (start, end).
107    Indexed(usize, usize),
108    /// A string derived from a concrete string.
109    Concrete(Cow<'c, str>),
110}
111
112impl<'c> CookieStr<'c> {
113    /// Retrieves the string `self` corresponds to. If `self` is derived from
114    /// indexes, the corresponding subslice of `string` is returned. Otherwise,
115    /// the concrete string is returned.
116    ///
117    /// # Panics
118    ///
119    /// Panics if `self` is an indexed string and `string` is None.
120    fn to_str<'s>(&'s self, string: Option<&'s Cow<str>>) -> &'s str {
121        match *self {
122            CookieStr::Indexed(i, j) => {
123                let s = string.expect("`Some` base string must exist when \
124                    converting indexed str to str! (This is a module invariant.)");
125                &s[i..j]
126            },
127            CookieStr::Concrete(ref cstr) => &*cstr,
128        }
129    }
130
131    #[allow(clippy::ptr_arg)]
132    fn to_raw_str<'s, 'b: 's>(&'s self, string: &'s Cow<'b, str>) -> Option<&'b str> {
133        match *self {
134            CookieStr::Indexed(i, j) => {
135                match *string {
136                    Cow::Borrowed(s) => Some(&s[i..j]),
137                    Cow::Owned(_) => None,
138                }
139            },
140            CookieStr::Concrete(_) => None,
141        }
142    }
143
144    fn into_owned(self) -> CookieStr<'static> {
145        use crate::CookieStr::*;
146
147        match self {
148            Indexed(a, b) => Indexed(a, b),
149            Concrete(Cow::Owned(c)) => Concrete(Cow::Owned(c)),
150            Concrete(Cow::Borrowed(c)) => Concrete(Cow::Owned(c.into())),
151        }
152    }
153}
154
155/// Representation of an HTTP cookie.
156///
157/// # Constructing a `Cookie`
158///
159/// To construct a cookie with only a name/value, use [`Cookie::new()`]:
160///
161/// ```rust
162/// use cookie::Cookie;
163///
164/// let cookie = Cookie::new("name", "value");
165/// assert_eq!(&cookie.to_string(), "name=value");
166/// ```
167///
168/// To construct more elaborate cookies, use [`Cookie::build()`] and
169/// [`CookieBuilder`] methods:
170///
171/// ```rust
172/// use cookie::Cookie;
173///
174/// let cookie = Cookie::build("name", "value")
175///     .domain("www.rust-lang.org")
176///     .path("/")
177///     .secure(true)
178///     .http_only(true)
179///     .finish();
180/// ```
181#[derive(Debug, Clone)]
182pub struct Cookie<'c> {
183    /// Storage for the cookie string. Only used if this structure was derived
184    /// from a string that was subsequently parsed.
185    cookie_string: Option<Cow<'c, str>>,
186    /// The cookie's name.
187    name: CookieStr<'c>,
188    /// The cookie's value.
189    value: CookieStr<'c>,
190    /// The cookie's expiration, if any.
191    expires: Option<Expiration>,
192    /// The cookie's maximum age, if any.
193    max_age: Option<Duration>,
194    /// The cookie's domain, if any.
195    domain: Option<CookieStr<'c>>,
196    /// The cookie's path domain, if any.
197    path: Option<CookieStr<'c>>,
198    /// Whether this cookie was marked Secure.
199    secure: Option<bool>,
200    /// Whether this cookie was marked HttpOnly.
201    http_only: Option<bool>,
202    /// The draft `SameSite` attribute.
203    same_site: Option<SameSite>,
204}
205
206impl<'c> Cookie<'c> {
207    /// Creates a new `Cookie` with the given name and value.
208    ///
209    /// # Example
210    ///
211    /// ```rust
212    /// use cookie::Cookie;
213    ///
214    /// let cookie = Cookie::new("name", "value");
215    /// assert_eq!(cookie.name_value(), ("name", "value"));
216    /// ```
217    pub fn new<N, V>(name: N, value: V) -> Self
218        where N: Into<Cow<'c, str>>,
219              V: Into<Cow<'c, str>>
220    {
221        Cookie {
222            cookie_string: None,
223            name: CookieStr::Concrete(name.into()),
224            value: CookieStr::Concrete(value.into()),
225            expires: None,
226            max_age: None,
227            domain: None,
228            path: None,
229            secure: None,
230            http_only: None,
231            same_site: None,
232        }
233    }
234
235    /// Creates a new `Cookie` with the given name and an empty value.
236    ///
237    /// # Example
238    ///
239    /// ```rust
240    /// use cookie::Cookie;
241    ///
242    /// let cookie = Cookie::named("name");
243    /// assert_eq!(cookie.name(), "name");
244    /// assert!(cookie.value().is_empty());
245    /// ```
246    pub fn named<N>(name: N) -> Cookie<'c>
247        where N: Into<Cow<'c, str>>
248    {
249        Cookie::new(name, "")
250    }
251
252    /// Creates a new `CookieBuilder` instance from the given key and value
253    /// strings.
254    ///
255    /// # Example
256    ///
257    /// ```
258    /// use cookie::Cookie;
259    ///
260    /// let c = Cookie::build("foo", "bar").finish();
261    /// assert_eq!(c.name_value(), ("foo", "bar"));
262    /// ```
263    pub fn build<N, V>(name: N, value: V) -> CookieBuilder<'c>
264        where N: Into<Cow<'c, str>>,
265              V: Into<Cow<'c, str>>
266    {
267        CookieBuilder::new(name, value)
268    }
269
270    /// Parses a `Cookie` from the given HTTP cookie header value string. Does
271    /// not perform any percent-decoding.
272    ///
273    /// # Example
274    ///
275    /// ```
276    /// use cookie::Cookie;
277    ///
278    /// let c = Cookie::parse("foo=bar%20baz; HttpOnly").unwrap();
279    /// assert_eq!(c.name_value(), ("foo", "bar%20baz"));
280    /// assert_eq!(c.http_only(), Some(true));
281    /// ```
282    pub fn parse<S>(s: S) -> Result<Cookie<'c>, ParseError>
283        where S: Into<Cow<'c, str>>
284    {
285        parse_cookie(s, false)
286    }
287
288    /// Parses a `Cookie` from the given HTTP cookie header value string where
289    /// the name and value fields are percent-encoded. Percent-decodes the
290    /// name/value fields.
291    ///
292    /// # Example
293    ///
294    /// ```
295    /// use cookie::Cookie;
296    ///
297    /// let c = Cookie::parse_encoded("foo=bar%20baz; HttpOnly").unwrap();
298    /// assert_eq!(c.name_value(), ("foo", "bar baz"));
299    /// assert_eq!(c.http_only(), Some(true));
300    /// ```
301    #[cfg(feature = "percent-encode")]
302    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "percent-encode")))]
303    pub fn parse_encoded<S>(s: S) -> Result<Cookie<'c>, ParseError>
304        where S: Into<Cow<'c, str>>
305    {
306        parse_cookie(s, true)
307    }
308
309    /// Converts `self` into a `Cookie` with a static lifetime with as few
310    /// allocations as possible.
311    ///
312    /// # Example
313    ///
314    /// ```
315    /// use cookie::Cookie;
316    ///
317    /// let c = Cookie::new("a", "b");
318    /// let owned_cookie = c.into_owned();
319    /// assert_eq!(owned_cookie.name_value(), ("a", "b"));
320    /// ```
321    pub fn into_owned(self) -> Cookie<'static> {
322        Cookie {
323            cookie_string: self.cookie_string.map(|s| s.into_owned().into()),
324            name: self.name.into_owned(),
325            value: self.value.into_owned(),
326            expires: self.expires,
327            max_age: self.max_age,
328            domain: self.domain.map(|s| s.into_owned()),
329            path: self.path.map(|s| s.into_owned()),
330            secure: self.secure,
331            http_only: self.http_only,
332            same_site: self.same_site,
333        }
334    }
335
336    /// Returns the name of `self`.
337    ///
338    /// # Example
339    ///
340    /// ```
341    /// use cookie::Cookie;
342    ///
343    /// let c = Cookie::new("name", "value");
344    /// assert_eq!(c.name(), "name");
345    /// ```
346    #[inline]
347    pub fn name(&self) -> &str {
348        self.name.to_str(self.cookie_string.as_ref())
349    }
350
351    /// Returns the value of `self`.
352    ///
353    /// # Example
354    ///
355    /// ```
356    /// use cookie::Cookie;
357    ///
358    /// let c = Cookie::new("name", "value");
359    /// assert_eq!(c.value(), "value");
360    /// ```
361    #[inline]
362    pub fn value(&self) -> &str {
363        self.value.to_str(self.cookie_string.as_ref())
364    }
365
366    /// Returns the name and value of `self` as a tuple of `(name, value)`.
367    ///
368    /// # Example
369    ///
370    /// ```
371    /// use cookie::Cookie;
372    ///
373    /// let c = Cookie::new("name", "value");
374    /// assert_eq!(c.name_value(), ("name", "value"));
375    /// ```
376    #[inline]
377    pub fn name_value(&self) -> (&str, &str) {
378        (self.name(), self.value())
379    }
380
381    /// Returns whether this cookie was marked `HttpOnly` or not. Returns
382    /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
383    /// `HttpOnly`, `Some(false)` when `http_only` was manually set to `false`,
384    /// and `None` otherwise.
385    ///
386    /// # Example
387    ///
388    /// ```
389    /// use cookie::Cookie;
390    ///
391    /// let c = Cookie::parse("name=value; httponly").unwrap();
392    /// assert_eq!(c.http_only(), Some(true));
393    ///
394    /// let mut c = Cookie::new("name", "value");
395    /// assert_eq!(c.http_only(), None);
396    ///
397    /// let mut c = Cookie::new("name", "value");
398    /// assert_eq!(c.http_only(), None);
399    ///
400    /// // An explicitly set "false" value.
401    /// c.set_http_only(false);
402    /// assert_eq!(c.http_only(), Some(false));
403    ///
404    /// // An explicitly set "true" value.
405    /// c.set_http_only(true);
406    /// assert_eq!(c.http_only(), Some(true));
407    /// ```
408    #[inline]
409    pub fn http_only(&self) -> Option<bool> {
410        self.http_only
411    }
412
413    /// Returns whether this cookie was marked `Secure` or not. Returns
414    /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
415    /// `Secure`, `Some(false)` when `secure` was manually set to `false`, and
416    /// `None` otherwise.
417    ///
418    /// # Example
419    ///
420    /// ```
421    /// use cookie::Cookie;
422    ///
423    /// let c = Cookie::parse("name=value; Secure").unwrap();
424    /// assert_eq!(c.secure(), Some(true));
425    ///
426    /// let mut c = Cookie::parse("name=value").unwrap();
427    /// assert_eq!(c.secure(), None);
428    ///
429    /// let mut c = Cookie::new("name", "value");
430    /// assert_eq!(c.secure(), None);
431    ///
432    /// // An explicitly set "false" value.
433    /// c.set_secure(false);
434    /// assert_eq!(c.secure(), Some(false));
435    ///
436    /// // An explicitly set "true" value.
437    /// c.set_secure(true);
438    /// assert_eq!(c.secure(), Some(true));
439    /// ```
440    #[inline]
441    pub fn secure(&self) -> Option<bool> {
442        self.secure
443    }
444
445    /// Returns the `SameSite` attribute of this cookie if one was specified.
446    ///
447    /// # Example
448    ///
449    /// ```
450    /// use cookie::{Cookie, SameSite};
451    ///
452    /// let c = Cookie::parse("name=value; SameSite=Lax").unwrap();
453    /// assert_eq!(c.same_site(), Some(SameSite::Lax));
454    /// ```
455    #[inline]
456    pub fn same_site(&self) -> Option<SameSite> {
457        self.same_site
458    }
459
460    /// Returns the specified max-age of the cookie if one was specified.
461    ///
462    /// # Example
463    ///
464    /// ```
465    /// use cookie::Cookie;
466    ///
467    /// let c = Cookie::parse("name=value").unwrap();
468    /// assert_eq!(c.max_age(), None);
469    ///
470    /// let c = Cookie::parse("name=value; Max-Age=3600").unwrap();
471    /// assert_eq!(c.max_age().map(|age| age.whole_hours()), Some(1));
472    /// ```
473    #[inline]
474    pub fn max_age(&self) -> Option<Duration> {
475        self.max_age
476    }
477
478    /// Returns the `Path` of the cookie if one was specified.
479    ///
480    /// # Example
481    ///
482    /// ```
483    /// use cookie::Cookie;
484    ///
485    /// let c = Cookie::parse("name=value").unwrap();
486    /// assert_eq!(c.path(), None);
487    ///
488    /// let c = Cookie::parse("name=value; Path=/").unwrap();
489    /// assert_eq!(c.path(), Some("/"));
490    ///
491    /// let c = Cookie::parse("name=value; path=/sub").unwrap();
492    /// assert_eq!(c.path(), Some("/sub"));
493    /// ```
494    #[inline]
495    pub fn path(&self) -> Option<&str> {
496        match self.path {
497            Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
498            None => None,
499        }
500    }
501
502    /// Returns the `Domain` of the cookie if one was specified.
503    ///
504    /// # Example
505    ///
506    /// ```
507    /// use cookie::Cookie;
508    ///
509    /// let c = Cookie::parse("name=value").unwrap();
510    /// assert_eq!(c.domain(), None);
511    ///
512    /// let c = Cookie::parse("name=value; Domain=crates.io").unwrap();
513    /// assert_eq!(c.domain(), Some("crates.io"));
514    /// ```
515    #[inline]
516    pub fn domain(&self) -> Option<&str> {
517        match self.domain {
518            Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
519            None => None,
520        }
521    }
522
523    /// Returns the [`Expiration`] of the cookie if one was specified.
524    ///
525    /// # Example
526    ///
527    /// ```
528    /// use cookie::{Cookie, Expiration};
529    ///
530    /// let c = Cookie::parse("name=value").unwrap();
531    /// assert_eq!(c.expires(), None);
532    ///
533    /// // Here, `cookie.expires_datetime()` returns `None`.
534    /// let c = Cookie::build("name", "value").expires(None).finish();
535    /// assert_eq!(c.expires(), Some(Expiration::Session));
536    ///
537    /// let expire_time = "Wed, 21 Oct 2017 07:28:00 GMT";
538    /// let cookie_str = format!("name=value; Expires={}", expire_time);
539    /// let c = Cookie::parse(cookie_str).unwrap();
540    /// assert_eq!(c.expires().and_then(|e| e.datetime()).map(|t| t.year()), Some(2017));
541    /// ```
542    #[inline]
543    pub fn expires(&self) -> Option<Expiration> {
544        self.expires
545    }
546
547    /// Returns the expiration date-time of the cookie if one was specified.
548    ///
549    /// # Example
550    ///
551    /// ```
552    /// use cookie::Cookie;
553    ///
554    /// let c = Cookie::parse("name=value").unwrap();
555    /// assert_eq!(c.expires_datetime(), None);
556    ///
557    /// // Here, `cookie.expires()` returns `Some`.
558    /// let c = Cookie::build("name", "value").expires(None).finish();
559    /// assert_eq!(c.expires_datetime(), None);
560    ///
561    /// let expire_time = "Wed, 21 Oct 2017 07:28:00 GMT";
562    /// let cookie_str = format!("name=value; Expires={}", expire_time);
563    /// let c = Cookie::parse(cookie_str).unwrap();
564    /// assert_eq!(c.expires_datetime().map(|t| t.year()), Some(2017));
565    /// ```
566    #[inline]
567    pub fn expires_datetime(&self) -> Option<OffsetDateTime> {
568        self.expires.and_then(|e| e.datetime())
569    }
570
571    /// Sets the name of `self` to `name`.
572    ///
573    /// # Example
574    ///
575    /// ```
576    /// use cookie::Cookie;
577    ///
578    /// let mut c = Cookie::new("name", "value");
579    /// assert_eq!(c.name(), "name");
580    ///
581    /// c.set_name("foo");
582    /// assert_eq!(c.name(), "foo");
583    /// ```
584    pub fn set_name<N: Into<Cow<'c, str>>>(&mut self, name: N) {
585        self.name = CookieStr::Concrete(name.into())
586    }
587
588    /// Sets the value of `self` to `value`.
589    ///
590    /// # Example
591    ///
592    /// ```
593    /// use cookie::Cookie;
594    ///
595    /// let mut c = Cookie::new("name", "value");
596    /// assert_eq!(c.value(), "value");
597    ///
598    /// c.set_value("bar");
599    /// assert_eq!(c.value(), "bar");
600    /// ```
601    pub fn set_value<V: Into<Cow<'c, str>>>(&mut self, value: V) {
602        self.value = CookieStr::Concrete(value.into())
603    }
604
605    /// Sets the value of `http_only` in `self` to `value`.  If `value` is
606    /// `None`, the field is unset.
607    ///
608    /// # Example
609    ///
610    /// ```
611    /// use cookie::Cookie;
612    ///
613    /// let mut c = Cookie::new("name", "value");
614    /// assert_eq!(c.http_only(), None);
615    ///
616    /// c.set_http_only(true);
617    /// assert_eq!(c.http_only(), Some(true));
618    ///
619    /// c.set_http_only(false);
620    /// assert_eq!(c.http_only(), Some(false));
621    ///
622    /// c.set_http_only(None);
623    /// assert_eq!(c.http_only(), None);
624    /// ```
625    #[inline]
626    pub fn set_http_only<T: Into<Option<bool>>>(&mut self, value: T) {
627        self.http_only = value.into();
628    }
629
630    /// Sets the value of `secure` in `self` to `value`. If `value` is `None`,
631    /// the field is unset.
632    ///
633    /// # Example
634    ///
635    /// ```
636    /// use cookie::Cookie;
637    ///
638    /// let mut c = Cookie::new("name", "value");
639    /// assert_eq!(c.secure(), None);
640    ///
641    /// c.set_secure(true);
642    /// assert_eq!(c.secure(), Some(true));
643    ///
644    /// c.set_secure(false);
645    /// assert_eq!(c.secure(), Some(false));
646    ///
647    /// c.set_secure(None);
648    /// assert_eq!(c.secure(), None);
649    /// ```
650    #[inline]
651    pub fn set_secure<T: Into<Option<bool>>>(&mut self, value: T) {
652        self.secure = value.into();
653    }
654
655    /// Sets the value of `same_site` in `self` to `value`. If `value` is
656    /// `None`, the field is unset. If `value` is `SameSite::None`, the "Secure"
657    /// flag will be set when the cookie is written out unless `secure` is
658    /// explicitly set to `false` via [`Cookie::set_secure()`] or the equivalent
659    /// builder method.
660    ///
661    /// [HTTP draft]: https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
662    ///
663    /// # Example
664    ///
665    /// ```
666    /// use cookie::{Cookie, SameSite};
667    ///
668    /// let mut c = Cookie::new("name", "value");
669    /// assert_eq!(c.same_site(), None);
670    ///
671    /// c.set_same_site(SameSite::None);
672    /// assert_eq!(c.same_site(), Some(SameSite::None));
673    /// assert_eq!(c.to_string(), "name=value; SameSite=None; Secure");
674    ///
675    /// c.set_secure(false);
676    /// assert_eq!(c.to_string(), "name=value; SameSite=None");
677    ///
678    /// let mut c = Cookie::new("name", "value");
679    /// assert_eq!(c.same_site(), None);
680    ///
681    /// c.set_same_site(SameSite::Strict);
682    /// assert_eq!(c.same_site(), Some(SameSite::Strict));
683    /// assert_eq!(c.to_string(), "name=value; SameSite=Strict");
684    ///
685    /// c.set_same_site(None);
686    /// assert_eq!(c.same_site(), None);
687    /// assert_eq!(c.to_string(), "name=value");
688    /// ```
689    #[inline]
690    pub fn set_same_site<T: Into<Option<SameSite>>>(&mut self, value: T) {
691        self.same_site = value.into();
692    }
693
694    /// Sets the value of `max_age` in `self` to `value`. If `value` is `None`,
695    /// the field is unset.
696    ///
697    /// # Example
698    ///
699    /// ```rust
700    /// # extern crate cookie;
701    /// use cookie::Cookie;
702    /// use cookie::time::Duration;
703    ///
704    /// # fn main() {
705    /// let mut c = Cookie::new("name", "value");
706    /// assert_eq!(c.max_age(), None);
707    ///
708    /// c.set_max_age(Duration::hours(10));
709    /// assert_eq!(c.max_age(), Some(Duration::hours(10)));
710    ///
711    /// c.set_max_age(None);
712    /// assert!(c.max_age().is_none());
713    /// # }
714    /// ```
715    #[inline]
716    pub fn set_max_age<D: Into<Option<Duration>>>(&mut self, value: D) {
717        self.max_age = value.into();
718    }
719
720    /// Sets the `path` of `self` to `path`.
721    ///
722    /// # Example
723    ///
724    /// ```rust
725    /// use cookie::Cookie;
726    ///
727    /// let mut c = Cookie::new("name", "value");
728    /// assert_eq!(c.path(), None);
729    ///
730    /// c.set_path("/");
731    /// assert_eq!(c.path(), Some("/"));
732    /// ```
733    pub fn set_path<P: Into<Cow<'c, str>>>(&mut self, path: P) {
734        self.path = Some(CookieStr::Concrete(path.into()));
735    }
736
737    /// Unsets the `path` of `self`.
738    ///
739    /// # Example
740    ///
741    /// ```
742    /// use cookie::Cookie;
743    ///
744    /// let mut c = Cookie::new("name", "value");
745    /// assert_eq!(c.path(), None);
746    ///
747    /// c.set_path("/");
748    /// assert_eq!(c.path(), Some("/"));
749    ///
750    /// c.unset_path();
751    /// assert_eq!(c.path(), None);
752    /// ```
753    pub fn unset_path(&mut self) {
754        self.path = None;
755    }
756
757    /// Sets the `domain` of `self` to `domain`.
758    ///
759    /// # Example
760    ///
761    /// ```
762    /// use cookie::Cookie;
763    ///
764    /// let mut c = Cookie::new("name", "value");
765    /// assert_eq!(c.domain(), None);
766    ///
767    /// c.set_domain("rust-lang.org");
768    /// assert_eq!(c.domain(), Some("rust-lang.org"));
769    /// ```
770    pub fn set_domain<D: Into<Cow<'c, str>>>(&mut self, domain: D) {
771        self.domain = Some(CookieStr::Concrete(domain.into()));
772    }
773
774    /// Unsets the `domain` of `self`.
775    ///
776    /// # Example
777    ///
778    /// ```
779    /// use cookie::Cookie;
780    ///
781    /// let mut c = Cookie::new("name", "value");
782    /// assert_eq!(c.domain(), None);
783    ///
784    /// c.set_domain("rust-lang.org");
785    /// assert_eq!(c.domain(), Some("rust-lang.org"));
786    ///
787    /// c.unset_domain();
788    /// assert_eq!(c.domain(), None);
789    /// ```
790    pub fn unset_domain(&mut self) {
791        self.domain = None;
792    }
793
794    /// Sets the expires field of `self` to `time`. If `time` is `None`, an
795    /// expiration of [`Session`](Expiration::Session) is set.
796    ///
797    /// # Example
798    ///
799    /// ```
800    /// # extern crate cookie;
801    /// use cookie::{Cookie, Expiration};
802    /// use cookie::time::{Duration, OffsetDateTime};
803    ///
804    /// let mut c = Cookie::new("name", "value");
805    /// assert_eq!(c.expires(), None);
806    ///
807    /// let mut now = OffsetDateTime::now_utc();
808    /// now += Duration::weeks(52);
809    ///
810    /// c.set_expires(now);
811    /// assert!(c.expires().is_some());
812    ///
813    /// c.set_expires(None);
814    /// assert_eq!(c.expires(), Some(Expiration::Session));
815    /// ```
816    pub fn set_expires<T: Into<Expiration>>(&mut self, time: T) {
817        static MAX_DATETIME: OffsetDateTime = datetime!(9999-12-31 23:59:59.999_999 UTC);
818
819        // RFC 6265 requires dates not to exceed 9999 years.
820        self.expires = Some(time.into()
821            .map(|time| std::cmp::min(time, MAX_DATETIME)));
822    }
823
824    /// Unsets the `expires` of `self`.
825    ///
826    /// # Example
827    ///
828    /// ```
829    /// use cookie::{Cookie, Expiration};
830    ///
831    /// let mut c = Cookie::new("name", "value");
832    /// assert_eq!(c.expires(), None);
833    ///
834    /// c.set_expires(None);
835    /// assert_eq!(c.expires(), Some(Expiration::Session));
836    ///
837    /// c.unset_expires();
838    /// assert_eq!(c.expires(), None);
839    /// ```
840    pub fn unset_expires(&mut self) {
841        self.expires = None;
842    }
843
844    /// Makes `self` a "permanent" cookie by extending its expiration and max
845    /// age 20 years into the future.
846    ///
847    /// # Example
848    ///
849    /// ```rust
850    /// # extern crate cookie;
851    /// use cookie::Cookie;
852    /// use cookie::time::Duration;
853    ///
854    /// # fn main() {
855    /// let mut c = Cookie::new("foo", "bar");
856    /// assert!(c.expires().is_none());
857    /// assert!(c.max_age().is_none());
858    ///
859    /// c.make_permanent();
860    /// assert!(c.expires().is_some());
861    /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
862    /// # }
863    /// ```
864    pub fn make_permanent(&mut self) {
865        let twenty_years = Duration::days(365 * 20);
866        self.set_max_age(twenty_years);
867        self.set_expires(OffsetDateTime::now_utc() + twenty_years);
868    }
869
870    /// Make `self` a "removal" cookie by clearing its value, setting a max-age
871    /// of `0`, and setting an expiration date far in the past.
872    ///
873    /// # Example
874    ///
875    /// ```rust
876    /// # extern crate cookie;
877    /// use cookie::Cookie;
878    /// use cookie::time::Duration;
879    ///
880    /// # fn main() {
881    /// let mut c = Cookie::new("foo", "bar");
882    /// c.make_permanent();
883    /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
884    /// assert_eq!(c.value(), "bar");
885    ///
886    /// c.make_removal();
887    /// assert_eq!(c.value(), "");
888    /// assert_eq!(c.max_age(), Some(Duration::ZERO));
889    /// # }
890    /// ```
891    pub fn make_removal(&mut self) {
892        self.set_value("");
893        self.set_max_age(Duration::seconds(0));
894        self.set_expires(OffsetDateTime::now_utc() - Duration::days(365));
895    }
896
897    fn fmt_parameters(&self, f: &mut fmt::Formatter) -> fmt::Result {
898        if let Some(true) = self.http_only() {
899            write!(f, "; HttpOnly")?;
900        }
901
902        if let Some(same_site) = self.same_site() {
903            write!(f, "; SameSite={}", same_site)?;
904
905            if same_site.is_none() && self.secure().is_none() {
906                write!(f, "; Secure")?;
907            }
908        }
909
910        if let Some(true) = self.secure() {
911            write!(f, "; Secure")?;
912        }
913
914        if let Some(path) = self.path() {
915            write!(f, "; Path={}", path)?;
916        }
917
918        if let Some(domain) = self.domain() {
919            write!(f, "; Domain={}", domain)?;
920        }
921
922        if let Some(max_age) = self.max_age() {
923            write!(f, "; Max-Age={}", max_age.whole_seconds())?;
924        }
925
926        if let Some(time) = self.expires_datetime() {
927            let time = time.to_offset(UtcOffset::UTC);
928            write!(f, "; Expires={}", time.format(&crate::parse::FMT1).map_err(|_| fmt::Error)?)?;
929        }
930
931        Ok(())
932    }
933
934    /// Returns the name of `self` as a string slice of the raw string `self`
935    /// was originally parsed from. If `self` was not originally parsed from a
936    /// raw string, returns `None`.
937    ///
938    /// This method differs from [`Cookie::name()`] in that it returns a string
939    /// with the same lifetime as the originally parsed string. This lifetime
940    /// may outlive `self`. If a longer lifetime is not required, or you're
941    /// unsure if you need a longer lifetime, use [`Cookie::name()`].
942    ///
943    /// # Example
944    ///
945    /// ```
946    /// use cookie::Cookie;
947    ///
948    /// let cookie_string = format!("{}={}", "foo", "bar");
949    ///
950    /// // `c` will be dropped at the end of the scope, but `name` will live on
951    /// let name = {
952    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
953    ///     c.name_raw()
954    /// };
955    ///
956    /// assert_eq!(name, Some("foo"));
957    /// ```
958    #[inline]
959    pub fn name_raw(&self) -> Option<&'c str> {
960        self.cookie_string.as_ref()
961            .and_then(|s| self.name.to_raw_str(s))
962    }
963
964    /// Returns the value of `self` as a string slice of the raw string `self`
965    /// was originally parsed from. If `self` was not originally parsed from a
966    /// raw string, returns `None`.
967    ///
968    /// This method differs from [`Cookie::value()`] in that it returns a
969    /// string with the same lifetime as the originally parsed string. This
970    /// lifetime may outlive `self`. If a longer lifetime is not required, or
971    /// you're unsure if you need a longer lifetime, use [`Cookie::value()`].
972    ///
973    /// # Example
974    ///
975    /// ```
976    /// use cookie::Cookie;
977    ///
978    /// let cookie_string = format!("{}={}", "foo", "bar");
979    ///
980    /// // `c` will be dropped at the end of the scope, but `value` will live on
981    /// let value = {
982    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
983    ///     c.value_raw()
984    /// };
985    ///
986    /// assert_eq!(value, Some("bar"));
987    /// ```
988    #[inline]
989    pub fn value_raw(&self) -> Option<&'c str> {
990        self.cookie_string.as_ref()
991            .and_then(|s| self.value.to_raw_str(s))
992    }
993
994    /// Returns the `Path` of `self` as a string slice of the raw string `self`
995    /// was originally parsed from. If `self` was not originally parsed from a
996    /// raw string, or if `self` doesn't contain a `Path`, or if the `Path` has
997    /// changed since parsing, returns `None`.
998    ///
999    /// This method differs from [`Cookie::path()`] in that it returns a
1000    /// string with the same lifetime as the originally parsed string. This
1001    /// lifetime may outlive `self`. If a longer lifetime is not required, or
1002    /// you're unsure if you need a longer lifetime, use [`Cookie::path()`].
1003    ///
1004    /// # Example
1005    ///
1006    /// ```
1007    /// use cookie::Cookie;
1008    ///
1009    /// let cookie_string = format!("{}={}; Path=/", "foo", "bar");
1010    ///
1011    /// // `c` will be dropped at the end of the scope, but `path` will live on
1012    /// let path = {
1013    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
1014    ///     c.path_raw()
1015    /// };
1016    ///
1017    /// assert_eq!(path, Some("/"));
1018    /// ```
1019    #[inline]
1020    pub fn path_raw(&self) -> Option<&'c str> {
1021        match (self.path.as_ref(), self.cookie_string.as_ref()) {
1022            (Some(path), Some(string)) => path.to_raw_str(string),
1023            _ => None,
1024        }
1025    }
1026
1027    /// Returns the `Domain` of `self` as a string slice of the raw string
1028    /// `self` was originally parsed from. If `self` was not originally parsed
1029    /// from a raw string, or if `self` doesn't contain a `Domain`, or if the
1030    /// `Domain` has changed since parsing, returns `None`.
1031    ///
1032    /// This method differs from [`Cookie::domain()`] in that it returns a
1033    /// string with the same lifetime as the originally parsed string. This
1034    /// lifetime may outlive `self` struct. If a longer lifetime is not
1035    /// required, or you're unsure if you need a longer lifetime, use
1036    /// [`Cookie::domain()`].
1037    ///
1038    /// # Example
1039    ///
1040    /// ```
1041    /// use cookie::Cookie;
1042    ///
1043    /// let cookie_string = format!("{}={}; Domain=crates.io", "foo", "bar");
1044    ///
1045    /// //`c` will be dropped at the end of the scope, but `domain` will live on
1046    /// let domain = {
1047    ///     let c = Cookie::parse(cookie_string.as_str()).unwrap();
1048    ///     c.domain_raw()
1049    /// };
1050    ///
1051    /// assert_eq!(domain, Some("crates.io"));
1052    /// ```
1053    #[inline]
1054    pub fn domain_raw(&self) -> Option<&'c str> {
1055        match (self.domain.as_ref(), self.cookie_string.as_ref()) {
1056            (Some(domain), Some(string)) => domain.to_raw_str(string),
1057            _ => None,
1058        }
1059    }
1060
1061    /// Wraps `self` in an encoded [`Display`]: a cost-free wrapper around
1062    /// `Cookie` whose [`fmt::Display`] implementation percent-encodes the name
1063    /// and value of the wrapped `Cookie`.
1064    ///
1065    /// The returned structure can be chained with [`Display::stripped()`] to
1066    /// display only the name and value.
1067    ///
1068    /// # Example
1069    ///
1070    /// ```rust
1071    /// use cookie::Cookie;
1072    ///
1073    /// let mut c = Cookie::build("my name", "this; value?").secure(true).finish();
1074    /// assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%3F; Secure");
1075    /// assert_eq!(&c.encoded().stripped().to_string(), "my%20name=this%3B%20value%3F");
1076    /// ```
1077    #[cfg(feature = "percent-encode")]
1078    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "percent-encode")))]
1079    #[inline(always)]
1080    pub fn encoded<'a>(&'a self) -> Display<'a, 'c> {
1081        Display::new_encoded(self)
1082    }
1083
1084    /// Wraps `self` in a stripped `Display`]: a cost-free wrapper around
1085    /// `Cookie` whose [`fmt::Display`] implementation prints only the `name`
1086    /// and `value` of the wrapped `Cookie`.
1087    ///
1088    /// The returned structure can be chained with [`Display::encoded()`] to
1089    /// encode the name and value.
1090    ///
1091    /// # Example
1092    ///
1093    /// ```rust
1094    /// use cookie::Cookie;
1095    ///
1096    /// let mut c = Cookie::build("key?", "value").secure(true).path("/").finish();
1097    /// assert_eq!(&c.stripped().to_string(), "key?=value");
1098    #[cfg_attr(feature = "percent-encode", doc = r##"
1099// Note: `encoded()` is only available when `percent-encode` is enabled.
1100assert_eq!(&c.stripped().encoded().to_string(), "key%3F=value");
1101    #"##)]
1102    /// ```
1103    #[inline(always)]
1104    pub fn stripped<'a>(&'a self) -> Display<'a, 'c> {
1105        Display::new_stripped(self)
1106    }
1107}
1108
1109#[cfg(feature = "percent-encode")]
1110mod encoding {
1111    use percent_encoding::{AsciiSet, CONTROLS};
1112
1113    /// https://url.spec.whatwg.org/#fragment-percent-encode-set
1114    const FRAGMENT: &AsciiSet = &CONTROLS
1115        .add(b' ')
1116        .add(b'"')
1117        .add(b'<')
1118        .add(b'>')
1119        .add(b'`');
1120
1121    /// https://url.spec.whatwg.org/#path-percent-encode-set
1122    const PATH: &AsciiSet = &FRAGMENT
1123        .add(b'#')
1124        .add(b'?')
1125        .add(b'{')
1126        .add(b'}');
1127
1128    /// https://url.spec.whatwg.org/#userinfo-percent-encode-set
1129    const USERINFO: &AsciiSet = &PATH
1130        .add(b'/')
1131        .add(b':')
1132        .add(b';')
1133        .add(b'=')
1134        .add(b'@')
1135        .add(b'[')
1136        .add(b'\\')
1137        .add(b']')
1138        .add(b'^')
1139        .add(b'|')
1140        .add(b'%');
1141
1142    /// https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1 + '(', ')'
1143    const COOKIE: &AsciiSet = &USERINFO
1144        .add(b'(')
1145        .add(b')')
1146        .add(b',');
1147
1148    /// Percent-encode a cookie name or value with the proper encoding set.
1149    pub fn encode(string: &str) -> impl std::fmt::Display + '_ {
1150        percent_encoding::percent_encode(string.as_bytes(), COOKIE)
1151    }
1152}
1153
1154/// Wrapper around `Cookie` whose `Display` implementation either
1155/// percent-encodes the cookie's name and value, skips displaying the cookie's
1156/// parameters (only displaying it's name and value), or both.
1157///
1158/// A value of this type can be obtained via [`Cookie::encoded()`] and
1159/// [`Cookie::stripped()`], or an arbitrary chaining of the two methods. This
1160/// type should only be used for its `Display` implementation.
1161///
1162/// # Example
1163///
1164/// ```rust
1165/// use cookie::Cookie;
1166///
1167/// let c = Cookie::build("my name", "this; value%?").secure(true).finish();
1168/// assert_eq!(&c.stripped().to_string(), "my name=this; value%?");
1169#[cfg_attr(feature = "percent-encode", doc = r##"
1170// Note: `encoded()` is only available when `percent-encode` is enabled.
1171assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%25%3F; Secure");
1172assert_eq!(&c.stripped().encoded().to_string(), "my%20name=this%3B%20value%25%3F");
1173assert_eq!(&c.encoded().stripped().to_string(), "my%20name=this%3B%20value%25%3F");
1174"##)]
1175/// ```
1176pub struct Display<'a, 'c: 'a> {
1177    cookie: &'a Cookie<'c>,
1178    #[cfg(feature = "percent-encode")]
1179    encode: bool,
1180    strip: bool,
1181}
1182
1183impl<'a, 'c: 'a> fmt::Display for Display<'a, 'c> {
1184    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1185        #[cfg(feature = "percent-encode")] {
1186            if self.encode {
1187                let name = encoding::encode(self.cookie.name());
1188                let value = encoding::encode(self.cookie.value());
1189                write!(f, "{}={}", name, value)?;
1190            } else {
1191                write!(f, "{}={}", self.cookie.name(), self.cookie.value())?;
1192            }
1193        }
1194
1195        #[cfg(not(feature = "percent-encode"))] {
1196            write!(f, "{}={}", self.cookie.name(), self.cookie.value())?;
1197        }
1198
1199        match self.strip {
1200            true => Ok(()),
1201            false => self.cookie.fmt_parameters(f)
1202        }
1203    }
1204}
1205
1206impl<'a, 'c> Display<'a, 'c> {
1207    #[cfg(feature = "percent-encode")]
1208    fn new_encoded(cookie: &'a Cookie<'c>) -> Self {
1209        Display { cookie, strip: false, encode: true }
1210    }
1211
1212    fn new_stripped(cookie: &'a Cookie<'c>) -> Self {
1213        Display { cookie, strip: true, #[cfg(feature = "percent-encode")] encode: false }
1214    }
1215
1216    /// Percent-encode the name and value pair.
1217    #[inline]
1218    #[cfg(feature = "percent-encode")]
1219    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "percent-encode")))]
1220    pub fn encoded(mut self) -> Self {
1221        self.encode = true;
1222        self
1223    }
1224
1225    /// Only display the name and value.
1226    #[inline]
1227    pub fn stripped(mut self) -> Self {
1228        self.strip = true;
1229        self
1230    }
1231}
1232
1233impl<'c> fmt::Display for Cookie<'c> {
1234    /// Formats the cookie `self` as a `Set-Cookie` header value.
1235    ///
1236    /// Does _not_ percent-encode any values. To percent-encode, use
1237    /// [`Cookie::encoded()`].
1238    ///
1239    /// # Example
1240    ///
1241    /// ```rust
1242    /// use cookie::Cookie;
1243    ///
1244    /// let mut cookie = Cookie::build("foo", "bar")
1245    ///     .path("/")
1246    ///     .finish();
1247    ///
1248    /// assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
1249    /// ```
1250    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1251        write!(f, "{}={}", self.name(), self.value())?;
1252        self.fmt_parameters(f)
1253    }
1254}
1255
1256impl FromStr for Cookie<'static> {
1257    type Err = ParseError;
1258
1259    fn from_str(s: &str) -> Result<Cookie<'static>, ParseError> {
1260        Cookie::parse(s).map(|c| c.into_owned())
1261    }
1262}
1263
1264impl<'a, 'b> PartialEq<Cookie<'b>> for Cookie<'a> {
1265    fn eq(&self, other: &Cookie<'b>) -> bool {
1266        let so_far_so_good = self.name() == other.name()
1267            && self.value() == other.value()
1268            && self.http_only() == other.http_only()
1269            && self.secure() == other.secure()
1270            && self.max_age() == other.max_age()
1271            && self.expires() == other.expires();
1272
1273        if !so_far_so_good {
1274            return false;
1275        }
1276
1277        match (self.path(), other.path()) {
1278            (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
1279            (None, None) => {}
1280            _ => return false,
1281        };
1282
1283        match (self.domain(), other.domain()) {
1284            (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
1285            (None, None) => {}
1286            _ => return false,
1287        };
1288
1289        true
1290    }
1291}
1292
1293#[cfg(test)]
1294mod tests {
1295    use crate::{Cookie, SameSite, parse::parse_date};
1296    use time::{Duration, OffsetDateTime};
1297
1298    #[test]
1299    fn format() {
1300        let cookie = Cookie::new("foo", "bar");
1301        assert_eq!(&cookie.to_string(), "foo=bar");
1302
1303        let cookie = Cookie::build("foo", "bar")
1304            .http_only(true).finish();
1305        assert_eq!(&cookie.to_string(), "foo=bar; HttpOnly");
1306
1307        let cookie = Cookie::build("foo", "bar")
1308            .max_age(Duration::seconds(10)).finish();
1309        assert_eq!(&cookie.to_string(), "foo=bar; Max-Age=10");
1310
1311        let cookie = Cookie::build("foo", "bar")
1312            .secure(true).finish();
1313        assert_eq!(&cookie.to_string(), "foo=bar; Secure");
1314
1315        let cookie = Cookie::build("foo", "bar")
1316            .path("/").finish();
1317        assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
1318
1319        let cookie = Cookie::build("foo", "bar")
1320            .domain("www.rust-lang.org").finish();
1321        assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");
1322
1323        let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
1324        let expires = parse_date(time_str, &crate::parse::FMT1).unwrap();
1325        let cookie = Cookie::build("foo", "bar")
1326            .expires(expires).finish();
1327        assert_eq!(&cookie.to_string(),
1328                   "foo=bar; Expires=Wed, 21 Oct 2015 07:28:00 GMT");
1329
1330        let cookie = Cookie::build("foo", "bar")
1331            .same_site(SameSite::Strict).finish();
1332        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Strict");
1333
1334        let cookie = Cookie::build("foo", "bar")
1335            .same_site(SameSite::Lax).finish();
1336        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Lax");
1337
1338        let mut cookie = Cookie::build("foo", "bar")
1339            .same_site(SameSite::None).finish();
1340        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=None; Secure");
1341
1342        cookie.set_same_site(None);
1343        assert_eq!(&cookie.to_string(), "foo=bar");
1344
1345        let mut cookie = Cookie::build("foo", "bar")
1346            .same_site(SameSite::None)
1347            .secure(false)
1348            .finish();
1349        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=None");
1350        cookie.set_secure(true);
1351        assert_eq!(&cookie.to_string(), "foo=bar; SameSite=None; Secure");
1352    }
1353
1354    #[test]
1355    #[ignore]
1356    fn format_date_wraps() {
1357        let expires = OffsetDateTime::UNIX_EPOCH + Duration::MAX;
1358        let cookie = Cookie::build("foo", "bar").expires(expires).finish();
1359        assert_eq!(&cookie.to_string(), "foo=bar; Expires=Fri, 31 Dec 9999 23:59:59 GMT");
1360
1361        let expires = time::macros::datetime!(9999-01-01 0:00 UTC) + Duration::days(1000);
1362        let cookie = Cookie::build("foo", "bar").expires(expires).finish();
1363        assert_eq!(&cookie.to_string(), "foo=bar; Expires=Fri, 31 Dec 9999 23:59:59 GMT");
1364    }
1365
1366    #[test]
1367    fn cookie_string_long_lifetimes() {
1368        let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
1369        let (name, value, path, domain) = {
1370            // Create a cookie passing a slice
1371            let c = Cookie::parse(cookie_string.as_str()).unwrap();
1372            (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1373        };
1374
1375        assert_eq!(name, Some("bar"));
1376        assert_eq!(value, Some("baz"));
1377        assert_eq!(path, Some("/subdir"));
1378        assert_eq!(domain, Some("crates.io"));
1379    }
1380
1381    #[test]
1382    fn owned_cookie_string() {
1383        let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
1384        let (name, value, path, domain) = {
1385            // Create a cookie passing an owned string
1386            let c = Cookie::parse(cookie_string).unwrap();
1387            (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1388        };
1389
1390        assert_eq!(name, None);
1391        assert_eq!(value, None);
1392        assert_eq!(path, None);
1393        assert_eq!(domain, None);
1394    }
1395
1396    #[test]
1397    fn owned_cookie_struct() {
1398        let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io";
1399        let (name, value, path, domain) = {
1400            // Create an owned cookie
1401            let c = Cookie::parse(cookie_string).unwrap().into_owned();
1402
1403            (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1404        };
1405
1406        assert_eq!(name, None);
1407        assert_eq!(value, None);
1408        assert_eq!(path, None);
1409        assert_eq!(domain, None);
1410    }
1411
1412    #[test]
1413    #[cfg(feature = "percent-encode")]
1414    fn format_encoded() {
1415        let cookie = Cookie::build("foo !%?=", "bar;;, a").finish();
1416        let cookie_str = cookie.encoded().to_string();
1417        assert_eq!(&cookie_str, "foo%20!%25%3F%3D=bar%3B%3B%2C%20a");
1418
1419        let cookie = Cookie::parse_encoded(cookie_str).unwrap();
1420        assert_eq!(cookie.name_value(), ("foo !%?=", "bar;;, a"));
1421    }
1422}