...

Source file src/github.com/Azure/go-autorest/autorest/preparer.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  	"context"
    20  	"encoding/json"
    21  	"encoding/xml"
    22  	"fmt"
    23  	"io"
    24  	"io/ioutil"
    25  	"mime/multipart"
    26  	"net/http"
    27  	"net/url"
    28  	"strings"
    29  )
    30  
    31  const (
    32  	mimeTypeJSON        = "application/json"
    33  	mimeTypeOctetStream = "application/octet-stream"
    34  	mimeTypeFormPost    = "application/x-www-form-urlencoded"
    35  
    36  	headerAuthorization    = "Authorization"
    37  	headerAuxAuthorization = "x-ms-authorization-auxiliary"
    38  	headerContentType      = "Content-Type"
    39  	headerUserAgent        = "User-Agent"
    40  )
    41  
    42  // used as a key type in context.WithValue()
    43  type ctxPrepareDecorators struct{}
    44  
    45  // WithPrepareDecorators adds the specified PrepareDecorators to the provided context.
    46  // If no PrepareDecorators are provided the context is unchanged.
    47  func WithPrepareDecorators(ctx context.Context, prepareDecorator []PrepareDecorator) context.Context {
    48  	if len(prepareDecorator) == 0 {
    49  		return ctx
    50  	}
    51  	return context.WithValue(ctx, ctxPrepareDecorators{}, prepareDecorator)
    52  }
    53  
    54  // GetPrepareDecorators returns the PrepareDecorators in the provided context or the provided default PrepareDecorators.
    55  func GetPrepareDecorators(ctx context.Context, defaultPrepareDecorators ...PrepareDecorator) []PrepareDecorator {
    56  	inCtx := ctx.Value(ctxPrepareDecorators{})
    57  	if pd, ok := inCtx.([]PrepareDecorator); ok {
    58  		return pd
    59  	}
    60  	return defaultPrepareDecorators
    61  }
    62  
    63  // Preparer is the interface that wraps the Prepare method.
    64  //
    65  // Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations
    66  // must ensure to not share or hold per-invocation state since Preparers may be shared and re-used.
    67  type Preparer interface {
    68  	Prepare(*http.Request) (*http.Request, error)
    69  }
    70  
    71  // PreparerFunc is a method that implements the Preparer interface.
    72  type PreparerFunc func(*http.Request) (*http.Request, error)
    73  
    74  // Prepare implements the Preparer interface on PreparerFunc.
    75  func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) {
    76  	return pf(r)
    77  }
    78  
    79  // PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the
    80  // http.Request and pass it along or, first, pass the http.Request along then affect the result.
    81  type PrepareDecorator func(Preparer) Preparer
    82  
    83  // CreatePreparer creates, decorates, and returns a Preparer.
    84  // Without decorators, the returned Preparer returns the passed http.Request unmodified.
    85  // Preparers are safe to share and re-use.
    86  func CreatePreparer(decorators ...PrepareDecorator) Preparer {
    87  	return DecoratePreparer(
    88  		Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })),
    89  		decorators...)
    90  }
    91  
    92  // DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it
    93  // applies to the Preparer. Decorators are applied in the order received, but their affect upon the
    94  // request depends on whether they are a pre-decorator (change the http.Request and then pass it
    95  // along) or a post-decorator (pass the http.Request along and alter it on return).
    96  func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer {
    97  	for _, decorate := range decorators {
    98  		p = decorate(p)
    99  	}
   100  	return p
   101  }
   102  
   103  // Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators.
   104  // It creates a Preparer from the decorators which it then applies to the passed http.Request.
   105  func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) {
   106  	if r == nil {
   107  		return nil, NewError("autorest", "Prepare", "Invoked without an http.Request")
   108  	}
   109  	return CreatePreparer(decorators...).Prepare(r)
   110  }
   111  
   112  // WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed
   113  // http.Request.
   114  func WithNothing() PrepareDecorator {
   115  	return func(p Preparer) Preparer {
   116  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   117  			return p.Prepare(r)
   118  		})
   119  	}
   120  }
   121  
   122  // WithHeader returns a PrepareDecorator that sets the specified HTTP header of the http.Request to
   123  // the passed value. It canonicalizes the passed header name (via http.CanonicalHeaderKey) before
   124  // adding the header.
   125  func WithHeader(header string, value string) PrepareDecorator {
   126  	return func(p Preparer) Preparer {
   127  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   128  			r, err := p.Prepare(r)
   129  			if err == nil {
   130  				setHeader(r, http.CanonicalHeaderKey(header), value)
   131  			}
   132  			return r, err
   133  		})
   134  	}
   135  }
   136  
   137  // WithHeaders returns a PrepareDecorator that sets the specified HTTP headers of the http.Request to
   138  // the passed value. It canonicalizes the passed headers name (via http.CanonicalHeaderKey) before
   139  // adding them.
   140  func WithHeaders(headers map[string]interface{}) PrepareDecorator {
   141  	h := ensureValueStrings(headers)
   142  	return func(p Preparer) Preparer {
   143  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   144  			r, err := p.Prepare(r)
   145  			if err == nil {
   146  				if r.Header == nil {
   147  					r.Header = make(http.Header)
   148  				}
   149  
   150  				for name, value := range h {
   151  					r.Header.Set(http.CanonicalHeaderKey(name), value)
   152  				}
   153  			}
   154  			return r, err
   155  		})
   156  	}
   157  }
   158  
   159  // WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
   160  // value is "Bearer " followed by the supplied token.
   161  func WithBearerAuthorization(token string) PrepareDecorator {
   162  	return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token))
   163  }
   164  
   165  // AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value
   166  // is the passed contentType.
   167  func AsContentType(contentType string) PrepareDecorator {
   168  	return WithHeader(headerContentType, contentType)
   169  }
   170  
   171  // WithUserAgent returns a PrepareDecorator that adds an HTTP User-Agent header whose value is the
   172  // passed string.
   173  func WithUserAgent(ua string) PrepareDecorator {
   174  	return WithHeader(headerUserAgent, ua)
   175  }
   176  
   177  // AsFormURLEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
   178  // "application/x-www-form-urlencoded".
   179  func AsFormURLEncoded() PrepareDecorator {
   180  	return AsContentType(mimeTypeFormPost)
   181  }
   182  
   183  // AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
   184  // "application/json".
   185  func AsJSON() PrepareDecorator {
   186  	return AsContentType(mimeTypeJSON)
   187  }
   188  
   189  // AsOctetStream returns a PrepareDecorator that adds the "application/octet-stream" Content-Type header.
   190  func AsOctetStream() PrepareDecorator {
   191  	return AsContentType(mimeTypeOctetStream)
   192  }
   193  
   194  // WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The
   195  // decorator does not validate that the passed method string is a known HTTP method.
   196  func WithMethod(method string) PrepareDecorator {
   197  	return func(p Preparer) Preparer {
   198  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   199  			r.Method = method
   200  			return p.Prepare(r)
   201  		})
   202  	}
   203  }
   204  
   205  // AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE.
   206  func AsDelete() PrepareDecorator { return WithMethod("DELETE") }
   207  
   208  // AsGet returns a PrepareDecorator that sets the HTTP method to GET.
   209  func AsGet() PrepareDecorator { return WithMethod("GET") }
   210  
   211  // AsHead returns a PrepareDecorator that sets the HTTP method to HEAD.
   212  func AsHead() PrepareDecorator { return WithMethod("HEAD") }
   213  
   214  // AsMerge returns a PrepareDecorator that sets the HTTP method to MERGE.
   215  func AsMerge() PrepareDecorator { return WithMethod("MERGE") }
   216  
   217  // AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS.
   218  func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") }
   219  
   220  // AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH.
   221  func AsPatch() PrepareDecorator { return WithMethod("PATCH") }
   222  
   223  // AsPost returns a PrepareDecorator that sets the HTTP method to POST.
   224  func AsPost() PrepareDecorator { return WithMethod("POST") }
   225  
   226  // AsPut returns a PrepareDecorator that sets the HTTP method to PUT.
   227  func AsPut() PrepareDecorator { return WithMethod("PUT") }
   228  
   229  // WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed
   230  // from the supplied baseUrl.  Query parameters will be encoded as required.
   231  func WithBaseURL(baseURL string) PrepareDecorator {
   232  	return func(p Preparer) Preparer {
   233  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   234  			r, err := p.Prepare(r)
   235  			if err == nil {
   236  				var u *url.URL
   237  				if u, err = url.Parse(baseURL); err != nil {
   238  					return r, err
   239  				}
   240  				if u.Scheme == "" {
   241  					return r, fmt.Errorf("autorest: No scheme detected in URL %s", baseURL)
   242  				}
   243  				if u.RawQuery != "" {
   244  					// handle unencoded semicolons (ideally the server would send them already encoded)
   245  					u.RawQuery = strings.Replace(u.RawQuery, ";", "%3B", -1)
   246  					q, err := url.ParseQuery(u.RawQuery)
   247  					if err != nil {
   248  						return r, err
   249  					}
   250  					u.RawQuery = q.Encode()
   251  				}
   252  				r.URL = u
   253  			}
   254  			return r, err
   255  		})
   256  	}
   257  }
   258  
   259  // WithBytes returns a PrepareDecorator that takes a list of bytes
   260  // which passes the bytes directly to the body
   261  func WithBytes(input *[]byte) PrepareDecorator {
   262  	return func(p Preparer) Preparer {
   263  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   264  			r, err := p.Prepare(r)
   265  			if err == nil {
   266  				if input == nil {
   267  					return r, fmt.Errorf("Input Bytes was nil")
   268  				}
   269  
   270  				r.ContentLength = int64(len(*input))
   271  				r.Body = ioutil.NopCloser(bytes.NewReader(*input))
   272  			}
   273  			return r, err
   274  		})
   275  	}
   276  }
   277  
   278  // WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the
   279  // request base URL (i.e., http.Request.URL) with the corresponding values from the passed map.
   280  func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator {
   281  	parameters := ensureValueStrings(urlParameters)
   282  	for key, value := range parameters {
   283  		baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1)
   284  	}
   285  	return WithBaseURL(baseURL)
   286  }
   287  
   288  // WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the
   289  // http.Request body.
   290  func WithFormData(v url.Values) PrepareDecorator {
   291  	return func(p Preparer) Preparer {
   292  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   293  			r, err := p.Prepare(r)
   294  			if err == nil {
   295  				s := v.Encode()
   296  
   297  				setHeader(r, http.CanonicalHeaderKey(headerContentType), mimeTypeFormPost)
   298  				r.ContentLength = int64(len(s))
   299  				r.Body = ioutil.NopCloser(strings.NewReader(s))
   300  			}
   301  			return r, err
   302  		})
   303  	}
   304  }
   305  
   306  // WithMultiPartFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) form parameters
   307  // into the http.Request body.
   308  func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDecorator {
   309  	return func(p Preparer) Preparer {
   310  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   311  			r, err := p.Prepare(r)
   312  			if err == nil {
   313  				var body bytes.Buffer
   314  				writer := multipart.NewWriter(&body)
   315  				for key, value := range formDataParameters {
   316  					if rc, ok := value.(io.ReadCloser); ok {
   317  						var fd io.Writer
   318  						if fd, err = writer.CreateFormFile(key, key); err != nil {
   319  							return r, err
   320  						}
   321  						if _, err = io.Copy(fd, rc); err != nil {
   322  							return r, err
   323  						}
   324  					} else {
   325  						if err = writer.WriteField(key, ensureValueString(value)); err != nil {
   326  							return r, err
   327  						}
   328  					}
   329  				}
   330  				if err = writer.Close(); err != nil {
   331  					return r, err
   332  				}
   333  				setHeader(r, http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType())
   334  				r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
   335  				r.ContentLength = int64(body.Len())
   336  				return r, err
   337  			}
   338  			return r, err
   339  		})
   340  	}
   341  }
   342  
   343  // WithFile returns a PrepareDecorator that sends file in request body.
   344  func WithFile(f io.ReadCloser) PrepareDecorator {
   345  	return func(p Preparer) Preparer {
   346  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   347  			r, err := p.Prepare(r)
   348  			if err == nil {
   349  				b, err := ioutil.ReadAll(f)
   350  				if err != nil {
   351  					return r, err
   352  				}
   353  				r.Body = ioutil.NopCloser(bytes.NewReader(b))
   354  				r.ContentLength = int64(len(b))
   355  			}
   356  			return r, err
   357  		})
   358  	}
   359  }
   360  
   361  // WithBool returns a PrepareDecorator that encodes the passed bool into the body of the request
   362  // and sets the Content-Length header.
   363  func WithBool(v bool) PrepareDecorator {
   364  	return WithString(fmt.Sprintf("%v", v))
   365  }
   366  
   367  // WithFloat32 returns a PrepareDecorator that encodes the passed float32 into the body of the
   368  // request and sets the Content-Length header.
   369  func WithFloat32(v float32) PrepareDecorator {
   370  	return WithString(fmt.Sprintf("%v", v))
   371  }
   372  
   373  // WithFloat64 returns a PrepareDecorator that encodes the passed float64 into the body of the
   374  // request and sets the Content-Length header.
   375  func WithFloat64(v float64) PrepareDecorator {
   376  	return WithString(fmt.Sprintf("%v", v))
   377  }
   378  
   379  // WithInt32 returns a PrepareDecorator that encodes the passed int32 into the body of the request
   380  // and sets the Content-Length header.
   381  func WithInt32(v int32) PrepareDecorator {
   382  	return WithString(fmt.Sprintf("%v", v))
   383  }
   384  
   385  // WithInt64 returns a PrepareDecorator that encodes the passed int64 into the body of the request
   386  // and sets the Content-Length header.
   387  func WithInt64(v int64) PrepareDecorator {
   388  	return WithString(fmt.Sprintf("%v", v))
   389  }
   390  
   391  // WithString returns a PrepareDecorator that encodes the passed string into the body of the request
   392  // and sets the Content-Length header.
   393  func WithString(v string) PrepareDecorator {
   394  	return func(p Preparer) Preparer {
   395  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   396  			r, err := p.Prepare(r)
   397  			if err == nil {
   398  				r.ContentLength = int64(len(v))
   399  				r.Body = ioutil.NopCloser(strings.NewReader(v))
   400  			}
   401  			return r, err
   402  		})
   403  	}
   404  }
   405  
   406  // WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the
   407  // request and sets the Content-Length header.
   408  func WithJSON(v interface{}) PrepareDecorator {
   409  	return func(p Preparer) Preparer {
   410  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   411  			r, err := p.Prepare(r)
   412  			if err == nil {
   413  				b, err := json.Marshal(v)
   414  				if err == nil {
   415  					r.ContentLength = int64(len(b))
   416  					r.Body = ioutil.NopCloser(bytes.NewReader(b))
   417  				}
   418  			}
   419  			return r, err
   420  		})
   421  	}
   422  }
   423  
   424  // WithXML returns a PrepareDecorator that encodes the data passed as XML into the body of the
   425  // request and sets the Content-Length header.
   426  func WithXML(v interface{}) PrepareDecorator {
   427  	return func(p Preparer) Preparer {
   428  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   429  			r, err := p.Prepare(r)
   430  			if err == nil {
   431  				b, err := xml.Marshal(v)
   432  				if err == nil {
   433  					// we have to tack on an XML header
   434  					withHeader := xml.Header + string(b)
   435  					bytesWithHeader := []byte(withHeader)
   436  
   437  					r.ContentLength = int64(len(bytesWithHeader))
   438  					setHeader(r, headerContentLength, fmt.Sprintf("%d", len(bytesWithHeader)))
   439  					r.Body = ioutil.NopCloser(bytes.NewReader(bytesWithHeader))
   440  				}
   441  			}
   442  			return r, err
   443  		})
   444  	}
   445  }
   446  
   447  // WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path
   448  // is absolute (that is, it begins with a "/"), it replaces the existing path.
   449  func WithPath(path string) PrepareDecorator {
   450  	return func(p Preparer) Preparer {
   451  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   452  			r, err := p.Prepare(r)
   453  			if err == nil {
   454  				if r.URL == nil {
   455  					return r, NewError("autorest", "WithPath", "Invoked with a nil URL")
   456  				}
   457  				if r.URL, err = parseURL(r.URL, path); err != nil {
   458  					return r, err
   459  				}
   460  			}
   461  			return r, err
   462  		})
   463  	}
   464  }
   465  
   466  // WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
   467  // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The
   468  // values will be escaped (aka URL encoded) before insertion into the path.
   469  func WithEscapedPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
   470  	parameters := escapeValueStrings(ensureValueStrings(pathParameters))
   471  	return func(p Preparer) Preparer {
   472  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   473  			r, err := p.Prepare(r)
   474  			if err == nil {
   475  				if r.URL == nil {
   476  					return r, NewError("autorest", "WithEscapedPathParameters", "Invoked with a nil URL")
   477  				}
   478  				for key, value := range parameters {
   479  					path = strings.Replace(path, "{"+key+"}", value, -1)
   480  				}
   481  				if r.URL, err = parseURL(r.URL, path); err != nil {
   482  					return r, err
   483  				}
   484  			}
   485  			return r, err
   486  		})
   487  	}
   488  }
   489  
   490  // WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
   491  // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map.
   492  func WithPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
   493  	parameters := ensureValueStrings(pathParameters)
   494  	return func(p Preparer) Preparer {
   495  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   496  			r, err := p.Prepare(r)
   497  			if err == nil {
   498  				if r.URL == nil {
   499  					return r, NewError("autorest", "WithPathParameters", "Invoked with a nil URL")
   500  				}
   501  				for key, value := range parameters {
   502  					path = strings.Replace(path, "{"+key+"}", value, -1)
   503  				}
   504  
   505  				if r.URL, err = parseURL(r.URL, path); err != nil {
   506  					return r, err
   507  				}
   508  			}
   509  			return r, err
   510  		})
   511  	}
   512  }
   513  
   514  func parseURL(u *url.URL, path string) (*url.URL, error) {
   515  	p := strings.TrimRight(u.String(), "/")
   516  	if !strings.HasPrefix(path, "/") {
   517  		path = "/" + path
   518  	}
   519  	return url.Parse(p + path)
   520  }
   521  
   522  // WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters
   523  // given in the supplied map (i.e., key=value).
   524  func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator {
   525  	parameters := MapToValues(queryParameters)
   526  	return func(p Preparer) Preparer {
   527  		return PreparerFunc(func(r *http.Request) (*http.Request, error) {
   528  			r, err := p.Prepare(r)
   529  			if err == nil {
   530  				if r.URL == nil {
   531  					return r, NewError("autorest", "WithQueryParameters", "Invoked with a nil URL")
   532  				}
   533  				v := r.URL.Query()
   534  				for key, value := range parameters {
   535  					for i := range value {
   536  						d, err := url.QueryUnescape(value[i])
   537  						if err != nil {
   538  							return r, err
   539  						}
   540  						value[i] = d
   541  					}
   542  					v[key] = value
   543  				}
   544  				r.URL.RawQuery = v.Encode()
   545  			}
   546  			return r, err
   547  		})
   548  	}
   549  }
   550  

View as plain text