1package 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 17import ( 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. 32type Responder interface { 33 Respond(*http.Response) error 34} 35 36// ResponderFunc is a method that implements the Responder interface. 37type ResponderFunc func(*http.Response) error 38 39// Respond implements the Responder interface on ResponderFunc. 40func (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. 46type 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. 55func 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). 65func 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. 74func 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. 83func 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. 93func 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. 109func 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. 126func 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. 142func 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// ByUnmarshallingBytes returns a RespondDecorator that copies the Bytes returned in the 157// response Body into the value pointed to by v. 158func ByUnmarshallingBytes(v *[]byte) 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 bytes, errInner := ioutil.ReadAll(resp.Body) 164 if errInner != nil { 165 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) 166 } else { 167 *v = bytes 168 } 169 } 170 return err 171 }) 172 } 173} 174 175// ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the 176// response Body into the value pointed to by v. 177func ByUnmarshallingJSON(v interface{}) RespondDecorator { 178 return func(r Responder) Responder { 179 return ResponderFunc(func(resp *http.Response) error { 180 err := r.Respond(resp) 181 if err == nil { 182 b, errInner := ioutil.ReadAll(resp.Body) 183 // Some responses might include a BOM, remove for successful unmarshalling 184 b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf")) 185 if errInner != nil { 186 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) 187 } else if len(strings.Trim(string(b), " ")) > 0 { 188 errInner = json.Unmarshal(b, v) 189 if errInner != nil { 190 err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b)) 191 } 192 } 193 } 194 return err 195 }) 196 } 197} 198 199// ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the 200// response Body into the value pointed to by v. 201func ByUnmarshallingXML(v interface{}) RespondDecorator { 202 return func(r Responder) Responder { 203 return ResponderFunc(func(resp *http.Response) error { 204 err := r.Respond(resp) 205 if err == nil { 206 b, errInner := ioutil.ReadAll(resp.Body) 207 if errInner != nil { 208 err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) 209 } else { 210 errInner = xml.Unmarshal(b, v) 211 if errInner != nil { 212 err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b)) 213 } 214 } 215 } 216 return err 217 }) 218 } 219} 220 221// WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response 222// StatusCode is among the set passed. On error, response body is fully read into a buffer and 223// presented in the returned error, as well as in the response body. 224func WithErrorUnlessStatusCode(codes ...int) RespondDecorator { 225 return func(r Responder) Responder { 226 return ResponderFunc(func(resp *http.Response) error { 227 err := r.Respond(resp) 228 if err == nil && !ResponseHasStatusCode(resp, codes...) { 229 derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s", 230 resp.Request.Method, 231 resp.Request.URL, 232 resp.Status) 233 if resp.Body != nil { 234 defer resp.Body.Close() 235 b, _ := ioutil.ReadAll(resp.Body) 236 derr.ServiceError = b 237 resp.Body = ioutil.NopCloser(bytes.NewReader(b)) 238 } 239 err = derr 240 } 241 return err 242 }) 243 } 244} 245 246// WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is 247// anything other than HTTP 200. 248func WithErrorUnlessOK() RespondDecorator { 249 return WithErrorUnlessStatusCode(http.StatusOK) 250} 251 252// ExtractHeader extracts all values of the specified header from the http.Response. It returns an 253// empty string slice if the passed http.Response is nil or the header does not exist. 254func ExtractHeader(header string, resp *http.Response) []string { 255 if resp != nil && resp.Header != nil { 256 return resp.Header[http.CanonicalHeaderKey(header)] 257 } 258 return nil 259} 260 261// ExtractHeaderValue extracts the first value of the specified header from the http.Response. It 262// returns an empty string if the passed http.Response is nil or the header does not exist. 263func ExtractHeaderValue(header string, resp *http.Response) string { 264 h := ExtractHeader(header, resp) 265 if len(h) > 0 { 266 return h[0] 267 } 268 return "" 269} 270