use crate::engine::interfaces::ConnectionObject;
use crate::layers::l7::http::httpx;
use crate::resources::{certs, kv::KvStore};
use anyhow::{Result, anyhow};
use fancy_log::{LogLevel, log};
use std::sync::Arc;
use tokio_rustls::{TlsAcceptor, rustls};
pub async fn terminate_and_handover(
conn: ConnectionObject,
kv: &mut KvStore,
target_protocol: String,
) -> Result<()> {
let ConnectionObject::Stream(stream) = conn else {
return Err(anyhow!(
"Cannot terminate TLS on non-stream connection object"
));
};
let cert_lookup_key = kv
.get("tls.termination.cert_sni")
.cloned()
.or_else(|| kv.get("tls.sni").cloned())
.unwrap_or_else(|| "default".to_owned());
let cert = match certs::arcswap::get_certificate(&cert_lookup_key) {
Some(c) => c,
None => {
if cert_lookup_key != "default" {
log(
LogLevel::Warn,
&format!("⚠ Certificate '{cert_lookup_key}' not found. Falling back to 'default'."),
);
certs::arcswap::get_certificate("default").ok_or_else(|| {
anyhow!("CRITICAL: Neither '{cert_lookup_key}' nor 'default' certificate found.")
})?
} else {
return Err(anyhow!("CRITICAL: Default certificate not found."));
}
}
};
log(
LogLevel::Debug,
&format!("⚙ Terminating TLS using certificate for: '{cert_lookup_key}'"),
);
let mut server_config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert.certs.clone(), cert.key_clone()?)
.map_err(|e| anyhow!("Invalid TLS configuration: {e}"))?;
if target_protocol == "httpx" || target_protocol == "h2" || target_protocol == "http/1.1" {
server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
}
let acceptor = TlsAcceptor::from(Arc::new(server_config));
match acceptor.accept(stream).await {
Ok(tls_stream) => {
log(
LogLevel::Debug,
"✓ TLS Handshake successful. Upgrading to L7.",
);
let l7_conn = ConnectionObject::Stream(Box::new(tls_stream));
httpx::handle_connection(l7_conn, target_protocol)
.await
.map_err(|e| anyhow!("L7 Engine Error: {e}"))
}
Err(e) => {
log(LogLevel::Error, &format!("✗ TLS Handshake failed: {e}"));
Err(anyhow::Error::from(e))
}
}
}