For audioserve update I wanted use new hyper with tokio-tls. Actually it required a bit of documentation reading, but finally turned our quite straightforward.
Here are supportive functions (in tls module):
use futures::{future, stream::{StreamExt, TryStreamExt}};
use hyper::server::accept::{from_stream, Accept};
use native_tls::Identity;
use std::fs::File;
use std::io::{self, Read};
use std::path::{Path, PathBuf};
use tokio::net::{TcpListener, TcpStream};
use tokio_tls::TlsStream;
type Error = Box<dyn std::error::Error + 'static>;
pub struct TlsConfig {
key_file: PathBuf,
key_password: String
}
impl TlsConfig {
pub fn new<P:Into<PathBuf>, S:Into<String>>(key_file: P, pass: S) -> Self {
TlsConfig{
key_file: key_file.into(),
key_password: pass.into()
}
}
}
fn load_private_key<P>(file: P, pass: &str) -> Result<Identity, Error>
where
P: AsRef<Path>,
{
let mut bytes = vec![];
let mut f = File::open(file)?;
f.read_to_end(&mut bytes)?;
let key = Identity::from_pkcs12(&bytes, pass)?;
Ok(key)
}
pub async fn tls_acceptor(
addr: &std::net::SocketAddr,
ssl: &TlsConfig,
) -> Result<impl Accept<Conn = TlsStream<TcpStream>, Error = io::Error>, Error> {
let private_key = load_private_key(&ssl.key_file, &ssl.key_password)?;
let tls_cx = native_tls::TlsAcceptor::builder(private_key).build()?;
let tls_cx = tokio_tls::TlsAcceptor::from(tls_cx);
let stream = TcpListener::bind(addr).await?.and_then(move |s| {
let acceptor = tls_cx.clone();
async move {
let conn = acceptor.accept(s).await;
conn.map_err(|e| {
error!("Error when accepting TLS connection {}", e);
io::Error::new(io::ErrorKind::Other, e)
})
}
})
.filter(|i| future::ready(i.is_ok())); // Need to filter out errors as they will stop server to accept connections
Ok(from_stream(stream))
}
Main trick is to filter out TLS accept errors – because an error in stream will stop the stream and new connections will not be accepted then. (And TLS errors are happening often – you may connect with just plain HTTP or client does not trust certificate, etc.).
With above function server can be started like this:
#[macro_use]
extern crate log;
use std::convert::Infallible;
use hyper::{Request, Response, Body, Server, service::{make_service_fn, service_fn}};
use tls::{TlsConfig, tls_acceptor};
mod tls;
type Error = Box<dyn std::error::Error + 'static>;
#[tokio::main]
pub async fn main() -> Result<(), Error> {
env_logger::init();
let addr = ([127, 0, 0, 1], 3000).into();
let tls_config = TlsConfig::new("/home/ivan/tls_key/audioserve.p12", "mypass");
let incoming = tls_acceptor(&addr,&tls_config).await?;
let make_svc = make_service_fn(|conn: &tokio_tls::TlsStream<tokio::net::TcpStream>| {
let peer = conn.get_ref().peer_addr().unwrap();
async move { Ok::<_, Infallible>(service_fn(move|req| {
debug!("Request from {} for path {}", peer, req.uri());
futures::future::ok::<_, Infallible>(Response::new(Body::from("Hello World!")))
})) }
});
let server = Server::builder(incoming).serve(make_svc);
info!("Listening on https://{}", addr);
server.await?;
Ok(())
}