Blame vendor/github.com/Azure/go-autorest/autorest/sender.go

Packit 63bb0d
package autorest
Packit 63bb0d
Packit 63bb0d
// Copyright 2017 Microsoft Corporation
Packit 63bb0d
//
Packit 63bb0d
//  Licensed under the Apache License, Version 2.0 (the "License");
Packit 63bb0d
//  you may not use this file except in compliance with the License.
Packit 63bb0d
//  You may obtain a copy of the License at
Packit 63bb0d
//
Packit 63bb0d
//      http://www.apache.org/licenses/LICENSE-2.0
Packit 63bb0d
//
Packit 63bb0d
//  Unless required by applicable law or agreed to in writing, software
Packit 63bb0d
//  distributed under the License is distributed on an "AS IS" BASIS,
Packit 63bb0d
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 63bb0d
//  See the License for the specific language governing permissions and
Packit 63bb0d
//  limitations under the License.
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"context"
Packit 63bb0d
	"crypto/tls"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"log"
Packit 63bb0d
	"math"
Packit 63bb0d
	"net/http"
Packit 63bb0d
	"net/http/cookiejar"
Packit 63bb0d
	"strconv"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/Azure/go-autorest/tracing"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// used as a key type in context.WithValue()
Packit 63bb0d
type ctxSendDecorators struct{}
Packit 63bb0d
Packit 63bb0d
// WithSendDecorators adds the specified SendDecorators to the provided context.
Packit 63bb0d
// If no SendDecorators are provided the context is unchanged.
Packit 63bb0d
func WithSendDecorators(ctx context.Context, sendDecorator []SendDecorator) context.Context {
Packit 63bb0d
	if len(sendDecorator) == 0 {
Packit 63bb0d
		return ctx
Packit 63bb0d
	}
Packit 63bb0d
	return context.WithValue(ctx, ctxSendDecorators{}, sendDecorator)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// GetSendDecorators returns the SendDecorators in the provided context or the provided default SendDecorators.
Packit 63bb0d
func GetSendDecorators(ctx context.Context, defaultSendDecorators ...SendDecorator) []SendDecorator {
Packit 63bb0d
	inCtx := ctx.Value(ctxSendDecorators{})
Packit 63bb0d
	if sd, ok := inCtx.([]SendDecorator); ok {
Packit 63bb0d
		return sd
Packit 63bb0d
	}
Packit 63bb0d
	return defaultSendDecorators
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Sender is the interface that wraps the Do method to send HTTP requests.
Packit 63bb0d
//
Packit 63bb0d
// The standard http.Client conforms to this interface.
Packit 63bb0d
type Sender interface {
Packit 63bb0d
	Do(*http.Request) (*http.Response, error)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// SenderFunc is a method that implements the Sender interface.
Packit 63bb0d
type SenderFunc func(*http.Request) (*http.Response, error)
Packit 63bb0d
Packit 63bb0d
// Do implements the Sender interface on SenderFunc.
Packit 63bb0d
func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) {
Packit 63bb0d
	return sf(r)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// SendDecorator takes and possibly decorates, by wrapping, a Sender. Decorators may affect the
Packit 63bb0d
// http.Request and pass it along or, first, pass the http.Request along then react to the
Packit 63bb0d
// http.Response result.
Packit 63bb0d
type SendDecorator func(Sender) Sender
Packit 63bb0d
Packit 63bb0d
// CreateSender creates, decorates, and returns, as a Sender, the default http.Client.
Packit 63bb0d
func CreateSender(decorators ...SendDecorator) Sender {
Packit 63bb0d
	return DecorateSender(sender(tls.RenegotiateNever), decorators...)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to
Packit 63bb0d
// the Sender. Decorators are applied in the order received, but their affect upon the request
Packit 63bb0d
// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a
Packit 63bb0d
// post-decorator (pass the http.Request along and react to the results in http.Response).
Packit 63bb0d
func DecorateSender(s Sender, decorators ...SendDecorator) Sender {
Packit 63bb0d
	for _, decorate := range decorators {
Packit 63bb0d
		s = decorate(s)
Packit 63bb0d
	}
Packit 63bb0d
	return s
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Send sends, by means of the default http.Client, the passed http.Request, returning the
Packit 63bb0d
// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which
Packit 63bb0d
// it will apply the http.Client before invoking the Do method.
Packit 63bb0d
//
Packit 63bb0d
// Send is a convenience method and not recommended for production. Advanced users should use
Packit 63bb0d
// SendWithSender, passing and sharing their own Sender (e.g., instance of http.Client).
Packit 63bb0d
//
Packit 63bb0d
// Send will not poll or retry requests.
Packit 63bb0d
func Send(r *http.Request, decorators ...SendDecorator) (*http.Response, error) {
Packit 63bb0d
	return SendWithSender(sender(tls.RenegotiateNever), r, decorators...)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// SendWithSender sends the passed http.Request, through the provided Sender, returning the
Packit 63bb0d
// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which
Packit 63bb0d
// it will apply the http.Client before invoking the Do method.
Packit 63bb0d
//
Packit 63bb0d
// SendWithSender will not poll or retry requests.
Packit 63bb0d
func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*http.Response, error) {
Packit 63bb0d
	return DecorateSender(s, decorators...).Do(r)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func sender(renengotiation tls.RenegotiationSupport) Sender {
Packit 63bb0d
	// Use behaviour compatible with DefaultTransport, but require TLS minimum version.
Packit 63bb0d
	defaultTransport := http.DefaultTransport.(*http.Transport)
Packit 63bb0d
	transport := &http.Transport{
Packit 63bb0d
		Proxy:                 defaultTransport.Proxy,
Packit 63bb0d
		DialContext:           defaultTransport.DialContext,
Packit 63bb0d
		MaxIdleConns:          defaultTransport.MaxIdleConns,
Packit 63bb0d
		IdleConnTimeout:       defaultTransport.IdleConnTimeout,
Packit 63bb0d
		TLSHandshakeTimeout:   defaultTransport.TLSHandshakeTimeout,
Packit 63bb0d
		ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
Packit 63bb0d
		TLSClientConfig: &tls.Config{
Packit 63bb0d
			MinVersion:    tls.VersionTLS12,
Packit 63bb0d
			Renegotiation: renengotiation,
Packit 63bb0d
		},
Packit 63bb0d
	}
Packit 63bb0d
	var roundTripper http.RoundTripper = transport
Packit 63bb0d
	if tracing.IsEnabled() {
Packit 63bb0d
		roundTripper = tracing.NewTransport(transport)
Packit 63bb0d
	}
Packit 63bb0d
	j, _ := cookiejar.New(nil)
Packit 63bb0d
	return &http.Client{Jar: j, Transport: roundTripper}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// AfterDelay returns a SendDecorator that delays for the passed time.Duration before
Packit 63bb0d
// invoking the Sender. The delay may be terminated by closing the optional channel on the
Packit 63bb0d
// http.Request. If canceled, no further Senders are invoked.
Packit 63bb0d
func AfterDelay(d time.Duration) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			if !DelayForBackoff(d, 0, r.Context().Done()) {
Packit 63bb0d
				return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay")
Packit 63bb0d
			}
Packit 63bb0d
			return s.Do(r)
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// AsIs returns a SendDecorator that invokes the passed Sender without modifying the http.Request.
Packit 63bb0d
func AsIs() SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			return s.Do(r)
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoCloseIfError returns a SendDecorator that first invokes the passed Sender after which
Packit 63bb0d
// it closes the response if the passed Sender returns an error and the response body exists.
Packit 63bb0d
func DoCloseIfError() SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			resp, err := s.Do(r)
Packit 63bb0d
			if err != nil {
Packit 63bb0d
				Respond(resp, ByDiscardingBody(), ByClosing())
Packit 63bb0d
			}
Packit 63bb0d
			return resp, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoErrorIfStatusCode returns a SendDecorator that emits an error if the response StatusCode is
Packit 63bb0d
// among the set passed. Since these are artificial errors, the response body may still require
Packit 63bb0d
// closing.
Packit 63bb0d
func DoErrorIfStatusCode(codes ...int) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			resp, err := s.Do(r)
Packit 63bb0d
			if err == nil && ResponseHasStatusCode(resp, codes...) {
Packit 63bb0d
				err = NewErrorWithResponse("autorest", "DoErrorIfStatusCode", resp, "%v %v failed with %s",
Packit 63bb0d
					resp.Request.Method,
Packit 63bb0d
					resp.Request.URL,
Packit 63bb0d
					resp.Status)
Packit 63bb0d
			}
Packit 63bb0d
			return resp, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoErrorUnlessStatusCode returns a SendDecorator that emits an error unless the response
Packit 63bb0d
// StatusCode is among the set passed. Since these are artificial errors, the response body
Packit 63bb0d
// may still require closing.
Packit 63bb0d
func DoErrorUnlessStatusCode(codes ...int) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			resp, err := s.Do(r)
Packit 63bb0d
			if err == nil && !ResponseHasStatusCode(resp, codes...) {
Packit 63bb0d
				err = NewErrorWithResponse("autorest", "DoErrorUnlessStatusCode", resp, "%v %v failed with %s",
Packit 63bb0d
					resp.Request.Method,
Packit 63bb0d
					resp.Request.URL,
Packit 63bb0d
					resp.Status)
Packit 63bb0d
			}
Packit 63bb0d
			return resp, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoPollForStatusCodes returns a SendDecorator that polls if the http.Response contains one of the
Packit 63bb0d
// passed status codes. It expects the http.Response to contain a Location header providing the
Packit 63bb0d
// URL at which to poll (using GET) and will poll until the time passed is equal to or greater than
Packit 63bb0d
// the supplied duration. It will delay between requests for the duration specified in the
Packit 63bb0d
// RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by
Packit 63bb0d
// closing the optional channel on the http.Request.
Packit 63bb0d
func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...int) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
Packit 63bb0d
			resp, err = s.Do(r)
Packit 63bb0d
Packit 63bb0d
			if err == nil && ResponseHasStatusCode(resp, codes...) {
Packit 63bb0d
				r, err = NewPollingRequestWithContext(r.Context(), resp)
Packit 63bb0d
Packit 63bb0d
				for err == nil && ResponseHasStatusCode(resp, codes...) {
Packit 63bb0d
					Respond(resp,
Packit 63bb0d
						ByDiscardingBody(),
Packit 63bb0d
						ByClosing())
Packit 63bb0d
					resp, err = SendWithSender(s, r,
Packit 63bb0d
						AfterDelay(GetRetryAfter(resp, delay)))
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
Packit 63bb0d
			return resp, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoRetryForAttempts returns a SendDecorator that retries a failed request for up to the specified
Packit 63bb0d
// number of attempts, exponentially backing off between requests using the supplied backoff
Packit 63bb0d
// time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on
Packit 63bb0d
// the http.Request.
Packit 63bb0d
func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
Packit 63bb0d
			rr := NewRetriableRequest(r)
Packit 63bb0d
			for attempt := 0; attempt < attempts; attempt++ {
Packit 63bb0d
				err = rr.Prepare()
Packit 63bb0d
				if err != nil {
Packit 63bb0d
					return resp, err
Packit 63bb0d
				}
Packit 63bb0d
				DrainResponseBody(resp)
Packit 63bb0d
				resp, err = s.Do(rr.Request())
Packit 63bb0d
				if err == nil {
Packit 63bb0d
					return resp, err
Packit 63bb0d
				}
Packit 63bb0d
				if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
Packit 63bb0d
					return nil, r.Context().Err()
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return resp, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Count429AsRetry indicates that a 429 response should be included as a retry attempt.
Packit 63bb0d
var Count429AsRetry = true
Packit 63bb0d
Packit 63bb0d
// Max429Delay is the maximum duration to wait between retries on a 429 if no Retry-After header was received.
Packit 63bb0d
var Max429Delay time.Duration
Packit 63bb0d
Packit 63bb0d
// DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified
Packit 63bb0d
// number of attempts, exponentially backing off between requests using the supplied backoff
Packit 63bb0d
// time.Duration (which may be zero). Retrying may be canceled by cancelling the context on the http.Request.
Packit 63bb0d
// NOTE: Code http.StatusTooManyRequests (429) will *not* be counted against the number of attempts.
Packit 63bb0d
func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			return doRetryForStatusCodesImpl(s, r, Count429AsRetry, attempts, backoff, 0, codes...)
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoRetryForStatusCodesWithCap returns a SendDecorator that retries for specified statusCodes for up to the
Packit 63bb0d
// specified number of attempts, exponentially backing off between requests using the supplied backoff
Packit 63bb0d
// time.Duration (which may be zero). To cap the maximum possible delay between iterations specify a value greater
Packit 63bb0d
// than zero for cap. Retrying may be canceled by cancelling the context on the http.Request.
Packit 63bb0d
func DoRetryForStatusCodesWithCap(attempts int, backoff, cap time.Duration, codes ...int) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			return doRetryForStatusCodesImpl(s, r, Count429AsRetry, attempts, backoff, cap, codes...)
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func doRetryForStatusCodesImpl(s Sender, r *http.Request, count429 bool, attempts int, backoff, cap time.Duration, codes ...int) (resp *http.Response, err error) {
Packit 63bb0d
	rr := NewRetriableRequest(r)
Packit 63bb0d
	// Increment to add the first call (attempts denotes number of retries)
Packit 63bb0d
	for attempt, delayCount := 0, 0; attempt < attempts+1; {
Packit 63bb0d
		err = rr.Prepare()
Packit 63bb0d
		if err != nil {
Packit 63bb0d
			return
Packit 63bb0d
		}
Packit 63bb0d
		DrainResponseBody(resp)
Packit 63bb0d
		resp, err = s.Do(rr.Request())
Packit 63bb0d
		// we want to retry if err is not nil (e.g. transient network failure).  note that for failed authentication
Packit 63bb0d
		// resp and err will both have a value, so in this case we don't want to retry as it will never succeed.
Packit 63bb0d
		if err == nil && !ResponseHasStatusCode(resp, codes...) || IsTokenRefreshError(err) {
Packit 63bb0d
			return resp, err
Packit 63bb0d
		}
Packit 63bb0d
		delayed := DelayWithRetryAfter(resp, r.Context().Done())
Packit 63bb0d
		// if this was a 429 set the delay cap as specified.
Packit 63bb0d
		// applicable only in the absence of a retry-after header.
Packit 63bb0d
		if resp != nil && resp.StatusCode == http.StatusTooManyRequests {
Packit 63bb0d
			cap = Max429Delay
Packit 63bb0d
		}
Packit 63bb0d
		if !delayed && !DelayForBackoffWithCap(backoff, cap, delayCount, r.Context().Done()) {
Packit 63bb0d
			return resp, r.Context().Err()
Packit 63bb0d
		}
Packit 63bb0d
		// when count429 == false don't count a 429 against the number
Packit 63bb0d
		// of attempts so that we continue to retry until it succeeds
Packit 63bb0d
		if count429 || (resp == nil || resp.StatusCode != http.StatusTooManyRequests) {
Packit 63bb0d
			attempt++
Packit 63bb0d
		}
Packit 63bb0d
		// delay count is tracked separately from attempts to
Packit 63bb0d
		// ensure that 429 participates in exponential back-off
Packit 63bb0d
		delayCount++
Packit 63bb0d
	}
Packit 63bb0d
	return resp, err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DelayWithRetryAfter invokes time.After for the duration specified in the "Retry-After" header.
Packit 63bb0d
// The value of Retry-After can be either the number of seconds or a date in RFC1123 format.
Packit 63bb0d
// The function returns true after successfully waiting for the specified duration.  If there is
Packit 63bb0d
// no Retry-After header or the wait is cancelled the return value is false.
Packit 63bb0d
func DelayWithRetryAfter(resp *http.Response, cancel <-chan struct{}) bool {
Packit 63bb0d
	if resp == nil {
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
	var dur time.Duration
Packit 63bb0d
	ra := resp.Header.Get("Retry-After")
Packit 63bb0d
	if retryAfter, _ := strconv.Atoi(ra); retryAfter > 0 {
Packit 63bb0d
		dur = time.Duration(retryAfter) * time.Second
Packit 63bb0d
	} else if t, err := time.Parse(time.RFC1123, ra); err == nil {
Packit 63bb0d
		dur = t.Sub(time.Now())
Packit 63bb0d
	}
Packit 63bb0d
	if dur > 0 {
Packit 63bb0d
		select {
Packit 63bb0d
		case <-time.After(dur):
Packit 63bb0d
			return true
Packit 63bb0d
		case <-cancel:
Packit 63bb0d
			return false
Packit 63bb0d
		}
Packit 63bb0d
	}
Packit 63bb0d
	return false
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DoRetryForDuration returns a SendDecorator that retries the request until the total time is equal
Packit 63bb0d
// to or greater than the specified duration, exponentially backing off between requests using the
Packit 63bb0d
// supplied backoff time.Duration (which may be zero). Retrying may be canceled by closing the
Packit 63bb0d
// optional channel on the http.Request.
Packit 63bb0d
func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
Packit 63bb0d
			rr := NewRetriableRequest(r)
Packit 63bb0d
			end := time.Now().Add(d)
Packit 63bb0d
			for attempt := 0; time.Now().Before(end); attempt++ {
Packit 63bb0d
				err = rr.Prepare()
Packit 63bb0d
				if err != nil {
Packit 63bb0d
					return resp, err
Packit 63bb0d
				}
Packit 63bb0d
				DrainResponseBody(resp)
Packit 63bb0d
				resp, err = s.Do(rr.Request())
Packit 63bb0d
				if err == nil {
Packit 63bb0d
					return resp, err
Packit 63bb0d
				}
Packit 63bb0d
				if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
Packit 63bb0d
					return nil, r.Context().Err()
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return resp, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithLogging returns a SendDecorator that implements simple before and after logging of the
Packit 63bb0d
// request.
Packit 63bb0d
func WithLogging(logger *log.Logger) SendDecorator {
Packit 63bb0d
	return func(s Sender) Sender {
Packit 63bb0d
		return SenderFunc(func(r *http.Request) (*http.Response, error) {
Packit 63bb0d
			logger.Printf("Sending %s %s", r.Method, r.URL)
Packit 63bb0d
			resp, err := s.Do(r)
Packit 63bb0d
			if err != nil {
Packit 63bb0d
				logger.Printf("%s %s received error '%v'", r.Method, r.URL, err)
Packit 63bb0d
			} else {
Packit 63bb0d
				logger.Printf("%s %s received %s", r.Method, r.URL, resp.Status)
Packit 63bb0d
			}
Packit 63bb0d
			return resp, err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DelayForBackoff invokes time.After for the supplied backoff duration raised to the power of
Packit 63bb0d
// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set
Packit 63bb0d
// to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early,
Packit 63bb0d
// returns false.
Packit 63bb0d
// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt
Packit 63bb0d
// count.
Packit 63bb0d
func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool {
Packit 63bb0d
	return DelayForBackoffWithCap(backoff, 0, attempt, cancel)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DelayForBackoffWithCap invokes time.After for the supplied backoff duration raised to the power of
Packit 63bb0d
// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set
Packit 63bb0d
// to zero for no delay. To cap the maximum possible delay specify a value greater than zero for cap.
Packit 63bb0d
// The delay may be canceled by closing the passed channel. If terminated early, returns false.
Packit 63bb0d
// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt
Packit 63bb0d
// count.
Packit 63bb0d
func DelayForBackoffWithCap(backoff, cap time.Duration, attempt int, cancel <-chan struct{}) bool {
Packit 63bb0d
	d := time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second
Packit 63bb0d
	if cap > 0 && d > cap {
Packit 63bb0d
		d = cap
Packit 63bb0d
	}
Packit 63bb0d
	select {
Packit 63bb0d
	case <-time.After(d):
Packit 63bb0d
		return true
Packit 63bb0d
	case <-cancel:
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
}