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::fmt;
26use std::num::IntErrorKind;
27use std::slice;
28use std::str;
29
30pub const DELTA_SECONDS_OVERFLOW_VALUE: u32 = i32::MAX as u32;
48pub const DELTA_SECONDS_OVERFLOW_DURATION: Duration =
49 Duration::from_secs(DELTA_SECONDS_OVERFLOW_VALUE as u64);
50
51#[derive(Clone, Debug, PartialEq, Eq, Hash)]
60pub enum DirectiveKey {
61 MaxAge,
63 SMaxAge,
65 NoCache,
67 NoStore,
69 Private,
71 Public,
73 MustRevalidate,
75 ProxyRevalidate,
77 MustUnderstand,
79 NoTransform,
81 Immutable,
83 StaleWhileRevalidate,
85 StaleIfError,
87 OnlyIfCached,
89 Unknown(String),
91}
92
93impl DirectiveKey {
94 pub fn as_str(&self) -> &str {
99 match self {
100 Self::MaxAge => "max-age",
101 Self::SMaxAge => "s-maxage",
102 Self::NoCache => "no-cache",
103 Self::NoStore => "no-store",
104 Self::Private => "private",
105 Self::Public => "public",
106 Self::MustRevalidate => "must-revalidate",
107 Self::ProxyRevalidate => "proxy-revalidate",
108 Self::MustUnderstand => "must-understand",
109 Self::NoTransform => "no-transform",
110 Self::Immutable => "immutable",
111 Self::StaleWhileRevalidate => "stale-while-revalidate",
112 Self::StaleIfError => "stale-if-error",
113 Self::OnlyIfCached => "only-if-cached",
114 Self::Unknown(s) => s.as_str(),
115 }
116 }
117
118 pub fn from_lowercase(s: &str) -> Self {
130 match s {
131 "max-age" => Self::MaxAge,
132 "s-maxage" => Self::SMaxAge,
133 "no-cache" => Self::NoCache,
134 "no-store" => Self::NoStore,
135 "private" => Self::Private,
136 "public" => Self::Public,
137 "must-revalidate" => Self::MustRevalidate,
138 "proxy-revalidate" => Self::ProxyRevalidate,
139 "must-understand" => Self::MustUnderstand,
140 "no-transform" => Self::NoTransform,
141 "immutable" => Self::Immutable,
142 "stale-while-revalidate" => Self::StaleWhileRevalidate,
143 "stale-if-error" => Self::StaleIfError,
144 "only-if-cached" => Self::OnlyIfCached,
145 other => Self::Unknown(other.to_owned()),
146 }
147 }
148
149 pub fn from_lowercase_owned(s: String) -> Self {
155 match s.as_str() {
157 "max-age" => Self::MaxAge,
158 "s-maxage" => Self::SMaxAge,
159 "no-cache" => Self::NoCache,
160 "no-store" => Self::NoStore,
161 "private" => Self::Private,
162 "public" => Self::Public,
163 "must-revalidate" => Self::MustRevalidate,
164 "proxy-revalidate" => Self::ProxyRevalidate,
165 "must-understand" => Self::MustUnderstand,
166 "no-transform" => Self::NoTransform,
167 "immutable" => Self::Immutable,
168 "stale-while-revalidate" => Self::StaleWhileRevalidate,
169 "stale-if-error" => Self::StaleIfError,
170 "only-if-cached" => Self::OnlyIfCached,
171 _ => Self::Unknown(s),
172 }
173 }
174}
175
176impl fmt::Display for DirectiveKey {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 f.write_str(self.as_str())
179 }
180}
181
182impl PartialEq<str> for DirectiveKey {
183 fn eq(&self, other: &str) -> bool {
184 self.as_str() == other
185 }
186}
187
188impl PartialEq<&str> for DirectiveKey {
189 fn eq(&self, other: &&str) -> bool {
190 self.as_str() == *other
191 }
192}
193
194#[derive(Debug)]
196pub struct DirectiveValue(pub Vec<u8>);
197
198impl AsRef<[u8]> for DirectiveValue {
199 fn as_ref(&self) -> &[u8] {
200 &self.0
201 }
202}
203
204impl DirectiveValue {
205 pub fn parse_as_bytes(&self) -> &[u8] {
207 self.0
208 .strip_prefix(b"\"")
209 .and_then(|bytes| bytes.strip_suffix(b"\""))
210 .unwrap_or(&self.0[..])
211 }
212
213 pub fn parse_as_str(&self) -> Result<&str> {
215 str::from_utf8(self.parse_as_bytes()).or_else(|e| {
216 Error::e_because(ErrorType::InternalError, "could not parse value as utf8", e)
217 })
218 }
219
220 pub fn parse_as_delta_seconds(&self) -> Result<u32> {
224 match self.parse_as_str()?.parse::<u32>() {
225 Ok(value) => Ok(value),
226 Err(e) => {
227 if e.kind() == &IntErrorKind::PosOverflow {
229 Ok(DELTA_SECONDS_OVERFLOW_VALUE)
230 } else {
231 Error::e_because(ErrorType::InternalError, "could not parse value as u32", e)
232 }
233 }
234 }
235 }
236
237 pub fn parse_as_delta_seconds_floor(&self) -> Result<u32> {
251 let s = self.parse_as_str()?;
254 match s.parse::<u32>() {
255 Ok(value) => Ok(value),
256 Err(e) if e.kind() == &IntErrorKind::PosOverflow => Ok(DELTA_SECONDS_OVERFLOW_VALUE),
257 Err(int_err) => {
258 match s.parse::<f64>() {
262 Ok(f) if f.is_finite() && f >= 0.0 => {
263 if f >= DELTA_SECONDS_OVERFLOW_VALUE as f64 {
264 Ok(DELTA_SECONDS_OVERFLOW_VALUE)
265 } else {
266 Ok(f.floor() as u32)
270 }
271 }
272 _ => Error::e_because(
273 ErrorType::InternalError,
274 "could not parse value as u32",
275 int_err,
276 ),
277 }
278 }
279 }
280 }
281}
282
283pub type DirectiveMap = IndexMap<DirectiveKey, Option<DirectiveValue>>;
285
286#[derive(Debug)]
288pub struct CacheControl {
289 pub directives: DirectiveMap,
291 pub allow_float_seconds: bool,
300}
301
302#[derive(Debug, PartialEq, Eq)]
304pub enum Cacheable {
305 Yes,
307 No,
309 Default,
311}
312
313pub struct ListValueIter<'a>(slice::Split<'a, u8, fn(&u8) -> bool>);
315
316impl<'a> ListValueIter<'a> {
317 pub fn from(value: &'a DirectiveValue) -> Self {
318 ListValueIter(value.parse_as_bytes().split(|byte| byte == &b','))
319 }
320}
321
322fn trim_ows(bytes: &[u8]) -> &[u8] {
325 fn not_ows(b: &u8) -> bool {
326 b != &b'\x20' && b != &b'\x09'
327 }
328 let head = bytes.iter().position(not_ows).unwrap_or(0);
330 let tail = bytes
331 .iter()
332 .rposition(not_ows)
333 .map(|rpos| rpos + 1)
334 .unwrap_or(head);
335 &bytes[head..tail]
336}
337
338impl<'a> Iterator for ListValueIter<'a> {
339 type Item = &'a [u8];
340
341 fn next(&mut self) -> Option<Self::Item> {
342 Some(trim_ows(self.0.next()?))
343 }
344}
345
346static RE_CACHE_DIRECTIVE: Lazy<Regex> =
356 Lazy::new(|| {
362 Regex::new(r#"(?-u)(?:^|(?:\s*[,;]\s*))([^\x00-\x20\(\)<>@,;:\\"/\[\]\?=\{\}\x7F]+)(?:=((?:[^\x00-\x20\(\)<>@,;:\\"/\[\]\?=\{\}\x7F]+|(?:"(?:[^"\\]|\\.)*"))))?"#).unwrap()
363 });
364
365impl CacheControl {
366 fn from_headers(headers: http::header::GetAll<HeaderValue>) -> Option<Self> {
374 let mut directives = IndexMap::new();
375 for line in headers {
377 for captures in RE_CACHE_DIRECTIVE.captures_iter(line.as_bytes()) {
378 let key = captures.get(1).and_then(|cap| {
383 str::from_utf8(cap.as_bytes())
384 .ok()
385 .map(|token| DirectiveKey::from_lowercase_owned(token.to_lowercase()))
386 });
387 if key.is_none() {
388 continue;
389 }
390 let value = captures
393 .get(2)
394 .map(|cap| DirectiveValue(cap.as_bytes().to_vec()));
395 directives.insert(key.unwrap(), value);
396 }
397 }
398 Some(CacheControl {
399 directives,
400 allow_float_seconds: false,
401 })
402 }
403
404 pub fn with_float_seconds(mut self) -> Self {
410 self.allow_float_seconds = true;
411 self
412 }
413
414 pub fn from_headers_named(header_name: &str, headers: &http::HeaderMap) -> Option<Self> {
416 if !headers.contains_key(header_name) {
417 return None;
418 }
419
420 Self::from_headers(headers.get_all(header_name))
421 }
422
423 pub fn from_req_headers_named(header_name: &str, req_header: &ReqHeader) -> Option<Self> {
425 Self::from_headers_named(header_name, &req_header.headers)
426 }
427
428 pub fn from_req_headers(req_header: &ReqHeader) -> Option<Self> {
430 Self::from_req_headers_named("cache-control", req_header)
431 }
432
433 pub fn from_resp_headers_named(header_name: &str, resp_header: &RespHeader) -> Option<Self> {
435 Self::from_headers_named(header_name, &resp_header.headers)
436 }
437
438 pub fn from_resp_headers(resp_header: &RespHeader) -> Option<Self> {
440 Self::from_resp_headers_named("cache-control", resp_header)
441 }
442
443 pub fn has_key(&self, key: &str) -> bool {
453 self.has_directive(&DirectiveKey::from_lowercase(key))
454 }
455
456 pub fn has_directive(&self, key: &DirectiveKey) -> bool {
458 self.directives.contains_key(key)
459 }
460
461 pub fn public(&self) -> bool {
463 self.has_directive(&DirectiveKey::Public)
464 }
465
466 fn has_key_without_value(&self, key: &DirectiveKey) -> bool {
468 matches!(self.directives.get(key), Some(None))
469 }
470
471 pub fn private(&self) -> bool {
478 self.has_key_without_value(&DirectiveKey::Private)
479 }
480
481 fn get_field_names(&self, key: &DirectiveKey) -> Option<ListValueIter<'_>> {
482 let value = self.directives.get(key)?.as_ref()?;
483 Some(ListValueIter::from(value))
484 }
485
486 pub fn private_field_names(&self) -> Option<ListValueIter<'_>> {
488 self.get_field_names(&DirectiveKey::Private)
489 }
490
491 pub fn no_cache(&self) -> bool {
493 self.has_key_without_value(&DirectiveKey::NoCache)
494 }
495
496 pub fn no_cache_field_names(&self) -> Option<ListValueIter<'_>> {
498 self.get_field_names(&DirectiveKey::NoCache)
499 }
500
501 pub fn no_store(&self) -> bool {
503 self.has_directive(&DirectiveKey::NoStore)
504 }
505
506 fn parse_delta_seconds(&self, key: &DirectiveKey) -> Result<Option<u32>> {
507 if let Some(Some(dir_value)) = self.directives.get(key) {
508 let value = if self.allow_float_seconds {
509 dir_value.parse_as_delta_seconds_floor()?
510 } else {
511 dir_value.parse_as_delta_seconds()?
512 };
513 Ok(Some(value))
514 } else {
515 Ok(None)
516 }
517 }
518
519 pub fn max_age(&self) -> Result<Option<u32>> {
521 self.parse_delta_seconds(&DirectiveKey::MaxAge)
522 }
523
524 pub fn s_maxage(&self) -> Result<Option<u32>> {
526 self.parse_delta_seconds(&DirectiveKey::SMaxAge)
527 }
528
529 pub fn stale_while_revalidate(&self) -> Result<Option<u32>> {
531 self.parse_delta_seconds(&DirectiveKey::StaleWhileRevalidate)
532 }
533
534 pub fn stale_if_error(&self) -> Result<Option<u32>> {
536 self.parse_delta_seconds(&DirectiveKey::StaleIfError)
537 }
538
539 pub fn must_revalidate(&self) -> bool {
541 self.has_directive(&DirectiveKey::MustRevalidate)
542 }
543
544 pub fn proxy_revalidate(&self) -> bool {
546 self.has_directive(&DirectiveKey::ProxyRevalidate)
547 }
548
549 pub fn only_if_cached(&self) -> bool {
551 self.has_directive(&DirectiveKey::OnlyIfCached)
552 }
553}
554
555impl InterpretCacheControl for CacheControl {
556 fn is_cacheable(&self) -> Cacheable {
557 if self.no_store() || self.private() {
558 return Cacheable::No;
559 }
560 if self.has_directive(&DirectiveKey::SMaxAge)
561 || self.has_directive(&DirectiveKey::MaxAge)
562 || self.public()
563 {
564 return Cacheable::Yes;
565 }
566 Cacheable::Default
567 }
568
569 fn allow_caching_authorized_req(&self) -> bool {
570 self.must_revalidate() || self.public() || self.has_directive(&DirectiveKey::SMaxAge)
574 }
575
576 fn fresh_duration(&self) -> Option<Duration> {
577 if self.no_cache() {
578 return Some(Duration::ZERO);
580 }
581 let seconds = self
582 .s_maxage()
583 .ok()?
584 .or_else(|| self.max_age().unwrap_or(None))
586 .map(|duration| Duration::from_secs(duration as u64))?;
587 Some(seconds)
588 }
589
590 fn serve_stale_while_revalidate_duration(&self) -> Option<Duration> {
591 if self.must_revalidate()
594 || self.proxy_revalidate()
595 || self.has_directive(&DirectiveKey::SMaxAge)
596 {
597 return Some(Duration::ZERO);
598 }
599 self.stale_while_revalidate()
600 .unwrap_or(None)
601 .map(|secs| Duration::from_secs(secs as u64))
602 }
603
604 fn serve_stale_if_error_duration(&self) -> Option<Duration> {
605 if self.must_revalidate()
606 || self.proxy_revalidate()
607 || self.has_directive(&DirectiveKey::SMaxAge)
608 {
609 return Some(Duration::ZERO);
610 }
611 self.stale_if_error()
612 .unwrap_or(None)
613 .map(|secs| Duration::from_secs(secs as u64))
614 }
615
616 fn strip_private_headers(&self, resp_header: &mut ResponseHeader) {
618 fn strip_listed_headers(resp: &mut ResponseHeader, field_names: ListValueIter) {
619 for name in field_names {
620 if let Ok(header) = HeaderName::from_bytes(name) {
621 resp.remove_header(&header);
622 }
623 }
624 }
625
626 if let Some(headers) = self.private_field_names() {
627 strip_listed_headers(resp_header, headers);
628 }
629 if let Some(headers) = self.no_cache_field_names() {
635 strip_listed_headers(resp_header, headers);
636 }
637 }
638}
639
640pub trait InterpretCacheControl {
647 fn is_cacheable(&self) -> Cacheable;
653
654 fn allow_caching_authorized_req(&self) -> bool;
657
658 fn fresh_duration(&self) -> Option<Duration>;
663
664 fn serve_stale_while_revalidate_duration(&self) -> Option<Duration>;
673
674 fn serve_stale_if_error_duration(&self) -> Option<Duration>;
683
684 fn strip_private_headers(&self, resp_header: &mut ResponseHeader);
687}
688
689#[cfg(test)]
690mod tests {
691 use super::*;
692 use http::header::CACHE_CONTROL;
693 use http::{request, response};
694
695 fn build_response(cc_key: HeaderName, cc_value: &str) -> response::Parts {
696 let (parts, _) = response::Builder::new()
697 .header(cc_key, cc_value)
698 .body(())
699 .unwrap()
700 .into_parts();
701 parts
702 }
703
704 #[test]
705 fn test_simple_cache_control() {
706 let resp = build_response(CACHE_CONTROL, "public, max-age=10000");
707 let cc = CacheControl::from_resp_headers(&resp).unwrap();
708 assert!(cc.public());
709 assert_eq!(cc.max_age().unwrap().unwrap(), 10000);
710 }
711
712 #[test]
713 fn test_private_cache_control() {
714 let resp = build_response(CACHE_CONTROL, "private");
715 let cc = CacheControl::from_resp_headers(&resp).unwrap();
716
717 assert!(cc.private());
718 assert!(cc.max_age().unwrap().is_none());
719 }
720
721 #[test]
722 fn test_directives_across_header_lines() {
723 let (parts, _) = response::Builder::new()
724 .header(CACHE_CONTROL, "public,")
725 .header("cache-Control", "max-age=10000")
726 .body(())
727 .unwrap()
728 .into_parts();
729 let cc = CacheControl::from_resp_headers(&parts).unwrap();
730
731 assert!(cc.public());
732 assert_eq!(cc.max_age().unwrap().unwrap(), 10000);
733 }
734
735 #[test]
736 fn test_recognizes_semicolons_as_delimiters() {
737 let resp = build_response(CACHE_CONTROL, "public; max-age=0");
738 let cc = CacheControl::from_resp_headers(&resp).unwrap();
739
740 assert!(cc.public());
741 assert_eq!(cc.max_age().unwrap().unwrap(), 0);
742 }
743
744 #[test]
745 fn test_unknown_directives() {
746 let resp = build_response(CACHE_CONTROL, "public,random1=random2, rand3=\"\"");
747 let cc = CacheControl::from_resp_headers(&resp).unwrap();
748 let mut directive_iter = cc.directives.iter();
749
750 let first = directive_iter.next().unwrap();
751 assert_eq!(first.0, &"public");
752 assert!(first.1.is_none());
753
754 let second = directive_iter.next().unwrap();
755 assert_eq!(second.0, &"random1");
756 assert_eq!(second.1.as_ref().unwrap().0, "random2".as_bytes());
757
758 let third = directive_iter.next().unwrap();
759 assert_eq!(third.0, &"rand3");
760 assert_eq!(third.1.as_ref().unwrap().0, "\"\"".as_bytes());
761
762 assert!(directive_iter.next().is_none());
763 }
764
765 #[test]
766 fn test_case_insensitive_directive_keys() {
767 let resp = build_response(
768 CACHE_CONTROL,
769 "Public=\"something\", mAx-AGe=\"10000\", foo=cRaZyCaSe, bAr=\"inQuotes\"",
770 );
771 let cc = CacheControl::from_resp_headers(&resp).unwrap();
772
773 assert!(cc.public());
774 assert_eq!(cc.max_age().unwrap().unwrap(), 10000);
775
776 let mut directive_iter = cc.directives.iter();
777 let first = directive_iter.next().unwrap();
778 assert_eq!(first.0, &"public");
779 assert_eq!(first.1.as_ref().unwrap().0, "\"something\"".as_bytes());
780
781 let second = directive_iter.next().unwrap();
782 assert_eq!(second.0, &"max-age");
783 assert_eq!(second.1.as_ref().unwrap().0, "\"10000\"".as_bytes());
784
785 let third = directive_iter.next().unwrap();
787 assert_eq!(third.0, &"foo");
788 assert_eq!(third.1.as_ref().unwrap().0, "cRaZyCaSe".as_bytes());
789
790 let fourth = directive_iter.next().unwrap();
791 assert_eq!(fourth.0, &"bar");
792 assert_eq!(fourth.1.as_ref().unwrap().0, "\"inQuotes\"".as_bytes());
793
794 assert!(directive_iter.next().is_none());
795 }
796
797 #[test]
798 fn test_non_ascii() {
799 let resp = build_response(CACHE_CONTROL, "püblic=💖, max-age=\"💯\"");
800 let cc = CacheControl::from_resp_headers(&resp).unwrap();
801
802 assert!(!cc.public());
804 assert_eq!(
805 cc.max_age().unwrap_err().context.unwrap().to_string(),
806 "could not parse value as u32"
807 );
808
809 let mut directive_iter = cc.directives.iter();
810 let first = directive_iter.next().unwrap();
811 assert_eq!(first.0, &"püblic");
812 assert_eq!(first.1.as_ref().unwrap().0, "💖".as_bytes());
813
814 let second = directive_iter.next().unwrap();
815 assert_eq!(second.0, &"max-age");
816 assert_eq!(second.1.as_ref().unwrap().0, "\"💯\"".as_bytes());
817
818 assert!(directive_iter.next().is_none());
819 }
820
821 #[test]
822 fn test_non_utf8_key() {
823 let mut resp = response::Builder::new().body(()).unwrap();
824 resp.headers_mut().insert(
825 CACHE_CONTROL,
826 HeaderValue::from_bytes(b"bar\xFF=\"baz\", a=b").unwrap(),
827 );
828 let (parts, _) = resp.into_parts();
829 let cc = CacheControl::from_resp_headers(&parts).unwrap();
830
831 let mut directive_iter = cc.directives.iter();
833 let first = directive_iter.next().unwrap();
834 assert_eq!(first.0, &"a");
835 assert_eq!(first.1.as_ref().unwrap().0, "b".as_bytes());
836
837 assert!(directive_iter.next().is_none());
838 }
839
840 #[test]
841 fn test_non_utf8_value() {
842 let mut resp = response::Builder::new().body(()).unwrap();
844 resp.headers_mut().insert(
845 CACHE_CONTROL,
846 HeaderValue::from_bytes(b"max-age=ba\xFFr, bar=\"baz\xFF\", a=b").unwrap(),
847 );
848 let (parts, _) = resp.into_parts();
849 let cc = CacheControl::from_resp_headers(&parts).unwrap();
850
851 assert_eq!(
852 cc.max_age().unwrap_err().context.unwrap().to_string(),
853 "could not parse value as utf8"
854 );
855
856 let mut directive_iter = cc.directives.iter();
857
858 let first = directive_iter.next().unwrap();
859 assert_eq!(first.0, &"max-age");
860 assert_eq!(first.1.as_ref().unwrap().0, b"ba\xFFr");
861
862 let second = directive_iter.next().unwrap();
863 assert_eq!(second.0, &"bar");
864 assert_eq!(second.1.as_ref().unwrap().0, b"\"baz\xFF\"");
865
866 let third = directive_iter.next().unwrap();
867 assert_eq!(third.0, &"a");
868 assert_eq!(third.1.as_ref().unwrap().0, "b".as_bytes());
869
870 assert!(directive_iter.next().is_none());
871 }
872
873 #[test]
874 fn test_age_overflow() {
875 let resp = build_response(
876 CACHE_CONTROL,
877 "max-age=-99999999999999999999999999, s-maxage=99999999999999999999999999",
878 );
879 let cc = CacheControl::from_resp_headers(&resp).unwrap();
880
881 assert_eq!(
882 cc.s_maxage().unwrap().unwrap(),
883 DELTA_SECONDS_OVERFLOW_VALUE
884 );
885 assert_eq!(
887 cc.max_age().unwrap_err().context.unwrap().to_string(),
888 "could not parse value as u32"
889 );
890 }
891
892 #[test]
893 fn test_fresh_sec() {
894 let resp = build_response(CACHE_CONTROL, "");
895 let cc = CacheControl::from_resp_headers(&resp).unwrap();
896 assert!(cc.fresh_duration().is_none());
897
898 let resp = build_response(CACHE_CONTROL, "max-age=12345");
899 let cc = CacheControl::from_resp_headers(&resp).unwrap();
900 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(12345));
901
902 let resp = build_response(CACHE_CONTROL, "max-age=99999,s-maxage=123");
903 let cc = CacheControl::from_resp_headers(&resp).unwrap();
904 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(123));
906 }
907
908 #[test]
909 fn test_cacheability() {
910 let resp = build_response(CACHE_CONTROL, "");
911 let cc = CacheControl::from_resp_headers(&resp).unwrap();
912 assert_eq!(cc.is_cacheable(), Cacheable::Default);
913
914 let resp = build_response(CACHE_CONTROL, "private, max-age=12345");
916 let cc = CacheControl::from_resp_headers(&resp).unwrap();
917 assert_eq!(cc.is_cacheable(), Cacheable::No);
918
919 let resp = build_response(CACHE_CONTROL, "no-store, max-age=12345");
920 let cc = CacheControl::from_resp_headers(&resp).unwrap();
921 assert_eq!(cc.is_cacheable(), Cacheable::No);
922
923 let resp = build_response(CACHE_CONTROL, "public");
925 let cc = CacheControl::from_resp_headers(&resp).unwrap();
926 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
927
928 let resp = build_response(CACHE_CONTROL, "max-age=0");
929 let cc = CacheControl::from_resp_headers(&resp).unwrap();
930 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
931 }
932
933 #[test]
934 fn test_no_cache() {
935 let resp = build_response(CACHE_CONTROL, "no-cache, max-age=12345");
936 let cc = CacheControl::from_resp_headers(&resp).unwrap();
937 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
938 assert_eq!(cc.fresh_duration().unwrap(), Duration::ZERO);
939 }
940
941 #[test]
942 fn test_no_cache_field_names() {
943 let resp = build_response(CACHE_CONTROL, "no-cache=\"set-cookie\", max-age=12345");
944 let cc = CacheControl::from_resp_headers(&resp).unwrap();
945 assert!(!cc.private());
946 assert_eq!(cc.is_cacheable(), Cacheable::Yes);
947 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(12345));
948 let mut field_names = cc.no_cache_field_names().unwrap();
949 assert_eq!(
950 str::from_utf8(field_names.next().unwrap()).unwrap(),
951 "set-cookie"
952 );
953 assert!(field_names.next().is_none());
954
955 let mut resp = response::Builder::new().body(()).unwrap();
956 resp.headers_mut().insert(
957 CACHE_CONTROL,
958 HeaderValue::from_bytes(
959 b"private=\"\", no-cache=\"a\xFF, set-cookie, Baz\x09 , c,d ,, \"",
960 )
961 .unwrap(),
962 );
963 let (parts, _) = resp.into_parts();
964 let cc = CacheControl::from_resp_headers(&parts).unwrap();
965 let mut field_names = cc.private_field_names().unwrap();
966 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "");
967 assert!(field_names.next().is_none());
968 let mut field_names = cc.no_cache_field_names().unwrap();
969 assert!(str::from_utf8(field_names.next().unwrap()).is_err());
970 assert_eq!(
971 str::from_utf8(field_names.next().unwrap()).unwrap(),
972 "set-cookie"
973 );
974 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "Baz");
975 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "c");
976 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "d");
977 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "");
978 assert_eq!(str::from_utf8(field_names.next().unwrap()).unwrap(), "");
979 assert!(field_names.next().is_none());
980 }
981
982 #[test]
983 fn test_strip_private_headers() {
984 let mut resp = ResponseHeader::build(200, None).unwrap();
985 resp.append_header(
986 CACHE_CONTROL,
987 "no-cache=\"x-private-header\", max-age=12345",
988 )
989 .unwrap();
990 resp.append_header("X-Private-Header", "dropped").unwrap();
991
992 let cc = CacheControl::from_resp_headers(&resp).unwrap();
993 cc.strip_private_headers(&mut resp);
994 assert!(!resp.headers.contains_key("X-Private-Header"));
995 }
996
997 #[test]
998 fn test_stale_while_revalidate() {
999 let resp = build_response(CACHE_CONTROL, "max-age=12345, stale-while-revalidate=5");
1000 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1001 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 5);
1002 assert_eq!(
1003 cc.serve_stale_while_revalidate_duration().unwrap(),
1004 Duration::from_secs(5)
1005 );
1006 assert!(cc.serve_stale_if_error_duration().is_none());
1007 }
1008
1009 #[test]
1010 fn test_stale_if_error() {
1011 let resp = build_response(CACHE_CONTROL, "max-age=12345, stale-if-error=3600");
1012 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1013 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 3600);
1014 assert_eq!(
1015 cc.serve_stale_if_error_duration().unwrap(),
1016 Duration::from_secs(3600)
1017 );
1018 assert!(cc.serve_stale_while_revalidate_duration().is_none());
1019 }
1020
1021 #[test]
1022 fn test_must_revalidate() {
1023 let resp = build_response(
1024 CACHE_CONTROL,
1025 "max-age=12345, stale-while-revalidate=60, stale-if-error=30, must-revalidate",
1026 );
1027 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1028 assert!(cc.must_revalidate());
1029 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 60);
1030 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 30);
1031 assert_eq!(
1032 cc.serve_stale_while_revalidate_duration().unwrap(),
1033 Duration::ZERO
1034 );
1035 assert_eq!(cc.serve_stale_if_error_duration().unwrap(), Duration::ZERO);
1036 }
1037
1038 #[test]
1039 fn test_proxy_revalidate() {
1040 let resp = build_response(
1041 CACHE_CONTROL,
1042 "max-age=12345, stale-while-revalidate=60, stale-if-error=30, proxy-revalidate",
1043 );
1044 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1045 assert!(cc.proxy_revalidate());
1046 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 60);
1047 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 30);
1048 assert_eq!(
1049 cc.serve_stale_while_revalidate_duration().unwrap(),
1050 Duration::ZERO
1051 );
1052 assert_eq!(cc.serve_stale_if_error_duration().unwrap(), Duration::ZERO);
1053 }
1054
1055 #[test]
1056 fn test_s_maxage_stale() {
1057 let resp = build_response(
1058 CACHE_CONTROL,
1059 "s-maxage=0, stale-while-revalidate=60, stale-if-error=30",
1060 );
1061 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1062 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 60);
1063 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 30);
1064 assert_eq!(
1065 cc.serve_stale_while_revalidate_duration().unwrap(),
1066 Duration::ZERO
1067 );
1068 assert_eq!(cc.serve_stale_if_error_duration().unwrap(), Duration::ZERO);
1069 }
1070
1071 #[test]
1072 fn test_authorized_request() {
1073 let resp = build_response(CACHE_CONTROL, "max-age=10");
1074 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1075 assert!(!cc.allow_caching_authorized_req());
1076
1077 let resp = build_response(CACHE_CONTROL, "s-maxage=10");
1078 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1079 assert!(cc.allow_caching_authorized_req());
1080
1081 let resp = build_response(CACHE_CONTROL, "public");
1082 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1083 assert!(cc.allow_caching_authorized_req());
1084
1085 let resp = build_response(CACHE_CONTROL, "must-revalidate, max-age=0");
1086 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1087 assert!(cc.allow_caching_authorized_req());
1088
1089 let resp = build_response(CACHE_CONTROL, "");
1090 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1091 assert!(!cc.allow_caching_authorized_req());
1092 }
1093
1094 fn build_request(cc_key: HeaderName, cc_value: &str) -> request::Parts {
1095 let (parts, _) = request::Builder::new()
1096 .header(cc_key, cc_value)
1097 .body(())
1098 .unwrap()
1099 .into_parts();
1100 parts
1101 }
1102
1103 #[test]
1104 fn test_request_only_if_cached() {
1105 let req = build_request(CACHE_CONTROL, "only-if-cached=1");
1106 let cc = CacheControl::from_req_headers(&req).unwrap();
1107 assert!(cc.only_if_cached())
1108 }
1109
1110 #[test]
1111 fn test_parse_as_delta_seconds_floor() {
1112 let v = DirectiveValue(b"10".to_vec());
1114 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 10);
1115
1116 let v = DirectiveValue(b"\"10\"".to_vec());
1117 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 10);
1118
1119 let v = DirectiveValue(b"\"1.5\"".to_vec());
1121 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 1);
1122
1123 let v = DirectiveValue(b"0".to_vec());
1124 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 0);
1125
1126 let v = DirectiveValue(b"99999999999999999999".to_vec());
1128 assert_eq!(
1129 v.parse_as_delta_seconds_floor().unwrap(),
1130 DELTA_SECONDS_OVERFLOW_VALUE
1131 );
1132
1133 let v = DirectiveValue(b"1.5".to_vec());
1135 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 1);
1136
1137 let v = DirectiveValue(b"1.9".to_vec());
1138 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 1);
1139
1140 let v = DirectiveValue(b"0.5".to_vec());
1141 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 0);
1142
1143 let v = DirectiveValue(b"3600.0".to_vec());
1144 assert_eq!(v.parse_as_delta_seconds_floor().unwrap(), 3600);
1145
1146 let v = DirectiveValue(b"99999999999.5".to_vec());
1148 assert_eq!(
1149 v.parse_as_delta_seconds_floor().unwrap(),
1150 DELTA_SECONDS_OVERFLOW_VALUE
1151 );
1152
1153 assert!(DirectiveValue(b"-1".to_vec())
1155 .parse_as_delta_seconds_floor()
1156 .is_err());
1157 assert!(DirectiveValue(b"-1.5".to_vec())
1158 .parse_as_delta_seconds_floor()
1159 .is_err());
1160
1161 assert!(DirectiveValue(b"NaN".to_vec())
1163 .parse_as_delta_seconds_floor()
1164 .is_err());
1165 assert!(DirectiveValue(b"inf".to_vec())
1166 .parse_as_delta_seconds_floor()
1167 .is_err());
1168 assert!(DirectiveValue(b"abc".to_vec())
1169 .parse_as_delta_seconds_floor()
1170 .is_err());
1171
1172 let v = DirectiveValue(b"ba\xFFr".to_vec());
1174 assert_eq!(
1175 v.parse_as_delta_seconds_floor()
1176 .unwrap_err()
1177 .context
1178 .unwrap()
1179 .to_string(),
1180 "could not parse value as utf8",
1181 );
1182 }
1183
1184 #[test]
1185 fn test_cache_control_allow_float_seconds_non_utf8_value() {
1186 let mut resp = response::Builder::new().body(()).unwrap();
1189 resp.headers_mut().insert(
1190 CACHE_CONTROL,
1191 HeaderValue::from_bytes(b"max-age=ba\xFFr").unwrap(),
1192 );
1193 let (parts, _) = resp.into_parts();
1194 let cc = CacheControl::from_resp_headers(&parts)
1195 .unwrap()
1196 .with_float_seconds();
1197 assert_eq!(
1198 cc.max_age().unwrap_err().context.unwrap().to_string(),
1199 "could not parse value as utf8",
1200 );
1201 }
1202
1203 #[test]
1204 fn test_cache_control_allow_float_seconds_default_off() {
1205 let resp = build_response(CACHE_CONTROL, "max-age=10.7");
1209 let cc = CacheControl::from_resp_headers(&resp).unwrap();
1210 assert!(!cc.allow_float_seconds);
1211 assert!(cc.max_age().is_err());
1212 assert!(cc.fresh_duration().is_none());
1213 }
1214
1215 #[test]
1216 fn test_cache_control_with_float_seconds() {
1217 let resp = build_response(CACHE_CONTROL, "max-age=10.7");
1219 let cc = CacheControl::from_resp_headers(&resp)
1220 .unwrap()
1221 .with_float_seconds();
1222 assert!(cc.allow_float_seconds);
1223 assert_eq!(cc.max_age().unwrap().unwrap(), 10);
1224 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(10));
1225
1226 let resp = build_response(CACHE_CONTROL, "s-maxage=3600.99, max-age=1800");
1228 let cc = CacheControl::from_resp_headers(&resp)
1229 .unwrap()
1230 .with_float_seconds();
1231 assert_eq!(cc.s_maxage().unwrap().unwrap(), 3600);
1232 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(3600));
1233
1234 let resp = build_response(
1236 CACHE_CONTROL,
1237 "max-age=10, stale-while-revalidate=60.5, stale-if-error=30.9",
1238 );
1239 let cc = CacheControl::from_resp_headers(&resp)
1240 .unwrap()
1241 .with_float_seconds();
1242 assert_eq!(cc.stale_while_revalidate().unwrap().unwrap(), 60);
1243 assert_eq!(cc.stale_if_error().unwrap().unwrap(), 30);
1244 assert_eq!(
1245 cc.serve_stale_while_revalidate_duration().unwrap(),
1246 Duration::from_secs(60)
1247 );
1248 assert_eq!(
1249 cc.serve_stale_if_error_duration().unwrap(),
1250 Duration::from_secs(30)
1251 );
1252
1253 let resp = build_response(CACHE_CONTROL, "max-age=12345");
1255 let cc = CacheControl::from_resp_headers(&resp)
1256 .unwrap()
1257 .with_float_seconds();
1258 assert_eq!(cc.fresh_duration().unwrap(), Duration::from_secs(12345));
1259
1260 let resp = build_response(CACHE_CONTROL, "max-age=abc");
1262 let cc = CacheControl::from_resp_headers(&resp)
1263 .unwrap()
1264 .with_float_seconds();
1265 assert!(cc.max_age().is_err());
1266
1267 let resp = build_response(CACHE_CONTROL, "max-age=-1.5");
1268 let cc = CacheControl::from_resp_headers(&resp)
1269 .unwrap()
1270 .with_float_seconds();
1271 assert!(cc.max_age().is_err());
1272 }
1273}