sender.go 12 KB

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