v0.34.7
This commit is contained in:
1 parent
1cd6b7ac99
commit
58c7d63194
5 files changed
+281
-71
No files matched your search
@@ -12,9 +12,6 @@ Release history focused on **features, settings, default behavior, compatibility
|
||||
|
||||
- **Settings → Cache → Varnish-compatible cache policy (`varnish_compatible`)** stays **ON by default**, including upgraded configurations without the field. An adjacent **i** popover (PL/EN) explains its effects, limitations and safety controls. OFF retains strict client hard-reload behavior. The live toggle invalidates existing entries across HOT/COLD.
|
||||
- Only client hard-reload `Cache-Control: no-cache`, `max-age=0` and `Pragma: no-cache` are ignored when considering a **fresh HIT**, retaining `Age` and hit counters. Kept the original `Cache-Control: no-store` bypass, `only-if-cached` 504 on cache MISS and no background origin refresh on HIT, and `min-fresh`/`max-stale`/positive `max-age` freshness constraints (including explicit `max-stale=0` during grace). Original request headers, Vary, access/privacy rules and Web Server handling remain intact.
|
||||
- **Origin `no-cache` regression fix:** ETag/Last-Modified entries remain cacheable for conditional revalidation when fallback TTL is disabled. With **explicit positive fallback TTL**, only origin responses marked **`public`** may be reused for that TTL (capped by max TTL), even without origin validators. This is an explicit administrative override of origin `no-cache`, not the default. `private`, `no-store`, `Set-Cookie` and compatibility-mode `Surrogate-Control: no-store` remain vetoes.
|
||||
- **Zero TTL regression fix:** keep expired representations for revalidation, client `max-stale` and configured `stale-if-error`; an expired object does not become a fresh HIT. Implicit grace defaults to **60 seconds**, can be set in Settings → Cache (`varnish_grace_seconds`, 0 disables it), and applies only to eligible objects stored fresh with no `stale-while-revalidate` directive; explicit `stale-while-revalidate=0` is never overwritten.
|
||||
- **UI layout:** nested Settings tab bars no longer stick over Cache checkboxes while scrolling; the top-level Settings navigation remains sticky.
|
||||
- Source-level regression cases cover hard reload, fallback TTL, validator revalidation, no-store/private/Surrogate protections, zero-TTL stale-if-error, min-fresh/max-stale, and `only-if-cached` for proxy and Web Server. Native Rust compilation and runtime acceptance are required before a production release claim. This remains a Varnish-inspired policy subset, not a VCL clone.
|
||||
|
||||
## 0.34.6 — Docker live logs, neutral Publish switch and Vhost Content-Type rules (source candidate)
|
||||
|
||||
@@ -614,7 +614,10 @@ 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_hit", "default compatibility mode must not revalidate a fresh object");
|
||||
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(
|
||||
@@ -641,17 +644,29 @@ mod cache_fast_path_tests {
|
||||
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;
|
||||
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");
|
||||
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;
|
||||
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");
|
||||
assert_eq!(
|
||||
origin.requests.load(Ordering::Relaxed),
|
||||
2,
|
||||
"no-store must bypass even a fresh HIT"
|
||||
);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
@@ -661,7 +676,10 @@ mod cache_fast_path_tests {
|
||||
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");
|
||||
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;
|
||||
@@ -678,11 +696,18 @@ mod cache_fast_path_tests {
|
||||
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");
|
||||
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");
|
||||
assert_eq!(
|
||||
origin.requests.load(Ordering::Relaxed),
|
||||
1,
|
||||
"configured fallback TTL avoids origin revalidation"
|
||||
);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
@@ -692,12 +717,21 @@ mod cache_fast_path_tests {
|
||||
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;
|
||||
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");
|
||||
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");
|
||||
assert_eq!(
|
||||
origin.requests.load(Ordering::Relaxed),
|
||||
1,
|
||||
"only-if-cached must not start async validation"
|
||||
);
|
||||
fixture.stop().await;
|
||||
}
|
||||
|
||||
@@ -705,14 +739,32 @@ mod cache_fast_path_tests {
|
||||
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;
|
||||
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!(
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ pub(super) fn web_origin_request_headers(
|
||||
cached: Option<&Revalidation>,
|
||||
) -> (HeaderMap, bool) {
|
||||
let mut headers = client.clone();
|
||||
let Some(cached) = cached else { return (headers, false) };
|
||||
let Some(cached) = cached else {
|
||||
return (headers, false);
|
||||
};
|
||||
headers.remove("if-none-match");
|
||||
headers.remove("if-modified-since");
|
||||
let sent = cached.apply_validators(&mut headers);
|
||||
@@ -25,7 +27,12 @@ fn web_merged_validation_headers(cached: &Revalidation, validation: &HeaderMap)
|
||||
let mut merged = cached.merge_validation_headers(validation);
|
||||
if cached.transformed() {
|
||||
for name in [
|
||||
"content-encoding", "vary", "accept-ranges", "content-md5", "digest", "content-digest",
|
||||
"content-encoding",
|
||||
"vary",
|
||||
"accept-ranges",
|
||||
"content-md5",
|
||||
"digest",
|
||||
"content-digest",
|
||||
] {
|
||||
merged.remove(name);
|
||||
for value in cached.headers().get_all(name) {
|
||||
@@ -50,7 +57,8 @@ pub(super) fn finish_web_revalidation(
|
||||
apply_rules: impl FnOnce(&mut HeaderMap),
|
||||
) -> Option<WebRevalidated> {
|
||||
let origin_merged = web_merged_validation_headers(cached, validation);
|
||||
let origin_safe = crate::cache::plan(StatusCode::OK, &origin_merged, settings, elapsed).is_some();
|
||||
let origin_safe =
|
||||
crate::cache::plan(StatusCode::OK, &origin_merged, settings, elapsed).is_some();
|
||||
let mut merged = origin_merged;
|
||||
apply_rules(&mut merged);
|
||||
if force_cache {
|
||||
@@ -66,8 +74,17 @@ pub(super) fn finish_web_revalidation(
|
||||
let range = crate::cache::request_range(method, client);
|
||||
let if_range = client.get("if-range");
|
||||
let published = if can_store {
|
||||
cache.publish_revalidated(ticket.clone(), cached, merged.clone(), validation, settings, elapsed)
|
||||
} else { None };
|
||||
cache.publish_revalidated(
|
||||
ticket.clone(),
|
||||
cached,
|
||||
merged.clone(),
|
||||
validation,
|
||||
settings,
|
||||
elapsed,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (mut response, hits, ttl, resident_age, transformed, reason) =
|
||||
if let Some(mut hit) = published {
|
||||
if !client_not_modified {
|
||||
@@ -76,8 +93,12 @@ pub(super) fn finish_web_revalidation(
|
||||
}
|
||||
}
|
||||
(
|
||||
hit.response, hit.hits, hit.remaining_ttl, hit.resident_age_seconds,
|
||||
hit.transformed, "cache_revalidated",
|
||||
hit.response,
|
||||
hit.hits,
|
||||
hit.remaining_ttl,
|
||||
hit.resident_age_seconds,
|
||||
hit.transformed,
|
||||
"cache_revalidated",
|
||||
)
|
||||
} else {
|
||||
if !cache.ticket_current(ticket) {
|
||||
@@ -93,7 +114,14 @@ pub(super) fn finish_web_revalidation(
|
||||
} else {
|
||||
cached.response(merged)
|
||||
};
|
||||
(response, 0, 0, 0, cached.transformed(), "cache_revalidated_uncached")
|
||||
(
|
||||
response,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
cached.transformed(),
|
||||
"cache_revalidated_uncached",
|
||||
)
|
||||
};
|
||||
if client_not_modified {
|
||||
response = crate::cache::make_not_modified(response);
|
||||
@@ -104,7 +132,11 @@ pub(super) fn finish_web_revalidation(
|
||||
settings.annotate_reason(response.headers_mut(), "HIT", hits, ttl, reason);
|
||||
settings.annotate_transformed(response.headers_mut(), transformed);
|
||||
settings.annotate_resident_age(response.headers_mut(), resident_age);
|
||||
Some(WebRevalidated { response, transformed, reason })
|
||||
Some(WebRevalidated {
|
||||
response,
|
||||
transformed,
|
||||
reason,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::*;
|
||||
use crate::{cache, web_server};
|
||||
use http_body_util::BodyExt;
|
||||
use tempfile::TempDir;
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn cache_settings() -> Settings {
|
||||
Settings {
|
||||
@@ -14,7 +14,13 @@ fn cache_settings() -> Settings {
|
||||
}
|
||||
}
|
||||
|
||||
async fn fixture() -> (TempDir, web_server::CompiledService, Arc<Cache>, Ticket, Settings) {
|
||||
async fn fixture() -> (
|
||||
TempDir,
|
||||
web_server::CompiledService,
|
||||
Arc<Cache>,
|
||||
Ticket,
|
||||
Settings,
|
||||
) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::write(dir.path().join("shCore.js"), b"before").unwrap();
|
||||
let service = web_server::Service {
|
||||
@@ -23,26 +29,51 @@ async fn fixture() -> (TempDir, web_server::CompiledService, Arc<Cache>, Ticket,
|
||||
document_root: dir.path().to_path_buf(),
|
||||
cache_control: Some("public, max-age=604800".into()),
|
||||
..Default::default()
|
||||
}.compile().unwrap();
|
||||
}
|
||||
.compile()
|
||||
.unwrap();
|
||||
let cache = Cache::new(1);
|
||||
let settings = cache_settings();
|
||||
let ticket = cache.ticket(
|
||||
1, "https", "blog.example.test", "wordpress",
|
||||
"/shCore.js?ver=3.0.9b", &HeaderMap::new(),
|
||||
).unwrap();
|
||||
let ticket = cache
|
||||
.ticket(
|
||||
1,
|
||||
"https",
|
||||
"blog.example.test",
|
||||
"wordpress",
|
||||
"/shCore.js?ver=3.0.9b",
|
||||
&HeaderMap::new(),
|
||||
)
|
||||
.unwrap();
|
||||
let initial = serve_file(&service, Method::GET, HeaderMap::new()).await;
|
||||
assert_eq!(initial.status(), StatusCode::OK);
|
||||
let plan = cache::plan(initial.status(), initial.headers(), &settings, Duration::ZERO)
|
||||
.expect("static origin response is cacheable");
|
||||
cache.wrap(initial, ticket.clone(), plan, &settings)
|
||||
.into_body().collect().await.unwrap();
|
||||
let plan = cache::plan(
|
||||
initial.status(),
|
||||
initial.headers(),
|
||||
&settings,
|
||||
Duration::ZERO,
|
||||
)
|
||||
.expect("static origin response is cacheable");
|
||||
cache
|
||||
.wrap(initial, ticket.clone(), plan, &settings)
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap();
|
||||
(dir, service, cache, ticket, settings)
|
||||
}
|
||||
|
||||
async fn serve_file(service: &web_server::CompiledService, method: Method, headers: HeaderMap) -> Response {
|
||||
async fn serve_file(
|
||||
service: &web_server::CompiledService,
|
||||
method: Method,
|
||||
headers: HeaderMap,
|
||||
) -> Response {
|
||||
web_server::serve(
|
||||
service, method, headers,
|
||||
"/shCore.js?ver=3.0.9b".parse().unwrap(), "/shCore.js", Body::empty(),
|
||||
service,
|
||||
method,
|
||||
headers,
|
||||
"/shCore.js?ver=3.0.9b".parse().unwrap(),
|
||||
"/shCore.js",
|
||||
Body::empty(),
|
||||
web_server::RequestContext {
|
||||
client_ip: "127.0.0.1".parse().unwrap(),
|
||||
client_port: Some(12345),
|
||||
@@ -51,22 +82,41 @@ async fn serve_file(service: &web_server::CompiledService, method: Method, heade
|
||||
tls: true,
|
||||
version: http::Version::HTTP_2,
|
||||
},
|
||||
).await
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn validate(
|
||||
service: &web_server::CompiledService, cache: &Cache, ticket: &Ticket,
|
||||
settings: &Settings, method: Method, client: HeaderMap,
|
||||
service: &web_server::CompiledService,
|
||||
cache: &Cache,
|
||||
ticket: &Ticket,
|
||||
settings: &Settings,
|
||||
method: Method,
|
||||
client: HeaderMap,
|
||||
) -> Option<WebRevalidated> {
|
||||
let cached = cache.revalidation_tiered(ticket).await.unwrap();
|
||||
let (headers, sent) = web_origin_request_headers(&client, Some(&cached));
|
||||
assert!(sent, "stored validator must be used for local source revalidation");
|
||||
assert!(
|
||||
sent,
|
||||
"stored validator must be used for local source revalidation"
|
||||
);
|
||||
let origin = serve_file(service, method.clone(), headers).await;
|
||||
if origin.status() != StatusCode::NOT_MODIFIED {
|
||||
return None;
|
||||
}
|
||||
finish_web_revalidation(cache, ticket, &cached, origin.headers(), settings,
|
||||
Duration::ZERO, &client, &method, false, false, |_| {})
|
||||
finish_web_revalidation(
|
||||
cache,
|
||||
ticket,
|
||||
&cached,
|
||||
origin.headers(),
|
||||
settings,
|
||||
Duration::ZERO,
|
||||
&client,
|
||||
&method,
|
||||
false,
|
||||
false,
|
||||
|_| {},
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -77,13 +127,22 @@ async fn browser_refresh_304_preserves_web_server_hit_counter() {
|
||||
let mut client = HeaderMap::new();
|
||||
client.insert("cache-control", directive.parse().unwrap());
|
||||
let response = validate(&service, &cache, &ticket, &settings, Method::GET, client)
|
||||
.await.unwrap().response;
|
||||
.await
|
||||
.unwrap()
|
||||
.response;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.headers()["x-cache"], "HIT");
|
||||
let hits = response.headers()["x-cache-hits"].to_str().unwrap().parse::<u64>().unwrap();
|
||||
let hits = response.headers()["x-cache-hits"]
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.parse::<u64>()
|
||||
.unwrap();
|
||||
assert!(hits >= 2);
|
||||
assert_eq!(response.headers()["x-cache-reason"], "cache_revalidated");
|
||||
assert_eq!(response.into_body().collect().await.unwrap().to_bytes(), "before");
|
||||
assert_eq!(
|
||||
response.into_body().collect().await.unwrap().to_bytes(),
|
||||
"before"
|
||||
);
|
||||
}
|
||||
assert_eq!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 4);
|
||||
}
|
||||
@@ -96,11 +155,19 @@ async fn browser_conditional_refresh_returns_304_without_resetting_hits() {
|
||||
client.insert("cache-control", HeaderValue::from_static("no-cache"));
|
||||
client.insert("if-none-match", etag);
|
||||
let response = validate(&service, &cache, &ticket, &settings, Method::GET, client)
|
||||
.await.unwrap().response;
|
||||
.await
|
||||
.unwrap()
|
||||
.response;
|
||||
assert_eq!(response.status(), StatusCode::NOT_MODIFIED);
|
||||
assert!(!response.headers().contains_key("content-length"));
|
||||
assert_eq!(response.headers()["x-cache-hits"], "1");
|
||||
assert!(response.into_body().collect().await.unwrap().to_bytes().is_empty());
|
||||
assert!(response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.is_empty());
|
||||
assert_eq!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 2);
|
||||
}
|
||||
|
||||
@@ -111,9 +178,14 @@ async fn mismatched_browser_validator_cannot_validate_different_cached_object()
|
||||
client.insert("cache-control", HeaderValue::from_static("no-cache"));
|
||||
client.insert("if-none-match", HeaderValue::from_static("\"unrelated\""));
|
||||
let response = validate(&service, &cache, &ticket, &settings, Method::GET, client)
|
||||
.await.unwrap().response;
|
||||
.await
|
||||
.unwrap()
|
||||
.response;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.into_body().collect().await.unwrap().to_bytes(), "before");
|
||||
assert_eq!(
|
||||
response.into_body().collect().await.unwrap().to_bytes(),
|
||||
"before"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -123,13 +195,33 @@ async fn range_and_head_requests_use_validated_cached_body() {
|
||||
range.insert("cache-control", HeaderValue::from_static("max-age=0"));
|
||||
range.insert("range", HeaderValue::from_static("bytes=0-2"));
|
||||
let partial = validate(&service, &cache, &ticket, &settings, Method::GET, range)
|
||||
.await.unwrap().response;
|
||||
.await
|
||||
.unwrap()
|
||||
.response;
|
||||
assert_eq!(partial.status(), StatusCode::PARTIAL_CONTENT);
|
||||
assert_eq!(partial.into_body().collect().await.unwrap().to_bytes(), "bef");
|
||||
let head = validate(&service, &cache, &ticket, &settings, Method::HEAD, HeaderMap::new())
|
||||
.await.unwrap().response;
|
||||
assert_eq!(
|
||||
partial.into_body().collect().await.unwrap().to_bytes(),
|
||||
"bef"
|
||||
);
|
||||
let head = validate(
|
||||
&service,
|
||||
&cache,
|
||||
&ticket,
|
||||
&settings,
|
||||
Method::HEAD,
|
||||
HeaderMap::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.response;
|
||||
assert_eq!(head.status(), StatusCode::OK);
|
||||
assert!(head.into_body().collect().await.unwrap().to_bytes().is_empty());
|
||||
assert!(head
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes()
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -143,8 +235,12 @@ async fn changed_local_file_is_not_falsely_revalidated() {
|
||||
assert_eq!(origin.status(), StatusCode::OK);
|
||||
let plan = cache::plan(origin.status(), origin.headers(), &settings, Duration::ZERO).unwrap();
|
||||
cache.discard(&ticket);
|
||||
cache.wrap(origin, ticket.clone(), plan, &settings)
|
||||
.into_body().collect().await.unwrap();
|
||||
cache
|
||||
.wrap(origin, ticket.clone(), plan, &settings)
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 1);
|
||||
}
|
||||
|
||||
@@ -153,12 +249,35 @@ async fn response_304_with_private_policy_discards_cached_representation() {
|
||||
let (_dir, _service, cache, ticket, settings) = fixture().await;
|
||||
let cached = cache.revalidation_tiered(&ticket).await.unwrap();
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("cache-control", HeaderValue::from_static("private, max-age=300"));
|
||||
headers.insert(
|
||||
"cache-control",
|
||||
HeaderValue::from_static("private, max-age=300"),
|
||||
);
|
||||
headers.insert("etag", cached.headers()["etag"].clone());
|
||||
let outcome = finish_web_revalidation(&cache, &ticket, &cached, &headers,
|
||||
&settings, Duration::ZERO, &HeaderMap::new(), &Method::GET,
|
||||
false, false, |_| {}).unwrap();
|
||||
let outcome = finish_web_revalidation(
|
||||
&cache,
|
||||
&ticket,
|
||||
&cached,
|
||||
&headers,
|
||||
&settings,
|
||||
Duration::ZERO,
|
||||
&HeaderMap::new(),
|
||||
&Method::GET,
|
||||
false,
|
||||
false,
|
||||
|_| {},
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(outcome.reason, "cache_revalidated_uncached");
|
||||
assert_eq!(outcome.response.into_body().collect().await.unwrap().to_bytes(), "before");
|
||||
assert_eq!(
|
||||
outcome
|
||||
.response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_bytes(),
|
||||
"before"
|
||||
);
|
||||
assert!(cache.revalidation_tiered(&ticket).await.is_none());
|
||||
}
|
||||
@@ -163,7 +163,10 @@ pub(super) fn fastcgi_params(
|
||||
if context.tls { "on" } else { "off" }.into(),
|
||||
);
|
||||
params.insert("GATEWAY_INTERFACE".into(), "CGI/1.1".into());
|
||||
params.insert("SERVER_SOFTWARE".into(), concat!("RustProxy/", env!("CARGO_PKG_VERSION")).into());
|
||||
params.insert(
|
||||
"SERVER_SOFTWARE".into(),
|
||||
concat!("RustProxy/", env!("CARGO_PKG_VERSION")).into(),
|
||||
);
|
||||
params.insert("REDIRECT_STATUS".into(), "200".into());
|
||||
if let Some(value) = headers
|
||||
.get(CONTENT_TYPE)
|
||||
@@ -199,7 +202,9 @@ pub(super) fn fastcgi_params(
|
||||
})
|
||||
.or_insert_with(|| value.to_owned());
|
||||
}
|
||||
params.entry("HTTP_HOST".into()).or_insert_with(|| context.server_name.clone());
|
||||
params
|
||||
.entry("HTTP_HOST".into())
|
||||
.or_insert_with(|| context.server_name.clone());
|
||||
for (name, value) in &php.custom_params {
|
||||
params.insert(name.clone(), value.clone());
|
||||
}
|
||||
@@ -216,7 +221,12 @@ pub(super) fn fastcgi_params(
|
||||
params.insert("PHP_VALUE".into(), value);
|
||||
}
|
||||
if !php.php_admin_values.is_empty() {
|
||||
let value = php.php_admin_values.iter().map(|(name, item)| format!("{name}={item}")).collect::<Vec<_>>().join("\n");
|
||||
let value = php
|
||||
.php_admin_values
|
||||
.iter()
|
||||
.map(|(name, item)| format!("{name}={item}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
params.insert("PHP_ADMIN_VALUE".into(), value);
|
||||
}
|
||||
let mut encoded = Vec::with_capacity(4096);
|
||||
|
||||
Reference in new issue
Block a user