cookie/
jar.rs

1use std::collections::HashSet;
2
3#[cfg(feature = "signed")] use crate::secure::SignedJar;
4#[cfg(feature = "private")] use crate::secure::PrivateJar;
5#[cfg(any(feature = "signed", feature = "private"))] use crate::secure::Key;
6
7use crate::delta::DeltaCookie;
8use crate::Cookie;
9
10/// A collection of cookies that tracks its modifications.
11///
12/// A `CookieJar` provides storage for any number of cookies. Any changes made
13/// to the jar are tracked; the changes can be retrieved via the
14/// [delta](#method.delta) method which returns an iterator over the changes.
15///
16/// # Usage
17///
18/// A jar's life begins via [new](#method.new) and calls to
19/// [`add_original`](#method.add_original):
20///
21/// ```rust
22/// use cookie::{Cookie, CookieJar};
23///
24/// let mut jar = CookieJar::new();
25/// jar.add_original(Cookie::new("name", "value"));
26/// jar.add_original(Cookie::new("second", "another"));
27/// ```
28///
29/// Cookies can be added via [add](#method.add) and removed via
30/// [remove](#method.remove). Finally, cookies can be looked up via
31/// [get](#method.get):
32///
33/// ```rust
34/// # use cookie::{Cookie, CookieJar};
35/// let mut jar = CookieJar::new();
36/// jar.add(Cookie::new("a", "one"));
37/// jar.add(Cookie::new("b", "two"));
38///
39/// assert_eq!(jar.get("a").map(|c| c.value()), Some("one"));
40/// assert_eq!(jar.get("b").map(|c| c.value()), Some("two"));
41///
42/// jar.remove(Cookie::named("b"));
43/// assert!(jar.get("b").is_none());
44/// ```
45///
46/// # Deltas
47///
48/// A jar keeps track of any modifications made to it over time. The
49/// modifications are recorded as cookies. The modifications can be retrieved
50/// via [delta](#method.delta). Any new `Cookie` added to a jar via `add`
51/// results in the same `Cookie` appearing in the `delta`; cookies added via
52/// `add_original` do not count towards the delta. Any _original_ cookie that is
53/// removed from a jar results in a "removal" cookie appearing in the delta. A
54/// "removal" cookie is a cookie that a server sends so that the cookie is
55/// removed from the client's machine.
56///
57/// Deltas are typically used to create `Set-Cookie` headers corresponding to
58/// the changes made to a cookie jar over a period of time.
59///
60/// ```rust
61/// # use cookie::{Cookie, CookieJar};
62/// let mut jar = CookieJar::new();
63///
64/// // original cookies don't affect the delta
65/// jar.add_original(Cookie::new("original", "value"));
66/// assert_eq!(jar.delta().count(), 0);
67///
68/// // new cookies result in an equivalent `Cookie` in the delta
69/// jar.add(Cookie::new("a", "one"));
70/// jar.add(Cookie::new("b", "two"));
71/// assert_eq!(jar.delta().count(), 2);
72///
73/// // removing an original cookie adds a "removal" cookie to the delta
74/// jar.remove(Cookie::named("original"));
75/// assert_eq!(jar.delta().count(), 3);
76///
77/// // removing a new cookie that was added removes that `Cookie` from the delta
78/// jar.remove(Cookie::named("a"));
79/// assert_eq!(jar.delta().count(), 2);
80/// ```
81#[derive(Default, Debug, Clone)]
82pub struct CookieJar {
83    original_cookies: HashSet<DeltaCookie>,
84    delta_cookies: HashSet<DeltaCookie>,
85}
86
87impl CookieJar {
88    /// Creates an empty cookie jar.
89    ///
90    /// # Example
91    ///
92    /// ```rust
93    /// use cookie::CookieJar;
94    ///
95    /// let jar = CookieJar::new();
96    /// assert_eq!(jar.iter().count(), 0);
97    /// ```
98    pub fn new() -> CookieJar {
99        CookieJar::default()
100    }
101
102    /// Returns a reference to the `Cookie` inside this jar with the name
103    /// `name`. If no such cookie exists, returns `None`.
104    ///
105    /// # Example
106    ///
107    /// ```rust
108    /// use cookie::{CookieJar, Cookie};
109    ///
110    /// let mut jar = CookieJar::new();
111    /// assert!(jar.get("name").is_none());
112    ///
113    /// jar.add(Cookie::new("name", "value"));
114    /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
115    /// ```
116    pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
117        self.delta_cookies
118            .get(name)
119            .or_else(|| self.original_cookies.get(name))
120            .and_then(|c| if c.removed { None } else { Some(&c.cookie) })
121    }
122
123    /// Adds an "original" `cookie` to this jar. If an original cookie with the
124    /// same name already exists, it is replaced with `cookie`. Cookies added
125    /// with `add` take precedence and are not replaced by this method.
126    ///
127    /// Adding an original cookie does not affect the [delta](#method.delta)
128    /// computation. This method is intended to be used to seed the cookie jar
129    /// with cookies received from a client's HTTP message.
130    ///
131    /// For accurate `delta` computations, this method should not be called
132    /// after calling `remove`.
133    ///
134    /// # Example
135    ///
136    /// ```rust
137    /// use cookie::{CookieJar, Cookie};
138    ///
139    /// let mut jar = CookieJar::new();
140    /// jar.add_original(Cookie::new("name", "value"));
141    /// jar.add_original(Cookie::new("second", "two"));
142    ///
143    /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
144    /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
145    /// assert_eq!(jar.iter().count(), 2);
146    /// assert_eq!(jar.delta().count(), 0);
147    /// ```
148    pub fn add_original(&mut self, cookie: Cookie<'static>) {
149        self.original_cookies.replace(DeltaCookie::added(cookie));
150    }
151
152    /// Adds `cookie` to this jar. If a cookie with the same name already
153    /// exists, it is replaced with `cookie`.
154    ///
155    /// # Example
156    ///
157    /// ```rust
158    /// use cookie::{CookieJar, Cookie};
159    ///
160    /// let mut jar = CookieJar::new();
161    /// jar.add(Cookie::new("name", "value"));
162    /// jar.add(Cookie::new("second", "two"));
163    ///
164    /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
165    /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
166    /// assert_eq!(jar.iter().count(), 2);
167    /// assert_eq!(jar.delta().count(), 2);
168    /// ```
169    pub fn add(&mut self, cookie: Cookie<'static>) {
170        self.delta_cookies.replace(DeltaCookie::added(cookie));
171    }
172
173    /// Removes `cookie` from this jar. If an _original_ cookie with the same
174    /// name as `cookie` is present in the jar, a _removal_ cookie will be
175    /// present in the `delta` computation. To properly generate the removal
176    /// cookie, `cookie` must contain the same `path` and `domain` as the cookie
177    /// that was initially set.
178    ///
179    /// A "removal" cookie is a cookie that has the same name as the original
180    /// cookie but has an empty value, a max-age of 0, and an expiration date
181    /// far in the past. See also [`Cookie::make_removal()`].
182    ///
183    /// # Example
184    ///
185    /// Removing an _original_ cookie results in a _removal_ cookie:
186    ///
187    /// ```rust
188    /// # extern crate cookie;
189    /// use cookie::{CookieJar, Cookie};
190    /// use cookie::time::Duration;
191    ///
192    /// # fn main() {
193    /// let mut jar = CookieJar::new();
194    ///
195    /// // Assume this cookie originally had a path of "/" and domain of "a.b".
196    /// jar.add_original(Cookie::new("name", "value"));
197    ///
198    /// // If the path and domain were set, they must be provided to `remove`.
199    /// jar.remove(Cookie::build("name", "").path("/").domain("a.b").finish());
200    ///
201    /// // The delta will contain the removal cookie.
202    /// let delta: Vec<_> = jar.delta().collect();
203    /// assert_eq!(delta.len(), 1);
204    /// assert_eq!(delta[0].name(), "name");
205    /// assert_eq!(delta[0].max_age(), Some(Duration::seconds(0)));
206    /// # }
207    /// ```
208    ///
209    /// Removing a new cookie does not result in a _removal_ cookie unless
210    /// there's an original cookie with the same name:
211    ///
212    /// ```rust
213    /// use cookie::{CookieJar, Cookie};
214    ///
215    /// let mut jar = CookieJar::new();
216    /// jar.add(Cookie::new("name", "value"));
217    /// assert_eq!(jar.delta().count(), 1);
218    ///
219    /// jar.remove(Cookie::named("name"));
220    /// assert_eq!(jar.delta().count(), 0);
221    ///
222    /// jar.add_original(Cookie::new("name", "value"));
223    /// jar.add(Cookie::new("name", "value"));
224    /// assert_eq!(jar.delta().count(), 1);
225    ///
226    /// jar.remove(Cookie::named("name"));
227    /// assert_eq!(jar.delta().count(), 1);
228    /// ```
229    pub fn remove(&mut self, mut cookie: Cookie<'static>) {
230        if self.original_cookies.contains(cookie.name()) {
231            cookie.make_removal();
232            self.delta_cookies.replace(DeltaCookie::removed(cookie));
233        } else {
234            self.delta_cookies.remove(cookie.name());
235        }
236    }
237
238    /// Removes `cookie` from this jar completely. This method differs from
239    /// `remove` in that no delta cookie is created under any condition. Neither
240    /// the `delta` nor `iter` methods will return a cookie that is removed
241    /// using this method.
242    ///
243    /// # Example
244    ///
245    /// Removing an _original_ cookie; no _removal_ cookie is generated:
246    ///
247    /// ```rust
248    /// # extern crate cookie;
249    /// use cookie::{CookieJar, Cookie};
250    /// use cookie::time::Duration;
251    ///
252    /// # fn main() {
253    /// let mut jar = CookieJar::new();
254    ///
255    /// // Add an original cookie and a new cookie.
256    /// jar.add_original(Cookie::new("name", "value"));
257    /// jar.add(Cookie::new("key", "value"));
258    /// assert_eq!(jar.delta().count(), 1);
259    /// assert_eq!(jar.iter().count(), 2);
260    ///
261    /// // Now force remove the original cookie.
262    /// jar.force_remove(&Cookie::named("name"));
263    /// assert_eq!(jar.delta().count(), 1);
264    /// assert_eq!(jar.iter().count(), 1);
265    ///
266    /// // Now force remove the new cookie.
267    /// jar.force_remove(&Cookie::named("key"));
268    /// assert_eq!(jar.delta().count(), 0);
269    /// assert_eq!(jar.iter().count(), 0);
270    /// # }
271    /// ```
272    pub fn force_remove<'a>(&mut self, cookie: &Cookie<'a>) {
273        self.original_cookies.remove(cookie.name());
274        self.delta_cookies.remove(cookie.name());
275    }
276
277    /// Removes all delta cookies, i.e. all cookies not added via
278    /// [`CookieJar::add_original()`], from this `CookieJar`. This undoes any
279    /// changes from [`CookieJar::add()`] and [`CookieJar::remove()`]
280    /// operations.
281    ///
282    /// # Example
283    ///
284    /// ```rust
285    /// use cookie::{CookieJar, Cookie};
286    ///
287    /// let mut jar = CookieJar::new();
288    ///
289    /// // Only original cookies will remain after calling `reset_delta`.
290    /// jar.add_original(Cookie::new("name", "value"));
291    /// jar.add_original(Cookie::new("language", "Rust"));
292    ///
293    /// // These operations, represented by delta cookies, will be reset.
294    /// jar.add(Cookie::new("language", "C++"));
295    /// jar.remove(Cookie::named("name"));
296    ///
297    /// // All is normal.
298    /// assert_eq!(jar.get("name"), None);
299    /// assert_eq!(jar.get("language").map(Cookie::value), Some("C++"));
300    /// assert_eq!(jar.iter().count(), 1);
301    /// assert_eq!(jar.delta().count(), 2);
302    ///
303    /// // Resetting undoes delta operations.
304    /// jar.reset_delta();
305    /// assert_eq!(jar.get("name").map(Cookie::value), Some("value"));
306    /// assert_eq!(jar.get("language").map(Cookie::value), Some("Rust"));
307    /// assert_eq!(jar.iter().count(), 2);
308    /// assert_eq!(jar.delta().count(), 0);
309    /// ```
310    pub fn reset_delta(&mut self) {
311        self.delta_cookies = HashSet::new();
312    }
313
314    /// Returns an iterator over cookies that represent the changes to this jar
315    /// over time. These cookies can be rendered directly as `Set-Cookie` header
316    /// values to affect the changes made to this jar on the client.
317    ///
318    /// # Example
319    ///
320    /// ```rust
321    /// use cookie::{CookieJar, Cookie};
322    ///
323    /// let mut jar = CookieJar::new();
324    /// jar.add_original(Cookie::new("name", "value"));
325    /// jar.add_original(Cookie::new("second", "two"));
326    ///
327    /// // Add new cookies.
328    /// jar.add(Cookie::new("new", "third"));
329    /// jar.add(Cookie::new("another", "fourth"));
330    /// jar.add(Cookie::new("yac", "fifth"));
331    ///
332    /// // Remove some cookies.
333    /// jar.remove(Cookie::named("name"));
334    /// jar.remove(Cookie::named("another"));
335    ///
336    /// // Delta contains two new cookies ("new", "yac") and a removal ("name").
337    /// assert_eq!(jar.delta().count(), 3);
338    /// ```
339    pub fn delta(&self) -> Delta {
340        Delta { iter: self.delta_cookies.iter() }
341    }
342
343    /// Returns an iterator over all of the cookies present in this jar.
344    ///
345    /// # Example
346    ///
347    /// ```rust
348    /// use cookie::{CookieJar, Cookie};
349    ///
350    /// let mut jar = CookieJar::new();
351    ///
352    /// jar.add_original(Cookie::new("name", "value"));
353    /// jar.add_original(Cookie::new("second", "two"));
354    ///
355    /// jar.add(Cookie::new("new", "third"));
356    /// jar.add(Cookie::new("another", "fourth"));
357    /// jar.add(Cookie::new("yac", "fifth"));
358    ///
359    /// jar.remove(Cookie::named("name"));
360    /// jar.remove(Cookie::named("another"));
361    ///
362    /// // There are three cookies in the jar: "second", "new", and "yac".
363    /// # assert_eq!(jar.iter().count(), 3);
364    /// for cookie in jar.iter() {
365    ///     match cookie.name() {
366    ///         "second" => assert_eq!(cookie.value(), "two"),
367    ///         "new" => assert_eq!(cookie.value(), "third"),
368    ///         "yac" => assert_eq!(cookie.value(), "fifth"),
369    ///         _ => unreachable!("there are only three cookies in the jar")
370    ///     }
371    /// }
372    /// ```
373    pub fn iter(&self) -> Iter {
374        Iter {
375            delta_cookies: self.delta_cookies.iter()
376                .chain(self.original_cookies.difference(&self.delta_cookies)),
377        }
378    }
379
380    /// Returns a read-only `PrivateJar` with `self` as its parent jar using the
381    /// key `key` to verify/decrypt cookies retrieved from the child jar. Any
382    /// retrievals from the child jar will be made from the parent jar.
383    ///
384    /// # Example
385    ///
386    /// ```rust
387    /// use cookie::{Cookie, CookieJar, Key};
388    ///
389    /// // Generate a secure key.
390    /// let key = Key::generate();
391    ///
392    /// // Add a private (signed + encrypted) cookie.
393    /// let mut jar = CookieJar::new();
394    /// jar.private_mut(&key).add(Cookie::new("private", "text"));
395    ///
396    /// // The cookie's contents are encrypted.
397    /// assert_ne!(jar.get("private").unwrap().value(), "text");
398    ///
399    /// // They can be decrypted and verified through the child jar.
400    /// assert_eq!(jar.private(&key).get("private").unwrap().value(), "text");
401    ///
402    /// // A tampered with cookie does not validate but still exists.
403    /// let mut cookie = jar.get("private").unwrap().clone();
404    /// jar.add(Cookie::new("private", cookie.value().to_string() + "!"));
405    /// assert!(jar.private(&key).get("private").is_none());
406    /// assert!(jar.get("private").is_some());
407    /// ```
408    #[cfg(feature = "private")]
409    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
410    pub fn private<'a>(&'a self, key: &Key) -> PrivateJar<&'a Self> {
411        PrivateJar::new(self, key)
412    }
413
414    /// Returns a read/write `PrivateJar` with `self` as its parent jar using
415    /// the key `key` to sign/encrypt and verify/decrypt cookies added/retrieved
416    /// from the child jar.
417    ///
418    /// Any modifications to the child jar will be reflected on the parent jar,
419    /// and any retrievals from the child jar will be made from the parent jar.
420    ///
421    /// # Example
422    ///
423    /// ```rust
424    /// use cookie::{Cookie, CookieJar, Key};
425    ///
426    /// // Generate a secure key.
427    /// let key = Key::generate();
428    ///
429    /// // Add a private (signed + encrypted) cookie.
430    /// let mut jar = CookieJar::new();
431    /// jar.private_mut(&key).add(Cookie::new("private", "text"));
432    ///
433    /// // Remove a cookie using the child jar.
434    /// jar.private_mut(&key).remove(Cookie::named("private"));
435    /// ```
436    #[cfg(feature = "private")]
437    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
438    pub fn private_mut<'a>(&'a mut self, key: &Key) -> PrivateJar<&'a mut Self> {
439        PrivateJar::new(self, key)
440    }
441
442    /// Returns a read-only `SignedJar` with `self` as its parent jar using the
443    /// key `key` to verify cookies retrieved from the child jar. Any retrievals
444    /// from the child jar will be made from the parent jar.
445    ///
446    /// # Example
447    ///
448    /// ```rust
449    /// use cookie::{Cookie, CookieJar, Key};
450    ///
451    /// // Generate a secure key.
452    /// let key = Key::generate();
453    ///
454    /// // Add a signed cookie.
455    /// let mut jar = CookieJar::new();
456    /// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
457    ///
458    /// // The cookie's contents are signed but still in plaintext.
459    /// assert_ne!(jar.get("signed").unwrap().value(), "text");
460    /// assert!(jar.get("signed").unwrap().value().contains("text"));
461    ///
462    /// // They can be verified through the child jar.
463    /// assert_eq!(jar.signed(&key).get("signed").unwrap().value(), "text");
464    ///
465    /// // A tampered with cookie does not validate but still exists.
466    /// let mut cookie = jar.get("signed").unwrap().clone();
467    /// jar.add(Cookie::new("signed", cookie.value().to_string() + "!"));
468    /// assert!(jar.signed(&key).get("signed").is_none());
469    /// assert!(jar.get("signed").is_some());
470    /// ```
471    #[cfg(feature = "signed")]
472    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
473    pub fn signed<'a>(&'a self, key: &Key) -> SignedJar<&'a Self> {
474        SignedJar::new(self, key)
475    }
476
477    /// Returns a read/write `SignedJar` with `self` as its parent jar using the
478    /// key `key` to sign/verify cookies added/retrieved from the child jar.
479    ///
480    /// Any modifications to the child jar will be reflected on the parent jar,
481    /// and any retrievals from the child jar will be made from the parent jar.
482    ///
483    /// # Example
484    ///
485    /// ```rust
486    /// use cookie::{Cookie, CookieJar, Key};
487    ///
488    /// // Generate a secure key.
489    /// let key = Key::generate();
490    ///
491    /// // Add a signed cookie.
492    /// let mut jar = CookieJar::new();
493    /// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
494    ///
495    /// // Remove a cookie.
496    /// jar.signed_mut(&key).remove(Cookie::named("signed"));
497    /// ```
498    #[cfg(feature = "signed")]
499    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
500    pub fn signed_mut<'a>(&'a mut self, key: &Key) -> SignedJar<&'a mut Self> {
501        SignedJar::new(self, key)
502    }
503}
504
505use std::collections::hash_set::Iter as HashSetIter;
506
507/// Iterator over the changes to a cookie jar.
508pub struct Delta<'a> {
509    iter: HashSetIter<'a, DeltaCookie>,
510}
511
512impl<'a> Iterator for Delta<'a> {
513    type Item = &'a Cookie<'static>;
514
515    fn next(&mut self) -> Option<&'a Cookie<'static>> {
516        self.iter.next().map(|c| &c.cookie)
517    }
518}
519
520use std::collections::hash_set::Difference;
521use std::collections::hash_map::RandomState;
522use std::iter::Chain;
523
524/// Iterator over all of the cookies in a jar.
525pub struct Iter<'a> {
526    delta_cookies: Chain<HashSetIter<'a, DeltaCookie>, Difference<'a, DeltaCookie, RandomState>>,
527}
528
529impl<'a> Iterator for Iter<'a> {
530    type Item = &'a Cookie<'static>;
531
532    fn next(&mut self) -> Option<&'a Cookie<'static>> {
533        for cookie in self.delta_cookies.by_ref() {
534            if !cookie.removed {
535                return Some(&*cookie);
536            }
537        }
538
539        None
540    }
541}
542
543#[cfg(test)]
544mod test {
545    use super::CookieJar;
546    use crate::Cookie;
547
548    #[test]
549    #[allow(deprecated)]
550    fn simple() {
551        let mut c = CookieJar::new();
552
553        c.add(Cookie::new("test", ""));
554        c.add(Cookie::new("test2", ""));
555        c.remove(Cookie::named("test"));
556
557        assert!(c.get("test").is_none());
558        assert!(c.get("test2").is_some());
559
560        c.add(Cookie::new("test3", ""));
561        c.remove(Cookie::named("test2"));
562        c.remove(Cookie::named("test3"));
563
564        assert!(c.get("test").is_none());
565        assert!(c.get("test2").is_none());
566        assert!(c.get("test3").is_none());
567    }
568
569    #[test]
570    fn jar_is_send() {
571        fn is_send<T: Send>(_: T) -> bool {
572            true
573        }
574
575        assert!(is_send(CookieJar::new()))
576    }
577
578    #[test]
579    #[cfg(all(feature = "signed", feature = "private"))]
580    fn iter() {
581        let key = crate::Key::generate();
582        let mut c = CookieJar::new();
583
584        c.add_original(Cookie::new("original", "original"));
585
586        c.add(Cookie::new("test", "test"));
587        c.add(Cookie::new("test2", "test2"));
588        c.add(Cookie::new("test3", "test3"));
589        assert_eq!(c.iter().count(), 4);
590
591        c.signed_mut(&key).add(Cookie::new("signed", "signed"));
592        c.private_mut(&key).add(Cookie::new("encrypted", "encrypted"));
593        assert_eq!(c.iter().count(), 6);
594
595        c.remove(Cookie::named("test"));
596        assert_eq!(c.iter().count(), 5);
597
598        c.remove(Cookie::named("signed"));
599        c.remove(Cookie::named("test2"));
600        assert_eq!(c.iter().count(), 3);
601
602        c.add(Cookie::new("test2", "test2"));
603        assert_eq!(c.iter().count(), 4);
604
605        c.remove(Cookie::named("test2"));
606        assert_eq!(c.iter().count(), 3);
607    }
608
609    #[test]
610    fn delta() {
611        use std::collections::HashMap;
612        use time::Duration;
613
614        let mut c = CookieJar::new();
615
616        c.add_original(Cookie::new("original", "original"));
617        c.add_original(Cookie::new("original1", "original1"));
618
619        c.add(Cookie::new("test", "test"));
620        c.add(Cookie::new("test2", "test2"));
621        c.add(Cookie::new("test3", "test3"));
622        c.add(Cookie::new("test4", "test4"));
623
624        c.remove(Cookie::named("test"));
625        c.remove(Cookie::named("original"));
626
627        assert_eq!(c.delta().count(), 4);
628
629        let names: HashMap<_, _> = c.delta()
630            .map(|c| (c.name(), c.max_age()))
631            .collect();
632
633        assert!(names.get("test2").unwrap().is_none());
634        assert!(names.get("test3").unwrap().is_none());
635        assert!(names.get("test4").unwrap().is_none());
636        assert_eq!(names.get("original").unwrap(), &Some(Duration::seconds(0)));
637    }
638
639    #[test]
640    fn replace_original() {
641        let mut jar = CookieJar::new();
642        jar.add_original(Cookie::new("original_a", "a"));
643        jar.add_original(Cookie::new("original_b", "b"));
644        assert_eq!(jar.get("original_a").unwrap().value(), "a");
645
646        jar.add(Cookie::new("original_a", "av2"));
647        assert_eq!(jar.get("original_a").unwrap().value(), "av2");
648    }
649
650    #[test]
651    fn empty_delta() {
652        let mut jar = CookieJar::new();
653        jar.add(Cookie::new("name", "val"));
654        assert_eq!(jar.delta().count(), 1);
655
656        jar.remove(Cookie::named("name"));
657        assert_eq!(jar.delta().count(), 0);
658
659        jar.add_original(Cookie::new("name", "val"));
660        assert_eq!(jar.delta().count(), 0);
661
662        jar.remove(Cookie::named("name"));
663        assert_eq!(jar.delta().count(), 1);
664
665        jar.add(Cookie::new("name", "val"));
666        assert_eq!(jar.delta().count(), 1);
667
668        jar.remove(Cookie::named("name"));
669        assert_eq!(jar.delta().count(), 1);
670    }
671
672    #[test]
673    fn add_remove_add() {
674        let mut jar = CookieJar::new();
675        jar.add_original(Cookie::new("name", "val"));
676        assert_eq!(jar.delta().count(), 0);
677
678        jar.remove(Cookie::named("name"));
679        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
680        assert_eq!(jar.delta().count(), 1);
681
682        // The cookie's been deleted. Another original doesn't change that.
683        jar.add_original(Cookie::new("name", "val"));
684        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
685        assert_eq!(jar.delta().count(), 1);
686
687        jar.remove(Cookie::named("name"));
688        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
689        assert_eq!(jar.delta().count(), 1);
690
691        jar.add(Cookie::new("name", "val"));
692        assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
693        assert_eq!(jar.delta().count(), 1);
694
695        jar.remove(Cookie::named("name"));
696        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
697        assert_eq!(jar.delta().count(), 1);
698    }
699
700    #[test]
701    fn replace_remove() {
702        let mut jar = CookieJar::new();
703        jar.add_original(Cookie::new("name", "val"));
704        assert_eq!(jar.delta().count(), 0);
705
706        jar.add(Cookie::new("name", "val"));
707        assert_eq!(jar.delta().count(), 1);
708        assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
709
710        jar.remove(Cookie::named("name"));
711        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
712    }
713
714    #[test]
715    fn remove_with_path() {
716        let mut jar = CookieJar::new();
717        jar.add_original(Cookie::build("name", "val").finish());
718        assert_eq!(jar.iter().count(), 1);
719        assert_eq!(jar.delta().count(), 0);
720        assert_eq!(jar.iter().filter(|c| c.path().is_none()).count(), 1);
721
722        jar.remove(Cookie::build("name", "").path("/").finish());
723        assert_eq!(jar.iter().count(), 0);
724        assert_eq!(jar.delta().count(), 1);
725        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
726        assert_eq!(jar.delta().filter(|c| c.path() == Some("/")).count(), 1);
727    }
728}