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(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_default()
325 .entry(String::from(&cookie.path))
326 .or_default()
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() -> Self {
468 Self {
469 cookies: DomainMap::new(),
470 #[cfg(feature = "public_suffix")]
471 public_suffix_list: None,
472 }
473 }
474
475 #[cfg(feature = "public_suffix")]
476 pub fn new_with_public_suffix(public_suffix_list: Option<publicsuffix::List>) -> Self {
477 Self {
478 cookies: DomainMap::new(),
479 public_suffix_list,
480 }
481 }
482}
483
484#[cfg(feature = "serde_json")]
485impl CookieStore {
488 #[deprecated(
497 since = "0.22.0",
498 note = "See `cookie_store::serde` modules for more robust de/serialization options"
499 )]
500 pub fn save_json<W: Write>(&self, writer: &mut W) -> StoreResult<()> {
501 self.save(writer, ::serde_json::to_string)
502 }
503
504 #[deprecated(
512 since = "0.22.0",
513 note = "See `cookie_store::serde` modules for more robust de/serialization options"
514 )]
515 pub fn save_incl_expired_and_nonpersistent_json<W: Write>(
516 &self,
517 writer: &mut W,
518 ) -> StoreResult<()> {
519 self.save_incl_expired_and_nonpersistent(writer, ::serde_json::to_string)
520 }
521
522 #[deprecated(
529 since = "0.22.0",
530 note = "See `cookie_store::serde` modules for more robust de/serialization options"
531 )]
532 pub fn load_json<R: BufRead>(reader: R) -> StoreResult<CookieStore> {
533 CookieStore::load(reader, |cookie| ::serde_json::from_str(cookie))
534 }
535
536 #[deprecated(
543 since = "0.22.0",
544 note = "See `cookie_store::serde` modules for more robust de/serialization options"
545 )]
546 pub fn load_json_all<R: BufRead>(reader: R) -> StoreResult<CookieStore> {
547 CookieStore::load_all(reader, |cookie| ::serde_json::from_str(cookie))
548 }
549}
550
551#[cfg(feature = "serde")]
552mod serde_legacy {
556 use serde::de::{SeqAccess, Visitor};
557 use serde::{Deserialize, Deserializer, Serialize, Serializer};
558
559 impl Serialize for super::CookieStore {
560 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
561 where
562 S: Serializer,
563 {
564 serializer.collect_seq(self.iter_unexpired().filter(|c| c.is_persistent()))
565 }
566 }
567
568 struct CookieStoreVisitor;
569
570 impl<'de> Visitor<'de> for CookieStoreVisitor {
571 type Value = super::CookieStore;
572
573 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
574 write!(formatter, "a sequence of cookies")
575 }
576
577 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
578 where
579 A: SeqAccess<'de>,
580 {
581 super::CookieStore::from_cookies(
582 std::iter::from_fn(|| seq.next_element().transpose()),
583 false,
584 )
585 }
586 }
587
588 impl<'de> Deserialize<'de> for super::CookieStore {
589 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
590 where
591 D: Deserializer<'de>,
592 {
593 deserializer.deserialize_seq(CookieStoreVisitor)
594 }
595 }
596}
597
598#[cfg(test)]
599mod tests {
600 use super::CookieStore;
601 use super::{InsertResult, StoreAction};
602 use crate::cookie::Cookie;
603 use crate::CookieError;
604 use ::cookie::Cookie as RawCookie;
605 use time::OffsetDateTime;
606
607 use crate::utils::test as test_utils;
608
609 macro_rules! inserted {
610 ($e: expr) => {
611 assert_eq!(Ok(StoreAction::Inserted), $e)
612 };
613 }
614 macro_rules! updated {
615 ($e: expr) => {
616 assert_eq!(Ok(StoreAction::UpdatedExisting), $e)
617 };
618 }
619 macro_rules! expired_existing {
620 ($e: expr) => {
621 assert_eq!(Ok(StoreAction::ExpiredExisting), $e)
622 };
623 }
624 macro_rules! domain_mismatch {
625 ($e: expr) => {
626 assert_eq!(Err(CookieError::DomainMismatch), $e)
627 };
628 }
629 macro_rules! non_http_scheme {
630 ($e: expr) => {
631 assert_eq!(Err(CookieError::NonHttpScheme), $e)
632 };
633 }
634 macro_rules! non_rel_scheme {
635 ($e: expr) => {
636 assert_eq!(Err(CookieError::NonRelativeScheme), $e)
637 };
638 }
639 macro_rules! expired_err {
640 ($e: expr) => {
641 assert_eq!(Err(CookieError::Expired), $e)
642 };
643 }
644 macro_rules! values_are {
645 ($store: expr, $url: expr, $values: expr) => {{
646 let mut matched_values = $store
647 .matches(&test_utils::url($url))
648 .iter()
649 .map(|c| &c.value()[..])
650 .collect::<Vec<_>>();
651 matched_values.sort();
652
653 let mut values: Vec<&str> = $values;
654 values.sort();
655
656 assert!(
657 matched_values == values,
658 "\n{:?}\n!=\n{:?}\n",
659 matched_values,
660 values
661 );
662 }};
663 }
664
665 fn add_cookie(
666 store: &mut CookieStore,
667 cookie: &str,
668 url: &str,
669 expires: Option<OffsetDateTime>,
670 max_age: Option<u64>,
671 ) -> InsertResult {
672 store.insert(
673 test_utils::make_cookie(cookie, url, expires, max_age),
674 &test_utils::url(url),
675 )
676 }
677
678 fn make_match_store() -> CookieStore {
679 let mut store = CookieStore::default();
680 inserted!(add_cookie(
681 &mut store,
682 "cookie1=1",
683 "http://example.com/foo/bar",
684 None,
685 Some(60 * 5),
686 ));
687 inserted!(add_cookie(
688 &mut store,
689 "cookie2=2; Secure",
690 "https://example.com/sec/",
691 None,
692 Some(60 * 5),
693 ));
694 inserted!(add_cookie(
695 &mut store,
696 "cookie3=3; HttpOnly",
697 "https://example.com/sec/",
698 None,
699 Some(60 * 5),
700 ));
701 inserted!(add_cookie(
702 &mut store,
703 "cookie4=4; Secure; HttpOnly",
704 "https://example.com/sec/",
705 None,
706 Some(60 * 5),
707 ));
708 inserted!(add_cookie(
709 &mut store,
710 "cookie5=5",
711 "http://example.com/foo/",
712 None,
713 Some(60 * 5),
714 ));
715 inserted!(add_cookie(
716 &mut store,
717 "cookie6=6",
718 "http://example.com/",
719 None,
720 Some(60 * 5),
721 ));
722 inserted!(add_cookie(
723 &mut store,
724 "cookie7=7",
725 "http://bar.example.com/foo/",
726 None,
727 Some(60 * 5),
728 ));
729
730 inserted!(add_cookie(
731 &mut store,
732 "cookie8=8",
733 "http://example.org/foo/bar",
734 None,
735 Some(60 * 5),
736 ));
737 inserted!(add_cookie(
738 &mut store,
739 "cookie9=9",
740 "http://bar.example.org/foo/bar",
741 None,
742 Some(60 * 5),
743 ));
744 store
745 }
746
747 macro_rules! check_matches {
748 ($store: expr) => {{
749 values_are!($store, "http://unknowndomain.org/foo/bar", vec![]);
750 values_are!($store, "http://example.org/foo/bar", vec!["8"]);
751 values_are!($store, "http://example.org/bus/bar", vec![]);
752 values_are!($store, "http://bar.example.org/foo/bar", vec!["9"]);
753 values_are!($store, "http://bar.example.org/bus/bar", vec![]);
754 values_are!(
755 $store,
756 "https://example.com/sec/foo",
757 vec!["6", "4", "3", "2"]
758 );
759 values_are!($store, "http://example.com/sec/foo", vec!["6", "3"]);
760 values_are!($store, "ftp://example.com/sec/foo", vec!["6"]);
761 values_are!($store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
762 values_are!(
763 $store,
764 "http://example.com/foo/bar/bus",
765 vec!["1", "5", "6"]
766 );
767 }};
768 }
769
770 #[test]
771 fn insert_raw() {
772 let mut store = CookieStore::default();
773 inserted!(store.insert_raw(
774 &RawCookie::parse("cookie1=value1").unwrap(),
775 &test_utils::url("http://example.com/foo/bar"),
776 ));
777 non_rel_scheme!(store.insert_raw(
778 &RawCookie::parse("cookie1=value1").unwrap(),
779 &test_utils::url("data:nonrelativescheme"),
780 ));
781 non_http_scheme!(store.insert_raw(
782 &RawCookie::parse("cookie1=value1; HttpOnly").unwrap(),
783 &test_utils::url("ftp://example.com/"),
784 ));
785 expired_existing!(store.insert_raw(
786 &RawCookie::parse("cookie1=value1; Max-Age=0").unwrap(),
787 &test_utils::url("http://example.com/foo/bar"),
788 ));
789 expired_err!(store.insert_raw(
790 &RawCookie::parse("cookie1=value1; Max-Age=-1").unwrap(),
791 &test_utils::url("http://example.com/foo/bar"),
792 ));
793 updated!(store.insert_raw(
794 &RawCookie::parse("cookie1=value1").unwrap(),
795 &test_utils::url("http://example.com/foo/bar"),
796 ));
797 expired_existing!(store.insert_raw(
798 &RawCookie::parse("cookie1=value1; Max-Age=-1").unwrap(),
799 &test_utils::url("http://example.com/foo/bar"),
800 ));
801 domain_mismatch!(store.insert_raw(
802 &RawCookie::parse("cookie1=value1; Domain=bar.example.com").unwrap(),
803 &test_utils::url("http://example.com/foo/bar"),
804 ));
805 }
806
807 #[test]
808 fn parse() {
809 let mut store = CookieStore::default();
810 inserted!(store.parse(
811 "cookie1=value1",
812 &test_utils::url("http://example.com/foo/bar"),
813 ));
814 non_rel_scheme!(store.parse("cookie1=value1", &test_utils::url("data:nonrelativescheme"),));
815 non_http_scheme!(store.parse(
816 "cookie1=value1; HttpOnly",
817 &test_utils::url("ftp://example.com/"),
818 ));
819 expired_existing!(store.parse(
820 "cookie1=value1; Max-Age=0",
821 &test_utils::url("http://example.com/foo/bar"),
822 ));
823 expired_err!(store.parse(
824 "cookie1=value1; Max-Age=-1",
825 &test_utils::url("http://example.com/foo/bar"),
826 ));
827 updated!(store.parse(
828 "cookie1=value1",
829 &test_utils::url("http://example.com/foo/bar"),
830 ));
831 expired_existing!(store.parse(
832 "cookie1=value1; Max-Age=-1",
833 &test_utils::url("http://example.com/foo/bar"),
834 ));
835 domain_mismatch!(store.parse(
836 "cookie1=value1; Domain=bar.example.com",
837 &test_utils::url("http://example.com/foo/bar"),
838 ));
839 }
840
841 #[test]
842 fn domains() {
843 let mut store = CookieStore::default();
844 fn domain_cookie_from(domain: &str, request_url: &str) -> Cookie<'static> {
851 let cookie_str = format!("cookie1=value1; Domain={domain}");
852 Cookie::parse(cookie_str, &test_utils::url(request_url)).unwrap()
853 }
854
855 {
856 let request_url = test_utils::url("http://foo.example.com");
857 inserted!(store.insert(
859 domain_cookie_from("example.com", "http://foo.example.com",),
860 &request_url,
861 ));
862 updated!(store.insert(
863 domain_cookie_from(".example.com", "http://foo.example.com",),
864 &request_url,
865 ));
866 inserted!(store.insert(
867 domain_cookie_from("foo.example.com", "http://foo.example.com",),
868 &request_url,
869 ));
870 updated!(store.insert(
871 domain_cookie_from(".foo.example.com", "http://foo.example.com",),
872 &request_url,
873 ));
874 domain_mismatch!(store.insert(
876 domain_cookie_from("bar.example.com", "http://bar.example.com",),
877 &request_url,
878 ));
879 domain_mismatch!(store.insert(
880 domain_cookie_from(".bar.example.com", "http://bar.example.com",),
881 &request_url,
882 ));
883 domain_mismatch!(store.insert(
885 domain_cookie_from("bar.foo.example.com", "http://bar.foo.example.com",),
886 &request_url,
887 ));
888 domain_mismatch!(store.insert(
889 domain_cookie_from(".bar.foo.example.com", "http://bar.foo.example.com",),
890 &request_url,
891 ));
892 }
893
894 {
895 let request_url = test_utils::url("http://bar.example.com");
896 updated!(store.insert(
898 domain_cookie_from("example.com", "http://foo.example.com",),
899 &request_url,
900 ));
901 updated!(store.insert(
902 domain_cookie_from(".example.com", "http://foo.example.com",),
903 &request_url,
904 ));
905 inserted!(store.insert(
906 domain_cookie_from("bar.example.com", "http://bar.example.com",),
907 &request_url,
908 ));
909 updated!(store.insert(
910 domain_cookie_from(".bar.example.com", "http://bar.example.com",),
911 &request_url,
912 ));
913 domain_mismatch!(store.insert(
915 domain_cookie_from("foo.example.com", "http://foo.example.com",),
916 &request_url,
917 ));
918 domain_mismatch!(store.insert(
919 domain_cookie_from(".foo.example.com", "http://foo.example.com",),
920 &request_url,
921 ));
922 }
923 {
924 let request_url = test_utils::url("http://example.com");
925 updated!(store.insert(
927 domain_cookie_from("example.com", "http://foo.example.com",),
928 &request_url,
929 ));
930 updated!(store.insert(
931 domain_cookie_from(".example.com", "http://foo.example.com",),
932 &request_url,
933 ));
934 domain_mismatch!(store.insert(
936 domain_cookie_from("foo.example.com", "http://foo.example.com",),
937 &request_url,
938 ));
939 domain_mismatch!(store.insert(
940 domain_cookie_from(".foo.example.com", "http://foo.example.com",),
941 &request_url,
942 ));
943 domain_mismatch!(store.insert(
944 domain_cookie_from("bar.example.com", "http://bar.example.com",),
945 &request_url,
946 ));
947 domain_mismatch!(store.insert(
948 domain_cookie_from(".bar.example.com", "http://bar.example.com",),
949 &request_url,
950 ));
951 }
952 }
953
954 #[test]
955 fn http_only() {
956 let mut store = CookieStore::default();
957 let c = Cookie::parse(
958 "cookie1=value1; HttpOnly",
959 &test_utils::url("http://example.com/foo/bar"),
960 )
961 .unwrap();
962 non_http_scheme!(store.insert(c, &test_utils::url("ftp://example.com/foo/bar"),));
964 }
965
966 #[test]
967 fn clear() {
968 let mut store = CookieStore::default();
969 inserted!(add_cookie(
970 &mut store,
971 "cookie1=value1",
972 "http://example.com/foo/bar",
973 Some(test_utils::in_days(1)),
974 None,
975 ));
976 assert!(
977 store
978 .iter_any()
979 .any(|c| c.name_value() == ("cookie1", "value1")),
980 "did not find expected cookie1=value1 cookie in store"
981 );
982 store.clear();
983 assert!(
984 store.iter_any().count() == 0,
985 "found unexpected cookies in cleared store"
986 );
987 }
988
989 #[test]
990 fn add_and_get() {
991 let mut store = CookieStore::default();
992 assert!(store.get("example.com", "/foo", "cookie1").is_none());
993
994 inserted!(add_cookie(
995 &mut store,
996 "cookie1=value1",
997 "http://example.com/foo/bar",
998 None,
999 None,
1000 ));
1001 assert!(store.get("example.com", "/foo/bar", "cookie1").is_none());
1002 assert!(store.get("example.com", "/foo", "cookie2").is_none());
1003 assert!(store.get("example.org", "/foo", "cookie1").is_none());
1004 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
1005
1006 updated!(add_cookie(
1007 &mut store,
1008 "cookie1=value2",
1009 "http://example.com/foo/bar",
1010 None,
1011 None,
1012 ));
1013 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
1014
1015 inserted!(add_cookie(
1016 &mut store,
1017 "cookie2=value3",
1018 "http://example.com/foo/bar",
1019 None,
1020 None,
1021 ));
1022 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
1023 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value3");
1024
1025 inserted!(add_cookie(
1026 &mut store,
1027 "cookie3=value4; HttpOnly",
1028 "http://example.com/foo/bar",
1029 None,
1030 None,
1031 ));
1032 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
1033 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value3");
1034 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value4");
1035
1036 non_http_scheme!(add_cookie(
1037 &mut store,
1038 "cookie3=value5",
1039 "ftp://example.com/foo/bar",
1040 None,
1041 None,
1042 ));
1043 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value2");
1044 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value3");
1045 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value4");
1046 }
1047
1048 #[test]
1049 fn matches() {
1050 let store = make_match_store();
1051 check_matches!(&store);
1052 }
1053
1054 fn matches_are(store: &CookieStore, url: &str, exp: Vec<&str>) {
1055 let matches = store
1056 .matches(&test_utils::url(url))
1057 .iter()
1058 .map(|c| format!("{}={}", c.name(), c.value()))
1059 .collect::<Vec<_>>();
1060 for e in &exp {
1061 assert!(
1062 matches.iter().any(|m| &m[..] == *e),
1063 "{url}: matches missing '{e}'\nmatches: {matches:?}\n exp: {exp:?}"
1064 );
1065 }
1066 assert!(
1067 matches.len() == exp.len(),
1068 "{url}: matches={matches:?} != exp={exp:?}"
1069 );
1070 }
1071
1072 #[test]
1073 fn some_non_https_uris_are_secure() {
1074 let secure_uris = vec![
1077 "http://localhost",
1078 "http://localhost:1234",
1079 "http://127.0.0.1",
1080 "http://127.0.0.2",
1081 "http://127.1.0.1",
1082 "http://[::1]",
1083 ];
1084 for secure_uri in secure_uris {
1085 let mut store = CookieStore::default();
1086 inserted!(add_cookie(
1087 &mut store,
1088 "cookie1=1a; Secure",
1089 secure_uri,
1090 None,
1091 None,
1092 ));
1093 matches_are(&store, secure_uri, vec!["cookie1=1a"]);
1094 }
1095 }
1096
1097 #[cfg(feature = "serde_json")]
1098 macro_rules! dump_json {
1099 ($e: expr, $i: ident) => {{
1100 use serde_json;
1101 println!("");
1102 println!(
1103 "==== {}: {} ====",
1104 $e,
1105 time::OffsetDateTime::now_utc()
1106 .format(crate::rfc3339_fmt::RFC3339_FORMAT)
1107 .unwrap()
1108 );
1109 for c in $i.iter_any() {
1110 println!(
1111 "{} {}",
1112 if c.is_expired() {
1113 "XXXXX"
1114 } else if c.is_persistent() {
1115 "PPPPP"
1116 } else {
1117 " "
1118 },
1119 serde_json::to_string(c).unwrap()
1120 );
1121 println!("----------------");
1122 }
1123 println!("================");
1124 }};
1125 }
1126
1127 #[test]
1128 fn domain_collisions() {
1129 let mut store = CookieStore::default();
1130 inserted!(add_cookie(
1132 &mut store,
1133 "cookie1=1a",
1134 "http://foo.bus.example.com/",
1135 None,
1136 None,
1137 ));
1138 inserted!(add_cookie(
1139 &mut store,
1140 "cookie1=1b",
1141 "http://bus.example.com/",
1142 None,
1143 None,
1144 ));
1145 inserted!(add_cookie(
1148 &mut store,
1149 "cookie2=2a; Domain=bus.example.com",
1150 "http://foo.bus.example.com/",
1151 None,
1152 None,
1153 ));
1154 inserted!(add_cookie(
1155 &mut store,
1156 "cookie2=2b; Domain=example.com",
1157 "http://bus.example.com/",
1158 None,
1159 None,
1160 ));
1161 #[cfg(feature = "serde_json")]
1162 dump_json!("domain_collisions", store);
1163 matches_are(
1164 &store,
1165 "http://foo.bus.example.com/",
1166 vec!["cookie1=1a", "cookie2=2a", "cookie2=2b"],
1167 );
1168 matches_are(
1169 &store,
1170 "http://bus.example.com/",
1171 vec!["cookie1=1b", "cookie2=2a", "cookie2=2b"],
1172 );
1173 matches_are(&store, "http://example.com/", vec!["cookie2=2b"]);
1174 matches_are(&store, "http://foo.example.com/", vec!["cookie2=2b"]);
1175 }
1176
1177 #[test]
1178 fn path_collisions() {
1179 let mut store = CookieStore::default();
1180 inserted!(add_cookie(
1183 &mut store,
1184 "cookie3=3a",
1185 "http://bus.example.com/foo/bar/",
1186 None,
1187 None,
1188 ));
1189 inserted!(add_cookie(
1190 &mut store,
1191 "cookie3=3b",
1192 "http://bus.example.com/foo/",
1193 None,
1194 None,
1195 ));
1196 inserted!(add_cookie(
1198 &mut store,
1199 "cookie4=4a; Path=/foo/bar/",
1200 "http://bus.example.com/",
1201 None,
1202 None,
1203 ));
1204 inserted!(add_cookie(
1205 &mut store,
1206 "cookie4=4b; Path=/foo/",
1207 "http://bus.example.com/",
1208 None,
1209 None,
1210 ));
1211 #[cfg(feature = "serde_json")]
1212 dump_json!("path_collisions", store);
1213 matches_are(
1214 &store,
1215 "http://bus.example.com/foo/bar/",
1216 vec!["cookie3=3a", "cookie3=3b", "cookie4=4a", "cookie4=4b"],
1217 );
1218 matches_are(
1222 &store,
1223 "http://bus.example.com/foo/bar",
1224 vec!["cookie3=3a", "cookie3=3b", "cookie4=4b"],
1225 );
1226 matches_are(
1227 &store,
1228 "http://bus.example.com/foo/ba",
1229 vec!["cookie3=3b", "cookie4=4b"],
1230 );
1231 matches_are(
1232 &store,
1233 "http://bus.example.com/foo/",
1234 vec!["cookie3=3b", "cookie4=4b"],
1235 );
1236 matches_are(&store, "http://bus.example.com/foo", vec!["cookie3=3b"]);
1240 matches_are(&store, "http://bus.example.com/fo", vec![]);
1241 matches_are(&store, "http://bus.example.com/", vec![]);
1242 matches_are(&store, "http://bus.example.com", vec![]);
1243 }
1244
1245 #[cfg(feature = "serde_json")]
1246 #[allow(deprecated)]
1247 mod serde_json_tests {
1248 use super::{add_cookie, make_match_store, CookieStore, StoreAction};
1249 use crate::cookie::Cookie;
1250 use crate::CookieError;
1251
1252 use crate::utils::test as test_utils;
1253
1254 macro_rules! has_str {
1255 ($e: expr, $i: ident) => {{
1256 let val = std::str::from_utf8(&$i[..]).unwrap();
1257 assert!(val.contains($e), "exp: {}\nval: {}", $e, val);
1258 }};
1259 }
1260 macro_rules! not_has_str {
1261 ($e: expr, $i: ident) => {{
1262 let val = std::str::from_utf8(&$i[..]).unwrap();
1263 assert!(!val.contains($e), "exp: {}\nval: {}", $e, val);
1264 }};
1265 }
1266
1267 #[test]
1268 fn save_json() {
1269 let mut output = vec![];
1270 let mut store = CookieStore::default();
1271 store.save_json(&mut output).unwrap();
1272 assert_eq!("", std::str::from_utf8(&output[..]).unwrap());
1273 inserted!(add_cookie(
1275 &mut store,
1276 "cookie0=value0",
1277 "http://example.com/foo/bar",
1278 None,
1279 None,
1280 ));
1281 store.save_json(&mut output).unwrap();
1282 assert_eq!("", std::str::from_utf8(&output[..]).unwrap());
1283
1284 inserted!(add_cookie(
1286 &mut store,
1287 "cookie1=value1",
1288 "http://example.com/foo/bar",
1289 None,
1290 Some(10),
1291 ));
1292 store.save_json(&mut output).unwrap();
1293 not_has_str!("cookie0=value0", output);
1294 has_str!("cookie1=value1", output);
1295 output.clear();
1296
1297 inserted!(add_cookie(
1299 &mut store,
1300 "cookie2=value2",
1301 "http://example.com/foo/bar",
1302 Some(test_utils::in_days(1)),
1303 None,
1304 ));
1305 store.save_json(&mut output).unwrap();
1306 not_has_str!("cookie0=value0", output);
1307 has_str!("cookie1=value1", output);
1308 has_str!("cookie2=value2", output);
1309 output.clear();
1310
1311 inserted!(add_cookie(
1312 &mut store,
1313 "cookie3=value3; Domain=example.com",
1314 "http://foo.example.com/foo/bar",
1315 Some(test_utils::in_days(1)),
1316 None,
1317 ));
1318 inserted!(add_cookie(
1319 &mut store,
1320 "cookie4=value4; Path=/foo/",
1321 "http://foo.example.com/foo/bar",
1322 Some(test_utils::in_days(1)),
1323 None,
1324 ));
1325 inserted!(add_cookie(
1326 &mut store,
1327 "cookie5=value5",
1328 "http://127.0.0.1/foo/bar",
1329 Some(test_utils::in_days(1)),
1330 None,
1331 ));
1332 inserted!(add_cookie(
1333 &mut store,
1334 "cookie6=value6",
1335 "http://[::1]/foo/bar",
1336 Some(test_utils::in_days(1)),
1337 None,
1338 ));
1339 inserted!(add_cookie(
1340 &mut store,
1341 "cookie7=value7; Secure",
1342 "https://[::1]/foo/bar",
1343 Some(test_utils::in_days(1)),
1344 None,
1345 ));
1346 inserted!(add_cookie(
1347 &mut store,
1348 "cookie8=value8; HttpOnly",
1349 "http://[::1]/foo/bar",
1350 Some(test_utils::in_days(1)),
1351 None,
1352 ));
1353 store.save_json(&mut output).unwrap();
1354 not_has_str!("cookie0=value0", output);
1355 has_str!("cookie1=value1", output);
1356 has_str!("cookie2=value2", output);
1357 has_str!("cookie3=value3", output);
1358 has_str!("cookie4=value4", output);
1359 has_str!("cookie5=value5", output);
1360 has_str!("cookie6=value6", output);
1361 has_str!("cookie7=value7; Secure", output);
1362 has_str!("cookie8=value8; HttpOnly", output);
1363 output.clear();
1364 }
1365
1366 #[test]
1367 fn serialize_json() {
1368 let mut output = vec![];
1369 let mut store = CookieStore::default();
1370 serde_json::to_writer(&mut output, &store).unwrap();
1371 assert_eq!("[]", std::str::from_utf8(&output[..]).unwrap());
1372 output.clear();
1373
1374 inserted!(add_cookie(
1376 &mut store,
1377 "cookie0=value0",
1378 "http://example.com/foo/bar",
1379 None,
1380 None,
1381 ));
1382 serde_json::to_writer(&mut output, &store).unwrap();
1383 assert_eq!("[]", std::str::from_utf8(&output[..]).unwrap());
1384 output.clear();
1385
1386 inserted!(add_cookie(
1388 &mut store,
1389 "cookie1=value1",
1390 "http://example.com/foo/bar",
1391 None,
1392 Some(10),
1393 ));
1394 serde_json::to_writer(&mut output, &store).unwrap();
1395 not_has_str!("cookie0=value0", output);
1396 has_str!("cookie1=value1", output);
1397 output.clear();
1398
1399 inserted!(add_cookie(
1401 &mut store,
1402 "cookie2=value2",
1403 "http://example.com/foo/bar",
1404 Some(test_utils::in_days(1)),
1405 None,
1406 ));
1407 serde_json::to_writer(&mut output, &store).unwrap();
1408 not_has_str!("cookie0=value0", output);
1409 has_str!("cookie1=value1", output);
1410 has_str!("cookie2=value2", output);
1411 output.clear();
1412
1413 inserted!(add_cookie(
1414 &mut store,
1415 "cookie3=value3; Domain=example.com",
1416 "http://foo.example.com/foo/bar",
1417 Some(test_utils::in_days(1)),
1418 None,
1419 ));
1420 inserted!(add_cookie(
1421 &mut store,
1422 "cookie4=value4; Path=/foo/",
1423 "http://foo.example.com/foo/bar",
1424 Some(test_utils::in_days(1)),
1425 None,
1426 ));
1427 inserted!(add_cookie(
1428 &mut store,
1429 "cookie5=value5",
1430 "http://127.0.0.1/foo/bar",
1431 Some(test_utils::in_days(1)),
1432 None,
1433 ));
1434 inserted!(add_cookie(
1435 &mut store,
1436 "cookie6=value6",
1437 "http://[::1]/foo/bar",
1438 Some(test_utils::in_days(1)),
1439 None,
1440 ));
1441 inserted!(add_cookie(
1442 &mut store,
1443 "cookie7=value7; Secure",
1444 "https://[::1]/foo/bar",
1445 Some(test_utils::in_days(1)),
1446 None,
1447 ));
1448 inserted!(add_cookie(
1449 &mut store,
1450 "cookie8=value8; HttpOnly",
1451 "http://[::1]/foo/bar",
1452 Some(test_utils::in_days(1)),
1453 None,
1454 ));
1455 serde_json::to_writer(&mut output, &store).unwrap();
1456 not_has_str!("cookie0=value0", output);
1457 has_str!("cookie1=value1", output);
1458 has_str!("cookie2=value2", output);
1459 has_str!("cookie3=value3", output);
1460 has_str!("cookie4=value4", output);
1461 has_str!("cookie5=value5", output);
1462 has_str!("cookie6=value6", output);
1463 has_str!("cookie7=value7; Secure", output);
1464 has_str!("cookie8=value8; HttpOnly", output);
1465 output.clear();
1466 }
1467
1468 #[test]
1469 fn load_json() {
1470 let mut store = CookieStore::default();
1471 inserted!(add_cookie(
1473 &mut store,
1474 "cookie0=value0",
1475 "http://example.com/foo/bar",
1476 None,
1477 None,
1478 ));
1479 inserted!(add_cookie(
1481 &mut store,
1482 "cookie1=value1",
1483 "http://example.com/foo/bar",
1484 None,
1485 Some(10),
1486 ));
1487 inserted!(add_cookie(
1489 &mut store,
1490 "cookie2=value2",
1491 "http://example.com/foo/bar",
1492 Some(test_utils::in_days(1)),
1493 None,
1494 ));
1495 inserted!(add_cookie(
1496 &mut store,
1497 "cookie3=value3; Domain=example.com",
1498 "http://foo.example.com/foo/bar",
1499 Some(test_utils::in_days(1)),
1500 None,
1501 ));
1502 inserted!(add_cookie(
1503 &mut store,
1504 "cookie4=value4; Path=/foo/",
1505 "http://foo.example.com/foo/bar",
1506 Some(test_utils::in_days(1)),
1507 None,
1508 ));
1509 inserted!(add_cookie(
1510 &mut store,
1511 "cookie5=value5",
1512 "http://127.0.0.1/foo/bar",
1513 Some(test_utils::in_days(1)),
1514 None,
1515 ));
1516 inserted!(add_cookie(
1517 &mut store,
1518 "cookie6=value6",
1519 "http://[::1]/foo/bar",
1520 Some(test_utils::in_days(1)),
1521 None,
1522 ));
1523 inserted!(add_cookie(
1524 &mut store,
1525 "cookie7=value7; Secure",
1526 "http://example.com/foo/bar",
1527 Some(test_utils::in_days(1)),
1528 None,
1529 ));
1530 inserted!(add_cookie(
1531 &mut store,
1532 "cookie8=value8; HttpOnly",
1533 "http://example.com/foo/bar",
1534 Some(test_utils::in_days(1)),
1535 None,
1536 ));
1537 let mut output = vec![];
1538 store.save_json(&mut output).unwrap();
1539 not_has_str!("cookie0=value0", output);
1540 has_str!("cookie1=value1", output);
1541 has_str!("cookie2=value2", output);
1542 has_str!("cookie3=value3", output);
1543 has_str!("cookie4=value4", output);
1544 has_str!("cookie5=value5", output);
1545 has_str!("cookie6=value6", output);
1546 has_str!("cookie7=value7; Secure", output);
1547 has_str!("cookie8=value8; HttpOnly", output);
1548 let store = CookieStore::load_json(&output[..]).unwrap();
1549 assert!(store.get("example.com", "/foo", "cookie0").is_none());
1550 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
1551 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value2");
1552 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value3");
1553 assert!(
1554 store
1555 .get("foo.example.com", "/foo/", "cookie4")
1556 .unwrap()
1557 .value()
1558 == "value4"
1559 );
1560 assert!(store.get("127.0.0.1", "/foo", "cookie5").unwrap().value() == "value5");
1561 assert!(store.get("[::1]", "/foo", "cookie6").unwrap().value() == "value6");
1562 assert!(store.get("example.com", "/foo", "cookie7").unwrap().value() == "value7");
1563 assert!(store.get("example.com", "/foo", "cookie8").unwrap().value() == "value8");
1564
1565 output.clear();
1566 let store = make_match_store();
1567 store.save_json(&mut output).unwrap();
1568 let store = CookieStore::load_json(&output[..]).unwrap();
1569 check_matches!(&store);
1570 }
1571
1572 #[test]
1573 fn deserialize_json() {
1574 let mut store = CookieStore::default();
1575 inserted!(add_cookie(
1577 &mut store,
1578 "cookie0=value0",
1579 "http://example.com/foo/bar",
1580 None,
1581 None,
1582 ));
1583 inserted!(add_cookie(
1585 &mut store,
1586 "cookie1=value1",
1587 "http://example.com/foo/bar",
1588 None,
1589 Some(10),
1590 ));
1591 inserted!(add_cookie(
1593 &mut store,
1594 "cookie2=value2",
1595 "http://example.com/foo/bar",
1596 Some(test_utils::in_days(1)),
1597 None,
1598 ));
1599 inserted!(add_cookie(
1600 &mut store,
1601 "cookie3=value3; Domain=example.com",
1602 "http://foo.example.com/foo/bar",
1603 Some(test_utils::in_days(1)),
1604 None,
1605 ));
1606 inserted!(add_cookie(
1607 &mut store,
1608 "cookie4=value4; Path=/foo/",
1609 "http://foo.example.com/foo/bar",
1610 Some(test_utils::in_days(1)),
1611 None,
1612 ));
1613 inserted!(add_cookie(
1614 &mut store,
1615 "cookie5=value5",
1616 "http://127.0.0.1/foo/bar",
1617 Some(test_utils::in_days(1)),
1618 None,
1619 ));
1620 inserted!(add_cookie(
1621 &mut store,
1622 "cookie6=value6",
1623 "http://[::1]/foo/bar",
1624 Some(test_utils::in_days(1)),
1625 None,
1626 ));
1627 inserted!(add_cookie(
1628 &mut store,
1629 "cookie7=value7; Secure",
1630 "http://example.com/foo/bar",
1631 Some(test_utils::in_days(1)),
1632 None,
1633 ));
1634 inserted!(add_cookie(
1635 &mut store,
1636 "cookie8=value8; HttpOnly",
1637 "http://example.com/foo/bar",
1638 Some(test_utils::in_days(1)),
1639 None,
1640 ));
1641 let mut output = vec![];
1642 serde_json::to_writer(&mut output, &store).unwrap();
1643 not_has_str!("cookie0=value0", output);
1644 has_str!("cookie1=value1", output);
1645 has_str!("cookie2=value2", output);
1646 has_str!("cookie3=value3", output);
1647 has_str!("cookie4=value4", output);
1648 has_str!("cookie5=value5", output);
1649 has_str!("cookie6=value6", output);
1650 has_str!("cookie7=value7; Secure", output);
1651 has_str!("cookie8=value8; HttpOnly", output);
1652 let store: CookieStore = serde_json::from_reader(&output[..]).unwrap();
1653 assert!(store.get("example.com", "/foo", "cookie0").is_none());
1654 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
1655 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value2");
1656 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value3");
1657 assert!(
1658 store
1659 .get("foo.example.com", "/foo/", "cookie4")
1660 .unwrap()
1661 .value()
1662 == "value4"
1663 );
1664 assert!(store.get("127.0.0.1", "/foo", "cookie5").unwrap().value() == "value5");
1665 assert!(store.get("[::1]", "/foo", "cookie6").unwrap().value() == "value6");
1666 assert!(store.get("example.com", "/foo", "cookie7").unwrap().value() == "value7");
1667 assert!(store.get("example.com", "/foo", "cookie8").unwrap().value() == "value8");
1668
1669 output.clear();
1670 let store = make_match_store();
1671 serde_json::to_writer(&mut output, &store).unwrap();
1672 let store: CookieStore = serde_json::from_reader(&output[..]).unwrap();
1673 check_matches!(&store);
1674 }
1675
1676 #[test]
1677 fn expiry_json() {
1678 let mut store = make_match_store();
1679 let request_url = test_utils::url("http://foo.example.com");
1680 let expired_cookie = Cookie::parse("cookie1=value1; Max-Age=-1", &request_url).unwrap();
1681 expired_err!(store.insert(expired_cookie, &request_url));
1682 check_matches!(&store);
1683 match store.get_mut("example.com", "/", "cookie6") {
1684 Some(cookie) => cookie.expire(),
1685 None => unreachable!(),
1686 }
1687 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1688 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1689 values_are!(store, "http://example.org/bus/bar", vec![]);
1690 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1691 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1692 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1693 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1694 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1695 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1696 values_are!(store, "http://example.com/foo/bar/bus", vec!["1", "5"]);
1697 match store.get_any("example.com", "/", "cookie6") {
1698 Some(cookie) => assert!(cookie.is_expired()),
1699 None => unreachable!(),
1700 }
1701 let request_url = test_utils::url("http://example.com/foo/");
1704 let expired_cookie = Cookie::parse("cookie5=value5; Max-Age=-1", &request_url).unwrap();
1705 expired_existing!(store.insert(expired_cookie, &request_url));
1706 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1707 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1708 values_are!(store, "http://example.org/bus/bar", vec![]);
1709 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1710 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1711 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1712 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1713 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1714 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1715 values_are!(store, "http://example.com/foo/bar/bus", vec!["1"]);
1716 match store.get_any("example.com", "/foo", "cookie5") {
1717 Some(cookie) => assert!(cookie.is_expired()),
1718 None => unreachable!(),
1719 }
1720 let mut output = vec![];
1722 store.save_json(&mut output).unwrap();
1723 store = CookieStore::load_json(&output[..]).unwrap();
1724 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1725 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1726 values_are!(store, "http://example.org/bus/bar", vec![]);
1727 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1728 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1729 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1730 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1731 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1732 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1733 values_are!(store, "http://example.com/foo/bar/bus", vec!["1"]);
1734 assert!(store.get_any("example.com", "/", "cookie6").is_none());
1735 assert!(store.get_any("example.com", "/foo", "cookie5").is_none());
1736 }
1737
1738 #[test]
1739 fn non_persistent_json() {
1740 let mut store = make_match_store();
1741 check_matches!(&store);
1742 let request_url = test_utils::url("http://example.com/tmp/");
1743 let non_persistent = Cookie::parse("cookie10=value10", &request_url).unwrap();
1744 inserted!(store.insert(non_persistent, &request_url));
1745 match store.get("example.com", "/tmp", "cookie10") {
1746 None => unreachable!(),
1747 Some(cookie) => assert_eq!("value10", cookie.value()),
1748 }
1749 let mut output = vec![];
1751 store.save_json(&mut output).unwrap();
1752 store = CookieStore::load_json(&output[..]).unwrap();
1753 check_matches!(&store);
1754 assert!(store.get("example.com", "/tmp", "cookie10").is_none());
1755 assert!(store.get_any("example.com", "/tmp", "cookie10").is_none());
1756 }
1757 }
1758}