#![deny(unsafe_code)]
#![deny(missing_docs)]
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::protocol::{AcmeError, Result, Url};
use crate::schema::directory::Directory;
pub mod account;
pub mod authorization;
mod client;
pub mod order;
pub use self::account::Account;
pub use self::authorization::Authorization;
pub use self::authorization::Challenge;
pub use self::order::Order;
use self::client::Client;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Provider {
name: Option<String>,
url: Url,
directory: Directory,
#[serde(skip, default)]
client: Client,
}
impl Provider {
pub async fn from_url(&self, url: Url) -> Result<Self> {
let client = Client::default();
let directory: Directory = client.get(url.clone()).await?.into_inner();
Ok(Provider {
name: None,
url,
directory,
client,
})
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn host(&self) -> &str {
self.url
.host_str()
.expect("ACME providers should have an https:// url with a well defined hostname")
}
pub fn directory(&self) -> &Directory {
&self.directory
}
pub fn account<K>(&self, key: Arc<K>) -> self::account::AccountBuilder<K>
where
K: Clone,
{
self::account::AccountBuilder::new(self.clone(), key)
}
#[inline]
pub(crate) fn client(&self) -> &self::client::Client {
&self.client
}
pub fn build() -> ProviderBuilder {
ProviderBuilder::new()
}
}
#[derive(Debug, Error)]
pub enum BuilderError {
#[error("Building HTTPS client: {0}")]
Client(#[source] reqwest::Error),
#[error("Missing provider URL")]
Url,
#[error("Fetching provider directory: {0}")]
Directory(#[source] AcmeError),
}
#[derive(Debug)]
pub struct ProviderBuilder {
client: crate::protocol::client::ClientBuilder,
url: Option<Url>,
directory: Option<Directory>,
name: Option<String>,
}
impl ProviderBuilder {
fn new() -> Self {
ProviderBuilder {
client: crate::protocol::client::AcmeClient::builder(),
url: None,
directory: None,
name: None,
}
}
pub fn add_root_certificate(mut self, cert: reqwest::Certificate) -> Self {
self.client = self.client.add_root_certificate(cert);
self
}
pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
self.client = self.client.timeout(timeout);
self
}
pub fn connect_timeout(mut self, timeout: std::time::Duration) -> Self {
self.client = self.client.connect_timeout(timeout);
self
}
pub fn directory_url(mut self, url: Url) -> Self {
self.url = Some(url);
self
}
pub fn directory(mut self, directory: Directory) -> Self {
self.directory = Some(directory);
self
}
pub fn name<S: Into<String>>(mut self, name: S) -> Self {
self.name = Some(name.into());
self
}
pub async fn build(self) -> ::std::result::Result<Provider, BuilderError> {
let mut client = self.client.build().map_err(BuilderError::Client)?;
let url = self.url.ok_or(BuilderError::Url)?;
let directory = if let Some(directory) = self.directory {
directory
} else {
client
.get(url.clone())
.await
.map_err(BuilderError::Directory)?
.into_inner()
};
client.set_new_nonce_url(directory.new_nonce.clone());
Ok(Provider {
name: self.name,
url,
directory,
client: Client::new(client),
})
}
}
pub mod provider {
#[cfg(feature = "pebble")]
pub const PEBBLE: &str = "https://localhost:14000/dir";
pub const LETSENCRYPT: &str = "https://acme-v02.api.letsencrypt.org/directory";
}