penumbra_sdk_auto_https/
lib.rs1use {
6 anyhow::Error,
7 futures::Future,
8 rustls::ServerConfig,
9 rustls_acme::{axum::AxumAcceptor, caches::DirCache, AcmeConfig, AcmeState},
10 std::{fmt::Debug, path::PathBuf, sync::Arc},
11};
12
13const ALPN_PROTOCOLS: [&[u8]; 2] = [b"h2", b"http/1.1"];
21
22const CACHE_DIR: &str = "tokio_rustls_acme_cache";
25
26pub fn axum_acceptor(
32 home: PathBuf,
33 domain: String,
34 production_api: bool,
35) -> (AxumAcceptor, impl Future<Output = Result<(), Error>>) {
36 let cache = home.join(CACHE_DIR);
38 let cache = DirCache::new(cache);
39
40 let state = AcmeConfig::new(vec![domain])
42 .cache(cache)
43 .directory_lets_encrypt(production_api)
44 .state();
45
46 let mut rustls_config = ServerConfig::builder()
48 .with_no_client_auth()
49 .with_cert_resolver(state.resolver());
50 rustls_config.alpn_protocols = self::alpn_protocols();
51 let rustls_config = Arc::new(rustls_config);
52
53 let acceptor = state.axum_acceptor(rustls_config.clone());
55 let worker = self::acme_worker(state);
56 (acceptor, worker)
57}
58
59#[tracing::instrument(level = "error", skip_all)]
63async fn acme_worker<EC, EA>(mut state: AcmeState<EC, EA>) -> Result<(), anyhow::Error>
64where
65 EC: Debug + 'static,
66 EA: Debug + 'static,
67{
68 use futures::StreamExt;
69 loop {
70 match state.next().await {
71 Some(Ok(ok)) => tracing::debug!("received acme event: {:?}", ok),
72 Some(Err(err)) => {
73 tracing::error!("acme error: {:?}", err);
74 anyhow::bail!("exiting due to acme error");
75 }
76 None => {
77 debug_assert!(false, "acme worker unexpectedly reached end-of-stream");
78 tracing::error!("acme worker unexpectedly reached end-of-stream");
79 anyhow::bail!("unexpected end-of-stream");
80 }
81 }
82 }
83}
84
85fn alpn_protocols() -> Vec<Vec<u8>> {
89 ALPN_PROTOCOLS.into_iter().map(<[u8]>::to_vec).collect()
90}