Blame vendor/github.com/Azure/go-autorest/autorest/responder.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
	"encoding/json"
Packit 63bb0d
	"encoding/xml"
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io"
Packit 63bb0d
	"io/ioutil"
Packit 63bb0d
	"net/http"
Packit 63bb0d
	"strings"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// Responder is the interface that wraps the Respond method.
Packit 63bb0d
//
Packit 63bb0d
// Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold
Packit 63bb0d
// state since Responders may be shared and re-used.
Packit 63bb0d
type Responder interface {
Packit 63bb0d
	Respond(*http.Response) error
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ResponderFunc is a method that implements the Responder interface.
Packit 63bb0d
type ResponderFunc func(*http.Response) error
Packit 63bb0d
Packit 63bb0d
// Respond implements the Responder interface on ResponderFunc.
Packit 63bb0d
func (rf ResponderFunc) Respond(r *http.Response) error {
Packit 63bb0d
	return rf(r)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to
Packit 63bb0d
// the http.Response and pass it along or, first, pass the http.Response along then react.
Packit 63bb0d
type RespondDecorator func(Responder) Responder
Packit 63bb0d
Packit 63bb0d
// CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned
Packit 63bb0d
// Responder returns the passed http.Response unmodified. Responders may or may not be safe to share
Packit 63bb0d
// and re-used: It depends on the applied decorators. For example, a standard decorator that closes
Packit 63bb0d
// the response body is fine to share whereas a decorator that reads the body into a passed struct
Packit 63bb0d
// is not.
Packit 63bb0d
//
Packit 63bb0d
// To prevent memory leaks, ensure that at least one Responder closes the response body.
Packit 63bb0d
func CreateResponder(decorators ...RespondDecorator) Responder {
Packit 63bb0d
	return DecorateResponder(
Packit 63bb0d
		Responder(ResponderFunc(func(r *http.Response) error { return nil })),
Packit 63bb0d
		decorators...)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it
Packit 63bb0d
// applies to the Responder. Decorators are applied in the order received, but their affect upon the
Packit 63bb0d
// request depends on whether they are a pre-decorator (react to the http.Response and then pass it
Packit 63bb0d
// along) or a post-decorator (pass the http.Response along and then react).
Packit 63bb0d
func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder {
Packit 63bb0d
	for _, decorate := range decorators {
Packit 63bb0d
		r = decorate(r)
Packit 63bb0d
	}
Packit 63bb0d
	return r
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// Respond accepts an http.Response and a, possibly empty, set of RespondDecorators.
Packit 63bb0d
// It creates a Responder from the decorators it then applies to the passed http.Response.
Packit 63bb0d
func Respond(r *http.Response, decorators ...RespondDecorator) error {
Packit 63bb0d
	if r == nil {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
	return CreateResponder(decorators...).Respond(r)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByIgnoring returns a RespondDecorator that ignores the passed http.Response passing it unexamined
Packit 63bb0d
// to the next RespondDecorator.
Packit 63bb0d
func ByIgnoring() RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			return r.Respond(resp)
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as
Packit 63bb0d
// the Body is read.
Packit 63bb0d
func ByCopying(b *bytes.Buffer) RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if err == nil && resp != nil && resp.Body != nil {
Packit 63bb0d
				resp.Body = TeeReadCloser(resp.Body, b)
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which
Packit 63bb0d
// it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed
Packit 63bb0d
// Responder is invoked prior to discarding the response body, the decorator may occur anywhere
Packit 63bb0d
// within the set.
Packit 63bb0d
func ByDiscardingBody() RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if err == nil && resp != nil && resp.Body != nil {
Packit 63bb0d
				if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
Packit 63bb0d
					return fmt.Errorf("Error discarding the response body: %v", err)
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByClosing returns a RespondDecorator that first invokes the passed Responder after which it
Packit 63bb0d
// closes the response body. Since the passed Responder is invoked prior to closing the response
Packit 63bb0d
// body, the decorator may occur anywhere within the set.
Packit 63bb0d
func ByClosing() RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if resp != nil && resp.Body != nil {
Packit 63bb0d
				if err := resp.Body.Close(); err != nil {
Packit 63bb0d
					return fmt.Errorf("Error closing the response body: %v", err)
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which
Packit 63bb0d
// it closes the response if the passed Responder returns an error and the response body exists.
Packit 63bb0d
func ByClosingIfError() RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if err != nil && resp != nil && resp.Body != nil {
Packit 63bb0d
				if err := resp.Body.Close(); err != nil {
Packit 63bb0d
					return fmt.Errorf("Error closing the response body: %v", err)
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByUnmarshallingBytes returns a RespondDecorator that copies the Bytes returned in the
Packit 63bb0d
// response Body into the value pointed to by v.
Packit 63bb0d
func ByUnmarshallingBytes(v *[]byte) RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if err == nil {
Packit 63bb0d
				bytes, errInner := ioutil.ReadAll(resp.Body)
Packit 63bb0d
				if errInner != nil {
Packit 63bb0d
					err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
Packit 63bb0d
				} else {
Packit 63bb0d
					*v = bytes
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the
Packit 63bb0d
// response Body into the value pointed to by v.
Packit 63bb0d
func ByUnmarshallingJSON(v interface{}) RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if err == nil {
Packit 63bb0d
				b, errInner := ioutil.ReadAll(resp.Body)
Packit 63bb0d
				// Some responses might include a BOM, remove for successful unmarshalling
Packit 63bb0d
				b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf"))
Packit 63bb0d
				if errInner != nil {
Packit 63bb0d
					err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
Packit 63bb0d
				} else if len(strings.Trim(string(b), " ")) > 0 {
Packit 63bb0d
					errInner = json.Unmarshal(b, v)
Packit 63bb0d
					if errInner != nil {
Packit 63bb0d
						err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b))
Packit 63bb0d
					}
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the
Packit 63bb0d
// response Body into the value pointed to by v.
Packit 63bb0d
func ByUnmarshallingXML(v interface{}) RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if err == nil {
Packit 63bb0d
				b, errInner := ioutil.ReadAll(resp.Body)
Packit 63bb0d
				if errInner != nil {
Packit 63bb0d
					err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
Packit 63bb0d
				} else {
Packit 63bb0d
					errInner = xml.Unmarshal(b, v)
Packit 63bb0d
					if errInner != nil {
Packit 63bb0d
						err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b))
Packit 63bb0d
					}
Packit 63bb0d
				}
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response
Packit 63bb0d
// StatusCode is among the set passed. On error, response body is fully read into a buffer and
Packit 63bb0d
// presented in the returned error, as well as in the response body.
Packit 63bb0d
func WithErrorUnlessStatusCode(codes ...int) RespondDecorator {
Packit 63bb0d
	return func(r Responder) Responder {
Packit 63bb0d
		return ResponderFunc(func(resp *http.Response) error {
Packit 63bb0d
			err := r.Respond(resp)
Packit 63bb0d
			if err == nil && !ResponseHasStatusCode(resp, codes...) {
Packit 63bb0d
				derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s",
Packit 63bb0d
					resp.Request.Method,
Packit 63bb0d
					resp.Request.URL,
Packit 63bb0d
					resp.Status)
Packit 63bb0d
				if resp.Body != nil {
Packit 63bb0d
					defer resp.Body.Close()
Packit 63bb0d
					b, _ := ioutil.ReadAll(resp.Body)
Packit 63bb0d
					derr.ServiceError = b
Packit 63bb0d
					resp.Body = ioutil.NopCloser(bytes.NewReader(b))
Packit 63bb0d
				}
Packit 63bb0d
				err = derr
Packit 63bb0d
			}
Packit 63bb0d
			return err
Packit 63bb0d
		})
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is
Packit 63bb0d
// anything other than HTTP 200.
Packit 63bb0d
func WithErrorUnlessOK() RespondDecorator {
Packit 63bb0d
	return WithErrorUnlessStatusCode(http.StatusOK)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ExtractHeader extracts all values of the specified header from the http.Response. It returns an
Packit 63bb0d
// empty string slice if the passed http.Response is nil or the header does not exist.
Packit 63bb0d
func ExtractHeader(header string, resp *http.Response) []string {
Packit 63bb0d
	if resp != nil && resp.Header != nil {
Packit 63bb0d
		return resp.Header[http.CanonicalHeaderKey(header)]
Packit 63bb0d
	}
Packit 63bb0d
	return nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// ExtractHeaderValue extracts the first value of the specified header from the http.Response. It
Packit 63bb0d
// returns an empty string if the passed http.Response is nil or the header does not exist.
Packit 63bb0d
func ExtractHeaderValue(header string, resp *http.Response) string {
Packit 63bb0d
	h := ExtractHeader(header, resp)
Packit 63bb0d
	if len(h) > 0 {
Packit 63bb0d
		return h[0]
Packit 63bb0d
	}
Packit 63bb0d
	return ""
Packit 63bb0d
}