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 /// extern crate time;
190 ///
191 /// use cookie::{CookieJar, Cookie};
192 /// use time::Duration;
193 ///
194 /// # fn main() {
195 /// let mut jar = CookieJar::new();
196 ///
197 /// // Assume this cookie originally had a path of "/" and domain of "a.b".
198 /// jar.add_original(Cookie::new("name", "value"));
199 ///
200 /// // If the path and domain were set, they must be provided to `remove`.
201 /// jar.remove(Cookie::build("name", "").path("/").domain("a.b").finish());
202 ///
203 /// // The delta will contain the removal cookie.
204 /// let delta: Vec<_> = jar.delta().collect();
205 /// assert_eq!(delta.len(), 1);
206 /// assert_eq!(delta[0].name(), "name");
207 /// assert_eq!(delta[0].max_age(), Some(Duration::seconds(0)));
208 /// # }
209 /// ```
210 ///
211 /// Removing a new cookie does not result in a _removal_ cookie unless
212 /// there's an original cookie with the same name:
213 ///
214 /// ```rust
215 /// use cookie::{CookieJar, Cookie};
216 ///
217 /// let mut jar = CookieJar::new();
218 /// jar.add(Cookie::new("name", "value"));
219 /// assert_eq!(jar.delta().count(), 1);
220 ///
221 /// jar.remove(Cookie::named("name"));
222 /// assert_eq!(jar.delta().count(), 0);
223 ///
224 /// jar.add_original(Cookie::new("name", "value"));
225 /// jar.add(Cookie::new("name", "value"));
226 /// assert_eq!(jar.delta().count(), 1);
227 ///
228 /// jar.remove(Cookie::named("name"));
229 /// assert_eq!(jar.delta().count(), 1);
230 /// ```
231 pub fn remove(&mut self, mut cookie: Cookie<'static>) {
232 if self.original_cookies.contains(cookie.name()) {
233 cookie.make_removal();
234 self.delta_cookies.replace(DeltaCookie::removed(cookie));
235 } else {
236 self.delta_cookies.remove(cookie.name());
237 }
238 }
239
240 /// Removes `cookie` from this jar completely. This method differs from
241 /// `remove` in that no delta cookie is created under any condition. Neither
242 /// the `delta` nor `iter` methods will return a cookie that is removed
243 /// using this method.
244 ///
245 /// # Example
246 ///
247 /// Removing an _original_ cookie; no _removal_ cookie is generated:
248 ///
249 /// ```rust
250 /// # extern crate cookie;
251 /// extern crate time;
252 ///
253 /// use cookie::{CookieJar, Cookie};
254 /// use time::Duration;
255 ///
256 /// # fn main() {
257 /// let mut jar = CookieJar::new();
258 ///
259 /// // Add an original cookie and a new cookie.
260 /// jar.add_original(Cookie::new("name", "value"));
261 /// jar.add(Cookie::new("key", "value"));
262 /// assert_eq!(jar.delta().count(), 1);
263 /// assert_eq!(jar.iter().count(), 2);
264 ///
265 /// // Now force remove the original cookie.
266 /// jar.force_remove(&Cookie::named("name"));
267 /// assert_eq!(jar.delta().count(), 1);
268 /// assert_eq!(jar.iter().count(), 1);
269 ///
270 /// // Now force remove the new cookie.
271 /// jar.force_remove(&Cookie::named("key"));
272 /// assert_eq!(jar.delta().count(), 0);
273 /// assert_eq!(jar.iter().count(), 0);
274 /// # }
275 /// ```
276 pub fn force_remove<'a>(&mut self, cookie: &Cookie<'a>) {
277 self.original_cookies.remove(cookie.name());
278 self.delta_cookies.remove(cookie.name());
279 }
280
281 /// Removes all delta cookies, i.e. all cookies not added via
282 /// [`CookieJar::add_original()`], from this `CookieJar`. This undoes any
283 /// changes from [`CookieJar::add()`] and [`CookieJar::remove()`]
284 /// operations.
285 ///
286 /// # Example
287 ///
288 /// ```rust
289 /// use cookie::{CookieJar, Cookie};
290 ///
291 /// let mut jar = CookieJar::new();
292 ///
293 /// // Only original cookies will remain after calling `reset_delta`.
294 /// jar.add_original(Cookie::new("name", "value"));
295 /// jar.add_original(Cookie::new("language", "Rust"));
296 ///
297 /// // These operations, represented by delta cookies, will be reset.
298 /// jar.add(Cookie::new("language", "C++"));
299 /// jar.remove(Cookie::named("name"));
300 ///
301 /// // All is normal.
302 /// assert_eq!(jar.get("name"), None);
303 /// assert_eq!(jar.get("language").map(Cookie::value), Some("C++"));
304 /// assert_eq!(jar.iter().count(), 1);
305 /// assert_eq!(jar.delta().count(), 2);
306 ///
307 /// // Resetting undoes delta operations.
308 /// jar.reset_delta();
309 /// assert_eq!(jar.get("name").map(Cookie::value), Some("value"));
310 /// assert_eq!(jar.get("language").map(Cookie::value), Some("Rust"));
311 /// assert_eq!(jar.iter().count(), 2);
312 /// assert_eq!(jar.delta().count(), 0);
313 /// ```
314 pub fn reset_delta(&mut self) {
315 self.delta_cookies = HashSet::new();
316 }
317
318 /// Returns an iterator over cookies that represent the changes to this jar
319 /// over time. These cookies can be rendered directly as `Set-Cookie` header
320 /// values to affect the changes made to this jar on the client.
321 ///
322 /// # Example
323 ///
324 /// ```rust
325 /// use cookie::{CookieJar, Cookie};
326 ///
327 /// let mut jar = CookieJar::new();
328 /// jar.add_original(Cookie::new("name", "value"));
329 /// jar.add_original(Cookie::new("second", "two"));
330 ///
331 /// // Add new cookies.
332 /// jar.add(Cookie::new("new", "third"));
333 /// jar.add(Cookie::new("another", "fourth"));
334 /// jar.add(Cookie::new("yac", "fifth"));
335 ///
336 /// // Remove some cookies.
337 /// jar.remove(Cookie::named("name"));
338 /// jar.remove(Cookie::named("another"));
339 ///
340 /// // Delta contains two new cookies ("new", "yac") and a removal ("name").
341 /// assert_eq!(jar.delta().count(), 3);
342 /// ```
343 pub fn delta(&self) -> Delta {
344 Delta { iter: self.delta_cookies.iter() }
345 }
346
347 /// Returns an iterator over all of the cookies present in this jar.
348 ///
349 /// # Example
350 ///
351 /// ```rust
352 /// use cookie::{CookieJar, Cookie};
353 ///
354 /// let mut jar = CookieJar::new();
355 ///
356 /// jar.add_original(Cookie::new("name", "value"));
357 /// jar.add_original(Cookie::new("second", "two"));
358 ///
359 /// jar.add(Cookie::new("new", "third"));
360 /// jar.add(Cookie::new("another", "fourth"));
361 /// jar.add(Cookie::new("yac", "fifth"));
362 ///
363 /// jar.remove(Cookie::named("name"));
364 /// jar.remove(Cookie::named("another"));
365 ///
366 /// // There are three cookies in the jar: "second", "new", and "yac".
367 /// # assert_eq!(jar.iter().count(), 3);
368 /// for cookie in jar.iter() {
369 /// match cookie.name() {
370 /// "second" => assert_eq!(cookie.value(), "two"),
371 /// "new" => assert_eq!(cookie.value(), "third"),
372 /// "yac" => assert_eq!(cookie.value(), "fifth"),
373 /// _ => unreachable!("there are only three cookies in the jar")
374 /// }
375 /// }
376 /// ```
377 pub fn iter(&self) -> Iter {
378 Iter {
379 delta_cookies: self.delta_cookies.iter()
380 .chain(self.original_cookies.difference(&self.delta_cookies)),
381 }
382 }
383
384 /// Returns a read-only `PrivateJar` with `self` as its parent jar using the
385 /// key `key` to verify/decrypt cookies retrieved from the child jar. Any
386 /// retrievals from the child jar will be made from the parent jar.
387 ///
388 /// # Example
389 ///
390 /// ```rust
391 /// use cookie::{Cookie, CookieJar, Key};
392 ///
393 /// // Generate a secure key.
394 /// let key = Key::generate();
395 ///
396 /// // Add a private (signed + encrypted) cookie.
397 /// let mut jar = CookieJar::new();
398 /// jar.private_mut(&key).add(Cookie::new("private", "text"));
399 ///
400 /// // The cookie's contents are encrypted.
401 /// assert_ne!(jar.get("private").unwrap().value(), "text");
402 ///
403 /// // They can be decrypted and verified through the child jar.
404 /// assert_eq!(jar.private(&key).get("private").unwrap().value(), "text");
405 ///
406 /// // A tampered with cookie does not validate but still exists.
407 /// let mut cookie = jar.get("private").unwrap().clone();
408 /// jar.add(Cookie::new("private", cookie.value().to_string() + "!"));
409 /// assert!(jar.private(&key).get("private").is_none());
410 /// assert!(jar.get("private").is_some());
411 /// ```
412 #[cfg(feature = "private")]
413 #[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
414 pub fn private<'a>(&'a self, key: &Key) -> PrivateJar<&'a Self> {
415 PrivateJar::new(self, key)
416 }
417
418 /// Returns a read/write `PrivateJar` with `self` as its parent jar using
419 /// the key `key` to sign/encrypt and verify/decrypt cookies added/retrieved
420 /// from the child jar.
421 ///
422 /// Any modifications to the child jar will be reflected on the parent jar,
423 /// and any retrievals from the child jar will be made from the parent jar.
424 ///
425 /// # Example
426 ///
427 /// ```rust
428 /// use cookie::{Cookie, CookieJar, Key};
429 ///
430 /// // Generate a secure key.
431 /// let key = Key::generate();
432 ///
433 /// // Add a private (signed + encrypted) cookie.
434 /// let mut jar = CookieJar::new();
435 /// jar.private_mut(&key).add(Cookie::new("private", "text"));
436 ///
437 /// // Remove a cookie using the child jar.
438 /// jar.private_mut(&key).remove(Cookie::named("private"));
439 /// ```
440 #[cfg(feature = "private")]
441 #[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
442 pub fn private_mut<'a>(&'a mut self, key: &Key) -> PrivateJar<&'a mut Self> {
443 PrivateJar::new(self, key)
444 }
445
446 /// Returns a read-only `SignedJar` with `self` as its parent jar using the
447 /// key `key` to verify cookies retrieved from the child jar. Any retrievals
448 /// from the child jar will be made from the parent jar.
449 ///
450 /// # Example
451 ///
452 /// ```rust
453 /// use cookie::{Cookie, CookieJar, Key};
454 ///
455 /// // Generate a secure key.
456 /// let key = Key::generate();
457 ///
458 /// // Add a signed cookie.
459 /// let mut jar = CookieJar::new();
460 /// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
461 ///
462 /// // The cookie's contents are signed but still in plaintext.
463 /// assert_ne!(jar.get("signed").unwrap().value(), "text");
464 /// assert!(jar.get("signed").unwrap().value().contains("text"));
465 ///
466 /// // They can be verified through the child jar.
467 /// assert_eq!(jar.signed(&key).get("signed").unwrap().value(), "text");
468 ///
469 /// // A tampered with cookie does not validate but still exists.
470 /// let mut cookie = jar.get("signed").unwrap().clone();
471 /// jar.add(Cookie::new("signed", cookie.value().to_string() + "!"));
472 /// assert!(jar.signed(&key).get("signed").is_none());
473 /// assert!(jar.get("signed").is_some());
474 /// ```
475 #[cfg(feature = "signed")]
476 #[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
477 pub fn signed<'a>(&'a self, key: &Key) -> SignedJar<&'a Self> {
478 SignedJar::new(self, key)
479 }
480
481 /// Returns a read/write `SignedJar` with `self` as its parent jar using the
482 /// key `key` to sign/verify cookies added/retrieved from the child jar.
483 ///
484 /// Any modifications to the child jar will be reflected on the parent jar,
485 /// and any retrievals from the child jar will be made from the parent jar.
486 ///
487 /// # Example
488 ///
489 /// ```rust
490 /// use cookie::{Cookie, CookieJar, Key};
491 ///
492 /// // Generate a secure key.
493 /// let key = Key::generate();
494 ///
495 /// // Add a signed cookie.
496 /// let mut jar = CookieJar::new();
497 /// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
498 ///
499 /// // Remove a cookie.
500 /// jar.signed_mut(&key).remove(Cookie::named("signed"));
501 /// ```
502 #[cfg(feature = "signed")]
503 #[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
504 pub fn signed_mut<'a>(&'a mut self, key: &Key) -> SignedJar<&'a mut Self> {
505 SignedJar::new(self, key)
506 }
507}
508
509use std::collections::hash_set::Iter as HashSetIter;
510
511/// Iterator over the changes to a cookie jar.
512pub struct Delta<'a> {
513 iter: HashSetIter<'a, DeltaCookie>,
514}
515
516impl<'a> Iterator for Delta<'a> {
517 type Item = &'a Cookie<'static>;
518
519 fn next(&mut self) -> Option<&'a Cookie<'static>> {
520 self.iter.next().map(|c| &c.cookie)
521 }
522}
523
524use std::collections::hash_set::Difference;
525use std::collections::hash_map::RandomState;
526use std::iter::Chain;
527
528/// Iterator over all of the cookies in a jar.
529pub struct Iter<'a> {
530 delta_cookies: Chain<HashSetIter<'a, DeltaCookie>, Difference<'a, DeltaCookie, RandomState>>,
531}
532
533impl<'a> Iterator for Iter<'a> {
534 type Item = &'a Cookie<'static>;
535
536 fn next(&mut self) -> Option<&'a Cookie<'static>> {
537 for cookie in self.delta_cookies.by_ref() {
538 if !cookie.removed {
539 return Some(&*cookie);
540 }
541 }
542
543 None
544 }
545}
546
547#[cfg(test)]
548mod test {
549 use super::CookieJar;
550 use crate::Cookie;
551
552 #[test]
553 #[allow(deprecated)]
554 fn simple() {
555 let mut c = CookieJar::new();
556
557 c.add(Cookie::new("test", ""));
558 c.add(Cookie::new("test2", ""));
559 c.remove(Cookie::named("test"));
560
561 assert!(c.get("test").is_none());
562 assert!(c.get("test2").is_some());
563
564 c.add(Cookie::new("test3", ""));
565 c.remove(Cookie::named("test2"));
566 c.remove(Cookie::named("test3"));
567
568 assert!(c.get("test").is_none());
569 assert!(c.get("test2").is_none());
570 assert!(c.get("test3").is_none());
571 }
572
573 #[test]
574 fn jar_is_send() {
575 fn is_send<T: Send>(_: T) -> bool {
576 true
577 }
578
579 assert!(is_send(CookieJar::new()))
580 }
581
582 #[test]
583 #[cfg(all(feature = "signed", feature = "private"))]
584 fn iter() {
585 let key = crate::Key::generate();
586 let mut c = CookieJar::new();
587
588 c.add_original(Cookie::new("original", "original"));
589
590 c.add(Cookie::new("test", "test"));
591 c.add(Cookie::new("test2", "test2"));
592 c.add(Cookie::new("test3", "test3"));
593 assert_eq!(c.iter().count(), 4);
594
595 c.signed_mut(&key).add(Cookie::new("signed", "signed"));
596 c.private_mut(&key).add(Cookie::new("encrypted", "encrypted"));
597 assert_eq!(c.iter().count(), 6);
598
599 c.remove(Cookie::named("test"));
600 assert_eq!(c.iter().count(), 5);
601
602 c.remove(Cookie::named("signed"));
603 c.remove(Cookie::named("test2"));
604 assert_eq!(c.iter().count(), 3);
605
606 c.add(Cookie::new("test2", "test2"));
607 assert_eq!(c.iter().count(), 4);
608
609 c.remove(Cookie::named("test2"));
610 assert_eq!(c.iter().count(), 3);
611 }
612
613 #[test]
614 fn delta() {
615 use std::collections::HashMap;
616 use time::Duration;
617
618 let mut c = CookieJar::new();
619
620 c.add_original(Cookie::new("original", "original"));
621 c.add_original(Cookie::new("original1", "original1"));
622
623 c.add(Cookie::new("test", "test"));
624 c.add(Cookie::new("test2", "test2"));
625 c.add(Cookie::new("test3", "test3"));
626 c.add(Cookie::new("test4", "test4"));
627
628 c.remove(Cookie::named("test"));
629 c.remove(Cookie::named("original"));
630
631 assert_eq!(c.delta().count(), 4);
632
633 let names: HashMap<_, _> = c.delta()
634 .map(|c| (c.name(), c.max_age()))
635 .collect();
636
637 assert!(names.get("test2").unwrap().is_none());
638 assert!(names.get("test3").unwrap().is_none());
639 assert!(names.get("test4").unwrap().is_none());
640 assert_eq!(names.get("original").unwrap(), &Some(Duration::seconds(0)));
641 }
642
643 #[test]
644 fn replace_original() {
645 let mut jar = CookieJar::new();
646 jar.add_original(Cookie::new("original_a", "a"));
647 jar.add_original(Cookie::new("original_b", "b"));
648 assert_eq!(jar.get("original_a").unwrap().value(), "a");
649
650 jar.add(Cookie::new("original_a", "av2"));
651 assert_eq!(jar.get("original_a").unwrap().value(), "av2");
652 }
653
654 #[test]
655 fn empty_delta() {
656 let mut jar = CookieJar::new();
657 jar.add(Cookie::new("name", "val"));
658 assert_eq!(jar.delta().count(), 1);
659
660 jar.remove(Cookie::named("name"));
661 assert_eq!(jar.delta().count(), 0);
662
663 jar.add_original(Cookie::new("name", "val"));
664 assert_eq!(jar.delta().count(), 0);
665
666 jar.remove(Cookie::named("name"));
667 assert_eq!(jar.delta().count(), 1);
668
669 jar.add(Cookie::new("name", "val"));
670 assert_eq!(jar.delta().count(), 1);
671
672 jar.remove(Cookie::named("name"));
673 assert_eq!(jar.delta().count(), 1);
674 }
675
676 #[test]
677 fn add_remove_add() {
678 let mut jar = CookieJar::new();
679 jar.add_original(Cookie::new("name", "val"));
680 assert_eq!(jar.delta().count(), 0);
681
682 jar.remove(Cookie::named("name"));
683 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
684 assert_eq!(jar.delta().count(), 1);
685
686 // The cookie's been deleted. Another original doesn't change that.
687 jar.add_original(Cookie::new("name", "val"));
688 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
689 assert_eq!(jar.delta().count(), 1);
690
691 jar.remove(Cookie::named("name"));
692 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
693 assert_eq!(jar.delta().count(), 1);
694
695 jar.add(Cookie::new("name", "val"));
696 assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
697 assert_eq!(jar.delta().count(), 1);
698
699 jar.remove(Cookie::named("name"));
700 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
701 assert_eq!(jar.delta().count(), 1);
702 }
703
704 #[test]
705 fn replace_remove() {
706 let mut jar = CookieJar::new();
707 jar.add_original(Cookie::new("name", "val"));
708 assert_eq!(jar.delta().count(), 0);
709
710 jar.add(Cookie::new("name", "val"));
711 assert_eq!(jar.delta().count(), 1);
712 assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
713
714 jar.remove(Cookie::named("name"));
715 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
716 }
717
718 #[test]
719 fn remove_with_path() {
720 let mut jar = CookieJar::new();
721 jar.add_original(Cookie::build("name", "val").finish());
722 assert_eq!(jar.iter().count(), 1);
723 assert_eq!(jar.delta().count(), 0);
724 assert_eq!(jar.iter().filter(|c| c.path().is_none()).count(), 1);
725
726 jar.remove(Cookie::build("name", "").path("/").finish());
727 assert_eq!(jar.iter().count(), 0);
728 assert_eq!(jar.delta().count(), 1);
729 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
730 assert_eq!(jar.delta().filter(|c| c.path() == Some("/")).count(), 1);
731 }
732}