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 {