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 "net/http"
24 "strings"
25 )
26 27 // Responder is the interface that wraps the Respond method.
28 //
29 // Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold
30 // state since Responders may be shared and re-used.
31 type Responder interface {
32 Respond(*http.Response) error
33 }
34 35 // ResponderFunc is a method that implements the Responder interface.
36 type ResponderFunc func(*http.Response) error
37 38 // Respond implements the Responder interface on ResponderFunc.
39 func (rf ResponderFunc) Respond(r *http.Response) error {
40 return rf(r)
41 }
42 43 // RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to
44 // the http.Response and pass it along or, first, pass the http.Response along then react.
45 type RespondDecorator func(Responder) Responder
46 47 // CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned
48 // Responder returns the passed http.Response unmodified. Responders may or may not be safe to share
49 // and re-used: It depends on the applied decorators. For example, a standard decorator that closes
50 // the response body is fine to share whereas a decorator that reads the body into a passed struct
51 // is not.
52 //
53 // To prevent memory leaks, ensure that at least one Responder closes the response body.
54 func CreateResponder(decorators ...RespondDecorator) Responder {
55 return DecorateResponder(
56 Responder(ResponderFunc(func(r *http.Response) error { return nil })),
57 decorators...)
58 }
59 60 // DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it
61 // applies to the Responder. Decorators are applied in the order received, but their affect upon the
62 // request depends on whether they are a pre-decorator (react to the http.Response and then pass it
63 // along) or a post-decorator (pass the http.Response along and then react).
64 func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder {
65 for _, decorate := range decorators {
66 r = decorate(r)
67 }
68 return r
69 }
70 71 // Respond accepts an http.Response and a, possibly empty, set of RespondDecorators.
72 // It creates a Responder from the decorators it then applies to the passed http.Response.
73 func Respond(r *http.Response, decorators ...RespondDecorator) error {
74 if r == nil {
75 return nil
76 }
77 return CreateResponder(decorators...).Respond(r)
78 }
79 80 // ByIgnoring returns a RespondDecorator that ignores the passed http.Response passing it unexamined
81 // to the next RespondDecorator.
82 func ByIgnoring() RespondDecorator {
83 return func(r Responder) Responder {
84 return ResponderFunc(func(resp *http.Response) error {
85 return r.Respond(resp)
86 })
87 }
88 }
89 90 // ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as
91 // the Body is read.
92 func ByCopying(b *bytes.Buffer) RespondDecorator {
93 return func(r Responder) Responder {
94 return ResponderFunc(func(resp *http.Response) error {
95 err := r.Respond(resp)
96 if err == nil && resp != nil && resp.Body != nil {
97 resp.Body = TeeReadCloser(resp.Body, b)
98 }
99 return err
100 })
101 }
102 }
103 104 // ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which
105 // it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed
106 // Responder is invoked prior to discarding the response body, the decorator may occur anywhere
107 // within the set.
108 func ByDiscardingBody() RespondDecorator {
109 return func(r Responder) Responder {
110 return ResponderFunc(func(resp *http.Response) error {
111 err := r.Respond(resp)
112 if err == nil && resp != nil && resp.Body != nil {
113 if _, err := io.Copy(io.Discard, resp.Body); err != nil {
114 return fmt.Errorf("Error discarding the response body: %v", err)
115 }
116 }
117 return err
118 })
119 }
120 }
121 122 // ByClosing returns a RespondDecorator that first invokes the passed Responder after which it
123 // closes the response body. Since the passed Responder is invoked prior to closing the response
124 // body, the decorator may occur anywhere within the set.
125 func ByClosing() RespondDecorator {
126 return func(r Responder) Responder {
127 return ResponderFunc(func(resp *http.Response) error {
128 err := r.Respond(resp)
129 if resp != nil && resp.Body != nil {
130 if err := resp.Body.Close(); err != nil {
131 return fmt.Errorf("Error closing the response body: %v", err)
132 }
133 }
134 return err
135 })
136 }
137 }
138 139 // ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which
140 // it closes the response if the passed Responder returns an error and the response body exists.
141 func ByClosingIfError() RespondDecorator {
142 return func(r Responder) Responder {
143 return ResponderFunc(func(resp *http.Response) error {
144 err := r.Respond(resp)
145 if err != nil && resp != nil && resp.Body != nil {
146 if err := resp.Body.Close(); err != nil {
147 return fmt.Errorf("Error closing the response body: %v", err)
148 }
149 }
150 return err
151 })
152 }
153 }
154 155 // ByUnmarshallingBytes returns a RespondDecorator that copies the Bytes returned in the
156 // response Body into the value pointed to by v.
157 func ByUnmarshallingBytes(v *[]byte) RespondDecorator {
158 return func(r Responder) Responder {
159 return ResponderFunc(func(resp *http.Response) error {
160 err := r.Respond(resp)
161 if err == nil {
162 bytes, errInner := io.ReadAll(resp.Body)
163 if errInner != nil {
164 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
165 } else {
166 *v = bytes
167 }
168 }
169 return err
170 })
171 }
172 }
173 174 // ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the
175 // response Body into the value pointed to by v.
176 func ByUnmarshallingJSON(v interface{}) RespondDecorator {
177 return func(r Responder) Responder {
178 return ResponderFunc(func(resp *http.Response) error {
179 err := r.Respond(resp)
180 if err == nil {
181 b, errInner := io.ReadAll(resp.Body)
182 // Some responses might include a BOM, remove for successful unmarshalling
183 b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf"))
184 if errInner != nil {
185 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
186 } else if len(strings.Trim(string(b), " ")) > 0 {
187 errInner = json.Unmarshal(b, v)
188 if errInner != nil {
189 err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b))
190 }
191 }
192 }
193 return err
194 })
195 }
196 }
197 198 // ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the
199 // response Body into the value pointed to by v.
200 func ByUnmarshallingXML(v interface{}) RespondDecorator {
201 return func(r Responder) Responder {
202 return ResponderFunc(func(resp *http.Response) error {
203 err := r.Respond(resp)
204 if err == nil {
205 b, errInner := io.ReadAll(resp.Body)
206 if errInner != nil {
207 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner)
208 } else {
209 errInner = xml.Unmarshal(b, v)
210 if errInner != nil {
211 err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b))
212 }
213 }
214 }
215 return err
216 })
217 }
218 }
219 220 // WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response
221 // StatusCode is among the set passed. On error, response body is fully read into a buffer and
222 // presented in the returned error, as well as in the response body.
223 func WithErrorUnlessStatusCode(codes ...int) RespondDecorator {
224 return func(r Responder) Responder {
225 return ResponderFunc(func(resp *http.Response) error {
226 err := r.Respond(resp)
227 if err == nil && !ResponseHasStatusCode(resp, codes...) {
228 derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s",
229 resp.Request.Method,
230 resp.Request.URL,
231 resp.Status)
232 if resp.Body != nil {
233 defer resp.Body.Close()
234 b, _ := io.ReadAll(resp.Body)
235 derr.ServiceError = b
236 resp.Body = io.NopCloser(bytes.NewReader(b))
237 }
238 err = derr
239 }
240 return err
241 })
242 }
243 }
244 245 // WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is
246 // anything other than HTTP 200.
247 func WithErrorUnlessOK() RespondDecorator {
248 return WithErrorUnlessStatusCode(http.StatusOK)
249 }
250 251 // ExtractHeader extracts all values of the specified header from the http.Response. It returns an
252 // empty string slice if the passed http.Response is nil or the header does not exist.
253 func ExtractHeader(header string, resp *http.Response) []string {
254 if resp != nil && resp.Header != nil {
255 return resp.Header[http.CanonicalHeaderKey(header)]
256 }
257 return nil
258 }
259 260 // ExtractHeaderValue extracts the first value of the specified header from the http.Response. It
261 // returns an empty string if the passed http.Response is nil or the header does not exist.
262 func ExtractHeaderValue(header string, resp *http.Response) string {
263 h := ExtractHeader(header, resp)
264 if len(h) > 0 {
265 return h[0]
266 }
267 return ""
268 }
269