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