venues 0.3.2

Privacy friendly online meeting service
Documentation
use std::io;

use async_std::{
    channel::Sender,
    net::{TcpStream, ToSocketAddrs},
};
use futures::AsyncWriteExt;
use log::*;
use rustls_acme::{futures_rustls::server::TlsStream, AccountCache, CertCache};

pub async fn acme(
    prod: bool,
    on: impl ToSocketAddrs + std::fmt::Debug,
    domains: impl IntoIterator<Item = impl AsRef<str>>,
    contact: Option<impl AsRef<str>>,
    cert_tx: Sender<Option<(Vec<String>, Vec<u8>)>>,
    conns_tx: Option<Sender<TlsStream<TcpStream>>>,
) {
    let res = acme_handle(prod, on, domains, contact, cert_tx.clone(), conns_tx).await;
    if let Err(e) = res {
        warn!("ACME failed: {e}")
    }
    // making sure we keep a ref to sender to keep channel alive
    cert_tx.send(None).await.expect("no certs to send");
}

async fn acme_handle(
    prod: bool,
    on: impl ToSocketAddrs + std::fmt::Debug,
    domains: impl IntoIterator<Item = impl AsRef<str>>,
    contact: Option<impl AsRef<str>>,
    cert_tx: Sender<Option<(Vec<String>, Vec<u8>)>>,
    conns_tx: Option<Sender<TlsStream<TcpStream>>>,
) -> std::io::Result<()> {
    use futures::prelude::stream::StreamExt;
    use rustls_acme::{caches::DirCache, AcmeConfig};

    let contact = contact
        .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "No ACME without contact (mailto:)"))?;

    let cfg = AcmeConfig::new(domains)
        .contact_push(contact)
        .cache(SharingCache(DirCache::new("./rustls_acme_cache"), cert_tx))
        .directory_lets_encrypt(prod);

    info!("Running ACME on {on:?}: {cfg:?}");

    let tcp_listener = async_std::net::TcpListener::bind(on).await?;

    let mut tls_incoming = cfg.incoming(tcp_listener.incoming(), Vec::new());

    while let Some(tls) = tls_incoming.next().await {
        let mut tls = tls?;
        if let Some(tx) = &conns_tx {
            tx.send(tls).await;
        } else {
            tls.write_all(HELLO).await?;
            tls.close().await?;
        }
    }
    const HELLO: &'static [u8] = br#"HTTP/1.1 200 OK
Content-Length: 10
Content-Type: text/plain; charset=utf-8

Hello Tls!"#;
    Ok(())
}

struct SharingCache<T>(T, Sender<Option<(Vec<String>, Vec<u8>)>>);

#[async_trait::async_trait]
impl<T> AccountCache for SharingCache<T>
where
    T: AccountCache,
{
    type EA = T::EA;

    #[must_use]
    #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
    async fn load_account(
        &self,
        contact: &[String],
        directory_url: &str,
    ) -> Result<Option<Vec<u8>>, Self::EA> {
        self.0.load_account(contact, directory_url).await
    }

    #[must_use]
    #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
    async fn store_account(
        &self,
        contact: &[String],
        directory_url: &str,
        account: &[u8],
    ) -> Result<(), Self::EA> {
        self.0.store_account(contact, directory_url, account).await
    }
}
#[async_trait::async_trait]

impl<T> CertCache for SharingCache<T>
where
    T: CertCache + std::marker::Sync + std::marker::Send,
    T::EC: std::marker::Sync + std::marker::Send,
{
    type EC = T::EC;

    #[must_use]
    #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
    async fn load_cert(
        &self,
        domains: &[String],
        directory_url: &str,
    ) -> Result<Option<Vec<u8>>, Self::EC> {
        let loaded = self.0.load_cert(domains, directory_url).await;
        match loaded {
            Ok(Some(cert)) => {
                self.1
                    .send(Some((domains.to_vec(), cert.clone())))
                    .await
                    .ok();
                Ok(Some(cert))
            }
            other => other,
        }
    }

    #[must_use]
    #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)]
    async fn store_cert(
        &self,
        domains: &[String],
        directory_url: &str,
        cert: &[u8],
    ) -> Result<(), Self::EC> {
        self.1
            .send(Some((domains.to_vec(), cert.to_vec())))
            .await
            .ok();
        self.0.store_cert(domains, directory_url, cert).await
    }
}