1// Copyright 2017 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package gensupport 16 17import ( 18 "io" 19 "net" 20 "net/http" 21 "time" 22 23 "golang.org/x/net/context" 24) 25 26// Retry invokes the given function, retrying it multiple times if the connection failed or 27// the HTTP status response indicates the request should be attempted again. ctx may be nil. 28func Retry(ctx context.Context, f func() (*http.Response, error), backoff BackoffStrategy) (*http.Response, error) { 29 for { 30 resp, err := f() 31 32 var status int 33 if resp != nil { 34 status = resp.StatusCode 35 } 36 37 // Return if we shouldn't retry. 38 pause, retry := backoff.Pause() 39 if !shouldRetry(status, err) || !retry { 40 return resp, err 41 } 42 43 // Ensure the response body is closed, if any. 44 if resp != nil && resp.Body != nil { 45 resp.Body.Close() 46 } 47 48 // Pause, but still listen to ctx.Done if context is not nil. 49 var done <-chan struct{} 50 if ctx != nil { 51 done = ctx.Done() 52 } 53 select { 54 case <-done: 55 return nil, ctx.Err() 56 case <-time.After(pause): 57 } 58 } 59} 60 61// DefaultBackoffStrategy returns a default strategy to use for retrying failed upload requests. 62func DefaultBackoffStrategy() BackoffStrategy { 63 return &ExponentialBackoff{ 64 Base: 250 * time.Millisecond, 65 Max: 16 * time.Second, 66 } 67} 68 69// shouldRetry returns true if the HTTP response / error indicates that the 70// request should be attempted again. 71func shouldRetry(status int, err error) bool { 72 if 500 <= status && status <= 599 { 73 return true 74 } 75 if status == statusTooManyRequests { 76 return true 77 } 78 if err == io.ErrUnexpectedEOF { 79 return true 80 } 81 if err, ok := err.(net.Error); ok { 82 return err.Temporary() 83 } 84 return false 85} 86