diff --git a/examples/provision.rs b/examples/provision.rs index f6da66b..a766c4c 100644 --- a/examples/provision.rs +++ b/examples/provision.rs @@ -82,7 +82,7 @@ async fn main() -> anyhow::Result<()> { // Exponentially back off until the order becomes ready or invalid. - let status = order.poll(&RetryPolicy::default()).await?; + let status = order.poll_ready(&RetryPolicy::default()).await?; if status != OrderStatus::Ready { return Err(anyhow::anyhow!("unexpected order status: {status:?}")); } diff --git a/src/order.rs b/src/order.rs index 60bed64..712b532 100644 --- a/src/order.rs +++ b/src/order.rs @@ -166,7 +166,7 @@ impl Order { /// /// Yields the [`OrderStatus`] immediately if `Ready` or `Invalid`, or yields an /// [`Error::Timeout`] if the [`RetryPolicy::timeout`] has been reached. - pub async fn poll(&mut self, retries: &RetryPolicy) -> Result { + pub async fn poll_ready(&mut self, retries: &RetryPolicy) -> Result { let mut retrying = retries.state(); self.retry_after = None; loop { @@ -183,6 +183,32 @@ impl Order { } } + /// Poll the certificate with the given [`RetryPolicy`] + /// + /// Yields the PEM encoded certificate chain for this order if the order state becomes + /// `Valid`. The function keeps polling as long as the order state is `Processing`. + /// An error is returned immediately: if the order state is `Invalid`, if polling runs + /// into a timeout, or if the ACME CA suggest to retry at a later time. + pub async fn poll_certificate(&mut self, retries: &RetryPolicy) -> Result { + let mut retrying = retries.state(); + self.retry_after = None; + loop { + if let ControlFlow::Break(err) = retrying.wait(self.retry_after.take()).await { + return Err(err); + } + + let state = self.refresh().await?; + if let Some(error) = &state.error { + return Err(Error::Api(error.clone())); + } else if let OrderStatus::Valid | OrderStatus::Invalid = state.status { + return self + .certificate() + .await? + .ok_or(Error::Str("no certificates received from ACME CA")); + } + } + } + /// Refresh the current state of the order pub async fn refresh(&mut self) -> Result<&OrderState, Error> { let rsp = self @@ -402,8 +428,8 @@ impl Deref for AuthorizationHandle<'_> { /// * Set up the challenge response in your infrastructure (details vary by challenge type) /// * Call [`ChallengeHandle::set_ready()`] for that challenge after setup is complete /// -/// After the challenges have been set to ready, call [`Order::poll()`] to wait until the order is -/// ready to be finalized (or to learn if it becomes invalid). Once it is ready, call +/// After the challenges have been set to ready, call [`Order::poll_ready()`] to wait until the +/// order is ready to be finalized (or to learn if it becomes invalid). Once it is ready, call /// [`Order::finalize()`] to get the certificate. /// /// Dereferences to the underlying [`Challenge`] for easy access to the challenge's state. diff --git a/tests/pebble.rs b/tests/pebble.rs index 3216b8f..a6efc09 100644 --- a/tests/pebble.rs +++ b/tests/pebble.rs @@ -597,7 +597,7 @@ impl Environment { } // Poll until the order is ready. - let status = order.poll(&RETRY_POLICY).await?; + let status = order.poll_ready(&RETRY_POLICY).await?; if status != OrderStatus::Ready { return Err(format!("unexpected order status: {status:?}").into()); } @@ -653,12 +653,7 @@ impl Environment { debug!(order_url = order.url(), "finalizing order"); order.finalize().await?; debug!(order_url = order.url(), "fetching order certificate chain"); - let cert_chain_pem = loop { - match order.certificate().await.unwrap() { - Some(cert_chain_pem) => break cert_chain_pem, - None => sleep(Duration::from_secs(1)).await, - } - }; + let cert_chain_pem = order.poll_certificate(&RETRY_POLICY).await.unwrap(); // Parse the PEM chain into a vec of DER certificates ordered ee -> intermediates. info!("successfully issued certificate");