0.32.7
This commit is contained in:
1 parent
1b9135117c
commit
4f36de240c
32 files changed
+1175
-704
No files matched your search
+41
-5
@@ -22,6 +22,13 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
/// Hyper's public error chain omits the GOAWAY reason, but Debug retains
|
||||
/// the peer-initiated H2 `NO_ERROR` variant. Restrict this to graceful closure.
|
||||
pub fn is_graceful_h2_goaway(error: &(dyn std::error::Error + 'static)) -> bool {
|
||||
let chain = format!("{error:?}");
|
||||
chain.contains("GoAway(") && chain.contains("NO_ERROR") && chain.contains("Remote")
|
||||
}
|
||||
|
||||
static MONOTONIC_START: OnceLock<Instant> = OnceLock::new();
|
||||
type RawProxyClient = Client<super::TimedConnector, Body>;
|
||||
|
||||
@@ -54,6 +61,7 @@ struct RotatingClient {
|
||||
observed_at_ms: AtomicU64,
|
||||
observed_h1: AtomicU64,
|
||||
observed_h2: AtomicU64,
|
||||
graceful_goaway_at_ms: AtomicU64,
|
||||
}
|
||||
|
||||
/// Backend HTTP client with bounded generation lifetime.
|
||||
@@ -107,19 +115,43 @@ impl ProxyClient {
|
||||
observed_at_ms: AtomicU64::new(0),
|
||||
observed_h1: AtomicU64::new(0),
|
||||
observed_h2: AtomicU64::new(0),
|
||||
graceful_goaway_at_ms: AtomicU64::new(0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn request(&self, request: Request<Body>) -> Result<Response<Incoming>, ClientError> {
|
||||
let client = self.inner.client_for_request();
|
||||
let generation = self.inner.generation.load(Ordering::Acquire);
|
||||
let response = client.request(request).await;
|
||||
if let Ok(value) = &response {
|
||||
self.inner.observe_protocol(value.version());
|
||||
match &response {
|
||||
Ok(value) => self.inner.observe_protocol(value.version()),
|
||||
Err(error) if is_graceful_h2_goaway(error) => {
|
||||
// Also applies to cache refresh and other backend HTTP callers,
|
||||
// not only the regular forward loop.
|
||||
self.note_graceful_goaway(generation);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
response
|
||||
}
|
||||
|
||||
/// A peer may gracefully close a reused H2 connection. Discard only the
|
||||
/// affected client generation; concurrent requests must not rotate again.
|
||||
pub fn note_graceful_goaway(&self, generation: u64) {
|
||||
self.inner
|
||||
.graceful_goaway_at_ms
|
||||
.store(monotonic_millis().saturating_add(1), Ordering::Release);
|
||||
self.inner.rotate(generation, "h2_goaway_no_error", false);
|
||||
}
|
||||
|
||||
/// A cancellation immediately following GOAWAY may be another stream on
|
||||
/// the closing H2 connection. Keep this correlation window short.
|
||||
pub fn recently_saw_graceful_goaway(&self) -> bool {
|
||||
let at = self.inner.graceful_goaway_at_ms.load(Ordering::Acquire);
|
||||
at != 0 && monotonic_millis().saturating_add(1).saturating_sub(at) <= 1_000
|
||||
}
|
||||
|
||||
pub fn protocol_stats(&self) -> UpstreamProtocolStats {
|
||||
self.inner.protocol_stats()
|
||||
}
|
||||
@@ -425,14 +457,18 @@ fn build_raw_client(factory: &ClientFactory) -> Result<RawProxyClient> {
|
||||
url::Host::Ipv6(ip) => format!("[{ip}]"),
|
||||
host => host.to_string(),
|
||||
};
|
||||
let port = origin.port_or_known_default().context("backend port missing")?;
|
||||
let tls_name = backend.tls_server_name
|
||||
let port = origin
|
||||
.port_or_known_default()
|
||||
.context("backend port missing")?;
|
||||
let tls_name = backend
|
||||
.tls_server_name
|
||||
.as_deref()
|
||||
.map(crate::config::normalize_host)
|
||||
.transpose()?
|
||||
.unwrap_or(network_host);
|
||||
let destination: http::Uri = format!("{}://{tls_name}:{port}", origin.scheme())
|
||||
.parse().context("invalid configured backend transport authority")?;
|
||||
.parse()
|
||||
.context("invalid configured backend transport authority")?;
|
||||
Ok(builder.build(TimedConnector {
|
||||
inner,
|
||||
policy: policy.clone(),
|
||||
|
||||
Reference in new issue
Block a user