diff --git a/acme/api/handler.go b/acme/api/handler.go index 33782a68..0aab42f9 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -183,7 +183,7 @@ func (h *Handler) GetChallenge(w http.ResponseWriter, r *http.Request) { } else if ch.Retry.Active { retryAfter := int(ch.Retry.Backoffs) * (10 - ch.Retry.Called) w.Header().Add("Retry-After", string(retryAfter)) - api.JSON(w, ch) + api.WriteProcessing(w, ch) } else { getLink := h.Auth.GetLink w.Header().Add("Link", link(getLink(acme.AuthzLink, acme.URLSafeProvisionerName(prov), true, ch.GetAuthzID()), "up")) diff --git a/acme/api/handler_test.go b/acme/api/handler_test.go index 92859611..67c4f366 100644 --- a/acme/api/handler_test.go +++ b/acme/api/handler_test.go @@ -792,17 +792,19 @@ func TestHandlerGetChallenge(t *testing.T) { assert.Equals(t, ae.Identifier, prob.Identifier) assert.Equals(t, ae.Subproblems, prob.Subproblems) assert.Equals(t, res.Header["Content-Type"], []string{"application/problem+json"}) - } else if res.StatusCode >= 200 && assert.True(t,res.Header["Retry-After"] == nil){ + } else if res.StatusCode >= 200 { expB, err := json.Marshal(tc.ch) assert.FatalError(t, err) assert.Equals(t, bytes.TrimSpace(body), expB) assert.Equals(t, res.Header["Link"], []string{fmt.Sprintf(";rel=\"up\"", tc.ch.AuthzID)}) assert.Equals(t, res.Header["Location"], []string{url}) assert.Equals(t, res.Header["Content-Type"], []string{"application/json"}) - } else { + } else if res.StatusCode >= 100 { expB, err := json.Marshal(tc.ch) assert.FatalError(t, err) assert.Equals(t, bytes.TrimSpace(body), expB) + assert.True(t, res.Header["Retry-After"] != nil) + assert.Equals(t, res.Header["Content-Type"], []string{"application/json"}) } }) } diff --git a/acme/authority.go b/acme/authority.go index 2ebf8e49..ff387892 100644 --- a/acme/authority.go +++ b/acme/authority.go @@ -278,7 +278,7 @@ func (a *Authority) ValidateChallenge(p provisioner.Interface, accID, chID strin dialer := &net.Dialer{ Timeout: 30 * time.Second, } - for i:=0; i < 10; i++ { + for ch.getRetry().Active { ch, err = ch.validate(a.db, jwk, validateOptions{ httpGet: client.Get, lookupTxt: net.LookupTXT, diff --git a/acme/challenge.go b/acme/challenge.go index 8f000277..00a7b9cc 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -261,11 +261,6 @@ func (bc *baseChallenge) storeError(db nosql.DB, err *Error) error { return clone.save(db, bc) } -func (bc *baseChallenge) storeRetry(db nosql.DB, retry *Retry) error { - clone := bc.clone() - return clone.save(db, bc) -} - // unmarshalChallenge unmarshals a challenge type into the correct sub-type. func unmarshalChallenge(data []byte) (challenge, error) { var getType struct { @@ -376,18 +371,7 @@ func (hc *http01Challenge) validate(db nosql.DB, jwk *jose.JSONWebKey, vo valida if keyAuth != expected { rejectedErr := RejectedIdentifierErr(errors.Errorf("keyAuthorization does not match; "+ "expected %s, but got %s", expected, keyAuth)) - upd := hc.clone() - upd.Error = rejectedErr.ToACME() - upd.Error.Subproblems = append(upd.Error.Subproblems, rejectedErr) - upd.Retry.Called ++ - upd.Retry.Active = true - if upd.Retry.Called >= 10 { - upd.Status = StatusInvalid - upd.Retry.Backoffs *= 2 - upd.Retry.Active = false - upd.Retry.Called = 0 - } - if err = upd.save(db, hc); err != nil { + if err = hc.updateRetry(db, rejectedErr); err != nil { return nil, err } return hc, err @@ -609,18 +593,7 @@ func (dc *dns01Challenge) validate(db nosql.DB, jwk *jose.JSONWebKey, vo validat if err != nil { dnsErr := DNSErr(errors.Wrapf(err, "error looking up TXT "+ "records for domain %s", domain)) - upd := dc.clone() - upd.Error = dnsErr.ToACME() - upd.Error.Subproblems = append(upd.Error.Subproblems, dnsErr) - upd.Retry.Called ++ - upd.Retry.Active = true - if upd.Retry.Called >= 10 { - upd.Status = StatusInvalid - upd.Retry.Backoffs *= 2 - upd.Retry.Active = false - upd.Retry.Called = 0 - } - if err = upd.save(db, dc); err != nil { + if err = dc.updateRetry(db, dnsErr); err != nil { return nil, err } return dc, nil @@ -677,3 +650,22 @@ func getChallenge(db nosql.DB, id string) (challenge, error) { } return ch, nil } + +// updateRetry updates a challenge's retry and error objects upon a failed validation attempt +func (bc *baseChallenge) updateRetry(db nosql.DB, error *Error) error { + upd := bc.clone() + upd.Error = error.ToACME() + upd.Error.Subproblems = append(upd.Error.Subproblems, error) + upd.Retry.Called ++ + upd.Retry.Active = true + if upd.Retry.Called >= 10 { + upd.Status = StatusInvalid + upd.Retry.Backoffs *= 2 + upd.Retry.Active = false + upd.Retry.Called = 0 + } + if err := upd.save(db, bc); err != nil { + return err + } + return nil +} diff --git a/api/utils.go b/api/utils.go index 0d87a065..154023d0 100644 --- a/api/utils.go +++ b/api/utils.go @@ -52,6 +52,11 @@ func JSON(w http.ResponseWriter, v interface{}) { JSONStatus(w, v, http.StatusOK) } +// JSON writes the passed value into the http.ResponseWriter. +func WriteProcessing(w http.ResponseWriter, v interface{}) { + JSONStatus(w, v, http.StatusProcessing) +} + // JSONStatus writes the given value into the http.ResponseWriter and the // given status is written as the status code of the response. func JSONStatus(w http.ResponseWriter, v interface{}, status int) {