3 // Copyright 2017 Microsoft Corporation
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
30 mimeTypeJSON = "application/json"
31 mimeTypeOctetStream = "application/octet-stream"
32 mimeTypeFormPost = "application/x-www-form-urlencoded"
34 headerAuthorization = "Authorization"
35 headerContentType = "Content-Type"
36 headerUserAgent = "User-Agent"
39 // Preparer is the interface that wraps the Prepare method.
41 // Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations
42 // must ensure to not share or hold per-invocation state since Preparers may be shared and re-used.
43 type Preparer interface {
44 Prepare(*http.Request) (*http.Request, error)
47 // PreparerFunc is a method that implements the Preparer interface.
48 type PreparerFunc func(*http.Request) (*http.Request, error)
50 // Prepare implements the Preparer interface on PreparerFunc.
51 func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) {
55 // PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the
56 // http.Request and pass it along or, first, pass the http.Request along then affect the result.
57 type PrepareDecorator func(Preparer) Preparer
59 // CreatePreparer creates, decorates, and returns a Preparer.
60 // Without decorators, the returned Preparer returns the passed http.Request unmodified.
61 // Preparers are safe to share and re-use.
62 func CreatePreparer(decorators ...PrepareDecorator) Preparer {
63 return DecoratePreparer(
64 Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })),
68 // DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it
69 // applies to the Preparer. Decorators are applied in the order received, but their affect upon the
70 // request depends on whether they are a pre-decorator (change the http.Request and then pass it
71 // along) or a post-decorator (pass the http.Request along and alter it on return).
72 func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer {
73 for _, decorate := range decorators {
79 // Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators.
80 // It creates a Preparer from the decorators which it then applies to the passed http.Request.
81 func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) {
83 return nil, NewError("autorest", "Prepare", "Invoked without an http.Request")
85 return CreatePreparer(decorators...).Prepare(r)
88 // WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed
90 func WithNothing() PrepareDecorator {
91 return func(p Preparer) Preparer {
92 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
98 // WithHeader returns a PrepareDecorator that sets the specified HTTP header of the http.Request to
99 // the passed value. It canonicalizes the passed header name (via http.CanonicalHeaderKey) before
100 // adding the header.
101 func WithHeader(header string, value string) PrepareDecorator {
102 return func(p Preparer) Preparer {
103 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
104 r, err := p.Prepare(r)
107 r.Header = make(http.Header)
109 r.Header.Set(http.CanonicalHeaderKey(header), value)
116 // WithHeaders returns a PrepareDecorator that sets the specified HTTP headers of the http.Request to
117 // the passed value. It canonicalizes the passed headers name (via http.CanonicalHeaderKey) before
119 func WithHeaders(headers map[string]interface{}) PrepareDecorator {
120 h := ensureValueStrings(headers)
121 return func(p Preparer) Preparer {
122 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
123 r, err := p.Prepare(r)
126 r.Header = make(http.Header)
129 for name, value := range h {
130 r.Header.Set(http.CanonicalHeaderKey(name), value)
138 // WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
139 // value is "Bearer " followed by the supplied token.
140 func WithBearerAuthorization(token string) PrepareDecorator {
141 return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token))
144 // AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value
145 // is the passed contentType.
146 func AsContentType(contentType string) PrepareDecorator {
147 return WithHeader(headerContentType, contentType)
150 // WithUserAgent returns a PrepareDecorator that adds an HTTP User-Agent header whose value is the
152 func WithUserAgent(ua string) PrepareDecorator {
153 return WithHeader(headerUserAgent, ua)
156 // AsFormURLEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
157 // "application/x-www-form-urlencoded".
158 func AsFormURLEncoded() PrepareDecorator {
159 return AsContentType(mimeTypeFormPost)
162 // AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
163 // "application/json".
164 func AsJSON() PrepareDecorator {
165 return AsContentType(mimeTypeJSON)
168 // AsOctetStream returns a PrepareDecorator that adds the "application/octet-stream" Content-Type header.
169 func AsOctetStream() PrepareDecorator {
170 return AsContentType(mimeTypeOctetStream)
173 // WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The
174 // decorator does not validate that the passed method string is a known HTTP method.
175 func WithMethod(method string) PrepareDecorator {
176 return func(p Preparer) Preparer {
177 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
184 // AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE.
185 func AsDelete() PrepareDecorator { return WithMethod("DELETE") }
187 // AsGet returns a PrepareDecorator that sets the HTTP method to GET.
188 func AsGet() PrepareDecorator { return WithMethod("GET") }
190 // AsHead returns a PrepareDecorator that sets the HTTP method to HEAD.
191 func AsHead() PrepareDecorator { return WithMethod("HEAD") }
193 // AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS.
194 func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") }
196 // AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH.
197 func AsPatch() PrepareDecorator { return WithMethod("PATCH") }
199 // AsPost returns a PrepareDecorator that sets the HTTP method to POST.
200 func AsPost() PrepareDecorator { return WithMethod("POST") }
202 // AsPut returns a PrepareDecorator that sets the HTTP method to PUT.
203 func AsPut() PrepareDecorator { return WithMethod("PUT") }
205 // WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed
206 // from the supplied baseUrl.
207 func WithBaseURL(baseURL string) PrepareDecorator {
208 return func(p Preparer) Preparer {
209 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
210 r, err := p.Prepare(r)
213 if u, err = url.Parse(baseURL); err != nil {
217 err = fmt.Errorf("autorest: No scheme detected in URL %s", baseURL)
228 // WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the
229 // request base URL (i.e., http.Request.URL) with the corresponding values from the passed map.
230 func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator {
231 parameters := ensureValueStrings(urlParameters)
232 for key, value := range parameters {
233 baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1)
235 return WithBaseURL(baseURL)
238 // WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the
239 // http.Request body.
240 func WithFormData(v url.Values) PrepareDecorator {
241 return func(p Preparer) Preparer {
242 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
243 r, err := p.Prepare(r)
248 r.Header = make(http.Header)
250 r.Header.Set(http.CanonicalHeaderKey(headerContentType), mimeTypeFormPost)
251 r.ContentLength = int64(len(s))
252 r.Body = ioutil.NopCloser(strings.NewReader(s))
259 // WithMultiPartFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) form parameters
260 // into the http.Request body.
261 func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDecorator {
262 return func(p Preparer) Preparer {
263 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
264 r, err := p.Prepare(r)
266 var body bytes.Buffer
267 writer := multipart.NewWriter(&body)
268 for key, value := range formDataParameters {
269 if rc, ok := value.(io.ReadCloser); ok {
271 if fd, err = writer.CreateFormFile(key, key); err != nil {
274 if _, err = io.Copy(fd, rc); err != nil {
278 if err = writer.WriteField(key, ensureValueString(value)); err != nil {
283 if err = writer.Close(); err != nil {
287 r.Header = make(http.Header)
289 r.Header.Set(http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType())
290 r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
291 r.ContentLength = int64(body.Len())
299 // WithFile returns a PrepareDecorator that sends file in request body.
300 func WithFile(f io.ReadCloser) PrepareDecorator {
301 return func(p Preparer) Preparer {
302 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
303 r, err := p.Prepare(r)
305 b, err := ioutil.ReadAll(f)
309 r.Body = ioutil.NopCloser(bytes.NewReader(b))
310 r.ContentLength = int64(len(b))
317 // WithBool returns a PrepareDecorator that encodes the passed bool into the body of the request
318 // and sets the Content-Length header.
319 func WithBool(v bool) PrepareDecorator {
320 return WithString(fmt.Sprintf("%v", v))
323 // WithFloat32 returns a PrepareDecorator that encodes the passed float32 into the body of the
324 // request and sets the Content-Length header.
325 func WithFloat32(v float32) PrepareDecorator {
326 return WithString(fmt.Sprintf("%v", v))
329 // WithFloat64 returns a PrepareDecorator that encodes the passed float64 into the body of the
330 // request and sets the Content-Length header.
331 func WithFloat64(v float64) PrepareDecorator {
332 return WithString(fmt.Sprintf("%v", v))
335 // WithInt32 returns a PrepareDecorator that encodes the passed int32 into the body of the request
336 // and sets the Content-Length header.
337 func WithInt32(v int32) PrepareDecorator {
338 return WithString(fmt.Sprintf("%v", v))
341 // WithInt64 returns a PrepareDecorator that encodes the passed int64 into the body of the request
342 // and sets the Content-Length header.
343 func WithInt64(v int64) PrepareDecorator {
344 return WithString(fmt.Sprintf("%v", v))
347 // WithString returns a PrepareDecorator that encodes the passed string into the body of the request
348 // and sets the Content-Length header.
349 func WithString(v string) PrepareDecorator {
350 return func(p Preparer) Preparer {
351 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
352 r, err := p.Prepare(r)
354 r.ContentLength = int64(len(v))
355 r.Body = ioutil.NopCloser(strings.NewReader(v))
362 // WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the
363 // request and sets the Content-Length header.
364 func WithJSON(v interface{}) PrepareDecorator {
365 return func(p Preparer) Preparer {
366 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
367 r, err := p.Prepare(r)
369 b, err := json.Marshal(v)
371 r.ContentLength = int64(len(b))
372 r.Body = ioutil.NopCloser(bytes.NewReader(b))
380 // WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path
381 // is absolute (that is, it begins with a "/"), it replaces the existing path.
382 func WithPath(path string) PrepareDecorator {
383 return func(p Preparer) Preparer {
384 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
385 r, err := p.Prepare(r)
388 return r, NewError("autorest", "WithPath", "Invoked with a nil URL")
390 if r.URL, err = parseURL(r.URL, path); err != nil {
399 // WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
400 // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The
401 // values will be escaped (aka URL encoded) before insertion into the path.
402 func WithEscapedPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
403 parameters := escapeValueStrings(ensureValueStrings(pathParameters))
404 return func(p Preparer) Preparer {
405 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
406 r, err := p.Prepare(r)
409 return r, NewError("autorest", "WithEscapedPathParameters", "Invoked with a nil URL")
411 for key, value := range parameters {
412 path = strings.Replace(path, "{"+key+"}", value, -1)
414 if r.URL, err = parseURL(r.URL, path); err != nil {
423 // WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
424 // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map.
425 func WithPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
426 parameters := ensureValueStrings(pathParameters)
427 return func(p Preparer) Preparer {
428 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
429 r, err := p.Prepare(r)
432 return r, NewError("autorest", "WithPathParameters", "Invoked with a nil URL")
434 for key, value := range parameters {
435 path = strings.Replace(path, "{"+key+"}", value, -1)
438 if r.URL, err = parseURL(r.URL, path); err != nil {
447 func parseURL(u *url.URL, path string) (*url.URL, error) {
448 p := strings.TrimRight(u.String(), "/")
449 if !strings.HasPrefix(path, "/") {
452 return url.Parse(p + path)
455 // WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters
456 // given in the supplied map (i.e., key=value).
457 func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator {
458 parameters := ensureValueStrings(queryParameters)
459 return func(p Preparer) Preparer {
460 return PreparerFunc(func(r *http.Request) (*http.Request, error) {
461 r, err := p.Prepare(r)
464 return r, NewError("autorest", "WithQueryParameters", "Invoked with a nil URL")
468 for key, value := range parameters {
469 d, err := url.QueryUnescape(value)
475 r.URL.RawQuery = v.Encode()