Skip to content

Commit e9132b5

Browse files
committed
fixup! ACME: certificate issue and renewal implementation.
1 parent 548e002 commit e9132b5

File tree

1 file changed

+64
-90
lines changed

1 file changed

+64
-90
lines changed

src/acme.rs

Lines changed: 64 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use core::cell::RefCell;
22
use core::ptr::NonNull;
33
use core::time::Duration;
44
use std::collections::VecDeque;
5-
use std::io;
65
use std::string::{String, ToString};
76

87
use anyhow::{anyhow, Result};
@@ -30,7 +29,6 @@ pub mod types;
3029
const DEFAULT_RETRY_INTERVAL: Duration = Duration::from_secs(1);
3130
static REPLAY_NONCE: http::HeaderName = http::HeaderName::from_static("replay-nonce");
3231

33-
#[derive(Debug)]
3432
pub struct NewCertificateOutput {
3533
pub chain: Bytes,
3634
pub x509: Vec<X509>,
@@ -64,12 +62,12 @@ impl NoncePool {
6462
}
6563

6664
pub fn add(&self, nonce: String) {
67-
self.0.borrow_mut().push_back(nonce.to_string());
65+
self.0.borrow_mut().push_back(nonce);
6866
}
6967

7068
pub fn add_from_response<T>(&self, res: &http::Response<T>) {
7169
if let Some(nonce) = try_get_header(res.headers(), &REPLAY_NONCE) {
72-
self.0.borrow_mut().push_back(nonce.to_string());
70+
self.add(nonce.to_string());
7371
}
7472
}
7573
}
@@ -130,51 +128,6 @@ where
130128
.map(String::from)
131129
}
132130

133-
pub async fn poll<T, P, F>(
134-
&self,
135-
url: &Uri,
136-
payload: P,
137-
mut tries: usize,
138-
predicate: F,
139-
) -> Result<T>
140-
where
141-
T: serde::de::DeserializeOwned,
142-
P: AsRef<[u8]>,
143-
F: Fn(&T) -> bool,
144-
{
145-
loop {
146-
let res = self.post(url, &payload).await?;
147-
148-
let retry_after = res
149-
.headers()
150-
.get(http::header::RETRY_AFTER)
151-
.and_then(parse_retry_after)
152-
.unwrap_or(DEFAULT_RETRY_INTERVAL);
153-
154-
let result = serde_json::from_slice(res.body())?;
155-
156-
if predicate(&result) {
157-
return Ok(result);
158-
}
159-
160-
tries -= 1;
161-
if tries == 0 {
162-
return Err(io::Error::from(io::ErrorKind::TimedOut).into());
163-
}
164-
165-
sleep(retry_after).await;
166-
}
167-
}
168-
169-
pub async fn post_json<T: serde::de::DeserializeOwned, P: AsRef<[u8]>>(
170-
&self,
171-
url: &Uri,
172-
payload: P,
173-
) -> Result<T> {
174-
let res = self.post(url, payload).await?;
175-
Ok(serde_json::from_slice(res.body())?)
176-
}
177-
178131
pub async fn get(&self, url: &Uri) -> Result<http::Response<Bytes>> {
179132
let req = http::Request::builder()
180133
.uri(url)
@@ -238,12 +191,6 @@ where
238191
.ok_or(anyhow!("no nonce in response"))?
239192
.to_string();
240193

241-
let retry_after = res
242-
.headers()
243-
.get(http::header::RETRY_AFTER)
244-
.and_then(parse_retry_after)
245-
.unwrap_or(DEFAULT_RETRY_INTERVAL);
246-
247194
let err: types::Problem = serde_json::from_slice(res.body())?;
248195

249196
let retriable = matches!(
@@ -257,7 +204,8 @@ where
257204
}
258205

259206
fails += 1;
260-
sleep(retry_after).await;
207+
208+
wait_for_retry(&res).await;
261209
ngx_log_debug!(self.log.as_ptr(), "retrying: {} of 3", fails + 1);
262210
};
263211

@@ -339,7 +287,8 @@ where
339287

340288
let mut authorizations: Vec<(http::Uri, types::Authorization)> = Vec::new();
341289
for auth_url in order.authorizations {
342-
let mut authorization: types::Authorization = self.post_json(&auth_url, &[]).await?;
290+
let res = self.post(&auth_url, b"").await?;
291+
let mut authorization: types::Authorization = serde_json::from_slice(res.body())?;
343292

344293
authorization
345294
.challenges
@@ -379,8 +328,8 @@ where
379328
self.do_authorization(&order, url, authorization).await?;
380329
}
381330

382-
let mut bytes = self.post(&order_url, &[]).await?.into_body();
383-
let mut order: types::Order = serde_json::from_slice(&bytes)?;
331+
let mut res = self.post(&order_url, b"").await?;
332+
let mut order: types::Order = serde_json::from_slice(res.body())?;
384333

385334
if order.status != OrderStatus::Ready {
386335
anyhow::bail!("not ready");
@@ -390,10 +339,10 @@ where
390339
let payload = std::format!(r#"{{"csr":"{}"}}"#, crate::jws::base64url(csr));
391340

392341
match self.post(&order.finalize, payload).await {
393-
Ok(res) => {
342+
Ok(x) => {
394343
drop(order);
395-
bytes = res.into_body();
396-
order = serde_json::from_slice(&bytes)?;
344+
res = x;
345+
order = serde_json::from_slice(res.body())?;
397346
}
398347
Err(err) => {
399348
if !err.to_string().contains("orderNotReady") {
@@ -403,25 +352,20 @@ where
403352
}
404353
};
405354

406-
let mut retry_after = DEFAULT_RETRY_INTERVAL;
355+
let mut tries = 10;
407356

408-
while order.status == OrderStatus::Processing {
409-
sleep(retry_after).await;
357+
while order.status == OrderStatus::Processing && tries > 0 {
358+
tries -= 1;
359+
wait_for_retry(&res).await;
410360

411-
let res = self.post(&order_url, &[]).await?;
412-
retry_after = res
413-
.headers()
414-
.get(http::header::RETRY_AFTER)
415-
.and_then(parse_retry_after)
416-
.unwrap_or(DEFAULT_RETRY_INTERVAL);
417361
drop(order);
418-
bytes = res.into_body();
419-
order = serde_json::from_slice(&bytes)?;
362+
res = self.post(&order_url, b"").await?;
363+
order = serde_json::from_slice(res.body())?;
420364
}
421365

422-
let certificate = order.certificate.ok_or(anyhow!("certifcate not ready"))?;
366+
let certificate = order.certificate.ok_or(anyhow!("certificate not ready"))?;
423367

424-
let chain = self.post(&certificate, &[]).await?.into_body();
368+
let chain = self.post(&certificate, b"").await?.into_body();
425369

426370
// FIXME: avoid reallocation from std::vec::Vec.
427371
let x509 = Vec::from_iter(X509::stack_from_pem(&chain)?);
@@ -453,8 +397,19 @@ where
453397

454398
result?;
455399

456-
let is_ready = |x: &types::Authorization| x.status != AuthorizationStatus::Pending;
457-
let result = self.poll(&url, b"", 5, is_ready).await?;
400+
let mut tries = 10;
401+
402+
let result = loop {
403+
let res = self.post(&url, b"").await?;
404+
let result: types::Authorization = serde_json::from_slice(res.body())?;
405+
406+
if result.status != AuthorizationStatus::Pending || tries == 0 {
407+
break result;
408+
}
409+
410+
tries -= 1;
411+
wait_for_retry(&res).await;
412+
};
458413

459414
ngx_log_debug!(
460415
self.log.as_ptr(),
@@ -476,14 +431,9 @@ where
476431
identifier: &Identifier<&str>,
477432
challenge: &types::Challenge,
478433
) -> Result<()> {
479-
fn is_ready(x: &types::Challenge) -> bool {
480-
!matches!(
481-
x.status,
482-
ChallengeStatus::Pending | ChallengeStatus::Processing,
483-
)
484-
}
434+
let res = self.post(&challenge.url, b"").await?;
435+
let result: types::Challenge = serde_json::from_slice(res.body())?;
485436

486-
let result: types::Challenge = self.post_json(&challenge.url, b"").await?;
487437
// Previous challenge result is still valid.
488438
// Should not happen as we already skip valid authorizations.
489439
if result.status == ChallengeStatus::Valid {
@@ -494,11 +444,25 @@ where
494444
.ok_or(anyhow!("no solver for {:?}", challenge.kind))?
495445
.register(ctx, identifier, challenge)?;
496446

497-
let result: types::Challenge = self.post_json(&challenge.url, b"{}").await?;
498-
let result = if is_ready(&result) {
499-
result
500-
} else {
501-
self.poll(&challenge.url, b"", 10, is_ready).await?
447+
// "{}" in request payload initiates the challenge, "" checks the status.
448+
let mut payload: &[u8] = b"{}";
449+
let mut tries = 10;
450+
451+
let result = loop {
452+
let res = self.post(&challenge.url, payload).await?;
453+
let result: types::Challenge = serde_json::from_slice(res.body())?;
454+
455+
if !matches!(
456+
result.status,
457+
ChallengeStatus::Pending | ChallengeStatus::Processing,
458+
) || tries == 0
459+
{
460+
break result;
461+
}
462+
463+
tries -= 1;
464+
payload = b"";
465+
wait_for_retry(&res).await;
502466
};
503467

504468
if result.status != ChallengeStatus::Valid {
@@ -547,7 +511,17 @@ pub fn make_certificate_request(
547511
Ok(req.build())
548512
}
549513

550-
pub fn parse_retry_after(val: &http::HeaderValue) -> Option<Duration> {
514+
/// Waits until the next retry attempt is allowed.
515+
async fn wait_for_retry<B>(res: &http::Response<B>) {
516+
let retry_after = res
517+
.headers()
518+
.get(http::header::RETRY_AFTER)
519+
.and_then(parse_retry_after)
520+
.unwrap_or(DEFAULT_RETRY_INTERVAL);
521+
sleep(retry_after).await
522+
}
523+
524+
fn parse_retry_after(val: &http::HeaderValue) -> Option<Duration> {
551525
let val = val.to_str().ok()?;
552526

553527
// Retry-After: <http-date>

0 commit comments

Comments
 (0)