Skip to content

Commit e83836b

Browse files
Nick Spaindjc
authored andcommitted
Retry bad nonce errors after generating a new nonce
Retry `urn:ietf:params:acme:error:badNonce` errors as they are defined as retryable[^1]. Requests failing with a bad nonce error will be retried three time retried three times before the failure is returned to the caller. This implenments the `BytesBody` trait for `bytes::Bytes` as we need to consume the response body to be able check if the failure was due to a bad nonce which required parsing the body. The body is only consumed if the response status is bad request, in all other cases the body is still lazily consumable. [^1]: https://datatracker.ietf.org/doc/html/rfc8555/#section-6.5
1 parent 643c585 commit e83836b

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

src/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,43 @@ impl Client {
490490
}
491491

492492
async fn post(
493+
&self,
494+
payload: Option<&impl Serialize>,
495+
mut nonce: Option<String>,
496+
signer: &impl Signer,
497+
url: &str,
498+
) -> Result<BytesResponse, Error> {
499+
let mut retries = 3;
500+
loop {
501+
let mut response = self
502+
.post_attempt(payload, nonce.clone(), signer, url)
503+
.await?;
504+
if response.parts.status != StatusCode::BAD_REQUEST {
505+
return Ok(response);
506+
}
507+
let body = response.body.into_bytes().await.map_err(Error::Other)?;
508+
let problem = serde_json::from_slice::<Problem>(&body)?;
509+
if let Some("urn:ietf:params:acme:error:badNonce") = problem.r#type.as_deref() {
510+
retries -= 1;
511+
if retries != 0 {
512+
// Retrieve the new nonce. If it isn't there (it
513+
// should be, the spec requires it) then we will
514+
// manually refresh a new one in `post_attempt`
515+
// due to `nonce` being `None` but getting it from
516+
// the response saves us making that request.
517+
nonce = nonce_from_response(&response);
518+
continue;
519+
}
520+
}
521+
522+
return Ok(BytesResponse {
523+
parts: response.parts,
524+
body: Box::new(body),
525+
});
526+
}
527+
}
528+
529+
async fn post_attempt(
493530
&self,
494531
payload: Option<&impl Serialize>,
495532
nonce: Option<String>,
@@ -789,6 +826,13 @@ where
789826
}
790827
}
791828

829+
#[async_trait]
830+
impl BytesBody for Bytes {
831+
async fn into_bytes(&mut self) -> Result<Bytes, Box<dyn StdError + Send + Sync + 'static>> {
832+
Ok(self.to_owned())
833+
}
834+
}
835+
792836
/// Object safe body trait
793837
#[async_trait]
794838
pub trait BytesBody: Send {

0 commit comments

Comments
 (0)