github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/putio/error.go (about) 1 package putio 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strconv" 8 "time" 9 10 "github.com/putdotio/go-putio/putio" 11 "github.com/rclone/rclone/fs/fserrors" 12 "github.com/rclone/rclone/lib/pacer" 13 ) 14 15 func checkStatusCode(resp *http.Response, expected ...int) error { 16 for _, code := range expected { 17 if resp.StatusCode == code { 18 return nil 19 } 20 } 21 return &statusCodeError{response: resp} 22 } 23 24 type statusCodeError struct { 25 response *http.Response 26 } 27 28 func (e *statusCodeError) Error() string { 29 return fmt.Sprintf("unexpected status code (%d) response while doing %s to %s", e.response.StatusCode, e.response.Request.Method, e.response.Request.URL.String()) 30 } 31 32 // This method is called from fserrors.ShouldRetry() to determine if an error should be retried. 33 // Some errors (e.g. 429 Too Many Requests) are handled before this step, so they are not included here. 34 func (e *statusCodeError) Temporary() bool { 35 return e.response.StatusCode >= 500 36 } 37 38 // shouldRetry returns a boolean as to whether this err deserves to be 39 // retried. It returns the err as a convenience 40 func shouldRetry(ctx context.Context, err error) (bool, error) { 41 if fserrors.ContextError(ctx, &err) { 42 return false, err 43 } 44 if err == nil { 45 return false, nil 46 } 47 if perr, ok := err.(*putio.ErrorResponse); ok { 48 err = &statusCodeError{response: perr.Response} 49 } 50 if scerr, ok := err.(*statusCodeError); ok && scerr.response.StatusCode == 429 { 51 delay := defaultRateLimitSleep 52 header := scerr.response.Header.Get("x-ratelimit-reset") 53 if header != "" { 54 if resetTime, cerr := strconv.ParseInt(header, 10, 64); cerr == nil { 55 delay = time.Until(time.Unix(resetTime+1, 0)) 56 } 57 } 58 return true, pacer.RetryAfterError(scerr, delay) 59 } 60 if fserrors.ShouldRetry(err) { 61 return true, err 62 } 63 return false, err 64 }