This commit is contained in:
Mateusz Gruszczyński committed 2026-10-10 00:18:06 +02:00
1 parent ad95bfb050
commit 1431dd47f0
62 files changed
+1360 -198

No files matched your search

+3 -3
View File
@@ -121,7 +121,7 @@ if let Some(ticket) = &cache_ticket {
hit.resident_age_seconds,
);
hit.response.extensions_mut().insert(ResponseRulesApplied);
return hit.response;
return intercept_route_origin_error(hit.response, route, &snapshot);
}
state.cache.record(scope, &host, "MISS", "cache_miss");
miss_recorded = true;
@@ -188,7 +188,7 @@ let serve_stale = |reason: &'static str, elapsed: Duration, origin_backend: &str
exchange.cache_status.store(1, Ordering::Relaxed);
*exchange.cache_reason.lock().unwrap_or_else(|e| e.into_inner()) = reason;
}
Some(hit.response)
Some(intercept_route_origin_error(hit.response, route, &snapshot))
};
if only_cached {
let mut response = error(
@@ -281,7 +281,7 @@ if handshake.is_none()
.lock()
.unwrap_or_else(|e| e.into_inner()) = reason;
}
return hit.response;
return intercept_route_origin_error(hit.response, route, &snapshot);
}
collapse_rounds = collapse_rounds.saturating_add(1);
if collapse_rounds >= 8 {
+4
View File
@@ -7,6 +7,10 @@
).await {
return fallback;
}
// Keep the upstream body as hyper::body::Incoming until the
// HTTP/2 WebSocket and regular response paths have adapted it.
// The opt-in application error page is applied to Response<Body>
// in success_prepare.rs, before transformation/cache admission.
let (origin_validation_headers, origin_plan, origin_reason, mut response) =
include!("success_prepare.rs");
include!("success_finish.rs")
+1 -1
View File
@@ -87,7 +87,7 @@ if response.status() == StatusCode::NOT_MODIFIED && revalidation_sent {
.unwrap_or_else(|e| e.into_inner()) = "cache_revalidated";
}
state.cache.record(scope, &host, "HIT", "cache_revalidated");
return revalidated;
return intercept_route_origin_error(revalidated, route, &snapshot);
}
}
}
+27
View File
@@ -333,6 +333,33 @@ let mut response = if h3_tunnel {
}
Response::from_parts(parts, Body::new(body))
};
// The upstream HTTP client returns Response<hyper::body::Incoming>. Adapt it
// before rendering a RustProxy error page, so HTTP/2 WebSocket tunnels can
// continue using the original Incoming body without type erasure.
// An internal named-location fallback has already won earlier in response.rs.
let intercept_origin_error = !route.internal && route.frontend.as_ref().is_some_and(|frontend| {
frontend.pages.intercept_statuses.contains(&response.status().as_u16())
});
// Mark every upstream response as application-supplied, even if this vhost
// does not intercept its status. The observer must not overwrite it merely
// because a global 404/5xx error page exists.
response = intercept_route_origin_error(response, route, &snapshot);
if intercept_origin_error {
// The earlier response rules modified the original origin headers;
// reapply them to the newly rendered page so the vhost's header policy
// remains effective. Do not allow a custom header rule to cache errors.
apply_response_rules(
response.headers_mut(),
&snapshot,
route.frontend.as_deref(),
Some(route),
admin_control_plane,
);
response.headers_mut().insert(
"cache-control",
HeaderValue::from_static("no-store"),
);
}
response.extensions_mut().insert(ResponseRulesApplied);
response
.extensions_mut()
+7 -2
View File
@@ -102,7 +102,7 @@
}
response.extensions_mut().insert(ResponseRulesApplied);
response.extensions_mut().insert(StaticSourceResponse);
return response;
return intercept_route_origin_error(response, route, &snapshot);
}
}
}
@@ -115,7 +115,7 @@
// await: the filesystem/PHP source owns the body for its full lifetime.
let static_body = std::mem::replace(request.body_mut(), Body::empty());
let started = Instant::now();
let mut response = crate::web_server::serve(service, request_method.clone(),
let response = crate::web_server::serve(service, request_method.clone(),
request_headers.clone(), request_uri, &service_path, static_body,
crate::web_server::RequestContext {
client_ip: context.client_ip,
@@ -130,6 +130,7 @@
response.status(), &fallback_request, &fallback_body,
).await { return fallback; }
let mut response = intercept_route_origin_error(response, route, &snapshot);
let origin_headers = response.headers().clone();
let original_plan = crate::cache::plan(response.status(), &origin_headers,
cache_settings, started.elapsed());
@@ -151,6 +152,10 @@
});
apply_response_rules(response.headers_mut(), &snapshot, route.frontend.as_deref(),
Some(route), false);
if response.extensions().get::<crate::pages::PageServed>().is_some() {
// A vhost header rule must not make a replaced error cacheable.
response.headers_mut().insert("cache-control", HeaderValue::from_static("no-store"));
}
response.extensions_mut().insert(ResponseRulesApplied);
response.extensions_mut().insert(StaticSourceResponse);