client.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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. "bytes"
  17. "crypto/tls"
  18. "errors"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "log"
  23. "net/http"
  24. "strings"
  25. "time"
  26. "github.com/Azure/go-autorest/logger"
  27. )
  28. const (
  29. // DefaultPollingDelay is a reasonable delay between polling requests.
  30. DefaultPollingDelay = 30 * time.Second
  31. // DefaultPollingDuration is a reasonable total polling duration.
  32. DefaultPollingDuration = 15 * time.Minute
  33. // DefaultRetryAttempts is number of attempts for retry status codes (5xx).
  34. DefaultRetryAttempts = 3
  35. // DefaultRetryDuration is the duration to wait between retries.
  36. DefaultRetryDuration = 30 * time.Second
  37. )
  38. var (
  39. // StatusCodesForRetry are a defined group of status code for which the client will retry
  40. StatusCodesForRetry = []int{
  41. http.StatusRequestTimeout, // 408
  42. http.StatusTooManyRequests, // 429
  43. http.StatusInternalServerError, // 500
  44. http.StatusBadGateway, // 502
  45. http.StatusServiceUnavailable, // 503
  46. http.StatusGatewayTimeout, // 504
  47. }
  48. )
  49. const (
  50. requestFormat = `HTTP Request Begin ===================================================
  51. %s
  52. ===================================================== HTTP Request End
  53. `
  54. responseFormat = `HTTP Response Begin ===================================================
  55. %s
  56. ===================================================== HTTP Response End
  57. `
  58. )
  59. // Response serves as the base for all responses from generated clients. It provides access to the
  60. // last http.Response.
  61. type Response struct {
  62. *http.Response `json:"-"`
  63. }
  64. // IsHTTPStatus returns true if the returned HTTP status code matches the provided status code.
  65. // If there was no response (i.e. the underlying http.Response is nil) the return value is false.
  66. func (r Response) IsHTTPStatus(statusCode int) bool {
  67. if r.Response == nil {
  68. return false
  69. }
  70. return r.Response.StatusCode == statusCode
  71. }
  72. // HasHTTPStatus returns true if the returned HTTP status code matches one of the provided status codes.
  73. // If there was no response (i.e. the underlying http.Response is nil) or not status codes are provided
  74. // the return value is false.
  75. func (r Response) HasHTTPStatus(statusCodes ...int) bool {
  76. return ResponseHasStatusCode(r.Response, statusCodes...)
  77. }
  78. // LoggingInspector implements request and response inspectors that log the full request and
  79. // response to a supplied log.
  80. type LoggingInspector struct {
  81. Logger *log.Logger
  82. }
  83. // WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The
  84. // body is restored after being emitted.
  85. //
  86. // Note: Since it reads the entire Body, this decorator should not be used where body streaming is
  87. // important. It is best used to trace JSON or similar body values.
  88. func (li LoggingInspector) WithInspection() PrepareDecorator {
  89. return func(p Preparer) Preparer {
  90. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  91. var body, b bytes.Buffer
  92. defer r.Body.Close()
  93. r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body))
  94. if err := r.Write(&b); err != nil {
  95. return nil, fmt.Errorf("Failed to write response: %v", err)
  96. }
  97. li.Logger.Printf(requestFormat, b.String())
  98. r.Body = ioutil.NopCloser(&body)
  99. return p.Prepare(r)
  100. })
  101. }
  102. }
  103. // ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The
  104. // body is restored after being emitted.
  105. //
  106. // Note: Since it reads the entire Body, this decorator should not be used where body streaming is
  107. // important. It is best used to trace JSON or similar body values.
  108. func (li LoggingInspector) ByInspecting() RespondDecorator {
  109. return func(r Responder) Responder {
  110. return ResponderFunc(func(resp *http.Response) error {
  111. var body, b bytes.Buffer
  112. defer resp.Body.Close()
  113. resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body))
  114. if err := resp.Write(&b); err != nil {
  115. return fmt.Errorf("Failed to write response: %v", err)
  116. }
  117. li.Logger.Printf(responseFormat, b.String())
  118. resp.Body = ioutil.NopCloser(&body)
  119. return r.Respond(resp)
  120. })
  121. }
  122. }
  123. // Client is the base for autorest generated clients. It provides default, "do nothing"
  124. // implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the
  125. // standard, undecorated http.Client as a default Sender.
  126. //
  127. // Generated clients should also use Error (see NewError and NewErrorWithError) for errors and
  128. // return responses that compose with Response.
  129. //
  130. // Most customization of generated clients is best achieved by supplying a custom Authorizer, custom
  131. // RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit
  132. // breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence
  133. // sending the request by providing a decorated Sender.
  134. type Client struct {
  135. Authorizer Authorizer
  136. Sender Sender
  137. RequestInspector PrepareDecorator
  138. ResponseInspector RespondDecorator
  139. // PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header
  140. PollingDelay time.Duration
  141. // PollingDuration sets the maximum polling time after which an error is returned.
  142. // Setting this to zero will use the provided context to control the duration.
  143. PollingDuration time.Duration
  144. // RetryAttempts sets the total number of times the client will attempt to make an HTTP request.
  145. // Set the value to 1 to disable retries. DO NOT set the value to less than 1.
  146. RetryAttempts int
  147. // RetryDuration sets the delay duration for retries.
  148. RetryDuration time.Duration
  149. // UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent
  150. // through the Do method.
  151. UserAgent string
  152. Jar http.CookieJar
  153. // Set to true to skip attempted registration of resource providers (false by default).
  154. SkipResourceProviderRegistration bool
  155. // SendDecorators can be used to override the default chain of SendDecorators.
  156. // This can be used to specify things like a custom retry SendDecorator.
  157. // Set this to an empty slice to use no SendDecorators.
  158. SendDecorators []SendDecorator
  159. }
  160. // NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
  161. // string.
  162. func NewClientWithUserAgent(ua string) Client {
  163. return newClient(ua, tls.RenegotiateNever)
  164. }
  165. // ClientOptions contains various Client configuration options.
  166. type ClientOptions struct {
  167. // UserAgent is an optional user-agent string to append to the default user agent.
  168. UserAgent string
  169. // Renegotiation is an optional setting to control client-side TLS renegotiation.
  170. Renegotiation tls.RenegotiationSupport
  171. }
  172. // NewClientWithOptions returns an instance of a Client with the specified values.
  173. func NewClientWithOptions(options ClientOptions) Client {
  174. return newClient(options.UserAgent, options.Renegotiation)
  175. }
  176. func newClient(ua string, renegotiation tls.RenegotiationSupport) Client {
  177. c := Client{
  178. PollingDelay: DefaultPollingDelay,
  179. PollingDuration: DefaultPollingDuration,
  180. RetryAttempts: DefaultRetryAttempts,
  181. RetryDuration: DefaultRetryDuration,
  182. UserAgent: UserAgent(),
  183. }
  184. c.Sender = c.sender(renegotiation)
  185. c.AddToUserAgent(ua)
  186. return c
  187. }
  188. // AddToUserAgent adds an extension to the current user agent
  189. func (c *Client) AddToUserAgent(extension string) error {
  190. if extension != "" {
  191. c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension)
  192. return nil
  193. }
  194. return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)
  195. }
  196. // Do implements the Sender interface by invoking the active Sender after applying authorization.
  197. // If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent
  198. // is set, apply set the User-Agent header.
  199. func (c Client) Do(r *http.Request) (*http.Response, error) {
  200. if r.UserAgent() == "" {
  201. r, _ = Prepare(r,
  202. WithUserAgent(c.UserAgent))
  203. }
  204. // NOTE: c.WithInspection() must be last in the list so that it can inspect all preceding operations
  205. r, err := Prepare(r,
  206. c.WithAuthorization(),
  207. c.WithInspection())
  208. if err != nil {
  209. var resp *http.Response
  210. if detErr, ok := err.(DetailedError); ok {
  211. // if the authorization failed (e.g. invalid credentials) there will
  212. // be a response associated with the error, be sure to return it.
  213. resp = detErr.Response
  214. }
  215. return resp, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed")
  216. }
  217. logger.Instance.WriteRequest(r, logger.Filter{
  218. Header: func(k string, v []string) (bool, []string) {
  219. // remove the auth token from the log
  220. if strings.EqualFold(k, "Authorization") || strings.EqualFold(k, "Ocp-Apim-Subscription-Key") {
  221. v = []string{"**REDACTED**"}
  222. }
  223. return true, v
  224. },
  225. })
  226. resp, err := SendWithSender(c.sender(tls.RenegotiateNever), r)
  227. if resp == nil && err == nil {
  228. err = errors.New("autorest: received nil response and error")
  229. }
  230. logger.Instance.WriteResponse(resp, logger.Filter{})
  231. Respond(resp, c.ByInspecting())
  232. return resp, err
  233. }
  234. // sender returns the Sender to which to send requests.
  235. func (c Client) sender(renengotiation tls.RenegotiationSupport) Sender {
  236. if c.Sender == nil {
  237. return sender(renengotiation)
  238. }
  239. return c.Sender
  240. }
  241. // WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator
  242. // from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer.
  243. func (c Client) WithAuthorization() PrepareDecorator {
  244. return c.authorizer().WithAuthorization()
  245. }
  246. // authorizer returns the Authorizer to use.
  247. func (c Client) authorizer() Authorizer {
  248. if c.Authorizer == nil {
  249. return NullAuthorizer{}
  250. }
  251. return c.Authorizer
  252. }
  253. // WithInspection is a convenience method that passes the request to the supplied RequestInspector,
  254. // if present, or returns the WithNothing PrepareDecorator otherwise.
  255. func (c Client) WithInspection() PrepareDecorator {
  256. if c.RequestInspector == nil {
  257. return WithNothing()
  258. }
  259. return c.RequestInspector
  260. }
  261. // ByInspecting is a convenience method that passes the response to the supplied ResponseInspector,
  262. // if present, or returns the ByIgnoring RespondDecorator otherwise.
  263. func (c Client) ByInspecting() RespondDecorator {
  264. if c.ResponseInspector == nil {
  265. return ByIgnoring()
  266. }
  267. return c.ResponseInspector
  268. }
  269. // Send sends the provided http.Request using the client's Sender or the default sender.
  270. // It returns the http.Response and possible error. It also accepts a, possibly empty,
  271. // default set of SendDecorators used when sending the request.
  272. // SendDecorators have the following precedence:
  273. // 1. In a request's context via WithSendDecorators()
  274. // 2. Specified on the client in SendDecorators
  275. // 3. The default values specified in this method
  276. func (c Client) Send(req *http.Request, decorators ...SendDecorator) (*http.Response, error) {
  277. if c.SendDecorators != nil {
  278. decorators = c.SendDecorators
  279. }
  280. inCtx := req.Context().Value(ctxSendDecorators{})
  281. if sd, ok := inCtx.([]SendDecorator); ok {
  282. decorators = sd
  283. }
  284. return SendWithSender(c, req, decorators...)
  285. }