retry.go 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. package gensupport
  2. import (
  3. "io"
  4. "net"
  5. "net/http"
  6. "time"
  7. "golang.org/x/net/context"
  8. )
  9. // Retry invokes the given function, retrying it multiple times if the connection failed or
  10. // the HTTP status response indicates the request should be attempted again. ctx may be nil.
  11. func Retry(ctx context.Context, f func() (*http.Response, error), backoff BackoffStrategy) (*http.Response, error) {
  12. for {
  13. resp, err := f()
  14. var status int
  15. if resp != nil {
  16. status = resp.StatusCode
  17. }
  18. // Return if we shouldn't retry.
  19. pause, retry := backoff.Pause()
  20. if !shouldRetry(status, err) || !retry {
  21. return resp, err
  22. }
  23. // Ensure the response body is closed, if any.
  24. if resp != nil && resp.Body != nil {
  25. resp.Body.Close()
  26. }
  27. // Pause, but still listen to ctx.Done if context is not nil.
  28. var done <-chan struct{}
  29. if ctx != nil {
  30. done = ctx.Done()
  31. }
  32. select {
  33. case <-done:
  34. return nil, ctx.Err()
  35. case <-time.After(pause):
  36. }
  37. }
  38. }
  39. // DefaultBackoffStrategy returns a default strategy to use for retrying failed upload requests.
  40. func DefaultBackoffStrategy() BackoffStrategy {
  41. return &ExponentialBackoff{
  42. Base: 250 * time.Millisecond,
  43. Max: 16 * time.Second,
  44. }
  45. }
  46. // shouldRetry returns true if the HTTP response / error indicates that the
  47. // request should be attempted again.
  48. func shouldRetry(status int, err error) bool {
  49. // Retry for 5xx response codes.
  50. if 500 <= status && status < 600 {
  51. return true
  52. }
  53. // Retry on statusTooManyRequests{
  54. if status == statusTooManyRequests {
  55. return true
  56. }
  57. // Retry on unexpected EOFs and temporary network errors.
  58. if err == io.ErrUnexpectedEOF {
  59. return true
  60. }
  61. if err, ok := err.(net.Error); ok {
  62. return err.Temporary()
  63. }
  64. return false
  65. }