1use super::*;
18use crate::cache_control::{CacheControl, Cacheable, InterpretCacheControl};
19use crate::RespCacheable::*;
20
21use cache_control::DELTA_SECONDS_OVERFLOW_VALUE;
22use http::{header, HeaderValue};
23use httpdate::HttpDate;
24use log::debug;
25use pingora_http::RequestHeader;
26
27pub fn request_cacheable(req_header: &ReqHeader) -> bool {
29 matches!(req_header.method, Method::GET | Method::HEAD)
31}
32
33pub fn resp_cacheable(
38 cache_control: Option<&CacheControl>,
39 mut resp_header: ResponseHeader,
40 authorization_present: bool,
41 defaults: &CacheMetaDefaults,
42) -> RespCacheable {
43 let now = SystemTime::now();
44 let expire_time = calculate_fresh_until(
45 now,
46 cache_control,
47 &resp_header,
48 authorization_present,
49 defaults,
50 );
51 if let Some(fresh_until) = expire_time {
52 let (stale_while_revalidate_duration, stale_if_error_duration) =
53 calculate_serve_stale_durations(cache_control, defaults);
54
55 if let Some(cc) = cache_control {
56 cc.strip_private_headers(&mut resp_header);
57 }
58 return Cacheable(CacheMeta::new(
59 fresh_until,
60 now,
61 stale_while_revalidate_duration,
62 stale_if_error_duration,
63 resp_header,
64 ));
65 }
66 Uncacheable(NoCacheReason::OriginNotCache)
67}
68
69pub fn calculate_fresh_until(
73 now: SystemTime,
74 cache_control: Option<&CacheControl>,
75 resp_header: &RespHeader,
76 authorization_present: bool,
77 defaults: &CacheMetaDefaults,
78) -> Option<SystemTime> {
79 fn freshness_ttl_to_time(now: SystemTime, fresh: Duration) -> Option<SystemTime> {
80 if fresh.is_zero() {
81 now.checked_sub(Duration::from_secs(1))
83 } else {
84 now.checked_add(fresh)
85 }
86 }
87
88 if authorization_present {
90 let uncacheable = cache_control
91 .as_ref()
92 .is_none_or(|cc| !cc.allow_caching_authorized_req());
93 if uncacheable {
94 return None;
95 }
96 }
97
98 let uncacheable = cache_control
99 .as_ref()
100 .is_some_and(|cc| cc.is_cacheable() == Cacheable::No);
101 if uncacheable {
102 return None;
103 }
104
105 cache_control
107 .and_then(|cc| {
108 cc.fresh_duration()
109 .and_then(|ttl| freshness_ttl_to_time(now, ttl))
110 })
111 .or_else(|| calculate_expires_header_time(resp_header))
112 .or_else(|| {
113 defaults
114 .fresh_sec(resp_header.status)
115 .and_then(|ttl| freshness_ttl_to_time(now, ttl))
116 })
117}
118
119pub fn calculate_expires_header_time(resp_header: &RespHeader) -> Option<SystemTime> {
121 fn parse_expires_value(expires_value: &HeaderValue) -> Option<SystemTime> {
127 let expires = expires_value.to_str().ok()?;
128 Some(SystemTime::from(
129 expires
130 .parse::<HttpDate>()
131 .map_err(|e| debug!("Invalid HttpDate in Expires: {}, error: {}", expires, e))
132 .ok()?,
133 ))
134 }
135
136 let mut expires_iter = resp_header.headers.get_all("expires").iter();
137 let expires_header = expires_iter.next();
138 if expires_header.is_none() || expires_iter.next().is_some() {
139 return None;
140 }
141 parse_expires_value(expires_header.unwrap()).or(Some(SystemTime::UNIX_EPOCH))
142}
143
144pub fn calculate_serve_stale_durations(
146 cache_control: Option<&impl InterpretCacheControl>,
147 defaults: &CacheMetaDefaults,
148) -> (u32, u32) {
149 let serve_stale_while_revalidate = cache_control
150 .and_then(|cc| cc.serve_stale_while_revalidate_duration())
151 .unwrap_or_else(|| Duration::from_secs(defaults.serve_stale_while_revalidate_sec() as u64));
152 let serve_stale_if_error = cache_control
153 .and_then(|cc| cc.serve_stale_if_error_duration())
154 .unwrap_or_else(|| Duration::from_secs(defaults.serve_stale_if_error_sec() as u64));
155 (
156 serve_stale_while_revalidate
157 .as_secs()
158 .try_into()
159 .unwrap_or(DELTA_SECONDS_OVERFLOW_VALUE),
160 serve_stale_if_error
161 .as_secs()
162 .try_into()
163 .unwrap_or(DELTA_SECONDS_OVERFLOW_VALUE),
164 )
165}
166
167pub mod upstream {
169 use super::*;
170
171 pub fn request_filter(req: &mut RequestHeader, meta: Option<&CacheMeta>) {
181 if req.method == Method::HEAD {
183 req.set_method(Method::GET);
184 }
185
186 req.remove_header(&header::IF_MATCH);
189 req.remove_header(&header::IF_NONE_MATCH);
190 req.remove_header(&header::IF_MODIFIED_SINCE);
191 req.remove_header(&header::IF_UNMODIFIED_SINCE);
192 req.remove_header(&header::IF_RANGE);
194
195 req.remove_header(&header::RANGE);
197
198 if let Some(m) = meta {
200 if let Some(since) = m.headers().get(&header::LAST_MODIFIED) {
203 req.insert_header(header::IF_MODIFIED_SINCE, since).unwrap();
204 }
205 if let Some(etag) = m.headers().get(&header::ETAG) {
206 req.insert_header(header::IF_NONE_MATCH, etag).unwrap();
207 }
208 }
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215 use crate::RespCacheable::Cacheable;
216 use http::header::{HeaderName, CACHE_CONTROL, EXPIRES, SET_COOKIE};
217 use http::StatusCode;
218 use httpdate::fmt_http_date;
219
220 fn init_log() {
221 let _ = env_logger::builder().is_test(true).try_init();
222 }
223
224 const DEFAULTS: CacheMetaDefaults = CacheMetaDefaults::new(
225 |status| {
226 match status {
227 StatusCode::OK => Some(10),
228 StatusCode::NOT_FOUND => Some(5),
229 StatusCode::PARTIAL_CONTENT => None,
230 _ => Some(1),
231 }
232 .map(Duration::from_secs)
233 },
234 0,
235 DELTA_SECONDS_OVERFLOW_VALUE, );
237
238 const BYPASS_CACHE_DEFAULTS: CacheMetaDefaults = CacheMetaDefaults::new(|_| None, 0, 0);
240
241 fn build_response(status: u16, headers: &[(HeaderName, &str)]) -> ResponseHeader {
242 let mut header = ResponseHeader::build(status, Some(headers.len())).unwrap();
243 for (k, v) in headers {
244 header.append_header(k.to_string(), *v).unwrap();
245 }
246 header
247 }
248
249 fn resp_cacheable_wrapper(
250 resp: ResponseHeader,
251 defaults: &CacheMetaDefaults,
252 authorization_present: bool,
253 ) -> Option<CacheMeta> {
254 if let Cacheable(meta) = resp_cacheable(
255 CacheControl::from_resp_headers(&resp).as_ref(),
256 resp,
257 authorization_present,
258 defaults,
259 ) {
260 Some(meta)
261 } else {
262 None
263 }
264 }
265
266 #[test]
267 fn test_resp_cacheable() {
268 let meta = resp_cacheable_wrapper(
269 build_response(200, &[(CACHE_CONTROL, "max-age=12345")]),
270 &DEFAULTS,
271 false,
272 );
273
274 let meta = meta.unwrap();
275 assert!(meta.is_fresh(SystemTime::now()));
276 assert!(meta.is_fresh(
277 SystemTime::now()
278 .checked_add(Duration::from_secs(12))
279 .unwrap()
280 ),);
281 assert!(!meta.is_fresh(
282 SystemTime::now()
283 .checked_add(Duration::from_secs(12346))
284 .unwrap()
285 ));
286 }
287
288 #[test]
289 fn test_resp_uncacheable_directives() {
290 let meta = resp_cacheable_wrapper(
291 build_response(200, &[(CACHE_CONTROL, "private, max-age=12345")]),
292 &DEFAULTS,
293 false,
294 );
295 assert!(meta.is_none());
296
297 let meta = resp_cacheable_wrapper(
298 build_response(200, &[(CACHE_CONTROL, "no-store, max-age=12345")]),
299 &DEFAULTS,
300 false,
301 );
302 assert!(meta.is_none());
303 }
304
305 #[test]
306 fn test_resp_cache_authorization() {
307 let meta = resp_cacheable_wrapper(build_response(200, &[]), &DEFAULTS, true);
308 assert!(meta.is_none());
309
310 let meta = resp_cacheable_wrapper(
311 build_response(200, &[(CACHE_CONTROL, "max-age=10")]),
312 &DEFAULTS,
313 true,
314 );
315 assert!(meta.is_none());
316
317 let meta = resp_cacheable_wrapper(
318 build_response(200, &[(CACHE_CONTROL, "s-maxage=10")]),
319 &DEFAULTS,
320 true,
321 );
322 assert!(meta.unwrap().is_fresh(SystemTime::now()));
323
324 let meta = resp_cacheable_wrapper(
325 build_response(200, &[(CACHE_CONTROL, "public, max-age=10")]),
326 &DEFAULTS,
327 true,
328 );
329 assert!(meta.unwrap().is_fresh(SystemTime::now()));
330
331 let meta = resp_cacheable_wrapper(
332 build_response(200, &[(CACHE_CONTROL, "must-revalidate")]),
333 &DEFAULTS,
334 true,
335 );
336 assert!(meta.unwrap().is_fresh(SystemTime::now()));
337 }
338
339 #[test]
340 fn test_resp_zero_max_age() {
341 let meta = resp_cacheable_wrapper(
342 build_response(200, &[(CACHE_CONTROL, "max-age=0, public")]),
343 &DEFAULTS,
344 false,
345 );
346
347 assert!(!meta.unwrap().is_fresh(SystemTime::now()));
349 }
350
351 #[test]
352 fn test_resp_expires() {
353 let five_sec_time = SystemTime::now()
354 .checked_add(Duration::from_secs(5))
355 .unwrap();
356
357 let meta = resp_cacheable_wrapper(
359 build_response(200, &[(EXPIRES, &fmt_http_date(five_sec_time))]),
360 &DEFAULTS,
361 false,
362 );
363
364 let meta = meta.unwrap();
365 assert!(meta.is_fresh(SystemTime::now()));
366 assert!(!meta.is_fresh(
367 SystemTime::now()
368 .checked_add(Duration::from_secs(6))
369 .unwrap()
370 ));
371
372 let meta = resp_cacheable_wrapper(
374 build_response(206, &[(EXPIRES, &fmt_http_date(five_sec_time))]),
375 &DEFAULTS,
376 false,
377 );
378 assert!(meta.is_some());
379 }
380
381 #[test]
382 fn test_resp_past_expires() {
383 let meta = resp_cacheable_wrapper(
385 build_response(200, &[(EXPIRES, "Fri, 15 May 2015 15:34:21 GMT")]),
386 &BYPASS_CACHE_DEFAULTS,
387 false,
388 );
389 assert!(!meta.unwrap().is_fresh(SystemTime::now()));
390 }
391
392 #[test]
393 fn test_resp_nonstandard_expires() {
394 init_log();
396
397 let meta = resp_cacheable_wrapper(
400 build_response(200, &[(EXPIRES, "Mon, 13 Feb 0002 12:00:00 GMT")]),
401 &BYPASS_CACHE_DEFAULTS,
402 false,
403 );
404 assert!(!meta.unwrap().is_fresh(SystemTime::now()));
405
406 let meta = resp_cacheable_wrapper(
407 build_response(200, &[(EXPIRES, "Fri, 01 Dec 99999 16:00:00 GMT")]),
408 &BYPASS_CACHE_DEFAULTS,
409 false,
410 );
411 assert!(!meta.unwrap().is_fresh(SystemTime::now()));
412
413 let meta = resp_cacheable_wrapper(
414 build_response(200, &[(EXPIRES, "0")]),
415 &BYPASS_CACHE_DEFAULTS,
416 false,
417 );
418 assert!(!meta.unwrap().is_fresh(SystemTime::now()));
419 }
420
421 #[test]
422 fn test_resp_multiple_expires() {
423 let five_sec_time = SystemTime::now()
424 .checked_add(Duration::from_secs(5))
425 .unwrap();
426 let ten_sec_time = SystemTime::now()
427 .checked_add(Duration::from_secs(10))
428 .unwrap();
429
430 let meta = resp_cacheable_wrapper(
432 build_response(
433 200,
434 &[
435 (EXPIRES, &fmt_http_date(five_sec_time)),
436 (EXPIRES, &fmt_http_date(ten_sec_time)),
437 ],
438 ),
439 &BYPASS_CACHE_DEFAULTS,
440 false,
441 );
442 assert!(meta.is_none());
443
444 let meta = resp_cacheable_wrapper(
446 build_response(
447 200,
448 &[
449 (EXPIRES, &fmt_http_date(five_sec_time)),
450 (EXPIRES, &fmt_http_date(ten_sec_time)),
451 ],
452 ),
453 &DEFAULTS,
454 false,
455 );
456 assert!(meta.is_some());
457 }
458
459 #[test]
460 fn test_resp_cache_control_with_expires() {
461 let five_sec_time = SystemTime::now()
462 .checked_add(Duration::from_secs(5))
463 .unwrap();
464 let meta = resp_cacheable_wrapper(
466 build_response(
467 200,
468 &[
469 (EXPIRES, &fmt_http_date(five_sec_time)),
470 (CACHE_CONTROL, "max-age=0"),
471 ],
472 ),
473 &DEFAULTS,
474 false,
475 );
476 assert!(!meta.unwrap().is_fresh(SystemTime::now()));
477 }
478
479 #[test]
480 fn test_resp_stale_while_revalidate() {
481 let meta = resp_cacheable_wrapper(
483 build_response(200, &[(CACHE_CONTROL, "max-age=10")]),
484 &DEFAULTS,
485 false,
486 );
487
488 let meta = meta.unwrap();
489 let eleven_sec_time = SystemTime::now()
490 .checked_add(Duration::from_secs(11))
491 .unwrap();
492 assert!(!meta.is_fresh(eleven_sec_time));
493 assert!(!meta.serve_stale_while_revalidate(SystemTime::now()));
494 assert!(!meta.serve_stale_while_revalidate(eleven_sec_time));
495
496 let meta = resp_cacheable_wrapper(
498 build_response(
499 200,
500 &[(CACHE_CONTROL, "max-age=10, stale-while-revalidate=5")],
501 ),
502 &DEFAULTS,
503 false,
504 );
505
506 let meta = meta.unwrap();
507 let eleven_sec_time = SystemTime::now()
508 .checked_add(Duration::from_secs(11))
509 .unwrap();
510 let sixteen_sec_time = SystemTime::now()
511 .checked_add(Duration::from_secs(16))
512 .unwrap();
513 assert!(!meta.is_fresh(eleven_sec_time));
514 assert!(meta.serve_stale_while_revalidate(eleven_sec_time));
515 assert!(!meta.serve_stale_while_revalidate(sixteen_sec_time));
516 }
517
518 #[test]
519 fn test_resp_stale_if_error() {
520 let meta = resp_cacheable_wrapper(
522 build_response(200, &[(CACHE_CONTROL, "max-age=10")]),
523 &DEFAULTS,
524 false,
525 );
526
527 let meta = meta.unwrap();
528 let fifty_years_time = SystemTime::now()
529 .checked_add(Duration::from_secs(86400 * 365 * 50))
530 .unwrap();
531 assert!(!meta.is_fresh(fifty_years_time));
532 assert!(meta.serve_stale_if_error(fifty_years_time));
533
534 let meta = resp_cacheable_wrapper(
536 build_response(
537 200,
538 &[(
539 CACHE_CONTROL,
540 "max-age=10, stale-while-revalidate=5, stale-if-error=60",
541 )],
542 ),
543 &DEFAULTS,
544 false,
545 );
546
547 let meta = meta.unwrap();
548 let eleven_sec_time = SystemTime::now()
549 .checked_add(Duration::from_secs(11))
550 .unwrap();
551 let seventy_sec_time = SystemTime::now()
552 .checked_add(Duration::from_secs(70))
553 .unwrap();
554 assert!(!meta.is_fresh(eleven_sec_time));
555 assert!(meta.serve_stale_if_error(SystemTime::now()));
556 assert!(meta.serve_stale_if_error(eleven_sec_time));
557 assert!(!meta.serve_stale_if_error(seventy_sec_time));
558
559 let meta = resp_cacheable_wrapper(
561 build_response(200, &[(CACHE_CONTROL, "max-age=10, stale-if-error=0")]),
562 &DEFAULTS,
563 false,
564 );
565
566 let meta = meta.unwrap();
567 let eleven_sec_time = SystemTime::now()
568 .checked_add(Duration::from_secs(11))
569 .unwrap();
570 assert!(!meta.is_fresh(eleven_sec_time));
571 assert!(!meta.serve_stale_if_error(eleven_sec_time));
572 }
573
574 #[test]
575 fn test_resp_status_cache_defaults() {
576 let meta = resp_cacheable_wrapper(build_response(200, &[]), &DEFAULTS, false);
578 assert!(meta.is_some());
579
580 let meta = meta.unwrap();
581 assert!(meta.is_fresh(
582 SystemTime::now()
583 .checked_add(Duration::from_secs(9))
584 .unwrap()
585 ));
586 assert!(!meta.is_fresh(
587 SystemTime::now()
588 .checked_add(Duration::from_secs(11))
589 .unwrap()
590 ));
591
592 let meta = resp_cacheable_wrapper(build_response(404, &[]), &DEFAULTS, false);
594 assert!(meta.is_some());
595
596 let meta = meta.unwrap();
597 assert!(meta.is_fresh(
598 SystemTime::now()
599 .checked_add(Duration::from_secs(4))
600 .unwrap()
601 ));
602 assert!(!meta.is_fresh(
603 SystemTime::now()
604 .checked_add(Duration::from_secs(6))
605 .unwrap()
606 ));
607
608 let meta = resp_cacheable_wrapper(build_response(206, &[]), &DEFAULTS, false);
610 assert!(meta.is_none());
611
612 let meta = resp_cacheable_wrapper(
614 build_response(206, &[(CACHE_CONTROL, "public, max-age=10")]),
615 &DEFAULTS,
616 false,
617 );
618 assert!(meta.is_some());
619
620 let meta = meta.unwrap();
621 assert!(meta.is_fresh(
622 SystemTime::now()
623 .checked_add(Duration::from_secs(9))
624 .unwrap()
625 ));
626 assert!(!meta.is_fresh(
627 SystemTime::now()
628 .checked_add(Duration::from_secs(11))
629 .unwrap()
630 ));
631
632 let meta = resp_cacheable_wrapper(build_response(416, &[]), &DEFAULTS, false);
634 assert!(meta.is_some());
635
636 let meta = meta.unwrap();
637 assert!(meta.is_fresh(SystemTime::now()));
638 assert!(!meta.is_fresh(
639 SystemTime::now()
640 .checked_add(Duration::from_secs(2))
641 .unwrap()
642 ));
643 }
644
645 #[test]
646 fn test_resp_cache_no_cache_fields() {
647 let meta = resp_cacheable_wrapper(
649 build_response(
650 200,
651 &[
652 (SET_COOKIE, "my-cookie"),
653 (CACHE_CONTROL, "private=\"something\", max-age=10"),
654 (HeaderName::from_bytes(b"Something").unwrap(), "foo"),
655 ],
656 ),
657 &DEFAULTS,
658 false,
659 );
660 let meta = meta.unwrap();
661 assert!(meta.headers().contains_key(SET_COOKIE));
662 assert!(!meta.headers().contains_key("Something"));
663
664 let meta = resp_cacheable_wrapper(
665 build_response(
666 200,
667 &[
668 (SET_COOKIE, "my-cookie"),
669 (
670 CACHE_CONTROL,
671 "max-age=0, no-cache=\"meta1, SeT-Cookie ,meta2\"",
672 ),
673 (HeaderName::from_bytes(b"meta1").unwrap(), "foo"),
674 ],
675 ),
676 &DEFAULTS,
677 false,
678 );
679 let meta = meta.unwrap();
680 assert!(!meta.headers().contains_key(SET_COOKIE));
681 assert!(!meta.headers().contains_key("meta1"));
682 }
683}