actori_http/cookie/mod.rs
1//! https://github.com/alexcrichton/cookie-rs fork
2//!
3//! HTTP cookie parsing and cookie jar management.
4//!
5//! This crates provides the [`Cookie`](struct.Cookie.html) type, which directly
6//! maps to an HTTP cookie, and the [`CookieJar`](struct.CookieJar.html) type,
7//! which allows for simple management of many cookies as well as encryption and
8//! signing of cookies for session management.
9//!
10//! # Features
11//!
12//! This crates can be configured at compile-time through the following Cargo
13//! features:
14//!
15//!
16//! * **secure** (disabled by default)
17//!
18//! Enables signed and private (signed + encrypted) cookie jars.
19//!
20//! When this feature is enabled, the
21//! [signed](struct.CookieJar.html#method.signed) and
22//! [private](struct.CookieJar.html#method.private) method of `CookieJar` and
23//! [`SignedJar`](struct.SignedJar.html) and
24//! [`PrivateJar`](struct.PrivateJar.html) structures are available. The jars
25//! act as "children jars", allowing for easy retrieval and addition of signed
26//! and/or encrypted cookies to a cookie jar. When this feature is disabled,
27//! none of the types are available.
28//!
29//! * **percent-encode** (disabled by default)
30//!
31//! Enables percent encoding and decoding of names and values in cookies.
32//!
33//! When this feature is enabled, the
34//! [encoded](struct.Cookie.html#method.encoded) and
35//! [`parse_encoded`](struct.Cookie.html#method.parse_encoded) methods of
36//! `Cookie` become available. The `encoded` method returns a wrapper around a
37//! `Cookie` whose `Display` implementation percent-encodes the name and value
38//! of the cookie. The `parse_encoded` method percent-decodes the name and
39//! value of a `Cookie` during parsing. When this feature is disabled, the
40//! `encoded` and `parse_encoded` methods are not available.
41//!
42//! You can enable features via the `Cargo.toml` file:
43//!
44//! ```ignore
45//! [dependencies.cookie]
46//! features = ["secure", "percent-encode"]
47//! ```
48
49#![doc(html_root_url = "https://docs.rs/cookie/0.11")]
50#![deny(missing_docs)]
51
52mod builder;
53mod delta;
54mod draft;
55mod jar;
56mod parse;
57
58#[cfg(feature = "secure-cookies")]
59#[macro_use]
60mod secure;
61#[cfg(feature = "secure-cookies")]
62pub use self::secure::*;
63
64use std::borrow::Cow;
65use std::fmt;
66use std::str::FromStr;
67
68use chrono::Duration;
69use percent_encoding::{percent_encode, AsciiSet, CONTROLS};
70use time::Tm;
71
72pub use self::builder::CookieBuilder;
73pub use self::draft::*;
74pub use self::jar::{CookieJar, Delta, Iter};
75use self::parse::parse_cookie;
76pub use self::parse::ParseError;
77
78/// https://url.spec.whatwg.org/#fragment-percent-encode-set
79const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
80
81/// https://url.spec.whatwg.org/#path-percent-encode-set
82const PATH: &AsciiSet = &FRAGMENT.add(b'#').add(b'?').add(b'{').add(b'}');
83
84/// https://url.spec.whatwg.org/#userinfo-percent-encode-set
85pub const USERINFO: &AsciiSet = &PATH
86 .add(b'/')
87 .add(b':')
88 .add(b';')
89 .add(b'=')
90 .add(b'@')
91 .add(b'[')
92 .add(b'\\')
93 .add(b']')
94 .add(b'^')
95 .add(b'|');
96
97#[derive(Debug, Clone)]
98enum CookieStr {
99 /// An string derived from indexes (start, end).
100 Indexed(usize, usize),
101 /// A string derived from a concrete string.
102 Concrete(Cow<'static, str>),
103}
104
105impl CookieStr {
106 /// Retrieves the string `self` corresponds to. If `self` is derived from
107 /// indexes, the corresponding subslice of `string` is returned. Otherwise,
108 /// the concrete string is returned.
109 ///
110 /// # Panics
111 ///
112 /// Panics if `self` is an indexed string and `string` is None.
113 fn to_str<'s>(&'s self, string: Option<&'s Cow<'_, str>>) -> &'s str {
114 match *self {
115 CookieStr::Indexed(i, j) => {
116 let s = string.expect(
117 "`Some` base string must exist when \
118 converting indexed str to str! (This is a module invariant.)",
119 );
120 &s[i..j]
121 }
122 CookieStr::Concrete(ref cstr) => &*cstr,
123 }
124 }
125
126 #[allow(clippy::ptr_arg)]
127 fn to_raw_str<'s, 'c: 's>(&'s self, string: &'s Cow<'c, str>) -> Option<&'c str> {
128 match *self {
129 CookieStr::Indexed(i, j) => match *string {
130 Cow::Borrowed(s) => Some(&s[i..j]),
131 Cow::Owned(_) => None,
132 },
133 CookieStr::Concrete(_) => None,
134 }
135 }
136}
137
138/// Representation of an HTTP cookie.
139///
140/// # Constructing a `Cookie`
141///
142/// To construct a cookie with only a name/value, use the [new](#method.new)
143/// method:
144///
145/// ```rust
146/// use actori_http::cookie::Cookie;
147///
148/// let cookie = Cookie::new("name", "value");
149/// assert_eq!(&cookie.to_string(), "name=value");
150/// ```
151///
152/// To construct more elaborate cookies, use the [build](#method.build) method
153/// and [`CookieBuilder`](struct.CookieBuilder.html) methods:
154///
155/// ```rust
156/// use actori_http::cookie::Cookie;
157///
158/// let cookie = Cookie::build("name", "value")
159/// .domain("www.rust-lang.org")
160/// .path("/")
161/// .secure(true)
162/// .http_only(true)
163/// .finish();
164/// ```
165#[derive(Debug, Clone)]
166pub struct Cookie<'c> {
167 /// Storage for the cookie string. Only used if this structure was derived
168 /// from a string that was subsequently parsed.
169 cookie_string: Option<Cow<'c, str>>,
170 /// The cookie's name.
171 name: CookieStr,
172 /// The cookie's value.
173 value: CookieStr,
174 /// The cookie's expiration, if any.
175 expires: Option<Tm>,
176 /// The cookie's maximum age, if any.
177 max_age: Option<Duration>,
178 /// The cookie's domain, if any.
179 domain: Option<CookieStr>,
180 /// The cookie's path domain, if any.
181 path: Option<CookieStr>,
182 /// Whether this cookie was marked Secure.
183 secure: Option<bool>,
184 /// Whether this cookie was marked HttpOnly.
185 http_only: Option<bool>,
186 /// The draft `SameSite` attribute.
187 same_site: Option<SameSite>,
188}
189
190impl Cookie<'static> {
191 /// Creates a new `Cookie` with the given name and value.
192 ///
193 /// # Example
194 ///
195 /// ```rust
196 /// use actori_http::cookie::Cookie;
197 ///
198 /// let cookie = Cookie::new("name", "value");
199 /// assert_eq!(cookie.name_value(), ("name", "value"));
200 /// ```
201 pub fn new<N, V>(name: N, value: V) -> Cookie<'static>
202 where
203 N: Into<Cow<'static, str>>,
204 V: Into<Cow<'static, str>>,
205 {
206 Cookie {
207 cookie_string: None,
208 name: CookieStr::Concrete(name.into()),
209 value: CookieStr::Concrete(value.into()),
210 expires: None,
211 max_age: None,
212 domain: None,
213 path: None,
214 secure: None,
215 http_only: None,
216 same_site: None,
217 }
218 }
219
220 /// Creates a new `Cookie` with the given name and an empty value.
221 ///
222 /// # Example
223 ///
224 /// ```rust
225 /// use actori_http::cookie::Cookie;
226 ///
227 /// let cookie = Cookie::named("name");
228 /// assert_eq!(cookie.name(), "name");
229 /// assert!(cookie.value().is_empty());
230 /// ```
231 pub fn named<N>(name: N) -> Cookie<'static>
232 where
233 N: Into<Cow<'static, str>>,
234 {
235 Cookie::new(name, "")
236 }
237
238 /// Creates a new `CookieBuilder` instance from the given key and value
239 /// strings.
240 ///
241 /// # Example
242 ///
243 /// ```rust
244 /// use actori_http::cookie::Cookie;
245 ///
246 /// let c = Cookie::build("foo", "bar").finish();
247 /// assert_eq!(c.name_value(), ("foo", "bar"));
248 /// ```
249 pub fn build<N, V>(name: N, value: V) -> CookieBuilder
250 where
251 N: Into<Cow<'static, str>>,
252 V: Into<Cow<'static, str>>,
253 {
254 CookieBuilder::new(name, value)
255 }
256}
257
258impl<'c> Cookie<'c> {
259 /// Parses a `Cookie` from the given HTTP cookie header value string. Does
260 /// not perform any percent-decoding.
261 ///
262 /// # Example
263 ///
264 /// ```rust
265 /// use actori_http::cookie::Cookie;
266 ///
267 /// let c = Cookie::parse("foo=bar%20baz; HttpOnly").unwrap();
268 /// assert_eq!(c.name_value(), ("foo", "bar%20baz"));
269 /// assert_eq!(c.http_only(), Some(true));
270 /// ```
271 pub fn parse<S>(s: S) -> Result<Cookie<'c>, ParseError>
272 where
273 S: Into<Cow<'c, str>>,
274 {
275 parse_cookie(s, false)
276 }
277
278 /// Parses a `Cookie` from the given HTTP cookie header value string where
279 /// the name and value fields are percent-encoded. Percent-decodes the
280 /// name/value fields.
281 ///
282 /// This API requires the `percent-encode` feature to be enabled on this
283 /// crate.
284 ///
285 /// # Example
286 ///
287 /// ```rust
288 /// use actori_http::cookie::Cookie;
289 ///
290 /// let c = Cookie::parse_encoded("foo=bar%20baz; HttpOnly").unwrap();
291 /// assert_eq!(c.name_value(), ("foo", "bar baz"));
292 /// assert_eq!(c.http_only(), Some(true));
293 /// ```
294 pub fn parse_encoded<S>(s: S) -> Result<Cookie<'c>, ParseError>
295 where
296 S: Into<Cow<'c, str>>,
297 {
298 parse_cookie(s, true)
299 }
300
301 /// Wraps `self` in an `EncodedCookie`: a cost-free wrapper around `Cookie`
302 /// whose `Display` implementation percent-encodes the name and value of the
303 /// wrapped `Cookie`.
304 ///
305 /// This method is only available when the `percent-encode` feature is
306 /// enabled.
307 ///
308 /// # Example
309 ///
310 /// ```rust
311 /// use actori_http::cookie::Cookie;
312 ///
313 /// let mut c = Cookie::new("my name", "this; value?");
314 /// assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%3F");
315 /// ```
316 pub fn encoded<'a>(&'a self) -> EncodedCookie<'a, 'c> {
317 EncodedCookie(self)
318 }
319
320 /// Converts `self` into a `Cookie` with a static lifetime. This method
321 /// results in at most one allocation.
322 ///
323 /// # Example
324 ///
325 /// ```rust
326 /// use actori_http::cookie::Cookie;
327 ///
328 /// let c = Cookie::new("a", "b");
329 /// let owned_cookie = c.into_owned();
330 /// assert_eq!(owned_cookie.name_value(), ("a", "b"));
331 /// ```
332 pub fn into_owned(self) -> Cookie<'static> {
333 Cookie {
334 cookie_string: self.cookie_string.map(|s| s.into_owned().into()),
335 name: self.name,
336 value: self.value,
337 expires: self.expires,
338 max_age: self.max_age,
339 domain: self.domain,
340 path: self.path,
341 secure: self.secure,
342 http_only: self.http_only,
343 same_site: self.same_site,
344 }
345 }
346
347 /// Returns the name of `self`.
348 ///
349 /// # Example
350 ///
351 /// ```rust
352 /// use actori_http::cookie::Cookie;
353 ///
354 /// let c = Cookie::new("name", "value");
355 /// assert_eq!(c.name(), "name");
356 /// ```
357 #[inline]
358 pub fn name(&self) -> &str {
359 self.name.to_str(self.cookie_string.as_ref())
360 }
361
362 /// Returns the value of `self`.
363 ///
364 /// # Example
365 ///
366 /// ```rust
367 /// use actori_http::cookie::Cookie;
368 ///
369 /// let c = Cookie::new("name", "value");
370 /// assert_eq!(c.value(), "value");
371 /// ```
372 #[inline]
373 pub fn value(&self) -> &str {
374 self.value.to_str(self.cookie_string.as_ref())
375 }
376
377 /// Returns the name and value of `self` as a tuple of `(name, value)`.
378 ///
379 /// # Example
380 ///
381 /// ```rust
382 /// use actori_http::cookie::Cookie;
383 ///
384 /// let c = Cookie::new("name", "value");
385 /// assert_eq!(c.name_value(), ("name", "value"));
386 /// ```
387 #[inline]
388 pub fn name_value(&self) -> (&str, &str) {
389 (self.name(), self.value())
390 }
391
392 /// Returns whether this cookie was marked `HttpOnly` or not. Returns
393 /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
394 /// `HttpOnly`, `Some(false)` when `http_only` was manually set to `false`,
395 /// and `None` otherwise.
396 ///
397 /// # Example
398 ///
399 /// ```rust
400 /// use actori_http::cookie::Cookie;
401 ///
402 /// let c = Cookie::parse("name=value; httponly").unwrap();
403 /// assert_eq!(c.http_only(), Some(true));
404 ///
405 /// let mut c = Cookie::new("name", "value");
406 /// assert_eq!(c.http_only(), None);
407 ///
408 /// let mut c = Cookie::new("name", "value");
409 /// assert_eq!(c.http_only(), None);
410 ///
411 /// // An explicitly set "false" value.
412 /// c.set_http_only(false);
413 /// assert_eq!(c.http_only(), Some(false));
414 ///
415 /// // An explicitly set "true" value.
416 /// c.set_http_only(true);
417 /// assert_eq!(c.http_only(), Some(true));
418 /// ```
419 #[inline]
420 pub fn http_only(&self) -> Option<bool> {
421 self.http_only
422 }
423
424 /// Returns whether this cookie was marked `Secure` or not. Returns
425 /// `Some(true)` when the cookie was explicitly set (manually or parsed) as
426 /// `Secure`, `Some(false)` when `secure` was manually set to `false`, and
427 /// `None` otherwise.
428 ///
429 /// # Example
430 ///
431 /// ```rust
432 /// use actori_http::cookie::Cookie;
433 ///
434 /// let c = Cookie::parse("name=value; Secure").unwrap();
435 /// assert_eq!(c.secure(), Some(true));
436 ///
437 /// let mut c = Cookie::parse("name=value").unwrap();
438 /// assert_eq!(c.secure(), None);
439 ///
440 /// let mut c = Cookie::new("name", "value");
441 /// assert_eq!(c.secure(), None);
442 ///
443 /// // An explicitly set "false" value.
444 /// c.set_secure(false);
445 /// assert_eq!(c.secure(), Some(false));
446 ///
447 /// // An explicitly set "true" value.
448 /// c.set_secure(true);
449 /// assert_eq!(c.secure(), Some(true));
450 /// ```
451 #[inline]
452 pub fn secure(&self) -> Option<bool> {
453 self.secure
454 }
455
456 /// Returns the `SameSite` attribute of this cookie if one was specified.
457 ///
458 /// # Example
459 ///
460 /// ```rust
461 /// use actori_http::cookie::{Cookie, SameSite};
462 ///
463 /// let c = Cookie::parse("name=value; SameSite=Lax").unwrap();
464 /// assert_eq!(c.same_site(), Some(SameSite::Lax));
465 /// ```
466 #[inline]
467 pub fn same_site(&self) -> Option<SameSite> {
468 self.same_site
469 }
470
471 /// Returns the specified max-age of the cookie if one was specified.
472 ///
473 /// # Example
474 ///
475 /// ```rust
476 /// use actori_http::cookie::Cookie;
477 ///
478 /// let c = Cookie::parse("name=value").unwrap();
479 /// assert_eq!(c.max_age(), None);
480 ///
481 /// let c = Cookie::parse("name=value; Max-Age=3600").unwrap();
482 /// assert_eq!(c.max_age().map(|age| age.num_hours()), Some(1));
483 /// ```
484 #[inline]
485 pub fn max_age(&self) -> Option<Duration> {
486 self.max_age
487 }
488
489 /// Returns the `Path` of the cookie if one was specified.
490 ///
491 /// # Example
492 ///
493 /// ```rust
494 /// use actori_http::cookie::Cookie;
495 ///
496 /// let c = Cookie::parse("name=value").unwrap();
497 /// assert_eq!(c.path(), None);
498 ///
499 /// let c = Cookie::parse("name=value; Path=/").unwrap();
500 /// assert_eq!(c.path(), Some("/"));
501 ///
502 /// let c = Cookie::parse("name=value; path=/sub").unwrap();
503 /// assert_eq!(c.path(), Some("/sub"));
504 /// ```
505 #[inline]
506 pub fn path(&self) -> Option<&str> {
507 match self.path {
508 Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
509 None => None,
510 }
511 }
512
513 /// Returns the `Domain` of the cookie if one was specified.
514 ///
515 /// # Example
516 ///
517 /// ```rust
518 /// use actori_http::cookie::Cookie;
519 ///
520 /// let c = Cookie::parse("name=value").unwrap();
521 /// assert_eq!(c.domain(), None);
522 ///
523 /// let c = Cookie::parse("name=value; Domain=crates.io").unwrap();
524 /// assert_eq!(c.domain(), Some("crates.io"));
525 /// ```
526 #[inline]
527 pub fn domain(&self) -> Option<&str> {
528 match self.domain {
529 Some(ref c) => Some(c.to_str(self.cookie_string.as_ref())),
530 None => None,
531 }
532 }
533
534 /// Returns the `Expires` time of the cookie if one was specified.
535 ///
536 /// # Example
537 ///
538 /// ```rust
539 /// use actori_http::cookie::Cookie;
540 ///
541 /// let c = Cookie::parse("name=value").unwrap();
542 /// assert_eq!(c.expires(), None);
543 ///
544 /// let expire_time = "Wed, 21 Oct 2017 07:28:00 GMT";
545 /// let cookie_str = format!("name=value; Expires={}", expire_time);
546 /// let c = Cookie::parse(cookie_str).unwrap();
547 /// assert_eq!(c.expires().map(|t| t.tm_year), Some(117));
548 /// ```
549 #[inline]
550 pub fn expires(&self) -> Option<Tm> {
551 self.expires
552 }
553
554 /// Sets the name of `self` to `name`.
555 ///
556 /// # Example
557 ///
558 /// ```rust
559 /// use actori_http::cookie::Cookie;
560 ///
561 /// let mut c = Cookie::new("name", "value");
562 /// assert_eq!(c.name(), "name");
563 ///
564 /// c.set_name("foo");
565 /// assert_eq!(c.name(), "foo");
566 /// ```
567 pub fn set_name<N: Into<Cow<'static, str>>>(&mut self, name: N) {
568 self.name = CookieStr::Concrete(name.into())
569 }
570
571 /// Sets the value of `self` to `value`.
572 ///
573 /// # Example
574 ///
575 /// ```rust
576 /// use actori_http::cookie::Cookie;
577 ///
578 /// let mut c = Cookie::new("name", "value");
579 /// assert_eq!(c.value(), "value");
580 ///
581 /// c.set_value("bar");
582 /// assert_eq!(c.value(), "bar");
583 /// ```
584 pub fn set_value<V: Into<Cow<'static, str>>>(&mut self, value: V) {
585 self.value = CookieStr::Concrete(value.into())
586 }
587
588 /// Sets the value of `http_only` in `self` to `value`.
589 ///
590 /// # Example
591 ///
592 /// ```rust
593 /// use actori_http::cookie::Cookie;
594 ///
595 /// let mut c = Cookie::new("name", "value");
596 /// assert_eq!(c.http_only(), None);
597 ///
598 /// c.set_http_only(true);
599 /// assert_eq!(c.http_only(), Some(true));
600 /// ```
601 #[inline]
602 pub fn set_http_only(&mut self, value: bool) {
603 self.http_only = Some(value);
604 }
605
606 /// Sets the value of `secure` in `self` to `value`.
607 ///
608 /// # Example
609 ///
610 /// ```rust
611 /// use actori_http::cookie::Cookie;
612 ///
613 /// let mut c = Cookie::new("name", "value");
614 /// assert_eq!(c.secure(), None);
615 ///
616 /// c.set_secure(true);
617 /// assert_eq!(c.secure(), Some(true));
618 /// ```
619 #[inline]
620 pub fn set_secure(&mut self, value: bool) {
621 self.secure = Some(value);
622 }
623
624 /// Sets the value of `same_site` in `self` to `value`.
625 ///
626 /// # Example
627 ///
628 /// ```rust
629 /// use actori_http::cookie::{Cookie, SameSite};
630 ///
631 /// let mut c = Cookie::new("name", "value");
632 /// assert!(c.same_site().is_none());
633 ///
634 /// c.set_same_site(SameSite::Strict);
635 /// assert_eq!(c.same_site(), Some(SameSite::Strict));
636 /// ```
637 #[inline]
638 pub fn set_same_site(&mut self, value: SameSite) {
639 self.same_site = Some(value);
640 }
641
642 /// Sets the value of `max_age` in `self` to `value`.
643 ///
644 /// # Example
645 ///
646 /// ```rust
647 /// use actori_http::cookie::Cookie;
648 /// use chrono::Duration;
649 ///
650 /// let mut c = Cookie::new("name", "value");
651 /// assert_eq!(c.max_age(), None);
652 ///
653 /// c.set_max_age(Duration::hours(10));
654 /// assert_eq!(c.max_age(), Some(Duration::hours(10)));
655 /// ```
656 #[inline]
657 pub fn set_max_age(&mut self, value: Duration) {
658 self.max_age = Some(value);
659 }
660
661 /// Sets the `path` of `self` to `path`.
662 ///
663 /// # Example
664 ///
665 /// ```rust
666 /// use actori_http::cookie::Cookie;
667 ///
668 /// let mut c = Cookie::new("name", "value");
669 /// assert_eq!(c.path(), None);
670 ///
671 /// c.set_path("/");
672 /// assert_eq!(c.path(), Some("/"));
673 /// ```
674 pub fn set_path<P: Into<Cow<'static, str>>>(&mut self, path: P) {
675 self.path = Some(CookieStr::Concrete(path.into()));
676 }
677
678 /// Sets the `domain` of `self` to `domain`.
679 ///
680 /// # Example
681 ///
682 /// ```rust
683 /// use actori_http::cookie::Cookie;
684 ///
685 /// let mut c = Cookie::new("name", "value");
686 /// assert_eq!(c.domain(), None);
687 ///
688 /// c.set_domain("rust-lang.org");
689 /// assert_eq!(c.domain(), Some("rust-lang.org"));
690 /// ```
691 pub fn set_domain<D: Into<Cow<'static, str>>>(&mut self, domain: D) {
692 self.domain = Some(CookieStr::Concrete(domain.into()));
693 }
694
695 /// Sets the expires field of `self` to `time`.
696 ///
697 /// # Example
698 ///
699 /// ```rust
700 /// use actori_http::cookie::Cookie;
701 ///
702 /// let mut c = Cookie::new("name", "value");
703 /// assert_eq!(c.expires(), None);
704 ///
705 /// let mut now = time::now();
706 /// now.tm_year += 1;
707 ///
708 /// c.set_expires(now);
709 /// assert!(c.expires().is_some())
710 /// ```
711 #[inline]
712 pub fn set_expires(&mut self, time: Tm) {
713 self.expires = Some(time);
714 }
715
716 /// Makes `self` a "permanent" cookie by extending its expiration and max
717 /// age 20 years into the future.
718 ///
719 /// # Example
720 ///
721 /// ```rust
722 /// use actori_http::cookie::Cookie;
723 /// use chrono::Duration;
724 ///
725 /// let mut c = Cookie::new("foo", "bar");
726 /// assert!(c.expires().is_none());
727 /// assert!(c.max_age().is_none());
728 ///
729 /// c.make_permanent();
730 /// assert!(c.expires().is_some());
731 /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
732 /// ```
733 pub fn make_permanent(&mut self) {
734 let twenty_years = Duration::days(365 * 20);
735 self.set_max_age(twenty_years);
736 self.set_expires(time::now() + twenty_years);
737 }
738
739 fn fmt_parameters(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
740 if let Some(true) = self.http_only() {
741 write!(f, "; HttpOnly")?;
742 }
743
744 if let Some(true) = self.secure() {
745 write!(f, "; Secure")?;
746 }
747
748 if let Some(same_site) = self.same_site() {
749 if !same_site.is_none() {
750 write!(f, "; SameSite={}", same_site)?;
751 }
752 }
753
754 if let Some(path) = self.path() {
755 write!(f, "; Path={}", path)?;
756 }
757
758 if let Some(domain) = self.domain() {
759 write!(f, "; Domain={}", domain)?;
760 }
761
762 if let Some(max_age) = self.max_age() {
763 write!(f, "; Max-Age={}", max_age.num_seconds())?;
764 }
765
766 if let Some(time) = self.expires() {
767 write!(f, "; Expires={}", time.rfc822())?;
768 }
769
770 Ok(())
771 }
772
773 /// Returns the name of `self` as a string slice of the raw string `self`
774 /// was originally parsed from. If `self` was not originally parsed from a
775 /// raw string, returns `None`.
776 ///
777 /// This method differs from [name](#method.name) in that it returns a
778 /// string with the same lifetime as the originally parsed string. This
779 /// lifetime may outlive `self`. If a longer lifetime is not required, or
780 /// you're unsure if you need a longer lifetime, use [name](#method.name).
781 ///
782 /// # Example
783 ///
784 /// ```rust
785 /// use actori_http::cookie::Cookie;
786 ///
787 /// let cookie_string = format!("{}={}", "foo", "bar");
788 ///
789 /// // `c` will be dropped at the end of the scope, but `name` will live on
790 /// let name = {
791 /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
792 /// c.name_raw()
793 /// };
794 ///
795 /// assert_eq!(name, Some("foo"));
796 /// ```
797 #[inline]
798 pub fn name_raw(&self) -> Option<&'c str> {
799 self.cookie_string
800 .as_ref()
801 .and_then(|s| self.name.to_raw_str(s))
802 }
803
804 /// Returns the value of `self` as a string slice of the raw string `self`
805 /// was originally parsed from. If `self` was not originally parsed from a
806 /// raw string, returns `None`.
807 ///
808 /// This method differs from [value](#method.value) in that it returns a
809 /// string with the same lifetime as the originally parsed string. This
810 /// lifetime may outlive `self`. If a longer lifetime is not required, or
811 /// you're unsure if you need a longer lifetime, use [value](#method.value).
812 ///
813 /// # Example
814 ///
815 /// ```rust
816 /// use actori_http::cookie::Cookie;
817 ///
818 /// let cookie_string = format!("{}={}", "foo", "bar");
819 ///
820 /// // `c` will be dropped at the end of the scope, but `value` will live on
821 /// let value = {
822 /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
823 /// c.value_raw()
824 /// };
825 ///
826 /// assert_eq!(value, Some("bar"));
827 /// ```
828 #[inline]
829 pub fn value_raw(&self) -> Option<&'c str> {
830 self.cookie_string
831 .as_ref()
832 .and_then(|s| self.value.to_raw_str(s))
833 }
834
835 /// Returns the `Path` of `self` as a string slice of the raw string `self`
836 /// was originally parsed from. If `self` was not originally parsed from a
837 /// raw string, or if `self` doesn't contain a `Path`, or if the `Path` has
838 /// changed since parsing, returns `None`.
839 ///
840 /// This method differs from [path](#method.path) in that it returns a
841 /// string with the same lifetime as the originally parsed string. This
842 /// lifetime may outlive `self`. If a longer lifetime is not required, or
843 /// you're unsure if you need a longer lifetime, use [path](#method.path).
844 ///
845 /// # Example
846 ///
847 /// ```rust
848 /// use actori_http::cookie::Cookie;
849 ///
850 /// let cookie_string = format!("{}={}; Path=/", "foo", "bar");
851 ///
852 /// // `c` will be dropped at the end of the scope, but `path` will live on
853 /// let path = {
854 /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
855 /// c.path_raw()
856 /// };
857 ///
858 /// assert_eq!(path, Some("/"));
859 /// ```
860 #[inline]
861 pub fn path_raw(&self) -> Option<&'c str> {
862 match (self.path.as_ref(), self.cookie_string.as_ref()) {
863 (Some(path), Some(string)) => path.to_raw_str(string),
864 _ => None,
865 }
866 }
867
868 /// Returns the `Domain` of `self` as a string slice of the raw string
869 /// `self` was originally parsed from. If `self` was not originally parsed
870 /// from a raw string, or if `self` doesn't contain a `Domain`, or if the
871 /// `Domain` has changed since parsing, returns `None`.
872 ///
873 /// This method differs from [domain](#method.domain) in that it returns a
874 /// string with the same lifetime as the originally parsed string. This
875 /// lifetime may outlive `self` struct. If a longer lifetime is not
876 /// required, or you're unsure if you need a longer lifetime, use
877 /// [domain](#method.domain).
878 ///
879 /// # Example
880 ///
881 /// ```rust
882 /// use actori_http::cookie::Cookie;
883 ///
884 /// let cookie_string = format!("{}={}; Domain=crates.io", "foo", "bar");
885 ///
886 /// //`c` will be dropped at the end of the scope, but `domain` will live on
887 /// let domain = {
888 /// let c = Cookie::parse(cookie_string.as_str()).unwrap();
889 /// c.domain_raw()
890 /// };
891 ///
892 /// assert_eq!(domain, Some("crates.io"));
893 /// ```
894 #[inline]
895 pub fn domain_raw(&self) -> Option<&'c str> {
896 match (self.domain.as_ref(), self.cookie_string.as_ref()) {
897 (Some(domain), Some(string)) => domain.to_raw_str(string),
898 _ => None,
899 }
900 }
901}
902
903/// Wrapper around `Cookie` whose `Display` implementation percent-encodes the
904/// cookie's name and value.
905///
906/// A value of this type can be obtained via the
907/// [encoded](struct.Cookie.html#method.encoded) method on
908/// [Cookie](struct.Cookie.html). This type should only be used for its
909/// `Display` implementation.
910///
911/// This type is only available when the `percent-encode` feature is enabled.
912///
913/// # Example
914///
915/// ```rust
916/// use actori_http::cookie::Cookie;
917///
918/// let mut c = Cookie::new("my name", "this; value?");
919/// assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%3F");
920/// ```
921pub struct EncodedCookie<'a, 'c>(&'a Cookie<'c>);
922
923impl<'a, 'c: 'a> fmt::Display for EncodedCookie<'a, 'c> {
924 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
925 // Percent-encode the name and value.
926 let name = percent_encode(self.0.name().as_bytes(), USERINFO);
927 let value = percent_encode(self.0.value().as_bytes(), USERINFO);
928
929 // Write out the name/value pair and the cookie's parameters.
930 write!(f, "{}={}", name, value)?;
931 self.0.fmt_parameters(f)
932 }
933}
934
935impl<'c> fmt::Display for Cookie<'c> {
936 /// Formats the cookie `self` as a `Set-Cookie` header value.
937 ///
938 /// # Example
939 ///
940 /// ```rust
941 /// use actori_http::cookie::Cookie;
942 ///
943 /// let mut cookie = Cookie::build("foo", "bar")
944 /// .path("/")
945 /// .finish();
946 ///
947 /// assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
948 /// ```
949 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
950 write!(f, "{}={}", self.name(), self.value())?;
951 self.fmt_parameters(f)
952 }
953}
954
955impl FromStr for Cookie<'static> {
956 type Err = ParseError;
957
958 fn from_str(s: &str) -> Result<Cookie<'static>, ParseError> {
959 Cookie::parse(s).map(|c| c.into_owned())
960 }
961}
962
963impl<'a, 'b> PartialEq<Cookie<'b>> for Cookie<'a> {
964 fn eq(&self, other: &Cookie<'b>) -> bool {
965 let so_far_so_good = self.name() == other.name()
966 && self.value() == other.value()
967 && self.http_only() == other.http_only()
968 && self.secure() == other.secure()
969 && self.max_age() == other.max_age()
970 && self.expires() == other.expires();
971
972 if !so_far_so_good {
973 return false;
974 }
975
976 match (self.path(), other.path()) {
977 (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
978 (None, None) => {}
979 _ => return false,
980 };
981
982 match (self.domain(), other.domain()) {
983 (Some(a), Some(b)) if a.eq_ignore_ascii_case(b) => {}
984 (None, None) => {}
985 _ => return false,
986 };
987
988 true
989 }
990}
991
992#[cfg(test)]
993mod tests {
994 use super::{Cookie, SameSite};
995 use time::strptime;
996
997 #[test]
998 fn format() {
999 let cookie = Cookie::new("foo", "bar");
1000 assert_eq!(&cookie.to_string(), "foo=bar");
1001
1002 let cookie = Cookie::build("foo", "bar").http_only(true).finish();
1003 assert_eq!(&cookie.to_string(), "foo=bar; HttpOnly");
1004
1005 let cookie = Cookie::build("foo", "bar").max_age(10).finish();
1006 assert_eq!(&cookie.to_string(), "foo=bar; Max-Age=10");
1007
1008 let cookie = Cookie::build("foo", "bar").secure(true).finish();
1009 assert_eq!(&cookie.to_string(), "foo=bar; Secure");
1010
1011 let cookie = Cookie::build("foo", "bar").path("/").finish();
1012 assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
1013
1014 let cookie = Cookie::build("foo", "bar")
1015 .domain("www.rust-lang.org")
1016 .finish();
1017 assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");
1018
1019 let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
1020 let expires = strptime(time_str, "%a, %d %b %Y %H:%M:%S %Z").unwrap();
1021 let cookie = Cookie::build("foo", "bar").expires(expires).finish();
1022 assert_eq!(
1023 &cookie.to_string(),
1024 "foo=bar; Expires=Wed, 21 Oct 2015 07:28:00 GMT"
1025 );
1026
1027 let cookie = Cookie::build("foo", "bar")
1028 .same_site(SameSite::Strict)
1029 .finish();
1030 assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Strict");
1031
1032 let cookie = Cookie::build("foo", "bar")
1033 .same_site(SameSite::Lax)
1034 .finish();
1035 assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Lax");
1036
1037 let cookie = Cookie::build("foo", "bar")
1038 .same_site(SameSite::None)
1039 .finish();
1040 assert_eq!(&cookie.to_string(), "foo=bar");
1041 }
1042
1043 #[test]
1044 fn cookie_string_long_lifetimes() {
1045 let cookie_string =
1046 "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
1047 let (name, value, path, domain) = {
1048 // Create a cookie passing a slice
1049 let c = Cookie::parse(cookie_string.as_str()).unwrap();
1050 (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1051 };
1052
1053 assert_eq!(name, Some("bar"));
1054 assert_eq!(value, Some("baz"));
1055 assert_eq!(path, Some("/subdir"));
1056 assert_eq!(domain, Some("crates.io"));
1057 }
1058
1059 #[test]
1060 fn owned_cookie_string() {
1061 let cookie_string =
1062 "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
1063 let (name, value, path, domain) = {
1064 // Create a cookie passing an owned string
1065 let c = Cookie::parse(cookie_string).unwrap();
1066 (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1067 };
1068
1069 assert_eq!(name, None);
1070 assert_eq!(value, None);
1071 assert_eq!(path, None);
1072 assert_eq!(domain, None);
1073 }
1074
1075 #[test]
1076 fn owned_cookie_struct() {
1077 let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io";
1078 let (name, value, path, domain) = {
1079 // Create an owned cookie
1080 let c = Cookie::parse(cookie_string).unwrap().into_owned();
1081
1082 (c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
1083 };
1084
1085 assert_eq!(name, None);
1086 assert_eq!(value, None);
1087 assert_eq!(path, None);
1088 assert_eq!(domain, None);
1089 }
1090
1091 #[test]
1092 fn format_encoded() {
1093 let cookie = Cookie::build("foo !?=", "bar;; a").finish();
1094 let cookie_str = cookie.encoded().to_string();
1095 assert_eq!(&cookie_str, "foo%20!%3F%3D=bar%3B%3B%20a");
1096
1097 let cookie = Cookie::parse_encoded(cookie_str).unwrap();
1098 assert_eq!(cookie.name_value(), ("foo !?=", "bar;; a"));
1099 }
1100}