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