ACMEv2 client library for Go.

util-retry.go 1.4KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. package acmeapi
  2. import (
  3. "context"
  4. "net/http"
  5. "strconv"
  6. "time"
  7. "git.devever.net/hlandau/goutils/clock"
  8. )
  9. var defaultClock = clock.Real
  10. func parseRetryAfter(h http.Header) (t time.Time, ok bool) {
  11. v := h.Get("Retry-After")
  12. if v == "" {
  13. return time.Time{}, false
  14. }
  15. n, err := strconv.ParseUint(v, 10, 31)
  16. if err != nil {
  17. t, err = time.Parse(time.RFC1123, v)
  18. if err != nil {
  19. return time.Time{}, false
  20. }
  21. return t, true
  22. }
  23. return defaultClock.Now().Add(time.Duration(n) * time.Second), true
  24. }
  25. func retryAtDefault(h http.Header, d time.Duration) time.Time {
  26. t, ok := parseRetryAfter(h)
  27. if ok {
  28. return t
  29. }
  30. return defaultClock.Now().Add(d)
  31. }
  32. // Wait until time t. If t is before the current time, returns immediately.
  33. // Cancellable via ctx, in which case err is passed through. Otherwise returns
  34. // nil.
  35. func waitUntil(ctx context.Context, t time.Time) error {
  36. var ch <-chan time.Time
  37. ch = closedChannel
  38. now := defaultClock.Now()
  39. if t.After(now) {
  40. ch = defaultClock.After(t.Sub(now))
  41. }
  42. // make sure ctx.Done() is checked here even when we are using closedChannel,
  43. // as select doesn't guarantee any particular priority.
  44. select {
  45. case <-ctx.Done():
  46. return ctx.Err()
  47. default:
  48. select {
  49. case <-ctx.Done():
  50. return ctx.Err()
  51. case <-ch:
  52. }
  53. }
  54. return nil
  55. }
  56. var closedChannel = make(chan time.Time)
  57. func init() {
  58. close(closedChannel)
  59. }