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

« back to all changes in this revision

Viewing changes to src/launchpad.net/gwacl/retry_policy.go

  • Committer: Nicholas Skaggs
  • Date: 2016-12-02 17:28:37 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161202172837-jkrbdlyjcxtrii2n
Initial commit of 1.25.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013 Canonical Ltd.  This software is licensed under the
 
2
// GNU Lesser General Public License version 3 (see the file COPYING).
 
3
 
 
4
package gwacl
 
5
 
 
6
import (
 
7
        forkedHttp "launchpad.net/gwacl/fork/http"
 
8
        "net/http"
 
9
        "time"
 
10
)
 
11
 
 
12
// A RetryPolicy object encapsulates all the information needed to define how
 
13
// requests should be retried when particular response codes are returned by
 
14
// the Windows Azure server.
 
15
type RetryPolicy struct {
 
16
        // The number of times a request could be retried.  This does not account
 
17
        // for the initial request so a value of 3 means that the request might be
 
18
        // performed 4 times in total.
 
19
        NbRetries int
 
20
        // The HTTP status codes of the response for which the request should be
 
21
        // retried.
 
22
        HttpStatusCodes []int
 
23
        // How long the client should wait between retries.
 
24
        Delay time.Duration
 
25
}
 
26
 
 
27
var (
 
28
        NoRetryPolicy = RetryPolicy{NbRetries: 0}
 
29
)
 
30
 
 
31
// isRetryCode returns whether or not the given http status code indicates that
 
32
// the request should be retried according to this policy.
 
33
func (policy RetryPolicy) isRetryCode(httpStatusCode int) bool {
 
34
        for _, code := range policy.HttpStatusCodes {
 
35
                if code == httpStatusCode {
 
36
                        return true
 
37
                }
 
38
        }
 
39
        return false
 
40
}
 
41
 
 
42
func (policy RetryPolicy) getRetryHelper() *retryHelper {
 
43
        return &retryHelper{retriesLeft: policy.NbRetries, policy: &policy}
 
44
}
 
45
 
 
46
// A retryHelper is a utility object used to enforce a retry policy when
 
47
// performing requests.
 
48
type retryHelper struct {
 
49
        // The maximum number of retries left to perform.
 
50
        retriesLeft int
 
51
        // The `RetryPolicy` enforced by this retrier.
 
52
        policy *RetryPolicy
 
53
}
 
54
 
 
55
// shouldRetry returns whether or not a request governed by the underlying
 
56
// retry policy should be retried.  When it returns 'true', `shouldRetry` also
 
57
// waits for the specified amount of time, as dictated by the retry policy.
 
58
func (ret *retryHelper) shouldRetry(httpStatusCode int) bool {
 
59
        if ret.retriesLeft > 0 && ret.policy.isRetryCode(httpStatusCode) {
 
60
                ret.retriesLeft--
 
61
                return true
 
62
        }
 
63
        return false
 
64
}
 
65
 
 
66
// A retrier is a struct used to repeat a request as governed by a retry
 
67
// policy.  retrier is usually created using RetryPolicy.getRetrier().
 
68
type retrier struct {
 
69
        *retryHelper
 
70
 
 
71
        // The client used to perform requests.
 
72
        client *http.Client
 
73
}
 
74
 
 
75
func (ret *retrier) RetryRequest(request *http.Request) (*http.Response, error) {
 
76
        for {
 
77
                response, err := ret.client.Do(request)
 
78
                if err != nil {
 
79
                        return nil, err
 
80
                }
 
81
                if !ret.shouldRetry(response.StatusCode) {
 
82
                        return response, nil
 
83
                }
 
84
                time.Sleep(ret.policy.Delay)
 
85
        }
 
86
}
 
87
 
 
88
// getRetrier returns a `retrier` object used to enforce the retry policy.
 
89
func (policy RetryPolicy) getRetrier(client *http.Client) *retrier {
 
90
        helper := policy.getRetryHelper()
 
91
        return &retrier{retryHelper: helper, client: client}
 
92
}
 
93
 
 
94
// A forkedHttpRetrier is a struct used to repeat a request as governed by a
 
95
// retry policy.  forkedHttpRetrier is usually created using
 
96
// RetryPolicy.getForkedHttpRetrier().  It's the same as the `retrier` struct
 
97
// except it deals with the forked version of the http package.
 
98
type forkedHttpRetrier struct {
 
99
        *retryHelper
 
100
 
 
101
        // The client used to perform requests.
 
102
        client *forkedHttp.Client
 
103
}
 
104
 
 
105
func (ret *forkedHttpRetrier) RetryRequest(request *forkedHttp.Request) (*forkedHttp.Response, error) {
 
106
        for {
 
107
                response, err := ret.client.Do(request)
 
108
                if err != nil {
 
109
                        return nil, err
 
110
                }
 
111
                if !ret.shouldRetry(response.StatusCode) {
 
112
                        return response, nil
 
113
                }
 
114
                time.Sleep(ret.policy.Delay)
 
115
        }
 
116
}
 
117
 
 
118
// getRetrier returns a `retrier` object used to enforce the retry policy.
 
119
func (policy RetryPolicy) getForkedHttpRetrier(client *forkedHttp.Client) *forkedHttpRetrier {
 
120
        helper := policy.getRetryHelper()
 
121
        return &forkedHttpRetrier{retryHelper: helper, client: client}
 
122
}