Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / Azure / go-autorest / autorest / sender.go
1 package autorest
2
3 // Copyright 2017 Microsoft Corporation
4 //
5 //  Licensed under the Apache License, Version 2.0 (the "License");
6 //  you may not use this file except in compliance with the License.
7 //  You may obtain a copy of the License at
8 //
9 //      http://www.apache.org/licenses/LICENSE-2.0
10 //
11 //  Unless required by applicable law or agreed to in writing, software
12 //  distributed under the License is distributed on an "AS IS" BASIS,
13 //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 //  See the License for the specific language governing permissions and
15 //  limitations under the License.
16
17 import (
18         "fmt"
19         "log"
20         "math"
21         "net/http"
22         "strconv"
23         "time"
24
25         "github.com/Azure/go-autorest/tracing"
26 )
27
28 // Sender is the interface that wraps the Do method to send HTTP requests.
29 //
30 // The standard http.Client conforms to this interface.
31 type Sender interface {
32         Do(*http.Request) (*http.Response, error)
33 }
34
35 // SenderFunc is a method that implements the Sender interface.
36 type SenderFunc func(*http.Request) (*http.Response, error)
37
38 // Do implements the Sender interface on SenderFunc.
39 func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) {
40         return sf(r)
41 }
42
43 // SendDecorator takes and possibly decorates, by wrapping, a Sender. Decorators may affect the
44 // http.Request and pass it along or, first, pass the http.Request along then react to the
45 // http.Response result.
46 type SendDecorator func(Sender) Sender
47
48 // CreateSender creates, decorates, and returns, as a Sender, the default http.Client.
49 func CreateSender(decorators ...SendDecorator) Sender {
50         return DecorateSender(&http.Client{}, decorators...)
51 }
52
53 // DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to
54 // the Sender. Decorators are applied in the order received, but their affect upon the request
55 // depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a
56 // post-decorator (pass the http.Request along and react to the results in http.Response).
57 func DecorateSender(s Sender, decorators ...SendDecorator) Sender {
58         for _, decorate := range decorators {
59                 s = decorate(s)
60         }
61         return s
62 }
63
64 // Send sends, by means of the default http.Client, the passed http.Request, returning the
65 // http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which
66 // it will apply the http.Client before invoking the Do method.
67 //
68 // Send is a convenience method and not recommended for production. Advanced users should use
69 // SendWithSender, passing and sharing their own Sender (e.g., instance of http.Client).
70 //
71 // Send will not poll or retry requests.
72 func Send(r *http.Request, decorators ...SendDecorator) (*http.Response, error) {
73         return SendWithSender(&http.Client{Transport: tracing.Transport}, r, decorators...)
74 }
75
76 // SendWithSender sends the passed http.Request, through the provided Sender, returning the
77 // http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which
78 // it will apply the http.Client before invoking the Do method.
79 //
80 // SendWithSender will not poll or retry requests.
81 func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*http.Response, error) {
82         return DecorateSender(s, decorators...).Do(r)
83 }
84
85 // AfterDelay returns a SendDecorator that delays for the passed time.Duration before
86 // invoking the Sender. The delay may be terminated by closing the optional channel on the
87 // http.Request. If canceled, no further Senders are invoked.
88 func AfterDelay(d time.Duration) SendDecorator {
89         return func(s Sender) Sender {
90                 return SenderFunc(func(r *http.Request) (*http.Response, error) {
91                         if !DelayForBackoff(d, 0, r.Context().Done()) {
92                                 return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay")
93                         }
94                         return s.Do(r)
95                 })
96         }
97 }
98
99 // AsIs returns a SendDecorator that invokes the passed Sender without modifying the http.Request.
100 func AsIs() SendDecorator {
101         return func(s Sender) Sender {
102                 return SenderFunc(func(r *http.Request) (*http.Response, error) {
103                         return s.Do(r)
104                 })
105         }
106 }
107
108 // DoCloseIfError returns a SendDecorator that first invokes the passed Sender after which
109 // it closes the response if the passed Sender returns an error and the response body exists.
110 func DoCloseIfError() SendDecorator {
111         return func(s Sender) Sender {
112                 return SenderFunc(func(r *http.Request) (*http.Response, error) {
113                         resp, err := s.Do(r)
114                         if err != nil {
115                                 Respond(resp, ByDiscardingBody(), ByClosing())
116                         }
117                         return resp, err
118                 })
119         }
120 }
121
122 // DoErrorIfStatusCode returns a SendDecorator that emits an error if the response StatusCode is
123 // among the set passed. Since these are artificial errors, the response body may still require
124 // closing.
125 func DoErrorIfStatusCode(codes ...int) SendDecorator {
126         return func(s Sender) Sender {
127                 return SenderFunc(func(r *http.Request) (*http.Response, error) {
128                         resp, err := s.Do(r)
129                         if err == nil && ResponseHasStatusCode(resp, codes...) {
130                                 err = NewErrorWithResponse("autorest", "DoErrorIfStatusCode", resp, "%v %v failed with %s",
131                                         resp.Request.Method,
132                                         resp.Request.URL,
133                                         resp.Status)
134                         }
135                         return resp, err
136                 })
137         }
138 }
139
140 // DoErrorUnlessStatusCode returns a SendDecorator that emits an error unless the response
141 // StatusCode is among the set passed. Since these are artificial errors, the response body
142 // may still require closing.
143 func DoErrorUnlessStatusCode(codes ...int) SendDecorator {
144         return func(s Sender) Sender {
145                 return SenderFunc(func(r *http.Request) (*http.Response, error) {
146                         resp, err := s.Do(r)
147                         if err == nil && !ResponseHasStatusCode(resp, codes...) {
148                                 err = NewErrorWithResponse("autorest", "DoErrorUnlessStatusCode", resp, "%v %v failed with %s",
149                                         resp.Request.Method,
150                                         resp.Request.URL,
151                                         resp.Status)
152                         }
153                         return resp, err
154                 })
155         }
156 }
157
158 // DoPollForStatusCodes returns a SendDecorator that polls if the http.Response contains one of the
159 // passed status codes. It expects the http.Response to contain a Location header providing the
160 // URL at which to poll (using GET) and will poll until the time passed is equal to or greater than
161 // the supplied duration. It will delay between requests for the duration specified in the
162 // RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by
163 // closing the optional channel on the http.Request.
164 func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...int) SendDecorator {
165         return func(s Sender) Sender {
166                 return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
167                         resp, err = s.Do(r)
168
169                         if err == nil && ResponseHasStatusCode(resp, codes...) {
170                                 r, err = NewPollingRequestWithContext(r.Context(), resp)
171
172                                 for err == nil && ResponseHasStatusCode(resp, codes...) {
173                                         Respond(resp,
174                                                 ByDiscardingBody(),
175                                                 ByClosing())
176                                         resp, err = SendWithSender(s, r,
177                                                 AfterDelay(GetRetryAfter(resp, delay)))
178                                 }
179                         }
180
181                         return resp, err
182                 })
183         }
184 }
185
186 // DoRetryForAttempts returns a SendDecorator that retries a failed request for up to the specified
187 // number of attempts, exponentially backing off between requests using the supplied backoff
188 // time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on
189 // the http.Request.
190 func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
191         return func(s Sender) Sender {
192                 return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
193                         rr := NewRetriableRequest(r)
194                         for attempt := 0; attempt < attempts; attempt++ {
195                                 err = rr.Prepare()
196                                 if err != nil {
197                                         return resp, err
198                                 }
199                                 resp, err = s.Do(rr.Request())
200                                 if err == nil {
201                                         return resp, err
202                                 }
203                                 if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
204                                         return nil, r.Context().Err()
205                                 }
206                         }
207                         return resp, err
208                 })
209         }
210 }
211
212 // DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified
213 // number of attempts, exponentially backing off between requests using the supplied backoff
214 // time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on
215 // the http.Request.
216 func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator {
217         return func(s Sender) Sender {
218                 return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
219                         rr := NewRetriableRequest(r)
220                         // Increment to add the first call (attempts denotes number of retries)
221                         for attempt := 0; attempt < attempts+1; {
222                                 err = rr.Prepare()
223                                 if err != nil {
224                                         return resp, err
225                                 }
226                                 resp, err = s.Do(rr.Request())
227                                 // if the error isn't temporary don't bother retrying
228                                 if err != nil && !IsTemporaryNetworkError(err) {
229                                         return nil, err
230                                 }
231                                 // we want to retry if err is not nil (e.g. transient network failure).  note that for failed authentication
232                                 // resp and err will both have a value, so in this case we don't want to retry as it will never succeed.
233                                 if err == nil && !ResponseHasStatusCode(resp, codes...) || IsTokenRefreshError(err) {
234                                         return resp, err
235                                 }
236                                 delayed := DelayWithRetryAfter(resp, r.Context().Done())
237                                 if !delayed && !DelayForBackoff(backoff, attempt, r.Context().Done()) {
238                                         return resp, r.Context().Err()
239                                 }
240                                 // don't count a 429 against the number of attempts
241                                 // so that we continue to retry until it succeeds
242                                 if resp == nil || resp.StatusCode != http.StatusTooManyRequests {
243                                         attempt++
244                                 }
245                         }
246                         return resp, err
247                 })
248         }
249 }
250
251 // DelayWithRetryAfter invokes time.After for the duration specified in the "Retry-After" header in
252 // responses with status code 429
253 func DelayWithRetryAfter(resp *http.Response, cancel <-chan struct{}) bool {
254         if resp == nil {
255                 return false
256         }
257         retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After"))
258         if resp.StatusCode == http.StatusTooManyRequests && retryAfter > 0 {
259                 select {
260                 case <-time.After(time.Duration(retryAfter) * time.Second):
261                         return true
262                 case <-cancel:
263                         return false
264                 }
265         }
266         return false
267 }
268
269 // DoRetryForDuration returns a SendDecorator that retries the request until the total time is equal
270 // to or greater than the specified duration, exponentially backing off between requests using the
271 // supplied backoff time.Duration (which may be zero). Retrying may be canceled by closing the
272 // optional channel on the http.Request.
273 func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator {
274         return func(s Sender) Sender {
275                 return SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
276                         rr := NewRetriableRequest(r)
277                         end := time.Now().Add(d)
278                         for attempt := 0; time.Now().Before(end); attempt++ {
279                                 err = rr.Prepare()
280                                 if err != nil {
281                                         return resp, err
282                                 }
283                                 resp, err = s.Do(rr.Request())
284                                 if err == nil {
285                                         return resp, err
286                                 }
287                                 if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
288                                         return nil, r.Context().Err()
289                                 }
290                         }
291                         return resp, err
292                 })
293         }
294 }
295
296 // WithLogging returns a SendDecorator that implements simple before and after logging of the
297 // request.
298 func WithLogging(logger *log.Logger) SendDecorator {
299         return func(s Sender) Sender {
300                 return SenderFunc(func(r *http.Request) (*http.Response, error) {
301                         logger.Printf("Sending %s %s", r.Method, r.URL)
302                         resp, err := s.Do(r)
303                         if err != nil {
304                                 logger.Printf("%s %s received error '%v'", r.Method, r.URL, err)
305                         } else {
306                                 logger.Printf("%s %s received %s", r.Method, r.URL, resp.Status)
307                         }
308                         return resp, err
309                 })
310         }
311 }
312
313 // DelayForBackoff invokes time.After for the supplied backoff duration raised to the power of
314 // passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set
315 // to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early,
316 // returns false.
317 // Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt
318 // count.
319 func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool {
320         select {
321         case <-time.After(time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second):
322                 return true
323         case <-cancel:
324                 return false
325         }
326 }