diff --git a/third_party/crypto/acme/acme.go b/third_party/crypto/acme/acme.go index cad8161a2..4578ee31c 100644 --- a/third_party/crypto/acme/acme.go +++ b/third_party/crypto/acme/acme.go @@ -394,7 +394,8 @@ func (c *Client) DeactivateAuthorization(ctx context.Context, url string) error // WaitAuthorization retrieves authorization details. If the authorization is not in // a final state (StatusValid/StatusInvalid), it retries the request until the authorization -// is final, ctx is cancelled by the caller, or an error response is received. +// is final, ctx is cancelled by the caller, an error response is received, or the ACME CA +// responded with a 4xx error. // // It returns a non-nil Authorization only if its Status is StatusValid. // In all other cases WaitAuthorization returns an error. @@ -411,6 +412,12 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat res.Body.Close() return nil, err } + if res.StatusCode >= 400 && res.StatusCode <= 499 { + // Non-retriable error. For instance, Let's Encrypt may return 404 Not Found + // when requesting an expired authorization. + defer res.Body.Close() + return nil, responseError(res) + } var raw wireAuthz err = json.NewDecoder(res.Body).Decode(&raw) res.Body.Close() diff --git a/third_party/crypto/acme/acme_test.go b/third_party/crypto/acme/acme_test.go index a44efc69c..59f369cf2 100644 --- a/third_party/crypto/acme/acme_test.go +++ b/third_party/crypto/acme/acme_test.go @@ -461,6 +461,34 @@ func TestWaitAuthorizationInvalid(t *testing.T) { } } +func TestWaitAuthorizationClientError(t *testing.T) { + const code = http.StatusBadRequest + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(code) + })) + defer ts.Close() + + ch := make(chan error, 1) + go func() { + var client Client + _, err := client.WaitAuthorization(context.Background(), ts.URL) + ch <- err + }() + + select { + case <-time.After(3 * time.Second): + t.Fatal("WaitAuthz took too long to return") + case err := <-ch: + res, ok := err.(*Error) + if !ok { + t.Fatalf("err is %v (%T); want a non-nil *Error", err, err) + } + if res.StatusCode != code { + t.Errorf("res.StatusCode = %d; want %d", res.StatusCode, code) + } + } +} + func TestWaitAuthorizationCancel(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Retry-After", "60")