tokio_rustls_acme/
config.rs1use crate::acme::{LETS_ENCRYPT_PRODUCTION_DIRECTORY, LETS_ENCRYPT_STAGING_DIRECTORY};
2use crate::caches::{BoxedErrCache, CompositeCache, NoCache};
3use crate::{AccountCache, Cache, CertCache};
4use crate::{AcmeState, Incoming};
5use futures::Stream;
6use rustls::{ClientConfig, RootCertStore};
7use std::convert::Infallible;
8use std::fmt::Debug;
9use std::sync::Arc;
10use tokio::io::{AsyncRead, AsyncWrite};
11use webpki_roots::TLS_SERVER_ROOTS;
12
13pub struct AcmeConfig<EC: Debug, EA: Debug = EC> {
17 pub(crate) client_config: Arc<ClientConfig>,
18 pub(crate) directory_url: String,
19 pub(crate) domains: Vec<String>,
20 pub(crate) contact: Vec<String>,
21 pub(crate) cache: Box<dyn Cache<EC = EC, EA = EA>>,
22}
23
24impl AcmeConfig<Infallible, Infallible> {
25 pub fn new(domains: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
52 let mut root_store = RootCertStore::empty();
53 root_store.extend(
54 TLS_SERVER_ROOTS
55 .iter()
56 .map(|ta| rustls::pki_types::TrustAnchor {
57 subject: ta.subject.clone(),
58 subject_public_key_info: ta.subject_public_key_info.clone(),
59 name_constraints: ta.name_constraints.clone(),
60 }),
61 );
62 let client_config = Arc::new(
63 ClientConfig::builder()
64 .with_root_certificates(root_store)
65 .with_no_client_auth(),
66 );
67 AcmeConfig {
68 client_config,
69 directory_url: LETS_ENCRYPT_STAGING_DIRECTORY.into(),
70 domains: domains.into_iter().map(|s| s.as_ref().into()).collect(),
71 contact: vec![],
72 cache: Box::new(NoCache::new()),
73 }
74 }
75}
76
77impl<EC: 'static + Debug, EA: 'static + Debug> AcmeConfig<EC, EA> {
78 pub fn client_tls_config(mut self, client_config: Arc<ClientConfig>) -> Self {
80 self.client_config = client_config;
81 self
82 }
83 pub fn directory(mut self, directory_url: impl AsRef<str>) -> Self {
84 self.directory_url = directory_url.as_ref().into();
85 self
86 }
87 pub fn directory_lets_encrypt(mut self, production: bool) -> Self {
88 self.directory_url = match production {
89 true => LETS_ENCRYPT_PRODUCTION_DIRECTORY,
90 false => LETS_ENCRYPT_STAGING_DIRECTORY,
91 }
92 .into();
93 self
94 }
95 pub fn domains(mut self, contact: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
96 self.domains = contact.into_iter().map(|s| s.as_ref().into()).collect();
97 self
98 }
99 pub fn domains_push(mut self, contact: impl AsRef<str>) -> Self {
100 self.domains.push(contact.as_ref().into());
101 self
102 }
103
104 pub fn contact(mut self, contact: impl IntoIterator<Item = impl AsRef<str>>) -> Self {
108 self.contact = contact.into_iter().map(|s| s.as_ref().into()).collect();
109 self
110 }
111
112 pub fn contact_push(mut self, contact: impl AsRef<str>) -> Self {
116 self.contact.push(contact.as_ref().into());
117 self
118 }
119
120 pub fn cache<C: 'static + Cache>(self, cache: C) -> AcmeConfig<C::EC, C::EA> {
121 AcmeConfig {
122 client_config: self.client_config,
123 directory_url: self.directory_url,
124 domains: self.domains,
125 contact: self.contact,
126 cache: Box::new(cache),
127 }
128 }
129 pub fn cache_compose<CC: 'static + CertCache, CA: 'static + AccountCache>(
130 self,
131 cert_cache: CC,
132 account_cache: CA,
133 ) -> AcmeConfig<CC::EC, CA::EA> {
134 self.cache(CompositeCache::new(cert_cache, account_cache))
135 }
136 pub fn cache_with_boxed_err<C: 'static + Cache>(self, cache: C) -> AcmeConfig<Box<dyn Debug>> {
137 self.cache(BoxedErrCache::new(cache))
138 }
139 pub fn cache_option<C: 'static + Cache>(self, cache: Option<C>) -> AcmeConfig<C::EC, C::EA> {
140 match cache {
141 Some(cache) => self.cache(cache),
142 None => self.cache(NoCache::<C::EC, C::EA>::new()),
143 }
144 }
145 pub fn state(self) -> AcmeState<EC, EA> {
146 AcmeState::new(self)
147 }
148 pub fn incoming<
152 TCP: AsyncRead + AsyncWrite + Unpin,
153 ETCP,
154 ITCP: Stream<Item = Result<TCP, ETCP>> + Unpin,
155 >(
156 self,
157 tcp_incoming: ITCP,
158 alpn_protocols: Vec<Vec<u8>>,
159 ) -> Incoming<TCP, ETCP, ITCP, EC, EA> {
160 self.state().incoming(tcp_incoming, alpn_protocols)
161 }
162}