From bb768b3e268d67c0c0fd2e97c459bcba42f6ce30 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Thu, 24 Apr 2025 17:31:51 +0200 Subject: [PATCH] MEDIUM: acme: use Retry-After value for retries Parse the Retry-After header in response and store it in order to use the value as the next delay for the next retry, fallback to 3s if the value couldn't be parse or does not exist. --- include/haproxy/acme-t.h | 1 + src/acme.c | 42 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/include/haproxy/acme-t.h b/include/haproxy/acme-t.h index 585728ef0..468962417 100644 --- a/include/haproxy/acme-t.h +++ b/include/haproxy/acme-t.h @@ -61,6 +61,7 @@ struct acme_ctx { enum acme_st state; enum http_st http_state; int retries; + int retryafter; struct httpclient *hc; struct acme_cfg *cfg; struct ckch_store *store; diff --git a/src/acme.c b/src/acme.c index 3a838320d..2debfdb4e 100644 --- a/src/acme.c +++ b/src/acme.c @@ -720,6 +720,10 @@ int acme_res_certificate(struct task *task, struct acme_ctx *ctx, char **errmsg) istfree(&ctx->nonce); ctx->nonce = istdup(hdr->v); } + /* get the next retry timing */ + if (isteqi(hdr->n, ist("Retry-After"))) { + ctx->retryafter = atol(hdr->v.ptr); + } } if (hc->res.status < 200 || hc->res.status >= 300) { @@ -787,6 +791,10 @@ int acme_res_chkorder(struct task *task, struct acme_ctx *ctx, char **errmsg) istfree(&ctx->nonce); ctx->nonce = istdup(hdr->v); } + /* get the next retry timing */ + if (isteqi(hdr->n, ist("Retry-After"))) { + ctx->retryafter = atol(hdr->v.ptr); + } } if (hc->res.status < 200 || hc->res.status >= 300) { @@ -912,6 +920,10 @@ int acme_res_finalize(struct task *task, struct acme_ctx *ctx, char **errmsg) istfree(&ctx->nonce); ctx->nonce = istdup(hdr->v); } + /* get the next retry timing */ + if (isteqi(hdr->n, ist("Retry-After"))) { + ctx->retryafter = atol(hdr->v.ptr); + } } if (hc->res.status < 200 || hc->res.status >= 300) { @@ -999,6 +1011,10 @@ int acme_res_challenge(struct task *task, struct acme_ctx *ctx, struct acme_auth istfree(&ctx->nonce); ctx->nonce = istdup(hdr->v); } + /* get the next retry timing */ + if (isteqi(hdr->n, ist("Retry-After"))) { + ctx->retryafter = atol(hdr->v.ptr); + } } if (hc->res.status < 200 || hc->res.status >= 300 || mjson_find(hc->res.buf.area, hc->res.buf.data, "$.error", NULL, NULL) == MJSON_TOK_OBJECT) { @@ -1087,6 +1103,11 @@ int acme_res_auth(struct task *task, struct acme_ctx *ctx, struct acme_auth *aut istfree(&ctx->nonce); ctx->nonce = istdup(hdr->v); } + /* get the next retry timing */ + if (isteqi(hdr->n, ist("Retry-After"))) { + ctx->retryafter = atol(hdr->v.ptr); + } + } if (hc->res.status < 200 || hc->res.status >= 300) { @@ -1238,6 +1259,10 @@ int acme_res_neworder(struct task *task, struct acme_ctx *ctx, char **errmsg) istfree(&ctx->nonce); ctx->nonce = istdup(hdr->v); } + /* get the next retry timing */ + if (isteqi(hdr->n, ist("Retry-After"))) { + ctx->retryafter = atol(hdr->v.ptr); + } /* get the order URL */ if (isteqi(hdr->n, ist("Location"))) { istfree(&ctx->order); @@ -1386,6 +1411,10 @@ int acme_res_account(struct task *task, struct acme_ctx *ctx, int newaccount, ch istfree(&ctx->kid); ctx->kid = istdup(hdr->v); } + /* get the next retry timing */ + if (isteqi(hdr->n, ist("Retry-After"))) { + ctx->retryafter = atol(hdr->v.ptr); + } if (isteqi(hdr->n, ist("Replay-Nonce"))) { istfree(&ctx->nonce); ctx->nonce = istdup(hdr->v); @@ -1742,9 +1771,16 @@ retry: int delay = 1; int i; - for (i = 0; i < ACME_RETRY - ctx->retries; i++) - delay *= 3; - send_log(NULL, LOG_NOTICE, "acme: %s: %s, retrying in %ds (%d/%d)...\n", ctx->store->path, errmsg ? errmsg : "", delay, ACME_RETRY - ctx->retries, ACME_RETRY); + if (ctx->retryafter > 0) { + /* Use the Retry-After value from the header */ + delay = ctx->retryafter; + ctx->retryafter = 0; + } else { + /* else does an exponential backoff * 3 */ + for (i = 0; i < ACME_RETRY - ctx->retries; i++) + delay *= 3; + } + send_log(NULL, LOG_NOTICE, "acme: %s: %s, retrying in %ds (%d/%d retries)...\n", ctx->store->path, errmsg ? errmsg : "", delay, ACME_RETRY - ctx->retries, ACME_RETRY); task->expire = tick_add(now_ms, delay * 1000); } else {