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