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