coil-tls 0.1.0

TLS management primitives for the Coil framework.
Documentation
use std::{error::Error, fs, io, net::SocketAddr, path::PathBuf, time::Duration};

use async_trait::async_trait;
use lers::{solver::Solver, solver::SolverHandle, solver::TlsAlpn01Solver};
use tokio::net::TcpListener;

#[derive(Debug, Clone)]
pub(crate) struct FilesystemHttp01Solver {
    root: PathBuf,
}

impl FilesystemHttp01Solver {
    pub(crate) fn new(root: PathBuf) -> Self {
        Self { root }
    }

    fn challenge_path(&self, token: &str) -> PathBuf {
        self.root
            .join(".well-known")
            .join("acme-challenge")
            .join(token)
    }
}

#[async_trait]
impl Solver for FilesystemHttp01Solver {
    async fn present(
        &self,
        _domain: String,
        token: String,
        key_authorization: String,
    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
        let path = self.challenge_path(&token);
        if let Some(parent) = path.parent() {
            fs::create_dir_all(parent).map_err(boxed_io_error)?;
        }
        fs::write(&path, key_authorization).map_err(boxed_io_error)?;
        Ok(())
    }

    async fn cleanup(&self, token: &str) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
        let path = self.challenge_path(token);
        match fs::remove_file(path) {
            Ok(()) => Ok(()),
            Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(()),
            Err(error) => Err(boxed_io_error(error)),
        }
    }

    fn attempts(&self) -> usize {
        60
    }

    fn interval(&self) -> Duration {
        Duration::from_secs(2)
    }
}

pub(crate) async fn start_tls_alpn_solver(
    address: SocketAddr,
) -> Result<(TlsAlpn01Solver, SolverHandle<io::Error>), io::Error> {
    let solver = TlsAlpn01Solver::new();
    let listener = TcpListener::bind(address).await?;
    let handle = solver.start_with_listener(listener)?;
    Ok((solver, handle))
}

fn boxed_io_error(error: io::Error) -> Box<dyn Error + Send + Sync + 'static> {
    Box::new(error)
}