v0.34.0_1

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-17 20:34:13 +02:00
1 parent 9b3d6731cc
commit 00f29f92c8
68 files changed
+2599 -513

No files matched your search

+20 -199
View File
@@ -134,6 +134,7 @@ async fn serve_inner(
stop,
grace,
http2_settings,
Default::default(),
)
.await;
},
@@ -178,6 +179,7 @@ async fn serve_inner(
stop,
grace,
Http2Connections::default(),
Default::default(),
)
.await;
return;
@@ -208,6 +210,7 @@ async fn serve_inner(
stop,
grace,
Http2Connections::default(),
Default::default(),
)
.await;
}
@@ -242,6 +245,22 @@ 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()
.any(|service| service.sendfile == crate::web_server::SendfileMode::Auto)
}) {
let (io, slots) = sendfile::SendfileIo::new(stream);
connection(
io, router, peer, local, Transport { tls: false }, None,
false, false, h2c, frontend_state, lease, stop, grace,
http2_settings, Some(slots),
).await;
return;
}
connection(
stream,
router,
@@ -257,6 +276,7 @@ async fn serve_inner(
stop,
grace,
http2_settings,
Default::default(),
)
.await;
}
@@ -273,205 +293,6 @@ async fn serve_inner(
while tasks.join_next().await.is_some() {}
Ok(())
}
#[allow(clippy::too_many_arguments)]
async fn connection<I>(
stream: I,
router: Router,
peer: SocketAddr,
local: SocketAddr,
transport: Transport,
tls_sni: Option<String>,
h2: bool,
extended_connect: bool,
h2c_upgrade: bool,
frontend_state: Option<Arc<App>>,
lease: Arc<ConnectionLease>,
stop: CancellationToken,
grace: Duration,
http2_settings: Http2Connections,
) where
I: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
if h2 {
http2_connection(
stream,
router,
peer,
local,
transport,
tls_sni.clone(),
extended_connect,
frontend_state.clone(),
lease,
stop,
grace,
http2_settings,
)
.await;
return;
}
let request_stop = stop.clone();
let service_frontend_state = frontend_state.clone();
let service = service_fn(move |mut request: Request<Incoming>| {
let router = router.clone();
let request_stop = request_stop.clone();
let request_lease = lease.clone();
let upgrade_state = service_frontend_state.clone();
async move {
request.extensions_mut().insert(PeerAddr(peer));
request.extensions_mut().insert(LocalAddr(local));
request.extensions_mut().insert(transport);
request.extensions_mut().insert(request_lease.clone());
request
.extensions_mut()
.insert(ServerShutdown(request_stop.clone()));
if h2c_attempt(&request) {
if !h2c_upgrade {
let (authority, host) = request_log_identity(&request);
log_protocol_error_with_identity(
upgrade_state.as_ref(),
"h2c_upgrade_rejected",
"h2c upgrade is disabled on this listener",
peer,
local,
"tcp",
"1.1",
400,
authority,
host,
None,
);
return Ok::<_, Infallible>(
Response::builder()
.status(http::StatusCode::BAD_REQUEST)
.header(http::header::CACHE_CONTROL, "no-store")
.body(Body::from("h2c upgrade is disabled on this listener"))
.expect("static h2c disabled response"),
);
}
let injected_headers = match h2_headers_frame(&request) {
Ok(frame) => frame,
Err(message) => {
let (authority, host) = request_log_identity(&request);
log_protocol_error_with_identity(
upgrade_state.as_ref(),
"h2c_upgrade_invalid",
message,
peer,
local,
"tcp",
"1.1",
400,
authority,
host,
None,
);
return Ok::<_, Infallible>(
Response::builder()
.status(http::StatusCode::BAD_REQUEST)
.header(http::header::CACHE_CONTROL, "no-store")
.body(Body::from(message))
.expect("static h2c validation response"),
);
}
};
let on_upgrade = hyper::upgrade::on(&mut request);
let upgrade_router = router.clone();
let upgrade_stop = request_stop.clone();
let upgrade_lease = request_lease.clone();
let upgrade_state_for_task = upgrade_state.clone();
let task = async move {
match on_upgrade.await {
Ok(upgraded) => {
if let Some(state) = &upgrade_state_for_task {
state
.metrics
.http2_connections
.fetch_add(1, Ordering::Relaxed);
}
let extended_connect =
upgrade_state_for_task.as_ref().is_some_and(|state| {
state.config.load().websocket_rfc8441_on(local, false)
});
let io = H2cUpgradeIo::new(TokioIo::new(upgraded), injected_headers);
http2_connection(
io,
upgrade_router,
peer,
local,
Transport { tls: false },
None,
extended_connect,
upgrade_state_for_task.clone(),
upgrade_lease,
upgrade_stop,
grace,
http2_settings,
)
.await;
}
Err(error) => {
log_protocol_error(
upgrade_state_for_task.as_ref(),
"h2c_transport_error",
&error,
peer,
local,
"tcp",
"2",
0,
None,
);
tracing::debug!(%error, "h2c upgrade transport failed");
}
}
};
if let Some(state) = upgrade_state {
state.tunnels.spawn(task);
} else {
tokio::spawn(task);
}
return Ok::<_, Infallible>(
Response::builder()
.status(http::StatusCode::SWITCHING_PROTOCOLS)
.header(http::header::CONNECTION, "Upgrade")
.header(http::header::UPGRADE, "h2c")
.body(Body::empty())
.expect("static h2c switching response"),
);
}
router.oneshot(request.map(Body::new)).await
}
});
let mut builder = http1::Builder::new();
builder
.timer(TokioTimer::new())
.keep_alive(true)
.max_headers(64)
.max_buf_size(65_536)
.header_read_timeout(Duration::from_secs(10));
let conn = builder
.serve_connection(TokioIo::new(stream), service)
.with_upgrades();
tokio::pin!(conn);
tokio::select! {
result = &mut conn => {
if let Err(error) = result {
log_protocol_error(
frontend_state.as_ref(), "http1_connection_error", &error, peer, local,
if transport.tls { "tls" } else { "tcp" }, "1.1", 400, tls_sni.clone(),
);
tracing::debug!(%error, "HTTP/1 connection closed");
}
},
_ = stop.cancelled() => {
conn.as_mut().graceful_shutdown(); let _ = tokio::time::timeout(grace, &mut conn).await;
},
}
}
#[derive(Clone, Copy, Debug)]
enum H2ShutdownCause {
Idle,