preparer.go raw

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