v0.34.7
This commit is contained in:
1 parent
3ea00045bf
commit
1cd6b7ac99
22 files changed
+550
-149
No files matched your search
@@ -470,10 +470,12 @@ mod cache_fast_path_tests {
|
||||
.insert("etag", HeaderValue::from_static("\"origin-v1\""));
|
||||
response.headers_mut().insert(
|
||||
"cache-control",
|
||||
HeaderValue::from_static(if parts.uri.path() == "/private" {
|
||||
"private, max-age=600"
|
||||
} else {
|
||||
"public, max-age=600"
|
||||
HeaderValue::from_static(match parts.uri.path() {
|
||||
"/private" => "private, max-age=600",
|
||||
"/backend-no-cache" => "public, no-cache, max-age=600",
|
||||
"/backend-zero-stale" => "public, max-age=0, stale-if-error=120",
|
||||
"/grace" => "public, max-age=1",
|
||||
_ => "public, max-age=600",
|
||||
}),
|
||||
);
|
||||
response
|
||||
@@ -612,7 +614,7 @@ mod cache_fast_path_tests {
|
||||
.await;
|
||||
assert_eq!(validated.status, StatusCode::OK);
|
||||
assert_eq!(validated.headers["x-cache"], "HIT");
|
||||
assert_eq!(validated.headers["x-cache-reason"], "cache_revalidated");
|
||||
assert_eq!(validated.headers["x-cache-reason"], "cache_hit", "default compatibility mode must not revalidate a fresh object");
|
||||
assert_eq!(validated.body, "origin-a");
|
||||
let conditional = fixture
|
||||
.get(
|
||||
@@ -631,7 +633,87 @@ mod cache_fast_path_tests {
|
||||
"MISS"
|
||||
);
|
||||
}
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 6);
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 5);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn compatible_only_if_cached_on_miss_never_contacts_origin() {
|
||||
let origin = origin().await;
|
||||
let fixture = Fixture::new(config_for(origin.addr, true)).await;
|
||||
let miss = fixture.get("/missing", &[("cache-control", "no-cache, only-if-cached")]).await;
|
||||
assert_eq!(miss.status, StatusCode::GATEWAY_TIMEOUT);
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 0, "only-if-cached MISS must not contact backend");
|
||||
let primed = fixture.get("/vary", &[]).await;
|
||||
assert_eq!(primed.headers["x-cache"], "MISS");
|
||||
let hit = fixture.get("/vary", &[("cache-control", "only-if-cached")]).await;
|
||||
assert_eq!(hit.headers["x-cache"], "HIT");
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 1);
|
||||
let blocked = fixture.get("/vary", &[("cache-control", "no-store")]).await;
|
||||
assert_eq!(blocked.headers["x-cache-reason"], "request_no_store");
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 2, "no-store must bypass even a fresh HIT");
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn compatible_origin_no_cache_preserves_etag_revalidation_and_fallback_ttl() {
|
||||
let origin = origin().await;
|
||||
let mut config = config_for(origin.addr, true);
|
||||
config.cache.default_ttl_seconds = 0;
|
||||
let fixture = Fixture::new(config).await;
|
||||
assert_eq!(fixture.get("/backend-no-cache", &[]).await.headers["x-cache"], "MISS");
|
||||
let cached_only = fixture
|
||||
.get("/backend-no-cache", &[("cache-control", "only-if-cached")])
|
||||
.await;
|
||||
assert_eq!(cached_only.status, StatusCode::GATEWAY_TIMEOUT);
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 1);
|
||||
let validated = fixture.get("/backend-no-cache", &[]).await;
|
||||
assert_eq!(validated.status, StatusCode::OK);
|
||||
assert_eq!(validated.headers["x-cache"], "HIT");
|
||||
assert_eq!(validated.headers["x-cache-reason"], "cache_revalidated");
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 2);
|
||||
fixture.stop().await;
|
||||
|
||||
let origin = origin().await;
|
||||
let mut config = config_for(origin.addr, true);
|
||||
config.cache.default_ttl_seconds = 45;
|
||||
let fixture = Fixture::new(config).await;
|
||||
assert_eq!(fixture.get("/backend-no-cache", &[]).await.headers["x-cache"], "MISS");
|
||||
let reused = fixture.get("/backend-no-cache", &[]).await;
|
||||
assert_eq!(reused.headers["x-cache"], "HIT");
|
||||
assert_eq!(reused.headers["x-cache-reason"], "cache_hit");
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 1, "configured fallback TTL avoids origin revalidation");
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn only_if_cached_stale_grace_never_background_refreshes_origin() {
|
||||
let origin = origin().await;
|
||||
let fixture = Fixture::new(config_for(origin.addr, true)).await;
|
||||
assert_eq!(fixture.get("/grace", &[]).await.headers["x-cache"], "MISS");
|
||||
tokio::time::sleep(Duration::from_millis(1200)).await;
|
||||
let stale = fixture.get("/grace", &[("cache-control", "only-if-cached")]).await;
|
||||
assert_eq!(stale.status, StatusCode::OK);
|
||||
assert_eq!(stale.headers["x-cache"], "HIT");
|
||||
assert_eq!(stale.headers["x-cache-reason"], "cache_stale_while_revalidate");
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
assert_eq!(origin.requests.load(Ordering::Relaxed), 1, "only-if-cached must not start async validation");
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn web_server_only_if_cached_on_miss_never_reads_origin() {
|
||||
let directory = TempDir::new().unwrap();
|
||||
let fixture = Fixture::new(web_config(&directory, true)).await;
|
||||
let miss = fixture.get("/style.css", &[("cache-control", "only-if-cached")]).await;
|
||||
assert_eq!(miss.status, StatusCode::GATEWAY_TIMEOUT);
|
||||
assert_eq!(fixture.get("/style.css", &[]).await.headers["x-cache"], "MISS");
|
||||
let hit = fixture.get("/style.css", &[("cache-control", "only-if-cached")]).await;
|
||||
assert_eq!(hit.headers["x-cache"], "HIT");
|
||||
let bypass = fixture.get("/style.css", &[("cache-control", "no-store, only-if-cached")]).await;
|
||||
assert_eq!(bypass.status, StatusCode::GATEWAY_TIMEOUT, "uncacheable only-if-cached must not read the Web Server file");
|
||||
let reload = fixture.get("/style.css", &[("cache-control", "no-cache")]).await;
|
||||
assert_eq!(reload.headers["x-cache-reason"], "cache_hit");
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@ if let Some(ticket) = &cache_ticket {
|
||||
} else {
|
||||
"cache_hit"
|
||||
};
|
||||
if hit.background_revalidate {
|
||||
// only-if-cached forbids any origin communication, including
|
||||
// asynchronous grace validation and proactive refresh.
|
||||
if hit.background_revalidate && !only_cached {
|
||||
if let (Some(cached), Some(guard)) = (
|
||||
cached_revalidation.clone(),
|
||||
cached_revalidation
|
||||
@@ -66,6 +68,7 @@ if let Some(ticket) = &cache_ticket {
|
||||
}
|
||||
}
|
||||
if cache_settings.proactive_refresh_enabled
|
||||
&& !only_cached
|
||||
&& !hit.stale
|
||||
&& hit.remaining_ttl > 0
|
||||
{
|
||||
|
||||
@@ -177,9 +177,8 @@ let original_policy = cache_plan.request_policy(
|
||||
request.uri().query().is_some(), cache_settings,
|
||||
);
|
||||
|
||||
let only_cached =
|
||||
cache_settings.active_for(route.cache) && !cache_settings.varnish_compatible
|
||||
&& crate::cache::only_if_cached(request.headers());
|
||||
let only_cached = cache_settings.active_for(route.cache)
|
||||
&& crate::cache::only_if_cached(request.headers());
|
||||
let origin_suffix = match route.rewritten_path(&path) {
|
||||
Ok(None) => public_suffix.clone(),
|
||||
Ok(Some(rewritten)) => {
|
||||
|
||||
@@ -113,8 +113,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
if cache_ticket.is_some() && !cache_settings.varnish_compatible
|
||||
&& crate::cache::only_if_cached(&request_headers) {
|
||||
if enabled && crate::cache::only_if_cached(&request_headers) {
|
||||
return error(StatusCode::GATEWAY_TIMEOUT,
|
||||
"RustProxy: only-if-cached request has no usable Web Server representation\n");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user