tokio_rustls_acme/caches/
dir.rs

1use crate::{AccountCache, CertCache};
2use async_trait::async_trait;
3use base64::engine::general_purpose::URL_SAFE_NO_PAD;
4use base64::Engine;
5use ring::digest::{Context, SHA256};
6use std::io::ErrorKind;
7use std::path::Path;
8use tokio::fs;
9
10pub struct DirCache<P: AsRef<Path> + Send + Sync> {
11    inner: P,
12}
13
14impl<P: AsRef<Path> + Send + Sync> DirCache<P> {
15    pub fn new(dir: P) -> Self {
16        Self { inner: dir }
17    }
18    async fn read_if_exist(
19        &self,
20        file: impl AsRef<Path>,
21    ) -> Result<Option<Vec<u8>>, std::io::Error> {
22        let path = self.inner.as_ref().join(file);
23        match fs::read(path).await {
24            Ok(content) => Ok(Some(content)),
25            Err(err) => match err.kind() {
26                ErrorKind::NotFound => Ok(None),
27                _ => Err(err),
28            },
29        }
30    }
31    async fn write(
32        &self,
33        file: impl AsRef<Path>,
34        contents: impl AsRef<[u8]>,
35    ) -> Result<(), std::io::Error> {
36        fs::create_dir_all(&self.inner).await?;
37        let path = self.inner.as_ref().join(file);
38        fs::write(path, contents).await
39    }
40
41    fn cached_account_file_name(contact: &[String], directory_url: impl AsRef<str>) -> String {
42        let mut ctx = Context::new(&SHA256);
43        for el in contact {
44            ctx.update(el.as_ref());
45            ctx.update(&[0])
46        }
47        ctx.update(directory_url.as_ref().as_bytes());
48        let hash = URL_SAFE_NO_PAD.encode(ctx.finish());
49        format!("cached_account_{}", hash)
50    }
51    fn cached_cert_file_name(domains: &[String], directory_url: impl AsRef<str>) -> String {
52        let mut ctx = Context::new(&SHA256);
53        for domain in domains {
54            ctx.update(domain.as_ref());
55            ctx.update(&[0])
56        }
57        ctx.update(directory_url.as_ref().as_bytes());
58        let hash = URL_SAFE_NO_PAD.encode(ctx.finish());
59        format!("cached_cert_{}", hash)
60    }
61}
62
63#[async_trait]
64impl<P: AsRef<Path> + Send + Sync> CertCache for DirCache<P> {
65    type EC = std::io::Error;
66    async fn load_cert(
67        &self,
68        domains: &[String],
69        directory_url: &str,
70    ) -> Result<Option<Vec<u8>>, Self::EC> {
71        let file_name = Self::cached_cert_file_name(domains, directory_url);
72        self.read_if_exist(file_name).await
73    }
74    async fn store_cert(
75        &self,
76        domains: &[String],
77        directory_url: &str,
78        cert: &[u8],
79    ) -> Result<(), Self::EC> {
80        let file_name = Self::cached_cert_file_name(domains, directory_url);
81        self.write(file_name, cert).await
82    }
83}
84
85#[async_trait]
86impl<P: AsRef<Path> + Send + Sync> AccountCache for DirCache<P> {
87    type EA = std::io::Error;
88    async fn load_account(
89        &self,
90        contact: &[String],
91        directory_url: &str,
92    ) -> Result<Option<Vec<u8>>, Self::EA> {
93        let file_name = Self::cached_account_file_name(contact, directory_url);
94        self.read_if_exist(file_name).await
95    }
96
97    async fn store_account(
98        &self,
99        contact: &[String],
100        directory_url: &str,
101        account: &[u8],
102    ) -> Result<(), Self::EA> {
103        let file_name = Self::cached_account_file_name(contact, directory_url);
104        self.write(file_name, account).await
105    }
106}