Files
RustProxy/src/proxy/forward/response.rs
T
2026-10-25 10:15:26 +01:00

133 lines
5.5 KiB
Rust

{
match upstream_result {
Ok(Ok(mut response)) => {
if let Some(fallback) = execute_error_fallback(
state.clone(), peer, local, &snapshot, &host, tls, route,
response.status(), &fallback_request, &fallback_body,
).await {
return fallback;
}
let (origin_validation_headers, origin_plan, origin_reason, mut response) =
include!("success_prepare.rs");
include!("success_finish.rs")
}
Ok(Err(cause)) => {
if let Some(session) = h2_direct_session.take() {
session.cancellation().cancel();
}
let elapsed = upstream_started.elapsed();
let connect_failure = cause.is_connect();
let detail = upstream_failure_detail(&cause, &active_backend, elapsed, connect_failure);
let failure_class =
upstream_health_error_class(&cause, connect_failure, &active_backend);
let graceful_h2_close = matches!(
failure_class,
"h2_graceful_goaway" | "h2_graceful_close"
);
if let Some(exchange) = &exchange {
exchange.websocket_error("upstream_request_failed", "upstream_http_error");
*exchange.upstream_error.lock().unwrap_or_else(|e| e.into_inner()) = Some(detail.clone());
}
if let Some(lease) = &active_lease {
lease.error_detail(failure_class, &detail, final_attempt_elapsed);
}
if connect_failure {
tracing::warn!(
backend = %active_backend.id,
origin = %active_backend.origin,
request_origin = %active_backend.request_origin,
tls_verify = active_backend.tls_verify,
elapsed_ms = elapsed.as_millis(),
error = %detail,
"upstream connection failed"
);
} else if graceful_h2_close {
tracing::trace!(
backend = %active_backend.id,
origin = %active_backend.origin,
elapsed_ms = elapsed.as_millis(),
error = %detail,
"upstream H2 graceful close left an unrecovered request"
);
} else {
tracing::debug!(
backend = %active_backend.id,
origin = %active_backend.origin,
elapsed_ms = elapsed.as_millis(),
error = %detail,
"upstream HTTP request failed"
);
}
let status = if connect_failure {
StatusCode::SERVICE_UNAVAILABLE
} else {
StatusCode::BAD_GATEWAY
};
let stale_class = classify_stale_error(&cause);
if cache_settings.allows_stale_error_class(stale_class) {
if let Some(stale) = serve_stale("cache_stale_if_error", elapsed, &active_backend.id, None) {
return stale;
}
}
if let Some(fallback) = execute_error_fallback(
state.clone(), peer, local, &snapshot, &host, tls, route,
status, &fallback_request, &fallback_body,
).await {
return fallback;
}
let mut response = error(
status,
if status == StatusCode::SERVICE_UNAVAILABLE {
"RustProxy: upstream unavailable (DNS, TCP or TLS connection failed)\n"
} else {
"RustProxy: upstream did not provide a valid HTTP response\n"
},
);
response
.headers_mut()
.insert("cache-control", HeaderValue::from_static("no-store"));
if status == StatusCode::SERVICE_UNAVAILABLE {
response
.headers_mut()
.insert("retry-after", HeaderValue::from_static("1"));
}
response
}
Err(_) => {
if let Some(session) = h2_direct_session.take() {
session.cancellation().cancel();
}
if let Some(exchange) = &exchange {
exchange.websocket_error("upstream_timeout", "header_timeout");
*exchange.upstream_error.lock().unwrap_or_else(|e| e.into_inner()) = Some("response_header_timeout".into());
}
if let Some(lease) = &active_lease {
lease.error_detail(
"response_header_timeout",
"response_header_timeout",
final_attempt_elapsed,
);
}
if cache_settings
.allows_stale_error_class(crate::cache::StaleErrorClass::Timeout)
{
if let Some(stale) =
serve_stale("cache_stale_if_error", upstream_started.elapsed(), &active_backend.id, None)
{
return stale;
}
}
if let Some(fallback) = execute_error_fallback(
state.clone(), peer, local, &snapshot, &host, tls, route,
StatusCode::GATEWAY_TIMEOUT, &fallback_request, &fallback_body,
).await {
return fallback;
}
error(
StatusCode::GATEWAY_TIMEOUT,
"RustProxy: upstream response deadline exceeded\n",
)
}
}
}