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::prefix::{Prefix, PrefixedJar};
9use crate::Cookie;
10
11/// A collection of cookies that tracks its modifications.
12///
13/// A `CookieJar` provides storage for any number of cookies. Any changes made
14/// to the jar are tracked; the changes can be retrieved via the
15/// [`delta`](#method.delta) method which returns an iterator over the changes.
16///
17/// # Usage
18///
19/// A jar's life begins via [`CookieJar::new()`] and calls to
20/// [`add_original()`](#method.add_original):
21///
22/// ```rust
23/// use cookie::{Cookie, CookieJar};
24///
25/// let mut jar = CookieJar::new();
26/// jar.add_original(("name", "value"));
27/// jar.add_original(("second", "another"));
28/// jar.add_original(Cookie::build(("third", "again")).path("/"));
29/// ```
30///
31/// Cookies can be added via [`CookieJar::add()`] and removed via
32/// [`CookieJar::remove()`]. Note that any `T: Into<Cookie>` can be passed into
33/// these methods; see [`Cookie::build()`] for a table of implementing types.
34///
35/// Finally, cookies can be retrieved with [`CookieJar::get()`].
36///
37/// ```rust
38/// # use cookie::{Cookie, CookieJar};
39/// let mut jar = CookieJar::new();
40/// jar.add(("a", "one"));
41/// jar.add(("b", "two"));
42///
43/// assert_eq!(jar.get("a").map(|c| c.value()), Some("one"));
44/// assert_eq!(jar.get("b").map(|c| c.value()), Some("two"));
45///
46/// jar.remove("b");
47/// assert!(jar.get("b").is_none());
48/// ```
49///
50/// # Deltas
51///
52/// A jar keeps track of any modifications made to it over time. The
53/// modifications are recorded as cookies. The modifications can be retrieved
54/// via [delta](#method.delta). Any new `Cookie` added to a jar via `add`
55/// results in the same `Cookie` appearing in the `delta`; cookies added via
56/// `add_original` do not count towards the delta. Any _original_ cookie that is
57/// removed from a jar results in a "removal" cookie appearing in the delta. A
58/// "removal" cookie is a cookie that a server sends so that the cookie is
59/// removed from the client's machine.
60///
61/// Deltas are typically used to create `Set-Cookie` headers corresponding to
62/// the changes made to a cookie jar over a period of time.
63///
64/// ```rust
65/// # use cookie::{Cookie, CookieJar};
66/// let mut jar = CookieJar::new();
67///
68/// // original cookies don't affect the delta
69/// jar.add_original(("original", "value"));
70/// assert_eq!(jar.delta().count(), 0);
71///
72/// // new cookies result in an equivalent `Cookie` in the delta
73/// jar.add(("a", "one"));
74/// jar.add(("b", "two"));
75/// assert_eq!(jar.delta().count(), 2);
76///
77/// // removing an original cookie adds a "removal" cookie to the delta
78/// jar.remove("original");
79/// assert_eq!(jar.delta().count(), 3);
80///
81/// // removing a new cookie that was added removes that `Cookie` from the delta
82/// jar.remove("a");
83/// assert_eq!(jar.delta().count(), 2);
84/// ```
85#[derive(Default, Debug, Clone)]
86pub struct CookieJar {
87 original_cookies: HashSet<DeltaCookie>,
88 delta_cookies: HashSet<DeltaCookie>,
89}
90
91impl CookieJar {
92 /// Creates an empty cookie jar.
93 ///
94 /// # Example
95 ///
96 /// ```rust
97 /// use cookie::CookieJar;
98 ///
99 /// let jar = CookieJar::new();
100 /// assert_eq!(jar.iter().count(), 0);
101 /// ```
102 pub fn new() -> CookieJar {
103 CookieJar::default()
104 }
105
106 /// Returns a reference to the `Cookie` inside this jar with the name
107 /// `name`. If no such cookie exists, returns `None`.
108 ///
109 /// # Example
110 ///
111 /// ```rust
112 /// use cookie::{CookieJar, Cookie};
113 ///
114 /// let mut jar = CookieJar::new();
115 /// assert!(jar.get("name").is_none());
116 ///
117 /// jar.add(("name", "value"));
118 /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
119 /// ```
120 pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
121 self.delta_cookies
122 .get(name)
123 .or_else(|| self.original_cookies.get(name))
124 .and_then(|c| if c.removed { None } else { Some(&c.cookie) })
125 }
126
127 /// Adds an "original" `cookie` to this jar. If an original cookie with the
128 /// same name already exists, it is replaced with `cookie`. Cookies added
129 /// with `add` take precedence and are not replaced by this method.
130 ///
131 /// Adding an original cookie does not affect the [delta](#method.delta)
132 /// computation. This method is intended to be used to seed the cookie jar
133 /// with cookies received from a client's HTTP message.
134 ///
135 /// For accurate `delta` computations, this method should not be called
136 /// after calling `remove`.
137 ///
138 /// # Example
139 ///
140 /// ```rust
141 /// use cookie::{CookieJar, Cookie};
142 ///
143 /// let mut jar = CookieJar::new();
144 /// jar.add_original(("name", "value"));
145 /// jar.add_original(("second", "two"));
146 ///
147 /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
148 /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
149 /// assert_eq!(jar.iter().count(), 2);
150 /// assert_eq!(jar.delta().count(), 0);
151 /// ```
152 pub fn add_original<C: Into<Cookie<'static>>>(&mut self, cookie: C) {
153 self.original_cookies.replace(DeltaCookie::added(cookie.into()));
154 }
155
156 /// Adds `cookie` to this jar. If a cookie with the same name already
157 /// exists, it is replaced with `cookie`.
158 ///
159 /// # Example
160 ///
161 /// ```rust
162 /// use cookie::{CookieJar, Cookie};
163 ///
164 /// let mut jar = CookieJar::new();
165 /// jar.add(("name", "value"));
166 /// jar.add(("second", "two"));
167 ///
168 /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
169 /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
170 /// assert_eq!(jar.iter().count(), 2);
171 /// assert_eq!(jar.delta().count(), 2);
172 /// ```
173 pub fn add<C: Into<Cookie<'static>>>(&mut self, cookie: C) {
174 self.delta_cookies.replace(DeltaCookie::added(cookie.into()));
175 }
176
177 /// Removes `cookie` from this jar. If an _original_ cookie with the same
178 /// name as `cookie` is present in the jar, a _removal_ cookie will be
179 /// present in the `delta` computation. **To properly generate the removal
180 /// cookie, `cookie` must contain the same `path` and `domain` as the cookie
181 /// that was initially set.**
182 ///
183 /// A "removal" cookie is a cookie that has the same name as the original
184 /// cookie but has an empty value, a max-age of 0, and an expiration date
185 /// far in the past. See also [`Cookie::make_removal()`].
186 ///
187 /// # Example
188 ///
189 /// Removing an _original_ cookie results in a _removal_ cookie:
190 ///
191 /// ```rust
192 /// use cookie::{CookieJar, Cookie};
193 /// use cookie::time::Duration;
194 ///
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(("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"));
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 /// Removing a new cookie does not result in a _removal_ cookie unless
211 /// there's an original cookie with the same name:
212 ///
213 /// ```rust
214 /// use cookie::{CookieJar, Cookie};
215 ///
216 /// let mut jar = CookieJar::new();
217 /// jar.add(("name", "value"));
218 /// assert_eq!(jar.delta().count(), 1);
219 ///
220 /// jar.remove("name");
221 /// assert_eq!(jar.delta().count(), 0);
222 ///
223 /// jar.add_original(("name", "value"));
224 /// jar.add(("name", "value"));
225 /// assert_eq!(jar.delta().count(), 1);
226 ///
227 /// jar.remove("name");
228 /// assert_eq!(jar.delta().count(), 1);
229 /// ```
230 pub fn remove<C: Into<Cookie<'static>>>(&mut self, cookie: C) {
231 let mut cookie = cookie.into();
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.
241 ///
242 /// This method differs from `remove` in that no delta cookie is created
243 /// under any condition. Thus, no path or domain are needed: only the
244 /// cookie's name. Neither the `delta` nor `iter` methods will return a
245 /// cookie that is removed using this method.
246 ///
247 /// # Example
248 ///
249 /// Removing an _original_ cookie; no _removal_ cookie is generated:
250 ///
251 /// ```rust
252 /// # extern crate cookie;
253 /// use cookie::{CookieJar, Cookie};
254 /// use cookie::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(("name", "value"));
261 /// jar.add(("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("name");
267 /// assert_eq!(jar.delta().count(), 1);
268 /// assert_eq!(jar.iter().count(), 1);
269 ///
270 /// // Now force remove the new cookie. `to_string()` for illustration only.
271 /// jar.force_remove("key".to_string());
272 /// assert_eq!(jar.delta().count(), 0);
273 /// assert_eq!(jar.iter().count(), 0);
274 /// # }
275 /// ```
276 pub fn force_remove<N: AsRef<str>>(&mut self, name: N) {
277 self.original_cookies.remove(name.as_ref());
278 self.delta_cookies.remove(name.as_ref());
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(("name", "value"));
295 /// jar.add_original(("language", "Rust"));
296 ///
297 /// // These operations, represented by delta cookies, will be reset.
298 /// jar.add(("language", "C++"));
299 /// jar.remove("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(("name", "value"));
329 /// jar.add_original(("second", "two"));
330 ///
331 /// // Add new cookies.
332 /// jar.add(("new", "third"));
333 /// jar.add(("another", "fourth"));
334 /// jar.add(("yac", "fifth"));
335 ///
336 /// // Remove some cookies.
337 /// jar.remove(("name"));
338 /// jar.remove(("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(("name", "value"));
357 /// jar.add_original(("second", "two"));
358 ///
359 /// jar.add(("new", "third"));
360 /// jar.add(("another", "fourth"));
361 /// jar.add(("yac", "fifth"));
362 ///
363 /// jar.remove("name");
364 /// jar.remove("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(("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(("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(("private", "text"));
436 ///
437 /// // Remove a cookie using the child jar.
438 /// jar.private_mut(&key).remove("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(("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(("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::{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(("signed", "text"));
498 ///
499 /// // Remove a cookie.
500 /// jar.signed_mut(&key).remove("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 /// Returns a read-only `PrefixedJar` with `self` as its parent jar that
509 /// prefixes the name of cookies with `prefix`. Any retrievals from the
510 /// child jar will be made from the parent jar.
511 ///
512 /// **Note:** Cookie prefixes are specified in an HTTP draft! Their meaning
513 /// and definition are subject to change.
514 ///
515 /// # Example
516 ///
517 /// ```rust
518 /// use cookie::CookieJar;
519 /// use cookie::prefix::{Host, Secure};
520 ///
521 /// // Add a `Host` prefixed cookie.
522 /// let mut jar = CookieJar::new();
523 /// jar.prefixed_mut(Host).add(("h0st", "value"));
524 /// jar.prefixed_mut(Secure).add(("secur3", "value"));
525 ///
526 /// // The cookie's name is prefixed in the parent jar.
527 /// assert!(matches!(jar.get("h0st"), None));
528 /// assert!(matches!(jar.get("__Host-h0st"), Some(_)));
529 /// assert!(matches!(jar.get("secur3"), None));
530 /// assert!(matches!(jar.get("__Secure-secur3"), Some(_)));
531 ///
532 /// // The prefixed jar automatically removes the prefix.
533 /// assert_eq!(jar.prefixed(Host).get("h0st").unwrap().name(), "h0st");
534 /// assert_eq!(jar.prefixed(Host).get("h0st").unwrap().value(), "value");
535 /// assert_eq!(jar.prefixed(Secure).get("secur3").unwrap().name(), "secur3");
536 /// assert_eq!(jar.prefixed(Secure).get("secur3").unwrap().value(), "value");
537 ///
538 /// // Only the correct prefixed jar retrieves the cookie.
539 /// assert!(matches!(jar.prefixed(Host).get("secur3"), None));
540 /// assert!(matches!(jar.prefixed(Secure).get("h0st"), None));
541 /// ```
542 #[inline(always)]
543 pub fn prefixed<'a, P: Prefix>(&'a self, prefix: P) -> PrefixedJar<P, &'a Self> {
544 let _ = prefix;
545 PrefixedJar::new(self)
546 }
547
548 /// Returns a read/write `PrefixedJar` with `self` as its parent jar that
549 /// prefixes the name of cookies with `prefix` and makes the cookie conform
550 /// to the prefix's requirements. This means that added cookies:
551 ///
552 /// 1. Have the [`Prefix::PREFIX`] prepended to their name.
553 /// 2. Modify the cookie via [`Prefix::conform()`] so that it conforms to
554 /// the prefix's requirements.
555 ///
556 /// Any modifications to the child jar will be reflected on the parent jar,
557 /// and any retrievals from the child jar will be made from the parent jar.
558 ///
559 /// **Note:** Cookie prefixes are specified in an HTTP draft! Their meaning
560 /// and definition are subject to change.
561 ///
562 /// # Example
563 ///
564 /// ```rust
565 /// use cookie::CookieJar;
566 /// use cookie::prefix::{Host, Secure};
567 ///
568 /// // Add some prefixed cookies.
569 /// let mut jar = CookieJar::new();
570 /// jar.prefixed_mut(Host).add(("one", "1"));
571 /// jar.prefixed_mut(Secure).add((2.to_string(), "2"));
572 /// jar.prefixed_mut(Host).add((format!("{:0b}", 3), "0b11"));
573 ///
574 /// // Fetch cookies with either `prefixed()` or `prefixed_mut()`.
575 /// assert_eq!(jar.prefixed(Host).get("one").unwrap().value(), "1");
576 /// assert_eq!(jar.prefixed(Secure).get("2").unwrap().value(), "2");
577 /// assert_eq!(jar.prefixed_mut(Host).get("11").unwrap().value(), "0b11");
578 ///
579 /// // Remove cookies.
580 /// jar.prefixed_mut(Host).remove("one");
581 /// assert!(jar.prefixed(Host).get("one").is_none());
582 /// ```
583 pub fn prefixed_mut<'a, P: Prefix>(&'a mut self, prefix: P) -> PrefixedJar<P, &'a mut Self> {
584 let _ = prefix;
585 PrefixedJar::new(self)
586 }
587}
588
589use std::collections::hash_set::Iter as HashSetIter;
590
591/// Iterator over the changes to a cookie jar.
592pub struct Delta<'a> {
593 iter: HashSetIter<'a, DeltaCookie>,
594}
595
596impl<'a> Iterator for Delta<'a> {
597 type Item = &'a Cookie<'static>;
598
599 fn next(&mut self) -> Option<&'a Cookie<'static>> {
600 self.iter.next().map(|c| &c.cookie)
601 }
602}
603
604use std::collections::hash_set::Difference;
605use std::collections::hash_map::RandomState;
606use std::iter::Chain;
607
608/// Iterator over all of the cookies in a jar.
609pub struct Iter<'a> {
610 delta_cookies: Chain<HashSetIter<'a, DeltaCookie>, Difference<'a, DeltaCookie, RandomState>>,
611}
612
613impl<'a> Iterator for Iter<'a> {
614 type Item = &'a Cookie<'static>;
615
616 fn next(&mut self) -> Option<&'a Cookie<'static>> {
617 for cookie in self.delta_cookies.by_ref() {
618 if !cookie.removed {
619 return Some(&*cookie);
620 }
621 }
622
623 None
624 }
625}
626
627#[cfg(test)]
628mod test {
629 use super::CookieJar;
630 use crate::Cookie;
631
632 #[test]
633 #[allow(deprecated)]
634 fn simple() {
635 let mut c = CookieJar::new();
636
637 c.add(("test", ""));
638 c.add(("test2", ""));
639 c.remove("test");
640
641 assert!(c.get("test").is_none());
642 assert!(c.get("test2").is_some());
643
644 c.add(("test3", ""));
645 c.remove("test2");
646 c.remove("test3");
647
648 assert!(c.get("test").is_none());
649 assert!(c.get("test2").is_none());
650 assert!(c.get("test3").is_none());
651 }
652
653 #[test]
654 fn jar_is_send() {
655 fn is_send<T: Send>(_: T) -> bool {
656 true
657 }
658
659 assert!(is_send(CookieJar::new()))
660 }
661
662 #[test]
663 #[cfg(all(feature = "signed", feature = "private"))]
664 fn iter() {
665 let key = crate::Key::generate();
666 let mut c = CookieJar::new();
667
668 c.add_original(Cookie::new("original", "original"));
669
670 c.add(Cookie::new("test", "test"));
671 c.add(Cookie::new("test2", "test2"));
672 c.add(Cookie::new("test3", "test3"));
673 assert_eq!(c.iter().count(), 4);
674
675 c.signed_mut(&key).add(Cookie::new("signed", "signed"));
676 c.private_mut(&key).add(Cookie::new("encrypted", "encrypted"));
677 assert_eq!(c.iter().count(), 6);
678
679 c.remove("test");
680 assert_eq!(c.iter().count(), 5);
681
682 c.remove("signed");
683 c.remove("test2");
684 assert_eq!(c.iter().count(), 3);
685
686 c.add(("test2", "test2"));
687 assert_eq!(c.iter().count(), 4);
688
689 c.remove("test2");
690 assert_eq!(c.iter().count(), 3);
691 }
692
693 #[test]
694 fn delta() {
695 use std::collections::HashMap;
696 use time::Duration;
697
698 let mut c = CookieJar::new();
699
700 c.add_original(Cookie::new("original", "original"));
701 c.add_original(Cookie::new("original1", "original1"));
702
703 c.add(Cookie::new("test", "test"));
704 c.add(Cookie::new("test2", "test2"));
705 c.add(Cookie::new("test3", "test3"));
706 c.add(Cookie::new("test4", "test4"));
707
708 c.remove("test");
709 c.remove("original");
710
711 assert_eq!(c.delta().count(), 4);
712
713 let names: HashMap<_, _> = c.delta()
714 .map(|c| (c.name(), c.max_age()))
715 .collect();
716
717 assert!(names.get("test2").unwrap().is_none());
718 assert!(names.get("test3").unwrap().is_none());
719 assert!(names.get("test4").unwrap().is_none());
720 assert_eq!(names.get("original").unwrap(), &Some(Duration::seconds(0)));
721 }
722
723 #[test]
724 fn replace_original() {
725 let mut jar = CookieJar::new();
726 jar.add_original(Cookie::new("original_a", "a"));
727 jar.add_original(Cookie::new("original_b", "b"));
728 assert_eq!(jar.get("original_a").unwrap().value(), "a");
729
730 jar.add(Cookie::new("original_a", "av2"));
731 assert_eq!(jar.get("original_a").unwrap().value(), "av2");
732 }
733
734 #[test]
735 fn empty_delta() {
736 let mut jar = CookieJar::new();
737 jar.add(Cookie::new("name", "val"));
738 assert_eq!(jar.delta().count(), 1);
739
740 jar.remove("name");
741 assert_eq!(jar.delta().count(), 0);
742
743 jar.add_original(Cookie::new("name", "val"));
744 assert_eq!(jar.delta().count(), 0);
745
746 jar.remove("name");
747 assert_eq!(jar.delta().count(), 1);
748
749 jar.add(Cookie::new("name", "val"));
750 assert_eq!(jar.delta().count(), 1);
751
752 jar.remove("name");
753 assert_eq!(jar.delta().count(), 1);
754 }
755
756 #[test]
757 fn add_remove_add() {
758 let mut jar = CookieJar::new();
759 jar.add_original(Cookie::new("name", "val"));
760 assert_eq!(jar.delta().count(), 0);
761
762 jar.remove("name");
763 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
764 assert_eq!(jar.delta().count(), 1);
765
766 // The cookie's been deleted. Another original doesn't change that.
767 jar.add_original(Cookie::new("name", "val"));
768 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
769 assert_eq!(jar.delta().count(), 1);
770
771 jar.remove("name");
772 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
773 assert_eq!(jar.delta().count(), 1);
774
775 jar.add(Cookie::new("name", "val"));
776 assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
777 assert_eq!(jar.delta().count(), 1);
778
779 jar.remove("name");
780 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
781 assert_eq!(jar.delta().count(), 1);
782 }
783
784 #[test]
785 fn replace_remove() {
786 let mut jar = CookieJar::new();
787 jar.add_original(Cookie::new("name", "val"));
788 assert_eq!(jar.delta().count(), 0);
789
790 jar.add(Cookie::new("name", "val"));
791 assert_eq!(jar.delta().count(), 1);
792 assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
793
794 jar.remove("name");
795 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
796 }
797
798 #[test]
799 fn remove_with_path() {
800 let mut jar = CookieJar::new();
801 jar.add_original(("name", "val"));
802 assert_eq!(jar.iter().count(), 1);
803 assert_eq!(jar.delta().count(), 0);
804 assert_eq!(jar.iter().filter(|c| c.path().is_none()).count(), 1);
805
806 jar.remove(Cookie::build("name").path("/"));
807 assert_eq!(jar.iter().count(), 0);
808 assert_eq!(jar.delta().count(), 1);
809 assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
810 assert_eq!(jar.delta().filter(|c| c.path() == Some("/")).count(), 1);
811 }
812}