v0.26.0
This commit is contained in:
1 parent
d168ee2afa
commit
ddc5c6593f
70 files changed
+2293
-594
No files matched your search
@@ -87,6 +87,31 @@ pub struct LocalAddr(pub SocketAddr);
|
||||
pub struct Transport {
|
||||
pub tls: bool,
|
||||
}
|
||||
|
||||
fn log_protocol_error(
|
||||
state: Option<&Arc<App>>,
|
||||
kind: &'static str,
|
||||
detail: impl std::fmt::Display,
|
||||
peer: SocketAddr,
|
||||
local: SocketAddr,
|
||||
transport: &'static str,
|
||||
http_version: &'static str,
|
||||
status: u16,
|
||||
tls_sni: Option<String>,
|
||||
) {
|
||||
if let Some(state) = state {
|
||||
state.logs.push_protocol_error(
|
||||
kind,
|
||||
detail,
|
||||
peer,
|
||||
local,
|
||||
transport,
|
||||
http_version,
|
||||
status,
|
||||
tls_sni,
|
||||
);
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct ServerShutdown(pub CancellationToken);
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -6,6 +6,7 @@ async fn http2_connection<I>(
|
||||
local: SocketAddr,
|
||||
transport: Transport,
|
||||
extended_connect: bool,
|
||||
frontend_state: Option<Arc<App>>,
|
||||
lease: Arc<ConnectionLease>,
|
||||
stop: CancellationToken,
|
||||
grace: Duration,
|
||||
@@ -66,6 +67,10 @@ async fn http2_connection<I>(
|
||||
tokio::select! {
|
||||
result = &mut conn => {
|
||||
if let Err(error) = result {
|
||||
log_protocol_error(
|
||||
frontend_state.as_ref(), "http2_connection_error", &error, peer, local,
|
||||
if transport.tls { "tls" } else { "tcp" }, "2", 0, None,
|
||||
);
|
||||
tracing::debug!(%error, "HTTP/2 connection closed");
|
||||
}
|
||||
return;
|
||||
@@ -94,6 +99,17 @@ async fn http2_connection<I>(
|
||||
match tokio::time::timeout(drain, &mut conn).await {
|
||||
Ok(Ok(())) => tracing::debug!(?cause, "HTTP/2 graceful shutdown drained"),
|
||||
Ok(Err(error)) => {
|
||||
log_protocol_error(
|
||||
frontend_state.as_ref(),
|
||||
"http2_drain_protocol_error",
|
||||
&error,
|
||||
peer,
|
||||
local,
|
||||
if transport.tls { "tls" } else { "tcp" },
|
||||
"2",
|
||||
0,
|
||||
None,
|
||||
);
|
||||
tracing::debug!(?cause, %error, "HTTP/2 graceful shutdown ended with protocol error")
|
||||
}
|
||||
Err(_) => tracing::debug!(
|
||||
|
||||
+108
-15
@@ -23,6 +23,18 @@ async fn serve_inner(
|
||||
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); }
|
||||
let log_state = match &tls {
|
||||
Some(TlsMode::Frontend(state, _)) => Some(state),
|
||||
_ => frontend_state.as_ref(),
|
||||
};
|
||||
log_protocol_error(
|
||||
log_state,
|
||||
"connection_limit_rejected",
|
||||
"connection concurrency limit reached",
|
||||
peer, local,
|
||||
if tls.is_some() { "tls" } else { "tcp" },
|
||||
"", 0, None,
|
||||
);
|
||||
drop(stream); continue;
|
||||
};
|
||||
let _ = stream.set_nodelay(true);
|
||||
@@ -46,16 +58,31 @@ async fn serve_inner(
|
||||
_ = stop.cancelled() => return,
|
||||
started = tokio::time::timeout_at(deadline, acceptor) => started,
|
||||
};
|
||||
let Ok(Ok(start)) = started else {
|
||||
state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
let start = match started {
|
||||
Ok(Ok(start)) => start,
|
||||
Ok(Err(error)) => {
|
||||
state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed);
|
||||
log_protocol_error(
|
||||
Some(&state), "tls_client_hello_error", error, peer, local,
|
||||
"tls", "", 0, None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
Err(_) => {
|
||||
state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed);
|
||||
log_protocol_error(
|
||||
Some(&state), "tls_client_hello_timeout",
|
||||
"TLS ClientHello timeout", peer, local, "tls", "", 0, None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let host = start
|
||||
.client_hello()
|
||||
.server_name()
|
||||
.and_then(|name| crate::config::request_host(name).ok());
|
||||
let config = {
|
||||
let snapshot = state.config.load();
|
||||
let host = start
|
||||
.client_hello()
|
||||
.server_name()
|
||||
.and_then(|name| crate::config::request_host(name).ok());
|
||||
let frontend = host
|
||||
.as_deref()
|
||||
.and_then(|host| snapshot.frontend(host, local, true));
|
||||
@@ -74,7 +101,14 @@ async fn serve_inner(
|
||||
snapshot.tls_http1.clone()
|
||||
}
|
||||
};
|
||||
let Some(config) = config else { return; };
|
||||
let Some(config) = config else {
|
||||
log_protocol_error(
|
||||
Some(&state), "tls_configuration_unavailable",
|
||||
"no TLS configuration is available for this connection",
|
||||
peer, local, "tls", "", 0, host.clone(),
|
||||
);
|
||||
return;
|
||||
};
|
||||
let accepted = tokio::select! {
|
||||
_ = stop.cancelled() => return,
|
||||
accepted = tokio::time::timeout_at(deadline, start.into_stream(config)) => accepted,
|
||||
@@ -94,7 +128,7 @@ async fn serve_inner(
|
||||
h2,
|
||||
extended_connect,
|
||||
false,
|
||||
None,
|
||||
Some(state.clone()),
|
||||
lease,
|
||||
stop,
|
||||
grace,
|
||||
@@ -102,7 +136,20 @@ async fn serve_inner(
|
||||
)
|
||||
.await;
|
||||
},
|
||||
_ => { state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed); },
|
||||
Ok(Err(error)) => {
|
||||
state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed);
|
||||
log_protocol_error(
|
||||
Some(&state), "tls_handshake_error", error, peer, local,
|
||||
"tls", "", 0, host.clone(),
|
||||
);
|
||||
},
|
||||
Err(_) => {
|
||||
state.metrics.tls_handshake_errors.fetch_add(1, Ordering::Relaxed);
|
||||
log_protocol_error(
|
||||
Some(&state), "tls_handshake_timeout", "TLS handshake timeout",
|
||||
peer, local, "tls", "", 0, host.clone(),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(TlsMode::Fixed(config, plaintext_router)) => {
|
||||
@@ -232,7 +279,7 @@ async fn connection<I>(
|
||||
h2: bool,
|
||||
extended_connect: bool,
|
||||
h2c_upgrade: bool,
|
||||
h2c_state: Option<Arc<App>>,
|
||||
frontend_state: Option<Arc<App>>,
|
||||
lease: Arc<ConnectionLease>,
|
||||
stop: CancellationToken,
|
||||
grace: Duration,
|
||||
@@ -248,6 +295,7 @@ async fn connection<I>(
|
||||
local,
|
||||
transport,
|
||||
extended_connect,
|
||||
frontend_state.clone(),
|
||||
lease,
|
||||
stop,
|
||||
grace,
|
||||
@@ -257,11 +305,12 @@ async fn connection<I>(
|
||||
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 = h2c_state.clone();
|
||||
let upgrade_state = service_frontend_state.clone();
|
||||
async move {
|
||||
request.extensions_mut().insert(PeerAddr(peer));
|
||||
request.extensions_mut().insert(LocalAddr(local));
|
||||
@@ -273,6 +322,17 @@ async fn connection<I>(
|
||||
|
||||
if h2c_attempt(&request) {
|
||||
if !h2c_upgrade {
|
||||
log_protocol_error(
|
||||
upgrade_state.as_ref(),
|
||||
"h2c_upgrade_rejected",
|
||||
"h2c upgrade is disabled on this listener",
|
||||
peer,
|
||||
local,
|
||||
"tcp",
|
||||
"1.1",
|
||||
400,
|
||||
None,
|
||||
);
|
||||
return Ok::<_, Infallible>(
|
||||
Response::builder()
|
||||
.status(http::StatusCode::BAD_REQUEST)
|
||||
@@ -284,13 +344,24 @@ async fn connection<I>(
|
||||
let injected_headers = match h2_headers_frame(&request) {
|
||||
Ok(frame) => frame,
|
||||
Err(message) => {
|
||||
log_protocol_error(
|
||||
upgrade_state.as_ref(),
|
||||
"h2c_upgrade_invalid",
|
||||
message,
|
||||
peer,
|
||||
local,
|
||||
"tcp",
|
||||
"1.1",
|
||||
400,
|
||||
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);
|
||||
@@ -319,6 +390,7 @@ async fn connection<I>(
|
||||
local,
|
||||
Transport { tls: false },
|
||||
extended_connect,
|
||||
upgrade_state_for_task.clone(),
|
||||
upgrade_lease,
|
||||
upgrade_stop,
|
||||
grace,
|
||||
@@ -326,7 +398,20 @@ async fn connection<I>(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(error) => tracing::debug!(%error, "h2c upgrade transport failed"),
|
||||
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 {
|
||||
@@ -359,7 +444,15 @@ async fn connection<I>(
|
||||
.with_upgrades();
|
||||
tokio::pin!(conn);
|
||||
tokio::select! {
|
||||
result = &mut conn => { if let Err(error) = result { tracing::debug!(%error, "HTTP/1 connection closed"); } },
|
||||
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, None,
|
||||
);
|
||||
tracing::debug!(%error, "HTTP/1 connection closed");
|
||||
}
|
||||
},
|
||||
_ = stop.cancelled() => {
|
||||
conn.as_mut().graceful_shutdown(); let _ = tokio::time::timeout(grace, &mut conn).await;
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user