v0.34.12
This commit is contained in:
1 parent
b3dfd570b5
commit
a4042d6439
16 files changed
+362
-55
No files matched your search
+15
-15
@@ -277,7 +277,7 @@ e80f84449412c4ae7bc753672a554160519bf1f186b5707252c3286cbe13f02e src/cache/cold
|
||||
83095bc93132f306c537b31ba929669fedac99af3c4f133d0533b5ed7ac4e6ed src/cache/cold/worker.rs
|
||||
a36a4a255a0fd2c38449bbc462b9c0e21488eae108c70326ab2a0e4c7cee0f97 src/cache/collapse.rs
|
||||
346fdb40d85627b583a01fa17c65e45c822dc7216198cdeadbb3d07904286fa3 src/cache/dashboard.rs
|
||||
7ce2e4cd201813cef10db0cd791d39deb8f2dec7abed934dd0878d4a47da2d7d src/cache/dashboard_tests.rs
|
||||
54785b64ab4a7b0382b84b4a313d1996c4b6986d69f1e598293f29122afbad33 src/cache/dashboard_tests.rs
|
||||
8b5a8c04a8ee115497635d2faf08eab5ca4db4225c6c70110675685346f9c0e5 src/cache/fill.rs
|
||||
675d18afb53f25c56cba49bbb431fa39ecd9d7bdd192c7b39b0698225de7a2a2 src/cache/invalidation.rs
|
||||
c1357a13658e1ed5158084d3a41ba9ea906512774a5863ce9c56cb508d3fa6c8 src/cache/invalidation/ban.rs
|
||||
@@ -285,27 +285,27 @@ bbd9f372648d89b5e14d5b58029582d37f66a89752caf2c14eb1cbf8f24bb7a0 src/cache/inva
|
||||
6223eaea3f061b111330cc26b28d85b9b239bef30328025f2ef337291e22a445 src/cache/invalidation/invalidate.rs
|
||||
9bae8be41d1e6b30cda51feff99fd77738940fa975bdd4ece7da75d936f07b47 src/cache/lifecycle.rs
|
||||
bd831807ca4720b3a41a6ea1d9a6c0ed2d935225e2dacbd9313be064f2e4a20e src/cache/lookup.rs
|
||||
9cfb83f414bdf4ccbedc3daf2eeeea4cb2ef83ab7c6aaef6063a331c9fbf5608 src/cache/lookup/hot.rs
|
||||
13ecc0fec5f9ea4c555bc23e7ad0aaec2ba10032e8f5fd2729675316edb28487 src/cache/lookup/hot.rs
|
||||
4148106b9baf70e41eeba24233b1edbe8dd6f927c11f2c7647e89fbbfb60555e src/cache/lookup/hot_hit_034_tests.rs
|
||||
e9c6ab0c2be9f96573f826e6a1b9e4c2b8b526f779e72e95a6463ae07e45ab55 src/cache/lookup/insert.rs
|
||||
4c75c0d574b5841639068dc6890c6e06892e53cfcb080fffebd0ef4e5284d726 src/cache/lookup/tiered.rs
|
||||
6943a1856da7aaffc9af335d0af7bc88cbb7a8ba9f67a83c91e4500ad431ce56 src/cache/lookup/tiered.rs
|
||||
639d5be6258699ec7e9f260ec33bd2c366203b6655bf0e98f30517c2e12ceeeb src/cache/mod.rs
|
||||
0c8b0d550e94d4474dcdf991aa0340228dc5f2f904f39350e6e666a16f16fe87 src/cache/model.rs
|
||||
c409174a9aaf20c69dc7b329108d1c26b5672a7341236cf639480df48800d262 src/cache/model/cache.rs
|
||||
0ec4f6d0adea1ed80a3ca17c68c9c85502491da2904d9d690b4169ab9b025b21 src/cache/model/cold_metadata.rs
|
||||
cf3c64de61e997da976b3cb54ac09b346679ccf53597900c7b2ea19a3e507b73 src/cache/model/entry.rs
|
||||
9f3143d99ee7e11abdb625cd8deff0af3ede8af716ea99b754e92803d1150bf7 src/cache/model/entry.rs
|
||||
ff1c3a1a3bc0d26218dcc943ef951c2a212c180bbf6e4bf1c542ffbf12a9f150 src/cache/model/http_invalidation.rs
|
||||
dc1153685671be70c28bef814d72d8ef09afe070598f528ea62260c54d43b26f src/cache/model/maintenance.rs
|
||||
81938ee9d96891a6e1c81ac1117c67bd4ce4a1530684eab84521b3de1a503dff src/cache/model/maintenance.rs
|
||||
19aaf66abe2ad30b973131a020b7ca8ec6d0646a51df5c604c5f8914db898416 src/cache/model/settings.rs
|
||||
395a82091ae520b78fa606553a0ec590ffb14540b83234cef5bbdcd2e57d39d5 src/cache/model/views.rs
|
||||
e26dce02df9c043329ca164fed215d271781bce8fb03240f528331bf3df6b9d7 src/cache/policy.rs
|
||||
896b1869f94973b30a0de0faa62a2c89fbc8991b02de0e5988ab4ce1f77b392d src/cache/policy/request.rs
|
||||
9dd790a35dbc15df8465664ed9fff296fe22847371d667aefe320504ab952b2b src/cache/policy/response.rs
|
||||
e59efd322f518ebd7b5b154115188f382c2cf7882ca0a401736ba5d685981ea5 src/cache/policy/scope_tests.rs
|
||||
2b556ac9179b8a92de368f5e907e4114c2fc21a89155fe2a05665c45c08b3883 src/cache/range.rs
|
||||
4179c2c1367a9c8ea8432bd5815b7e7afbca820d7589742b3ad4775c0cc8e0bc src/cache/range.rs
|
||||
e1d3f0512d6290b0ac4998f47398a0a7c404db249c5bbdbc4ad4767071013a72 src/cache/stats.rs
|
||||
8a300f5dad44815401eb23f4f5a157893761852ba4f3005041e0eb3f16892ce3 src/cache/tests.rs
|
||||
895b589b9866fafafce9f464c4a0d3d2ced55989a1d909bc152ab1529c2a1550 src/cache/tickets.rs
|
||||
8d9532ea1f30ec431195698a9aba82f2d31083d4b4ee98cb8d9eb47b2b01c7b9 src/cache/tickets.rs
|
||||
b7c063ed4c8ca96714e1dc5d279f38fc521da2132faad7bd4f40311352a71e7c src/cache/v022_cache_tests.rs
|
||||
452ff2f162f70c3e4d9259de4b13a7751874742c6922c5e0e0196eeadee4ccaa src/cache/varnish_compatible_tests.rs
|
||||
5a8022f6f4721f9fabdc715e01127d9299d47ee9981a79d26df1981448487d20 src/compression/mod.rs
|
||||
@@ -391,15 +391,15 @@ d134b0eb617f82f9971b3f3461379ea37c57bd0079bf54748d0c434b2ef1b475 src/proxy/cach
|
||||
a03b1735daa8fa8e40cf5bd9c6e200d211baf19c30f62522d4237ed56642bce7 src/proxy/cache_plan/mod.rs
|
||||
3404b03096862de1050088f62cdd811ea1ab61d191f902501173107f81f23b9b src/proxy/cache_plan/tests.rs
|
||||
a0b9e2e9ab0bc89c7b64bd19365198234b1c5c13f455ea47be122a5660639b17 src/proxy/error_fallback.rs
|
||||
e466e765ba49c36af75042a078fc5dec3ac803b180ae240057ae832b9896ac53 src/proxy/forward/cache_fast_path_tests.rs
|
||||
e8ef0466a11eb72724d640adbd802915461c776d0983509625b3e14e42d2c2e9 src/proxy/forward/cache_lookup.rs
|
||||
3efd124f2c27ac2d5b6c6c0ab6a5b91b7b18ce2f3bf097a00fca5475b05e36c0 src/proxy/forward/cache_fast_path_tests.rs
|
||||
f7903200756dc6393392560656b23364f0d444d6357d264713b17c63fd8b636b src/proxy/forward/cache_lookup.rs
|
||||
287012c81144d8f15af4a148ce3ca911d431bc59adff44e697567561aa372bb3 src/proxy/forward/h3_navigation_tests.rs
|
||||
ce97cfcb07d90415937f0520ee7175502b22e60694c1ea8e3d2b47678d190e5f src/proxy/forward/response.rs
|
||||
a6580dc359d1f377a48ccc1a902056dc5c2ef86c89180b9b0995068d9817ae8a src/proxy/forward/response.rs
|
||||
1a7322aadbe90e2a6940a27882c13e274b1c92f14de3ebad717054a9614d4ba8 src/proxy/forward/route_cache.rs
|
||||
ac449238bdc9b3c2c693b773c023d34c0844a40d9472154e4526a46540004a35 src/proxy/forward/success_finish.rs
|
||||
aa30766a5d584c2143488f16caef19c1d369a2c7c00b5ff960eaf2ccf4045c31 src/proxy/forward/success_prepare.rs
|
||||
0e6e288fb8a0575aaa4e3bcb5c47ed5412a584386a2b00fb25db8428d652cbf3 src/proxy/forward/success_finish.rs
|
||||
744daef016d4a6c821ab12eeedaa2f29e2de72e2ac9dae16481d4f43476cb713 src/proxy/forward/success_prepare.rs
|
||||
e9ac9be64f6f83c4ca2cd5d299649f24dd16197712083c6dc10e55c662c65b80 src/proxy/forward/upstream.rs
|
||||
1bf132dee1d89d1e9b8eeb0c5b6f4fcc9e2a1d741b73876300221d0264696aa5 src/proxy/forward/web_source.rs
|
||||
fcac33e29f518964e8a8d90f44f58a984cf610a5960150a0149c54425491ea86 src/proxy/forward/web_source.rs
|
||||
3340583d395fcc859cf3421f4e33cf8347915ee4c468f7d0e4abebce8156fdca src/proxy/forward_entry.rs
|
||||
9b25f5e3ea4443a63ee6f45d12e1957dd316fe8e85325fc00ea416faaab1025a src/proxy/h2_graceful.rs
|
||||
684ea1e2a7591a17e3c633576fc66e2a30d6636738fbce61dab5af44f2c51a67 src/proxy/headers/authority_tests.rs
|
||||
@@ -412,8 +412,8 @@ de1763b89d98607e20f0f6e3c9b546483927cbe5450efc2e5634fe691fd52ad6 src/proxy/requ
|
||||
b9a2307c6aa7f16613d935508a61574dfee964020b1b5be86293df2d684615d8 src/proxy/request_id/tests.rs
|
||||
580fefdc7dfb27da73f86f4d2067d46a1f856d2f398b0598d37a743e880d3dac src/proxy/types.rs
|
||||
37b20f962563babd3602d21ed6326946635281c0b0d97ca59f4e33de170ae93f src/proxy/upstream_helpers.rs
|
||||
e061db49b6ee6074621a539cc2b8d13e16d499fea2ec7b3086aec5ce4a8f4e99 src/proxy/web_cache_revalidation/mod.rs
|
||||
b9e2c34cfe0ac8627aca64b234e42024103b1bad6efc2f1bcc4347d929aee8f3 src/proxy/web_cache_revalidation/tests.rs
|
||||
56a921ee4f30da154176998a383bcb5a40d3c7784a04829c59b8e6536728e2d4 src/proxy/web_cache_revalidation/mod.rs
|
||||
fdc589ed3968915558a98a11c7cadc36145b5b0b0dca9393613391990d138fc3 src/proxy/web_cache_revalidation/tests.rs
|
||||
3d591201595a3b1e928ded20a8736477a47e5acc3bcbde158f7c17547ce155ba src/proxy/websocket.rs
|
||||
f8972bedb1a487fac86177774cd98cf2619fb25af97e57cfac2853db5cbf91a9 src/proxy/websocket/handshake.rs
|
||||
c02d42b5c7eeedbc54c58e98691f7d8e39f8e9be63a48f6d6b4ad6b8ea431b7e src/proxy/websocket/http2_tests.rs
|
||||
|
||||
Vendored
+74
@@ -409,6 +409,80 @@ mod dashboard_tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rfc9211_method_bypass_and_stale_hit_do_not_misstate_freshness() {
|
||||
assert_eq!(cache_status_ttl(1, 2), -1);
|
||||
assert_eq!(cache_status_ttl(10, 10), 0);
|
||||
assert_eq!(cache_status_ttl(600, 5), 595);
|
||||
let settings = Settings {
|
||||
expose_status: true,
|
||||
expose_cache_status: true,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
settings.annotate_reason(&mut headers, "BYPASS", 0, 0, "request_method");
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; fwd=method; detail=\"request_method\"",
|
||||
);
|
||||
let mut headers = HeaderMap::new();
|
||||
settings.annotate_reason_transformed_freshness(
|
||||
&mut headers, "HIT", 3, -7, "cache_max_stale", false,
|
||||
);
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; hit; ttl=-7; detail=\"cache_max_stale\"",
|
||||
);
|
||||
let mut headers = HeaderMap::new();
|
||||
settings.annotate_reason_transformed_freshness(
|
||||
&mut headers, "HIT", 3, -5, "cache_stale_while_revalidate", false,
|
||||
);
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; hit; ttl=-5; detail=\"cache_stale_while_revalidate\"",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rfc9211_forwarded_stale_error_and_forced_validation_report_real_hops() {
|
||||
let settings = Settings {
|
||||
expose_status: true,
|
||||
expose_cache_status: true,
|
||||
..Settings::default()
|
||||
};
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.append("cache-status", HeaderValue::from_static("OriginCache; hit"));
|
||||
settings.annotate_reason_transformed_forwarded(
|
||||
&mut headers, "HIT", 3, 0, "cache_stale_if_error", false,
|
||||
"stale", Some(StatusCode::SERVICE_UNAVAILABLE),
|
||||
);
|
||||
assert_eq!(headers["x-cache"], "HIT");
|
||||
let values: Vec<_> = headers.get_all("cache-status").iter()
|
||||
.map(|value| value.to_str().unwrap()).collect();
|
||||
assert_eq!(values, vec![
|
||||
"OriginCache; hit",
|
||||
"RustProxy; fwd=stale; fwd-status=503; detail=\"cache_stale_if_error\"",
|
||||
]);
|
||||
let mut headers = HeaderMap::new();
|
||||
settings.annotate_reason_transformed_forwarded(
|
||||
&mut headers, "HIT", 2, 42, "cache_revalidated", false,
|
||||
"request", Some(StatusCode::NOT_MODIFIED),
|
||||
);
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; fwd=request; fwd-status=304; detail=\"cache_revalidated\"",
|
||||
);
|
||||
let mut headers = HeaderMap::new();
|
||||
settings.annotate_reason_transformed_forwarded(
|
||||
&mut headers, "HIT", 2, 0, "cache_stale_if_error", false,
|
||||
"stale", None,
|
||||
);
|
||||
assert_eq!(
|
||||
headers["cache-status"],
|
||||
"RustProxy; fwd=stale; detail=\"cache_stale_if_error\"",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_domain_metrics_do_not_lock_hot_store_and_preserve_reasons() {
|
||||
let cache = Cache::new(7);
|
||||
|
||||
Vendored
+4
@@ -52,6 +52,7 @@ impl Cache {
|
||||
transformed,
|
||||
skip_compression,
|
||||
remaining_ttl,
|
||||
entry_ttl,
|
||||
body_bytes,
|
||||
) = {
|
||||
let entry = store.entries.get_mut(&storage_key)?;
|
||||
@@ -67,6 +68,7 @@ impl Cache {
|
||||
entry.transformed,
|
||||
entry.skip_compression,
|
||||
entry.ttl.saturating_sub(age),
|
||||
entry.ttl,
|
||||
entry.body.len(),
|
||||
)
|
||||
};
|
||||
@@ -97,6 +99,7 @@ impl Cache {
|
||||
hits,
|
||||
transformed,
|
||||
remaining_ttl,
|
||||
cache_status_ttl: cache_status_ttl(entry_ttl, age),
|
||||
resident_age_seconds,
|
||||
stale: state.stale(),
|
||||
background_revalidate: state.background_revalidate(),
|
||||
@@ -301,6 +304,7 @@ impl Cache {
|
||||
hits,
|
||||
transformed: entry.transformed,
|
||||
remaining_ttl: entry.ttl.saturating_sub(age),
|
||||
cache_status_ttl: cache_status_ttl(entry.ttl, age),
|
||||
resident_age_seconds,
|
||||
stale: state.stale(),
|
||||
background_revalidate: state.background_revalidate(),
|
||||
|
||||
Vendored
+1
@@ -110,6 +110,7 @@ impl Cache {
|
||||
hits,
|
||||
transformed: cached.transformed,
|
||||
remaining_ttl,
|
||||
cache_status_ttl: cache_status_ttl(plan.ttl, age),
|
||||
resident_age_seconds: resident_age.as_secs(),
|
||||
stale: false,
|
||||
background_revalidate: false,
|
||||
|
||||
Vendored
+79
-9
@@ -171,7 +171,7 @@ impl Settings {
|
||||
.any(|item| item.eq_ignore_ascii_case(extension))
|
||||
}
|
||||
pub fn annotate(&self, headers: &mut HeaderMap, outcome: &'static str, hits: u64, ttl: u64) {
|
||||
self.annotate_internal(headers, outcome, hits, ttl, None, false);
|
||||
self.annotate_internal(headers, outcome, hits, ttl as i64, None, false, None);
|
||||
}
|
||||
|
||||
pub fn annotate_reason(
|
||||
@@ -182,7 +182,15 @@ impl Settings {
|
||||
ttl: u64,
|
||||
reason: &'static str,
|
||||
) {
|
||||
self.annotate_internal(headers, outcome, hits, ttl, Some(reason), false);
|
||||
self.annotate_internal(
|
||||
headers,
|
||||
outcome,
|
||||
hits,
|
||||
ttl as i64,
|
||||
Some(reason),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn annotate_reason_transformed(
|
||||
@@ -194,7 +202,57 @@ impl Settings {
|
||||
reason: &'static str,
|
||||
transformed: bool,
|
||||
) {
|
||||
self.annotate_internal(headers, outcome, hits, ttl, Some(reason), transformed);
|
||||
self.annotate_internal(
|
||||
headers,
|
||||
outcome,
|
||||
hits,
|
||||
ttl as i64,
|
||||
Some(reason),
|
||||
transformed,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn annotate_reason_transformed_freshness(
|
||||
&self,
|
||||
headers: &mut HeaderMap,
|
||||
outcome: &'static str,
|
||||
hits: u64,
|
||||
freshness_ttl: i64,
|
||||
reason: &'static str,
|
||||
transformed: bool,
|
||||
) {
|
||||
self.annotate_internal(
|
||||
headers,
|
||||
outcome,
|
||||
hits,
|
||||
freshness_ttl,
|
||||
Some(reason),
|
||||
transformed,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn annotate_reason_transformed_forwarded(
|
||||
&self,
|
||||
headers: &mut HeaderMap,
|
||||
outcome: &'static str,
|
||||
hits: u64,
|
||||
ttl: u64,
|
||||
reason: &'static str,
|
||||
transformed: bool,
|
||||
forward: &'static str,
|
||||
forward_status: Option<StatusCode>,
|
||||
) {
|
||||
self.annotate_internal(
|
||||
headers,
|
||||
outcome,
|
||||
hits,
|
||||
ttl as i64,
|
||||
Some(reason),
|
||||
transformed,
|
||||
Some((forward, forward_status)),
|
||||
);
|
||||
}
|
||||
|
||||
fn annotate_internal(
|
||||
@@ -202,9 +260,10 @@ impl Settings {
|
||||
headers: &mut HeaderMap,
|
||||
outcome: &'static str,
|
||||
hits: u64,
|
||||
ttl: u64,
|
||||
ttl: i64,
|
||||
reason: Option<&'static str>,
|
||||
transformed: bool,
|
||||
forwarded: Option<(&'static str, Option<StatusCode>)>,
|
||||
) {
|
||||
if !self.enabled {
|
||||
return;
|
||||
@@ -245,15 +304,26 @@ impl Settings {
|
||||
);
|
||||
}
|
||||
if self.expose_cache_status {
|
||||
let mut value = if matches!(
|
||||
reason,
|
||||
Some("cache_revalidated" | "cache_revalidated_uncached")
|
||||
) {
|
||||
"RustProxy; fwd=stale; fwd-status=304".to_owned()
|
||||
let forwarded = forwarded.or_else(|| {
|
||||
matches!(
|
||||
reason,
|
||||
Some("cache_revalidated" | "cache_revalidated_uncached")
|
||||
)
|
||||
.then_some(("stale", Some(StatusCode::NOT_MODIFIED)))
|
||||
});
|
||||
let mut value = if let Some((why, status)) = forwarded {
|
||||
let mut value = format!("RustProxy; fwd={why}");
|
||||
if let Some(status) = status {
|
||||
value.push_str(&format!("; fwd-status={}", status.as_u16()));
|
||||
}
|
||||
value
|
||||
} else {
|
||||
match outcome {
|
||||
"HIT" => format!("RustProxy; hit; ttl={ttl}"),
|
||||
"MISS" => "RustProxy; fwd=miss".to_owned(),
|
||||
"BYPASS" if reason == Some("request_method") => {
|
||||
"RustProxy; fwd=method".to_owned()
|
||||
}
|
||||
_ => "RustProxy; fwd=bypass".to_owned(),
|
||||
}
|
||||
};
|
||||
|
||||
Vendored
+15
@@ -126,12 +126,18 @@ impl Drop for ColdReadGuard<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn cache_status_ttl(ttl: u64, age: u64) -> i64 {
|
||||
const MAX: i128 = 999_999_999_999_999;
|
||||
(i128::from(ttl) - i128::from(age)).clamp(-MAX, MAX) as i64
|
||||
}
|
||||
|
||||
pub struct Hit {
|
||||
pub response: Response,
|
||||
body: Bytes,
|
||||
pub hits: u64,
|
||||
pub transformed: bool,
|
||||
pub remaining_ttl: u64,
|
||||
pub cache_status_ttl: i64,
|
||||
pub resident_age_seconds: u64,
|
||||
pub stale: bool,
|
||||
pub background_revalidate: bool,
|
||||
@@ -163,6 +169,15 @@ impl Revalidation {
|
||||
pub fn reusable_without_validation(&self) -> bool {
|
||||
self.reusable_without_validation
|
||||
}
|
||||
pub fn cache_status_forward_reason(&self, request_forces_validation: bool) -> &'static str {
|
||||
if self.age >= self.ttl {
|
||||
"stale"
|
||||
} else if request_forces_validation && self.reusable_without_validation {
|
||||
"request"
|
||||
} else {
|
||||
"miss"
|
||||
}
|
||||
}
|
||||
pub fn headers(&self) -> &HeaderMap {
|
||||
&self.headers
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -173,6 +173,7 @@ impl Revalidation {
|
||||
hits: self.hits,
|
||||
transformed: self.transformed,
|
||||
remaining_ttl: self.ttl.saturating_sub(self.age),
|
||||
cache_status_ttl: cache_status_ttl(self.ttl, self.age),
|
||||
stale: false,
|
||||
background_revalidate: false,
|
||||
resident_age_seconds: self.resident_age_at_lookup.as_secs(),
|
||||
|
||||
Vendored
+1
@@ -329,6 +329,7 @@ impl Cache {
|
||||
hits,
|
||||
transformed: cached.transformed,
|
||||
remaining_ttl: 0,
|
||||
cache_status_ttl: cache_status_ttl(cached.ttl, cached.age),
|
||||
resident_age_seconds: cached.resident_age_at_lookup.as_secs(),
|
||||
stale: true,
|
||||
background_revalidate: false,
|
||||
|
||||
@@ -448,7 +448,7 @@ mod cache_fast_path_tests {
|
||||
let app = Router::new().fallback(move |request: Request| {
|
||||
let count = count.clone();
|
||||
async move {
|
||||
count.fetch_add(1, Ordering::Relaxed);
|
||||
let request_number = count.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
let (parts, body) = request.into_parts();
|
||||
if parts.uri.path() == "/slow" {
|
||||
tokio::time::sleep(Duration::from_millis(25)).await;
|
||||
@@ -475,8 +475,10 @@ mod cache_fast_path_tests {
|
||||
"/backend-no-cache" | "/cache-headers-revalidate" => {
|
||||
"public, no-cache, max-age=600"
|
||||
}
|
||||
"/backend-zero-stale" => "public, max-age=0, stale-if-error=120",
|
||||
"/grace" => "public, max-age=1",
|
||||
"/backend-zero-stale" | "/stale-then-503" => {
|
||||
"public, max-age=0, stale-if-error=120"
|
||||
}
|
||||
"/grace" | "/stale-no-validator" => "public, max-age=1",
|
||||
_ => "public, max-age=600",
|
||||
}),
|
||||
);
|
||||
@@ -521,7 +523,13 @@ mod cache_fast_path_tests {
|
||||
response.headers_mut().insert(echoed, value.clone());
|
||||
}
|
||||
}
|
||||
if parts
|
||||
if parts.uri.path() == "/stale-no-validator" {
|
||||
response.headers_mut().remove("etag");
|
||||
}
|
||||
if parts.uri.path() == "/stale-then-503" && request_number > 1 {
|
||||
*response.status_mut() = StatusCode::SERVICE_UNAVAILABLE;
|
||||
*response.body_mut() = Body::from("origin failed");
|
||||
} else if parts
|
||||
.headers
|
||||
.get("if-none-match")
|
||||
.is_some_and(|v| v == "\"origin-v1\"")
|
||||
@@ -719,6 +727,69 @@ mod cache_fast_path_tests {
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rfc9211_fresh_forced_validation_is_request_not_stale() {
|
||||
let origin = origin().await;
|
||||
let mut config = config_for(origin.addr, true);
|
||||
config.cache.varnish_compatible = false;
|
||||
let fixture = Fixture::new(config).await;
|
||||
let first = fixture.get("/vary", &[]).await;
|
||||
assert_eq!(first.headers["x-cache"], "MISS");
|
||||
assert!(cache_chain(&first.headers).last().unwrap().starts_with("RustProxy; fwd=miss"));
|
||||
let revalidated = fixture.get("/vary", &[("cache-control", "no-cache")]).await;
|
||||
assert_eq!(revalidated.status, StatusCode::OK);
|
||||
assert_eq!(revalidated.headers["x-cache"], "HIT");
|
||||
assert_eq!(
|
||||
cache_chain(&revalidated.headers).last().copied(),
|
||||
Some("RustProxy; fwd=request; fwd-status=304; detail=\"cache_revalidated\""),
|
||||
);
|
||||
let posted = fixture.send(Method::POST, "/echo", &[], Body::from("posted")).await;
|
||||
assert_eq!(posted.status, StatusCode::OK);
|
||||
assert_eq!(posted.body, "posted");
|
||||
assert_eq!(
|
||||
cache_chain(&posted.headers).last().copied(),
|
||||
Some("RustProxy; fwd=method; detail=\"request_method\""),
|
||||
);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rfc9211_upstream_503_stale_if_error_reports_forwarded_status() {
|
||||
let origin = origin().await;
|
||||
let fixture = Fixture::new(config_for(origin.addr, true)).await;
|
||||
let first = fixture.get("/stale-then-503", &[]).await;
|
||||
assert_eq!(first.status, StatusCode::OK);
|
||||
assert_eq!(first.headers["x-cache"], "MISS");
|
||||
let stale = fixture.get("/stale-then-503", &[]).await;
|
||||
assert_eq!(stale.status, StatusCode::OK);
|
||||
assert_eq!(stale.headers["x-cache"], "HIT");
|
||||
assert_eq!(
|
||||
cache_chain(&stale.headers).last().copied(),
|
||||
Some("RustProxy; fwd=stale; fwd-status=503; detail=\"cache_stale_if_error\""),
|
||||
);
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 2);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rfc9211_stale_origin_200_is_fwd_stale_not_fwd_miss() {
|
||||
let origin = origin().await;
|
||||
let mut config = config_for(origin.addr, true);
|
||||
config.cache.varnish_grace_seconds = 0;
|
||||
let fixture = Fixture::new(config).await;
|
||||
let first = fixture.get("/stale-no-validator", &[]).await;
|
||||
assert_eq!(first.headers["x-cache"], "MISS");
|
||||
tokio::time::sleep(Duration::from_millis(1200)).await;
|
||||
let stale = fixture.get("/stale-no-validator", &[]).await;
|
||||
assert_eq!(stale.status, StatusCode::OK);
|
||||
assert_eq!(stale.headers["x-cache"], "MISS");
|
||||
assert!(
|
||||
cache_chain(&stale.headers).last().unwrap().starts_with("RustProxy; fwd=stale"),
|
||||
);
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 2);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn proxy_cache_on_preserves_vary_private_revalidation_and_ranges() {
|
||||
let origin = origin().await;
|
||||
@@ -869,6 +940,12 @@ mod cache_fast_path_tests {
|
||||
stale.headers["x-cache-reason"],
|
||||
"cache_stale_while_revalidate"
|
||||
);
|
||||
let cache_status = cache_chain(&stale.headers);
|
||||
let last = cache_status.last().unwrap();
|
||||
let freshness = last.strip_prefix("RustProxy; hit; ttl=").unwrap()
|
||||
.split(';').next().unwrap().parse::<i64>().unwrap();
|
||||
assert!(freshness <= 0, "expired response must not claim positive freshness");
|
||||
assert!(last.ends_with("; detail=\"cache_stale_while_revalidate\""));
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
assert_eq!(
|
||||
origin.requests.load(Ordering::Relaxed),
|
||||
|
||||
@@ -107,11 +107,11 @@ if let Some(ticket) = &cache_ticket {
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner()) = reason;
|
||||
}
|
||||
cache_settings.annotate_reason_transformed(
|
||||
cache_settings.annotate_reason_transformed_freshness(
|
||||
hit.response.headers_mut(),
|
||||
"HIT",
|
||||
hit.hits,
|
||||
hit.remaining_ttl,
|
||||
hit.cache_status_ttl,
|
||||
reason,
|
||||
hit.transformed,
|
||||
);
|
||||
@@ -158,20 +158,30 @@ if let Some(ticket) = &cache_ticket {
|
||||
.unwrap_or_else(|e| e.into_inner()) = reason;
|
||||
}
|
||||
}
|
||||
let serve_stale = |reason: &'static str, elapsed: Duration, origin_backend: &str| -> Option<Response> {
|
||||
let serve_stale = |reason: &'static str, elapsed: Duration, origin_backend: &str,
|
||||
upstream_status: Option<StatusCode>| -> Option<Response> {
|
||||
let cached = cached_revalidation.as_ref()?;
|
||||
let mut hit = state.cache.stale_if_error_hit(cached)?;
|
||||
if let crate::cache::RangeSelection::Single(range) = range_selection {
|
||||
hit = hit.apply_range(range, if_range.as_ref());
|
||||
}
|
||||
cache_settings.annotate_reason_transformed(
|
||||
hit.response.headers_mut(),
|
||||
"HIT",
|
||||
hit.hits,
|
||||
0,
|
||||
reason,
|
||||
hit.transformed,
|
||||
);
|
||||
if reason == "cache_stale_if_error" {
|
||||
cache_settings.annotate_reason_transformed_forwarded(
|
||||
hit.response.headers_mut(),
|
||||
"HIT",
|
||||
hit.hits,
|
||||
0,
|
||||
reason,
|
||||
hit.transformed,
|
||||
"stale",
|
||||
upstream_status,
|
||||
);
|
||||
} else {
|
||||
cache_settings.annotate_reason_transformed_freshness(
|
||||
hit.response.headers_mut(), "HIT", hit.hits, hit.cache_status_ttl,
|
||||
reason, hit.transformed,
|
||||
);
|
||||
}
|
||||
if hit.transformed {
|
||||
state.transform_metrics.record_cache_hit(scope);
|
||||
}
|
||||
@@ -254,11 +264,11 @@ if handshake.is_none()
|
||||
if conditional {
|
||||
hit.response = crate::cache::make_not_modified(hit.response);
|
||||
}
|
||||
cache_settings.annotate_reason_transformed(
|
||||
cache_settings.annotate_reason_transformed_freshness(
|
||||
hit.response.headers_mut(),
|
||||
"HIT",
|
||||
hit.hits,
|
||||
hit.remaining_ttl,
|
||||
hit.cache_status_ttl,
|
||||
reason,
|
||||
hit.transformed,
|
||||
);
|
||||
@@ -296,7 +306,7 @@ if handshake.is_none()
|
||||
crate::cache::CollapseTimeoutFallback::StaleIfAvailable
|
||||
) {
|
||||
if let Some(stale) =
|
||||
serve_stale("cache_collapse_timeout_stale", Duration::ZERO, &backend.id)
|
||||
serve_stale("cache_collapse_timeout_stale", Duration::ZERO, &backend.id, None)
|
||||
{
|
||||
return stale;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
};
|
||||
let stale_class = classify_stale_error(&cause);
|
||||
if cache_settings.allows_stale_error_class(stale_class) {
|
||||
if let Some(stale) = serve_stale("cache_stale_if_error", elapsed, &active_backend.id) {
|
||||
if let Some(stale) = serve_stale("cache_stale_if_error", elapsed, &active_backend.id, None) {
|
||||
return stale;
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@
|
||||
.allows_stale_error_class(crate::cache::StaleErrorClass::Timeout)
|
||||
{
|
||||
if let Some(stale) =
|
||||
serve_stale("cache_stale_if_error", upstream_started.elapsed(), &active_backend.id)
|
||||
serve_stale("cache_stale_if_error", upstream_started.elapsed(), &active_backend.id, None)
|
||||
{
|
||||
return stale;
|
||||
}
|
||||
|
||||
@@ -21,13 +21,17 @@ if response.status() == StatusCode::NOT_MODIFIED && revalidation_sent {
|
||||
}
|
||||
let resident_age = hit.resident_age_seconds;
|
||||
let mut response = hit.response;
|
||||
cache_settings.annotate_reason_transformed(
|
||||
cache_settings.annotate_reason_transformed_forwarded(
|
||||
response.headers_mut(),
|
||||
"HIT",
|
||||
hit.hits,
|
||||
hit.remaining_ttl,
|
||||
"cache_revalidated",
|
||||
hit.transformed,
|
||||
cached.cache_status_forward_reason(
|
||||
revalidation_reason.is_some() || freshness_revalidation_reason.is_some(),
|
||||
),
|
||||
Some(StatusCode::NOT_MODIFIED),
|
||||
);
|
||||
if hit.transformed {
|
||||
state.transform_metrics.record_cache_hit(scope);
|
||||
@@ -45,13 +49,17 @@ if response.status() == StatusCode::NOT_MODIFIED && revalidation_sent {
|
||||
} else {
|
||||
cached.response(merged)
|
||||
};
|
||||
cache_settings.annotate_reason_transformed(
|
||||
cache_settings.annotate_reason_transformed_forwarded(
|
||||
response.headers_mut(),
|
||||
"HIT",
|
||||
0,
|
||||
0,
|
||||
"cache_revalidated_uncached",
|
||||
cached.transformed(),
|
||||
cached.cache_status_forward_reason(
|
||||
revalidation_reason.is_some() || freshness_revalidation_reason.is_some(),
|
||||
),
|
||||
Some(StatusCode::NOT_MODIFIED),
|
||||
);
|
||||
if cached.transformed() {
|
||||
state.transform_metrics.record_cache_hit(scope);
|
||||
@@ -182,8 +190,18 @@ let transformed = response
|
||||
.extensions()
|
||||
.get::<crate::transform::CachedTransformed>()
|
||||
.is_some();
|
||||
cache_settings.annotate_reason_transformed(
|
||||
response.headers_mut(), outcome, 0, 0, reason, transformed,
|
||||
);
|
||||
if let Some(cached) = &cached_revalidation {
|
||||
cache_settings.annotate_reason_transformed_forwarded(
|
||||
response.headers_mut(), outcome, 0, 0, reason, transformed,
|
||||
cached.cache_status_forward_reason(
|
||||
revalidation_reason.is_some() || freshness_revalidation_reason.is_some(),
|
||||
),
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
cache_settings.annotate_reason_transformed(
|
||||
response.headers_mut(), outcome, 0, 0, reason, transformed,
|
||||
);
|
||||
}
|
||||
response
|
||||
}
|
||||
@@ -24,6 +24,7 @@ if cache_settings.allows_stale_error_status(response.status()) {
|
||||
"cache_stale_if_error",
|
||||
upstream_started.elapsed(),
|
||||
&active_backend.id,
|
||||
Some(response.status()),
|
||||
) {
|
||||
return stale;
|
||||
}
|
||||
|
||||
@@ -96,8 +96,8 @@
|
||||
if request_method == Method::HEAD {
|
||||
*response.body_mut() = Body::empty();
|
||||
}
|
||||
cache_settings.annotate_reason_transformed(response.headers_mut(), "HIT", hit.hits,
|
||||
hit.remaining_ttl, "cache_hit", hit.transformed);
|
||||
cache_settings.annotate_reason_transformed_freshness(response.headers_mut(), "HIT", hit.hits,
|
||||
hit.cache_status_ttl, "cache_hit", hit.transformed);
|
||||
cache_settings.annotate_resident_age(response.headers_mut(), hit.resident_age_seconds);
|
||||
state.cache.record(scope, &host, "HIT", "cache_hit");
|
||||
if hit.transformed { state.transform_metrics.record_cache_hit(scope); }
|
||||
@@ -257,9 +257,20 @@
|
||||
}
|
||||
state.cache.record(scope, &host, outcome, reason);
|
||||
let transformed = response.extensions().get::<crate::transform::Applied>().is_some();
|
||||
cache_settings.annotate_reason_transformed(
|
||||
response.headers_mut(), outcome, 0, 0, reason, transformed,
|
||||
);
|
||||
if let Some(cached) = &cached_revalidation {
|
||||
cache_settings.annotate_reason_transformed_forwarded(
|
||||
response.headers_mut(), outcome, 0, 0, reason, transformed,
|
||||
cached.cache_status_forward_reason(
|
||||
policy.revalidation_reason().is_some()
|
||||
|| cached.request_freshness_revalidation_reason(&freshness_headers).is_some(),
|
||||
),
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
cache_settings.annotate_reason_transformed(
|
||||
response.headers_mut(), outcome, 0, 0, reason, transformed,
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
if !enabled && identity_request && request_method == Method::GET
|
||||
&& !transform_settings.is_some_and(|settings| settings.enabled)
|
||||
|
||||
@@ -129,13 +129,21 @@ pub(super) fn finish_web_revalidation(
|
||||
if *method == Method::HEAD {
|
||||
*response.body_mut() = Body::empty();
|
||||
}
|
||||
settings.annotate_reason_transformed(
|
||||
let request_forces_validation = crate::cache::request_policy(
|
||||
method, client, true, "", false, settings,
|
||||
).revalidation_reason().is_some()
|
||||
|| cached.request_freshness_revalidation_reason(
|
||||
&crate::cache::request_freshness_headers(client, settings),
|
||||
).is_some();
|
||||
settings.annotate_reason_transformed_forwarded(
|
||||
response.headers_mut(),
|
||||
"HIT",
|
||||
hits,
|
||||
ttl,
|
||||
reason,
|
||||
transformed,
|
||||
cached.cache_status_forward_reason(request_forces_validation),
|
||||
Some(StatusCode::NOT_MODIFIED),
|
||||
);
|
||||
settings.annotate_resident_age(response.headers_mut(), resident_age);
|
||||
Some(WebRevalidated {
|
||||
|
||||
@@ -147,6 +147,22 @@ async fn browser_refresh_304_preserves_web_server_hit_counter() {
|
||||
assert_eq!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 4);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn strict_browser_refresh_uses_request_forward_reason() {
|
||||
let (_dir, service, cache, ticket, mut settings) = fixture().await;
|
||||
settings.varnish_compatible = false;
|
||||
let mut client = HeaderMap::new();
|
||||
client.insert("cache-control", HeaderValue::from_static("no-cache"));
|
||||
let response = validate(&service, &cache, &ticket, &settings, Method::GET, client)
|
||||
.await.unwrap().response;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.headers()["x-cache"], "HIT");
|
||||
assert_eq!(
|
||||
response.headers()["cache-status"],
|
||||
"RustProxy; fwd=request; fwd-status=304; detail=\"cache_revalidated\"",
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn browser_conditional_refresh_returns_304_without_resetting_hits() {
|
||||
let (_dir, service, cache, ticket, settings) = fixture().await;
|
||||
|
||||
Reference in new issue
Block a user