v0.25.0
This commit is contained in:
1 parent
2e4aa4df4d
commit
87e571c170
106 files changed
+17952
-6185
No files matched your search
Vendored
+315
-124
@@ -172,7 +172,14 @@ mod tests {
|
||||
let mut single_range = HeaderMap::new();
|
||||
single_range.insert("range", HeaderValue::from_static("bytes=0-3"));
|
||||
assert_eq!(
|
||||
request_policy(&Method::GET, &single_range, true, "/asset.bin", false, &settings()),
|
||||
request_policy(
|
||||
&Method::GET,
|
||||
&single_range,
|
||||
true,
|
||||
"/asset.bin",
|
||||
false,
|
||||
&settings()
|
||||
),
|
||||
RequestPolicy::Use
|
||||
);
|
||||
assert!(request_cacheable(
|
||||
@@ -185,7 +192,14 @@ mod tests {
|
||||
));
|
||||
single_range.insert("range", HeaderValue::from_static("bytes=0-3,7-9"));
|
||||
assert_eq!(
|
||||
request_policy(&Method::GET, &single_range, true, "/asset.bin", false, &settings()),
|
||||
request_policy(
|
||||
&Method::GET,
|
||||
&single_range,
|
||||
true,
|
||||
"/asset.bin",
|
||||
false,
|
||||
&settings()
|
||||
),
|
||||
RequestPolicy::Bypass("request_range_unsupported")
|
||||
);
|
||||
}
|
||||
@@ -210,11 +224,23 @@ mod tests {
|
||||
fn request_max_stale_is_bounded_and_respects_revalidation_flags() {
|
||||
let unbounded = headers("max-stale");
|
||||
let bounded = headers("max-stale=15");
|
||||
assert_eq!(stale_mode(120, 60, 0, false, false, false, &unbounded), Some(false));
|
||||
assert_eq!(stale_mode(70, 60, 0, false, false, false, &bounded), Some(false));
|
||||
assert_eq!(
|
||||
stale_mode(120, 60, 0, false, false, false, &unbounded),
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(
|
||||
stale_mode(70, 60, 0, false, false, false, &bounded),
|
||||
Some(false)
|
||||
);
|
||||
assert_eq!(stale_mode(80, 60, 0, false, false, false, &bounded), None);
|
||||
assert_eq!(stale_mode(70, 60, 30, false, false, false, &HeaderMap::new()), Some(true));
|
||||
assert_eq!(stale_mode(100, 60, 30, false, false, false, &HeaderMap::new()), None);
|
||||
assert_eq!(
|
||||
stale_mode(70, 60, 30, false, false, false, &HeaderMap::new()),
|
||||
Some(true)
|
||||
);
|
||||
assert_eq!(
|
||||
stale_mode(100, 60, 30, false, false, false, &HeaderMap::new()),
|
||||
None
|
||||
);
|
||||
assert_eq!(stale_mode(70, 60, 30, true, false, false, &unbounded), None);
|
||||
assert_eq!(stale_mode(70, 60, 30, false, true, false, &unbounded), None);
|
||||
}
|
||||
@@ -260,9 +286,21 @@ mod tests {
|
||||
origin_last_modified: None,
|
||||
};
|
||||
assert!(base.can_stale_if_error());
|
||||
assert!(!Revalidation { age: 91, ..base.clone() }.can_stale_if_error());
|
||||
assert!(!Revalidation { must_revalidate: true, ..base.clone() }.can_stale_if_error());
|
||||
assert!(!Revalidation { proxy_revalidate: true, ..base }.can_stale_if_error());
|
||||
assert!(!Revalidation {
|
||||
age: 91,
|
||||
..base.clone()
|
||||
}
|
||||
.can_stale_if_error());
|
||||
assert!(!Revalidation {
|
||||
must_revalidate: true,
|
||||
..base.clone()
|
||||
}
|
||||
.can_stale_if_error());
|
||||
assert!(!Revalidation {
|
||||
proxy_revalidate: true,
|
||||
..base
|
||||
}
|
||||
.can_stale_if_error());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -370,14 +408,10 @@ mod tests {
|
||||
json: true,
|
||||
..Default::default()
|
||||
};
|
||||
let transformed = crate::transform::apply(
|
||||
response,
|
||||
"/data.json",
|
||||
&transform_settings,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let transformed =
|
||||
crate::transform::apply(response, "/data.json", &transform_settings, false)
|
||||
.await
|
||||
.unwrap();
|
||||
let transformed_plan = plan(
|
||||
StatusCode::OK,
|
||||
transformed.headers(),
|
||||
@@ -395,7 +429,9 @@ mod tests {
|
||||
let first = wrapped.into_body().collect().await.unwrap().to_bytes();
|
||||
assert_eq!(&first[..], br#"{"message":"hello"}"#);
|
||||
|
||||
let hit = cache.get(&ticket).expect("transformed representation should be cached");
|
||||
let hit = cache
|
||||
.get(&ticket)
|
||||
.expect("transformed representation should be cached");
|
||||
assert_eq!(hit.response.headers()["etag"], expected_etag);
|
||||
assert_eq!(
|
||||
hit.response.headers()["content-length"].to_str().unwrap(),
|
||||
@@ -452,10 +488,9 @@ mod tests {
|
||||
"cache-control",
|
||||
HeaderValue::from_static("public, max-age=60"),
|
||||
);
|
||||
response.headers_mut().insert(
|
||||
"content-type",
|
||||
HeaderValue::from_str(content_type).unwrap(),
|
||||
);
|
||||
response
|
||||
.headers_mut()
|
||||
.insert("content-type", HeaderValue::from_str(content_type).unwrap());
|
||||
response.headers_mut().insert(
|
||||
"content-length",
|
||||
HeaderValue::from_str(&origin_body.len().to_string()).unwrap(),
|
||||
@@ -488,14 +523,9 @@ mod tests {
|
||||
"json" => transform_settings.json = true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let transformed = crate::transform::apply(
|
||||
response,
|
||||
path,
|
||||
&transform_settings,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let transformed = crate::transform::apply(response, path, &transform_settings, false)
|
||||
.await
|
||||
.unwrap();
|
||||
let transformed_plan = plan(
|
||||
StatusCode::OK,
|
||||
transformed.headers(),
|
||||
@@ -513,9 +543,15 @@ mod tests {
|
||||
);
|
||||
let first = wrapped.into_body().collect().await.unwrap().to_bytes();
|
||||
assert!(!first.is_empty(), "{kind}");
|
||||
assert_ne!(first.as_ref(), origin_body, "{kind} should change the chosen fixture");
|
||||
assert_ne!(
|
||||
first.as_ref(),
|
||||
origin_body,
|
||||
"{kind} should change the chosen fixture"
|
||||
);
|
||||
|
||||
let hit = cache.get(&ticket).expect("transformed representation should be cached");
|
||||
let hit = cache
|
||||
.get(&ticket)
|
||||
.expect("transformed representation should be cached");
|
||||
assert_eq!(hit.response.headers()["etag"], expected_etag, "{kind}");
|
||||
assert_eq!(
|
||||
hit.response.headers()["content-length"],
|
||||
@@ -693,7 +729,9 @@ mod tests {
|
||||
let mut s = Settings::default();
|
||||
s.transient_max_bytes = 128 * 1024;
|
||||
s.cold_queue_max_bytes = 64 * 1024;
|
||||
let first = cache.try_reserve_transient(96 * 1024, &s).expect("first reservation");
|
||||
let first = cache
|
||||
.try_reserve_transient(96 * 1024, &s)
|
||||
.expect("first reservation");
|
||||
assert!(cache.try_reserve_transient(40 * 1024, &s).is_none());
|
||||
assert_eq!(cache.view().transient_bytes, 96 * 1024);
|
||||
drop(first);
|
||||
@@ -748,9 +786,7 @@ mod tests {
|
||||
must_revalidate: false,
|
||||
proxy_revalidate: false,
|
||||
origin_etag: Some(HeaderValue::from_static("W/\"origin\"")),
|
||||
origin_last_modified: Some(HeaderValue::from_static(
|
||||
"Mon, 26 Jan 2026 23:11:40 GMT",
|
||||
)),
|
||||
origin_last_modified: Some(HeaderValue::from_static("Mon, 26 Jan 2026 23:11:40 GMT")),
|
||||
};
|
||||
let mut matching = HeaderMap::new();
|
||||
matching.insert("if-none-match", HeaderValue::from_static("\"origin\""));
|
||||
@@ -773,13 +809,7 @@ mod tests {
|
||||
let mut response_headers = headers("public, max-age=60");
|
||||
response_headers.insert("etag", public_etag.clone());
|
||||
response_headers.insert("last-modified", modified.clone());
|
||||
let plan = plan(
|
||||
StatusCode::OK,
|
||||
&response_headers,
|
||||
&settings,
|
||||
Duration::ZERO,
|
||||
)
|
||||
.unwrap();
|
||||
let plan = plan(StatusCode::OK, &response_headers, &settings, Duration::ZERO).unwrap();
|
||||
let mut response = Response::new(Body::from("transformed"));
|
||||
*response.headers_mut() = response_headers;
|
||||
response
|
||||
@@ -810,9 +840,15 @@ mod tests {
|
||||
assert_eq!(client_conditional["if-modified-since"], modified);
|
||||
|
||||
let mut validation = HeaderMap::new();
|
||||
validation.insert("cache-control", HeaderValue::from_static("public, max-age=60"));
|
||||
validation.insert(
|
||||
"cache-control",
|
||||
HeaderValue::from_static("public, max-age=60"),
|
||||
);
|
||||
validation.insert("etag", HeaderValue::from_static("\"origin-new\""));
|
||||
validation.insert("last-modified", HeaderValue::from_static("Mon, 26 Jan 2026 23:12:40 GMT"));
|
||||
validation.insert(
|
||||
"last-modified",
|
||||
HeaderValue::from_static("Mon, 26 Jan 2026 23:12:40 GMT"),
|
||||
);
|
||||
let merged = cached.merge_validation_headers(&validation);
|
||||
assert_eq!(merged["etag"], public_etag);
|
||||
}
|
||||
@@ -907,18 +943,13 @@ mod tests {
|
||||
let mut origin_headers = headers("public, max-age=60");
|
||||
origin_headers.insert("vary", HeaderValue::from_static("Accept-Encoding"));
|
||||
origin_headers.insert("content-length", HeaderValue::from_static("7"));
|
||||
let origin_plan = plan(StatusCode::OK, &origin_headers, &s, Duration::ZERO)
|
||||
.expect("origin plan");
|
||||
let origin_plan =
|
||||
plan(StatusCode::OK, &origin_headers, &s, Duration::ZERO).expect("origin plan");
|
||||
|
||||
let mut transformed_headers = headers("public, max-age=60");
|
||||
transformed_headers.insert("content-length", HeaderValue::from_static("7"));
|
||||
let transformed_plan = plan(
|
||||
StatusCode::OK,
|
||||
&transformed_headers,
|
||||
&s,
|
||||
Duration::ZERO,
|
||||
)
|
||||
.expect("transformed plan");
|
||||
let transformed_plan = plan(StatusCode::OK, &transformed_headers, &s, Duration::ZERO)
|
||||
.expect("transformed plan");
|
||||
let cache_plan = origin_plan
|
||||
.without_vary("accept-encoding")
|
||||
.restrict(transformed_plan);
|
||||
@@ -954,21 +985,13 @@ mod tests {
|
||||
dark.insert("x-theme", HeaderValue::from_static("dark"));
|
||||
|
||||
let mut origin_headers = headers("public, max-age=60");
|
||||
origin_headers.insert(
|
||||
"vary",
|
||||
HeaderValue::from_static("Accept-Encoding, X-Theme"),
|
||||
);
|
||||
let origin_plan = plan(StatusCode::OK, &origin_headers, &s, Duration::ZERO)
|
||||
.expect("origin plan");
|
||||
origin_headers.insert("vary", HeaderValue::from_static("Accept-Encoding, X-Theme"));
|
||||
let origin_plan =
|
||||
plan(StatusCode::OK, &origin_headers, &s, Duration::ZERO).expect("origin plan");
|
||||
let mut transformed_headers = headers("public, max-age=60");
|
||||
transformed_headers.insert("vary", HeaderValue::from_static("X-Theme"));
|
||||
let transformed_plan = plan(
|
||||
StatusCode::OK,
|
||||
&transformed_headers,
|
||||
&s,
|
||||
Duration::ZERO,
|
||||
)
|
||||
.expect("transformed plan");
|
||||
let transformed_plan = plan(StatusCode::OK, &transformed_headers, &s, Duration::ZERO)
|
||||
.expect("transformed plan");
|
||||
let cache_plan = origin_plan
|
||||
.without_vary("accept-encoding")
|
||||
.restrict(transformed_plan);
|
||||
@@ -1059,11 +1082,20 @@ mod tests {
|
||||
let entry = store.entries.values_mut().next().expect("stored object");
|
||||
entry.received = Instant::now() - Duration::from_secs(2);
|
||||
}
|
||||
assert!(cache.get(&initial).is_none(), "normal request must not receive stale data");
|
||||
assert_eq!(cache.view().entries, 1, "freshness expiry must not physically evict the object");
|
||||
assert!(
|
||||
cache.get(&initial).is_none(),
|
||||
"normal request must not receive stale data"
|
||||
);
|
||||
assert_eq!(
|
||||
cache.view().entries,
|
||||
1,
|
||||
"freshness expiry must not physically evict the object"
|
||||
);
|
||||
|
||||
let stale = ticket(&cache, &headers("max-stale"));
|
||||
let hit = cache.get(&stale).expect("explicit max-stale may reuse retained stale data");
|
||||
let hit = cache
|
||||
.get(&stale)
|
||||
.expect("explicit max-stale may reuse retained stale data");
|
||||
assert!(hit.stale);
|
||||
assert_eq!(
|
||||
hit.response.into_body().collect().await.unwrap().to_bytes(),
|
||||
@@ -1099,7 +1131,14 @@ mod tests {
|
||||
validation.insert("etag", HeaderValue::from_static("\"origin\""));
|
||||
let merged = candidate.merge_validation_headers(&validation);
|
||||
let hit = cache
|
||||
.publish_revalidated(t.clone(), &candidate, merged, &validation, &s, Duration::ZERO)
|
||||
.publish_revalidated(
|
||||
t.clone(),
|
||||
&candidate,
|
||||
merged,
|
||||
&validation,
|
||||
&s,
|
||||
Duration::ZERO,
|
||||
)
|
||||
.expect("validated body can be delivered");
|
||||
assert_eq!(
|
||||
hit.response.into_body().collect().await.unwrap().to_bytes(),
|
||||
@@ -1142,7 +1181,14 @@ mod tests {
|
||||
validation.insert("etag", HeaderValue::from_static("\"origin\""));
|
||||
let merged = candidate.merge_validation_headers(&validation);
|
||||
let hit = cache
|
||||
.publish_revalidated(t, &candidate, merged, &validation, &s, Duration::from_secs(2))
|
||||
.publish_revalidated(
|
||||
t,
|
||||
&candidate,
|
||||
merged,
|
||||
&validation,
|
||||
&s,
|
||||
Duration::from_secs(2),
|
||||
)
|
||||
.expect("validated object");
|
||||
assert!(hit.resident_age_seconds >= 32);
|
||||
}
|
||||
@@ -1284,7 +1330,10 @@ mod tests {
|
||||
assert!(after.hot_allocated_bytes <= s.max_bytes);
|
||||
assert_eq!(after.fill_bytes_active, 0);
|
||||
assert_eq!(after.transient_bytes, 0);
|
||||
assert!(cache.get(&first).is_none(), "LRU entry should make room before the next fill allocation");
|
||||
assert!(
|
||||
cache.get(&first).is_none(),
|
||||
"LRU entry should make room before the next fill allocation"
|
||||
);
|
||||
assert!(cache.get(&second).is_some());
|
||||
assert!(after.evictions >= 1);
|
||||
}
|
||||
@@ -1418,12 +1467,17 @@ mod tests {
|
||||
assert_eq!(first, Bytes::from_static(b"first-"));
|
||||
drop(body);
|
||||
|
||||
assert!(cache.get(&t).is_none(), "partial body must never become a HIT");
|
||||
assert!(
|
||||
cache.get(&t).is_none(),
|
||||
"partial body must never become a HIT"
|
||||
);
|
||||
assert_eq!(cache.view().fills_active, 0);
|
||||
assert_eq!(cache.view().rejected_fills, 1);
|
||||
|
||||
fill(&cache, t.clone(), "complete", &s).await;
|
||||
let hit = cache.get(&t).expect("a later complete GET can fill the cache");
|
||||
let hit = cache
|
||||
.get(&t)
|
||||
.expect("a later complete GET can fill the cache");
|
||||
assert_eq!(
|
||||
hit.response.into_body().collect().await.unwrap().to_bytes(),
|
||||
Bytes::from_static(b"complete")
|
||||
@@ -1528,18 +1582,30 @@ mod scope_tests {
|
||||
assert!(effective.enabled && effective.expose_status);
|
||||
let mut globally_disabled = global.clone();
|
||||
globally_disabled.enabled = false;
|
||||
assert!(!Overrides { enabled: Some(true), ..Overrides::default() }
|
||||
assert!(
|
||||
!Overrides {
|
||||
enabled: Some(true),
|
||||
..Overrides::default()
|
||||
}
|
||||
.resolve(&globally_disabled)
|
||||
.unwrap()
|
||||
.enabled);
|
||||
assert!(!Overrides { enabled: Some(false), ..Overrides::default() }
|
||||
.enabled
|
||||
);
|
||||
assert!(
|
||||
!Overrides {
|
||||
enabled: Some(false),
|
||||
..Overrides::default()
|
||||
}
|
||||
.resolve(&global)
|
||||
.unwrap()
|
||||
.enabled);
|
||||
assert!(!Overrides::default()
|
||||
.resolve(&globally_disabled)
|
||||
.unwrap()
|
||||
.enabled);
|
||||
.enabled
|
||||
);
|
||||
assert!(
|
||||
!Overrides::default()
|
||||
.resolve(&globally_disabled)
|
||||
.unwrap()
|
||||
.enabled
|
||||
);
|
||||
assert_eq!(effective.max_bytes, global.max_bytes);
|
||||
assert_eq!(effective.max_ttl_seconds, 30);
|
||||
assert!(Overrides {
|
||||
@@ -1861,8 +1927,22 @@ mod v022_cache_tests {
|
||||
)
|
||||
.await;
|
||||
let cases = [
|
||||
(ByteRange { start: None, end: Some(3) }, "hij", "bytes 7-9/10"),
|
||||
(ByteRange { start: Some(7), end: None }, "hij", "bytes 7-9/10"),
|
||||
(
|
||||
ByteRange {
|
||||
start: None,
|
||||
end: Some(3),
|
||||
},
|
||||
"hij",
|
||||
"bytes 7-9/10",
|
||||
),
|
||||
(
|
||||
ByteRange {
|
||||
start: Some(7),
|
||||
end: None,
|
||||
},
|
||||
"hij",
|
||||
"bytes 7-9/10",
|
||||
),
|
||||
];
|
||||
for (range, expected, content_range) in cases {
|
||||
let hit = cache.get(&ticket).unwrap().apply_range(range, None);
|
||||
@@ -1873,38 +1953,66 @@ mod v022_cache_tests {
|
||||
}
|
||||
|
||||
let unsatisfied = cache.get(&ticket).unwrap().apply_range(
|
||||
ByteRange { start: Some(50), end: None },
|
||||
ByteRange {
|
||||
start: Some(50),
|
||||
end: None,
|
||||
},
|
||||
None,
|
||||
);
|
||||
assert_eq!(unsatisfied.response.status(), StatusCode::RANGE_NOT_SATISFIABLE);
|
||||
assert_eq!(unsatisfied.response.headers()["content-range"], "bytes */10");
|
||||
assert_eq!(
|
||||
unsatisfied.response.into_body().collect().await.unwrap().to_bytes().len(),
|
||||
unsatisfied.response.status(),
|
||||
StatusCode::RANGE_NOT_SATISFIABLE
|
||||
);
|
||||
assert_eq!(
|
||||
unsatisfied.response.headers()["content-range"],
|
||||
"bytes */10"
|
||||
);
|
||||
assert_eq!(
|
||||
unsatisfied
|
||||
.response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.len(),
|
||||
0
|
||||
);
|
||||
|
||||
let mismatch = HeaderValue::from_static("\"asset-v2\"");
|
||||
let full = cache.get(&ticket).unwrap().apply_range(
|
||||
ByteRange { start: Some(0), end: Some(2) },
|
||||
ByteRange {
|
||||
start: Some(0),
|
||||
end: Some(2),
|
||||
},
|
||||
Some(&mismatch),
|
||||
);
|
||||
assert_eq!(full.response.status(), StatusCode::OK);
|
||||
assert!(!full.response.headers().contains_key("content-range"));
|
||||
assert_eq!(
|
||||
full.response.into_body().collect().await.unwrap().to_bytes(),
|
||||
full.response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes(),
|
||||
Bytes::from_static(b"abcdefghij")
|
||||
);
|
||||
|
||||
let matched = HeaderValue::from_static("\"asset-v1\"");
|
||||
let partial = cache.get(&ticket).unwrap().apply_range(
|
||||
ByteRange { start: Some(0), end: Some(2) },
|
||||
ByteRange {
|
||||
start: Some(0),
|
||||
end: Some(2),
|
||||
},
|
||||
Some(&matched),
|
||||
);
|
||||
assert_eq!(partial.response.status(), StatusCode::PARTIAL_CONTENT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range_parser_rejects_multipart_and_duplicate_fields_and_if_range_date_allows_older_representation() {
|
||||
fn range_parser_rejects_multipart_and_duplicate_fields_and_if_range_date_allows_older_representation(
|
||||
) {
|
||||
let mut multipart = HeaderMap::new();
|
||||
multipart.insert("range", HeaderValue::from_static("bytes=0-1,4-5"));
|
||||
assert_eq!(
|
||||
@@ -1999,13 +2107,23 @@ mod v022_cache_tests {
|
||||
let mut settings = active_settings();
|
||||
settings.collapse_wait_timeout_ms = 10;
|
||||
let ticket = cache
|
||||
.ticket(22, "http", "collapse.example", "origin", "/timeout", &HeaderMap::new())
|
||||
.ticket(
|
||||
22,
|
||||
"http",
|
||||
"collapse.example",
|
||||
"origin",
|
||||
"/timeout",
|
||||
&HeaderMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
let leader = match cache.claim_fill(&ticket, &settings).await {
|
||||
FillClaim::Leader(guard) => guard,
|
||||
_ => panic!("missing leader"),
|
||||
};
|
||||
assert!(matches!(cache.claim_fill(&ticket, &settings).await, FillClaim::Timeout));
|
||||
assert!(matches!(
|
||||
cache.claim_fill(&ticket, &settings).await,
|
||||
FillClaim::Timeout
|
||||
));
|
||||
assert_eq!(cache.view().collapse_timeouts, 1);
|
||||
drop(leader);
|
||||
assert!(matches!(
|
||||
@@ -2020,7 +2138,14 @@ mod v022_cache_tests {
|
||||
let mut settings = active_settings();
|
||||
settings.collapse_wait_timeout_ms = 5_000;
|
||||
let ticket = cache
|
||||
.ticket(22, "http", "collapse.example", "origin", "/cancel", &HeaderMap::new())
|
||||
.ticket(
|
||||
22,
|
||||
"http",
|
||||
"collapse.example",
|
||||
"origin",
|
||||
"/cancel",
|
||||
&HeaderMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
let leader = match cache.claim_fill(&ticket, &settings).await {
|
||||
FillClaim::Leader(guard) => guard,
|
||||
@@ -2058,10 +2183,24 @@ mod v022_cache_tests {
|
||||
let mut pl_headers = HeaderMap::new();
|
||||
pl_headers.insert("accept-language", HeaderValue::from_static("pl"));
|
||||
let en = cache
|
||||
.ticket(22, "http", "vary-collapse.example", "origin", "/page", &en_headers)
|
||||
.ticket(
|
||||
22,
|
||||
"http",
|
||||
"vary-collapse.example",
|
||||
"origin",
|
||||
"/page",
|
||||
&en_headers,
|
||||
)
|
||||
.unwrap();
|
||||
let pl = cache
|
||||
.ticket(22, "http", "vary-collapse.example", "origin", "/page", &pl_headers)
|
||||
.ticket(
|
||||
22,
|
||||
"http",
|
||||
"vary-collapse.example",
|
||||
"origin",
|
||||
"/page",
|
||||
&pl_headers,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(en.key, pl.key, "Vary is learned from the response");
|
||||
let leader = match cache.claim_fill(&en, &settings).await {
|
||||
@@ -2136,7 +2275,14 @@ mod v022_cache_tests {
|
||||
fn proactive_refresh_guards_deduplicate_and_bound_parallel_work() {
|
||||
let cache = Cache::new(22);
|
||||
let ticket = cache
|
||||
.ticket(22, "http", "refresh.example", "origin", "/asset", &HeaderMap::new())
|
||||
.ticket(
|
||||
22,
|
||||
"http",
|
||||
"refresh.example",
|
||||
"origin",
|
||||
"/asset",
|
||||
&HeaderMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut settings = active_settings();
|
||||
settings.proactive_refresh_enabled = true;
|
||||
@@ -2192,7 +2338,9 @@ mod v022_cache_tests {
|
||||
&HeaderMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(cache.begin_proactive_schedule(&overflow, &settings).is_none());
|
||||
assert!(cache
|
||||
.begin_proactive_schedule(&overflow, &settings)
|
||||
.is_none());
|
||||
assert!(cache.view().proactive_skipped > 0);
|
||||
drop(guards);
|
||||
}
|
||||
@@ -2502,7 +2650,10 @@ mod v022_cache_tests {
|
||||
"fresh",
|
||||
)
|
||||
.await;
|
||||
assert!(cache.get(&fresh).is_some(), "old BAN must not reject newer objects");
|
||||
assert!(
|
||||
cache.get(&fresh).is_some(),
|
||||
"old BAN must not reject newer objects"
|
||||
);
|
||||
assert_eq!(cache.bans().len(), 1);
|
||||
assert!(cache.remove_ban(ban_id));
|
||||
assert!(cache.bans().is_empty());
|
||||
@@ -2729,9 +2880,10 @@ mod cold_tier_tests {
|
||||
"public, max-age=120, stale-while-revalidate=30, stale-if-error=90",
|
||||
),
|
||||
);
|
||||
transformed
|
||||
.headers_mut()
|
||||
.insert("etag", HeaderValue::from_static("\"rustproxy-representation\""));
|
||||
transformed.headers_mut().insert(
|
||||
"etag",
|
||||
HeaderValue::from_static("\"rustproxy-representation\""),
|
||||
);
|
||||
transformed
|
||||
.headers_mut()
|
||||
.insert("content-length", HeaderValue::from_static("7"));
|
||||
@@ -2739,15 +2891,15 @@ mod cold_tier_tests {
|
||||
.extensions_mut()
|
||||
.insert(crate::transform::OriginValidators {
|
||||
etag: Some(HeaderValue::from_static("\"origin-v1\"")),
|
||||
last_modified: Some(HeaderValue::from_static(
|
||||
"Mon, 26 Jan 2026 23:11:40 GMT",
|
||||
)),
|
||||
last_modified: Some(HeaderValue::from_static("Mon, 26 Jan 2026 23:11:40 GMT")),
|
||||
});
|
||||
transformed
|
||||
.extensions_mut()
|
||||
.insert(crate::transform::Applied {
|
||||
kind: crate::transform::Kind::Json,
|
||||
original_bytes: 9,
|
||||
transformed_bytes: 7,
|
||||
});
|
||||
transformed.extensions_mut().insert(crate::transform::Applied {
|
||||
kind: crate::transform::Kind::Json,
|
||||
original_bytes: 9,
|
||||
transformed_bytes: 7,
|
||||
});
|
||||
let transformed_plan = plan(
|
||||
StatusCode::OK,
|
||||
transformed.headers(),
|
||||
@@ -2868,7 +3020,9 @@ mod cold_tier_tests {
|
||||
.await
|
||||
.expect("no-transform object after restart");
|
||||
assert_eq!(protected_hit.response.headers()["etag"], "\"origin-css\"");
|
||||
assert!(crate::transform::no_transform(protected_hit.response.headers()));
|
||||
assert!(crate::transform::no_transform(
|
||||
protected_hit.response.headers()
|
||||
));
|
||||
assert!(protected_hit
|
||||
.response
|
||||
.extensions()
|
||||
@@ -2994,7 +3148,10 @@ mod cold_tier_tests {
|
||||
assert_eq!(view.entries, 0);
|
||||
assert_eq!(view.resident_bytes, 0);
|
||||
assert_eq!(view.hot_logical_bytes, 0);
|
||||
assert_eq!(view.transient_bytes, 0, "persisted ColdEntry must not retain the body allocation");
|
||||
assert_eq!(
|
||||
view.transient_bytes, 0,
|
||||
"persisted ColdEntry must not retain the body allocation"
|
||||
);
|
||||
assert_eq!(view.cold_entries, 2);
|
||||
shutdown.cancel();
|
||||
|
||||
@@ -3072,17 +3229,23 @@ mod cold_tier_tests {
|
||||
for item in std::fs::read_dir(&root).unwrap() {
|
||||
let item = item.unwrap();
|
||||
let name = item.file_name().to_string_lossy().to_string();
|
||||
assert!(item.path().is_dir(), "cold root must contain shard directories only");
|
||||
assert!(
|
||||
item.path().is_dir(),
|
||||
"cold root must contain shard directories only"
|
||||
);
|
||||
assert_eq!(name.len(), 2);
|
||||
assert!(name.bytes().all(|b| b.is_ascii_hexdigit()));
|
||||
shards.push(item.path());
|
||||
}
|
||||
assert!(!shards.is_empty());
|
||||
assert!(shards.iter().any(|shard| {
|
||||
std::fs::read_dir(shard).unwrap().filter_map(|entry| entry.ok()).any(|entry| {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
name.ends_with(".json") || name.ends_with(".body")
|
||||
})
|
||||
std::fs::read_dir(shard)
|
||||
.unwrap()
|
||||
.filter_map(|entry| entry.ok())
|
||||
.any(|entry| {
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
name.ends_with(".json") || name.ends_with(".body")
|
||||
})
|
||||
}));
|
||||
shutdown.cancel();
|
||||
}
|
||||
@@ -3096,7 +3259,12 @@ mod cold_tier_tests {
|
||||
std::fs::write(root.join("legacy.body"), b"old").unwrap();
|
||||
std::fs::write(root.join("legacy.json"), b"{}").unwrap();
|
||||
let shutdown = CancellationToken::new();
|
||||
let cache = Cache::start(11, Some(dir.path().to_path_buf()), settings, shutdown.clone());
|
||||
let cache = Cache::start(
|
||||
11,
|
||||
Some(dir.path().to_path_buf()),
|
||||
settings,
|
||||
shutdown.clone(),
|
||||
);
|
||||
assert_eq!(cache.view().cold_entries, 0);
|
||||
assert!(!root.join("legacy.body").exists());
|
||||
assert!(!root.join("legacy.json").exists());
|
||||
@@ -3109,12 +3277,23 @@ mod cold_tier_tests {
|
||||
let mut settings = cold_settings();
|
||||
settings.max_entries = 1;
|
||||
let shutdown = CancellationToken::new();
|
||||
let cache = Cache::start(11, Some(dir.path().to_path_buf()), settings.clone(), shutdown.clone());
|
||||
let cache = Cache::start(
|
||||
11,
|
||||
Some(dir.path().to_path_buf()),
|
||||
settings.clone(),
|
||||
shutdown.clone(),
|
||||
);
|
||||
fill(&cache, &settings, "/one", "first-payload").await;
|
||||
fill(&cache, &settings, "/two", "second-payload").await;
|
||||
fill(&cache, &settings, "/three", "third-payload").await;
|
||||
wait_for_cold(&cache, 2).await;
|
||||
let job = cache.start_cold_cleanup(ColdCleanupRequest { unused_for_seconds: None, oldest: Some(1), hits_zero: false }).unwrap();
|
||||
let job = cache
|
||||
.start_cold_cleanup(ColdCleanupRequest {
|
||||
unused_for_seconds: None,
|
||||
oldest: Some(1),
|
||||
hits_zero: false,
|
||||
})
|
||||
.unwrap();
|
||||
for _ in 0..100 {
|
||||
let current = cache.cold_cleanup_job(job.id).unwrap();
|
||||
if current.status == "done" {
|
||||
@@ -3134,7 +3313,12 @@ mod cold_tier_tests {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let settings = cold_settings();
|
||||
let shutdown = CancellationToken::new();
|
||||
let cache = Cache::start(11, Some(dir.path().to_path_buf()), settings, shutdown.clone());
|
||||
let cache = Cache::start(
|
||||
11,
|
||||
Some(dir.path().to_path_buf()),
|
||||
settings,
|
||||
shutdown.clone(),
|
||||
);
|
||||
let _held = cache.cleanup_slots.clone().try_acquire_owned().unwrap();
|
||||
let result = cache.start_cold_cleanup(ColdCleanupRequest {
|
||||
unused_for_seconds: None,
|
||||
@@ -3178,7 +3362,14 @@ mod cold_tier_tests {
|
||||
assert_eq!(result.removed_entries, 0);
|
||||
assert!(cache.cold.lock().unwrap().entries.contains_key(&key));
|
||||
|
||||
cache.cold.lock().unwrap().entries.get_mut(&key).unwrap().pinned_reads = 0;
|
||||
cache
|
||||
.cold
|
||||
.lock()
|
||||
.unwrap()
|
||||
.entries
|
||||
.get_mut(&key)
|
||||
.unwrap()
|
||||
.pinned_reads = 0;
|
||||
let result = cache
|
||||
.remove_cold_candidates(Some(&request), None, None, 1)
|
||||
.await;
|
||||
|
||||
Reference in new issue
Block a user