summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/xenolf/lego/acme/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/xenolf/lego/acme/client.go')
-rw-r--r--vendor/github.com/xenolf/lego/acme/client.go149
1 files changed, 85 insertions, 64 deletions
diff --git a/vendor/github.com/xenolf/lego/acme/client.go b/vendor/github.com/xenolf/lego/acme/client.go
index 9f837af36..e824f5080 100644
--- a/vendor/github.com/xenolf/lego/acme/client.go
+++ b/vendor/github.com/xenolf/lego/acme/client.go
@@ -11,6 +11,7 @@ import (
"io/ioutil"
"log"
"net"
+ "net/http"
"regexp"
"strconv"
"strings"
@@ -22,6 +23,9 @@ var (
Logger *log.Logger
)
+// maxBodySize is the maximum size of body that we will read.
+const maxBodySize = 1024 * 1024
+
// logf writes a log entry. It uses Logger if not
// nil, otherwise it uses the default log.Logger.
func logf(format string, args ...interface{}) {
@@ -49,12 +53,11 @@ type validateFunc func(j *jws, domain, uri string, chlng challenge) error
// Client is the user-friendy way to ACME
type Client struct {
- directory directory
- user User
- jws *jws
- keyType KeyType
- issuerCert []byte
- solvers map[Challenge]solver
+ directory directory
+ user User
+ jws *jws
+ keyType KeyType
+ solvers map[Challenge]solver
}
// NewClient creates a new ACME client on behalf of the user. The client will depend on
@@ -611,89 +614,108 @@ func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle
return CertificateResource{}, err
}
- cerRes := CertificateResource{
+ certRes := CertificateResource{
Domain: commonName.Domain,
CertURL: resp.Header.Get("Location"),
- PrivateKey: privateKeyPem}
+ PrivateKey: privateKeyPem,
+ }
- for {
- switch resp.StatusCode {
- case 201, 202:
- cert, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
- resp.Body.Close()
- if err != nil {
- return CertificateResource{}, err
- }
+ maxChecks := 1000
+ for i := 0; i < maxChecks; i++ {
+ done, err := c.checkCertResponse(resp, &certRes, bundle)
+ resp.Body.Close()
+ if err != nil {
+ return CertificateResource{}, err
+ }
+ if done {
+ break
+ }
+ if i == maxChecks-1 {
+ return CertificateResource{}, fmt.Errorf("polled for certificate %d times; giving up", i)
+ }
+ resp, err = httpGet(certRes.CertURL)
+ if err != nil {
+ return CertificateResource{}, err
+ }
+ }
+
+ return certRes, nil
+}
+
+// checkCertResponse checks resp to see if a certificate is contained in the
+// response, and if so, loads it into certRes and returns true. If the cert
+// is not yet ready, it returns false. This function honors the waiting period
+// required by the Retry-After header of the response, if specified. This
+// function may read from resp.Body but does NOT close it. The certRes input
+// should already have the Domain (common name) field populated. If bundle is
+// true, the certificate will be bundled with the issuer's cert.
+func (c *Client) checkCertResponse(resp *http.Response, certRes *CertificateResource, bundle bool) (bool, error) {
+ switch resp.StatusCode {
+ case 201, 202:
+ cert, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
+ if err != nil {
+ return false, err
+ }
+
+ // The server returns a body with a length of zero if the
+ // certificate was not ready at the time this request completed.
+ // Otherwise the body is the certificate.
+ if len(cert) > 0 {
+ certRes.CertStableURL = resp.Header.Get("Content-Location")
+ certRes.AccountRef = c.user.GetRegistration().URI
- // The server returns a body with a length of zero if the
- // certificate was not ready at the time this request completed.
- // Otherwise the body is the certificate.
- if len(cert) > 0 {
+ issuedCert := pemEncode(derCertificateBytes(cert))
- cerRes.CertStableURL = resp.Header.Get("Content-Location")
- cerRes.AccountRef = c.user.GetRegistration().URI
+ // The issuer certificate link is always supplied via an "up" link
+ // in the response headers of a new certificate.
+ links := parseLinks(resp.Header["Link"])
+ issuerCert, err := c.getIssuerCertificate(links["up"])
+ if err != nil {
+ // If we fail to acquire the issuer cert, return the issued certificate - do not fail.
+ logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
+ } else {
+ issuerCert = pemEncode(derCertificateBytes(issuerCert))
- issuedCert := pemEncode(derCertificateBytes(cert))
// If bundle is true, we want to return a certificate bundle.
- // To do this, we need the issuer certificate.
+ // To do this, we append the issuer cert to the issued cert.
if bundle {
- // The issuer certificate link is always supplied via an "up" link
- // in the response headers of a new certificate.
- links := parseLinks(resp.Header["Link"])
- issuerCert, err := c.getIssuerCertificate(links["up"])
- if err != nil {
- // If we fail to acquire the issuer cert, return the issued certificate - do not fail.
- logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err)
- } else {
- // Success - append the issuer cert to the issued cert.
- issuerCert = pemEncode(derCertificateBytes(issuerCert))
- issuedCert = append(issuedCert, issuerCert...)
- }
+ issuedCert = append(issuedCert, issuerCert...)
}
-
- cerRes.Certificate = issuedCert
- logf("[INFO][%s] Server responded with a certificate.", commonName.Domain)
- return cerRes, nil
- }
-
- // The certificate was granted but is not yet issued.
- // Check retry-after and loop.
- ra := resp.Header.Get("Retry-After")
- retryAfter, err := strconv.Atoi(ra)
- if err != nil {
- return CertificateResource{}, err
}
- logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", commonName.Domain, retryAfter)
- time.Sleep(time.Duration(retryAfter) * time.Second)
-
- break
- default:
- return CertificateResource{}, handleHTTPError(resp)
+ certRes.Certificate = issuedCert
+ certRes.IssuerCertificate = issuerCert
+ logf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
+ return true, nil
}
- resp, err = httpGet(cerRes.CertURL)
+ // The certificate was granted but is not yet issued.
+ // Check retry-after and loop.
+ ra := resp.Header.Get("Retry-After")
+ retryAfter, err := strconv.Atoi(ra)
if err != nil {
- return CertificateResource{}, err
+ return false, err
}
+
+ logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", certRes.Domain, retryAfter)
+ time.Sleep(time.Duration(retryAfter) * time.Second)
+
+ return false, nil
+ default:
+ return false, handleHTTPError(resp)
}
}
-// getIssuerCertificate requests the issuer certificate and caches it for
-// subsequent requests.
+// getIssuerCertificate requests the issuer certificate
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
logf("[INFO] acme: Requesting issuer cert from %s", url)
- if c.issuerCert != nil {
- return c.issuerCert, nil
- }
-
resp, err := httpGet(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
- issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
+ issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
if err != nil {
return nil, err
}
@@ -703,7 +725,6 @@ func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
return nil, err
}
- c.issuerCert = issuerBytes
return issuerBytes, err
}