Maintain OrderState within Order type

This commit is contained in:
Dirkjan Ochtman 2023-03-15 16:26:52 +01:00
parent 652c4815ec
commit a2d4129201
2 changed files with 53 additions and 37 deletions

View File

@ -34,19 +34,20 @@ async fn main() -> anyhow::Result<()> {
// process multiple orders in parallel for a single account.
let identifier = Identifier::Dns(opts.name);
let (mut order, state) = account
let mut order = account
.new_order(&NewOrder {
identifiers: &[identifier],
})
.await
.unwrap();
let state = order.state();
info!("order state: {:#?}", state);
assert!(matches!(state.status, OrderStatus::Pending));
// Pick the desired challenge type and prepare the response.
let authorizations = order.authorizations(&state.authorizations).await.unwrap();
let authorizations = order.authorizations().await.unwrap();
let mut challenges = Vec::with_capacity(authorizations.len());
for authz in &authorizations {
match authz.status {
@ -87,12 +88,12 @@ async fn main() -> anyhow::Result<()> {
let mut tries = 1u8;
let mut delay = Duration::from_millis(250);
let state = loop {
loop {
sleep(delay).await;
let state = order.state().await.unwrap();
let state = order.refresh().await.unwrap();
if let OrderStatus::Ready | OrderStatus::Invalid = state.status {
info!("order state: {:#?}", state);
break state;
break;
}
delay *= 2;
@ -104,10 +105,14 @@ async fn main() -> anyhow::Result<()> {
return Err(anyhow::anyhow!("order is not ready"));
}
}
};
}
if state.status == OrderStatus::Invalid {
return Err(anyhow::anyhow!("order is invalid"));
let state = order.state();
if state.status != OrderStatus::Ready {
return Err(anyhow::anyhow!(
"unexpected order status: {:?}",
state.status
));
}
let mut names = Vec::with_capacity(challenges.len());
@ -125,7 +130,7 @@ async fn main() -> anyhow::Result<()> {
// Finalize the order and print certificate chain, private key and account credentials.
let cert_chain_pem = order.finalize(&csr, &state.finalize).await.unwrap();
let cert_chain_pem = order.finalize(&csr).await.unwrap();
info!("certficate chain:\n\n{}", cert_chain_pem,);
info!("private key:\n\n{}", cert.serialize_private_key_pem());
info!(

View File

@ -36,7 +36,8 @@ use types::{
pub struct Order {
account: Arc<AccountInner>,
nonce: Option<String>,
order_url: String,
url: String,
state: OrderState,
}
impl Order {
@ -55,12 +56,9 @@ impl Order {
/// After the challenges have been set up, check the [`Order::state()`] to see
/// if the order is ready to be finalized (or becomes invalid). Once it is
/// ready, call `Order::finalize()` to get the certificate.
pub async fn authorizations(
&mut self,
authz_urls: &[String],
) -> Result<Vec<Authorization>, Error> {
let mut authorizations = Vec::with_capacity(authz_urls.len());
for url in authz_urls {
pub async fn authorizations(&mut self) -> Result<Vec<Authorization>, Error> {
let mut authorizations = Vec::with_capacity(self.state.authorizations.len());
for url in &self.state.authorizations {
authorizations.push(self.account.get(&mut self.nonce, url).await?);
}
Ok(authorizations)
@ -77,15 +75,15 @@ impl Order {
/// Request a certificate from the given Certificate Signing Request (CSR)
///
/// Creating a CSR is outside of the scope of instant-acme. Make sure you pass in a
/// DER representation of the CSR in `csr_der` and the [`OrderState::finalize`] URL
/// in `finalize_url`. The resulting `String` will contain the PEM-encoded certificate chain.
pub async fn finalize(&mut self, csr_der: &[u8], finalize_url: &str) -> Result<String, Error> {
/// DER representation of the CSR in `csr_der`. The resulting `String` will contain the
/// PEM-encoded certificate chain.
pub async fn finalize(&mut self, csr_der: &[u8]) -> Result<String, Error> {
let rsp = self
.account
.post(
Some(&FinalizeRequest::new(csr_der)),
self.nonce.take(),
finalize_url,
&self.state.finalize,
)
.await?;
@ -129,9 +127,28 @@ impl Order {
self.account.get(&mut self.nonce, challenge_url).await
}
/// Get the current state of the order
pub async fn state(&mut self) -> Result<OrderState, Error> {
self.account.get(&mut self.nonce, &self.order_url).await
/// Refresh the current state of the order
pub async fn refresh(&mut self) -> Result<&OrderState, Error> {
let rsp = self
.account
.post(None::<&Empty>, self.nonce.take(), &self.url)
.await?;
self.nonce = nonce_from_response(&rsp);
self.state = Problem::check::<OrderState>(rsp).await?;
Ok(&self.state)
}
/// Get the last known state of the order
///
/// Call `refresh()` to get the latest state from the server.
pub fn state(&mut self) -> &OrderState {
&self.state
}
/// Get the URL of the order
pub fn url(&self) -> &str {
&self.url
}
}
@ -185,11 +202,8 @@ impl Account {
/// Create a new order based on the given [`NewOrder`]
///
/// Returns both an [`Order`] instance and the initial [`OrderState`].
pub async fn new_order<'a>(
&'a self,
order: &NewOrder<'_>,
) -> Result<(Order, OrderState), Error> {
/// Returns an [`Order`] instance. Use the [`Order::state()`] method to inspect its state.
pub async fn new_order<'a>(&'a self, order: &NewOrder<'_>) -> Result<Order, Error> {
let rsp = self
.inner
.post(Some(order), None, &self.inner.client.urls.new_order)
@ -202,15 +216,12 @@ impl Account {
.and_then(|hv| hv.to_str().ok())
.map(|s| s.to_owned());
let status = Problem::check(rsp).await?;
Ok((
Order {
Ok(Order {
account: self.inner.clone(),
nonce,
order_url: order_url.ok_or("no order URL found")?,
},
status,
))
url: order_url.ok_or("no order URL found")?,
state: Problem::check::<OrderState>(rsp).await?,
})
}
/// Get the account's credentials, which can be serialized