~juju-qa/ubuntu/yakkety/juju/juju-1.25.8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright 2013 Canonical Ltd.  This software is licensed under the
// GNU Lesser General Public License version 3 (see the file COPYING).

package gwacl

import (
	forkedHttp "launchpad.net/gwacl/fork/http"
	"net/http"
	"time"
)

// A RetryPolicy object encapsulates all the information needed to define how
// requests should be retried when particular response codes are returned by
// the Windows Azure server.
type RetryPolicy struct {
	// The number of times a request could be retried.  This does not account
	// for the initial request so a value of 3 means that the request might be
	// performed 4 times in total.
	NbRetries int
	// The HTTP status codes of the response for which the request should be
	// retried.
	HttpStatusCodes []int
	// How long the client should wait between retries.
	Delay time.Duration
}

var (
	NoRetryPolicy = RetryPolicy{NbRetries: 0}
)

// isRetryCode returns whether or not the given http status code indicates that
// the request should be retried according to this policy.
func (policy RetryPolicy) isRetryCode(httpStatusCode int) bool {
	for _, code := range policy.HttpStatusCodes {
		if code == httpStatusCode {
			return true
		}
	}
	return false
}

func (policy RetryPolicy) getRetryHelper() *retryHelper {
	return &retryHelper{retriesLeft: policy.NbRetries, policy: &policy}
}

// A retryHelper is a utility object used to enforce a retry policy when
// performing requests.
type retryHelper struct {
	// The maximum number of retries left to perform.
	retriesLeft int
	// The `RetryPolicy` enforced by this retrier.
	policy *RetryPolicy
}

// shouldRetry returns whether or not a request governed by the underlying
// retry policy should be retried.  When it returns 'true', `shouldRetry` also
// waits for the specified amount of time, as dictated by the retry policy.
func (ret *retryHelper) shouldRetry(httpStatusCode int) bool {
	if ret.retriesLeft > 0 && ret.policy.isRetryCode(httpStatusCode) {
		ret.retriesLeft--
		return true
	}
	return false
}

// A retrier is a struct used to repeat a request as governed by a retry
// policy.  retrier is usually created using RetryPolicy.getRetrier().
type retrier struct {
	*retryHelper

	// The client used to perform requests.
	client *http.Client
}

func (ret *retrier) RetryRequest(request *http.Request) (*http.Response, error) {
	for {
		response, err := ret.client.Do(request)
		if err != nil {
			return nil, err
		}
		if !ret.shouldRetry(response.StatusCode) {
			return response, nil
		}
		time.Sleep(ret.policy.Delay)
	}
}

// getRetrier returns a `retrier` object used to enforce the retry policy.
func (policy RetryPolicy) getRetrier(client *http.Client) *retrier {
	helper := policy.getRetryHelper()
	return &retrier{retryHelper: helper, client: client}
}

// A forkedHttpRetrier is a struct used to repeat a request as governed by a
// retry policy.  forkedHttpRetrier is usually created using
// RetryPolicy.getForkedHttpRetrier().  It's the same as the `retrier` struct
// except it deals with the forked version of the http package.
type forkedHttpRetrier struct {
	*retryHelper

	// The client used to perform requests.
	client *forkedHttp.Client
}

func (ret *forkedHttpRetrier) RetryRequest(request *forkedHttp.Request) (*forkedHttp.Response, error) {
	for {
		response, err := ret.client.Do(request)
		if err != nil {
			return nil, err
		}
		if !ret.shouldRetry(response.StatusCode) {
			return response, nil
		}
		time.Sleep(ret.policy.Delay)
	}
}

// getRetrier returns a `retrier` object used to enforce the retry policy.
func (policy RetryPolicy) getForkedHttpRetrier(client *forkedHttp.Client) *forkedHttpRetrier {
	helper := policy.getRetryHelper()
	return &forkedHttpRetrier{retryHelper: helper, client: client}
}