Blame vendor/github.com/Azure/go-autorest/autorest/client.go

Packit 63bb0d
package autorest
Packit 63bb0d
Packit 63bb0d
// Copyright 2017 Microsoft Corporation
Packit 63bb0d
//
Packit 63bb0d
//  Licensed under the Apache License, Version 2.0 (the "License");
Packit 63bb0d
//  you may not use this file except in compliance with the License.
Packit 63bb0d
//  You may obtain a copy of the License at
Packit 63bb0d
//
Packit 63bb0d
//      http://www.apache.org/licenses/LICENSE-2.0
Packit 63bb0d
//
Packit 63bb0d
//  Unless required by applicable law or agreed to in writing, software
Packit 63bb0d
//  distributed under the License is distributed on an "AS IS" BASIS,
Packit 63bb0d
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 63bb0d
//  See the License for the specific language governing permissions and
Packit 63bb0d
//  limitations under the License.
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"bytes"
Packit 63bb0d
	"crypto/tls"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io"
Packit 63bb0d
	"io/ioutil"
Packit 63bb0d
	"log"
Packit 63bb0d
	"net/http"
Packit 63bb0d
	"strings"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/Azure/go-autorest/logger"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	// DefaultPollingDelay is a reasonable delay between polling requests.
Packit 63bb0d
	DefaultPollingDelay = 60 * time.Second
Packit 63bb0d
Packit 63bb0d
	// DefaultPollingDuration is a reasonable total polling duration.
Packit 63bb0d
	DefaultPollingDuration = 15 * time.Minute
Packit 63bb0d
Packit 63bb0d
	// DefaultRetryAttempts is number of attempts for retry status codes (5xx).
Packit 63bb0d
	DefaultRetryAttempts = 3
Packit 63bb0d
Packit 63bb0d
	// DefaultRetryDuration is the duration to wait between retries.
Packit 63bb0d
	DefaultRetryDuration = 30 * time.Second
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
var (
Packit 63bb0d
	// StatusCodesForRetry are a defined group of status code for which the client will retry
Packit 63bb0d
	StatusCodesForRetry = []int{
Packit 63bb0d
		http.StatusRequestTimeout,      // 408
Packit 63bb0d
		http.StatusTooManyRequests,     // 429
Packit 63bb0d
		http.StatusInternalServerError, // 500
Packit 63bb0d
		http.StatusBadGateway,          // 502
Packit 63bb0d
		http.StatusServiceUnavailable,  // 503
Packit 63bb0d
		http.StatusGatewayTimeout,      // 504
Packit 63bb0d
	}
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
const (
Packit 63bb0d
	requestFormat = `HTTP Request Begin ===================================================
Packit 63bb0d
%s
Packit 63bb0d
===================================================== HTTP Request End
Packit 63bb0d
`
Packit 63bb0d
	responseFormat = `HTTP Response Begin ===================================================
Packit 63bb0d
%s
Packit 63bb0d
===================================================== HTTP Response End
Packit 63bb0d
`
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// Response serves as the base for all responses from generated clients. It provides access to the
Packit 63bb0d
// last http.Response.
Packit 63bb0d
type Response struct {
Packit 63bb0d
	*http.Response `json:"-"`
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// IsHTTPStatus returns true if the returned HTTP status code matches the provided status code.
Packit 63bb0d
// If there was no response (i.e. the underlying http.Response is nil) the return value is false.
Packit 63bb0d
func (r Response) IsHTTPStatus(statusCode int) bool {
Packit 63bb0d
	if r.Response == nil {
Packit 63bb0d
		return false
Packit 63bb0d
	}
Packit 63bb0d
	return r.Response.StatusCode == statusCode
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// HasHTTPStatus returns true if the returned HTTP status code matches one of the provided status codes.
Packit 63bb0d
// If there was no response (i.e. the underlying http.Response is nil) or not status codes are provided
Packit 63bb0d
// the return value is false.
Packit 63bb0d
func (r Response) HasHTTPStatus(statusCodes ...int) bool {
Packit 63bb0d
	return ResponseHasStatusCode(r.Response, statusCodes...)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// LoggingInspector implements request and response inspectors that log the full request and
Packit 63bb0d
// response to a supplied log.
Packit 63bb0d
type LoggingInspector struct {
Packit 63bb0d
	Logger *log.Logger
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The
Packit 63bb0d
// body is restored after being emitted.
Packit 63bb0d
//
Packit 63bb0d
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
Packit 63bb0d
// important. It is best used to trace JSON or similar body values.
Packit 63bb0d
func (li LoggingInspector) WithInspection() PrepareDecorator {
Packit 63bb0d
	return func(p Preparer) Preparer {
Packit 63bb0d
		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
Packit 63bb0d
			var body, b bytes.Buffer
Packit 63bb0d
Packit 63bb0d
			defer r.Body.Close()
Packit 63bb0d
Packit 63bb0d
			r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body))
Packit 63bb0d
			if err := r.Write(&b); err != nil {
Packit 63bb0d
				return nil, fmt.Errorf("Failed to write response: %v", err)
Packit 63bb0d
			}
Packit 63bb0d
Packit 63bb0d
			li.Logger.Printf(requestFormat, b.String())
Packit 63bb0d
Packit 63bb0d
			r.Body = ioutil.NopCloser(&body)
Packit 63bb0d
			return p.Prepare(r)
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The
Packit 63bb0d
// body is restored after being emitted.
Packit 63bb0d
//
Packit 63bb0d
// Note: Since it reads the entire Body, this decorator should not be used where body streaming is
Packit 63bb0d
// important. It is best used to trace JSON or similar body values.
Packit 63bb0d
func (li LoggingInspector) ByInspecting() RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			var body, b bytes.Buffer
Packit 63bb0d
			defer resp.Body.Close()
Packit 63bb0d
			resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body))
Packit 63bb0d
			if err := resp.Write(&b); err != nil {
Packit 63bb0d
				return fmt.Errorf("Failed to write response: %v", err)
Packit 63bb0d
			}
Packit 63bb0d
Packit 63bb0d
			li.Logger.Printf(responseFormat, b.String())
Packit 63bb0d
Packit 63bb0d
			resp.Body = ioutil.NopCloser(&body)
Packit 63bb0d
			return r.Respond(resp)
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Client is the base for autorest generated clients. It provides default, "do nothing"
Packit 63bb0d
// implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the
Packit 63bb0d
// standard, undecorated http.Client as a default Sender.
Packit 63bb0d
//
Packit 63bb0d
// Generated clients should also use Error (see NewError and NewErrorWithError) for errors and
Packit 63bb0d
// return responses that compose with Response.
Packit 63bb0d
//
Packit 63bb0d
// Most customization of generated clients is best achieved by supplying a custom Authorizer, custom
Packit 63bb0d
// RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit
Packit 63bb0d
// breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence
Packit 63bb0d
// sending the request by providing a decorated Sender.
Packit 63bb0d
type Client struct {
Packit 63bb0d
	Authorizer        Authorizer
Packit 63bb0d
	Sender            Sender
Packit 63bb0d
	RequestInspector  PrepareDecorator
Packit 63bb0d
	ResponseInspector RespondDecorator
Packit 63bb0d
Packit 63bb0d
	// PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header
Packit 63bb0d
	PollingDelay time.Duration
Packit 63bb0d
Packit 63bb0d
	// PollingDuration sets the maximum polling time after which an error is returned.
Packit 63bb0d
	// Setting this to zero will use the provided context to control the duration.
Packit 63bb0d
	PollingDuration time.Duration
Packit 63bb0d
Packit 63bb0d
	// RetryAttempts sets the default number of retry attempts for client.
Packit 63bb0d
	RetryAttempts int
Packit 63bb0d
Packit 63bb0d
	// RetryDuration sets the delay duration for retries.
Packit 63bb0d
	RetryDuration time.Duration
Packit 63bb0d
Packit 63bb0d
	// UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent
Packit 63bb0d
	// through the Do method.
Packit 63bb0d
	UserAgent string
Packit 63bb0d
Packit 63bb0d
	Jar http.CookieJar
Packit 63bb0d
Packit 63bb0d
	// Set to true to skip attempted registration of resource providers (false by default).
Packit 63bb0d
	SkipResourceProviderRegistration bool
Packit 63bb0d
Packit 63bb0d
	// SendDecorators can be used to override the default chain of SendDecorators.
Packit 63bb0d
	// This can be used to specify things like a custom retry SendDecorator.
Packit 63bb0d
	// Set this to an empty slice to use no SendDecorators.
Packit 63bb0d
	SendDecorators []SendDecorator
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed
Packit 63bb0d
// string.
Packit 63bb0d
func NewClientWithUserAgent(ua string) Client {
Packit 63bb0d
	return newClient(ua, tls.RenegotiateNever)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ClientOptions contains various Client configuration options.
Packit 63bb0d
type ClientOptions struct {
Packit 63bb0d
	// UserAgent is an optional user-agent string to append to the default user agent.
Packit 63bb0d
	UserAgent string
Packit 63bb0d
Packit 63bb0d
	// Renegotiation is an optional setting to control client-side TLS renegotiation.
Packit 63bb0d
	Renegotiation tls.RenegotiationSupport
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// NewClientWithOptions returns an instance of a Client with the specified values.
Packit 63bb0d
func NewClientWithOptions(options ClientOptions) Client {
Packit 63bb0d
	return newClient(options.UserAgent, options.Renegotiation)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func newClient(ua string, renegotiation tls.RenegotiationSupport) Client {
Packit 63bb0d
	c := Client{
Packit 63bb0d
		PollingDelay:    DefaultPollingDelay,
Packit 63bb0d
		PollingDuration: DefaultPollingDuration,
Packit 63bb0d
		RetryAttempts:   DefaultRetryAttempts,
Packit 63bb0d
		RetryDuration:   DefaultRetryDuration,
Packit 63bb0d
		UserAgent:       UserAgent(),
Packit 63bb0d
	}
Packit 63bb0d
	c.Sender = c.sender(renegotiation)
Packit 63bb0d
	c.AddToUserAgent(ua)
Packit 63bb0d
	return c
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// AddToUserAgent adds an extension to the current user agent
Packit 63bb0d
func (c *Client) AddToUserAgent(extension string) error {
Packit 63bb0d
	if extension != "" {
Packit 63bb0d
		c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension)
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
	return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Do implements the Sender interface by invoking the active Sender after applying authorization.
Packit 63bb0d
// If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent
Packit 63bb0d
// is set, apply set the User-Agent header.
Packit 63bb0d
func (c Client) Do(r *http.Request) (*http.Response, error) {
Packit 63bb0d
	if r.UserAgent() == "" {
Packit 63bb0d
		r, _ = Prepare(r,
Packit 63bb0d
			WithUserAgent(c.UserAgent))
Packit 63bb0d
	}
Packit 63bb0d
	// NOTE: c.WithInspection() must be last in the list so that it can inspect all preceding operations
Packit 63bb0d
	r, err := Prepare(r,
Packit 63bb0d
		c.WithAuthorization(),
Packit 63bb0d
		c.WithInspection())
Packit 63bb0d
	if err != nil {
Packit 63bb0d
		var resp *http.Response
Packit 63bb0d
		if detErr, ok := err.(DetailedError); ok {
Packit 63bb0d
			// if the authorization failed (e.g. invalid credentials) there will
Packit 63bb0d
			// be a response associated with the error, be sure to return it.
Packit 63bb0d
			resp = detErr.Response
Packit 63bb0d
		}
Packit 63bb0d
		return resp, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed")
Packit 63bb0d
	}
Packit 63bb0d
	logger.Instance.WriteRequest(r, logger.Filter{
Packit 63bb0d
		Header: func(k string, v []string) (bool, []string) {
Packit 63bb0d
			// remove the auth token from the log
Packit 63bb0d
			if strings.EqualFold(k, "Authorization") || strings.EqualFold(k, "Ocp-Apim-Subscription-Key") {
Packit 63bb0d
				v = []string{"**REDACTED**"}
Packit 63bb0d
			}
Packit 63bb0d
			return true, v
Packit 63bb0d
		},
Packit 63bb0d
	})
Packit 63bb0d
	resp, err := SendWithSender(c.sender(tls.RenegotiateNever), r)
Packit 63bb0d
	logger.Instance.WriteResponse(resp, logger.Filter{})
Packit 63bb0d
	Respond(resp, c.ByInspecting())
Packit 63bb0d
	return resp, err
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// sender returns the Sender to which to send requests.
Packit 63bb0d
func (c Client) sender(renengotiation tls.RenegotiationSupport) Sender {
Packit 63bb0d
	if c.Sender == nil {
Packit 63bb0d
		return sender(renengotiation)
Packit 63bb0d
	}
Packit 63bb0d
	return c.Sender
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator
Packit 63bb0d
// from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer.
Packit 63bb0d
func (c Client) WithAuthorization() PrepareDecorator {
Packit 63bb0d
	return c.authorizer().WithAuthorization()
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// authorizer returns the Authorizer to use.
Packit 63bb0d
func (c Client) authorizer() Authorizer {
Packit 63bb0d
	if c.Authorizer == nil {
Packit 63bb0d
		return NullAuthorizer{}
Packit 63bb0d
	}
Packit 63bb0d
	return c.Authorizer
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithInspection is a convenience method that passes the request to the supplied RequestInspector,
Packit 63bb0d
// if present, or returns the WithNothing PrepareDecorator otherwise.
Packit 63bb0d
func (c Client) WithInspection() PrepareDecorator {
Packit 63bb0d
	if c.RequestInspector == nil {
Packit 63bb0d
		return WithNothing()
Packit 63bb0d
	}
Packit 63bb0d
	return c.RequestInspector
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByInspecting is a convenience method that passes the response to the supplied ResponseInspector,
Packit 63bb0d
// if present, or returns the ByIgnoring RespondDecorator otherwise.
Packit 63bb0d
func (c Client) ByInspecting() RespondDecorator {
Packit 63bb0d
	if c.ResponseInspector == nil {
Packit 63bb0d
		return ByIgnoring()
Packit 63bb0d
	}
Packit 63bb0d
	return c.ResponseInspector
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Send sends the provided http.Request using the client's Sender or the default sender.
Packit 63bb0d
// It returns the http.Response and possible error. It also accepts a, possibly empty,
Packit 63bb0d
// default set of SendDecorators used when sending the request.
Packit 63bb0d
// SendDecorators have the following precedence:
Packit 63bb0d
// 1. In a request's context via WithSendDecorators()
Packit 63bb0d
// 2. Specified on the client in SendDecorators
Packit 63bb0d
// 3. The default values specified in this method
Packit 63bb0d
func (c Client) Send(req *http.Request, decorators ...SendDecorator) (*http.Response, error) {
Packit 63bb0d
	if c.SendDecorators != nil {
Packit 63bb0d
		decorators = c.SendDecorators
Packit 63bb0d
	}
Packit 63bb0d
	inCtx := req.Context().Value(ctxSendDecorators{})
Packit 63bb0d
	if sd, ok := inCtx.([]SendDecorator); ok {
Packit 63bb0d
		decorators = sd
Packit 63bb0d
	}
Packit 63bb0d
	return SendWithSender(c, req, decorators...)
Packit 63bb0d
}