...

Source file src/github.com/Azure/go-autorest/autorest/responder.go

Documentation: github.com/Azure/go-autorest/autorest

     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  // ByUnmarshallingBytes returns a RespondDecorator that copies the Bytes returned in the
   157  // response Body into the value pointed to by v.
   158  func 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.
   177  func 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.
   201  func 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.
   224  func 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.
   248  func 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.
   254  func 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.
   263  func 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  

View as plain text