client.go raw

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