use std::fs::File;
use std::io::{BufRead, BufReader, Cursor};
use std::path::Path;
use std::sync::Arc;
use thiserror::Error;
use tokio_rustls::rustls;
#[derive(Error, Debug)]
pub enum TlsAcceptorError {
#[error("no valid pem data")]
NoValidPem,
#[error("no valid private keys in pem data")]
NoValidKey,
#[error("failed to create ServerConfig")]
ServerConfig(#[from] rustls::Error),
#[error("failed to open pem file")]
FileOpen(#[source] std::io::Error),
#[error(transparent)]
Io(#[from] std::io::Error),
}
#[derive(Debug, Clone, Copy)]
pub enum HttpProtocol {
Http1,
Http2,
Both,
}
pub fn get_tlsacceptor_from_pem_data(
cert_data: &str,
key_data: &str,
protocol: HttpProtocol,
) -> Result<tokio_rustls::TlsAcceptor, TlsAcceptorError> {
let mut cert_reader = BufReader::new(Cursor::new(cert_data));
let mut key_reader = BufReader::new(Cursor::new(key_data));
get_tlsacceptor_from_readers(&mut cert_reader, &mut key_reader, protocol)
}
pub fn get_tlsacceptor_from_files(
cert_path: impl AsRef<Path>,
key_path: impl AsRef<Path>,
protocol: HttpProtocol,
) -> Result<tokio_rustls::TlsAcceptor, TlsAcceptorError> {
let cert_file = File::open(cert_path).map_err(TlsAcceptorError::FileOpen)?;
let key_file = File::open(key_path).map_err(TlsAcceptorError::FileOpen)?;
let mut cert_reader = BufReader::new(cert_file);
let mut key_reader = BufReader::new(key_file);
get_tlsacceptor_from_readers(&mut cert_reader, &mut key_reader, protocol)
}
fn get_tlsacceptor_from_readers(
cert_reader: &mut dyn BufRead,
key_reader: &mut dyn BufRead,
protocol: HttpProtocol,
) -> Result<tokio_rustls::TlsAcceptor, TlsAcceptorError> {
let certs: Vec<_> = rustls_pemfile::certs(cert_reader)
.filter_map(Result::ok)
.collect();
let key = rustls_pemfile::read_one(key_reader)?.ok_or(TlsAcceptorError::NoValidPem)?;
let key = match key {
rustls_pemfile::Item::Sec1Key(data) => data.into(),
rustls_pemfile::Item::Pkcs1Key(data) => data.into(),
rustls_pemfile::Item::Pkcs8Key(data) => data.into(),
_ => return Err(TlsAcceptorError::NoValidKey),
};
let mut cfg = rustls::server::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)?;
cfg.alpn_protocols = match protocol {
HttpProtocol::Http1 => vec![b"http/1.1".to_vec(), b"http/1.0".to_vec()],
HttpProtocol::Http2 => vec![b"h2".to_vec()],
HttpProtocol::Both => vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()],
};
let acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(cfg));
Ok(acceptor)
}