src: Add DMA localagent
[barometer.git] / src / dma / vendor / golang.org / x / crypto / acme / http.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package acme
6
7 import (
8         "bytes"
9         "context"
10         "crypto"
11         "crypto/rand"
12         "encoding/json"
13         "fmt"
14         "io/ioutil"
15         "math/big"
16         "net/http"
17         "strconv"
18         "strings"
19         "time"
20 )
21
22 // retryTimer encapsulates common logic for retrying unsuccessful requests.
23 // It is not safe for concurrent use.
24 type retryTimer struct {
25         // backoffFn provides backoff delay sequence for retries.
26         // See Client.RetryBackoff doc comment.
27         backoffFn func(n int, r *http.Request, res *http.Response) time.Duration
28         // n is the current retry attempt.
29         n int
30 }
31
32 func (t *retryTimer) inc() {
33         t.n++
34 }
35
36 // backoff pauses the current goroutine as described in Client.RetryBackoff.
37 func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error {
38         d := t.backoffFn(t.n, r, res)
39         if d <= 0 {
40                 return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n)
41         }
42         wakeup := time.NewTimer(d)
43         defer wakeup.Stop()
44         select {
45         case <-ctx.Done():
46                 return ctx.Err()
47         case <-wakeup.C:
48                 return nil
49         }
50 }
51
52 func (c *Client) retryTimer() *retryTimer {
53         f := c.RetryBackoff
54         if f == nil {
55                 f = defaultBackoff
56         }
57         return &retryTimer{backoffFn: f}
58 }
59
60 // defaultBackoff provides default Client.RetryBackoff implementation
61 // using a truncated exponential backoff algorithm,
62 // as described in Client.RetryBackoff.
63 //
64 // The n argument is always bounded between 1 and 30.
65 // The returned value is always greater than 0.
66 func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
67         const max = 10 * time.Second
68         var jitter time.Duration
69         if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
70                 // Set the minimum to 1ms to avoid a case where
71                 // an invalid Retry-After value is parsed into 0 below,
72                 // resulting in the 0 returned value which would unintentionally
73                 // stop the retries.
74                 jitter = (1 + time.Duration(x.Int64())) * time.Millisecond
75         }
76         if v, ok := res.Header["Retry-After"]; ok {
77                 return retryAfter(v[0]) + jitter
78         }
79
80         if n < 1 {
81                 n = 1
82         }
83         if n > 30 {
84                 n = 30
85         }
86         d := time.Duration(1<<uint(n-1))*time.Second + jitter
87         if d > max {
88                 return max
89         }
90         return d
91 }
92
93 // retryAfter parses a Retry-After HTTP header value,
94 // trying to convert v into an int (seconds) or use http.ParseTime otherwise.
95 // It returns zero value if v cannot be parsed.
96 func retryAfter(v string) time.Duration {
97         if i, err := strconv.Atoi(v); err == nil {
98                 return time.Duration(i) * time.Second
99         }
100         t, err := http.ParseTime(v)
101         if err != nil {
102                 return 0
103         }
104         return t.Sub(timeNow())
105 }
106
107 // resOkay is a function that reports whether the provided response is okay.
108 // It is expected to keep the response body unread.
109 type resOkay func(*http.Response) bool
110
111 // wantStatus returns a function which reports whether the code
112 // matches the status code of a response.
113 func wantStatus(codes ...int) resOkay {
114         return func(res *http.Response) bool {
115                 for _, code := range codes {
116                         if code == res.StatusCode {
117                                 return true
118                         }
119                 }
120                 return false
121         }
122 }
123
124 // get issues an unsigned GET request to the specified URL.
125 // It returns a non-error value only when ok reports true.
126 //
127 // get retries unsuccessful attempts according to c.RetryBackoff
128 // until the context is done or a non-retriable error is received.
129 func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
130         retry := c.retryTimer()
131         for {
132                 req, err := http.NewRequest("GET", url, nil)
133                 if err != nil {
134                         return nil, err
135                 }
136                 res, err := c.doNoRetry(ctx, req)
137                 switch {
138                 case err != nil:
139                         return nil, err
140                 case ok(res):
141                         return res, nil
142                 case isRetriable(res.StatusCode):
143                         retry.inc()
144                         resErr := responseError(res)
145                         res.Body.Close()
146                         // Ignore the error value from retry.backoff
147                         // and return the one from last retry, as received from the CA.
148                         if retry.backoff(ctx, req, res) != nil {
149                                 return nil, resErr
150                         }
151                 default:
152                         defer res.Body.Close()
153                         return nil, responseError(res)
154                 }
155         }
156 }
157
158 // post issues a signed POST request in JWS format using the provided key
159 // to the specified URL.
160 // It returns a non-error value only when ok reports true.
161 //
162 // post retries unsuccessful attempts according to c.RetryBackoff
163 // until the context is done or a non-retriable error is received.
164 // It uses postNoRetry to make individual requests.
165 func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body interface{}, ok resOkay) (*http.Response, error) {
166         retry := c.retryTimer()
167         for {
168                 res, req, err := c.postNoRetry(ctx, key, url, body)
169                 if err != nil {
170                         return nil, err
171                 }
172                 if ok(res) {
173                         return res, nil
174                 }
175                 resErr := responseError(res)
176                 res.Body.Close()
177                 switch {
178                 // Check for bad nonce before isRetriable because it may have been returned
179                 // with an unretriable response code such as 400 Bad Request.
180                 case isBadNonce(resErr):
181                         // Consider any previously stored nonce values to be invalid.
182                         c.clearNonces()
183                 case !isRetriable(res.StatusCode):
184                         return nil, resErr
185                 }
186                 retry.inc()
187                 // Ignore the error value from retry.backoff
188                 // and return the one from last retry, as received from the CA.
189                 if err := retry.backoff(ctx, req, res); err != nil {
190                         return nil, resErr
191                 }
192         }
193 }
194
195 // postNoRetry signs the body with the given key and POSTs it to the provided url.
196 // The body argument must be JSON-serializable.
197 // It is used by c.post to retry unsuccessful attempts.
198 func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
199         nonce, err := c.popNonce(ctx, url)
200         if err != nil {
201                 return nil, nil, err
202         }
203         b, err := jwsEncodeJSON(body, key, nonce)
204         if err != nil {
205                 return nil, nil, err
206         }
207         req, err := http.NewRequest("POST", url, bytes.NewReader(b))
208         if err != nil {
209                 return nil, nil, err
210         }
211         req.Header.Set("Content-Type", "application/jose+json")
212         res, err := c.doNoRetry(ctx, req)
213         if err != nil {
214                 return nil, nil, err
215         }
216         c.addNonce(res.Header)
217         return res, req, nil
218 }
219
220 // doNoRetry issues a request req, replacing its context (if any) with ctx.
221 func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
222         res, err := c.httpClient().Do(req.WithContext(ctx))
223         if err != nil {
224                 select {
225                 case <-ctx.Done():
226                         // Prefer the unadorned context error.
227                         // (The acme package had tests assuming this, previously from ctxhttp's
228                         // behavior, predating net/http supporting contexts natively)
229                         // TODO(bradfitz): reconsider this in the future. But for now this
230                         // requires no test updates.
231                         return nil, ctx.Err()
232                 default:
233                         return nil, err
234                 }
235         }
236         return res, nil
237 }
238
239 func (c *Client) httpClient() *http.Client {
240         if c.HTTPClient != nil {
241                 return c.HTTPClient
242         }
243         return http.DefaultClient
244 }
245
246 // isBadNonce reports whether err is an ACME "badnonce" error.
247 func isBadNonce(err error) bool {
248         // According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
249         // However, ACME servers in the wild return their versions of the error.
250         // See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
251         // and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
252         ae, ok := err.(*Error)
253         return ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce")
254 }
255
256 // isRetriable reports whether a request can be retried
257 // based on the response status code.
258 //
259 // Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
260 // Callers should parse the response and check with isBadNonce.
261 func isRetriable(code int) bool {
262         return code <= 399 || code >= 500 || code == http.StatusTooManyRequests
263 }
264
265 // responseError creates an error of Error type from resp.
266 func responseError(resp *http.Response) error {
267         // don't care if ReadAll returns an error:
268         // json.Unmarshal will fail in that case anyway
269         b, _ := ioutil.ReadAll(resp.Body)
270         e := &wireError{Status: resp.StatusCode}
271         if err := json.Unmarshal(b, e); err != nil {
272                 // this is not a regular error response:
273                 // populate detail with anything we received,
274                 // e.Status will already contain HTTP response code value
275                 e.Detail = string(b)
276                 if e.Detail == "" {
277                         e.Detail = resp.Status
278                 }
279         }
280         return e.error(resp.Header)
281 }