diff --git a/examples/provision.rs b/examples/provision.rs index 5127826..04df707 100644 --- a/examples/provision.rs +++ b/examples/provision.rs @@ -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!( diff --git a/src/lib.rs b/src/lib.rs index c8265a6..92b5fd4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,8 @@ use types::{ pub struct Order { account: Arc, nonce: Option, - 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, Error> { - let mut authorizations = Vec::with_capacity(authz_urls.len()); - for url in authz_urls { + pub async fn authorizations(&mut self) -> Result, 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 { + /// 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 { 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 { - 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::(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 { 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 { - account: self.inner.clone(), - nonce, - order_url: order_url.ok_or("no order URL found")?, - }, - status, - )) + Ok(Order { + account: self.inner.clone(), + nonce, + url: order_url.ok_or("no order URL found")?, + state: Problem::check::(rsp).await?, + }) } /// Get the account's credentials, which can be serialized