use std::io::BufReader;
use std::sync::Arc;
use tokio_rustls::rustls::ServerConfig;
use crate::error::{ProxyError, ProxyResult};
pub const ALPN_PROTOCOLS: [&[u8]; 2] = [b"h2", b"http/1.1"];
pub(crate) fn install_crypto_provider() {
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {
let _ = tokio_rustls::rustls::crypto::aws_lc_rs::default_provider().install_default();
});
}
pub fn build_server_config(cert_pem: &[u8], key_pem: &[u8]) -> ProxyResult<Arc<ServerConfig>> {
install_crypto_provider();
let certs: Vec<_> = rustls_pemfile::certs(&mut BufReader::new(cert_pem))
.filter_map(|r| r.ok())
.collect();
if certs.is_empty() {
return Err(ProxyError::Tls("no certificates found in PEM".into()));
}
let key = rustls_pemfile::private_key(&mut BufReader::new(key_pem))
.map_err(|e| ProxyError::Tls(format!("could not parse private key: {}", e)))?
.ok_or_else(|| ProxyError::Tls("no private key found in PEM".into()))?;
let mut config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| ProxyError::Tls(format!("could not build TLS config: {}", e)))?;
config.alpn_protocols = ALPN_PROTOCOLS.iter().map(|p| p.to_vec()).collect();
Ok(Arc::new(config))
}
pub fn build_server_config_with_resolver(
resolver: Arc<dyn tokio_rustls::rustls::server::ResolvesServerCert>,
) -> Arc<ServerConfig> {
install_crypto_provider();
let mut config = ServerConfig::builder()
.with_no_client_auth()
.with_cert_resolver(resolver);
config.alpn_protocols = ALPN_PROTOCOLS.iter().map(|p| p.to_vec()).collect();
Arc::new(config)
}
pub async fn load_server_config(cert_file: &str, key_file: &str) -> ProxyResult<Arc<ServerConfig>> {
let cert_pem = tokio::fs::read(cert_file)
.await
.map_err(|e| ProxyError::Tls(format!("could not read {}: {}", cert_file, e)))?;
let key_pem = tokio::fs::read(key_file)
.await
.map_err(|e| ProxyError::Tls(format!("could not read {}: {}", key_file, e)))?;
build_server_config(&cert_pem, &key_pem)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alpn_offers_h2_first() {
assert_eq!(ALPN_PROTOCOLS[0], b"h2");
assert_eq!(ALPN_PROTOCOLS[1], b"http/1.1");
}
#[test]
fn test_rejects_pem_without_certificate() {
let err = build_server_config(b"not a certificate", b"not a key").unwrap_err();
assert!(
err.to_string().contains("no certificates"),
"unexpected error: {err}"
);
}
#[test]
fn test_rejects_certificate_without_key() {
let cert = b"-----BEGIN CERTIFICATE-----\nMA==\n-----END CERTIFICATE-----\n";
let err = build_server_config(cert, b"").unwrap_err();
assert!(
err.to_string().contains("no private key"),
"unexpected error: {err}"
);
}
}