tokio_rustls_acme/caches/
dir.rs1use 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}