diff --git a/test/integration/ari_test.go b/test/integration/ari_test.go index 202b38b69ec..05f533319a8 100644 --- a/test/integration/ari_test.go +++ b/test/integration/ari_test.go @@ -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. @@ -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) { diff --git a/wfe2/wfe.go b/wfe2/wfe.go index a51117e06ca..3e4fe762b69 100644 --- a/wfe2/wfe.go +++ b/wfe2/wfe.go @@ -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) @@ -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 { + factor := 0.2 * (2*rand.Float64() - 1) + jittered := int(float64(base) * (1 + factor)) + + if jittered < 0 { + return createJitter(base) + } + + return jittered +} diff --git a/wfe2/wfe_test.go b/wfe2/wfe_test.go index 4715d7f2262..b39e7bef355 100644 --- a/wfe2/wfe_test.go +++ b/wfe2/wfe_test.go @@ -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") @@ -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") @@ -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)