Support POST-as-GET. (#695)

This commit is contained in:
Ludovic Fernandez 2018-11-04 01:51:53 +01:00 committed by GitHub
parent 5992cc0ece
commit 4f36f4354b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 26 deletions

View file

@ -665,7 +665,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error)
go func(authzURL string) { go func(authzURL string) {
var authz authorization var authz authorization
_, err := getJSON(authzURL, &authz) _, err := postAsGet(c.jws, authzURL, &authz)
if err != nil { if err != nil {
errc <- domainError{Domain: authz.Identifier.Value, Error: err} errc <- domainError{Domain: authz.Identifier.Value, Error: err}
return return
@ -789,7 +789,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
case <-stopTimer.C: case <-stopTimer.C:
return nil, errors.New("certificate polling timed out") return nil, errors.New("certificate polling timed out")
case <-retryTick.C: case <-retryTick.C:
_, err := getJSON(order.URL, &retOrder) _, err := postAsGet(c.jws, order.URL, &retOrder)
if err != nil { if err != nil {
return nil, err 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) { func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResource, bundle bool) (bool, error) {
switch order.Status { switch order.Status {
case statusValid: case statusValid:
resp, err := httpGet(order.Certificate) resp, err := postAsGet(c.jws, order.Certificate, nil)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -871,7 +871,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
// getIssuerCertificate requests the issuer certificate // getIssuerCertificate requests the issuer certificate
func (c *Client) getIssuerCertificate(url string) ([]byte, error) { func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
log.Infof("acme: Requesting issuer cert from %s", url) log.Infof("acme: Requesting issuer cert from %s", url)
resp, err := httpGet(url) resp, err := postAsGet(c.jws, url, nil)
if err != nil { if err != nil {
return nil, err 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. // If it doesn't, we'll just poll hard.
ra = 5 ra = 5
} }
time.Sleep(time.Duration(ra) * time.Second) time.Sleep(time.Duration(ra) * time.Second)
hdr, err = getJSON(uri, &chlng) resp, err := postAsGet(j, uri, &chlng)
if err != nil { if err != nil {
return err return err
} }
if resp != nil {
hdr = resp.Header
}
} }
} }

View file

@ -155,10 +155,10 @@ func TestValidate(t *testing.T) {
privKey, err := rsa.GenerateKey(rand.Reader, 512) privKey, err := rsa.GenerateKey(rand.Reader, 512)
require.NoError(t, err) require.NoError(t, err)
// validateNoBody reads the http.Request POST body, parses the JWS and // validateNoBody reads the http.Request POST body, parses the JWS and validates it to read the body.
// validates it to read the body. If there is an error doing this, or if the // If there is an error doing this,
// JWS body is not the empty JSON payload "{}" an error is returned. We use // or if the JWS body is not the empty JSON payload "{}" or a POST-as-GET payload "" an error is returned.
// this to verify challenge POSTs to the ts below do not send a JWS body. // We use this to verify challenge POSTs to the ts below do not send a JWS body.
validateNoBody := func(r *http.Request) error { validateNoBody := func(r *http.Request) error {
reqBody, err := ioutil.ReadAll(r.Body) reqBody, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
@ -178,8 +178,8 @@ func TestValidate(t *testing.T) {
return err return err
} }
if bodyStr := string(body); bodyStr != "{}" { if bodyStr := string(body); bodyStr != "{}" && bodyStr != "" {
return fmt.Errorf(`expected JWS POST body "{}", got %q`, bodyStr) return fmt.Errorf(`expected JWS POST body "{}" or "", got %q`, bodyStr)
} }
return nil return nil
} }

View file

@ -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") return nil, errors.New("failed to marshal network message")
} }
resp, err := j.post(uri, jsonBytes) resp, err := post(j, uri, jsonBytes, respBody)
if err != nil { if resp == nil {
return nil, fmt.Errorf("failed to post JWS message. -> %v", err) return nil, err
} }
defer resp.Body.Close() 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 { if resp.StatusCode >= http.StatusBadRequest {
err = handleHTTPError(resp) err = handleHTTPError(resp)
switch err.(type) { switch err.(type) {
case NonceError: case NonceError:
// Retry once if the nonce was invalidated // Retry once if the nonce was invalidated
retryResp, errP := j.post(uri, jsonBytes) retryResp, errP := j.post(uri, reqBody)
if errP != nil { if errP != nil {
return nil, fmt.Errorf("failed to post JWS message. -> %v", errP) return nil, fmt.Errorf("failed to post JWS message. -> %v", errP)
} }
defer retryResp.Body.Close()
if retryResp.StatusCode >= http.StatusBadRequest { if retryResp.StatusCode >= http.StatusBadRequest {
return retryResp.Header, handleHTTPError(retryResp) return retryResp, handleHTTPError(retryResp)
} }
if respBody == nil { 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: default:
return resp.Header, err return resp, err
} }
} }
if respBody == nil { 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. // userAgent builds and returns the User-Agent string to use in requests.