Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions test/integration/ari_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ func TestARIAndReplacement(t *testing.T) {
test.AssertNotError(t, err, "ARI request should have succeeded")
test.AssertEquals(t, ari.SuggestedWindow.Start.Sub(time.Now()).Round(time.Hour), 1418*time.Hour)
test.AssertEquals(t, ari.SuggestedWindow.End.Sub(time.Now()).Round(time.Hour), 1461*time.Hour)
test.AssertEquals(t, ari.RetryAfter.Sub(time.Now()).Round(time.Hour), 6*time.Hour)
lowerRetryAfterLimit := 17280 * time.Second
upperRetryAfterLimit := 25920 * time.Second
retryAfter := ari.RetryAfter.Sub(time.Now()).Round(time.Second)
test.Assert(t, retryAfter >= lowerRetryAfterLimit && retryAfter <= upperRetryAfterLimit, "retry after amount is not within expected range")

// Make a new order which indicates that it replaces the cert issued above,
// and verify that the replacement order succeeds.
Expand Down Expand Up @@ -94,7 +97,10 @@ func TestARIShortLived(t *testing.T) {
test.AssertNotError(t, err, "ARI request should have succeeded")
test.AssertEquals(t, ari.SuggestedWindow.Start.Sub(time.Now()).Round(time.Hour), 78*time.Hour)
test.AssertEquals(t, ari.SuggestedWindow.End.Sub(time.Now()).Round(time.Hour), 81*time.Hour)
test.AssertEquals(t, ari.RetryAfter.Sub(time.Now()).Round(time.Hour), 6*time.Hour)
lowerRetryAfterLimit := 17280 * time.Second
upperRetryAfterLimit := 25920 * time.Second
retryAfter := ari.RetryAfter.Sub(time.Now()).Round(time.Second)
test.Assert(t, retryAfter >= lowerRetryAfterLimit && retryAfter <= upperRetryAfterLimit, "retry after amount is not within expected range")
}

func TestARIRevoked(t *testing.T) {
Expand Down
17 changes: 16 additions & 1 deletion wfe2/wfe.go
Original file line number Diff line number Diff line change
Expand Up @@ -2726,7 +2726,8 @@ func (wfe *WebFrontEndImpl) RenewalInfo(ctx context.Context, logEvent *web.Reque
return
}

response.Header().Set(headerRetryAfter, fmt.Sprintf("%d", int(6*time.Hour/time.Second)))
retryAfter := createJitter(int(6 * time.Hour / time.Second))
response.Header().Set(headerRetryAfter, fmt.Sprintf("%d", retryAfter))
err = wfe.writeJsonResponse(response, logEvent, http.StatusOK, renewalInfo)
if err != nil {
wfe.sendError(response, logEvent, probs.ServerInternal("Error marshalling renewalInfo"), err)
Expand All @@ -2737,3 +2738,17 @@ func (wfe *WebFrontEndImpl) RenewalInfo(ctx context.Context, logEvent *web.Reque
func urlForAuthz(authz core.Authorization, request *http.Request) string {
return web.RelativeEndpoint(request, fmt.Sprintf("%s%d/%s", authzPath, authz.RegistrationID, authz.ID))
}

// createJitter will return a random integer within a 20% window of the base that is provided.
// If the jittered amount is a negative number, the jitter is retried until a positive
// number is generated
func createJitter(base int) int {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two minor comments:

  1. Since this is a hyper-specific helper function, it might be cleaner for this function to take a time.Duration as input and return a string-formatted integer. Then it could simply be called like
response.Header().Set(headerRetryAfter, jitterHeader(6 * time.Hour))
  1. The check for negative numbers is a bug. Even if the input is just 1, it should be impossible for this function to return anything less than 0. And if the input is negative, this will loop infinitely. I'd remove the check.

factor := 0.2 * (2*rand.Float64() - 1)
jittered := int(float64(base) * (1 + factor))

if jittered < 0 {
Copy link

@nmxhc nmxhc Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

I think that this condition should never be met for positive inputs since then jittered>base*0.8>0.
For negative inputs however, this creates an infinite recursion (up to rounding errors for bases close to 0) since jittered<base*0.8<0.

From my understanding of the context, I suggest to remove this branch and adjust the comment.

return createJitter(base)
}

return jittered
}
14 changes: 10 additions & 4 deletions wfe2/wfe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3862,7 +3862,9 @@ func TestARI(t *testing.T) {
resp := httptest.NewRecorder()
wfe.RenewalInfo(context.Background(), event, resp, req)
test.AssertEquals(t, resp.Code, http.StatusOK)
test.AssertEquals(t, resp.Header().Get("Retry-After"), "21600")
retryAfter, err := strconv.Atoi(resp.Header().Get("Retry-After"))
test.AssertNotError(t, err, "failed to convert retry after header to int")
test.Assert(t, retryAfter >= 17280 && retryAfter <= 25920, "retry after amount is not within expected range")
var ri core.RenewalInfo
err = json.Unmarshal(resp.Body.Bytes(), &ri)
test.AssertNotError(t, err, "unmarshalling renewal info")
Expand All @@ -3876,7 +3878,9 @@ func TestARI(t *testing.T) {
resp = httptest.NewRecorder()
wfe.RenewalInfo(context.Background(), event, resp, req)
test.AssertEquals(t, resp.Code, http.StatusOK)
test.AssertEquals(t, resp.Header().Get("Retry-After"), "21600")
retryAfter, err = strconv.Atoi(resp.Header().Get("Retry-After"))
test.AssertNotError(t, err, "failed to convert retry after header to int")
test.Assert(t, retryAfter >= 17280 && retryAfter <= 25920, "retry after amount is not within expected range")
err = json.Unmarshal(resp.Body.Bytes(), &ri)
test.AssertNotError(t, err, "unmarshalling renewal info")
test.Assert(t, ri.SuggestedWindow.End.Before(wfe.clk.Now()), "suggested window should end in the past")
Expand Down Expand Up @@ -3942,9 +3946,11 @@ func TestIncidentARI(t *testing.T) {
resp := httptest.NewRecorder()
wfe.RenewalInfo(context.Background(), event, resp, req)
test.AssertEquals(t, resp.Code, 200)
test.AssertEquals(t, resp.Header().Get("Retry-After"), "21600")
retryAfter, err := strconv.Atoi(resp.Header().Get("Retry-After"))
test.AssertNotError(t, err, "failed to convert retry after header to int")
test.Assert(t, retryAfter >= 17280 && retryAfter <= 25920, "retry after amount is not within expected range")
var ri core.RenewalInfo
err := json.Unmarshal(resp.Body.Bytes(), &ri)
err = json.Unmarshal(resp.Body.Bytes(), &ri)
test.AssertNotError(t, err, "unmarshalling renewal info")
// The start of the window should be in the past.
test.AssertEquals(t, ri.SuggestedWindow.Start.Before(wfe.clk.Now()), true)
Expand Down
Loading