1use super::*;
18
19use http::header::HeaderName;
20use http::HeaderValue;
21use indexmap::IndexMap;
22use once_cell::sync::Lazy;
23use pingora_error::{Error, ErrorType};
24use regex::bytes::Regex;
25use std::num::IntErrorKind;
26use std::slice;
27use std::str;
28
29pub const DELTA_SECONDS_OVERFLOW_VALUE: u32 = i32::MAX as u32;
47pub const DELTA_SECONDS_OVERFLOW_DURATION: Duration =
48 Duration::from_secs(DELTA_SECONDS_OVERFLOW_VALUE as u64);
49
50pub type DirectiveKey = String;
52
53#[derive(Debug)]
55pub struct DirectiveValue(pub Vec<u8>);
56
57impl AsRef<[u8]> for DirectiveValue {
58 fn as_ref(&self) -> &[u8] {
59 &self.0
60 }
61}
62
63impl DirectiveValue {
64 pub fn parse_as_bytes(&self) -> &[u8] {
66 self.0
67 .strip_prefix(b"\"")
68 .and_then(|bytes| bytes.strip_suffix(b"\""))
69 .unwrap_or(&self.0[..])
70 }
71
72 pub fn parse_as_str(&self) -> Result<&str> {
74 str::from_utf8(self.parse_as_bytes()).or_else(|e| {
75 Error::e_because(ErrorType::InternalError, "could not parse value as utf8", e)
76 })
77 }
78
79 pub fn parse_as_delta_seconds(&self) -> Result<u32> {
83 match self.parse_as_str()?.parse::<u32>() {
84 Ok(value) => Ok(value),
85 Err(e) => {
86 if e.kind() == &IntErrorKind::PosOverflow {
88 Ok(DELTA_SECONDS_OVERFLOW_VALUE)
89 } else {
90 Error::e_because(ErrorType::InternalError, "could not parse value as u32", e)
91 }
92 }
93 }
94 }
95}
96
97pub type DirectiveMap = IndexMap<DirectiveKey, Option<DirectiveValue>>;
99
100#[derive(Debug)]
102pub struct CacheControl {
103 pub directives: DirectiveMap,
105}
106
107#[derive(Debug, PartialEq, Eq)]
109pub enum Cacheable {
110 Yes,
112 No,
114 Default,
116}
117
118pub struct ListValueIter<'a>(slice::Split<'a, u8, fn(&u8) -> bool>);
120
121impl<'a> ListValueIter<'a> {
122 pub fn from(value: &'a DirectiveValue) -> Self {
123 ListValueIter(value.parse_as_bytes().split(|byte| byte == &b','))
124 }
125}
126
127fn trim_ows(bytes: &[u8]) -> &[u8] {
130 fn not_ows(b: &u8) -> bool {
131 b != &b'\x20' && b != &b'\x09'
132 }
133 let head = bytes.iter().position(not_ows).unwrap_or(0);
135 let tail = bytes
136 .iter()
137 .rposition(not_ows)
138 .map(|rpos| rpos + 1)
139 .unwrap_or(head);
140 &bytes[head..tail]
141}
142
143impl<'a> Iterator for ListValueIter<'a> {
144 type Item = &'a [u8];
145
146 fn next(&mut self) -> Option<Self::Item> {
147 Some(trim_ows(self.0.next()?))
148 }
149}
150
151static RE_CACHE_DIRECTIVE: Lazy<Regex> =
161 Lazy::new(|| {
167 Regex::new(r#"(?-u)(?:^|(?:\s*[,;]\s*))([^\x00-\x20\(\)<>@,;:\\"/\[\]\?=\{\}\x7F]+)(?:=((?:[^\x00-\x20\(\)<>@,;:\\"/\[\]\?=\{\}\x7F]+|(?:"(?:[^"\\]|\\.)*"))))?"#).unwrap()
168 });
169
170impl CacheControl {
171 fn from_headers(headers: http::header::GetAll<HeaderValue>) -> Option<Self> {
179 let mut directives = IndexMap::new();
180 for line in headers {
182 for captures in RE_CACHE_DIRECTIVE.captures_iter(line.as_bytes()) {
183 let key = captures.get(1).and_then(|cap| {
186 str::from_utf8(cap.as_bytes())
187 .ok()
188 .map(|token| token.to_lowercase())
189 });
190 if key.is_none() {
191 continue;
192 }
193 let value = captures
196 .get(2)
197 .map(|cap| DirectiveValue(cap.as_bytes().to_vec()));
198 directives.insert(key.unwrap(), value);
199 }
200 }
201 Some(CacheControl { directives })
202 }
203
204 pub fn from_headers_named(header_name: &str, headers: &http::HeaderMap) -> Option<Self> {
206 if !headers.contains_key(header_name) {
207 return None;
208 }
209
210 Self::from_headers(headers.get_all(header_name))
211 }
212
213 pub fn from_req_headers_named(header_name: &str, req_header: &ReqHeader) -> Option<Self> {
215 Self::from_headers_named(header_name, &req_header.headers)
216 }
217
218 pub fn from_req_headers(req_header: &ReqHeader) -> Option<Self> {
220 Self::from_req_headers_named("cache-control", req_header)
221 }
222
223 pub fn from_resp_headers_named(header_name: &str, resp_header: &RespHeader) -> Option<Self> {
225 Self::from_headers_named(header_name, &resp_header.headers)
226 }
227
228 pub fn from_resp_headers(resp_header: &RespHeader) -> Option<Self> {
230 Self::from_resp_headers_named("cache-control", resp_header)
231 }
232
233 pub fn has_key(&self, key: &str) -> bool {
235 self.directives.contains_key(key)
236 }
237
238 pub fn public(&self) -> bool {
240 self.has_key("public")
241 }
242
243 fn has_key_without_value(&self, key: &str) -> bool {
245 matches!(self.directives.get(key), Some(None))
246 }
247
248 pub fn private(&self) -> bool {
255 self.has_key_without_value("private")
256 }
257
258 fn get_field_names(&self, key: &str) -> Option<ListValueIter<'_>> {
259 let value = self.directives.get(key)?.as_ref()?;
260 Some(ListValueIter::from(value))
261 }
262
263 pub fn private_field_names(&self) -> Option<ListValueIter<'_>> {
265 self.get_field_names("private")
266 }
267
268 pub fn no_cache(&self) -> bool {
270 self.has_key_without_value("no-cache")
271 }
272
273 pub fn no_cache_field_names(&self) -> Option<ListValueIter<'_>> {
275 self.get_field_names("no-cache")
276 }
277
278 pub fn no_store(&self) -> bool {
280 self.has_key("no-store")
281 }
282
283 fn parse_delta_seconds(&self, key: &str) -> Result<Option<u32>> {
284 if let Some(Some(dir_value)) = self.directives.get(key) {
285 Ok(Some(dir_value.parse_as_delta_seconds()?))
286 } else {
287 Ok(None)
288 }
289 }
290
291 pub fn max_age(&self) -> Result<Option<u32>> {
293 self.parse_delta_seconds("max-age")
294 }
295
296 pub fn s_maxage(&self) -> Result<Option<u32>> {
298 self.parse_delta_seconds("s-maxage")
299 }
300
301 pub fn stale_while_revalidate(&self) -> Result<Option<u32>> {
303 self.parse_delta_seconds("stale-while-revalidate")
304 }
305
306 pub fn stale_if_error(&self) -> Result<Option<u32>> {
308 self.parse_delta_seconds("stale-if-error")
309 }
310
311 pub fn must_revalidate(&self) -> bool {
313 self.has_key("must-revalidate")
314 }
315
316 pub fn proxy_revalidate(&self) -> bool {
318 self.has_key("proxy-revalidate")
319 }
320
321 pub fn only_if_cached(&self) -> bool {
323 self.has_key("only-if-cached")
324 }
325}
326
327impl InterpretCacheControl for CacheControl {
328 fn is_cacheable(&self) -> Cacheable {
329 if self.no_store() || self.private() {
330 return Cacheable::No;
331 }
332 if self.has_key("s-maxage") || self.has_key("max-age") || self.public() {
333 return Cacheable::Yes;
334 }
335 Cacheable::Default
336 }
337
338 fn allow_caching_authorized_req(&self) -> bool {
339 self.must_revalidate() || self.public() || self.has_key("s-maxage")
343 }
344
345 fn fresh_duration(&self) -> Option<Duration> {
346 if self.no_cache() {
347 return Some(Duration::ZERO);
349 }
350 let seconds = self
351 .s_maxage()
352 .ok()?
353 .or_else(|| self.max_age().unwrap_or(None))
355 .map(|duration| Duration::from_secs(duration as u64))?;
356 Some(seconds)
357 }
358
359 fn serve_stale_while_revalidate_duration(&self) -> Option<Duration> {
360 if self.must_revalidate() || self.proxy_revalidate() || self.has_key("s-maxage") {
363 return Some(Duration::ZERO);
364 }
365 self.stale_while_revalidate()
366 .unwrap_or(None)
367 .map(|secs| Duration::from_secs(secs as u64))
368 }
369
370 fn serve_stale_if_error_duration(&self) -> Option<Duration> {
371 if self.must_revalidate() || self.proxy_revalidate() || self.has_key("s-maxage") {
372 return Some(Duration::ZERO);
373 }
374 self.stale_if_error()
375 .unwrap_or(None)
376 .map(|secs| Duration::from_secs(secs as u64))
377 }
378
379 fn strip_private_headers(&self, resp_header: &mut ResponseHeader) {
381 fn strip_listed_headers(resp: &mut ResponseHeader, field_names: ListValueIter) {
382 for name in field_names {
383 if let Ok(header) = HeaderName::from_bytes(name) {
384 resp.remove_header(&header);
385 }
386 }
387 }
388
389 if let Some(headers) = self.private_field_names() {
390 strip_listed_headers(resp_header, headers);
391 }
392 if let Some(headers) = self.no_cache_field_names() {
398 strip_listed_headers(resp_header, headers);
399 }
400 }
401}
402
403pub trait InterpretCacheControl {
410 fn is_cacheable(&self) -> Cacheable;
416
417 fn allow_caching_authorized_req(&self) -> bool;
420
421 fn fresh_duration(&self) -> Option<Duration>;
426
427 fn serve_stale_while_revalidate_duration(&self) -> Option<Duration>;
436
437 fn serve_stale_if_error_duration(&self) -> Option<Duration>;
446
447 fn strip_private_headers(&self, resp_header: &mut ResponseHeader);
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455 use http::header::CACHE_CONTROL;
456 use http::{request, response};
457
458 fn build_response(cc_key: HeaderName, cc_value: &str) -> response::Parts {
459 let (parts, _) = response::Builder::new()
460 .header(cc_key, cc_value)
461 .body(())
462 .unwrap()
463 .into_parts();
464 parts
465 }
466
467 #[test]
468 fn test_simple_cache_control() {
469 let resp = build_response(CACHE_CONTROL, "public, max-age=10000");
470 let cc = CacheControl::from_resp_headers(&resp).unwrap();
471 assert!(cc.public());
472 assert_eq!(cc.max_age().unwrap().unwrap(), 10000);
473 }
474
475 #[test]
476 fn test_private_cache_control() {
477 let resp = build_response(CACHE_CONTROL, "private");
478 let cc = CacheControl::from_resp_headers(&resp).unwrap();
479
480 assert!(cc.private());
481 assert!(cc.max_age().unwrap().is_none());
482 }
483
484 #[test]
485 fn test_directives_across_header_lines() {
486 let (parts, _) = response::Builder::new()
487 .header(CACHE_CONTROL, "public,")
488 .header("cache-Control", "max-age=10000")
489 .body(())
490 .unwrap()
491 .into_parts();
492 let cc = CacheControl::from_resp_headers(&parts).unwrap();
493
494 assert!(cc.public());
495 assert_eq!(cc.max_age().unwrap().unwrap(), 10000);
496 }
497
498 #[test]
499 fn test_recognizes_semicolons_as_delimiters() {
500 let resp = build_response(CACHE_CONTROL, "public; max-age=0");
501 let cc = CacheControl::from_resp_headers(&resp).unwrap();
502
503 assert!(cc.public());
504 assert_eq!(cc.max_age().unwrap().unwrap(), 0);
505 }
506
507 #[test]
508 fn test_unknown_directives() {
509 let resp = build_response(CACHE_CONTROL, "public,random1=random2, rand3=\"\"");
510 let cc = CacheControl::from_resp_headers(&resp).unwrap();
511 let mut directive_iter = cc.directives.iter();
512
513 let first = directive_iter.next().unwrap();
514 assert_eq!(first.0, &"public");
515 assert!(first.1.is_none());
516
517 let second = directive_iter.next().unwrap();
518 assert_eq!(second.0, &"random1");
519 assert_eq!(second.1.as_ref().unwrap().0, "random2".as_bytes());
520
521 let third = directive_iter.next().unwrap();
522 assert_eq!(third.0, &"rand3");
523 assert_eq!(third.1.as_ref().unwrap().0, "\"\"".as_bytes());
524
525 assert!(directive_iter.next().is_none());
526 }
527
528 #[test]
529 fn test_case_insensitive_directive_keys() {
530 let resp = build_response(
531 CACHE_CONTROL,
532 "Public=\"something\", mAx-AGe=\"10000\", foo=cRaZyCaSe, bAr=\"inQuotes\"",
533 );
534 let cc = CacheControl::from_resp_headers(&resp).unwrap();
535
536 assert!(cc.public());
537 assert_eq!(cc.max_age().unwrap().unwrap(), 10000);
538
539 let mut directive_iter = cc.directives.iter();
540 let first = directive_iter.next().unwrap();
541 assert_eq!(first.0, &"public");
542 assert_eq!(first.1.as_ref().unwrap().0, "\"something\"".as_bytes());
543
544 let second = directive_iter.next().unwrap();
545 assert_eq!(second.0, &"max-age");
546 assert_eq!(second.1.as_ref().unwrap().0, "\"10000\"".as_bytes());
547
548 let third = directive_iter.next().unwrap();
550 assert_eq!(third.0, &"foo");
551 assert_eq!(third.1.as_ref().unwrap().0, "cRaZyCaSe".as_bytes());
552
553 let fourth = directive_iter.next().unwrap();
554 assert_eq!(fourth.0, &"bar");
555 assert_eq!(fourth.1.as_ref().unwrap().0, "\"inQuotes\"".as_bytes());
556
557 assert!(directive_iter.next().is_none());
558 }
559
560 #[test]
561 fn test_non_ascii() {
562 let resp = build_response(CACHE_CONTROL, "püblic=💖, max-age=\"💯\"");
563 let cc = CacheControl::from_resp_headers(&resp).unwrap();
564
565 assert!(!cc.public());
567 assert_eq!(
568 cc.max_age().unwrap_err().context.unwrap().to_string(),
569 "could not parse value as u32"
570 );
571
572 let mut directive_iter = cc.directives.iter();
573 let first = directive_iter.next().unwrap();
574 assert_eq!(first.0, &"püblic");
575 assert_eq!(first.1.as_ref().unwrap().0, "💖".as_bytes());
576
577 let second = directive_iter.next().unwrap();
578 assert_eq!(second.0, &"max-age");
579 assert_eq!(second.1.as_ref().unwrap().0, "\"💯\"".as_bytes());
580
581 assert!(directive_iter.next().is_none());
582 }
583
584 #[test]
585 fn test_non_utf8_key() {
586 let mut resp = response::Builder::new().body(()).unwrap();
587 resp.headers_mut().insert(
588 CACHE_CONTROL,
589 HeaderValue::from_bytes(b"bar\xFF=\"baz\", a=b").unwrap(),
590 );
591 let (parts, _) = resp.into_parts();
592 let cc = CacheControl::from_resp_headers(&parts).unwrap();
593
594 let mut directive_iter = cc.directives.iter();
596 let first = directive_iter.next().unwrap();
597 assert_eq!(first.0, &"a");
598 assert_eq!(first.1.as_ref().unwrap().0, "b".as_bytes());
599
600 assert!(directive_iter.next().is_none());
601 }
602
603 #[test]
604 fn test_non_utf8_value() {
605 let mut resp = response::Builder::new().body(()).unwrap();
607 resp.headers_mut().insert(
608 CACHE_CONTROL,
609 HeaderValue::from_bytes(b"max-age=ba\xFFr, bar=\"baz\xFF\", a=b").unwrap(),
610 );
611 let (parts, _) = resp.into_parts();
612 let cc = CacheControl::from_resp_headers(&parts).unwrap();
613
614 assert_eq!(
615 cc.max_age().unwrap_err().context.unwrap().to_string(),
616 "could not parse value as utf8"
617 );
618
619 let mut directive_iter = cc.directives.iter();
620
621 let first = directive_iter.next().unwrap();
622 assert_eq!(first.0, &"max-age");
623 assert_eq!(first.1.as_ref().unwrap().0, b"ba\xFFr");
624
625 let second = directive_iter.next().unwrap();
626 assert_eq!(second.0, &"bar");
627 assert_eq!(second.1.as_ref().unwrap().0, b"\"baz\xFF\"");
628
629 let third = directive_iter.next().unwrap();
630 assert_eq!(third.0, &"a");
631 assert_eq!(third.1.as_ref().unwrap().0, "b".as_bytes());
632
633 assert!(directive_iter.next().is_none());
634 }
635
636 #[test]
637 fn test_age_overflow() {
638 let resp = build_response(
639 CACHE_CONTROL,
640 "max-age=-99999999999999999999999999, s-maxage=99999999999999999999999999",
641 );
642 let cc = CacheControl::from_resp_headers(&resp).unwrap();
643
644 assert_eq!(
645 cc.s_maxage().unwrap().unwrap(),
646 DELTA_SECONDS_OVERFLOW_VALUE
647 );
648 assert_eq!(
650 cc.max_age().unwrap_err().context.unwrap().to_string(),
651 "could not parse value as u32"
652 );
653 }
654
655 #[test]
656 fn test_fresh_sec() {
657 let resp = build_response(CACHE_CONTROL, "");
658 let cc = CacheControl::from_resp_headers(&resp).unwrap();
659 assert!(cc.fresh_duration().is_none());
660
661 let resp = build_response(CACHE_CONTROL, "max-age=12345");
662 let cc = CacheControl::from_resp_headers(&resp).unwrap();
663 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(12345));
664
665 let resp = build_response(CACHE_CONTROL, "max-age=99999,s-maxage=123");
666 let cc = CacheControl::from_resp_headers(&resp).unwrap();
667 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(123));
669 }
670
671 #[test]
672 fn test_cacheability() {
673 let resp = build_response(CACHE_CONTROL, "");
674 let cc = CacheControl::from_resp_headers(&resp).unwrap();
675 assert_eq!(cc.is_cacheable(), Cacheable::Default);
676
677 let resp = build_response(CACHE_CONTROL, "private, max-age=12345");
679 let cc = CacheControl::from_resp_headers(&resp).unwrap();
680 assert_eq!(cc.is_cacheable(), Cacheable::No);
681
682 let resp = build_response(CACHE_CONTROL, "no-store, max-age=12345");
683 let cc = CacheControl::from_resp_headers(&resp).unwrap();
684 assert_eq!(cc.is_cacheable(), Cacheable::No);
685
686 let resp = build_response(CACHE_CONTROL, "public");
688 let cc = CacheControl::from_resp_headers(&resp).unwrap();
689 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
690
691 let resp = build_response(CACHE_CONTROL, "max-age=0");
692 let cc = CacheControl::from_resp_headers(&resp).unwrap();
693 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
694 }
695
696 #[test]
697 fn test_no_cache() {
698 let resp = build_response(CACHE_CONTROL, "no-cache, max-age=12345");
699 let cc = CacheControl::from_resp_headers(&resp).unwrap();
700 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
701 assert_eq!(cc.fresh_duration().unwrap(), Duration::ZERO);
702 }
703
704 #[test]
705 fn test_no_cache_field_names() {
706 let resp = build_response(CACHE_CONTROL, "no-cache=\"set-cookie\", max-age=12345");
707 let cc = CacheControl::from_resp_headers(&resp).unwrap();
708 assert!(!cc.private());
709 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
710 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(12345));
711 let mut field_names = cc.no_cache_field_names().unwrap();
712 assert_eq!(
713 str::from_utf8(field_names.next().unwrap()).unwrap(),
714 "set-cookie"
715 );
716 assert!(field_names.next().is_none());
717
718 let mut resp = response::Builder::new().body(()).unwrap();
719 resp.headers_mut().insert(
720 CACHE_CONTROL,
721 HeaderValue::from_bytes(
722 b"private=\"\", no-cache=\"a\xFF, set-cookie, Baz\x09 , c,d ,, \"",
723 )
724 .unwrap(),
725 );
726 let (parts, _) = resp.into_parts();
727 let cc = CacheControl::from_resp_headers(&parts).unwrap();
728 let mut field_names = cc.private_field_names().unwrap();
729 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "");
730 assert!(field_names.next().is_none());
731 let mut field_names = cc.no_cache_field_names().unwrap();
732 assert!(str::from_utf8(field_names.next().unwrap()).is_err());
733 assert_eq!(
734 str::from_utf8(field_names.next().unwrap()).unwrap(),
735 "set-cookie"
736 );
737 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "Baz");
738 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "c");
739 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "d");
740 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "");
741 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "");
742 assert!(field_names.next().is_none());
743 }
744
745 #[test]
746 fn test_strip_private_headers() {
747 let mut resp = ResponseHeader::build(200, None).unwrap();
748 resp.append_header(
749 CACHE_CONTROL,
750 "no-cache=\"x-private-header\", max-age=12345",
751 )
752 .unwrap();
753 resp.append_header("X-Private-Header", "dropped").unwrap();
754
755 let cc = CacheControl::from_resp_headers(&resp).unwrap();
756 cc.strip_private_headers(&mut resp);
757 assert!(!resp.headers.contains_key("X-Private-Header"));
758 }
759
760 #[test]
761 fn test_stale_while_revalidate() {
762 let resp = build_response(CACHE_CONTROL, "max-age=12345, stale-while-revalidate=5");
763 let cc = CacheControl::from_resp_headers(&resp).unwrap();
764 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 5);
765 assert_eq!(
766 cc.serve_stale_while_revalidate_duration().unwrap(),
767 Duration::from_secs(5)
768 );
769 assert!(cc.serve_stale_if_error_duration().is_none());
770 }
771
772 #[test]
773 fn test_stale_if_error() {
774 let resp = build_response(CACHE_CONTROL, "max-age=12345, stale-if-error=3600");
775 let cc = CacheControl::from_resp_headers(&resp).unwrap();
776 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 3600);
777 assert_eq!(
778 cc.serve_stale_if_error_duration().unwrap(),
779 Duration::from_secs(3600)
780 );
781 assert!(cc.serve_stale_while_revalidate_duration().is_none());
782 }
783
784 #[test]
785 fn test_must_revalidate() {
786 let resp = build_response(
787 CACHE_CONTROL,
788 "max-age=12345, stale-while-revalidate=60, stale-if-error=30, must-revalidate",
789 );
790 let cc = CacheControl::from_resp_headers(&resp).unwrap();
791 assert!(cc.must_revalidate());
792 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 60);
793 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 30);
794 assert_eq!(
795 cc.serve_stale_while_revalidate_duration().unwrap(),
796 Duration::ZERO
797 );
798 assert_eq!(cc.serve_stale_if_error_duration().unwrap(), Duration::ZERO);
799 }
800
801 #[test]
802 fn test_proxy_revalidate() {
803 let resp = build_response(
804 CACHE_CONTROL,
805 "max-age=12345, stale-while-revalidate=60, stale-if-error=30, proxy-revalidate",
806 );
807 let cc = CacheControl::from_resp_headers(&resp).unwrap();
808 assert!(cc.proxy_revalidate());
809 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 60);
810 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 30);
811 assert_eq!(
812 cc.serve_stale_while_revalidate_duration().unwrap(),
813 Duration::ZERO
814 );
815 assert_eq!(cc.serve_stale_if_error_duration().unwrap(), Duration::ZERO);
816 }
817
818 #[test]
819 fn test_s_maxage_stale() {
820 let resp = build_response(
821 CACHE_CONTROL,
822 "s-maxage=0, stale-while-revalidate=60, stale-if-error=30",
823 );
824 let cc = CacheControl::from_resp_headers(&resp).unwrap();
825 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 60);
826 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 30);
827 assert_eq!(
828 cc.serve_stale_while_revalidate_duration().unwrap(),
829 Duration::ZERO
830 );
831 assert_eq!(cc.serve_stale_if_error_duration().unwrap(), Duration::ZERO);
832 }
833
834 #[test]
835 fn test_authorized_request() {
836 let resp = build_response(CACHE_CONTROL, "max-age=10");
837 let cc = CacheControl::from_resp_headers(&resp).unwrap();
838 assert!(!cc.allow_caching_authorized_req());
839
840 let resp = build_response(CACHE_CONTROL, "s-maxage=10");
841 let cc = CacheControl::from_resp_headers(&resp).unwrap();
842 assert!(cc.allow_caching_authorized_req());
843
844 let resp = build_response(CACHE_CONTROL, "public");
845 let cc = CacheControl::from_resp_headers(&resp).unwrap();
846 assert!(cc.allow_caching_authorized_req());
847
848 let resp = build_response(CACHE_CONTROL, "must-revalidate, max-age=0");
849 let cc = CacheControl::from_resp_headers(&resp).unwrap();
850 assert!(cc.allow_caching_authorized_req());
851
852 let resp = build_response(CACHE_CONTROL, "");
853 let cc = CacheControl::from_resp_headers(&resp).unwrap();
854 assert!(!cc.allow_caching_authorized_req());
855 }
856
857 fn build_request(cc_key: HeaderName, cc_value: &str) -> request::Parts {
858 let (parts, _) = request::Builder::new()
859 .header(cc_key, cc_value)
860 .body(())
861 .unwrap()
862 .into_parts();
863 parts
864 }
865
866 #[test]
867 fn test_request_only_if_cached() {
868 let req = build_request(CACHE_CONTROL, "only-if-cached=1");
869 let cc = CacheControl::from_req_headers(&req).unwrap();
870 assert!(cc.only_if_cached())
871 }
872}