Consolidate startup logic into main.rs following operations.md sequence: 1. Parse/validate config, 2. Init DynamicConfig ArcSwap, 3. Init shared state (rate limiter, clients, logging), 4. Bind health check port, 5. Bind admin socket, 6. Bind all listener ports (HTTP+HTTPS), 7. Load TLS config, 8. Start TCP listeners, 9. Start background tasks, 10. Signal readiness Key changes: - main.rs: Complete startup orchestration with proper sequence, TLS handling, ConnectInfo propagation, sd_notify, graceful shutdown - server.rs: Simplified to just serve_https_listener with shutdown support - proxy/mod.rs: Added build_router() with correct middleware order (rate limiting → body limit → routing → proxy handler)
102 lines
3.1 KiB
Rust
102 lines
3.1 KiB
Rust
use std::net::SocketAddr;
|
|
|
|
use axum::extract::ConnectInfo;
|
|
use axum::http::Request;
|
|
use axum::response::Response;
|
|
use axum::Router;
|
|
use hyper_util::rt::TokioExecutor;
|
|
use hyper_util::service::TowerToHyperService;
|
|
use tokio::net::TcpListener;
|
|
use tokio_rustls::TlsAcceptor;
|
|
use tower::Service;
|
|
use tracing::{error, warn};
|
|
|
|
pub async fn serve_https_listener(
|
|
tcp_listener: TcpListener,
|
|
tls_acceptor: TlsAcceptor,
|
|
router: Router,
|
|
mut shutdown_rx: tokio::sync::watch::Receiver<bool>,
|
|
) {
|
|
let local_addr = tcp_listener.local_addr();
|
|
|
|
loop {
|
|
tokio::select! {
|
|
accept_result = tcp_listener.accept() => {
|
|
let (tcp_stream, remote_addr) = match accept_result {
|
|
Ok(conn) => conn,
|
|
Err(e) => {
|
|
error!(error = %e, "failed to accept TCP connection");
|
|
continue;
|
|
}
|
|
};
|
|
|
|
let tls_acceptor = tls_acceptor.clone();
|
|
let router = router.clone();
|
|
|
|
tokio::spawn(async move {
|
|
let tls_stream = match tls_acceptor.accept(tcp_stream).await {
|
|
Ok(stream) => stream,
|
|
Err(e) => {
|
|
warn!(error = %e, "TLS handshake failed");
|
|
return;
|
|
}
|
|
};
|
|
|
|
let svc = ConnectInfoService {
|
|
inner: router.into_service::<hyper::body::Incoming>(),
|
|
remote_addr,
|
|
};
|
|
|
|
let svc = TowerToHyperService::new(svc);
|
|
let io = hyper_util::rt::TokioIo::new(tls_stream);
|
|
|
|
if let Err(e) = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
|
|
.serve_connection_with_upgrades(io, svc)
|
|
.await
|
|
{
|
|
if e.to_string().contains("incomplete message") {
|
|
return;
|
|
}
|
|
error!(error = %e, "HTTPS connection error");
|
|
}
|
|
});
|
|
}
|
|
_ = shutdown_rx.changed() => {
|
|
if let Ok(addr) = local_addr {
|
|
tracing::info!(addr = %addr, "HTTPS listener shutting down");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
struct ConnectInfoService<S> {
|
|
inner: S,
|
|
remote_addr: SocketAddr,
|
|
}
|
|
|
|
impl<S, B> Service<Request<B>> for ConnectInfoService<S>
|
|
where
|
|
S: Service<Request<B>, Response = Response> + Clone + Send + 'static,
|
|
S::Future: Send + 'static,
|
|
B: Send + 'static,
|
|
{
|
|
type Response = S::Response;
|
|
type Error = S::Error;
|
|
type Future = S::Future;
|
|
|
|
fn poll_ready(
|
|
&mut self,
|
|
cx: &mut std::task::Context<'_>,
|
|
) -> std::task::Poll<Result<(), Self::Error>> {
|
|
self.inner.poll_ready(cx)
|
|
}
|
|
|
|
fn call(&mut self, mut req: Request<B>) -> Self::Future {
|
|
req.extensions_mut().insert(ConnectInfo(self.remote_addr));
|
|
self.inner.call(req)
|
|
}
|
|
}
|