0.34.6_repo_cleanup
This commit is contained in:
1 parent
ce1a90397c
commit
133d2f0f91
273 files changed
+5969
-6926
No files matched your search
@@ -22,8 +22,6 @@ 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")
|
||||
@@ -64,12 +62,6 @@ struct RotatingClient {
|
||||
graceful_goaway_at_ms: AtomicU64,
|
||||
}
|
||||
|
||||
/// Backend HTTP client with bounded generation lifetime.
|
||||
///
|
||||
/// Hyper reuses a client/pool for efficiency. Under sustained traffic that also
|
||||
/// makes connection/TLS/buffer high-water allocations live as long as the
|
||||
/// compiled vhost snapshot. Recycling only this client generation bounds that
|
||||
/// lifetime without reloading the vhost or touching cache/configuration state.
|
||||
#[derive(Clone)]
|
||||
pub struct ProxyClient {
|
||||
inner: Arc<RotatingClient>,
|
||||
@@ -127,8 +119,6 @@ impl ProxyClient {
|
||||
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(_) => {}
|
||||
@@ -136,8 +126,6 @@ impl ProxyClient {
|
||||
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
|
||||
@@ -145,8 +133,6 @@ impl ProxyClient {
|
||||
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
|
||||
@@ -160,15 +146,10 @@ impl ProxyClient {
|
||||
self.inner.stats()
|
||||
}
|
||||
|
||||
/// Whether a newly compiled backend client has exactly the same connection
|
||||
/// policy and lifecycle settings as this one. Compatible clients can be
|
||||
/// retained across unrelated snapshot rebuilds so generation age and
|
||||
/// rotation counters are not restarted from zero.
|
||||
pub(crate) fn reusable_with(&self, other: &Self) -> bool {
|
||||
self.inner.factory == other.inner.factory
|
||||
}
|
||||
|
||||
/// Recycle an aged connection pool even when no user request arrives.
|
||||
pub(crate) fn maintain_rotation(&self) {
|
||||
self.inner.maintain_rotation();
|
||||
}
|
||||
@@ -276,8 +257,6 @@ impl RotatingClient {
|
||||
Ok(client) => {
|
||||
let next_generation = observed_generation.saturating_add(1);
|
||||
self.current.store(Arc::new(client));
|
||||
// Request-driven rotation sends the triggering request through
|
||||
// the new generation. Maintenance-driven rotation happens idle.
|
||||
self.generation_requests
|
||||
.store(if triggering_request { 1 } else { 0 }, Ordering::Relaxed);
|
||||
self.generation_started_ms
|
||||
@@ -292,9 +271,6 @@ impl RotatingClient {
|
||||
);
|
||||
}
|
||||
Err(error) => {
|
||||
// The same immutable settings built the current client. If a
|
||||
// future dependency makes rebuilding fail, keep serving through
|
||||
// the current generation and retry only after another interval.
|
||||
self.generation_requests.store(0, Ordering::Relaxed);
|
||||
self.generation_started_ms
|
||||
.store(monotonic_millis(), Ordering::Relaxed);
|
||||
@@ -352,9 +328,6 @@ fn build_raw_client(factory: &ClientFactory) -> Result<RawProxyClient> {
|
||||
let keepalive = &factory.keepalive;
|
||||
let timeout = factory.timeout;
|
||||
let is_https = url::Url::parse(&backend.url)?.scheme() == "https";
|
||||
// WebSocket HTTP/1.1 Upgrade retains its isolated H1 client. Preserve
|
||||
// the 0.32.2 `http2` meaning: HTTPS allows H2/H1 via ALPN, while
|
||||
// plaintext uses H2C. Only the NEW `h2` value is strict on HTTPS.
|
||||
let protocol = if websocket {
|
||||
UpstreamProtocol::Http1
|
||||
} else if is_https && backend.protocol == UpstreamProtocol::Http2 {
|
||||
@@ -409,10 +382,6 @@ fn build_raw_client(factory: &ClientFactory) -> Result<RawProxyClient> {
|
||||
let builder = HttpsConnectorBuilder::new()
|
||||
.with_tls_config(config)
|
||||
.https_or_http();
|
||||
// TLS Auto offers h2 then HTTP/1.1 in a single handshake. Negotiating
|
||||
// HTTP/1.1 here happens *before* any request is sent: there is no unsafe
|
||||
// automatic replay after a timed-out or partially delivered POST/PATCH.
|
||||
// Cleartext Auto uses H1; H2C requires the explicit Http2 mode.
|
||||
let protocol = if !is_https && protocol == UpstreamProtocol::Auto {
|
||||
UpstreamProtocol::Http1
|
||||
} else {
|
||||
@@ -449,9 +418,6 @@ fn build_raw_client(factory: &ClientFactory) -> Result<RawProxyClient> {
|
||||
)
|
||||
.http2_keep_alive_timeout(Duration::from_secs(10))
|
||||
.http2_keep_alive_while_idle(false);
|
||||
// The request URI is free to carry the preserved public H2 :authority,
|
||||
// while TLS SNI/verification uses tls_server_name (or backend URL) and
|
||||
// DNS/TCP remains restricted to backend.url by GuardedResolver.
|
||||
let origin = url::Url::parse(&backend.url)?;
|
||||
let network_host = match origin.host().context("backend host missing")? {
|
||||
url::Host::Ipv6(ip) => format!("[{ip}]"),
|
||||
|
||||
@@ -17,8 +17,6 @@ mod tests {
|
||||
let client = client(&backend, AddressPolicy::new(&backend).unwrap(), false).unwrap();
|
||||
assert_eq!(client.protocol_stats().last_version, None);
|
||||
let shared = client.clone();
|
||||
// Exercise the response-version recorder independently of the network;
|
||||
// a live TLS ALPN acceptance test must still run on staging.
|
||||
shared.inner.observe_protocol(Version::HTTP_11);
|
||||
shared.inner.observe_protocol(Version::HTTP_2);
|
||||
let observed = client.protocol_stats();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Real Hyper + Rustls upstream peers, independent of the Python mock.
|
||||
async fn tls_origin(
|
||||
offer_h2: bool,
|
||||
) -> (
|
||||
@@ -26,8 +25,6 @@ async fn tls_origin(
|
||||
let (observed_tx, observed_rx) = tokio::sync::oneshot::channel();
|
||||
let server = tokio::spawn(async move {
|
||||
let (tcp, _) = listener.accept().await.unwrap();
|
||||
// For an H2-only client and H1-only server, negotiation can fail
|
||||
// before any HTTP request is delivered. That is the desired error.
|
||||
let Ok(tls) = acceptor.accept(tcp).await else {
|
||||
return;
|
||||
};
|
||||
@@ -222,8 +219,6 @@ fn default_protocol_and_serialization_are_backward_compatible() {
|
||||
}
|
||||
}
|
||||
|
||||
// Exercise real TLS ALPN and the actual Hyper connector rather than only the
|
||||
// local authority helper. The public Host must never become a DNS/TCP target.
|
||||
async fn tls_authority_origin(
|
||||
offer_h2: bool,
|
||||
) -> (
|
||||
@@ -334,8 +329,6 @@ async fn auto_h2_alpn_retains_same_public_host_and_pinned_tls_origin() {
|
||||
assert_eq!(response.version(), Version::HTTP_2);
|
||||
let (version, authority, host, sni) = seen.await.unwrap();
|
||||
assert_eq!(version, Version::HTTP_2);
|
||||
// Legacy Hyper may preserve H1-compatible Host on ALPN H2; neither value
|
||||
// may select a different origin or differ from the intended public name.
|
||||
assert_eq!(authority.or(host).as_deref(), Some("unitraklub.pl"));
|
||||
assert_eq!(sni.as_deref(), Some("localhost"));
|
||||
server.abort();
|
||||
|
||||
@@ -80,8 +80,6 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn explicit_h2c_backend_uses_http2_prior_knowledge() {
|
||||
// The persisted HTTP/2 value and the new strict H2 value both retain
|
||||
// plaintext HTTP/2 prior knowledge, with no HTTP/1.1 probe.
|
||||
for mode in ["http2", "h2"] {
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let address = listener.local_addr().unwrap();
|
||||
|
||||
Reference in new issue
Block a user