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
This commit is contained in:
parent
643c5859a6
commit
e83836b719
44
src/lib.rs
44
src/lib.rs
|
@ -490,6 +490,43 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post(
|
async fn post(
|
||||||
|
&self,
|
||||||
|
payload: Option<&impl Serialize>,
|
||||||
|
mut nonce: Option<String>,
|
||||||
|
signer: &impl Signer,
|
||||||
|
url: &str,
|
||||||
|
) -> Result<BytesResponse, Error> {
|
||||||
|
let mut retries = 3;
|
||||||
|
loop {
|
||||||
|
let mut response = self
|
||||||
|
.post_attempt(payload, nonce.clone(), signer, url)
|
||||||
|
.await?;
|
||||||
|
if response.parts.status != StatusCode::BAD_REQUEST {
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
let body = response.body.into_bytes().await.map_err(Error::Other)?;
|
||||||
|
let problem = serde_json::from_slice::<Problem>(&body)?;
|
||||||
|
if let Some("urn:ietf:params:acme:error:badNonce") = problem.r#type.as_deref() {
|
||||||
|
retries -= 1;
|
||||||
|
if retries != 0 {
|
||||||
|
// Retrieve the new nonce. If it isn't there (it
|
||||||
|
// should be, the spec requires it) then we will
|
||||||
|
// manually refresh a new one in `post_attempt`
|
||||||
|
// due to `nonce` being `None` but getting it from
|
||||||
|
// the response saves us making that request.
|
||||||
|
nonce = nonce_from_response(&response);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(BytesResponse {
|
||||||
|
parts: response.parts,
|
||||||
|
body: Box::new(body),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_attempt(
|
||||||
&self,
|
&self,
|
||||||
payload: Option<&impl Serialize>,
|
payload: Option<&impl Serialize>,
|
||||||
nonce: Option<String>,
|
nonce: Option<String>,
|
||||||
|
@ -789,6 +826,13 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl BytesBody for Bytes {
|
||||||
|
async fn into_bytes(&mut self) -> Result<Bytes, Box<dyn StdError + Send + Sync + 'static>> {
|
||||||
|
Ok(self.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Object safe body trait
|
/// Object safe body trait
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait BytesBody: Send {
|
pub trait BytesBody: Send {
|
||||||
|
|
Loading…
Reference in New Issue