0.34.6_repo_cleanup

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-22 09:31:15 +02:00
1 parent ce1a90397c
commit 133d2f0f91
273 files changed
+5969 -6926

No files matched your search

-10
View File
@@ -34,8 +34,6 @@ where
async fn wait_for_h2_idle(state: Arc<H2ConnectionState>, idle: Duration) {
loop {
// Register for a notification before inspecting the state. This avoids
// losing the final stream-drop notification between the load and await.
let notified = state.notify.notified();
tokio::pin!(notified);
if state.active_streams.load(Ordering::Acquire) > 0 {
@@ -166,8 +164,6 @@ enum TlsMode {
Frontend(Arc<App>, bool),
Fixed(Arc<rustls::ServerConfig>, Option<Router>),
}
/// Upgraded tunnels hold this lease after Hyper finishes its HTTP connection.
/// This prevents WebSockets from escaping the public connection limit/counter.
pub struct ConnectionLease {
_permit: OwnedSemaphorePermit,
metrics: Option<Arc<Metrics>>,
@@ -253,9 +249,6 @@ pub async fn serve_frontend(
.await
}
/// Start one compiled frontend listener with its immutable per-listener H2
/// lifecycle policy. The public `serve_frontend` wrapper retains the legacy
/// global fallback for callers that do not provide a compiled listener plan.
pub async fn serve_frontend_with_http2(
listener: TcpListener,
router: Router,
@@ -304,9 +297,6 @@ pub async fn serve_tls(
)
.await
}
/// Serve an HTTPS socket while turning accidental/plain HTTP requests on the
/// same port into redirects. No administration handler is exposed through the
/// plaintext router.
pub async fn serve_tls_with_plain_redirect(
listener: TcpListener,
router: Router,
-5
View File
@@ -1,4 +1,3 @@
// HTTP/1 dispatcher and upgrade flow; split from TLS listener runtime.
#[allow(clippy::too_many_arguments)]
async fn connection<I>(
stream: I,
@@ -49,8 +48,6 @@ async fn connection<I>(
#[cfg(target_os = "linux")]
let sendfile_slots = sendfile_slots.clone();
async move {
// Reuse immutable per-socket formatting and insert extensions in
// one pass. Configuration and trust policies are still per-request.
let extensions = request.extensions_mut();
extensions.insert(PeerAddr(peer));
extensions.insert(LocalAddr(local));
@@ -179,8 +176,6 @@ async fn connection<I>(
);
}
// Axum Router is always ready; avoid the per-request Oneshot
// readiness adapter while retaining the same middleware stack.
router.call(request.map(Body::new)).await
}
});
+3 -13
View File
@@ -83,13 +83,11 @@ fn hpack_integer(out: &mut Vec<u8>, value: usize, prefix_bits: u8, first: u8) {
}
fn hpack_string(out: &mut Vec<u8>, value: &[u8]) {
// Huffman bit stays clear; the bootstrap encoder is deliberately simple.
hpack_integer(out, value.len(), 7, 0);
out.extend_from_slice(value);
}
fn hpack_literal(out: &mut Vec<u8>, name: &str, value: &str) {
// Literal Header Field without Indexing, new name (RFC 7541 section 6.2.2).
out.push(0);
hpack_string(out, name.as_bytes());
hpack_string(out, value.as_bytes());
@@ -203,22 +201,17 @@ fn h2_headers_frame<B>(request: &Request<B>) -> std::result::Result<Bytes, &'sta
((length >> 16) & 0xff) as u8,
((length >> 8) & 0xff) as u8,
(length & 0xff) as u8,
0x1, // HEADERS
0x5, // END_STREAM | END_HEADERS
0x1,
0x5,
0,
0,
0,
1, // stream 1 is the HTTP/1.1 upgrade request
1,
]);
frame.extend_from_slice(&block);
Ok(Bytes::from(frame))
}
/// After a successful 101 response the client sends the normal HTTP/2 client
/// connection preface. Hyper still needs to observe the upgrade request as
/// stream 1, so this adapter injects one synthetic HEADERS frame immediately
/// after the client's mandatory initial SETTINGS frame. All later streams are
/// handled by Hyper's normal HTTP/2 state machine.
struct H2cUpgradeIo<I> {
inner: I,
injected_headers: Bytes,
@@ -344,9 +337,6 @@ impl<I: AsyncWrite + Unpin> AsyncWrite for H2cUpgradeIo<I> {
}
}
/// Shared HTTP/2 activity state. Idle shutdown is allowed only when there are
/// no active streams and no recent frame traffic. Lifetime shutdown ignores idle
/// state but still uses the same GOAWAY -> drain -> TCP close sequence.
struct H2ConnectionState {
started: std::time::Instant,
last_activity_ms: AtomicU64,
+1 -22
View File
@@ -1,13 +1,3 @@
//! Linux HTTP/1 kernel sendfile bridge.
//!
//! Hyper's Body uses Bytes, not file descriptors. This opt-in bridge conveys a
//! uniquely tagged, exact-length placeholder through the ordinary metering
//! pipeline; a TCP-only AsyncWrite adapter replaces those bytes with the
//! original, already-validated file via Linux sendfile(2). An unmatched marker
//! or short file is a hard I/O error, never silently returned as user content.
//! Never register a placeholder for cached, compressed, transformed, TLS, H2,
//! H3, ranged or non-GET responses.
use bytes::Bytes;
use http_body::{Body as HttpBody, Frame, SizeHint};
use std::{
@@ -54,8 +44,6 @@ pub(crate) struct SendfileBody {
web_metrics: Arc<crate::web_server::metrics::PhpMetrics>,
}
// Web Server counters count emitted body frames before any downstream encoder.
// Atomic reference is supplied by the Web Server that originally opened the file.
impl SendfileSlots {
pub fn create_body(
&self,
@@ -68,7 +56,7 @@ impl SendfileSlots {
let mut registry = self.0.lock().unwrap_or_else(|e| e.into_inner());
if registry.slots.len() >= MAX_PENDING { return None; }
let mut tag = [0u8; TAG_LEN];
tag[0] = 0; // A valid HTTP/1 response header never contains NUL.
tag[0] = 0;
tag[1..].copy_from_slice(&rand::random::<[u8; TAG_LEN - 1]>());
if registry.slots.iter().any(|slot| slot.tag == tag) { return None; }
let mut data = vec![0u8; FRAME_LEN];
@@ -83,8 +71,6 @@ impl SendfileSlots {
fn plan(&self, buf: &[u8]) -> WritePlan {
let registry = self.0.lock().unwrap_or_else(|e| e.into_inner());
// Critical for mixed reverse-proxy traffic: almost all socket writes
// have no pending static file; do not inspect their payload at all.
if registry.slots.is_empty() { return WritePlan::Plain(buf.len()); }
if let Some(tag) = registry.active {
if let Some(slot) = registry.slots.iter().find(|s| s.tag == tag) {
@@ -92,9 +78,6 @@ impl SendfileSlots {
}
return WritePlan::Invalid;
}
// A marker is an opaque 32-byte random tag, searched only on a
// connection that has a registered response. Headers preceding a
// marker are written intact; each subsequent body byte is validated.
for start in 0..buf.len() {
if buf[start] != 0 { continue; }
for slot in &registry.slots {
@@ -211,8 +194,6 @@ impl AsyncWrite for SendfileIo {
}
fn is_write_vectored(&self) -> bool { self.stream.is_write_vectored() }
fn poll_write_vectored(mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[io::IoSlice<'_>]) -> Poll<io::Result<usize>> {
// Preserve zero-extra-syscall vectored I/O whenever no sendfile body
// has been prepared on this connection.
if self.slots.0.lock().unwrap_or_else(|e| e.into_inner()).slots.is_empty() {
return Pin::new(&mut self.stream).poll_write_vectored(cx, bufs);
}
@@ -273,8 +254,6 @@ mod tests {
file: Arc::new(file), length: expected.len() as u64,
}, web.clone()).unwrap();
let first = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n", expected.len());
// Combine header and first marker into one socket write: the
// bridge must send the header before enabling kernel transfer.
let mut body = body;
let first_frame = http_body_util::BodyExt::frame(&mut body).await.unwrap().unwrap().into_data().unwrap();
let mut joined = first.into_bytes();
@@ -79,8 +79,6 @@ async fn http2_connection<I>(
_ = stop.cancelled() => break H2ShutdownCause::Process,
_ = &mut lifetime => break H2ShutdownCause::Lifetime,
_ = wait_for_h2_idle(activity.clone(), settings.idle_timeout()) => {
// Re-check immediately before GOAWAY so activity racing the timer
// cannot turn a busy connection into an idle shutdown.
if activity.is_idle(settings.idle_timeout()) {
break H2ShutdownCause::Idle;
}
@@ -88,8 +86,6 @@ async fn http2_connection<I>(
}
};
// Hyper's graceful shutdown sends GOAWAY and refuses newer streams while
// continuing to poll streams that were already accepted.
conn.as_mut().graceful_shutdown();
let configured_drain = settings.drain_timeout();
let drain = if matches!(cause, H2ShutdownCause::Process) {
@@ -122,4 +118,4 @@ async fn http2_connection<I>(
}
#[cfg(test)]
include!("serve/tests.rs");
include!("tests.rs");
-1
View File
@@ -162,7 +162,6 @@ mod h2c_tests {
.write_all(HTTP2_PRIOR_KNOWLEDGE_PREFACE)
.await
.unwrap();
// Client connection preface must begin with a non-ACK SETTINGS frame.
client
.write_all(&[0, 0, 0, 0x4, 0, 0, 0, 0, 0])
.await
-7
View File
@@ -18,8 +18,6 @@ async fn serve_inner(
_ = tasks.join_next(), if !tasks.is_empty() => {},
accepted = listener.accept() => {
let (stream, peer) = accepted.context("listener accept failed")?;
// Never substitute the wildcard listener address or a forwarded
// header for the destination IP of this accepted connection.
let local = stream.local_addr().context("accepted local address unavailable")?;
let Ok(permit) = permits.clone().try_acquire_owned() else {
if let Some(m) = &metrics { m.rejected_connections.fetch_add(1, Ordering::Relaxed); }
@@ -47,8 +45,6 @@ async fn serve_inner(
tasks.spawn(async move {
match tls {
Some(TlsMode::Frontend(state, http2)) => {
// Read ClientHello before committing to ALPN so a vhost can opt out
// of HTTP/2 or narrow TLS policy while sharing one TLS listener.
let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
let acceptor = tokio_rustls::LazyConfigAcceptor::new(
rustls::server::Acceptor::default(),
@@ -245,9 +241,6 @@ async fn serve_inner(
&& frontend_state
.as_ref()
.is_some_and(|state| state.config.load().websocket_rfc8441_on(local, false));
// The existing Hyper transport remains untouched when the
// option is disabled. Connections opened after a hot config
// change acquire a new bridge automatically.
#[cfg(target_os = "linux")]
if !prior_h2 && frontend_state.as_ref().is_some_and(|state| {
state.config.load().web_php_services.values()