v0.32.2
This commit is contained in:
1 parent
101df02a5f
commit
bb025dcf44
27 files changed
+541
-85
No files matched your search
@@ -0,0 +1,166 @@
|
||||
use super::*;
|
||||
use crate::{cache, web_server};
|
||||
use http_body_util::BodyExt;
|
||||
use tempfile::TempDir;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn cache_settings() -> Settings {
|
||||
Settings {
|
||||
enabled: true,
|
||||
expose_status: true,
|
||||
expose_hits: true,
|
||||
expose_cache_status: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
id: "wordpress".into(),
|
||||
name: "WordPress".into(),
|
||||
document_root: dir.path().to_path_buf(),
|
||||
cache_control: Some("public, max-age=604800".into()),
|
||||
..Default::default()
|
||||
}.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 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();
|
||||
(dir, service, cache, ticket, settings)
|
||||
}
|
||||
|
||||
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(),
|
||||
web_server::RequestContext {
|
||||
client_ip: "127.0.0.1".parse().unwrap(),
|
||||
client_port: Some(12345),
|
||||
server_name: "blog.example.test".into(),
|
||||
server_port: 443,
|
||||
tls: true,
|
||||
version: http::Version::HTTP_2,
|
||||
},
|
||||
).await
|
||||
}
|
||||
|
||||
async fn validate(
|
||||
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");
|
||||
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, |_| {})
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn browser_refresh_304_preserves_web_server_hit_counter() {
|
||||
let (_dir, service, cache, ticket, settings) = fixture().await;
|
||||
assert_eq!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 1);
|
||||
for directive in ["max-age=0", "no-cache"] {
|
||||
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;
|
||||
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();
|
||||
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!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 4);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn browser_conditional_refresh_returns_304_without_resetting_hits() {
|
||||
let (_dir, service, cache, ticket, settings) = fixture().await;
|
||||
let etag = cache.revalidation_tiered(&ticket).await.unwrap().headers()["etag"].clone();
|
||||
let mut client = HeaderMap::new();
|
||||
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;
|
||||
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_eq!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mismatched_browser_validator_cannot_validate_different_cached_object() {
|
||||
let (_dir, service, cache, ticket, settings) = fixture().await;
|
||||
let mut client = HeaderMap::new();
|
||||
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;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(response.into_body().collect().await.unwrap().to_bytes(), "before");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn range_and_head_requests_use_validated_cached_body() {
|
||||
let (_dir, service, cache, ticket, settings) = fixture().await;
|
||||
let mut range = HeaderMap::new();
|
||||
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;
|
||||
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!(head.status(), StatusCode::OK);
|
||||
assert!(head.into_body().collect().await.unwrap().to_bytes().is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn changed_local_file_is_not_falsely_revalidated() {
|
||||
let (dir, service, cache, ticket, settings) = fixture().await;
|
||||
// A size change guarantees different ETag even on filesystems with
|
||||
// coarse modification-time resolution.
|
||||
std::fs::write(dir.path().join("shCore.js"), b"new file content").unwrap();
|
||||
let cached = cache.revalidation_tiered(&ticket).await.unwrap();
|
||||
let (headers, sent) = web_origin_request_headers(&HeaderMap::new(), Some(&cached));
|
||||
assert!(sent);
|
||||
let origin = serve_file(&service, Method::GET, headers).await;
|
||||
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();
|
||||
assert_eq!(cache.get_tiered(&ticket, &settings).await.unwrap().hits, 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn 304_new_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("etag", cached.headers()["etag"].clone());
|
||||
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!(cache.revalidation_tiered(&ticket).await.is_none());
|
||||
}
|
||||
Reference in new issue
Block a user