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}