3 // Copyright 2017 Microsoft Corporation
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
28 // Responder is the interface that wraps the Respond method.
30 // Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold
31 // state since Responders may be shared and re-used.
32 type Responder interface {
33 Respond(*http.Response) error
36 // ResponderFunc is a method that implements the Responder interface.
37 type ResponderFunc func(*http.Response) error
39 // Respond implements the Responder interface on ResponderFunc.
40 func (rf ResponderFunc) Respond(r *http.Response) error {
44 // RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to
45 // the http.Response and pass it along or, first, pass the http.Response along then react.
46 type RespondDecorator func(Responder) Responder
48 // CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned
49 // Responder returns the passed http.Response unmodified. Responders may or may not be safe to share
50 // and re-used: It depends on the applied decorators. For example, a standard decorator that closes
51 // the response body is fine to share whereas a decorator that reads the body into a passed struct
54 // To prevent memory leaks, ensure that at least one Responder closes the response body.
55 func CreateResponder(decorators ...RespondDecorator) Responder {
56 return DecorateResponder(
57 Responder(ResponderFunc(func(r *http.Response) error { return nil })),
61 // DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it
62 // applies to the Responder. Decorators are applied in the order received, but their affect upon the
63 // request depends on whether they are a pre-decorator (react to the http.Response and then pass it
64 // along) or a post-decorator (pass the http.Response along and then react).
65 func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder {
66 for _, decorate := range decorators {
72 // Respond accepts an http.Response and a, possibly empty, set of RespondDecorators.
73 // It creates a Responder from the decorators it then applies to the passed http.Response.
74 func Respond(r *http.Response, decorators ...RespondDecorator) error {
78 return CreateResponder(decorators...).Respond(r)
81 // ByIgnoring returns a RespondDecorator that ignores the passed http.Response passing it unexamined
82 // to the next RespondDecorator.
83 func ByIgnoring() RespondDecorator {
84 return func(r Responder) Responder {
85 return ResponderFunc(func(resp *http.Response) error {
86 return r.Respond(resp)
91 // ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as
93 func ByCopying(b *bytes.Buffer) RespondDecorator {
94 return func(r Responder) Responder {
95 return ResponderFunc(func(resp *http.Response) error {
96 err := r.Respond(resp)
97 if err == nil && resp != nil && resp.Body != nil {
98 resp.Body = TeeReadCloser(resp.Body, b)
105 // ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which
106 // it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed
107 // Responder is invoked prior to discarding the response body, the decorator may occur anywhere
109 func ByDiscardingBody() RespondDecorator {
110 return func(r Responder) Responder {
111 return ResponderFunc(func(resp *http.Response) error {
112 err := r.Respond(resp)
113 if err == nil && resp != nil && resp.Body != nil {
114 if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
115 return fmt.Errorf("Error discarding the response body: %v", err)
123 // ByClosing returns a RespondDecorator that first invokes the passed Responder after which it
124 // closes the response body. Since the passed Responder is invoked prior to closing the response
125 // body, the decorator may occur anywhere within the set.
126 func ByClosing() RespondDecorator {
127 return func(r Responder) Responder {
128 return ResponderFunc(func(resp *http.Response) error {
129 err := r.Respond(resp)
130 if resp != nil && resp.Body != nil {
131 if err := resp.Body.Close(); err != nil {
132 return fmt.Errorf("Error closing the response body: %v", err)
140 // ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which
141 // it closes the response if the passed Responder returns an error and the response body exists.
142 func ByClosingIfError() RespondDecorator {
143 return func(r Responder) Responder {
144 return ResponderFunc(func(resp *http.Response) error {
145 err := r.Respond(resp)
146 if err != nil && resp != nil && resp.Body != nil {
147 if err := resp.Body.Close(); err != nil {
148 return fmt.Errorf("Error closing the response body: %v", err)
156 // ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the
157 // response Body into the value pointed to by v.
158 func ByUnmarshallingJSON(v interface{}) RespondDecorator {
159 return func(r Responder) Responder {
160 return ResponderFunc(func(resp *http.Response) error {
161 err := r.Respond(resp)
163 b, errInner := ioutil.ReadAll(resp.Body)
164 // Some responses might include a BOM, remove for successful unmarshalling
165 b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf"))
167 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
168 } else if len(strings.Trim(string(b), " ")) > 0 {
169 errInner = json.Unmarshal(b, v)
171 err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b))
180 // ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the
181 // response Body into the value pointed to by v.
182 func ByUnmarshallingXML(v interface{}) RespondDecorator {
183 return func(r Responder) Responder {
184 return ResponderFunc(func(resp *http.Response) error {
185 err := r.Respond(resp)
187 b, errInner := ioutil.ReadAll(resp.Body)
189 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
191 errInner = xml.Unmarshal(b, v)
193 err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b))
202 // WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response
203 // StatusCode is among the set passed. On error, response body is fully read into a buffer and
204 // presented in the returned error, as well as in the response body.
205 func WithErrorUnlessStatusCode(codes ...int) RespondDecorator {
206 return func(r Responder) Responder {
207 return ResponderFunc(func(resp *http.Response) error {
208 err := r.Respond(resp)
209 if err == nil && !ResponseHasStatusCode(resp, codes...) {
210 derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s",
214 if resp.Body != nil {
215 defer resp.Body.Close()
216 b, _ := ioutil.ReadAll(resp.Body)
217 derr.ServiceError = b
218 resp.Body = ioutil.NopCloser(bytes.NewReader(b))
227 // WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is
228 // anything other than HTTP 200.
229 func WithErrorUnlessOK() RespondDecorator {
230 return WithErrorUnlessStatusCode(http.StatusOK)
233 // ExtractHeader extracts all values of the specified header from the http.Response. It returns an
234 // empty string slice if the passed http.Response is nil or the header does not exist.
235 func ExtractHeader(header string, resp *http.Response) []string {
236 if resp != nil && resp.Header != nil {
237 return resp.Header[http.CanonicalHeaderKey(header)]
242 // ExtractHeaderValue extracts the first value of the specified header from the http.Response. It
243 // returns an empty string if the passed http.Response is nil or the header does not exist.
244 func ExtractHeaderValue(header string, resp *http.Response) string {
245 h := ExtractHeader(header, resp)