1use std::io::{BufRead, Write};
2use std::ops::Deref;
3
4use cookie::Cookie as RawCookie;
5use log::debug;
6use url::Url;
7
8use crate::cookie::Cookie;
9use crate::cookie_domain::is_match as domain_match;
10use crate::cookie_path::is_match as path_match;
11use crate::utils::{is_http_scheme, is_secure};
12use crate::CookieError;
13
14#[cfg(feature = "preserve_order")]
15use indexmap::IndexMap;
16#[cfg(not(feature = "preserve_order"))]
17use std::collections::HashMap;
18#[cfg(feature = "preserve_order")]
19type Map<K, V> = IndexMap<K, V>;
20#[cfg(not(feature = "preserve_order"))]
21type Map<K, V> = HashMap<K, V>;
22
23type NameMap = Map<String, Cookie<'static>>;
24type PathMap = Map<String, NameMap>;
25type DomainMap = Map<String, PathMap>;
26
27#[derive(PartialEq, Clone, Debug, Eq)]
28pub enum StoreAction {
29 Inserted,
31 ExpiredExisting,
33 UpdatedExisting,
35}
36
37pub type StoreResult<T> = Result<T, crate::Error>;
38pub type InsertResult = Result<StoreAction, CookieError>;
39
40#[derive(Debug, Default, Clone)]
41pub struct CookieStore {
44 cookies: DomainMap,
46 #[cfg(feature = "public_suffix")]
47 public_suffix_list: Option<publicsuffix::List>,
49}
50
51impl CookieStore {
52 #[deprecated(
53 since = "0.14.1",
54 note = "Please use the `get_request_values` function instead"
55 )]
56 pub fn get_request_cookies(&self, url: &Url) -> impl Iterator<Item = &RawCookie<'static>> {
62 self.matches(url).into_iter().map(|c| c.deref())
63 }
64
65 pub fn get_request_values(&self, url: &Url) -> impl Iterator<Item = (&str, &str)> {
69 self.matches(url).into_iter().map(|c| c.name_value())
70 }
71
72 pub fn store_response_cookies<I: Iterator<Item = RawCookie<'static>>>(
74 &mut self,
75 cookies: I,
76 url: &Url,
77 ) {
78 for cookie in cookies {
79 if cookie.secure() != Some(true) || cfg!(feature = "log_secure_cookie_values") {
80 debug!("inserting Set-Cookie '{:?}'", cookie);
81 } else {
82 debug!("inserting secure cookie '{}'", cookie.name());
83 }
84
85 if let Err(e) = self.insert_raw(&cookie, url) {
86 debug!("unable to store Set-Cookie: {:?}", e);
87 }
88 }
89 }
90
91 #[cfg(feature = "public_suffix")]
94 pub fn with_suffix_list(self, psl: publicsuffix::List) -> CookieStore {
95 CookieStore {
96 cookies: self.cookies,
97 public_suffix_list: Some(psl),
98 }
99 }
100
101 pub fn contains(&self, domain: &str, path: &str, name: &str) -> bool {
104 self.get(domain, path, name).is_some()
105 }
106
107 pub fn contains_any(&self, domain: &str, path: &str, name: &str) -> bool {
110 self.get_any(domain, path, name).is_some()
111 }
112
113 pub fn get(&self, domain: &str, path: &str, name: &str) -> Option<&Cookie<'_>> {
116 self.get_any(domain, path, name).and_then(|cookie| {
117 if cookie.is_expired() {
118 None
119 } else {
120 Some(cookie)
121 }
122 })
123 }
124
125 fn get_mut(&mut self, domain: &str, path: &str, name: &str) -> Option<&mut Cookie<'static>> {
128 self.get_mut_any(domain, path, name).and_then(|cookie| {
129 if cookie.is_expired() {
130 None
131 } else {
132 Some(cookie)
133 }
134 })
135 }
136
137 pub fn get_any(&self, domain: &str, path: &str, name: &str) -> Option<&Cookie<'static>> {
140 self.cookies.get(domain).and_then(|domain_cookies| {
141 domain_cookies
142 .get(path)
143 .and_then(|path_cookies| path_cookies.get(name))
144 })
145 }
146
147 fn get_mut_any(
150 &mut self,
151 domain: &str,
152 path: &str,
153 name: &str,
154 ) -> Option<&mut Cookie<'static>> {
155 self.cookies.get_mut(domain).and_then(|domain_cookies| {
156 domain_cookies
157 .get_mut(path)
158 .and_then(|path_cookies| path_cookies.get_mut(name))
159 })
160 }
161
162 pub fn remove(&mut self, domain: &str, path: &str, name: &str) -> Option<Cookie<'static>> {
164 #[cfg(not(feature = "preserve_order"))]
165 fn map_remove<K, V, Q>(map: &mut Map<K, V>, key: &Q) -> Option<V>
166 where
167 K: std::borrow::Borrow<Q> + std::cmp::Eq + std::hash::Hash,
168 Q: std::cmp::Eq + std::hash::Hash + ?Sized,
169 {
170 map.remove(key)
171 }
172 #[cfg(feature = "preserve_order")]
173 fn map_remove<K, V, Q>(map: &mut Map<K, V>, key: &Q) -> Option<V>
174 where
175 K: std::borrow::Borrow<Q> + std::cmp::Eq + std::hash::Hash,
176 Q: std::cmp::Eq + std::hash::Hash + ?Sized,
177 {
178 map.shift_remove(key)
179 }
180
181 let (removed, remove_domain) = match self.cookies.get_mut(domain) {
182 None => (None, false),
183 Some(domain_cookies) => {
184 let (removed, remove_path) = match domain_cookies.get_mut(path) {
185 None => (None, false),
186 Some(path_cookies) => {
187 let removed = map_remove(path_cookies, name);
188 (removed, path_cookies.is_empty())
189 }
190 };
191
192 if remove_path {
193 map_remove(domain_cookies, path);
194 (removed, domain_cookies.is_empty())
195 } else {
196 (removed, false)
197 }
198 }
199 };
200
201 if remove_domain {
202 map_remove(&mut self.cookies, domain);
203 }
204
205 removed
206 }
207
208 pub fn matches(&self, request_url: &Url) -> Vec<&Cookie<'static>> {
212 let cookies = self
218 .cookies
219 .iter()
220 .filter(|&(d, _)| domain_match(d, request_url))
221 .flat_map(|(_, dcs)| {
222 dcs.iter()
223 .filter(|&(p, _)| path_match(p, request_url))
224 .flat_map(|(_, pcs)| {
225 pcs.values()
226 .filter(|c| !c.is_expired() && c.matches(request_url))
227 })
228 });
229 match (!is_http_scheme(request_url), !is_secure(request_url)) {
230 (true, true) => cookies
231 .filter(|c| !c.http_only().unwrap_or(false) && !c.secure().unwrap_or(false))
232 .collect(),
233 (true, false) => cookies
234 .filter(|c| !c.http_only().unwrap_or(false))
235 .collect(),
236 (false, true) => cookies.filter(|c| !c.secure().unwrap_or(false)).collect(),
237 (false, false) => cookies.collect(),
238 }
239 }
240
241 pub fn parse(&mut self, cookie_str: &str, request_url: &Url) -> InsertResult {
243 Cookie::parse(cookie_str, request_url)
244 .and_then(|cookie| self.insert(cookie.into_owned(), request_url))
245 }
246
247 pub fn insert_raw(&mut self, cookie: &RawCookie<'_>, request_url: &Url) -> InsertResult {
250 Cookie::try_from_raw_cookie(cookie, request_url)
251 .and_then(|cookie| self.insert(cookie.into_owned(), request_url))
252 }
253
254 pub fn insert(&mut self, cookie: Cookie<'static>, request_url: &Url) -> InsertResult {
261 if cookie.http_only().unwrap_or(false) && !is_http_scheme(request_url) {
262 return Err(CookieError::NonHttpScheme);
266 }
267 #[cfg(feature = "public_suffix")]
268 let mut cookie = cookie;
269 #[cfg(feature = "public_suffix")]
270 if let Some(ref psl) = self.public_suffix_list {
271 if cookie.domain.is_public_suffix(psl) {
273 if cookie.domain.host_is_identical(request_url) {
275 cookie.domain = crate::cookie_domain::CookieDomain::host_only(request_url)?;
281 } else {
282 return Err(CookieError::PublicSuffix);
285 }
286 }
287 }
288 if !cookie.domain.matches(request_url) {
289 return Err(CookieError::DomainMismatch);
293 }
294 {
300 let cookie_domain = cookie
303 .domain
304 .as_cow()
305 .ok_or_else(|| CookieError::UnspecifiedDomain)?;
306 if let Some(old_cookie) = self.get_mut(&cookie_domain, &cookie.path, cookie.name()) {
307 if old_cookie.http_only().unwrap_or(false) && !is_http_scheme(request_url) {
308 return Err(CookieError::NonHttpScheme);
312 } else if cookie.is_expired() {
313 old_cookie.expire();
314 return Ok(StoreAction::ExpiredExisting);
315 }
316 }
317 }
318
319 if !cookie.is_expired() {
320 Ok(
321 if self
322 .cookies
323 .entry(String::from(&cookie.domain))
324 .or_insert_with(Map::new)
325 .entry(String::from(&cookie.path))
326 .or_insert_with(Map::new)
327 .insert(cookie.name().to_owned(), cookie)
328 .is_none()
329 {
330 StoreAction::Inserted
331 } else {
332 StoreAction::UpdatedExisting
333 },
334 )
335 } else {
336 Err(CookieError::Expired)
337 }
338 }
339
340 pub fn clear(&mut self) {
342 self.cookies.clear()
343 }
344
345 pub fn iter_unexpired<'a>(&'a self) -> impl Iterator<Item = &'a Cookie<'static>> + 'a {
347 self.cookies
348 .values()
349 .flat_map(|dcs| dcs.values())
350 .flat_map(|pcs| pcs.values())
351 .filter(|c| !c.is_expired())
352 }
353
354 pub fn iter_any<'a>(&'a self) -> impl Iterator<Item = &'a Cookie<'static>> + 'a {
356 self.cookies
357 .values()
358 .flat_map(|dcs| dcs.values())
359 .flat_map(|pcs| pcs.values())
360 }
361
362 pub fn save<W, E, F>(&self, writer: &mut W, cookie_to_string: F) -> StoreResult<()>
365 where
366 W: Write,
367 F: Fn(&Cookie<'static>) -> Result<String, E>,
368 crate::Error: From<E>,
369 {
370 for cookie in self.iter_unexpired().filter_map(|c| {
371 if c.is_persistent() {
372 Some(cookie_to_string(c))
373 } else {
374 None
375 }
376 }) {
377 writeln!(writer, "{}", cookie?)?;
378 }
379 Ok(())
380 }
381
382 pub fn save_incl_expired_and_nonpersistent<W, E, F>(
384 &self,
385 writer: &mut W,
386 cookie_to_string: F,
387 ) -> StoreResult<()>
388 where
389 W: Write,
390 F: Fn(&Cookie<'static>) -> Result<String, E>,
391 crate::Error: From<E>,
392 {
393 for cookie in self.iter_any() {
394 writeln!(writer, "{}", cookie_to_string(cookie)?)?;
395 }
396 Ok(())
397 }
398
399 pub fn load<R, E, F>(reader: R, cookie_from_str: F) -> StoreResult<CookieStore>
402 where
403 R: BufRead,
404 F: Fn(&str) -> Result<Cookie<'static>, E>,
405 crate::Error: From<E>,
406 {
407 CookieStore::load_from(reader, cookie_from_str, false)
408 }
409
410 pub fn load_all<R, E, F>(reader: R, cookie_from_str: F) -> StoreResult<CookieStore>
413 where
414 R: BufRead,
415 F: Fn(&str) -> Result<Cookie<'static>, E>,
416 crate::Error: From<E>,
417 {
418 CookieStore::load_from(reader, cookie_from_str, true)
419 }
420
421 fn load_from<R, E, F>(
422 reader: R,
423 cookie_from_str: F,
424 include_expired: bool,
425 ) -> StoreResult<CookieStore>
426 where
427 R: BufRead,
428 F: Fn(&str) -> Result<Cookie<'static>, E>,
429 crate::Error: From<E>,
430 {
431 let cookies = reader.lines().map(|line_result| {
432 line_result
433 .map_err(Into::into)
434 .and_then(|line| cookie_from_str(&line).map_err(crate::Error::from))
435 });
436 Self::from_cookies(cookies, include_expired)
437 }
438
439 pub fn from_cookies<I, E>(iter: I, include_expired: bool) -> Result<Self, E>
445 where
446 I: IntoIterator<Item = Result<Cookie<'static>, E>>,
447 {
448 let mut cookies = Map::new();
449 for cookie in iter {
450 let cookie = cookie?;
451 if include_expired || !cookie.is_expired() {
452 cookies
453 .entry(String::from(&cookie.domain))
454 .or_insert_with(Map::new)
455 .entry(String::from(&cookie.path))
456 .or_insert_with(Map::new)
457 .insert(cookie.name().to_owned(), cookie);
458 }
459 }
460 Ok(Self {
461 cookies,
462 #[cfg(feature = "public_suffix")]
463 public_suffix_list: None,
464 })
465 }
466
467 pub fn new(
468 #[cfg(feature = "public_suffix")] public_suffix_list: Option<publicsuffix::List>,
469 ) -> Self {
470 Self {
471 cookies: DomainMap::new(),
472 #[cfg(feature = "public_suffix")]
473 public_suffix_list,
474 }
475 }
476}
477
478
479#[cfg(feature = "serde_json")]
480impl CookieStore {
483 #[deprecated(
492 since = "0.22.0",
493 note = "See `cookie_store::serde` modules for more robust de/serialization options"
494 )]
495 pub fn save_json<W: Write>(&self, writer: &mut W) -> StoreResult<()> {
496 self.save(writer, ::serde_json::to_string)
497 }
498
499 #[deprecated(
507 since = "0.22.0",
508 note = "See `cookie_store::serde` modules for more robust de/serialization options"
509 )]
510 pub fn save_incl_expired_and_nonpersistent_json<W: Write>(
511 &self,
512 writer: &mut W,
513 ) -> StoreResult<()> {
514 self.save_incl_expired_and_nonpersistent(writer, ::serde_json::to_string)
515 }
516
517 #[deprecated(
524 since = "0.22.0",
525 note = "See `cookie_store::serde` modules for more robust de/serialization options"
526 )]
527 pub fn load_json<R: BufRead>(reader: R) -> StoreResult<CookieStore> {
528 CookieStore::load(reader, |cookie| ::serde_json::from_str(cookie))
529 }
530
531 #[deprecated(
538 since = "0.22.0",
539 note = "See `cookie_store::serde` modules for more robust de/serialization options"
540 )]
541 pub fn load_json_all<R: BufRead>(reader: R) -> StoreResult<CookieStore> {
542 CookieStore::load_all(reader, |cookie| ::serde_json::from_str(cookie))
543 }
544}
545
546#[cfg(feature = "serde")]
547mod serde_legacy {
551 use serde::de::{SeqAccess, Visitor};
552 use serde::{Deserialize, Deserializer, Serialize, Serializer};
553
554 impl Serialize for super::CookieStore {
555 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
556 where
557 S: Serializer,
558 {
559 serializer.collect_seq(self.iter_unexpired().filter(|c| c.is_persistent()))
560 }
561 }
562
563 struct CookieStoreVisitor;
564
565 impl<'de> Visitor<'de> for CookieStoreVisitor {
566 type Value = super::CookieStore;
567
568 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
569 write!(formatter, "a sequence of cookies")
570 }
571
572 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
573 where
574 A: SeqAccess<'de>,
575 {
576 super::CookieStore::from_cookies(std::iter::from_fn(|| seq.next_element().transpose()), false)
577 }
578 }
579
580 impl<'de> Deserialize<'de> for super::CookieStore {
581 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
582 where
583 D: Deserializer<'de>,
584 {
585 deserializer.deserialize_seq(CookieStoreVisitor)
586 }
587 }
588}
589
590#[cfg(test)]
591mod tests {
592 use super::CookieStore;
593 use super::{InsertResult, StoreAction};
594 use crate::cookie::Cookie;
595 use crate::CookieError;
596 use ::cookie::Cookie as RawCookie;
597 use time::OffsetDateTime;
598
599 use crate::utils::test as test_utils;
600
601 macro_rules! inserted {
602 ($e: expr) => {
603 assert_eq!(Ok(StoreAction::Inserted), $e)
604 };
605 }
606 macro_rules! updated {
607 ($e: expr) => {
608 assert_eq!(Ok(StoreAction::UpdatedExisting), $e)
609 };
610 }
611 macro_rules! expired_existing {
612 ($e: expr) => {
613 assert_eq!(Ok(StoreAction::ExpiredExisting), $e)
614 };
615 }
616 macro_rules! domain_mismatch {
617 ($e: expr) => {
618 assert_eq!(Err(CookieError::DomainMismatch), $e)
619 };
620 }
621 macro_rules! non_http_scheme {
622 ($e: expr) => {
623 assert_eq!(Err(CookieError::NonHttpScheme), $e)
624 };
625 }
626 macro_rules! non_rel_scheme {
627 ($e: expr) => {
628 assert_eq!(Err(CookieError::NonRelativeScheme), $e)
629 };
630 }
631 macro_rules! expired_err {
632 ($e: expr) => {
633 assert_eq!(Err(CookieError::Expired), $e)
634 };
635 }
636 macro_rules! values_are {
637 ($store: expr, $url: expr, $values: expr) => {{
638 let mut matched_values = $store
639 .matches(&test_utils::url($url))
640 .iter()
641 .map(|c| &c.value()[..])
642 .collect::<Vec<_>>();
643 matched_values.sort();
644
645 let mut values: Vec<&str> = $values;
646 values.sort();
647
648 assert!(
649 matched_values == values,
650 "\n{:?}\n!=\n{:?}\n",
651 matched_values,
652 values
653 );
654 }};
655 }
656
657 fn add_cookie(
658 store: &mut CookieStore,
659 cookie: &str,
660 url: &str,
661 expires: Option<OffsetDateTime>,
662 max_age: Option<u64>,
663 ) -> InsertResult {
664 store.insert(
665 test_utils::make_cookie(cookie, url, expires, max_age),
666 &test_utils::url(url),
667 )
668 }
669
670 fn make_match_store() -> CookieStore {
671 let mut store = CookieStore::default();
672 inserted!(add_cookie(
673 &mut store,
674 "cookie1=1",
675 "http://example.com/foo/bar",
676 None,
677 Some(60 * 5),
678 ));
679 inserted!(add_cookie(
680 &mut store,
681 "cookie2=2; Secure",
682 "https://example.com/sec/",
683 None,
684 Some(60 * 5),
685 ));
686 inserted!(add_cookie(
687 &mut store,
688 "cookie3=3; HttpOnly",
689 "https://example.com/sec/",
690 None,
691 Some(60 * 5),
692 ));
693 inserted!(add_cookie(
694 &mut store,
695 "cookie4=4; Secure; HttpOnly",
696 "https://example.com/sec/",
697 None,
698 Some(60 * 5),
699 ));
700 inserted!(add_cookie(
701 &mut store,
702 "cookie5=5",
703 "http://example.com/foo/",
704 None,
705 Some(60 * 5),
706 ));
707 inserted!(add_cookie(
708 &mut store,
709 "cookie6=6",
710 "http://example.com/",
711 None,
712 Some(60 * 5),
713 ));
714 inserted!(add_cookie(
715 &mut store,
716 "cookie7=7",
717 "http://bar.example.com/foo/",
718 None,
719 Some(60 * 5),
720 ));
721
722 inserted!(add_cookie(
723 &mut store,
724 "cookie8=8",
725 "http://example.org/foo/bar",
726 None,
727 Some(60 * 5),
728 ));
729 inserted!(add_cookie(
730 &mut store,
731 "cookie9=9",
732 "http://bar.example.org/foo/bar",
733 None,
734 Some(60 * 5),
735 ));
736 store
737 }
738
739 macro_rules! check_matches {
740 ($store: expr) => {{
741 values_are!($store, "http://unknowndomain.org/foo/bar", vec![]);
742 values_are!($store, "http://example.org/foo/bar", vec!["8"]);
743 values_are!($store, "http://example.org/bus/bar", vec![]);
744 values_are!($store, "http://bar.example.org/foo/bar", vec!["9"]);
745 values_are!($store, "http://bar.example.org/bus/bar", vec![]);
746 values_are!(
747 $store,
748 "https://example.com/sec/foo",
749 vec!["6", "4", "3", "2"]
750 );
751 values_are!($store, "http://example.com/sec/foo", vec!["6", "3"]);
752 values_are!($store, "ftp://example.com/sec/foo", vec!["6"]);
753 values_are!($store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
754 values_are!(
755 $store,
756 "http://example.com/foo/bar/bus",
757 vec!["1", "5", "6"]
758 );
759 }};
760 }
761
762 #[test]
763 fn insert_raw() {
764 let mut store = CookieStore::default();
765 inserted!(store.insert_raw(
766 &RawCookie::parse("cookie1=value1").unwrap(),
767 &test_utils::url("http://example.com/foo/bar"),
768 ));
769 non_rel_scheme!(store.insert_raw(
770 &RawCookie::parse("cookie1=value1").unwrap(),
771 &test_utils::url("data:nonrelativescheme"),
772 ));
773 non_http_scheme!(store.insert_raw(
774 &RawCookie::parse("cookie1=value1; HttpOnly").unwrap(),
775 &test_utils::url("ftp://example.com/"),
776 ));
777 expired_existing!(store.insert_raw(
778 &RawCookie::parse("cookie1=value1; Max-Age=0").unwrap(),
779 &test_utils::url("http://example.com/foo/bar"),
780 ));
781 expired_err!(store.insert_raw(
782 &RawCookie::parse("cookie1=value1; Max-Age=-1").unwrap(),
783 &test_utils::url("http://example.com/foo/bar"),
784 ));
785 updated!(store.insert_raw(
786 &RawCookie::parse("cookie1=value1").unwrap(),
787 &test_utils::url("http://example.com/foo/bar"),
788 ));
789 expired_existing!(store.insert_raw(
790 &RawCookie::parse("cookie1=value1; Max-Age=-1").unwrap(),
791 &test_utils::url("http://example.com/foo/bar"),
792 ));
793 domain_mismatch!(store.insert_raw(
794 &RawCookie::parse("cookie1=value1; Domain=bar.example.com").unwrap(),
795 &test_utils::url("http://example.com/foo/bar"),
796 ));
797 }
798
799 #[test]
800 fn parse() {
801 let mut store = CookieStore::default();
802 inserted!(store.parse(
803 "cookie1=value1",
804 &test_utils::url("http://example.com/foo/bar"),
805 ));
806 non_rel_scheme!(store.parse("cookie1=value1", &test_utils::url("data:nonrelativescheme"),));
807 non_http_scheme!(store.parse(
808 "cookie1=value1; HttpOnly",
809 &test_utils::url("ftp://example.com/"),
810 ));
811 expired_existing!(store.parse(
812 "cookie1=value1; Max-Age=0",
813 &test_utils::url("http://example.com/foo/bar"),
814 ));
815 expired_err!(store.parse(
816 "cookie1=value1; Max-Age=-1",
817 &test_utils::url("http://example.com/foo/bar"),
818 ));
819 updated!(store.parse(
820 "cookie1=value1",
821 &test_utils::url("http://example.com/foo/bar"),
822 ));
823 expired_existing!(store.parse(
824 "cookie1=value1; Max-Age=-1",
825 &test_utils::url("http://example.com/foo/bar"),
826 ));
827 domain_mismatch!(store.parse(
828 "cookie1=value1; Domain=bar.example.com",
829 &test_utils::url("http://example.com/foo/bar"),
830 ));
831 }
832
833 #[test]
834 fn domains() {
835 let mut store = CookieStore::default();
836 fn domain_cookie_from(domain: &str, request_url: &str) -> Cookie<'static> {
843 let cookie_str = format!("cookie1=value1; Domain={}", domain);
844 Cookie::parse(cookie_str, &test_utils::url(request_url)).unwrap()
845 }
846
847 {
848 let request_url = test_utils::url("http://foo.example.com");
849 inserted!(store.insert(
851 domain_cookie_from("example.com", "http://foo.example.com",),
852 &request_url,
853 ));
854 updated!(store.insert(
855 domain_cookie_from(".example.com", "http://foo.example.com",),
856 &request_url,
857 ));
858 inserted!(store.insert(
859 domain_cookie_from("foo.example.com", "http://foo.example.com",),
860 &request_url,
861 ));
862 updated!(store.insert(
863 domain_cookie_from(".foo.example.com", "http://foo.example.com",),
864 &request_url,
865 ));
866 domain_mismatch!(store.insert(
868 domain_cookie_from("bar.example.com", "http://bar.example.com",),
869 &request_url,
870 ));
871 domain_mismatch!(store.insert(
872 domain_cookie_from(".bar.example.com", "http://bar.example.com",),
873 &request_url,
874 ));
875 domain_mismatch!(store.insert(
877 domain_cookie_from("bar.foo.example.com", "http://bar.foo.example.com",),
878 &request_url,
879 ));
880 domain_mismatch!(store.insert(
881 domain_cookie_from(".bar.foo.example.com", "http://bar.foo.example.com",),
882 &request_url,
883 ));
884 }
885
886 {
887 let request_url = test_utils::url("http://bar.example.com");
888 updated!(store.insert(
890 domain_cookie_from("example.com", "http://foo.example.com",),
891 &request_url,
892 ));
893 updated!(store.insert(
894 domain_cookie_from(".example.com", "http://foo.example.com",),
895 &request_url,
896 ));
897 inserted!(store.insert(
898 domain_cookie_from("bar.example.com", "http://bar.example.com",),
899 &request_url,
900 ));
901 updated!(store.insert(
902 domain_cookie_from(".bar.example.com", "http://bar.example.com",),
903 &request_url,
904 ));
905 domain_mismatch!(store.insert(
907 domain_cookie_from("foo.example.com", "http://foo.example.com",),
908 &request_url,
909 ));
910 domain_mismatch!(store.insert(
911 domain_cookie_from(".foo.example.com", "http://foo.example.com",),
912 &request_url,
913 ));
914 }
915 {
916 let request_url = test_utils::url("http://example.com");
917 updated!(store.insert(
919 domain_cookie_from("example.com", "http://foo.example.com",),
920 &request_url,
921 ));
922 updated!(store.insert(
923 domain_cookie_from(".example.com", "http://foo.example.com",),
924 &request_url,
925 ));
926 domain_mismatch!(store.insert(
928 domain_cookie_from("foo.example.com", "http://foo.example.com",),
929 &request_url,
930 ));
931 domain_mismatch!(store.insert(
932 domain_cookie_from(".foo.example.com", "http://foo.example.com",),
933 &request_url,
934 ));
935 domain_mismatch!(store.insert(
936 domain_cookie_from("bar.example.com", "http://bar.example.com",),
937 &request_url,
938 ));
939 domain_mismatch!(store.insert(
940 domain_cookie_from(".bar.example.com", "http://bar.example.com",),
941 &request_url,
942 ));
943 }
944 }
945
946 #[test]
947 fn http_only() {
948 let mut store = CookieStore::default();
949 let c = Cookie::parse(
950 "cookie1=value1; HttpOnly",
951 &test_utils::url("http://example.com/foo/bar"),
952 )
953 .unwrap();
954 non_http_scheme!(store.insert(c, &test_utils::url("ftp://example.com/foo/bar"),));
956 }
957
958 #[test]
959 fn clear() {
960 let mut store = CookieStore::default();
961 inserted!(add_cookie(
962 &mut store,
963 "cookie1=value1",
964 "http://example.com/foo/bar",
965 Some(test_utils::in_days(1)),
966 None,
967 ));
968 assert!(store.iter_any().any(|c| c.name_value() == ("cookie1", "value1")), "did not find expected cookie1=value1 cookie in store");
969 store.clear();
970 assert!(store.iter_any().count() == 0, "found unexpected cookies in cleared store");
971 }
972
973 #[test]
974 fn add_and_get() {
975 let mut store = CookieStore::default();
976 assert!(store.get("example.com", "/foo", "cookie1").is_none());
977
978 inserted!(add_cookie(
979 &mut store,
980 "cookie1=value1",
981 "http://example.com/foo/bar",
982 None,
983 None,
984 ));
985 assert!(store.get("example.com", "/foo/bar", "cookie1").is_none());
986 assert!(store.get("example.com", "/foo", "cookie2").is_none());
987 assert!(store.get("example.org", "/foo", "cookie1").is_none());
988 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
989
990 updated!(add_cookie(
991 &mut store,
992 "cookie1=value2",
993 "http://example.com/foo/bar",
994 None,
995 None,
996 ));
997 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
998
999 inserted!(add_cookie(
1000 &mut store,
1001 "cookie2=value3",
1002 "http://example.com/foo/bar",
1003 None,
1004 None,
1005 ));
1006 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
1007 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value3");
1008
1009 inserted!(add_cookie(
1010 &mut store,
1011 "cookie3=value4; HttpOnly",
1012 "http://example.com/foo/bar",
1013 None,
1014 None,
1015 ));
1016 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
1017 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value3");
1018 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value4");
1019
1020 non_http_scheme!(add_cookie(
1021 &mut store,
1022 "cookie3=value5",
1023 "ftp://example.com/foo/bar",
1024 None,
1025 None,
1026 ));
1027 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
1028 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value3");
1029 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value4");
1030 }
1031
1032 #[test]
1033 fn matches() {
1034 let store = make_match_store();
1035 check_matches!(&store);
1036 }
1037
1038 fn matches_are(store: &CookieStore, url: &str, exp: Vec<&str>) {
1039 let matches = store
1040 .matches(&test_utils::url(url))
1041 .iter()
1042 .map(|c| format!("{}={}", c.name(), c.value()))
1043 .collect::<Vec<_>>();
1044 for e in &exp {
1045 assert!(
1046 matches.iter().any(|m| &m[..] == *e),
1047 "{}: matches missing '{}'\nmatches: {:?}\n exp: {:?}",
1048 url,
1049 e,
1050 matches,
1051 exp
1052 );
1053 }
1054 assert!(
1055 matches.len() == exp.len(),
1056 "{}: matches={:?} != exp={:?}",
1057 url,
1058 matches,
1059 exp
1060 );
1061 }
1062
1063 #[test]
1064 fn some_non_https_uris_are_secure() {
1065 let secure_uris = vec![
1068 "http://localhost",
1069 "http://localhost:1234",
1070 "http://127.0.0.1",
1071 "http://127.0.0.2",
1072 "http://127.1.0.1",
1073 "http://[::1]",
1074 ];
1075 for secure_uri in secure_uris {
1076 let mut store = CookieStore::default();
1077 inserted!(add_cookie(
1078 &mut store,
1079 "cookie1=1a; Secure",
1080 secure_uri,
1081 None,
1082 None,
1083 ));
1084 matches_are(&store, secure_uri, vec!["cookie1=1a"]);
1085 }
1086 }
1087
1088 #[cfg(feature = "serde_json")]
1089 macro_rules! dump_json {
1090 ($e: expr, $i: ident) => {{
1091 use serde_json;
1092 println!("");
1093 println!(
1094 "==== {}: {} ====",
1095 $e,
1096 time::OffsetDateTime::now_utc()
1097 .format(crate::rfc3339_fmt::RFC3339_FORMAT)
1098 .unwrap()
1099 );
1100 for c in $i.iter_any() {
1101 println!(
1102 "{} {}",
1103 if c.is_expired() {
1104 "XXXXX"
1105 } else if c.is_persistent() {
1106 "PPPPP"
1107 } else {
1108 " "
1109 },
1110 serde_json::to_string(c).unwrap()
1111 );
1112 println!("----------------");
1113 }
1114 println!("================");
1115 }};
1116 }
1117
1118 #[test]
1119 fn domain_collisions() {
1120 let mut store = CookieStore::default();
1121 inserted!(add_cookie(
1123 &mut store,
1124 "cookie1=1a",
1125 "http://foo.bus.example.com/",
1126 None,
1127 None,
1128 ));
1129 inserted!(add_cookie(
1130 &mut store,
1131 "cookie1=1b",
1132 "http://bus.example.com/",
1133 None,
1134 None,
1135 ));
1136 inserted!(add_cookie(
1139 &mut store,
1140 "cookie2=2a; Domain=bus.example.com",
1141 "http://foo.bus.example.com/",
1142 None,
1143 None,
1144 ));
1145 inserted!(add_cookie(
1146 &mut store,
1147 "cookie2=2b; Domain=example.com",
1148 "http://bus.example.com/",
1149 None,
1150 None,
1151 ));
1152 #[cfg(feature = "serde_json")]
1153 dump_json!("domain_collisions", store);
1154 matches_are(
1155 &store,
1156 "http://foo.bus.example.com/",
1157 vec!["cookie1=1a", "cookie2=2a", "cookie2=2b"],
1158 );
1159 matches_are(
1160 &store,
1161 "http://bus.example.com/",
1162 vec!["cookie1=1b", "cookie2=2a", "cookie2=2b"],
1163 );
1164 matches_are(&store, "http://example.com/", vec!["cookie2=2b"]);
1165 matches_are(&store, "http://foo.example.com/", vec!["cookie2=2b"]);
1166 }
1167
1168 #[test]
1169 fn path_collisions() {
1170 let mut store = CookieStore::default();
1171 inserted!(add_cookie(
1174 &mut store,
1175 "cookie3=3a",
1176 "http://bus.example.com/foo/bar/",
1177 None,
1178 None,
1179 ));
1180 inserted!(add_cookie(
1181 &mut store,
1182 "cookie3=3b",
1183 "http://bus.example.com/foo/",
1184 None,
1185 None,
1186 ));
1187 inserted!(add_cookie(
1189 &mut store,
1190 "cookie4=4a; Path=/foo/bar/",
1191 "http://bus.example.com/",
1192 None,
1193 None,
1194 ));
1195 inserted!(add_cookie(
1196 &mut store,
1197 "cookie4=4b; Path=/foo/",
1198 "http://bus.example.com/",
1199 None,
1200 None,
1201 ));
1202 #[cfg(feature = "serde_json")]
1203 dump_json!("path_collisions", store);
1204 matches_are(
1205 &store,
1206 "http://bus.example.com/foo/bar/",
1207 vec!["cookie3=3a", "cookie3=3b", "cookie4=4a", "cookie4=4b"],
1208 );
1209 matches_are(
1213 &store,
1214 "http://bus.example.com/foo/bar",
1215 vec!["cookie3=3a", "cookie3=3b", "cookie4=4b"],
1216 );
1217 matches_are(
1218 &store,
1219 "http://bus.example.com/foo/ba",
1220 vec!["cookie3=3b", "cookie4=4b"],
1221 );
1222 matches_are(
1223 &store,
1224 "http://bus.example.com/foo/",
1225 vec!["cookie3=3b", "cookie4=4b"],
1226 );
1227 matches_are(&store, "http://bus.example.com/foo", vec!["cookie3=3b"]);
1231 matches_are(&store, "http://bus.example.com/fo", vec![]);
1232 matches_are(&store, "http://bus.example.com/", vec![]);
1233 matches_are(&store, "http://bus.example.com", vec![]);
1234 }
1235
1236 #[cfg(feature = "serde_json")]
1237 #[allow(deprecated)]
1238 mod serde_json_tests {
1239 use super::{CookieStore, StoreAction, add_cookie, make_match_store};
1240 use crate::cookie::Cookie;
1241 use crate::CookieError;
1242
1243 use crate::utils::test as test_utils;
1244
1245 macro_rules! has_str {
1246 ($e: expr, $i: ident) => {{
1247 let val = std::str::from_utf8(&$i[..]).unwrap();
1248 assert!(val.contains($e), "exp: {}\nval: {}", $e, val);
1249 }};
1250 }
1251 macro_rules! not_has_str {
1252 ($e: expr, $i: ident) => {{
1253 let val = std::str::from_utf8(&$i[..]).unwrap();
1254 assert!(!val.contains($e), "exp: {}\nval: {}", $e, val);
1255 }};
1256 }
1257
1258 #[test]
1259 fn save_json() {
1260 let mut output = vec![];
1261 let mut store = CookieStore::default();
1262 store.save_json(&mut output).unwrap();
1263 assert_eq!("", std::str::from_utf8(&output[..]).unwrap());
1264 inserted!(add_cookie(
1266 &mut store,
1267 "cookie0=value0",
1268 "http://example.com/foo/bar",
1269 None,
1270 None,
1271 ));
1272 store.save_json(&mut output).unwrap();
1273 assert_eq!("", std::str::from_utf8(&output[..]).unwrap());
1274
1275 inserted!(add_cookie(
1277 &mut store,
1278 "cookie1=value1",
1279 "http://example.com/foo/bar",
1280 None,
1281 Some(10),
1282 ));
1283 store.save_json(&mut output).unwrap();
1284 not_has_str!("cookie0=value0", output);
1285 has_str!("cookie1=value1", output);
1286 output.clear();
1287
1288 inserted!(add_cookie(
1290 &mut store,
1291 "cookie2=value2",
1292 "http://example.com/foo/bar",
1293 Some(test_utils::in_days(1)),
1294 None,
1295 ));
1296 store.save_json(&mut output).unwrap();
1297 not_has_str!("cookie0=value0", output);
1298 has_str!("cookie1=value1", output);
1299 has_str!("cookie2=value2", output);
1300 output.clear();
1301
1302 inserted!(add_cookie(
1303 &mut store,
1304 "cookie3=value3; Domain=example.com",
1305 "http://foo.example.com/foo/bar",
1306 Some(test_utils::in_days(1)),
1307 None,
1308 ));
1309 inserted!(add_cookie(
1310 &mut store,
1311 "cookie4=value4; Path=/foo/",
1312 "http://foo.example.com/foo/bar",
1313 Some(test_utils::in_days(1)),
1314 None,
1315 ));
1316 inserted!(add_cookie(
1317 &mut store,
1318 "cookie5=value5",
1319 "http://127.0.0.1/foo/bar",
1320 Some(test_utils::in_days(1)),
1321 None,
1322 ));
1323 inserted!(add_cookie(
1324 &mut store,
1325 "cookie6=value6",
1326 "http://[::1]/foo/bar",
1327 Some(test_utils::in_days(1)),
1328 None,
1329 ));
1330 inserted!(add_cookie(
1331 &mut store,
1332 "cookie7=value7; Secure",
1333 "https://[::1]/foo/bar",
1334 Some(test_utils::in_days(1)),
1335 None,
1336 ));
1337 inserted!(add_cookie(
1338 &mut store,
1339 "cookie8=value8; HttpOnly",
1340 "http://[::1]/foo/bar",
1341 Some(test_utils::in_days(1)),
1342 None,
1343 ));
1344 store.save_json(&mut output).unwrap();
1345 not_has_str!("cookie0=value0", output);
1346 has_str!("cookie1=value1", output);
1347 has_str!("cookie2=value2", output);
1348 has_str!("cookie3=value3", output);
1349 has_str!("cookie4=value4", output);
1350 has_str!("cookie5=value5", output);
1351 has_str!("cookie6=value6", output);
1352 has_str!("cookie7=value7; Secure", output);
1353 has_str!("cookie8=value8; HttpOnly", output);
1354 output.clear();
1355 }
1356
1357 #[test]
1358 fn serialize_json() {
1359 let mut output = vec![];
1360 let mut store = CookieStore::default();
1361 serde_json::to_writer(&mut output, &store).unwrap();
1362 assert_eq!("[]", std::str::from_utf8(&output[..]).unwrap());
1363 output.clear();
1364
1365 inserted!(add_cookie(
1367 &mut store,
1368 "cookie0=value0",
1369 "http://example.com/foo/bar",
1370 None,
1371 None,
1372 ));
1373 serde_json::to_writer(&mut output, &store).unwrap();
1374 assert_eq!("[]", std::str::from_utf8(&output[..]).unwrap());
1375 output.clear();
1376
1377 inserted!(add_cookie(
1379 &mut store,
1380 "cookie1=value1",
1381 "http://example.com/foo/bar",
1382 None,
1383 Some(10),
1384 ));
1385 serde_json::to_writer(&mut output, &store).unwrap();
1386 not_has_str!("cookie0=value0", output);
1387 has_str!("cookie1=value1", output);
1388 output.clear();
1389
1390 inserted!(add_cookie(
1392 &mut store,
1393 "cookie2=value2",
1394 "http://example.com/foo/bar",
1395 Some(test_utils::in_days(1)),
1396 None,
1397 ));
1398 serde_json::to_writer(&mut output, &store).unwrap();
1399 not_has_str!("cookie0=value0", output);
1400 has_str!("cookie1=value1", output);
1401 has_str!("cookie2=value2", output);
1402 output.clear();
1403
1404 inserted!(add_cookie(
1405 &mut store,
1406 "cookie3=value3; Domain=example.com",
1407 "http://foo.example.com/foo/bar",
1408 Some(test_utils::in_days(1)),
1409 None,
1410 ));
1411 inserted!(add_cookie(
1412 &mut store,
1413 "cookie4=value4; Path=/foo/",
1414 "http://foo.example.com/foo/bar",
1415 Some(test_utils::in_days(1)),
1416 None,
1417 ));
1418 inserted!(add_cookie(
1419 &mut store,
1420 "cookie5=value5",
1421 "http://127.0.0.1/foo/bar",
1422 Some(test_utils::in_days(1)),
1423 None,
1424 ));
1425 inserted!(add_cookie(
1426 &mut store,
1427 "cookie6=value6",
1428 "http://[::1]/foo/bar",
1429 Some(test_utils::in_days(1)),
1430 None,
1431 ));
1432 inserted!(add_cookie(
1433 &mut store,
1434 "cookie7=value7; Secure",
1435 "https://[::1]/foo/bar",
1436 Some(test_utils::in_days(1)),
1437 None,
1438 ));
1439 inserted!(add_cookie(
1440 &mut store,
1441 "cookie8=value8; HttpOnly",
1442 "http://[::1]/foo/bar",
1443 Some(test_utils::in_days(1)),
1444 None,
1445 ));
1446 serde_json::to_writer(&mut output, &store).unwrap();
1447 not_has_str!("cookie0=value0", output);
1448 has_str!("cookie1=value1", output);
1449 has_str!("cookie2=value2", output);
1450 has_str!("cookie3=value3", output);
1451 has_str!("cookie4=value4", output);
1452 has_str!("cookie5=value5", output);
1453 has_str!("cookie6=value6", output);
1454 has_str!("cookie7=value7; Secure", output);
1455 has_str!("cookie8=value8; HttpOnly", output);
1456 output.clear();
1457 }
1458
1459 #[test]
1460 fn load_json() {
1461 let mut store = CookieStore::default();
1462 inserted!(add_cookie(
1464 &mut store,
1465 "cookie0=value0",
1466 "http://example.com/foo/bar",
1467 None,
1468 None,
1469 ));
1470 inserted!(add_cookie(
1472 &mut store,
1473 "cookie1=value1",
1474 "http://example.com/foo/bar",
1475 None,
1476 Some(10),
1477 ));
1478 inserted!(add_cookie(
1480 &mut store,
1481 "cookie2=value2",
1482 "http://example.com/foo/bar",
1483 Some(test_utils::in_days(1)),
1484 None,
1485 ));
1486 inserted!(add_cookie(
1487 &mut store,
1488 "cookie3=value3; Domain=example.com",
1489 "http://foo.example.com/foo/bar",
1490 Some(test_utils::in_days(1)),
1491 None,
1492 ));
1493 inserted!(add_cookie(
1494 &mut store,
1495 "cookie4=value4; Path=/foo/",
1496 "http://foo.example.com/foo/bar",
1497 Some(test_utils::in_days(1)),
1498 None,
1499 ));
1500 inserted!(add_cookie(
1501 &mut store,
1502 "cookie5=value5",
1503 "http://127.0.0.1/foo/bar",
1504 Some(test_utils::in_days(1)),
1505 None,
1506 ));
1507 inserted!(add_cookie(
1508 &mut store,
1509 "cookie6=value6",
1510 "http://[::1]/foo/bar",
1511 Some(test_utils::in_days(1)),
1512 None,
1513 ));
1514 inserted!(add_cookie(
1515 &mut store,
1516 "cookie7=value7; Secure",
1517 "http://example.com/foo/bar",
1518 Some(test_utils::in_days(1)),
1519 None,
1520 ));
1521 inserted!(add_cookie(
1522 &mut store,
1523 "cookie8=value8; HttpOnly",
1524 "http://example.com/foo/bar",
1525 Some(test_utils::in_days(1)),
1526 None,
1527 ));
1528 let mut output = vec![];
1529 store.save_json(&mut output).unwrap();
1530 not_has_str!("cookie0=value0", output);
1531 has_str!("cookie1=value1", output);
1532 has_str!("cookie2=value2", output);
1533 has_str!("cookie3=value3", output);
1534 has_str!("cookie4=value4", output);
1535 has_str!("cookie5=value5", output);
1536 has_str!("cookie6=value6", output);
1537 has_str!("cookie7=value7; Secure", output);
1538 has_str!("cookie8=value8; HttpOnly", output);
1539 let store = CookieStore::load_json(&output[..]).unwrap();
1540 assert!(store.get("example.com", "/foo", "cookie0").is_none());
1541 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
1542 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value2");
1543 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value3");
1544 assert!(
1545 store
1546 .get("foo.example.com", "/foo/", "cookie4")
1547 .unwrap()
1548 .value()
1549 == "value4"
1550 );
1551 assert!(store.get("127.0.0.1", "/foo", "cookie5").unwrap().value() == "value5");
1552 assert!(store.get("[::1]", "/foo", "cookie6").unwrap().value() == "value6");
1553 assert!(store.get("example.com", "/foo", "cookie7").unwrap().value() == "value7");
1554 assert!(store.get("example.com", "/foo", "cookie8").unwrap().value() == "value8");
1555
1556 output.clear();
1557 let store = make_match_store();
1558 store.save_json(&mut output).unwrap();
1559 let store = CookieStore::load_json(&output[..]).unwrap();
1560 check_matches!(&store);
1561 }
1562
1563 #[test]
1564 fn deserialize_json() {
1565 let mut store = CookieStore::default();
1566 inserted!(add_cookie(
1568 &mut store,
1569 "cookie0=value0",
1570 "http://example.com/foo/bar",
1571 None,
1572 None,
1573 ));
1574 inserted!(add_cookie(
1576 &mut store,
1577 "cookie1=value1",
1578 "http://example.com/foo/bar",
1579 None,
1580 Some(10),
1581 ));
1582 inserted!(add_cookie(
1584 &mut store,
1585 "cookie2=value2",
1586 "http://example.com/foo/bar",
1587 Some(test_utils::in_days(1)),
1588 None,
1589 ));
1590 inserted!(add_cookie(
1591 &mut store,
1592 "cookie3=value3; Domain=example.com",
1593 "http://foo.example.com/foo/bar",
1594 Some(test_utils::in_days(1)),
1595 None,
1596 ));
1597 inserted!(add_cookie(
1598 &mut store,
1599 "cookie4=value4; Path=/foo/",
1600 "http://foo.example.com/foo/bar",
1601 Some(test_utils::in_days(1)),
1602 None,
1603 ));
1604 inserted!(add_cookie(
1605 &mut store,
1606 "cookie5=value5",
1607 "http://127.0.0.1/foo/bar",
1608 Some(test_utils::in_days(1)),
1609 None,
1610 ));
1611 inserted!(add_cookie(
1612 &mut store,
1613 "cookie6=value6",
1614 "http://[::1]/foo/bar",
1615 Some(test_utils::in_days(1)),
1616 None,
1617 ));
1618 inserted!(add_cookie(
1619 &mut store,
1620 "cookie7=value7; Secure",
1621 "http://example.com/foo/bar",
1622 Some(test_utils::in_days(1)),
1623 None,
1624 ));
1625 inserted!(add_cookie(
1626 &mut store,
1627 "cookie8=value8; HttpOnly",
1628 "http://example.com/foo/bar",
1629 Some(test_utils::in_days(1)),
1630 None,
1631 ));
1632 let mut output = vec![];
1633 serde_json::to_writer(&mut output, &store).unwrap();
1634 not_has_str!("cookie0=value0", output);
1635 has_str!("cookie1=value1", output);
1636 has_str!("cookie2=value2", output);
1637 has_str!("cookie3=value3", output);
1638 has_str!("cookie4=value4", output);
1639 has_str!("cookie5=value5", output);
1640 has_str!("cookie6=value6", output);
1641 has_str!("cookie7=value7; Secure", output);
1642 has_str!("cookie8=value8; HttpOnly", output);
1643 let store: CookieStore = serde_json::from_reader(&output[..]).unwrap();
1644 assert!(store.get("example.com", "/foo", "cookie0").is_none());
1645 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
1646 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value2");
1647 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value3");
1648 assert!(
1649 store
1650 .get("foo.example.com", "/foo/", "cookie4")
1651 .unwrap()
1652 .value()
1653 == "value4"
1654 );
1655 assert!(store.get("127.0.0.1", "/foo", "cookie5").unwrap().value() == "value5");
1656 assert!(store.get("[::1]", "/foo", "cookie6").unwrap().value() == "value6");
1657 assert!(store.get("example.com", "/foo", "cookie7").unwrap().value() == "value7");
1658 assert!(store.get("example.com", "/foo", "cookie8").unwrap().value() == "value8");
1659
1660 output.clear();
1661 let store = make_match_store();
1662 serde_json::to_writer(&mut output, &store).unwrap();
1663 let store: CookieStore = serde_json::from_reader(&output[..]).unwrap();
1664 check_matches!(&store);
1665 }
1666
1667 #[test]
1668 fn expiry_json() {
1669 let mut store = make_match_store();
1670 let request_url = test_utils::url("http://foo.example.com");
1671 let expired_cookie = Cookie::parse("cookie1=value1; Max-Age=-1", &request_url).unwrap();
1672 expired_err!(store.insert(expired_cookie, &request_url));
1673 check_matches!(&store);
1674 match store.get_mut("example.com", "/", "cookie6") {
1675 Some(cookie) => cookie.expire(),
1676 None => unreachable!(),
1677 }
1678 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1679 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1680 values_are!(store, "http://example.org/bus/bar", vec![]);
1681 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1682 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1683 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1684 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1685 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1686 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1687 values_are!(store, "http://example.com/foo/bar/bus", vec!["1", "5"]);
1688 match store.get_any("example.com", "/", "cookie6") {
1689 Some(cookie) => assert!(cookie.is_expired()),
1690 None => unreachable!(),
1691 }
1692 let request_url = test_utils::url("http://example.com/foo/");
1695 let expired_cookie = Cookie::parse("cookie5=value5; Max-Age=-1", &request_url).unwrap();
1696 expired_existing!(store.insert(expired_cookie, &request_url));
1697 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1698 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1699 values_are!(store, "http://example.org/bus/bar", vec![]);
1700 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1701 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1702 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1703 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1704 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1705 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1706 values_are!(store, "http://example.com/foo/bar/bus", vec!["1"]);
1707 match store.get_any("example.com", "/foo", "cookie5") {
1708 Some(cookie) => assert!(cookie.is_expired()),
1709 None => unreachable!(),
1710 }
1711 let mut output = vec![];
1713 store.save_json(&mut output).unwrap();
1714 store = CookieStore::load_json(&output[..]).unwrap();
1715 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1716 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1717 values_are!(store, "http://example.org/bus/bar", vec![]);
1718 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1719 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1720 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1721 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1722 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1723 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1724 values_are!(store, "http://example.com/foo/bar/bus", vec!["1"]);
1725 assert!(store.get_any("example.com", "/", "cookie6").is_none());
1726 assert!(store.get_any("example.com", "/foo", "cookie5").is_none());
1727 }
1728
1729 #[test]
1730 fn non_persistent_json() {
1731 let mut store = make_match_store();
1732 check_matches!(&store);
1733 let request_url = test_utils::url("http://example.com/tmp/");
1734 let non_persistent = Cookie::parse("cookie10=value10", &request_url).unwrap();
1735 inserted!(store.insert(non_persistent, &request_url));
1736 match store.get("example.com", "/tmp", "cookie10") {
1737 None => unreachable!(),
1738 Some(cookie) => assert_eq!("value10", cookie.value()),
1739 }
1740 let mut output = vec![];
1742 store.save_json(&mut output).unwrap();
1743 store = CookieStore::load_json(&output[..]).unwrap();
1744 check_matches!(&store);
1745 assert!(store.get("example.com", "/tmp", "cookie10").is_none());
1746 assert!(store.get_any("example.com", "/tmp", "cookie10").is_none());
1747 }
1748
1749 }
1750}
1751