0.32.7
This commit is contained in:
1 parent
1b9135117c
commit
4f36de240c
32 files changed
+1175
-704
No files matched your search
@@ -28,7 +28,7 @@
|
||||
}
|
||||
if let Some(lease) = &active_lease {
|
||||
lease.error_detail(
|
||||
upstream_error_class(&cause, connect_failure),
|
||||
upstream_health_error_class(&cause, connect_failure, &active_backend),
|
||||
&detail,
|
||||
final_attempt_elapsed,
|
||||
);
|
||||
|
||||
@@ -141,6 +141,7 @@ let mut active_uri = upstream_target;
|
||||
let mut request_attempt = Some(request);
|
||||
let mut attempted_backends = Vec::<String>::new();
|
||||
let mut retries = 0u8;
|
||||
let mut graceful_retry_done = false;
|
||||
let upstream_result = loop {
|
||||
let client = if handshake.is_some() && !websocket_upstream_h2 {
|
||||
&active_backend.websocket_client
|
||||
@@ -153,6 +154,36 @@ let upstream_result = loop {
|
||||
)
|
||||
.await;
|
||||
let attempt_elapsed = attempt_started.elapsed();
|
||||
if let Ok(Err(cause)) = &result {
|
||||
if !graceful_retry_done
|
||||
&& !admin_control_plane
|
||||
&& handshake.is_none()
|
||||
&& request_body_empty
|
||||
&& retry_safe_method(&retry_method)
|
||||
&& (is_graceful_h2_goaway(cause)
|
||||
|| is_related_h2_close(cause, &active_backend))
|
||||
{
|
||||
if let Some(next_request) = retry_request(
|
||||
&retry_method,
|
||||
upstream_request_version(&active_backend, false),
|
||||
&retry_headers,
|
||||
&active_backend,
|
||||
&original_host,
|
||||
&origin_suffix,
|
||||
retry_host_follows_backend,
|
||||
) {
|
||||
graceful_retry_done = true;
|
||||
if let Some(lease) = &active_lease {
|
||||
lease.retry_attempted();
|
||||
}
|
||||
tracing::debug!(backend = %active_backend.id,
|
||||
"retrying safe empty request after graceful upstream H2 shutdown");
|
||||
request_attempt = Some(next_request);
|
||||
attempt_started = Instant::now();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(policy) = retry_policy.as_ref() else {
|
||||
break result;
|
||||
};
|
||||
@@ -220,7 +251,7 @@ let upstream_result = loop {
|
||||
connect_failure,
|
||||
);
|
||||
lease.error_detail(
|
||||
upstream_error_class(cause, connect_failure),
|
||||
upstream_health_error_class(cause, connect_failure, &active_backend),
|
||||
&detail,
|
||||
attempt_elapsed,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
fn is_graceful_h2_goaway(error: &(dyn std::error::Error + 'static)) -> bool {
|
||||
crate::upstream::is_graceful_h2_goaway(error)
|
||||
}
|
||||
|
||||
fn is_related_h2_close(
|
||||
error: &(dyn std::error::Error + 'static),
|
||||
backend: &crate::config::CompiledBackend,
|
||||
) -> bool {
|
||||
let chain = format!("{error:?}");
|
||||
backend.client.recently_saw_graceful_goaway()
|
||||
&& chain.contains("Canceled")
|
||||
&& chain.contains("connection closed")
|
||||
}
|
||||
|
||||
fn upstream_health_error_class(
|
||||
error: &(dyn std::error::Error + 'static),
|
||||
connect_failure: bool,
|
||||
backend: &crate::config::CompiledBackend,
|
||||
) -> &'static str {
|
||||
if is_graceful_h2_goaway(error) {
|
||||
"h2_graceful_goaway"
|
||||
} else if is_related_h2_close(error, backend) {
|
||||
"h2_graceful_close"
|
||||
} else {
|
||||
upstream_error_class(error, connect_failure)
|
||||
}
|
||||
}
|
||||
@@ -571,3 +571,28 @@ mod authority_tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod graceful_h2_tests {
|
||||
use super::*;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct H2DebugError(&'static str);
|
||||
impl fmt::Display for H2DebugError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "client error (SendRequest): {}", self.0)
|
||||
}
|
||||
}
|
||||
impl std::error::Error for H2DebugError {}
|
||||
|
||||
#[test]
|
||||
fn only_graceful_remote_h2_goaway_is_neutral() {
|
||||
let valid = H2DebugError("GoAway(b\"\", NO_ERROR, Remote)");
|
||||
let protocol = H2DebugError("GoAway(b\"\", PROTOCOL_ERROR, Remote)");
|
||||
let local = H2DebugError("GoAway(b\"\", NO_ERROR, Local)");
|
||||
assert!(is_graceful_h2_goaway(&valid));
|
||||
assert!(!is_graceful_h2_goaway(&protocol));
|
||||
assert!(!is_graceful_h2_goaway(&local));
|
||||
}
|
||||
}
|
||||
+8
-5
@@ -138,6 +138,8 @@ fn upstream_error_class(
|
||||
}
|
||||
}
|
||||
|
||||
include!("h2_graceful.rs");
|
||||
|
||||
fn retry_safe_method(method: &Method) -> bool {
|
||||
method == Method::GET || method == Method::HEAD || method == Method::OPTIONS
|
||||
}
|
||||
@@ -171,11 +173,12 @@ fn upstream_request_target(
|
||||
websocket_h1: bool,
|
||||
websocket_h2: bool,
|
||||
) -> Option<Uri> {
|
||||
let h2_capable = !websocket_h1 && match backend.protocol {
|
||||
crate::config::UpstreamProtocol::Http1 => false,
|
||||
crate::config::UpstreamProtocol::Auto => backend.origin.starts_with("https://"),
|
||||
crate::config::UpstreamProtocol::Http2 | crate::config::UpstreamProtocol::H2 => true,
|
||||
};
|
||||
let h2_capable = !websocket_h1
|
||||
&& match backend.protocol {
|
||||
crate::config::UpstreamProtocol::Http1 => false,
|
||||
crate::config::UpstreamProtocol::Auto => backend.origin.starts_with("https://"),
|
||||
crate::config::UpstreamProtocol::Http2 | crate::config::UpstreamProtocol::H2 => true,
|
||||
};
|
||||
if !h2_capable {
|
||||
return Some(uri.clone());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user