Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / Azure / go-autorest / autorest / responder.go
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         "encoding/json"
20         "encoding/xml"
21         "fmt"
22         "io"
23         "io/ioutil"
24         "net/http"
25         "strings"
26 )
27
28 // Responder is the interface that wraps the Respond method.
29 //
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
34 }
35
36 // ResponderFunc is a method that implements the Responder interface.
37 type ResponderFunc func(*http.Response) error
38
39 // Respond implements the Responder interface on ResponderFunc.
40 func (rf ResponderFunc) Respond(r *http.Response) error {
41         return rf(r)
42 }
43
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
47
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
52 // is not.
53 //
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 })),
58                 decorators...)
59 }
60
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 {
67                 r = decorate(r)
68         }
69         return r
70 }
71
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 {
75         if r == nil {
76                 return nil
77         }
78         return CreateResponder(decorators...).Respond(r)
79 }
80
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)
87                 })
88         }
89 }
90
91 // ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as
92 // the Body is read.
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)
99                         }
100                         return err
101                 })
102         }
103 }
104
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
108 // within the set.
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)
116                                 }
117                         }
118                         return err
119                 })
120         }
121 }
122
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)
133                                 }
134                         }
135                         return err
136                 })
137         }
138 }
139
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)
149                                 }
150                         }
151                         return err
152                 })
153         }
154 }
155
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)
162                         if err == nil {
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"))
166                                 if errInner != nil {
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)
170                                         if errInner != nil {
171                                                 err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b))
172                                         }
173                                 }
174                         }
175                         return err
176                 })
177         }
178 }
179
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)
186                         if err == nil {
187                                 b, errInner := ioutil.ReadAll(resp.Body)
188                                 if errInner != nil {
189                                         err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
190                                 } else {
191                                         errInner = xml.Unmarshal(b, v)
192                                         if errInner != nil {
193                                                 err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b))
194                                         }
195                                 }
196                         }
197                         return err
198                 })
199         }
200 }
201
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",
211                                         resp.Request.Method,
212                                         resp.Request.URL,
213                                         resp.Status)
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))
219                                 }
220                                 err = derr
221                         }
222                         return err
223                 })
224         }
225 }
226
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)
231 }
232
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)]
238         }
239         return nil
240 }
241
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)
246         if len(h) > 0 {
247                 return h[0]
248         }
249         return ""
250 }