v0.34.12
This commit is contained in:
1 parent
b3dfd570b5
commit
a4042d6439
16 files changed
+362
-55
No files matched your search
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user