http.go 10 KB

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