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