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() -> 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 "{}: matches missing '{}'\nmatches: {:?}\n exp: {:?}",
1064 url,
1065 e,
1066 matches,
1067 exp
1068 );
1069 }
1070 assert!(
1071 matches.len() == exp.len(),
1072 "{}: matches={:?} != exp={:?}",
1073 url,
1074 matches,
1075 exp
1076 );
1077 }
1078
1079 #[test]
1080 fn some_non_https_uris_are_secure() {
1081 let secure_uris = vec![
1084 "http://localhost",
1085 "http://localhost:1234",
1086 "http://127.0.0.1",
1087 "http://127.0.0.2",
1088 "http://127.1.0.1",
1089 "http://[::1]",
1090 ];
1091 for secure_uri in secure_uris {
1092 let mut store = CookieStore::default();
1093 inserted!(add_cookie(
1094 &mut store,
1095 "cookie1=1a; Secure",
1096 secure_uri,
1097 None,
1098 None,
1099 ));
1100 matches_are(&store, secure_uri, vec!["cookie1=1a"]);
1101 }
1102 }
1103
1104 #[cfg(feature = "serde_json")]
1105 macro_rules! dump_json {
1106 ($e: expr, $i: ident) => {{
1107 use serde_json;
1108 println!("");
1109 println!(
1110 "==== {}: {} ====",
1111 $e,
1112 time::OffsetDateTime::now_utc()
1113 .format(crate::rfc3339_fmt::RFC3339_FORMAT)
1114 .unwrap()
1115 );
1116 for c in $i.iter_any() {
1117 println!(
1118 "{} {}",
1119 if c.is_expired() {
1120 "XXXXX"
1121 } else if c.is_persistent() {
1122 "PPPPP"
1123 } else {
1124 " "
1125 },
1126 serde_json::to_string(c).unwrap()
1127 );
1128 println!("----------------");
1129 }
1130 println!("================");
1131 }};
1132 }
1133
1134 #[test]
1135 fn domain_collisions() {
1136 let mut store = CookieStore::default();
1137 inserted!(add_cookie(
1139 &mut store,
1140 "cookie1=1a",
1141 "http://foo.bus.example.com/",
1142 None,
1143 None,
1144 ));
1145 inserted!(add_cookie(
1146 &mut store,
1147 "cookie1=1b",
1148 "http://bus.example.com/",
1149 None,
1150 None,
1151 ));
1152 inserted!(add_cookie(
1155 &mut store,
1156 "cookie2=2a; Domain=bus.example.com",
1157 "http://foo.bus.example.com/",
1158 None,
1159 None,
1160 ));
1161 inserted!(add_cookie(
1162 &mut store,
1163 "cookie2=2b; Domain=example.com",
1164 "http://bus.example.com/",
1165 None,
1166 None,
1167 ));
1168 #[cfg(feature = "serde_json")]
1169 dump_json!("domain_collisions", store);
1170 matches_are(
1171 &store,
1172 "http://foo.bus.example.com/",
1173 vec!["cookie1=1a", "cookie2=2a", "cookie2=2b"],
1174 );
1175 matches_are(
1176 &store,
1177 "http://bus.example.com/",
1178 vec!["cookie1=1b", "cookie2=2a", "cookie2=2b"],
1179 );
1180 matches_are(&store, "http://example.com/", vec!["cookie2=2b"]);
1181 matches_are(&store, "http://foo.example.com/", vec!["cookie2=2b"]);
1182 }
1183
1184 #[test]
1185 fn path_collisions() {
1186 let mut store = CookieStore::default();
1187 inserted!(add_cookie(
1190 &mut store,
1191 "cookie3=3a",
1192 "http://bus.example.com/foo/bar/",
1193 None,
1194 None,
1195 ));
1196 inserted!(add_cookie(
1197 &mut store,
1198 "cookie3=3b",
1199 "http://bus.example.com/foo/",
1200 None,
1201 None,
1202 ));
1203 inserted!(add_cookie(
1205 &mut store,
1206 "cookie4=4a; Path=/foo/bar/",
1207 "http://bus.example.com/",
1208 None,
1209 None,
1210 ));
1211 inserted!(add_cookie(
1212 &mut store,
1213 "cookie4=4b; Path=/foo/",
1214 "http://bus.example.com/",
1215 None,
1216 None,
1217 ));
1218 #[cfg(feature = "serde_json")]
1219 dump_json!("path_collisions", store);
1220 matches_are(
1221 &store,
1222 "http://bus.example.com/foo/bar/",
1223 vec!["cookie3=3a", "cookie3=3b", "cookie4=4a", "cookie4=4b"],
1224 );
1225 matches_are(
1229 &store,
1230 "http://bus.example.com/foo/bar",
1231 vec!["cookie3=3a", "cookie3=3b", "cookie4=4b"],
1232 );
1233 matches_are(
1234 &store,
1235 "http://bus.example.com/foo/ba",
1236 vec!["cookie3=3b", "cookie4=4b"],
1237 );
1238 matches_are(
1239 &store,
1240 "http://bus.example.com/foo/",
1241 vec!["cookie3=3b", "cookie4=4b"],
1242 );
1243 matches_are(&store, "http://bus.example.com/foo", vec!["cookie3=3b"]);
1247 matches_are(&store, "http://bus.example.com/fo", vec![]);
1248 matches_are(&store, "http://bus.example.com/", vec![]);
1249 matches_are(&store, "http://bus.example.com", vec![]);
1250 }
1251
1252 #[cfg(feature = "serde_json")]
1253 #[allow(deprecated)]
1254 mod serde_json_tests {
1255 use super::{add_cookie, make_match_store, CookieStore, StoreAction};
1256 use crate::cookie::Cookie;
1257 use crate::CookieError;
1258
1259 use crate::utils::test as test_utils;
1260
1261 macro_rules! has_str {
1262 ($e: expr, $i: ident) => {{
1263 let val = std::str::from_utf8(&$i[..]).unwrap();
1264 assert!(val.contains($e), "exp: {}\nval: {}", $e, val);
1265 }};
1266 }
1267 macro_rules! not_has_str {
1268 ($e: expr, $i: ident) => {{
1269 let val = std::str::from_utf8(&$i[..]).unwrap();
1270 assert!(!val.contains($e), "exp: {}\nval: {}", $e, val);
1271 }};
1272 }
1273
1274 #[test]
1275 fn save_json() {
1276 let mut output = vec![];
1277 let mut store = CookieStore::default();
1278 store.save_json(&mut output).unwrap();
1279 assert_eq!("", std::str::from_utf8(&output[..]).unwrap());
1280 inserted!(add_cookie(
1282 &mut store,
1283 "cookie0=value0",
1284 "http://example.com/foo/bar",
1285 None,
1286 None,
1287 ));
1288 store.save_json(&mut output).unwrap();
1289 assert_eq!("", std::str::from_utf8(&output[..]).unwrap());
1290
1291 inserted!(add_cookie(
1293 &mut store,
1294 "cookie1=value1",
1295 "http://example.com/foo/bar",
1296 None,
1297 Some(10),
1298 ));
1299 store.save_json(&mut output).unwrap();
1300 not_has_str!("cookie0=value0", output);
1301 has_str!("cookie1=value1", output);
1302 output.clear();
1303
1304 inserted!(add_cookie(
1306 &mut store,
1307 "cookie2=value2",
1308 "http://example.com/foo/bar",
1309 Some(test_utils::in_days(1)),
1310 None,
1311 ));
1312 store.save_json(&mut output).unwrap();
1313 not_has_str!("cookie0=value0", output);
1314 has_str!("cookie1=value1", output);
1315 has_str!("cookie2=value2", output);
1316 output.clear();
1317
1318 inserted!(add_cookie(
1319 &mut store,
1320 "cookie3=value3; Domain=example.com",
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 "cookie4=value4; Path=/foo/",
1328 "http://foo.example.com/foo/bar",
1329 Some(test_utils::in_days(1)),
1330 None,
1331 ));
1332 inserted!(add_cookie(
1333 &mut store,
1334 "cookie5=value5",
1335 "http://127.0.0.1/foo/bar",
1336 Some(test_utils::in_days(1)),
1337 None,
1338 ));
1339 inserted!(add_cookie(
1340 &mut store,
1341 "cookie6=value6",
1342 "http://[::1]/foo/bar",
1343 Some(test_utils::in_days(1)),
1344 None,
1345 ));
1346 inserted!(add_cookie(
1347 &mut store,
1348 "cookie7=value7; Secure",
1349 "https://[::1]/foo/bar",
1350 Some(test_utils::in_days(1)),
1351 None,
1352 ));
1353 inserted!(add_cookie(
1354 &mut store,
1355 "cookie8=value8; HttpOnly",
1356 "http://[::1]/foo/bar",
1357 Some(test_utils::in_days(1)),
1358 None,
1359 ));
1360 store.save_json(&mut output).unwrap();
1361 not_has_str!("cookie0=value0", output);
1362 has_str!("cookie1=value1", output);
1363 has_str!("cookie2=value2", output);
1364 has_str!("cookie3=value3", output);
1365 has_str!("cookie4=value4", output);
1366 has_str!("cookie5=value5", output);
1367 has_str!("cookie6=value6", output);
1368 has_str!("cookie7=value7; Secure", output);
1369 has_str!("cookie8=value8; HttpOnly", output);
1370 output.clear();
1371 }
1372
1373 #[test]
1374 fn serialize_json() {
1375 let mut output = vec![];
1376 let mut store = CookieStore::default();
1377 serde_json::to_writer(&mut output, &store).unwrap();
1378 assert_eq!("[]", std::str::from_utf8(&output[..]).unwrap());
1379 output.clear();
1380
1381 inserted!(add_cookie(
1383 &mut store,
1384 "cookie0=value0",
1385 "http://example.com/foo/bar",
1386 None,
1387 None,
1388 ));
1389 serde_json::to_writer(&mut output, &store).unwrap();
1390 assert_eq!("[]", std::str::from_utf8(&output[..]).unwrap());
1391 output.clear();
1392
1393 inserted!(add_cookie(
1395 &mut store,
1396 "cookie1=value1",
1397 "http://example.com/foo/bar",
1398 None,
1399 Some(10),
1400 ));
1401 serde_json::to_writer(&mut output, &store).unwrap();
1402 not_has_str!("cookie0=value0", output);
1403 has_str!("cookie1=value1", output);
1404 output.clear();
1405
1406 inserted!(add_cookie(
1408 &mut store,
1409 "cookie2=value2",
1410 "http://example.com/foo/bar",
1411 Some(test_utils::in_days(1)),
1412 None,
1413 ));
1414 serde_json::to_writer(&mut output, &store).unwrap();
1415 not_has_str!("cookie0=value0", output);
1416 has_str!("cookie1=value1", output);
1417 has_str!("cookie2=value2", output);
1418 output.clear();
1419
1420 inserted!(add_cookie(
1421 &mut store,
1422 "cookie3=value3; Domain=example.com",
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 "cookie4=value4; Path=/foo/",
1430 "http://foo.example.com/foo/bar",
1431 Some(test_utils::in_days(1)),
1432 None,
1433 ));
1434 inserted!(add_cookie(
1435 &mut store,
1436 "cookie5=value5",
1437 "http://127.0.0.1/foo/bar",
1438 Some(test_utils::in_days(1)),
1439 None,
1440 ));
1441 inserted!(add_cookie(
1442 &mut store,
1443 "cookie6=value6",
1444 "http://[::1]/foo/bar",
1445 Some(test_utils::in_days(1)),
1446 None,
1447 ));
1448 inserted!(add_cookie(
1449 &mut store,
1450 "cookie7=value7; Secure",
1451 "https://[::1]/foo/bar",
1452 Some(test_utils::in_days(1)),
1453 None,
1454 ));
1455 inserted!(add_cookie(
1456 &mut store,
1457 "cookie8=value8; HttpOnly",
1458 "http://[::1]/foo/bar",
1459 Some(test_utils::in_days(1)),
1460 None,
1461 ));
1462 serde_json::to_writer(&mut output, &store).unwrap();
1463 not_has_str!("cookie0=value0", output);
1464 has_str!("cookie1=value1", output);
1465 has_str!("cookie2=value2", output);
1466 has_str!("cookie3=value3", output);
1467 has_str!("cookie4=value4", output);
1468 has_str!("cookie5=value5", output);
1469 has_str!("cookie6=value6", output);
1470 has_str!("cookie7=value7; Secure", output);
1471 has_str!("cookie8=value8; HttpOnly", output);
1472 output.clear();
1473 }
1474
1475 #[test]
1476 fn load_json() {
1477 let mut store = CookieStore::default();
1478 inserted!(add_cookie(
1480 &mut store,
1481 "cookie0=value0",
1482 "http://example.com/foo/bar",
1483 None,
1484 None,
1485 ));
1486 inserted!(add_cookie(
1488 &mut store,
1489 "cookie1=value1",
1490 "http://example.com/foo/bar",
1491 None,
1492 Some(10),
1493 ));
1494 inserted!(add_cookie(
1496 &mut store,
1497 "cookie2=value2",
1498 "http://example.com/foo/bar",
1499 Some(test_utils::in_days(1)),
1500 None,
1501 ));
1502 inserted!(add_cookie(
1503 &mut store,
1504 "cookie3=value3; Domain=example.com",
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 "cookie4=value4; Path=/foo/",
1512 "http://foo.example.com/foo/bar",
1513 Some(test_utils::in_days(1)),
1514 None,
1515 ));
1516 inserted!(add_cookie(
1517 &mut store,
1518 "cookie5=value5",
1519 "http://127.0.0.1/foo/bar",
1520 Some(test_utils::in_days(1)),
1521 None,
1522 ));
1523 inserted!(add_cookie(
1524 &mut store,
1525 "cookie6=value6",
1526 "http://[::1]/foo/bar",
1527 Some(test_utils::in_days(1)),
1528 None,
1529 ));
1530 inserted!(add_cookie(
1531 &mut store,
1532 "cookie7=value7; Secure",
1533 "http://example.com/foo/bar",
1534 Some(test_utils::in_days(1)),
1535 None,
1536 ));
1537 inserted!(add_cookie(
1538 &mut store,
1539 "cookie8=value8; HttpOnly",
1540 "http://example.com/foo/bar",
1541 Some(test_utils::in_days(1)),
1542 None,
1543 ));
1544 let mut output = vec![];
1545 store.save_json(&mut output).unwrap();
1546 not_has_str!("cookie0=value0", output);
1547 has_str!("cookie1=value1", output);
1548 has_str!("cookie2=value2", output);
1549 has_str!("cookie3=value3", output);
1550 has_str!("cookie4=value4", output);
1551 has_str!("cookie5=value5", output);
1552 has_str!("cookie6=value6", output);
1553 has_str!("cookie7=value7; Secure", output);
1554 has_str!("cookie8=value8; HttpOnly", output);
1555 let store = CookieStore::load_json(&output[..]).unwrap();
1556 assert!(store.get("example.com", "/foo", "cookie0").is_none());
1557 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
1558 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value2");
1559 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value3");
1560 assert!(
1561 store
1562 .get("foo.example.com", "/foo/", "cookie4")
1563 .unwrap()
1564 .value()
1565 == "value4"
1566 );
1567 assert!(store.get("127.0.0.1", "/foo", "cookie5").unwrap().value() == "value5");
1568 assert!(store.get("[::1]", "/foo", "cookie6").unwrap().value() == "value6");
1569 assert!(store.get("example.com", "/foo", "cookie7").unwrap().value() == "value7");
1570 assert!(store.get("example.com", "/foo", "cookie8").unwrap().value() == "value8");
1571
1572 output.clear();
1573 let store = make_match_store();
1574 store.save_json(&mut output).unwrap();
1575 let store = CookieStore::load_json(&output[..]).unwrap();
1576 check_matches!(&store);
1577 }
1578
1579 #[test]
1580 fn deserialize_json() {
1581 let mut store = CookieStore::default();
1582 inserted!(add_cookie(
1584 &mut store,
1585 "cookie0=value0",
1586 "http://example.com/foo/bar",
1587 None,
1588 None,
1589 ));
1590 inserted!(add_cookie(
1592 &mut store,
1593 "cookie1=value1",
1594 "http://example.com/foo/bar",
1595 None,
1596 Some(10),
1597 ));
1598 inserted!(add_cookie(
1600 &mut store,
1601 "cookie2=value2",
1602 "http://example.com/foo/bar",
1603 Some(test_utils::in_days(1)),
1604 None,
1605 ));
1606 inserted!(add_cookie(
1607 &mut store,
1608 "cookie3=value3; Domain=example.com",
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 "cookie4=value4; Path=/foo/",
1616 "http://foo.example.com/foo/bar",
1617 Some(test_utils::in_days(1)),
1618 None,
1619 ));
1620 inserted!(add_cookie(
1621 &mut store,
1622 "cookie5=value5",
1623 "http://127.0.0.1/foo/bar",
1624 Some(test_utils::in_days(1)),
1625 None,
1626 ));
1627 inserted!(add_cookie(
1628 &mut store,
1629 "cookie6=value6",
1630 "http://[::1]/foo/bar",
1631 Some(test_utils::in_days(1)),
1632 None,
1633 ));
1634 inserted!(add_cookie(
1635 &mut store,
1636 "cookie7=value7; Secure",
1637 "http://example.com/foo/bar",
1638 Some(test_utils::in_days(1)),
1639 None,
1640 ));
1641 inserted!(add_cookie(
1642 &mut store,
1643 "cookie8=value8; HttpOnly",
1644 "http://example.com/foo/bar",
1645 Some(test_utils::in_days(1)),
1646 None,
1647 ));
1648 let mut output = vec![];
1649 serde_json::to_writer(&mut output, &store).unwrap();
1650 not_has_str!("cookie0=value0", output);
1651 has_str!("cookie1=value1", output);
1652 has_str!("cookie2=value2", output);
1653 has_str!("cookie3=value3", output);
1654 has_str!("cookie4=value4", output);
1655 has_str!("cookie5=value5", output);
1656 has_str!("cookie6=value6", output);
1657 has_str!("cookie7=value7; Secure", output);
1658 has_str!("cookie8=value8; HttpOnly", output);
1659 let store: CookieStore = serde_json::from_reader(&output[..]).unwrap();
1660 assert!(store.get("example.com", "/foo", "cookie0").is_none());
1661 assert!(store.get("example.com", "/foo", "cookie1").unwrap().value() == "value1");
1662 assert!(store.get("example.com", "/foo", "cookie2").unwrap().value() == "value2");
1663 assert!(store.get("example.com", "/foo", "cookie3").unwrap().value() == "value3");
1664 assert!(
1665 store
1666 .get("foo.example.com", "/foo/", "cookie4")
1667 .unwrap()
1668 .value()
1669 == "value4"
1670 );
1671 assert!(store.get("127.0.0.1", "/foo", "cookie5").unwrap().value() == "value5");
1672 assert!(store.get("[::1]", "/foo", "cookie6").unwrap().value() == "value6");
1673 assert!(store.get("example.com", "/foo", "cookie7").unwrap().value() == "value7");
1674 assert!(store.get("example.com", "/foo", "cookie8").unwrap().value() == "value8");
1675
1676 output.clear();
1677 let store = make_match_store();
1678 serde_json::to_writer(&mut output, &store).unwrap();
1679 let store: CookieStore = serde_json::from_reader(&output[..]).unwrap();
1680 check_matches!(&store);
1681 }
1682
1683 #[test]
1684 fn expiry_json() {
1685 let mut store = make_match_store();
1686 let request_url = test_utils::url("http://foo.example.com");
1687 let expired_cookie = Cookie::parse("cookie1=value1; Max-Age=-1", &request_url).unwrap();
1688 expired_err!(store.insert(expired_cookie, &request_url));
1689 check_matches!(&store);
1690 match store.get_mut("example.com", "/", "cookie6") {
1691 Some(cookie) => cookie.expire(),
1692 None => unreachable!(),
1693 }
1694 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1695 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1696 values_are!(store, "http://example.org/bus/bar", vec![]);
1697 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1698 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1699 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1700 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1701 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1702 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1703 values_are!(store, "http://example.com/foo/bar/bus", vec!["1", "5"]);
1704 match store.get_any("example.com", "/", "cookie6") {
1705 Some(cookie) => assert!(cookie.is_expired()),
1706 None => unreachable!(),
1707 }
1708 let request_url = test_utils::url("http://example.com/foo/");
1711 let expired_cookie = Cookie::parse("cookie5=value5; Max-Age=-1", &request_url).unwrap();
1712 expired_existing!(store.insert(expired_cookie, &request_url));
1713 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1714 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1715 values_are!(store, "http://example.org/bus/bar", vec![]);
1716 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1717 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1718 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1719 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1720 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1721 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1722 values_are!(store, "http://example.com/foo/bar/bus", vec!["1"]);
1723 match store.get_any("example.com", "/foo", "cookie5") {
1724 Some(cookie) => assert!(cookie.is_expired()),
1725 None => unreachable!(),
1726 }
1727 let mut output = vec![];
1729 store.save_json(&mut output).unwrap();
1730 store = CookieStore::load_json(&output[..]).unwrap();
1731 values_are!(store, "http://unknowndomain.org/foo/bar", vec![]);
1732 values_are!(store, "http://example.org/foo/bar", vec!["8"]);
1733 values_are!(store, "http://example.org/bus/bar", vec![]);
1734 values_are!(store, "http://bar.example.org/foo/bar", vec!["9"]);
1735 values_are!(store, "http://bar.example.org/bus/bar", vec![]);
1736 values_are!(store, "https://example.com/sec/foo", vec!["4", "3", "2"]);
1737 values_are!(store, "http://example.com/sec/foo", vec!["3"]);
1738 values_are!(store, "ftp://example.com/sec/foo", vec![]);
1739 values_are!(store, "http://bar.example.com/foo/bar/bus", vec!["7"]);
1740 values_are!(store, "http://example.com/foo/bar/bus", vec!["1"]);
1741 assert!(store.get_any("example.com", "/", "cookie6").is_none());
1742 assert!(store.get_any("example.com", "/foo", "cookie5").is_none());
1743 }
1744
1745 #[test]
1746 fn non_persistent_json() {
1747 let mut store = make_match_store();
1748 check_matches!(&store);
1749 let request_url = test_utils::url("http://example.com/tmp/");
1750 let non_persistent = Cookie::parse("cookie10=value10", &request_url).unwrap();
1751 inserted!(store.insert(non_persistent, &request_url));
1752 match store.get("example.com", "/tmp", "cookie10") {
1753 None => unreachable!(),
1754 Some(cookie) => assert_eq!("value10", cookie.value()),
1755 }
1756 let mut output = vec![];
1758 store.save_json(&mut output).unwrap();
1759 store = CookieStore::load_json(&output[..]).unwrap();
1760 check_matches!(&store);
1761 assert!(store.get("example.com", "/tmp", "cookie10").is_none());
1762 assert!(store.get_any("example.com", "/tmp", "cookie10").is_none());
1763 }
1764 }
1765}