src: Add DMA localagent
[barometer.git] / src / dma / vendor / golang.org / x / crypto / acme / http.go
diff --git a/src/dma/vendor/golang.org/x/crypto/acme/http.go b/src/dma/vendor/golang.org/x/crypto/acme/http.go
new file mode 100644 (file)
index 0000000..a43ce6a
--- /dev/null
@@ -0,0 +1,281 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package acme
+
+import (
+       "bytes"
+       "context"
+       "crypto"
+       "crypto/rand"
+       "encoding/json"
+       "fmt"
+       "io/ioutil"
+       "math/big"
+       "net/http"
+       "strconv"
+       "strings"
+       "time"
+)
+
+// retryTimer encapsulates common logic for retrying unsuccessful requests.
+// It is not safe for concurrent use.
+type retryTimer struct {
+       // backoffFn provides backoff delay sequence for retries.
+       // See Client.RetryBackoff doc comment.
+       backoffFn func(n int, r *http.Request, res *http.Response) time.Duration
+       // n is the current retry attempt.
+       n int
+}
+
+func (t *retryTimer) inc() {
+       t.n++
+}
+
+// backoff pauses the current goroutine as described in Client.RetryBackoff.
+func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error {
+       d := t.backoffFn(t.n, r, res)
+       if d <= 0 {
+               return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n)
+       }
+       wakeup := time.NewTimer(d)
+       defer wakeup.Stop()
+       select {
+       case <-ctx.Done():
+               return ctx.Err()
+       case <-wakeup.C:
+               return nil
+       }
+}
+
+func (c *Client) retryTimer() *retryTimer {
+       f := c.RetryBackoff
+       if f == nil {
+               f = defaultBackoff
+       }
+       return &retryTimer{backoffFn: f}
+}
+
+// defaultBackoff provides default Client.RetryBackoff implementation
+// using a truncated exponential backoff algorithm,
+// as described in Client.RetryBackoff.
+//
+// The n argument is always bounded between 1 and 30.
+// The returned value is always greater than 0.
+func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
+       const max = 10 * time.Second
+       var jitter time.Duration
+       if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
+               // Set the minimum to 1ms to avoid a case where
+               // an invalid Retry-After value is parsed into 0 below,
+               // resulting in the 0 returned value which would unintentionally
+               // stop the retries.
+               jitter = (1 + time.Duration(x.Int64())) * time.Millisecond
+       }
+       if v, ok := res.Header["Retry-After"]; ok {
+               return retryAfter(v[0]) + jitter
+       }
+
+       if n < 1 {
+               n = 1
+       }
+       if n > 30 {
+               n = 30
+       }
+       d := time.Duration(1<<uint(n-1))*time.Second + jitter
+       if d > max {
+               return max
+       }
+       return d
+}
+
+// retryAfter parses a Retry-After HTTP header value,
+// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
+// It returns zero value if v cannot be parsed.
+func retryAfter(v string) time.Duration {
+       if i, err := strconv.Atoi(v); err == nil {
+               return time.Duration(i) * time.Second
+       }
+       t, err := http.ParseTime(v)
+       if err != nil {
+               return 0
+       }
+       return t.Sub(timeNow())
+}
+
+// resOkay is a function that reports whether the provided response is okay.
+// It is expected to keep the response body unread.
+type resOkay func(*http.Response) bool
+
+// wantStatus returns a function which reports whether the code
+// matches the status code of a response.
+func wantStatus(codes ...int) resOkay {
+       return func(res *http.Response) bool {
+               for _, code := range codes {
+                       if code == res.StatusCode {
+                               return true
+                       }
+               }
+               return false
+       }
+}
+
+// get issues an unsigned GET request to the specified URL.
+// It returns a non-error value only when ok reports true.
+//
+// get retries unsuccessful attempts according to c.RetryBackoff
+// until the context is done or a non-retriable error is received.
+func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
+       retry := c.retryTimer()
+       for {
+               req, err := http.NewRequest("GET", url, nil)
+               if err != nil {
+                       return nil, err
+               }
+               res, err := c.doNoRetry(ctx, req)
+               switch {
+               case err != nil:
+                       return nil, err
+               case ok(res):
+                       return res, nil
+               case isRetriable(res.StatusCode):
+                       retry.inc()
+                       resErr := responseError(res)
+                       res.Body.Close()
+                       // Ignore the error value from retry.backoff
+                       // and return the one from last retry, as received from the CA.
+                       if retry.backoff(ctx, req, res) != nil {
+                               return nil, resErr
+                       }
+               default:
+                       defer res.Body.Close()
+                       return nil, responseError(res)
+               }
+       }
+}
+
+// post issues a signed POST request in JWS format using the provided key
+// to the specified URL.
+// It returns a non-error value only when ok reports true.
+//
+// post retries unsuccessful attempts according to c.RetryBackoff
+// until the context is done or a non-retriable error is received.
+// It uses postNoRetry to make individual requests.
+func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body interface{}, ok resOkay) (*http.Response, error) {
+       retry := c.retryTimer()
+       for {
+               res, req, err := c.postNoRetry(ctx, key, url, body)
+               if err != nil {
+                       return nil, err
+               }
+               if ok(res) {
+                       return res, nil
+               }
+               resErr := responseError(res)
+               res.Body.Close()
+               switch {
+               // Check for bad nonce before isRetriable because it may have been returned
+               // with an unretriable response code such as 400 Bad Request.
+               case isBadNonce(resErr):
+                       // Consider any previously stored nonce values to be invalid.
+                       c.clearNonces()
+               case !isRetriable(res.StatusCode):
+                       return nil, resErr
+               }
+               retry.inc()
+               // Ignore the error value from retry.backoff
+               // and return the one from last retry, as received from the CA.
+               if err := retry.backoff(ctx, req, res); err != nil {
+                       return nil, resErr
+               }
+       }
+}
+
+// postNoRetry signs the body with the given key and POSTs it to the provided url.
+// The body argument must be JSON-serializable.
+// It is used by c.post to retry unsuccessful attempts.
+func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
+       nonce, err := c.popNonce(ctx, url)
+       if err != nil {
+               return nil, nil, err
+       }
+       b, err := jwsEncodeJSON(body, key, nonce)
+       if err != nil {
+               return nil, nil, err
+       }
+       req, err := http.NewRequest("POST", url, bytes.NewReader(b))
+       if err != nil {
+               return nil, nil, err
+       }
+       req.Header.Set("Content-Type", "application/jose+json")
+       res, err := c.doNoRetry(ctx, req)
+       if err != nil {
+               return nil, nil, err
+       }
+       c.addNonce(res.Header)
+       return res, req, nil
+}
+
+// doNoRetry issues a request req, replacing its context (if any) with ctx.
+func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
+       res, err := c.httpClient().Do(req.WithContext(ctx))
+       if err != nil {
+               select {
+               case <-ctx.Done():
+                       // Prefer the unadorned context error.
+                       // (The acme package had tests assuming this, previously from ctxhttp's
+                       // behavior, predating net/http supporting contexts natively)
+                       // TODO(bradfitz): reconsider this in the future. But for now this
+                       // requires no test updates.
+                       return nil, ctx.Err()
+               default:
+                       return nil, err
+               }
+       }
+       return res, nil
+}
+
+func (c *Client) httpClient() *http.Client {
+       if c.HTTPClient != nil {
+               return c.HTTPClient
+       }
+       return http.DefaultClient
+}
+
+// isBadNonce reports whether err is an ACME "badnonce" error.
+func isBadNonce(err error) bool {
+       // According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
+       // However, ACME servers in the wild return their versions of the error.
+       // See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
+       // and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
+       ae, ok := err.(*Error)
+       return ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce")
+}
+
+// isRetriable reports whether a request can be retried
+// based on the response status code.
+//
+// Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
+// Callers should parse the response and check with isBadNonce.
+func isRetriable(code int) bool {
+       return code <= 399 || code >= 500 || code == http.StatusTooManyRequests
+}
+
+// responseError creates an error of Error type from resp.
+func responseError(resp *http.Response) error {
+       // don't care if ReadAll returns an error:
+       // json.Unmarshal will fail in that case anyway
+       b, _ := ioutil.ReadAll(resp.Body)
+       e := &wireError{Status: resp.StatusCode}
+       if err := json.Unmarshal(b, e); err != nil {
+               // this is not a regular error response:
+               // populate detail with anything we received,
+               // e.Status will already contain HTTP response code value
+               e.Detail = string(b)
+               if e.Detail == "" {
+                       e.Detail = resp.Status
+               }
+       }
+       return e.error(resp.Header)
+}