preparer.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. package autorest
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "bytes"
  17. "encoding/json"
  18. "fmt"
  19. "io"
  20. "io/ioutil"
  21. "mime/multipart"
  22. "net/http"
  23. "net/url"
  24. "strings"
  25. )
  26. const (
  27. mimeTypeJSON = "application/json"
  28. mimeTypeOctetStream = "application/octet-stream"
  29. mimeTypeFormPost = "application/x-www-form-urlencoded"
  30. headerAuthorization = "Authorization"
  31. headerContentType = "Content-Type"
  32. headerUserAgent = "User-Agent"
  33. )
  34. // Preparer is the interface that wraps the Prepare method.
  35. //
  36. // Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations
  37. // must ensure to not share or hold per-invocation state since Preparers may be shared and re-used.
  38. type Preparer interface {
  39. Prepare(*http.Request) (*http.Request, error)
  40. }
  41. // PreparerFunc is a method that implements the Preparer interface.
  42. type PreparerFunc func(*http.Request) (*http.Request, error)
  43. // Prepare implements the Preparer interface on PreparerFunc.
  44. func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) {
  45. return pf(r)
  46. }
  47. // PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the
  48. // http.Request and pass it along or, first, pass the http.Request along then affect the result.
  49. type PrepareDecorator func(Preparer) Preparer
  50. // CreatePreparer creates, decorates, and returns a Preparer.
  51. // Without decorators, the returned Preparer returns the passed http.Request unmodified.
  52. // Preparers are safe to share and re-use.
  53. func CreatePreparer(decorators ...PrepareDecorator) Preparer {
  54. return DecoratePreparer(
  55. Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })),
  56. decorators...)
  57. }
  58. // DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it
  59. // applies to the Preparer. Decorators are applied in the order received, but their affect upon the
  60. // request depends on whether they are a pre-decorator (change the http.Request and then pass it
  61. // along) or a post-decorator (pass the http.Request along and alter it on return).
  62. func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer {
  63. for _, decorate := range decorators {
  64. p = decorate(p)
  65. }
  66. return p
  67. }
  68. // Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators.
  69. // It creates a Preparer from the decorators which it then applies to the passed http.Request.
  70. func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) {
  71. if r == nil {
  72. return nil, NewError("autorest", "Prepare", "Invoked without an http.Request")
  73. }
  74. return CreatePreparer(decorators...).Prepare(r)
  75. }
  76. // WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed
  77. // http.Request.
  78. func WithNothing() PrepareDecorator {
  79. return func(p Preparer) Preparer {
  80. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  81. return p.Prepare(r)
  82. })
  83. }
  84. }
  85. // WithHeader returns a PrepareDecorator that sets the specified HTTP header of the http.Request to
  86. // the passed value. It canonicalizes the passed header name (via http.CanonicalHeaderKey) before
  87. // adding the header.
  88. func WithHeader(header string, value string) PrepareDecorator {
  89. return func(p Preparer) Preparer {
  90. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  91. r, err := p.Prepare(r)
  92. if err == nil {
  93. if r.Header == nil {
  94. r.Header = make(http.Header)
  95. }
  96. r.Header.Set(http.CanonicalHeaderKey(header), value)
  97. }
  98. return r, err
  99. })
  100. }
  101. }
  102. // WithHeaders returns a PrepareDecorator that sets the specified HTTP headers of the http.Request to
  103. // the passed value. It canonicalizes the passed headers name (via http.CanonicalHeaderKey) before
  104. // adding them.
  105. func WithHeaders(headers map[string]interface{}) PrepareDecorator {
  106. h := ensureValueStrings(headers)
  107. return func(p Preparer) Preparer {
  108. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  109. r, err := p.Prepare(r)
  110. if err == nil {
  111. if r.Header == nil {
  112. r.Header = make(http.Header)
  113. }
  114. for name, value := range h {
  115. r.Header.Set(http.CanonicalHeaderKey(name), value)
  116. }
  117. }
  118. return r, err
  119. })
  120. }
  121. }
  122. // WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
  123. // value is "Bearer " followed by the supplied token.
  124. func WithBearerAuthorization(token string) PrepareDecorator {
  125. return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token))
  126. }
  127. // AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value
  128. // is the passed contentType.
  129. func AsContentType(contentType string) PrepareDecorator {
  130. return WithHeader(headerContentType, contentType)
  131. }
  132. // WithUserAgent returns a PrepareDecorator that adds an HTTP User-Agent header whose value is the
  133. // passed string.
  134. func WithUserAgent(ua string) PrepareDecorator {
  135. return WithHeader(headerUserAgent, ua)
  136. }
  137. // AsFormURLEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
  138. // "application/x-www-form-urlencoded".
  139. func AsFormURLEncoded() PrepareDecorator {
  140. return AsContentType(mimeTypeFormPost)
  141. }
  142. // AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is
  143. // "application/json".
  144. func AsJSON() PrepareDecorator {
  145. return AsContentType(mimeTypeJSON)
  146. }
  147. // AsOctetStream returns a PrepareDecorator that adds the "application/octet-stream" Content-Type header.
  148. func AsOctetStream() PrepareDecorator {
  149. return AsContentType(mimeTypeOctetStream)
  150. }
  151. // WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The
  152. // decorator does not validate that the passed method string is a known HTTP method.
  153. func WithMethod(method string) PrepareDecorator {
  154. return func(p Preparer) Preparer {
  155. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  156. r.Method = method
  157. return p.Prepare(r)
  158. })
  159. }
  160. }
  161. // AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE.
  162. func AsDelete() PrepareDecorator { return WithMethod("DELETE") }
  163. // AsGet returns a PrepareDecorator that sets the HTTP method to GET.
  164. func AsGet() PrepareDecorator { return WithMethod("GET") }
  165. // AsHead returns a PrepareDecorator that sets the HTTP method to HEAD.
  166. func AsHead() PrepareDecorator { return WithMethod("HEAD") }
  167. // AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS.
  168. func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") }
  169. // AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH.
  170. func AsPatch() PrepareDecorator { return WithMethod("PATCH") }
  171. // AsPost returns a PrepareDecorator that sets the HTTP method to POST.
  172. func AsPost() PrepareDecorator { return WithMethod("POST") }
  173. // AsPut returns a PrepareDecorator that sets the HTTP method to PUT.
  174. func AsPut() PrepareDecorator { return WithMethod("PUT") }
  175. // WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed
  176. // from the supplied baseUrl.
  177. func WithBaseURL(baseURL string) PrepareDecorator {
  178. return func(p Preparer) Preparer {
  179. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  180. r, err := p.Prepare(r)
  181. if err == nil {
  182. var u *url.URL
  183. if u, err = url.Parse(baseURL); err != nil {
  184. return r, err
  185. }
  186. if u.Scheme == "" {
  187. err = fmt.Errorf("autorest: No scheme detected in URL %s", baseURL)
  188. }
  189. if err == nil {
  190. r.URL = u
  191. }
  192. }
  193. return r, err
  194. })
  195. }
  196. }
  197. // WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the
  198. // request base URL (i.e., http.Request.URL) with the corresponding values from the passed map.
  199. func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator {
  200. parameters := ensureValueStrings(urlParameters)
  201. for key, value := range parameters {
  202. baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1)
  203. }
  204. return WithBaseURL(baseURL)
  205. }
  206. // WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the
  207. // http.Request body.
  208. func WithFormData(v url.Values) PrepareDecorator {
  209. return func(p Preparer) Preparer {
  210. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  211. r, err := p.Prepare(r)
  212. if err == nil {
  213. s := v.Encode()
  214. if r.Header == nil {
  215. r.Header = make(http.Header)
  216. }
  217. r.Header.Set(http.CanonicalHeaderKey(headerContentType), mimeTypeFormPost)
  218. r.ContentLength = int64(len(s))
  219. r.Body = ioutil.NopCloser(strings.NewReader(s))
  220. }
  221. return r, err
  222. })
  223. }
  224. }
  225. // WithMultiPartFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) form parameters
  226. // into the http.Request body.
  227. func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDecorator {
  228. return func(p Preparer) Preparer {
  229. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  230. r, err := p.Prepare(r)
  231. if err == nil {
  232. var body bytes.Buffer
  233. writer := multipart.NewWriter(&body)
  234. for key, value := range formDataParameters {
  235. if rc, ok := value.(io.ReadCloser); ok {
  236. var fd io.Writer
  237. if fd, err = writer.CreateFormFile(key, key); err != nil {
  238. return r, err
  239. }
  240. if _, err = io.Copy(fd, rc); err != nil {
  241. return r, err
  242. }
  243. } else {
  244. if err = writer.WriteField(key, ensureValueString(value)); err != nil {
  245. return r, err
  246. }
  247. }
  248. }
  249. if err = writer.Close(); err != nil {
  250. return r, err
  251. }
  252. if r.Header == nil {
  253. r.Header = make(http.Header)
  254. }
  255. r.Header.Set(http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType())
  256. r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
  257. r.ContentLength = int64(body.Len())
  258. return r, err
  259. }
  260. return r, err
  261. })
  262. }
  263. }
  264. // WithFile returns a PrepareDecorator that sends file in request body.
  265. func WithFile(f io.ReadCloser) PrepareDecorator {
  266. return func(p Preparer) Preparer {
  267. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  268. r, err := p.Prepare(r)
  269. if err == nil {
  270. b, err := ioutil.ReadAll(f)
  271. if err != nil {
  272. return r, err
  273. }
  274. r.Body = ioutil.NopCloser(bytes.NewReader(b))
  275. r.ContentLength = int64(len(b))
  276. }
  277. return r, err
  278. })
  279. }
  280. }
  281. // WithBool returns a PrepareDecorator that encodes the passed bool into the body of the request
  282. // and sets the Content-Length header.
  283. func WithBool(v bool) PrepareDecorator {
  284. return WithString(fmt.Sprintf("%v", v))
  285. }
  286. // WithFloat32 returns a PrepareDecorator that encodes the passed float32 into the body of the
  287. // request and sets the Content-Length header.
  288. func WithFloat32(v float32) PrepareDecorator {
  289. return WithString(fmt.Sprintf("%v", v))
  290. }
  291. // WithFloat64 returns a PrepareDecorator that encodes the passed float64 into the body of the
  292. // request and sets the Content-Length header.
  293. func WithFloat64(v float64) PrepareDecorator {
  294. return WithString(fmt.Sprintf("%v", v))
  295. }
  296. // WithInt32 returns a PrepareDecorator that encodes the passed int32 into the body of the request
  297. // and sets the Content-Length header.
  298. func WithInt32(v int32) PrepareDecorator {
  299. return WithString(fmt.Sprintf("%v", v))
  300. }
  301. // WithInt64 returns a PrepareDecorator that encodes the passed int64 into the body of the request
  302. // and sets the Content-Length header.
  303. func WithInt64(v int64) PrepareDecorator {
  304. return WithString(fmt.Sprintf("%v", v))
  305. }
  306. // WithString returns a PrepareDecorator that encodes the passed string into the body of the request
  307. // and sets the Content-Length header.
  308. func WithString(v string) 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. r.ContentLength = int64(len(v))
  314. r.Body = ioutil.NopCloser(strings.NewReader(v))
  315. }
  316. return r, err
  317. })
  318. }
  319. }
  320. // WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the
  321. // request and sets the Content-Length header.
  322. func WithJSON(v interface{}) PrepareDecorator {
  323. return func(p Preparer) Preparer {
  324. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  325. r, err := p.Prepare(r)
  326. if err == nil {
  327. b, err := json.Marshal(v)
  328. if err == nil {
  329. r.ContentLength = int64(len(b))
  330. r.Body = ioutil.NopCloser(bytes.NewReader(b))
  331. }
  332. }
  333. return r, err
  334. })
  335. }
  336. }
  337. // WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path
  338. // is absolute (that is, it begins with a "/"), it replaces the existing path.
  339. func WithPath(path string) PrepareDecorator {
  340. return func(p Preparer) Preparer {
  341. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  342. r, err := p.Prepare(r)
  343. if err == nil {
  344. if r.URL == nil {
  345. return r, NewError("autorest", "WithPath", "Invoked with a nil URL")
  346. }
  347. if r.URL, err = parseURL(r.URL, path); err != nil {
  348. return r, err
  349. }
  350. }
  351. return r, err
  352. })
  353. }
  354. }
  355. // WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
  356. // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The
  357. // values will be escaped (aka URL encoded) before insertion into the path.
  358. func WithEscapedPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
  359. parameters := escapeValueStrings(ensureValueStrings(pathParameters))
  360. return func(p Preparer) Preparer {
  361. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  362. r, err := p.Prepare(r)
  363. if err == nil {
  364. if r.URL == nil {
  365. return r, NewError("autorest", "WithEscapedPathParameters", "Invoked with a nil URL")
  366. }
  367. for key, value := range parameters {
  368. path = strings.Replace(path, "{"+key+"}", value, -1)
  369. }
  370. if r.URL, err = parseURL(r.URL, path); err != nil {
  371. return r, err
  372. }
  373. }
  374. return r, err
  375. })
  376. }
  377. }
  378. // WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the
  379. // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map.
  380. func WithPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator {
  381. parameters := ensureValueStrings(pathParameters)
  382. return func(p Preparer) Preparer {
  383. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  384. r, err := p.Prepare(r)
  385. if err == nil {
  386. if r.URL == nil {
  387. return r, NewError("autorest", "WithPathParameters", "Invoked with a nil URL")
  388. }
  389. for key, value := range parameters {
  390. path = strings.Replace(path, "{"+key+"}", value, -1)
  391. }
  392. if r.URL, err = parseURL(r.URL, path); err != nil {
  393. return r, err
  394. }
  395. }
  396. return r, err
  397. })
  398. }
  399. }
  400. func parseURL(u *url.URL, path string) (*url.URL, error) {
  401. p := strings.TrimRight(u.String(), "/")
  402. if !strings.HasPrefix(path, "/") {
  403. path = "/" + path
  404. }
  405. return url.Parse(p + path)
  406. }
  407. // WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters
  408. // given in the supplied map (i.e., key=value).
  409. func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator {
  410. parameters := ensureValueStrings(queryParameters)
  411. return func(p Preparer) Preparer {
  412. return PreparerFunc(func(r *http.Request) (*http.Request, error) {
  413. r, err := p.Prepare(r)
  414. if err == nil {
  415. if r.URL == nil {
  416. return r, NewError("autorest", "WithQueryParameters", "Invoked with a nil URL")
  417. }
  418. v := r.URL.Query()
  419. for key, value := range parameters {
  420. d, err := url.QueryUnescape(value)
  421. if err != nil {
  422. return r, err
  423. }
  424. v.Add(key, d)
  425. }
  426. r.URL.RawQuery = v.Encode()
  427. }
  428. return r, err
  429. })
  430. }
  431. }