v0.33.8
This commit is contained in:
1 parent
6b937aaf85
commit
87c8393d86
79 files changed
+6727
-297
No files matched your search
@@ -167,7 +167,7 @@ let body_empty = if cache_enabled
|
||||
}
|
||||
}
|
||||
} else {
|
||||
request.body().is_end_stream()
|
||||
cache_enabled && request.body().is_end_stream()
|
||||
};
|
||||
let public_suffix = request
|
||||
.uri()
|
||||
@@ -175,29 +175,18 @@ let public_suffix = request
|
||||
.map(|v| v.as_str())
|
||||
.unwrap_or("/")
|
||||
.to_owned();
|
||||
let force_cache = policy_reason.is_none()
|
||||
&& cache_settings.active_for(route.cache)
|
||||
&& route
|
||||
let cache_plan = cache_plan::CachePlan::new(policy_reason, cache_settings, &path, || {
|
||||
cache_settings.active_for(route.cache) && route
|
||||
.frontend
|
||||
.as_ref()
|
||||
.is_some_and(|frontend| frontend.force_cache.matches(&path, &public_suffix));
|
||||
let ignore_static_cookie = !force_cache && cache_settings.ignore_cookie_for_path(&path);
|
||||
let original_policy = policy_reason
|
||||
.map(crate::cache::RequestPolicy::Bypass)
|
||||
.unwrap_or_else(|| {
|
||||
if force_cache {
|
||||
crate::cache::forced_request_policy(request.method(), request.headers(), body_empty)
|
||||
} else {
|
||||
crate::cache::request_policy(
|
||||
request.method(),
|
||||
request.headers(),
|
||||
body_empty,
|
||||
&path,
|
||||
request.uri().query().is_some(),
|
||||
cache_settings,
|
||||
)
|
||||
}
|
||||
});
|
||||
.is_some_and(|frontend| frontend.force_cache.matches(&path, &public_suffix))
|
||||
});
|
||||
let force_cache = cache_plan.force_cache();
|
||||
let ignore_static_cookie = cache_plan.ignore_cookie();
|
||||
let original_policy = cache_plan.request_policy(
|
||||
request.method(), request.headers(), body_empty, &path,
|
||||
request.uri().query().is_some(), cache_settings,
|
||||
);
|
||||
|
||||
let only_cached =
|
||||
cache_settings.active_for(route.cache) && crate::cache::only_if_cached(request.headers());
|
||||
@@ -282,48 +271,31 @@ if websocket_legacy_support {
|
||||
}
|
||||
}
|
||||
}
|
||||
let range_selection = crate::cache::request_range(request.method(), request.headers());
|
||||
let if_range = request.headers().get("if-range").cloned();
|
||||
let range_requested = !matches!(range_selection, crate::cache::RangeSelection::None);
|
||||
let post_policy = if force_cache {
|
||||
crate::cache::forced_request_policy(request.method(), request.headers(), body_empty)
|
||||
} else {
|
||||
crate::cache::request_policy(
|
||||
request.method(),
|
||||
request.headers(),
|
||||
body_empty,
|
||||
&path,
|
||||
request.uri().query().is_some(),
|
||||
cache_settings,
|
||||
let effective_policy = cache_plan::combine_policy(original_policy, || {
|
||||
cache_plan.request_policy(
|
||||
request.method(), request.headers(), body_empty, &path,
|
||||
request.uri().query().is_some(), cache_settings,
|
||||
)
|
||||
};
|
||||
});
|
||||
let force_vary_cookie = force_cache && request.headers().contains_key("cookie");
|
||||
let force_vary_authorization = force_cache && request.headers().contains_key("authorization");
|
||||
let effective_policy = match (original_policy, post_policy) {
|
||||
(crate::cache::RequestPolicy::Bypass(reason), _) => {
|
||||
crate::cache::RequestPolicy::Bypass(reason)
|
||||
}
|
||||
(_, crate::cache::RequestPolicy::Bypass(reason)) => {
|
||||
crate::cache::RequestPolicy::Bypass(reason)
|
||||
}
|
||||
(crate::cache::RequestPolicy::Revalidate(reason), _) => {
|
||||
crate::cache::RequestPolicy::Revalidate(reason)
|
||||
}
|
||||
(_, crate::cache::RequestPolicy::Revalidate(reason)) => {
|
||||
crate::cache::RequestPolicy::Revalidate(reason)
|
||||
}
|
||||
_ => crate::cache::RequestPolicy::Use,
|
||||
};
|
||||
let effective_reason = effective_policy.bypass_reason();
|
||||
let revalidation_reason = effective_policy.revalidation_reason();
|
||||
let effective_cacheable = effective_reason.is_none();
|
||||
let range_selection = cache_plan::range_selection(
|
||||
effective_cacheable, request.method(), request.headers(),
|
||||
);
|
||||
let if_range = if effective_cacheable {
|
||||
request.headers().get("if-range").cloned()
|
||||
} else { None };
|
||||
let range_requested = !matches!(range_selection, crate::cache::RangeSelection::None);
|
||||
|
||||
// Match Varnish-style cache identity: a load-balanced backend member must not
|
||||
// fragment the object cache. The vhost scope + canonical host + request target
|
||||
// already identify the public resource; Vary handles representation variants.
|
||||
let cache_route = route.id.as_str();
|
||||
let mut cache_key_headers = None;
|
||||
if ignore_static_cookie && request.headers().contains_key("cookie") {
|
||||
if effective_cacheable && ignore_static_cookie && request.headers().contains_key("cookie") {
|
||||
let mut headers = request.headers().clone();
|
||||
headers.remove("cookie");
|
||||
cache_key_headers = Some(headers);
|
||||
@@ -344,7 +316,9 @@ let cache_ticket = if effective_cacheable {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let client_conditionals = request.headers().clone();
|
||||
let client_conditionals = if cache_ticket.is_some() {
|
||||
request.headers().clone()
|
||||
} else { HeaderMap::new() };
|
||||
let cached_revalidation = if let Some(ticket) = cache_ticket.as_ref() {
|
||||
state.cache.revalidation_tiered(ticket).await
|
||||
} else {
|
||||
|
||||
@@ -94,7 +94,8 @@ if response.status() == StatusCode::NOT_MODIFIED && revalidation_sent {
|
||||
// Both origin and transformed policies must be cacheable; hiding a
|
||||
// Set-Cookie/private header never authorizes storage of private content.
|
||||
let transformed_plan =
|
||||
if ignore_static_cookie && crate::cache::varies_on(response.headers(), "cookie") {
|
||||
if origin_plan.is_none()
|
||||
|| (ignore_static_cookie && crate::cache::varies_on(response.headers(), "cookie")) {
|
||||
None
|
||||
} else {
|
||||
crate::cache::plan(
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
let origin_validation_headers = response.headers().clone();
|
||||
// Only a real cache revalidation needs origin headers after response rules.
|
||||
let origin_validation_headers = if revalidation_sent
|
||||
&& response.status() == StatusCode::NOT_MODIFIED
|
||||
{
|
||||
response.headers().clone()
|
||||
} else { HeaderMap::new() };
|
||||
if let Some(exchange) = &exchange {
|
||||
exchange
|
||||
.upstream_status
|
||||
|
||||
@@ -119,7 +119,9 @@ if websocket_upstream_h2 {
|
||||
// Do not classify a slow client upload as a slow origin response. Probes
|
||||
// independently measure origin liveness/latency with body-free requests.
|
||||
let request_body_empty = request.body().size_hint().exact() == Some(0);
|
||||
let proactive_base_headers = request.headers().clone();
|
||||
let proactive_base_headers = if cache_ticket.is_some() && cache_settings.proactive_refresh_enabled {
|
||||
request.headers().clone()
|
||||
} else { HeaderMap::new() };
|
||||
let response_header_timeout = frontend
|
||||
.and_then(|frontend| frontend.proxy_timeouts.response_header_timeout())
|
||||
.unwrap_or(state.header_timeout);
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
// makes PHP-FPM omit HTTP_HOST, breaking WordPress.
|
||||
request.headers_mut().insert(HOST, original_host.clone());
|
||||
let request_method = request.method().clone();
|
||||
let request_headers = request.headers().clone();
|
||||
// This branch owns local-origin delivery. Move its headers instead of
|
||||
// cloning them; the request remains only for exchange/body extensions.
|
||||
let mut request_headers = std::mem::take(request.headers_mut());
|
||||
let request_uri = request.uri().clone();
|
||||
let request_version = request.version();
|
||||
let public_suffix = request_uri.path_and_query()
|
||||
@@ -41,32 +43,33 @@
|
||||
Err(_) => return error(StatusCode::BAD_REQUEST,
|
||||
"RustProxy: invalid or incomplete request body\n"),
|
||||
}
|
||||
} else { request.body().is_end_stream() };
|
||||
let force_cache = enabled && route.frontend.as_ref().is_some_and(|frontend|
|
||||
frontend.force_cache.matches(&path, &public_suffix));
|
||||
let policy = if enabled {
|
||||
if force_cache {
|
||||
crate::cache::forced_request_policy(&request_method, &request_headers, empty_body)
|
||||
} else {
|
||||
crate::cache::request_policy(
|
||||
&request_method, &request_headers, empty_body, &path,
|
||||
request_uri.query().is_some(), cache_settings)
|
||||
}
|
||||
} else {
|
||||
crate::cache::RequestPolicy::Bypass("cache_disabled")
|
||||
};
|
||||
} else { enabled && request.body().is_end_stream() };
|
||||
let cache_plan = cache_plan::CachePlan::new(
|
||||
(!enabled).then_some("cache_disabled"), cache_settings, &path,
|
||||
|| route.frontend.as_ref().is_some_and(|frontend|
|
||||
frontend.force_cache.matches(&path, &public_suffix)),
|
||||
);
|
||||
let force_cache = cache_plan.force_cache();
|
||||
let ignore_cookie = cache_plan.ignore_cookie();
|
||||
let policy = cache_plan.request_policy(
|
||||
&request_method, &request_headers, empty_body, &path,
|
||||
request_uri.query().is_some(), cache_settings,
|
||||
);
|
||||
// HEAD can use an existing GET representation but may not fill a cache
|
||||
// entry with its empty downstream body.
|
||||
let eligible = enabled && policy.bypass_reason().is_none();
|
||||
let mut key_headers = request_headers.clone();
|
||||
let ignore_cookie = !force_cache && cache_settings.ignore_cookie_for_path(&path);
|
||||
if ignore_cookie { key_headers.remove("cookie"); }
|
||||
let key_headers = if eligible && ignore_cookie && request_headers.contains_key("cookie") {
|
||||
let mut headers = request_headers.clone();
|
||||
headers.remove("cookie");
|
||||
Some(headers)
|
||||
} else { None };
|
||||
let cache_ticket = if eligible {
|
||||
state.cache.ticket_scoped(state.cache.revision(), scope, context.scheme,
|
||||
original_host.to_str().unwrap_or(""), &route.id, &public_suffix, &key_headers)
|
||||
original_host.to_str().unwrap_or(""), &route.id, &public_suffix,
|
||||
key_headers.as_ref().unwrap_or(&request_headers))
|
||||
} else { None };
|
||||
let range = crate::cache::request_range(&request_method, &request_headers);
|
||||
let if_range = request_headers.get("if-range").cloned();
|
||||
let range = cache_plan::range_selection(eligible, &request_method, &request_headers);
|
||||
let if_range = if eligible { request_headers.get("if-range").cloned() } else { None };
|
||||
let range_requested = !matches!(range, crate::cache::RangeSelection::None);
|
||||
// Unlike the original Web Server path, retain stale and client-forced
|
||||
// revalidation candidates. A local 304 can refresh them without replacing
|
||||
@@ -122,9 +125,14 @@
|
||||
// Neither the Request nor the streaming Body is held across a borrowed
|
||||
// await: the filesystem/PHP source owns the body for its full lifetime.
|
||||
let static_body = std::mem::replace(request.body_mut(), Body::empty());
|
||||
let (origin_request_headers, revalidation_sent) =
|
||||
let (origin_request_headers, revalidation_sent) = if cache_ticket.is_some() {
|
||||
web_cache_revalidation::web_origin_request_headers(
|
||||
&request_headers, cached_revalidation.as_ref());
|
||||
&request_headers, cached_revalidation.as_ref())
|
||||
} else {
|
||||
// No cache result can need the original conditionals later. Preserve
|
||||
// all origin HTTP semantics by handing over the untouched header map.
|
||||
(std::mem::take(&mut request_headers), false)
|
||||
};
|
||||
let started = Instant::now();
|
||||
let response = crate::web_server::serve(service, request_method.clone(),
|
||||
origin_request_headers, request_uri, &service_path, static_body,
|
||||
@@ -181,25 +189,27 @@
|
||||
state.cache.discard(ticket);
|
||||
}
|
||||
}
|
||||
let origin_headers = response.headers().clone();
|
||||
let original_plan = crate::cache::plan(response.status(), &origin_headers,
|
||||
cache_settings, started.elapsed());
|
||||
// Do not allow a later response-header rewrite to override a private,
|
||||
// explicitly uncacheable PHP/CGI origin response.
|
||||
let explicit_origin_policy = origin_headers.contains_key("cache-control")
|
||||
|| origin_headers.contains_key("expires")
|
||||
|| origin_headers.contains_key("set-cookie")
|
||||
|| origin_headers.contains_key("vary");
|
||||
let origin_safe = !explicit_origin_policy || original_plan.is_some();
|
||||
let no_transform = crate::transform::no_transform(&origin_headers);
|
||||
let skip_compression = crate::compression::no_transform(&origin_headers);
|
||||
let origin_headers = response.headers();
|
||||
let origin_safe = cache_ticket.is_some() && {
|
||||
let explicit_origin_policy = origin_headers.contains_key("cache-control")
|
||||
|| origin_headers.contains_key("expires")
|
||||
|| origin_headers.contains_key("set-cookie")
|
||||
|| origin_headers.contains_key("vary");
|
||||
!explicit_origin_policy || crate::cache::plan(response.status(), origin_headers,
|
||||
cache_settings, started.elapsed()).is_some()
|
||||
};
|
||||
let no_transform = crate::transform::no_transform(origin_headers);
|
||||
let skip_compression = crate::compression::no_transform(origin_headers);
|
||||
let origin_validators = crate::transform::OriginValidators {
|
||||
etag: origin_headers.get("etag").cloned(),
|
||||
last_modified: origin_headers.get("last-modified").cloned(),
|
||||
};
|
||||
if skip_compression || range_requested {
|
||||
response.extensions_mut().insert(crate::compression::Skip);
|
||||
}
|
||||
response.extensions_mut().insert(crate::transform::OriginValidators {
|
||||
etag: origin_headers.get("etag").cloned(),
|
||||
last_modified: origin_headers.get("last-modified").cloned(),
|
||||
});
|
||||
response.extensions_mut().insert(origin_validators);
|
||||
apply_response_rules(response.headers_mut(), &snapshot, route.frontend.as_deref(),
|
||||
Some(route), false);
|
||||
if response.extensions().get::<crate::pages::PageServed>().is_some() {
|
||||
@@ -271,4 +281,4 @@
|
||||
let transformed = response.extensions().get::<crate::transform::Applied>().is_some();
|
||||
cache_settings.annotate_transformed(response.headers_mut(), transformed);
|
||||
response
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user