本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
9.3KB

  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. package acme
  5. import (
  6. "bytes"
  7. "context"
  8. "crypto"
  9. "crypto/rand"
  10. "encoding/json"
  11. "fmt"
  12. "io/ioutil"
  13. "math/big"
  14. "net/http"
  15. "strconv"
  16. "strings"
  17. "time"
  18. )
  19. // retryTimer encapsulates common logic for retrying unsuccessful requests.
  20. // It is not safe for concurrent use.
  21. type retryTimer struct {
  22. // backoffFn provides backoff delay sequence for retries.
  23. // See Client.RetryBackoff doc comment.
  24. backoffFn func(n int, r *http.Request, res *http.Response) time.Duration
  25. // n is the current retry attempt.
  26. n int
  27. }
  28. func (t *retryTimer) inc() {
  29. t.n++
  30. }
  31. // backoff pauses the current goroutine as described in Client.RetryBackoff.
  32. func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error {
  33. d := t.backoffFn(t.n, r, res)
  34. if d <= 0 {
  35. return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n)
  36. }
  37. wakeup := time.NewTimer(d)
  38. defer wakeup.Stop()
  39. select {
  40. case <-ctx.Done():
  41. return ctx.Err()
  42. case <-wakeup.C:
  43. return nil
  44. }
  45. }
  46. func (c *Client) retryTimer() *retryTimer {
  47. f := c.RetryBackoff
  48. if f == nil {
  49. f = defaultBackoff
  50. }
  51. return &retryTimer{backoffFn: f}
  52. }
  53. // defaultBackoff provides default Client.RetryBackoff implementation
  54. // using a truncated exponential backoff algorithm,
  55. // as described in Client.RetryBackoff.
  56. //
  57. // The n argument is always bounded between 1 and 30.
  58. // The returned value is always greater than 0.
  59. func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
  60. const max = 10 * time.Second
  61. var jitter time.Duration
  62. if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
  63. // Set the minimum to 1ms to avoid a case where
  64. // an invalid Retry-After value is parsed into 0 below,
  65. // resulting in the 0 returned value which would unintentionally
  66. // stop the retries.
  67. jitter = (1 + time.Duration(x.Int64())) * time.Millisecond
  68. }
  69. if v, ok := res.Header["Retry-After"]; ok {
  70. return retryAfter(v[0]) + jitter
  71. }
  72. if n < 1 {
  73. n = 1
  74. }
  75. if n > 30 {
  76. n = 30
  77. }
  78. d := time.Duration(1<<uint(n-1))*time.Second + jitter
  79. if d > max {
  80. return max
  81. }
  82. return d
  83. }
  84. // retryAfter parses a Retry-After HTTP header value,
  85. // trying to convert v into an int (seconds) or use http.ParseTime otherwise.
  86. // It returns zero value if v cannot be parsed.
  87. func retryAfter(v string) time.Duration {
  88. if i, err := strconv.Atoi(v); err == nil {
  89. return time.Duration(i) * time.Second
  90. }
  91. t, err := http.ParseTime(v)
  92. if err != nil {
  93. return 0
  94. }
  95. return t.Sub(timeNow())
  96. }
  97. // resOkay is a function that reports whether the provided response is okay.
  98. // It is expected to keep the response body unread.
  99. type resOkay func(*http.Response) bool
  100. // wantStatus returns a function which reports whether the code
  101. // matches the status code of a response.
  102. func wantStatus(codes ...int) resOkay {
  103. return func(res *http.Response) bool {
  104. for _, code := range codes {
  105. if code == res.StatusCode {
  106. return true
  107. }
  108. }
  109. return false
  110. }
  111. }
  112. // get issues an unsigned GET request to the specified URL.
  113. // It returns a non-error value only when ok reports true.
  114. //
  115. // get retries unsuccessful attempts according to c.RetryBackoff
  116. // until the context is done or a non-retriable error is received.
  117. func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
  118. retry := c.retryTimer()
  119. for {
  120. req, err := http.NewRequest("GET", url, nil)
  121. if err != nil {
  122. return nil, err
  123. }
  124. res, err := c.doNoRetry(ctx, req)
  125. switch {
  126. case err != nil:
  127. return nil, err
  128. case ok(res):
  129. return res, nil
  130. case isRetriable(res.StatusCode):
  131. retry.inc()
  132. resErr := responseError(res)
  133. res.Body.Close()
  134. // Ignore the error value from retry.backoff
  135. // and return the one from last retry, as received from the CA.
  136. if retry.backoff(ctx, req, res) != nil {
  137. return nil, resErr
  138. }
  139. default:
  140. defer res.Body.Close()
  141. return nil, responseError(res)
  142. }
  143. }
  144. }
  145. // post issues a signed POST request in JWS format using the provided key
  146. // to the specified URL. If key is nil, c.Key is used instead.
  147. // It returns a non-error value only when ok reports true.
  148. //
  149. // post retries unsuccessful attempts according to c.RetryBackoff
  150. // until the context is done or a non-retriable error is received.
  151. // It uses postNoRetry to make individual requests.
  152. func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body interface{}, ok resOkay) (*http.Response, error) {
  153. retry := c.retryTimer()
  154. for {
  155. res, req, err := c.postNoRetry(ctx, key, url, body)
  156. if err != nil {
  157. return nil, err
  158. }
  159. if ok(res) {
  160. return res, nil
  161. }
  162. resErr := responseError(res)
  163. res.Body.Close()
  164. switch {
  165. // Check for bad nonce before isRetriable because it may have been returned
  166. // with an unretriable response code such as 400 Bad Request.
  167. case isBadNonce(resErr):
  168. // Consider any previously stored nonce values to be invalid.
  169. c.clearNonces()
  170. case !isRetriable(res.StatusCode):
  171. return nil, resErr
  172. }
  173. retry.inc()
  174. // Ignore the error value from retry.backoff
  175. // and return the one from last retry, as received from the CA.
  176. if err := retry.backoff(ctx, req, res); err != nil {
  177. return nil, resErr
  178. }
  179. }
  180. }
  181. // postNoRetry signs the body with the given key and POSTs it to the provided url.
  182. // It is used by c.post to retry unsuccessful attempts.
  183. // The body argument must be JSON-serializable.
  184. //
  185. // If key argument is nil, c.Key is used to sign the request.
  186. // If key argument is nil and c.accountKID returns a non-zero keyID,
  187. // the request is sent in KID form. Otherwise, JWK form is used.
  188. //
  189. // In practice, when interfacing with RFC compliant CAs most requests are sent in KID form
  190. // and JWK is used only when KID is unavailable: new account endpoint and certificate
  191. // revocation requests authenticated by a cert key.
  192. // See jwsEncodeJSON for other details.
  193. func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
  194. kid := noKeyID
  195. if key == nil {
  196. key = c.Key
  197. kid = c.accountKID(ctx)
  198. }
  199. nonce, err := c.popNonce(ctx, url)
  200. if err != nil {
  201. return nil, nil, err
  202. }
  203. b, err := jwsEncodeJSON(body, key, kid, nonce, url)
  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. // doNoRetry issues a request req, replacing its context (if any) with ctx.
  220. func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
  221. req.Header.Set("User-Agent", c.userAgent())
  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. func (c *Client) httpClient() *http.Client {
  239. if c.HTTPClient != nil {
  240. return c.HTTPClient
  241. }
  242. return http.DefaultClient
  243. }
  244. // packageVersion is the version of the module that contains this package, for
  245. // sending as part of the User-Agent header. It's set in version_go112.go.
  246. var packageVersion string
  247. // userAgent returns the User-Agent header value. It includes the package name,
  248. // the module version (if available), and the c.UserAgent value (if set).
  249. func (c *Client) userAgent() string {
  250. ua := "golang.org/x/crypto/acme"
  251. if packageVersion != "" {
  252. ua += "@" + packageVersion
  253. }
  254. if c.UserAgent != "" {
  255. ua = c.UserAgent + " " + ua
  256. }
  257. return ua
  258. }
  259. // isBadNonce reports whether err is an ACME "badnonce" error.
  260. func isBadNonce(err error) bool {
  261. // According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
  262. // However, ACME servers in the wild return their versions of the error.
  263. // See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
  264. // and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
  265. ae, ok := err.(*Error)
  266. return ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce")
  267. }
  268. // isRetriable reports whether a request can be retried
  269. // based on the response status code.
  270. //
  271. // Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
  272. // Callers should parse the response and check with isBadNonce.
  273. func isRetriable(code int) bool {
  274. return code <= 399 || code >= 500 || code == http.StatusTooManyRequests
  275. }
  276. // responseError creates an error of Error type from resp.
  277. func responseError(resp *http.Response) error {
  278. // don't care if ReadAll returns an error:
  279. // json.Unmarshal will fail in that case anyway
  280. b, _ := ioutil.ReadAll(resp.Body)
  281. e := &wireError{Status: resp.StatusCode}
  282. if err := json.Unmarshal(b, e); err != nil {
  283. // this is not a regular error response:
  284. // populate detail with anything we received,
  285. // e.Status will already contain HTTP response code value
  286. e.Detail = string(b)
  287. if e.Detail == "" {
  288. e.Detail = resp.Status
  289. }
  290. }
  291. return e.error(resp.Header)
  292. }
上海开阖软件有限公司 沪ICP备12045867号-1