diff --git a/acme/client.go b/acme/client.go index d8b2187e..f9857ebd 100644 --- a/acme/client.go +++ b/acme/client.go @@ -665,7 +665,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error) go func(authzURL string) { var authz authorization - _, err := getJSON(authzURL, &authz) + _, err := postAsGet(c.jws, authzURL, &authz) if err != nil { errc <- domainError{Domain: authz.Identifier.Value, Error: err} return @@ -789,7 +789,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr case <-stopTimer.C: return nil, errors.New("certificate polling timed out") case <-retryTick.C: - _, err := getJSON(order.URL, &retOrder) + _, err := postAsGet(c.jws, order.URL, &retOrder) if err != nil { return nil, err } @@ -813,7 +813,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResource, bundle bool) (bool, error) { switch order.Status { case statusValid: - resp, err := httpGet(order.Certificate) + resp, err := postAsGet(c.jws, order.Certificate, nil) if err != nil { return false, err } @@ -871,7 +871,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou // getIssuerCertificate requests the issuer certificate func (c *Client) getIssuerCertificate(url string) ([]byte, error) { log.Infof("acme: Requesting issuer cert from %s", url) - resp, err := httpGet(url) + resp, err := postAsGet(c.jws, url, nil) if err != nil { return nil, err } @@ -943,11 +943,15 @@ func validate(j *jws, domain, uri string, c challenge) error { // If it doesn't, we'll just poll hard. ra = 5 } + time.Sleep(time.Duration(ra) * time.Second) - hdr, err = getJSON(uri, &chlng) + resp, err := postAsGet(j, uri, &chlng) if err != nil { return err } + if resp != nil { + hdr = resp.Header + } } } diff --git a/acme/client_test.go b/acme/client_test.go index 51b57596..bb596447 100644 --- a/acme/client_test.go +++ b/acme/client_test.go @@ -155,10 +155,10 @@ func TestValidate(t *testing.T) { privKey, err := rsa.GenerateKey(rand.Reader, 512) require.NoError(t, err) - // validateNoBody reads the http.Request POST body, parses the JWS and - // validates it to read the body. If there is an error doing this, or if the - // JWS body is not the empty JSON payload "{}" an error is returned. We use - // this to verify challenge POSTs to the ts below do not send a JWS body. + // validateNoBody reads the http.Request POST body, parses the JWS and validates it to read the body. + // If there is an error doing this, + // or if the JWS body is not the empty JSON payload "{}" or a POST-as-GET payload "" an error is returned. + // We use this to verify challenge POSTs to the ts below do not send a JWS body. validateNoBody := func(r *http.Request) error { reqBody, err := ioutil.ReadAll(r.Body) if err != nil { @@ -178,8 +178,8 @@ func TestValidate(t *testing.T) { return err } - if bodyStr := string(body); bodyStr != "{}" { - return fmt.Errorf(`expected JWS POST body "{}", got %q`, bodyStr) + if bodyStr := string(body); bodyStr != "{}" && bodyStr != "" { + return fmt.Errorf(`expected JWS POST body "{}" or "", got %q`, bodyStr) } return nil } diff --git a/acme/http.go b/acme/http.go index 2ddbc0f1..8a8bf6db 100644 --- a/acme/http.go +++ b/acme/http.go @@ -151,48 +151,56 @@ func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, e return nil, errors.New("failed to marshal network message") } - resp, err := j.post(uri, jsonBytes) - if err != nil { - return nil, fmt.Errorf("failed to post JWS message. -> %v", err) + resp, err := post(j, uri, jsonBytes, respBody) + if resp == nil { + return nil, err } defer resp.Body.Close() + return resp.Header, err +} + +func postAsGet(j *jws, uri string, respBody interface{}) (*http.Response, error) { + return post(j, uri, []byte{}, respBody) +} + +func post(j *jws, uri string, reqBody []byte, respBody interface{}) (*http.Response, error) { + resp, err := j.post(uri, reqBody) + if err != nil { + return nil, fmt.Errorf("failed to post JWS message. -> %v", err) + } + if resp.StatusCode >= http.StatusBadRequest { err = handleHTTPError(resp) switch err.(type) { case NonceError: // Retry once if the nonce was invalidated - retryResp, errP := j.post(uri, jsonBytes) + retryResp, errP := j.post(uri, reqBody) if errP != nil { return nil, fmt.Errorf("failed to post JWS message. -> %v", errP) } - defer retryResp.Body.Close() - if retryResp.StatusCode >= http.StatusBadRequest { - return retryResp.Header, handleHTTPError(retryResp) + return retryResp, handleHTTPError(retryResp) } if respBody == nil { - return retryResp.Header, nil + return retryResp, nil } - return retryResp.Header, json.NewDecoder(retryResp.Body).Decode(respBody) - + return retryResp, json.NewDecoder(retryResp.Body).Decode(respBody) default: - return resp.Header, err - + return resp, err } - } if respBody == nil { - return resp.Header, nil + return resp, nil } - return resp.Header, json.NewDecoder(resp.Body).Decode(respBody) + return resp, json.NewDecoder(resp.Body).Decode(respBody) } // userAgent builds and returns the User-Agent string to use in requests.