#![cfg(all(feature = "tls", feature = "executor_tokio"))]
use std::prelude::v1::*;
#[cfg(feature = "framework_axum")]
use core::error::Error;
use std::sync;
#[cfg(feature = "framework_axum")]
use product_os_net::SocketAddr;
use rustls::ServerConfig;
#[cfg(feature = "framework_axum")]
use hyper::service::Service;
#[cfg(feature = "framework_axum")]
use product_os_async_executor::{Executor, TokioExecutor};
#[cfg(feature = "framework_axum")]
use product_os_router::{Request, Service as RouterService, Router};
use product_os_security::certificates::{
Certificates, CertificateDer, CertificatesFormat, PrivateKeyDer, PrivatePkcs8KeyDer,
};
use rustls::pki_types::pem::PemObject;
#[cfg(feature = "framework_axum")]
pub async fn create_https_tokio_service(socket_address: SocketAddr, certs: Option<Certificates>, router: Router, with_connect_info: bool) -> Result<(), Box<dyn Error + Send + Sync>> {
let tls_config = create_rustls_config(certs)?;
tracing::info!("Starting HTTPS server listening on {}", socket_address);
let tls_acceptor = tokio_rustls::TlsAcceptor::from(tls_config);
let listener = tokio::net::TcpListener::bind(socket_address).await
.map_err(|e| -> Box<dyn Error + Send + Sync> { Box::new(e) })?;
match with_connect_info {
true => {
let router_service = router.clone().into_make_service_with_connect_info::<SocketAddr>();
let make_service = hyper_util::service::TowerToHyperService::new(router_service);
let tower_service = make_service.call(socket_address).await
.map_err(|e| -> Box<dyn Error + Send + Sync> { Box::new(e) })?;
loop {
let (cnx, addr) = match listener.accept().await {
Ok(conn) => conn,
Err(e) => {
tracing::error!("Failed to accept connection: {:?}", e);
continue;
}
};
let tls_acceptor = tls_acceptor.clone();
let tower_service = tower_service.clone();
tokio::spawn(async move {
let stream = match tls_acceptor.accept(cnx).await {
Ok(s) => s,
Err(e) => {
tracing::error!("error during tls handshake connection from {}: {:?}", addr, e);
return;
}
};
let stream = hyper_util::rt::TokioIo::new(stream);
let hyper_service = hyper::service::service_fn(move |request: Request<hyper::body::Incoming>| {
tower_service.clone().call(request)
});
match TokioExecutor::context_sync() {
Ok(executor) => {
let _ret = hyper_util::server::conn::auto::Builder::new(executor)
.serve_connection_with_upgrades(stream, hyper_service)
.await;
}
Err(e) => tracing::error!("Failed to get executor for connection from {}: {:?}", addr, e)
}
});
}
}
false => {
let tower_service = router;
loop {
let (cnx, addr) = match listener.accept().await {
Ok(conn) => conn,
Err(e) => {
tracing::error!("Failed to accept connection: {:?}", e);
continue;
}
};
let tls_acceptor = tls_acceptor.clone();
let tower_service = tower_service.clone();
tokio::spawn(async move {
let Ok(stream) = tls_acceptor.accept(cnx).await else {
tracing::error!("error during tls handshake connection from {}", addr);
return;
};
let stream = hyper_util::rt::TokioIo::new(stream);
let hyper_service = hyper::service::service_fn(move |request: Request<hyper::body::Incoming>| {
tower_service.clone().call(request)
});
match TokioExecutor::context_sync() {
Ok(executor) => {
let _ret = hyper_util::server::conn::auto::Builder::new(executor)
.serve_connection_with_upgrades(stream, hyper_service)
.await;
}
Err(e) => tracing::error!("Failed to get executor for connection from {}: {:?}", addr, e)
}
});
}
}
}
}
pub fn create_rustls_config(certs: Option<Certificates>) -> Result<sync::Arc<ServerConfig>, Box<dyn std::error::Error + Send + Sync>> {
let cert_data = certs.unwrap_or_else(|| {
Certificates::new_self_signed(
vec![],
Some(vec![String::from("localhost"), String::from("127.0.0.1")]),
None,
None,
None,
None,
)
});
let private_key = load_private_key(&cert_data)?;
let certificates = load_certificates(&cert_data)?;
let mut config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certificates, private_key)
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) })?;
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
Ok(sync::Arc::new(config))
}
fn load_private_key(
cert_data: &Certificates,
) -> Result<PrivateKeyDer<'static>, Box<dyn std::error::Error + Send + Sync>> {
match cert_data.format {
CertificatesFormat::Pem => PrivateKeyDer::from_pem_slice(&cert_data.private)
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) }),
CertificatesFormat::Pkcs8 | CertificatesFormat::Der | CertificatesFormat::Raw => Ok(
PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(cert_data.private.clone())),
),
}
}
fn load_certificates(
cert_data: &Certificates,
) -> Result<Vec<CertificateDer<'static>>, Box<dyn std::error::Error + Send + Sync>> {
match cert_data.format {
CertificatesFormat::Pem => cert_data
.certificates
.iter()
.map(|cert| {
CertificateDer::from_pem_slice(cert)
.map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) })
})
.collect(),
CertificatesFormat::Pkcs8 | CertificatesFormat::Der | CertificatesFormat::Raw => {
Ok(cert_data
.certificates
.iter()
.cloned()
.map(CertificateDer::from)
.collect())
}
}
}