diff options
author | Edward Wang <[email protected]> | 2024-11-01 17:01:59 -0700 |
---|---|---|
committer | Edward Wang <[email protected]> | 2024-11-07 02:15:39 +0000 |
commit | 66b5930abbeb666216b5201240274867fe496f89 (patch) | |
tree | 2d4a1e1956a71d699c54fd482dd090f15d0936f5 | |
parent | 2d110d8e68c261e772e2a8b51f5a3d5a5a9e2bc8 (diff) | |
download | pingora-bleeper-mgumport-ef8c591ce8.tar.gz pingora-bleeper-mgumport-ef8c591ce8.zip |
if-range support for cache rangesbleeper-mgumport-ef8c591ce8
-rw-r--r-- | .bleep | 2 | ||||
-rw-r--r-- | pingora-proxy/src/proxy_cache.rs | 76 |
2 files changed, 77 insertions, 1 deletions
@@ -1 +1 @@ -6917c93caac52f862080f3b1572efdc2e82f21b0
\ No newline at end of file +ef8c591ce876042b96a4efe6331606f73e2f124d
\ No newline at end of file diff --git a/pingora-proxy/src/proxy_cache.rs b/pingora-proxy/src/proxy_cache.rs index ed611f8..f8b0035 100644 --- a/pingora-proxy/src/proxy_cache.rs +++ b/pingora-proxy/src/proxy_cache.rs @@ -936,6 +936,27 @@ pub(crate) mod range_filter { return RangeType::None; }; + // if-range wants to understand if the Last-Modified / ETag value matches exactly for use + // with resumable downloads. + // https://datatracker.ietf.org/doc/html/rfc9110#name-if-range + // Note that the RFC wants strong validation, and suggests that + // "A valid entity-tag can be distinguished from a valid HTTP-date + // by examining the first three characters for a DQUOTE," + // but this current etag matching behavior most closely mirrors nginx. + if let Some(if_range) = req.headers.get(IF_RANGE) { + let ir = if_range.as_bytes(); + let matches = if ir.len() >= 2 && ir.last() == Some(&b'"') { + resp.headers.get(ETAG).is_some_and(|etag| etag == if_range) + } else if let Some(last_modified) = resp.headers.get(LAST_MODIFIED) { + last_modified == if_range + } else { + false + }; + if !matches { + return RangeType::None; + } + } + // TODO: we can also check Accept-Range header from resp. Nginx gives uses the option // see proxy_force_ranges @@ -1015,6 +1036,61 @@ pub(crate) mod range_filter { ); } + #[test] + fn test_if_range() { + const DATE: &str = "Fri, 07 Jul 2023 22:03:29 GMT"; + const ETAG: &str = "\"1234\""; + + fn gen_req() -> RequestHeader { + let mut req = RequestHeader::build(http::Method::GET, b"/", Some(1)).unwrap(); + req.append_header("Range", "bytes=0-1").unwrap(); + req + } + fn gen_resp() -> ResponseHeader { + let mut resp = ResponseHeader::build(200, Some(1)).unwrap(); + resp.append_header("Content-Length", "10").unwrap(); + resp.append_header("Last-Modified", DATE).unwrap(); + resp.append_header("ETag", ETAG).unwrap(); + resp + } + + // matching Last-Modified date + let mut req = gen_req(); + req.insert_header("If-Range", DATE).unwrap(); + let mut resp = gen_resp(); + assert_eq!( + RangeType::new_single(0, 2), + range_header_filter(&req, &mut resp) + ); + + // non-matching date + let mut req = gen_req(); + req.insert_header("If-Range", "Fri, 07 Jul 2023 22:03:25 GMT") + .unwrap(); + let mut resp = gen_resp(); + assert_eq!(RangeType::None, range_header_filter(&req, &mut resp)); + + // match ETag + let mut req = gen_req(); + req.insert_header("If-Range", ETAG).unwrap(); + let mut resp = gen_resp(); + assert_eq!( + RangeType::new_single(0, 2), + range_header_filter(&req, &mut resp) + ); + + // non-matching ETags do not result in range + let mut req = gen_req(); + req.insert_header("If-Range", "\"4567\"").unwrap(); + let mut resp = gen_resp(); + assert_eq!(RangeType::None, range_header_filter(&req, &mut resp)); + + let mut req = gen_req(); + req.insert_header("If-Range", "1234").unwrap(); + let mut resp = gen_resp(); + assert_eq!(RangeType::None, range_header_filter(&req, &mut resp)); + } + pub struct RangeBodyFilter { range: RangeType, current: usize, |