#![forbid(unsafe_code)]
#![deny(missing_docs)]
use std::fmt::Debug;
use async_std::{net::TcpStream, stream::StreamExt};
use futures_lite::io::AsyncWriteExt;
pub use rustls_acme::{self, AcmeConfig};
use tide_rustls::async_rustls::{server::TlsStream, TlsAcceptor};
use tide_rustls::rustls::Session;
use tracing::{error, info, info_span, Instrument};
pub struct AcmeTlsAcceptor(TlsAcceptor);
impl AcmeTlsAcceptor {
pub fn new<EC: 'static + Debug, EA: 'static + Debug>(config: AcmeConfig<EC, EA>) -> Self {
let mut state = config.state();
let acceptor = state.acceptor();
async_std::task::spawn(async move {
loop {
async {
match state
.next()
.await
.expect("AcmeState::next() always returns Some")
{
Ok(event) => info!(?event, "AcmeState::next() processed an event"),
Err(event) => error!(?event, "AcmeState::next() returned an error"),
}
}
.instrument(info_span!("AcmeState::next()"))
.await
}
});
Self(acceptor)
}
}
#[async_trait::async_trait]
impl tide_rustls::CustomTlsAcceptor for AcmeTlsAcceptor {
async fn accept(&self, stream: TcpStream) -> std::io::Result<Option<TlsStream<TcpStream>>> {
let mut tls = self.0.accept(stream).await?;
match tls.get_ref().1.get_alpn_protocol() {
Some(rustls_acme::acme::ACME_TLS_ALPN_NAME) => {
info_span!("AcmeTlsAcceptor::accept()")
.in_scope(|| info!("received acme-tls/1 validation request"));
tls.close().await?;
Ok(None)
}
_ => Ok(Some(tls)),
}
}
}
pub trait TideRustlsExt {
fn acme<EC: 'static + Debug, EA: 'static + Debug>(self, config: AcmeConfig<EC, EA>) -> Self;
}
impl<State> TideRustlsExt for tide_rustls::TlsListenerBuilder<State> {
fn acme<EC: 'static + Debug, EA: 'static + Debug>(self, config: AcmeConfig<EC, EA>) -> Self {
self.tls_acceptor(std::sync::Arc::new(AcmeTlsAcceptor::new(config)))
}
}