mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
glide update
This commit is contained in:
26
vendor/github.com/go-resty/resty/.gitignore
generated
vendored
Normal file
26
vendor/github.com/go-resty/resty/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
coverage.out
|
||||
31
vendor/github.com/go-resty/resty/.travis.yml
generated
vendored
Normal file
31
vendor/github.com/go-resty/resty/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
language: go
|
||||
|
||||
sudo: false
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- integration
|
||||
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- tip
|
||||
|
||||
install:
|
||||
- go get -v -t ./...
|
||||
- go get -v golang.org/x/tools/cmd/cover
|
||||
|
||||
script:
|
||||
- go test ./... -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
21
vendor/github.com/go-resty/resty/LICENSE
generated
vendored
Normal file
21
vendor/github.com/go-resty/resty/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Jeevanandam M., https://myjeeva.com <jeeva@myjeeva.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
648
vendor/github.com/go-resty/resty/README.md
generated
vendored
Normal file
648
vendor/github.com/go-resty/resty/README.md
generated
vendored
Normal file
@@ -0,0 +1,648 @@
|
||||
# resty [](https://travis-ci.org/go-resty/resty) [](https://codecov.io/gh/go-resty/resty/branch/master) [](https://goreportcard.com/report/go-resty/resty) [](https://github.com/go-resty/resty/releases/latest) [](https://godoc.org/github.com/go-resty/resty) [](LICENSE)
|
||||
|
||||
Simple HTTP and REST client for Go inspired by Ruby rest-client. [Features](#features) section describes in detail about resty capabilities.
|
||||
|
||||
***v0.13 [released](https://github.com/go-resty/resty/releases/latest) and tagged on Jun 22, 2017.***
|
||||
|
||||
*Since Go v1.6 HTTP/2 & HTTP/1.1 protocol is used transparently. `Resty` works fine with HTTP/2 and HTTP/1.1.*
|
||||
|
||||
#### Roadmap
|
||||
***v1.0***
|
||||
|
||||
Go Resty first released on Sep 15, 2015 then go-resty grew gradually as a very handy and helpful library of HTTP & REST Client in the golang community. I'm planning to freeze API and make v1.0 release.
|
||||
|
||||
#### Features
|
||||
* GET, POST, PUT, DELETE, HEAD, PATCH and OPTIONS
|
||||
* Simple and chainable methods for settings and request
|
||||
* Request Body can be `string`, `[]byte`, `struct`, `map`, `slice` and `io.Reader` too
|
||||
* Auto detects `Content-Type`
|
||||
* [Response](https://godoc.org/github.com/go-resty/resty#Response) object gives you more possibility
|
||||
* Access as `[]byte` array - `response.Body()` OR Access as `string` - `response.String()`
|
||||
* Know your `response.Time()` and when we `response.ReceivedAt()`
|
||||
* Automatic marshal and unmarshal for `JSON` and `XML` content type
|
||||
* Default is `JSON`, if you supply `struct/map` without header `Content-Type`
|
||||
* For auto-unmarshal, refer to -
|
||||
- Success scenario [Request.SetResult()](https://godoc.org/gopkg.in/resty.v0#Request.SetResult) and [Response.Result()](https://godoc.org/gopkg.in/resty.v0#Response.Result).
|
||||
- Error scenario [Request.SetError()](https://godoc.org/gopkg.in/resty.v0#Request.SetError) and [Response.Error()](https://godoc.org/gopkg.in/resty.v0#Response.Error).
|
||||
* Easy to upload one or more file(s) via `multipart/form-data`
|
||||
* Backoff Retry Mechanism with retry condition function [reference](retry_test.go)
|
||||
* resty client HTTP & REST [Request](https://godoc.org/github.com/go-resty/resty#Client.OnBeforeRequest) and [Response](https://godoc.org/github.com/go-resty/resty#Client.OnAfterResponse) middlewares
|
||||
* `Request.SetContext` supported `go1.7` and above
|
||||
* Authorization option of `BasicAuth` and `Bearer` token
|
||||
* Set request `ContentLength` value for all request or particular request
|
||||
* Choose between HTTP and REST mode. Default is `REST`
|
||||
* `HTTP` - default up to 10 redirects and no automatic response unmarshal
|
||||
* `REST` - defaults to no redirects and automatic response marshal/unmarshal for `JSON` & `XML`
|
||||
* Custom [Root Certificates](https://godoc.org/github.com/go-resty/resty#Client.SetRootCertificate) and Client [Certificates](https://godoc.org/github.com/go-resty/resty#Client.SetCertificates)
|
||||
* Download/Save HTTP response directly into File, like `curl -o` flag. See [SetOutputDirectory](https://godoc.org/github.com/go-resty/resty#Client.SetOutputDirectory) & [SetOutput](https://godoc.org/github.com/go-resty/resty#Request.SetOutput).
|
||||
* Cookies for your request and CookieJar support
|
||||
* SRV Record based request instead of Host URL
|
||||
* Client settings like `Timeout`, `RedirectPolicy`, `Proxy`, `TLSClientConfig`, `Transport`, etc.
|
||||
* Optionally allows GET request with payload, see [SetAllowGetMethodPayload](https://godoc.org/github.com/go-resty/resty#Client.SetAllowGetMethodPayload)
|
||||
* resty design
|
||||
* Have client level settings & options and also override at Request level if you want to
|
||||
* Request and Response middlewares
|
||||
* Create Multiple clients if you want to `resty.New()`
|
||||
* goroutine concurrent safe
|
||||
* REST and HTTP modes
|
||||
* Debug mode - clean and informative logging presentation
|
||||
* Gzip - I'm not doing anything here. Go does it automatically
|
||||
* Well tested client library
|
||||
|
||||
resty tested with Go `v1.3` and above.
|
||||
|
||||
#### Included Batteries
|
||||
* Redirect Policies - see [how to use](#redirect-policy)
|
||||
* NoRedirectPolicy
|
||||
* FlexibleRedirectPolicy
|
||||
* DomainCheckRedirectPolicy
|
||||
* etc. [more info](redirect.go)
|
||||
* Retry Mechanism [how to use](#retries)
|
||||
* Backoff Retry
|
||||
* Conditional Retry
|
||||
* SRV Record based request instead of Host URL [how to use](resty_test.go#L1412)
|
||||
* etc (upcoming - throw your idea's [here](https://github.com/go-resty/resty/issues)).
|
||||
|
||||
## Installation
|
||||
#### Stable Version - Production Ready
|
||||
Please refer section [Versioning](#versioning) for detailed info.
|
||||
```sh
|
||||
# install the library
|
||||
go get -u gopkg.in/resty.v0
|
||||
```
|
||||
#### Latest Version - Development Edge
|
||||
```sh
|
||||
# install the latest & greatest library
|
||||
go get -u github.com/go-resty/resty
|
||||
```
|
||||
|
||||
## It might interest you :)
|
||||
|
||||
Resty author also published following projects to Go Community.
|
||||
|
||||
* [aah framework](https://aahframework.org) - Web and API framework for Go.
|
||||
* [go-model](https://github.com/jeevatkm/go-model) - Robust & Easy to use model mapper and utility methods for Go `struct`.
|
||||
|
||||
## Usage
|
||||
The following samples will assist you to become as comfortable as possible with resty library. Resty comes with ready to use DefaultClient.
|
||||
|
||||
Import resty into your code and refer it as `resty`.
|
||||
```go
|
||||
import (
|
||||
"gopkg.in/resty.v0"
|
||||
)
|
||||
```
|
||||
|
||||
#### Simple GET
|
||||
```go
|
||||
// GET request
|
||||
resp, err := resty.R().Get("http://httpbin.org/get")
|
||||
|
||||
// explore response object
|
||||
fmt.Printf("\nError: %v", err)
|
||||
fmt.Printf("\nResponse Status Code: %v", resp.StatusCode())
|
||||
fmt.Printf("\nResponse Status: %v", resp.Status())
|
||||
fmt.Printf("\nResponse Time: %v", resp.Time())
|
||||
fmt.Printf("\nResponse Recevied At: %v", resp.ReceivedAt())
|
||||
fmt.Printf("\nResponse Body: %v", resp) // or resp.String() or string(resp.Body())
|
||||
// more...
|
||||
|
||||
/* Output
|
||||
Error: <nil>
|
||||
Response Status Code: 200
|
||||
Response Status: 200 OK
|
||||
Response Time: 644.290186ms
|
||||
Response Recevied At: 2015-09-15 12:05:28.922780103 -0700 PDT
|
||||
Response Body: {
|
||||
"args": {},
|
||||
"headers": {
|
||||
"Accept-Encoding": "gzip",
|
||||
"Host": "httpbin.org",
|
||||
"User-Agent": "go-resty v0.1 - https://github.com/go-resty/resty"
|
||||
},
|
||||
"origin": "0.0.0.0",
|
||||
"url": "http://httpbin.org/get"
|
||||
}
|
||||
*/
|
||||
```
|
||||
#### Enhanced GET
|
||||
```go
|
||||
resp, err := resty.R().
|
||||
SetQueryParams(map[string]string{
|
||||
"page_no": "1",
|
||||
"limit": "20",
|
||||
"sort":"name",
|
||||
"order": "asc",
|
||||
"random":strconv.FormatInt(time.Now().Unix(), 10),
|
||||
}).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
|
||||
Get("/search_result")
|
||||
|
||||
|
||||
// Sample of using Request.SetQueryString method
|
||||
resp, err := resty.R().
|
||||
SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more").
|
||||
SetHeader("Accept", "application/json").
|
||||
SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
|
||||
Get("/show_product")
|
||||
```
|
||||
|
||||
#### Various POST method combinations
|
||||
```go
|
||||
// POST JSON string
|
||||
// No need to set content type, if you have client level setting
|
||||
resp, err := resty.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(`{"username":"testuser", "password":"testpass"}`).
|
||||
SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
// POST []byte array
|
||||
// No need to set content type, if you have client level setting
|
||||
resp, err := resty.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody([]byte(`{"username":"testuser", "password":"testpass"}`)).
|
||||
SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
// POST Struct, default is JSON content type. No need to set one
|
||||
resp, err := resty.R().
|
||||
SetBody(User{Username: "testuser", Password: "testpass"}).
|
||||
SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}).
|
||||
SetError(&AuthError{}). // or SetError(AuthError{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
// POST Map, default is JSON content type. No need to set one
|
||||
resp, err := resty.R().
|
||||
SetBody(map[string]interface{}{"username": "testuser", "password": "testpass"}).
|
||||
SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}).
|
||||
SetError(&AuthError{}). // or SetError(AuthError{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
// POST of raw bytes for file upload. For example: upload file to Dropbox
|
||||
fileBytes, _ := ioutil.ReadFile("/Users/jeeva/mydocument.pdf")
|
||||
|
||||
// See we are not setting content-type header, since go-resty automatically detects Content-Type for you
|
||||
resp, err := resty.R().
|
||||
SetBody(fileBytes).
|
||||
SetContentLength(true). // Dropbox expects this value
|
||||
SetAuthToken("<your-auth-token>").
|
||||
SetError(&DropboxError{}). // or SetError(DropboxError{}).
|
||||
Post("https://content.dropboxapi.com/1/files_put/auto/resty/mydocument.pdf") // for upload Dropbox supports PUT too
|
||||
|
||||
// Note: resty detects Content-Type for request body/payload if content type header is not set.
|
||||
// * For struct and map data type defaults to 'application/json'
|
||||
// * Fallback is plain text content type
|
||||
```
|
||||
|
||||
#### Sample PUT
|
||||
You can use various combinations of `PUT` method call like demonstrated for `POST`.
|
||||
```go
|
||||
// Note: This is one sample of PUT method usage, refer POST for more combination
|
||||
|
||||
// Request goes as JSON content type
|
||||
// No need to set auth token, error, if you have client level settings
|
||||
resp, err := resty.R().
|
||||
SetBody(Article{
|
||||
Title: "go-resty",
|
||||
Content: "This is my article content, oh ya!",
|
||||
Author: "Jeevanandam M",
|
||||
Tags: []string{"article", "sample", "resty"},
|
||||
}).
|
||||
SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
|
||||
SetError(&Error{}). // or SetError(Error{}).
|
||||
Put("https://myapp.com/article/1234")
|
||||
```
|
||||
|
||||
#### Sample PATCH
|
||||
You can use various combinations of `PATCH` method call like demonstrated for `POST`.
|
||||
```go
|
||||
// Note: This is one sample of PUT method usage, refer POST for more combination
|
||||
|
||||
// Request goes as JSON content type
|
||||
// No need to set auth token, error, if you have client level settings
|
||||
resp, err := resty.R().
|
||||
SetBody(Article{
|
||||
Tags: []string{"new tag1", "new tag2"},
|
||||
}).
|
||||
SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
|
||||
SetError(&Error{}). // or SetError(Error{}).
|
||||
Patch("https://myapp.com/articles/1234")
|
||||
```
|
||||
|
||||
#### Sample DELETE, HEAD, OPTIONS
|
||||
```go
|
||||
// DELETE a article
|
||||
// No need to set auth token, error, if you have client level settings
|
||||
resp, err := resty.R().
|
||||
SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
|
||||
SetError(&Error{}). // or SetError(Error{}).
|
||||
Delete("https://myapp.com/articles/1234")
|
||||
|
||||
// DELETE a articles with payload/body as a JSON string
|
||||
// No need to set auth token, error, if you have client level settings
|
||||
resp, err := resty.R().
|
||||
SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
|
||||
SetError(&Error{}). // or SetError(Error{}).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(`{article_ids: [1002, 1006, 1007, 87683, 45432] }`).
|
||||
Delete("https://myapp.com/articles")
|
||||
|
||||
// HEAD of resource
|
||||
// No need to set auth token, if you have client level settings
|
||||
resp, err := resty.R().
|
||||
SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
|
||||
Head("https://myapp.com/videos/hi-res-video")
|
||||
|
||||
// OPTIONS of resource
|
||||
// No need to set auth token, if you have client level settings
|
||||
resp, err := resty.R().
|
||||
SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
|
||||
Options("https://myapp.com/servers/nyc-dc-01")
|
||||
```
|
||||
|
||||
### Multipart File(s) upload
|
||||
#### Using io.Reader
|
||||
```go
|
||||
profileImgBytes, _ := ioutil.ReadFile("/Users/jeeva/test-img.png")
|
||||
notesBytes, _ := ioutil.ReadFile("/Users/jeeva/text-file.txt")
|
||||
|
||||
resp, err := dclr().
|
||||
SetFileReader("profile_img", "test-img.png", bytes.NewReader(profileImgBytes)).
|
||||
SetFileReader("notes", "text-file.txt", bytes.NewReader(notesBytes)).
|
||||
SetFormData(map[string]string{
|
||||
"first_name": "Jeevanandam",
|
||||
"last_name": "M",
|
||||
}).
|
||||
Post(t"http://myapp.com/upload")
|
||||
```
|
||||
|
||||
#### Using File directly from Path
|
||||
```go
|
||||
// Single file scenario
|
||||
resp, err := resty.R().
|
||||
SetFile("profile_img", "/Users/jeeva/test-img.png").
|
||||
Post("http://myapp.com/upload")
|
||||
|
||||
// Multiple files scenario
|
||||
resp, err := resty.R().
|
||||
SetFiles(map[string]string{
|
||||
"profile_img": "/Users/jeeva/test-img.png",
|
||||
"notes": "/Users/jeeva/text-file.txt",
|
||||
}).
|
||||
Post("http://myapp.com/upload")
|
||||
|
||||
// Multipart of form fields and files
|
||||
resp, err := resty.R().
|
||||
SetFiles(map[string]string{
|
||||
"profile_img": "/Users/jeeva/test-img.png",
|
||||
"notes": "/Users/jeeva/text-file.txt",
|
||||
}).
|
||||
SetFormData(map[string]string{
|
||||
"first_name": "Jeevanandam",
|
||||
"last_name": "M",
|
||||
"zip_code": "00001",
|
||||
"city": "my city",
|
||||
"access_token": "C6A79608-782F-4ED0-A11D-BD82FAD829CD",
|
||||
}).
|
||||
Post("http://myapp.com/profile")
|
||||
```
|
||||
|
||||
#### Sample Form submision
|
||||
```go
|
||||
// just mentioning about POST as an example with simple flow
|
||||
// User Login
|
||||
resp, err := resty.R().
|
||||
SetFormData(map[string]string{
|
||||
"username": "jeeva",
|
||||
"password": "mypass",
|
||||
}).
|
||||
Post("http://myapp.com/login")
|
||||
|
||||
// Followed by profile update
|
||||
resp, err := resty.R().
|
||||
SetFormData(map[string]string{
|
||||
"first_name": "Jeevanandam",
|
||||
"last_name": "M",
|
||||
"zip_code": "00001",
|
||||
"city": "new city update",
|
||||
}).
|
||||
Post("http://myapp.com/profile")
|
||||
|
||||
// Multi value form data
|
||||
criteria := url.Values{
|
||||
"search_criteria": []string{"book", "glass", "pencil"},
|
||||
}
|
||||
resp, err := resty.R().
|
||||
SetMultiValueFormData(criteria).
|
||||
Post("http://myapp.com/search")
|
||||
```
|
||||
|
||||
#### Save HTTP Response into File
|
||||
```go
|
||||
// Setting output directory path, If directory not exists then resty creates one!
|
||||
// This is optional one, if you're planning using absoule path in
|
||||
// `Request.SetOutput` and can used together.
|
||||
resty.SetOutputDirectory("/Users/jeeva/Downloads")
|
||||
|
||||
// HTTP response gets saved into file, similar to curl -o flag
|
||||
_, err := resty.R().
|
||||
SetOutput("plugin/ReplyWithHeader-v5.1-beta.zip").
|
||||
Get("http://bit.ly/1LouEKr")
|
||||
|
||||
// OR using absolute path
|
||||
// Note: output directory path is not used for absoulte path
|
||||
_, err := resty.R().
|
||||
SetOutput("/MyDownloads/plugin/ReplyWithHeader-v5.1-beta.zip").
|
||||
Get("http://bit.ly/1LouEKr")
|
||||
```
|
||||
|
||||
#### Request and Response Middleware
|
||||
Resty provides middleware ability to manipulate for Request and Response. It is more flexible than callback approach.
|
||||
```go
|
||||
// Registering Request Middleware
|
||||
resty.OnBeforeRequest(func(c *resty.Client, req *resty.Request) error {
|
||||
// Now you have access to Client and current Request object
|
||||
// manipulate it as per your need
|
||||
|
||||
return nil // if its success otherwise return error
|
||||
})
|
||||
|
||||
// Registering Response Middleware
|
||||
resty.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error {
|
||||
// Now you have access to Client and current Response object
|
||||
// manipulate it as per your need
|
||||
|
||||
return nil // if its success otherwise return error
|
||||
})
|
||||
```
|
||||
|
||||
#### Redirect Policy
|
||||
Resty provides few ready to use redirect policy(s) also it supports multiple policies together.
|
||||
```go
|
||||
// Assign Client Redirect Policy. Create one as per you need
|
||||
resty.SetRedirectPolicy(resty.FlexibleRedirectPolicy(15))
|
||||
|
||||
// Wanna multiple policies such as redirect count, domain name check, etc
|
||||
resty.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20),
|
||||
resty.DomainCheckRedirectPolicy("host1.com", "host2.org", "host3.net"))
|
||||
```
|
||||
|
||||
##### Custom Redirect Policy
|
||||
Implement [RedirectPolicy](redirect.go#L20) interface and register it with resty client. Have a look [redirect.go](redirect.go) for more information.
|
||||
```go
|
||||
// Using raw func into resty.SetRedirectPolicy
|
||||
resty.SetRedirectPolicy(resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||
// Implement your logic here
|
||||
|
||||
// return nil for continue redirect otherwise return error to stop/prevent redirect
|
||||
return nil
|
||||
}))
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
// Using struct create more flexible redirect policy
|
||||
type CustomRedirectPolicy struct {
|
||||
// variables goes here
|
||||
}
|
||||
|
||||
func (c *CustomRedirectPolicy) Apply(req *http.Request, via []*http.Request) error {
|
||||
// Implement your logic here
|
||||
|
||||
// return nil for continue redirect otherwise return error to stop/prevent redirect
|
||||
return nil
|
||||
}
|
||||
|
||||
// Registering in resty
|
||||
resty.SetRedirectPolicy(CustomRedirectPolicy{/* initialize variables */})
|
||||
```
|
||||
|
||||
#### Custom Root Certificates and Client Certifcates
|
||||
```go
|
||||
// Custom Root certificates, just supply .pem file.
|
||||
// you can add one or more root certificates, its get appended
|
||||
resty.SetRootCertificate("/path/to/root/pemFile1.pem")
|
||||
resty.SetRootCertificate("/path/to/root/pemFile2.pem")
|
||||
// ... and so on!
|
||||
|
||||
// Adding Client Certificates, you add one or more certificates
|
||||
// Sample for creating certificate object
|
||||
// Parsing public/private key pair from a pair of files. The files must contain PEM encoded data.
|
||||
cert1, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")
|
||||
if err != nil {
|
||||
log.Fatalf("ERROR client certificate: %s", err)
|
||||
}
|
||||
// ...
|
||||
|
||||
// You add one or more certificates
|
||||
resty.SetCertificates(cert1, cert2, cert3)
|
||||
```
|
||||
|
||||
#### Proxy Settings - Client as well as at Request Level
|
||||
Default `Go` supports Proxy via environment variable `HTTP_PROXY`. Resty provides support via `SetProxy` & `RemoveProxy`.
|
||||
Choose as per your need.
|
||||
|
||||
**Client Level Proxy** settings applied to all the request
|
||||
```go
|
||||
// Setting a Proxy URL and Port
|
||||
resty.SetProxy("http://proxyserver:8888")
|
||||
|
||||
// Want to remove proxy setting
|
||||
resty.RemoveProxy()
|
||||
```
|
||||
**Request Level Proxy** settings, gives control to override at individal request level
|
||||
```go
|
||||
// Set proxy for current request
|
||||
resp, err := c.R().
|
||||
SetProxy("http://sampleproxy:8888").
|
||||
Get("http://httpbin.org/get")
|
||||
```
|
||||
|
||||
#### Retries
|
||||
|
||||
Resty uses [backoff](http://www.awsarchitectureblog.com/2015/03/backoff.html)
|
||||
to increase retry intervals after each attempt.
|
||||
|
||||
Usage example:
|
||||
```go
|
||||
// Retries are configured per client
|
||||
resty.DefaultClient.
|
||||
// Set retry count to non zero to enable retries
|
||||
SetRetryCount(3).
|
||||
// You can override initial retry wait time.
|
||||
// Default is 100 milliseconds.
|
||||
SetRetryWaitTime(5 * time.Second).
|
||||
// MaxWaitTime can be overridden as well.
|
||||
// Default is 2 seconds.
|
||||
SetRetryMaxWaitTime(20 * time.Second)
|
||||
```
|
||||
|
||||
Above setup will result in resty retrying requests returned non nil error up to
|
||||
3 times with delay increased after each attempt.
|
||||
|
||||
You can optionally provide client with custom retry conditions:
|
||||
|
||||
```go
|
||||
resty.DefaultClient.
|
||||
AddRetryCondition(
|
||||
// Condition function will be provided with *resty.Response as a
|
||||
// parameter. It is expected to return (bool, error) pair. Resty will retry
|
||||
// in case condition returns true or non nil error.
|
||||
func(r *resty.Response) (bool, error) {
|
||||
return r.StatusCode() == http.StatusTooManyRequests, nil
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Above example will make resty retry requests ended with `429 Too Many Requests`
|
||||
status code.
|
||||
|
||||
Multiple retry conditions can be added.
|
||||
|
||||
It is also possible to use `resty.Backoff(...)` to get arbitrary retry scenarios
|
||||
implemented. [Reference](retry_test.go).
|
||||
|
||||
#### Choose REST or HTTP mode
|
||||
```go
|
||||
// REST mode. This is Default.
|
||||
resty.SetRESTMode()
|
||||
|
||||
// HTTP mode
|
||||
resty.SetHTTPMode()
|
||||
```
|
||||
|
||||
#### Allow GET request with Payload
|
||||
```go
|
||||
// Allow GET request with Payload. This is disabled by default.
|
||||
resty.SetAllowGetMethodPayload(true)
|
||||
```
|
||||
|
||||
#### Wanna Multiple Clients
|
||||
```go
|
||||
// Here you go!
|
||||
// Client 1
|
||||
client1 := resty.New()
|
||||
client1.R().Get("http://httpbin.org")
|
||||
// ...
|
||||
|
||||
// Client 2
|
||||
client2 := resty.New()
|
||||
client1.R().Head("http://httpbin.org")
|
||||
// ...
|
||||
|
||||
// Bend it as per your need!!!
|
||||
```
|
||||
|
||||
#### Remaining Client Settings & its Options
|
||||
```go
|
||||
// Unique settings at Client level
|
||||
//--------------------------------
|
||||
// Enable debug mode
|
||||
resty.SetDebug(true)
|
||||
|
||||
// Using you custom log writer
|
||||
logFile, _ := os.OpenFile("/Users/jeeva/go-resty.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
resty.SetLogger(logFile)
|
||||
|
||||
// Assign Client TLSClientConfig
|
||||
// One can set custom root-certificate. Refer: http://golang.org/pkg/crypto/tls/#example_Dial
|
||||
resty.SetTLSClientConfig(&tls.Config{ RootCAs: roots })
|
||||
|
||||
// or One can disable security check (https)
|
||||
resty.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })
|
||||
|
||||
// Set client timeout as per your need
|
||||
resty.SetTimeout(time.Duration(1 * time.Minute))
|
||||
|
||||
|
||||
// You can override all below settings and options at request level if you want to
|
||||
//--------------------------------------------------------------------------------
|
||||
// Host URL for all request. So you can use relative URL in the request
|
||||
resty.SetHostURL("http://httpbin.org")
|
||||
|
||||
// Headers for all request
|
||||
resty.SetHeader("Accept", "application/json")
|
||||
resty.SetHeaders(map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "My custom User Agent String",
|
||||
})
|
||||
|
||||
// Cookies for all request
|
||||
resty.SetCookie(&http.Cookie{
|
||||
Name:"go-resty",
|
||||
Value:"This is cookie value",
|
||||
Path: "/",
|
||||
Domain: "sample.com",
|
||||
MaxAge: 36000,
|
||||
HttpOnly: true,
|
||||
Secure: false,
|
||||
})
|
||||
resty.SetCookies(cookies)
|
||||
|
||||
// URL query parameters for all request
|
||||
resty.SetQueryParam("user_id", "00001")
|
||||
resty.SetQueryParams(map[string]string{ // sample of those who use this manner
|
||||
"api_key": "api-key-here",
|
||||
"api_secert": "api-secert",
|
||||
})
|
||||
resty.R().SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more")
|
||||
|
||||
// Form data for all request. Typically used with POST and PUT
|
||||
resty.SetFormData(map[string]string{
|
||||
"access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
|
||||
})
|
||||
|
||||
// Basic Auth for all request
|
||||
resty.SetBasicAuth("myuser", "mypass")
|
||||
|
||||
// Bearer Auth Token for all request
|
||||
resty.SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")
|
||||
|
||||
// Enabling Content length value for all request
|
||||
resty.SetContentLength(true)
|
||||
|
||||
// Registering global Error object structure for JSON/XML request
|
||||
resty.SetError(&Error{}) // or resty.SetError(Error{})
|
||||
```
|
||||
|
||||
#### Unix Socket
|
||||
|
||||
```go
|
||||
unixSocket := "/var/run/my_socket.sock"
|
||||
|
||||
// Create a Go's http.Transport so we can set it in resty.
|
||||
transport := http.Transport{
|
||||
Dial: func(_, _ string) (net.Conn, error) {
|
||||
return net.Dial("unix", unixSocket)
|
||||
},
|
||||
}
|
||||
|
||||
// Set the previous transport that we created, set the scheme of the communication to the
|
||||
// socket and set the unixSocket as the HostURL.
|
||||
r := resty.New().SetTransport(&transport).SetScheme("http").SetHostURL(unixSocket)
|
||||
|
||||
// No need to write the host's URL on the request, just the path.
|
||||
r.R().Get("/index.html")
|
||||
|
||||
```
|
||||
|
||||
## Versioning
|
||||
resty releases versions according to [Semantic Versioning](http://semver.org)
|
||||
|
||||
`gopkg.in/resty.vX` points to appropriate tag versions; `X` denotes version number and it's a stable release. It's recommended to use version, for eg. `gopkg.in/resty.v0`. Development takes place at the master branch. Although the code in master should always compile and test successfully, it might break API's. We aim to maintain backwards compatibility, but API's and behaviour might be changed to fix a bug.
|
||||
|
||||
|
||||
## Contributing
|
||||
Welcome! If you find any improvement or issue you want to fix, feel free to send a pull request, I like pull requests that include test cases for fix/enhancement. I have done my best to bring pretty good code coverage. Feel free to write tests.
|
||||
|
||||
BTW, I'd like to know what you think about go-resty. Kindly open an issue or send me an email; it'd mean a lot to me.
|
||||
|
||||
## Author
|
||||
Jeevanandam M. - jeeva@myjeeva.com
|
||||
|
||||
## Contributors
|
||||
Have a look on [Contributors](https://github.com/go-resty/resty/graphs/contributors) page.
|
||||
|
||||
## License
|
||||
resty released under MIT license, refer [LICENSE](LICENSE) file.
|
||||
926
vendor/github.com/go-resty/resty/client.go
generated
vendored
Normal file
926
vendor/github.com/go-resty/resty/client.go
generated
vendored
Normal file
@@ -0,0 +1,926 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// MethodGet HTTP method
|
||||
MethodGet = "GET"
|
||||
|
||||
// MethodPost HTTP method
|
||||
MethodPost = "POST"
|
||||
|
||||
// MethodPut HTTP method
|
||||
MethodPut = "PUT"
|
||||
|
||||
// MethodDelete HTTP method
|
||||
MethodDelete = "DELETE"
|
||||
|
||||
// MethodPatch HTTP method
|
||||
MethodPatch = "PATCH"
|
||||
|
||||
// MethodHead HTTP method
|
||||
MethodHead = "HEAD"
|
||||
|
||||
// MethodOptions HTTP method
|
||||
MethodOptions = "OPTIONS"
|
||||
)
|
||||
|
||||
var (
|
||||
hdrUserAgentKey = http.CanonicalHeaderKey("User-Agent")
|
||||
hdrAcceptKey = http.CanonicalHeaderKey("Accept")
|
||||
hdrContentTypeKey = http.CanonicalHeaderKey("Content-Type")
|
||||
hdrContentLengthKey = http.CanonicalHeaderKey("Content-Length")
|
||||
hdrAuthorizationKey = http.CanonicalHeaderKey("Authorization")
|
||||
|
||||
plainTextType = "text/plain; charset=utf-8"
|
||||
jsonContentType = "application/json; charset=utf-8"
|
||||
formContentType = "application/x-www-form-urlencoded"
|
||||
|
||||
jsonCheck = regexp.MustCompile("(?i:[application|text]/json)")
|
||||
xmlCheck = regexp.MustCompile("(?i:[application|text]/xml)")
|
||||
|
||||
hdrUserAgentValue = "go-resty v%s - https://github.com/go-resty/resty"
|
||||
bufPool = &sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}
|
||||
)
|
||||
|
||||
// Client type is used for HTTP/RESTful global values
|
||||
// for all request raised from the client
|
||||
type Client struct {
|
||||
HostURL string
|
||||
QueryParam url.Values
|
||||
FormData url.Values
|
||||
Header http.Header
|
||||
UserInfo *User
|
||||
Token string
|
||||
Cookies []*http.Cookie
|
||||
Error reflect.Type
|
||||
Debug bool
|
||||
DisableWarn bool
|
||||
AllowGetMethodPayload bool
|
||||
Log *log.Logger
|
||||
RetryCount int
|
||||
RetryWaitTime time.Duration
|
||||
RetryMaxWaitTime time.Duration
|
||||
RetryConditions []RetryConditionFunc
|
||||
JSONMarshal func(v interface{}) ([]byte, error)
|
||||
JSONUnmarshal func(data []byte, v interface{}) error
|
||||
|
||||
httpClient *http.Client
|
||||
transport *http.Transport
|
||||
setContentLength bool
|
||||
isHTTPMode bool
|
||||
outputDirectory string
|
||||
scheme string
|
||||
proxyURL *url.URL
|
||||
closeConnection bool
|
||||
beforeRequest []func(*Client, *Request) error
|
||||
udBeforeRequest []func(*Client, *Request) error
|
||||
preReqHook func(*Client, *Request) error
|
||||
afterResponse []func(*Client, *Response) error
|
||||
}
|
||||
|
||||
// User type is to hold an username and password information
|
||||
type User struct {
|
||||
Username, Password string
|
||||
}
|
||||
|
||||
// SetHostURL method is to set Host URL in the client instance. It will be used with request
|
||||
// raised from this client with relative URL
|
||||
// // Setting HTTP address
|
||||
// resty.SetHostURL("http://myjeeva.com")
|
||||
//
|
||||
// // Setting HTTPS address
|
||||
// resty.SetHostURL("https://myjeeva.com")
|
||||
//
|
||||
func (c *Client) SetHostURL(url string) *Client {
|
||||
c.HostURL = strings.TrimRight(url, "/")
|
||||
return c
|
||||
}
|
||||
|
||||
// SetHeader method sets a single header field and its value in the client instance.
|
||||
// These headers will be applied to all requests raised from this client instance.
|
||||
// Also it can be overridden at request level header options, see `resty.R().SetHeader`
|
||||
// or `resty.R().SetHeaders`.
|
||||
//
|
||||
// Example: To set `Content-Type` and `Accept` as `application/json`
|
||||
//
|
||||
// resty.
|
||||
// SetHeader("Content-Type", "application/json").
|
||||
// SetHeader("Accept", "application/json")
|
||||
//
|
||||
func (c *Client) SetHeader(header, value string) *Client {
|
||||
c.Header.Set(header, value)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetHeaders method sets multiple headers field and its values at one go in the client instance.
|
||||
// These headers will be applied to all requests raised from this client instance. Also it can be
|
||||
// overridden at request level headers options, see `resty.R().SetHeaders` or `resty.R().SetHeader`.
|
||||
//
|
||||
// Example: To set `Content-Type` and `Accept` as `application/json`
|
||||
//
|
||||
// resty.SetHeaders(map[string]string{
|
||||
// "Content-Type": "application/json",
|
||||
// "Accept": "application/json",
|
||||
// })
|
||||
//
|
||||
func (c *Client) SetHeaders(headers map[string]string) *Client {
|
||||
for h, v := range headers {
|
||||
c.Header.Set(h, v)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetCookieJar method sets custom http.CookieJar in the resty client. Its way to override default.
|
||||
// Example: sometimes we don't want to save cookies in api contacting, we can remove the default
|
||||
// CookieJar in resty client.
|
||||
//
|
||||
// resty.SetCookieJar(nil)
|
||||
//
|
||||
func (c *Client) SetCookieJar(jar http.CookieJar) *Client {
|
||||
c.httpClient.Jar = jar
|
||||
return c
|
||||
}
|
||||
|
||||
// SetCookie method appends a single cookie in the client instance.
|
||||
// These cookies will be added to all the request raised from this client instance.
|
||||
// resty.SetCookie(&http.Cookie{
|
||||
// Name:"go-resty",
|
||||
// Value:"This is cookie value",
|
||||
// Path: "/",
|
||||
// Domain: "sample.com",
|
||||
// MaxAge: 36000,
|
||||
// HttpOnly: true,
|
||||
// Secure: false,
|
||||
// })
|
||||
//
|
||||
func (c *Client) SetCookie(hc *http.Cookie) *Client {
|
||||
c.Cookies = append(c.Cookies, hc)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetCookies method sets an array of cookies in the client instance.
|
||||
// These cookies will be added to all the request raised from this client instance.
|
||||
// cookies := make([]*http.Cookie, 0)
|
||||
//
|
||||
// cookies = append(cookies, &http.Cookie{
|
||||
// Name:"go-resty-1",
|
||||
// Value:"This is cookie 1 value",
|
||||
// Path: "/",
|
||||
// Domain: "sample.com",
|
||||
// MaxAge: 36000,
|
||||
// HttpOnly: true,
|
||||
// Secure: false,
|
||||
// })
|
||||
//
|
||||
// cookies = append(cookies, &http.Cookie{
|
||||
// Name:"go-resty-2",
|
||||
// Value:"This is cookie 2 value",
|
||||
// Path: "/",
|
||||
// Domain: "sample.com",
|
||||
// MaxAge: 36000,
|
||||
// HttpOnly: true,
|
||||
// Secure: false,
|
||||
// })
|
||||
//
|
||||
// // Setting a cookies into resty
|
||||
// resty.SetCookies(cookies)
|
||||
//
|
||||
func (c *Client) SetCookies(cs []*http.Cookie) *Client {
|
||||
c.Cookies = append(c.Cookies, cs...)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetQueryParam method sets single parameter and its value in the client instance.
|
||||
// It will be formed as query string for the request. For example: `search=kitchen%20papers&size=large`
|
||||
// in the URL after `?` mark. These query params will be added to all the request raised from
|
||||
// this client instance. Also it can be overridden at request level Query Param options,
|
||||
// see `resty.R().SetQueryParam` or `resty.R().SetQueryParams`.
|
||||
// resty.
|
||||
// SetQueryParam("search", "kitchen papers").
|
||||
// SetQueryParam("size", "large")
|
||||
//
|
||||
func (c *Client) SetQueryParam(param, value string) *Client {
|
||||
c.QueryParam.Set(param, value)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetQueryParams method sets multiple parameters and their values at one go in the client instance.
|
||||
// It will be formed as query string for the request. For example: `search=kitchen%20papers&size=large`
|
||||
// in the URL after `?` mark. These query params will be added to all the request raised from this
|
||||
// client instance. Also it can be overridden at request level Query Param options,
|
||||
// see `resty.R().SetQueryParams` or `resty.R().SetQueryParam`.
|
||||
// resty.SetQueryParams(map[string]string{
|
||||
// "search": "kitchen papers",
|
||||
// "size": "large",
|
||||
// })
|
||||
//
|
||||
func (c *Client) SetQueryParams(params map[string]string) *Client {
|
||||
for p, v := range params {
|
||||
c.SetQueryParam(p, v)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetFormData method sets Form parameters and their values in the client instance.
|
||||
// It's applicable only HTTP method `POST` and `PUT` and requets content type would be set as
|
||||
// `application/x-www-form-urlencoded`. These form data will be added to all the request raised from
|
||||
// this client instance. Also it can be overridden at request level form data, see `resty.R().SetFormData`.
|
||||
// resty.SetFormData(map[string]string{
|
||||
// "access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
|
||||
// "user_id": "3455454545",
|
||||
// })
|
||||
//
|
||||
func (c *Client) SetFormData(data map[string]string) *Client {
|
||||
for k, v := range data {
|
||||
c.FormData.Set(k, v)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetBasicAuth method sets the basic authentication header in the HTTP request. Example:
|
||||
// Authorization: Basic <base64-encoded-value>
|
||||
//
|
||||
// Example: To set the header for username "go-resty" and password "welcome"
|
||||
// resty.SetBasicAuth("go-resty", "welcome")
|
||||
//
|
||||
// This basic auth information gets added to all the request rasied from this client instance.
|
||||
// Also it can be overridden or set one at the request level is supported, see `resty.R().SetBasicAuth`.
|
||||
//
|
||||
func (c *Client) SetBasicAuth(username, password string) *Client {
|
||||
c.UserInfo = &User{Username: username, Password: password}
|
||||
return c
|
||||
}
|
||||
|
||||
// SetAuthToken method sets bearer auth token header in the HTTP request. Example:
|
||||
// Authorization: Bearer <auth-token-value-comes-here>
|
||||
//
|
||||
// Example: To set auth token BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F
|
||||
//
|
||||
// resty.SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")
|
||||
//
|
||||
// This bearer auth token gets added to all the request rasied from this client instance.
|
||||
// Also it can be overridden or set one at the request level is supported, see `resty.R().SetAuthToken`.
|
||||
//
|
||||
func (c *Client) SetAuthToken(token string) *Client {
|
||||
c.Token = token
|
||||
return c
|
||||
}
|
||||
|
||||
// R method creates a request instance, its used for Get, Post, Put, Delete, Patch, Head and Options.
|
||||
func (c *Client) R() *Request {
|
||||
r := &Request{
|
||||
URL: "",
|
||||
Method: "",
|
||||
QueryParam: url.Values{},
|
||||
FormData: url.Values{},
|
||||
Header: http.Header{},
|
||||
Body: nil,
|
||||
Result: nil,
|
||||
Error: nil,
|
||||
RawRequest: nil,
|
||||
client: c,
|
||||
bodyBuf: nil,
|
||||
multipartFiles: []*File{},
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// OnBeforeRequest method appends request middleware into the before request chain.
|
||||
// Its gets applied after default `go-resty` request middlewares and before request
|
||||
// been sent from `go-resty` to host server.
|
||||
// resty.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {
|
||||
// // Now you have access to Client and Request instance
|
||||
// // manipulate it as per your need
|
||||
//
|
||||
// return nil // if its success otherwise return error
|
||||
// })
|
||||
//
|
||||
func (c *Client) OnBeforeRequest(m func(*Client, *Request) error) *Client {
|
||||
c.udBeforeRequest = append(c.udBeforeRequest, m)
|
||||
return c
|
||||
}
|
||||
|
||||
// OnAfterResponse method appends response middleware into the after response chain.
|
||||
// Once we receive response from host server, default `go-resty` response middleware
|
||||
// gets applied and then user assigened response middlewares applied.
|
||||
// resty.OnAfterResponse(func(c *resty.Client, r *resty.Response) error {
|
||||
// // Now you have access to Client and Response instance
|
||||
// // manipulate it as per your need
|
||||
//
|
||||
// return nil // if its success otherwise return error
|
||||
// })
|
||||
//
|
||||
func (c *Client) OnAfterResponse(m func(*Client, *Response) error) *Client {
|
||||
c.afterResponse = append(c.afterResponse, m)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetPreRequestHook method sets the given pre-request function into resty client.
|
||||
// It is called right before the request is fired.
|
||||
//
|
||||
// Note: Only one pre-request hook can be registered. Use `resty.OnBeforeRequest` for mutilple.
|
||||
func (c *Client) SetPreRequestHook(h func(*Client, *Request) error) *Client {
|
||||
if c.preReqHook != nil {
|
||||
c.Log.Printf("Overwriting an existing pre-request hook: %s", functionName(h))
|
||||
}
|
||||
c.preReqHook = h
|
||||
return c
|
||||
}
|
||||
|
||||
// SetDebug method enables the debug mode on `go-resty` client. Client logs details of every request and response.
|
||||
// For `Request` it logs information such as HTTP verb, Relative URL path, Host, Headers, Body if it has one.
|
||||
// For `Response` it logs information such as Status, Response Time, Headers, Body if it has one.
|
||||
// resty.SetDebug(true)
|
||||
//
|
||||
func (c *Client) SetDebug(d bool) *Client {
|
||||
c.Debug = d
|
||||
return c
|
||||
}
|
||||
|
||||
// SetDisableWarn method disables the warning message on `go-resty` client.
|
||||
// For example: go-resty warns the user when BasicAuth used on HTTP mode.
|
||||
// resty.SetDisableWarn(true)
|
||||
//
|
||||
func (c *Client) SetDisableWarn(d bool) *Client {
|
||||
c.DisableWarn = d
|
||||
return c
|
||||
}
|
||||
|
||||
// SetAllowGetMethodPayload method allows the GET method with payload on `go-resty` client.
|
||||
// For example: go-resty allows the user sends request with a payload on HTTP GET method.
|
||||
// resty.SetAllowGetMethodPayload(true)
|
||||
//
|
||||
func (c *Client) SetAllowGetMethodPayload(a bool) *Client {
|
||||
c.AllowGetMethodPayload = a
|
||||
return c
|
||||
}
|
||||
|
||||
// SetLogger method sets given writer for logging go-resty request and response details.
|
||||
// Default is os.Stderr
|
||||
// file, _ := os.OpenFile("/Users/jeeva/go-resty.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
//
|
||||
// resty.SetLogger(file)
|
||||
//
|
||||
func (c *Client) SetLogger(w io.Writer) *Client {
|
||||
c.Log = getLogger(w)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetContentLength method enables the HTTP header `Content-Length` value for every request.
|
||||
// By default go-resty won't set `Content-Length`.
|
||||
// resty.SetContentLength(true)
|
||||
//
|
||||
// Also you have an option to enable for particular request. See `resty.R().SetContentLength`
|
||||
//
|
||||
func (c *Client) SetContentLength(l bool) *Client {
|
||||
c.setContentLength = l
|
||||
return c
|
||||
}
|
||||
|
||||
// SetTimeout method sets timeout for request raised from client.
|
||||
// resty.SetTimeout(time.Duration(1 * time.Minute))
|
||||
//
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
c.httpClient.Timeout = timeout
|
||||
return c
|
||||
}
|
||||
|
||||
// SetError method is to register the global or client common `Error` object into go-resty.
|
||||
// It is used for automatic unmarshalling if response status code is greater than 399 and
|
||||
// content type either JSON or XML. Can be pointer or non-pointer.
|
||||
// resty.SetError(&Error{})
|
||||
// // OR
|
||||
// resty.SetError(Error{})
|
||||
//
|
||||
func (c *Client) SetError(err interface{}) *Client {
|
||||
c.Error = typeOf(err)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetRedirectPolicy method sets the client redirect poilicy. go-resty provides ready to use
|
||||
// redirect policies. Wanna create one for yourself refer `redirect.go`.
|
||||
//
|
||||
// resty.SetRedirectPolicy(FlexibleRedirectPolicy(20))
|
||||
//
|
||||
// // Need multiple redirect policies together
|
||||
// resty.SetRedirectPolicy(FlexibleRedirectPolicy(20), DomainCheckRedirectPolicy("host1.com", "host2.net"))
|
||||
//
|
||||
func (c *Client) SetRedirectPolicy(policies ...interface{}) *Client {
|
||||
for _, p := range policies {
|
||||
if _, ok := p.(RedirectPolicy); !ok {
|
||||
c.Log.Printf("ERORR: %v does not implement resty.RedirectPolicy (missing Apply method)",
|
||||
functionName(p))
|
||||
}
|
||||
}
|
||||
|
||||
c.httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
for _, p := range policies {
|
||||
if err := p.(RedirectPolicy).Apply(req, via); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil // looks good, go ahead
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetRetryCount method enables retry on `go-resty` client and allows you
|
||||
// to set no. of retry count. Resty uses a Backoff mechanism.
|
||||
func (c *Client) SetRetryCount(count int) *Client {
|
||||
c.RetryCount = count
|
||||
return c
|
||||
}
|
||||
|
||||
// SetRetryWaitTime method sets default wait time to sleep before retrying
|
||||
// request.
|
||||
// Default is 100 milliseconds.
|
||||
func (c *Client) SetRetryWaitTime(waitTime time.Duration) *Client {
|
||||
c.RetryWaitTime = waitTime
|
||||
return c
|
||||
}
|
||||
|
||||
// SetRetryMaxWaitTime method sets max wait time to sleep before retrying
|
||||
// request.
|
||||
// Default is 2 seconds.
|
||||
func (c *Client) SetRetryMaxWaitTime(maxWaitTime time.Duration) *Client {
|
||||
c.RetryMaxWaitTime = maxWaitTime
|
||||
return c
|
||||
}
|
||||
|
||||
// AddRetryCondition method adds a retry condition function to array of functions
|
||||
// that are checked to determine if the request is retried. The request will
|
||||
// retry if any of the functions return true and error is nil.
|
||||
func (c *Client) AddRetryCondition(condition RetryConditionFunc) *Client {
|
||||
c.RetryConditions = append(c.RetryConditions, condition)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetHTTPMode method sets go-resty mode into HTTP
|
||||
func (c *Client) SetHTTPMode() *Client {
|
||||
return c.SetMode("http")
|
||||
}
|
||||
|
||||
// SetRESTMode method sets go-resty mode into RESTful
|
||||
func (c *Client) SetRESTMode() *Client {
|
||||
return c.SetMode("rest")
|
||||
}
|
||||
|
||||
// SetMode method sets go-resty client mode to given value such as 'http' & 'rest'.
|
||||
// RESTful:
|
||||
// - No Redirect
|
||||
// - Automatic response unmarshal if it is JSON or XML
|
||||
// HTML:
|
||||
// - Up to 10 Redirects
|
||||
// - No automatic unmarshall. Response will be treated as `response.String()`
|
||||
//
|
||||
// If you want more redirects, use FlexibleRedirectPolicy
|
||||
// resty.SetRedirectPolicy(FlexibleRedirectPolicy(20))
|
||||
//
|
||||
func (c *Client) SetMode(mode string) *Client {
|
||||
// HTTP
|
||||
if mode == "http" {
|
||||
c.isHTTPMode = true
|
||||
c.SetRedirectPolicy(FlexibleRedirectPolicy(10))
|
||||
c.afterResponse = []func(*Client, *Response) error{
|
||||
responseLogger,
|
||||
saveResponseIntoFile,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// RESTful
|
||||
c.isHTTPMode = false
|
||||
c.SetRedirectPolicy(NoRedirectPolicy())
|
||||
c.afterResponse = []func(*Client, *Response) error{
|
||||
responseLogger,
|
||||
parseResponseBody,
|
||||
saveResponseIntoFile,
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Mode method returns the current client mode. Typically its a "http" or "rest".
|
||||
// Default is "rest"
|
||||
func (c *Client) Mode() string {
|
||||
if c.isHTTPMode {
|
||||
return "http"
|
||||
}
|
||||
return "rest"
|
||||
}
|
||||
|
||||
// SetTLSClientConfig method sets TLSClientConfig for underling client Transport.
|
||||
//
|
||||
// Example:
|
||||
// // One can set custom root-certificate. Refer: http://golang.org/pkg/crypto/tls/#example_Dial
|
||||
// resty.SetTLSClientConfig(&tls.Config{ RootCAs: roots })
|
||||
//
|
||||
// // or One can disable security check (https)
|
||||
// resty.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })
|
||||
// Note: This method overwrites existing `TLSClientConfig`.
|
||||
//
|
||||
func (c *Client) SetTLSClientConfig(config *tls.Config) *Client {
|
||||
c.transport.TLSClientConfig = config
|
||||
c.httpClient.Transport = c.transport
|
||||
return c
|
||||
}
|
||||
|
||||
// SetProxy method sets the Proxy URL and Port for resty client.
|
||||
// resty.SetProxy("http://proxyserver:8888")
|
||||
//
|
||||
// Alternatives: At request level proxy, see `Request.SetProxy`. OR Without this `SetProxy` method,
|
||||
// you can also set Proxy via environment variable. By default `Go` uses setting from `HTTP_PROXY`.
|
||||
//
|
||||
func (c *Client) SetProxy(proxyURL string) *Client {
|
||||
if pURL, err := url.Parse(proxyURL); err == nil {
|
||||
c.proxyURL = pURL
|
||||
c.transport.Proxy = http.ProxyURL(c.proxyURL)
|
||||
c.httpClient.Transport = c.transport
|
||||
} else {
|
||||
c.Log.Printf("ERROR [%v]", err)
|
||||
c.RemoveProxy()
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// RemoveProxy method removes the proxy configuration from resty client
|
||||
// resty.RemoveProxy()
|
||||
//
|
||||
func (c *Client) RemoveProxy() *Client {
|
||||
c.proxyURL = nil
|
||||
c.transport.Proxy = nil
|
||||
c.httpClient.Transport = c.transport
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetCertificates method helps to set client certificates into resty conveniently.
|
||||
//
|
||||
func (c *Client) SetCertificates(certs ...tls.Certificate) *Client {
|
||||
config := c.getTLSConfig()
|
||||
config.Certificates = append(config.Certificates, certs...)
|
||||
return c
|
||||
}
|
||||
|
||||
// SetRootCertificate method helps to add one or more root certificates into resty client
|
||||
// resty.SetRootCertificate("/path/to/root/pemFile.pem")
|
||||
//
|
||||
func (c *Client) SetRootCertificate(pemFilePath string) *Client {
|
||||
rootPemData, err := ioutil.ReadFile(pemFilePath)
|
||||
if err != nil {
|
||||
c.Log.Printf("ERROR [%v]", err)
|
||||
return c
|
||||
}
|
||||
|
||||
config := c.getTLSConfig()
|
||||
if config.RootCAs == nil {
|
||||
config.RootCAs = x509.NewCertPool()
|
||||
}
|
||||
|
||||
config.RootCAs.AppendCertsFromPEM(rootPemData)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetOutputDirectory method sets output directory for saving HTTP response into file.
|
||||
// If the output directory not exists then resty creates one. This setting is optional one,
|
||||
// if you're planning using absoule path in `Request.SetOutput` and can used together.
|
||||
// resty.SetOutputDirectory("/save/http/response/here")
|
||||
//
|
||||
func (c *Client) SetOutputDirectory(dirPath string) *Client {
|
||||
err := createDirectory(dirPath)
|
||||
if err != nil {
|
||||
c.Log.Printf("ERROR [%v]", err)
|
||||
}
|
||||
|
||||
c.outputDirectory = dirPath
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetTransport method sets custom *http.Transport in the resty client. Its way to override default.
|
||||
//
|
||||
// **Note:** It overwrites the default resty transport instance and its configurations.
|
||||
// transport := &http.Transport{
|
||||
// // somthing like Proxying to httptest.Server, etc...
|
||||
// Proxy: func(req *http.Request) (*url.URL, error) {
|
||||
// return url.Parse(server.URL)
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// resty.SetTransport(transport)
|
||||
//
|
||||
func (c *Client) SetTransport(transport *http.Transport) *Client {
|
||||
if transport != nil {
|
||||
c.transport = transport
|
||||
c.httpClient.Transport = c.transport
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetScheme method sets custom scheme in the resty client. It's way to override default.
|
||||
// resty.SetScheme("http")
|
||||
//
|
||||
func (c *Client) SetScheme(scheme string) *Client {
|
||||
if !IsStringEmpty(scheme) {
|
||||
c.scheme = scheme
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// SetCloseConnection method sets variable Close in http request struct with the given
|
||||
// value. More info: https://golang.org/src/net/http/request.go
|
||||
func (c *Client) SetCloseConnection(close bool) *Client {
|
||||
c.closeConnection = close
|
||||
return c
|
||||
}
|
||||
|
||||
// IsProxySet method returns the true if proxy is set on client otherwise false.
|
||||
func (c *Client) IsProxySet() bool {
|
||||
return c.proxyURL != nil
|
||||
}
|
||||
|
||||
// executes the given `Request` object and returns response
|
||||
func (c *Client) execute(req *Request) (*Response, error) {
|
||||
defer putBuffer(req.bodyBuf)
|
||||
// Apply Request middleware
|
||||
var err error
|
||||
|
||||
// user defined on before request methods
|
||||
// to modify the *resty.Request object
|
||||
for _, f := range c.udBeforeRequest {
|
||||
if err = f(c, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// resty middlewares
|
||||
for _, f := range c.beforeRequest {
|
||||
if err = f(c, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// call pre-request if defined
|
||||
if c.preReqHook != nil {
|
||||
if err = c.preReqHook(c, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req.Time = time.Now()
|
||||
resp, err := c.httpClient.Do(req.RawRequest)
|
||||
|
||||
response := &Response{
|
||||
Request: req,
|
||||
RawResponse: resp,
|
||||
receivedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if !req.isSaveResponse {
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if response.body, err = ioutil.ReadAll(resp.Body); err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
response.size = int64(len(response.body))
|
||||
}
|
||||
|
||||
// Apply Response middleware
|
||||
for _, f := range c.afterResponse {
|
||||
if err = f(c, response); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
// enables a log prefix
|
||||
func (c *Client) enableLogPrefix() {
|
||||
c.Log.SetFlags(log.LstdFlags)
|
||||
c.Log.SetPrefix("RESTY ")
|
||||
}
|
||||
|
||||
// disables a log prefix
|
||||
func (c *Client) disableLogPrefix() {
|
||||
c.Log.SetFlags(0)
|
||||
c.Log.SetPrefix("")
|
||||
}
|
||||
|
||||
// getting TLS client config if not exists then create one
|
||||
func (c *Client) getTLSConfig() *tls.Config {
|
||||
if c.transport.TLSClientConfig == nil {
|
||||
c.transport.TLSClientConfig = &tls.Config{}
|
||||
c.httpClient.Transport = c.transport
|
||||
}
|
||||
return c.transport.TLSClientConfig
|
||||
}
|
||||
|
||||
//
|
||||
// File
|
||||
//
|
||||
|
||||
// File represent file information for multipart request
|
||||
type File struct {
|
||||
Name string
|
||||
ParamName string
|
||||
io.Reader
|
||||
}
|
||||
|
||||
// String returns string value of current file details
|
||||
func (f *File) String() string {
|
||||
return fmt.Sprintf("ParamName: %v; FileName: %v", f.ParamName, f.Name)
|
||||
}
|
||||
|
||||
//
|
||||
// Helper methods
|
||||
//
|
||||
|
||||
// IsStringEmpty method tells whether given string is empty or not
|
||||
func IsStringEmpty(str string) bool {
|
||||
return (len(strings.TrimSpace(str)) == 0)
|
||||
}
|
||||
|
||||
// DetectContentType method is used to figure out `Request.Body` content type for request header
|
||||
func DetectContentType(body interface{}) string {
|
||||
contentType := plainTextType
|
||||
kind := kindOf(body)
|
||||
switch kind {
|
||||
case reflect.Struct, reflect.Map:
|
||||
contentType = jsonContentType
|
||||
case reflect.String:
|
||||
contentType = plainTextType
|
||||
default:
|
||||
if b, ok := body.([]byte); ok {
|
||||
contentType = http.DetectContentType(b)
|
||||
} else if kind == reflect.Slice {
|
||||
contentType = jsonContentType
|
||||
}
|
||||
}
|
||||
|
||||
return contentType
|
||||
}
|
||||
|
||||
// IsJSONType method is to check JSON content type or not
|
||||
func IsJSONType(ct string) bool {
|
||||
return jsonCheck.MatchString(ct)
|
||||
}
|
||||
|
||||
// IsXMLType method is to check XML content type or not
|
||||
func IsXMLType(ct string) bool {
|
||||
return xmlCheck.MatchString(ct)
|
||||
}
|
||||
|
||||
// Unmarshal content into object from JSON or XML
|
||||
// Deprecated: kept for backward compatibility
|
||||
func Unmarshal(ct string, b []byte, d interface{}) (err error) {
|
||||
if IsJSONType(ct) {
|
||||
err = json.Unmarshal(b, d)
|
||||
} else if IsXMLType(ct) {
|
||||
err = xml.Unmarshal(b, d)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshalc content into object from JSON or XML
|
||||
func Unmarshalc(c *Client, ct string, b []byte, d interface{}) (err error) {
|
||||
if IsJSONType(ct) {
|
||||
err = c.JSONUnmarshal(b, d)
|
||||
} else if IsXMLType(ct) {
|
||||
err = xml.Unmarshal(b, d)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getLogger(w io.Writer) *log.Logger {
|
||||
return log.New(w, "RESTY ", log.LstdFlags)
|
||||
}
|
||||
|
||||
func addFile(w *multipart.Writer, fieldName, path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
part, err := w.CreateFormFile(fieldName, filepath.Base(path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(part, file)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func addFileReader(w *multipart.Writer, f *File) error {
|
||||
part, err := w.CreateFormFile(f.ParamName, f.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(part, f.Reader)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func getPointer(v interface{}) interface{} {
|
||||
vv := valueOf(v)
|
||||
if vv.Kind() == reflect.Ptr {
|
||||
return v
|
||||
}
|
||||
return reflect.New(vv.Type()).Interface()
|
||||
}
|
||||
|
||||
func isPayloadSupported(m string, allowMethodGet bool) bool {
|
||||
return (m == MethodPost || m == MethodPut || m == MethodDelete || m == MethodPatch || (allowMethodGet && m == MethodGet))
|
||||
}
|
||||
|
||||
func typeOf(i interface{}) reflect.Type {
|
||||
return indirect(valueOf(i)).Type()
|
||||
}
|
||||
|
||||
func valueOf(i interface{}) reflect.Value {
|
||||
return reflect.ValueOf(i)
|
||||
}
|
||||
|
||||
func indirect(v reflect.Value) reflect.Value {
|
||||
return reflect.Indirect(v)
|
||||
}
|
||||
|
||||
func kindOf(v interface{}) reflect.Kind {
|
||||
return typeOf(v).Kind()
|
||||
}
|
||||
|
||||
func createDirectory(dir string) (err error) {
|
||||
if _, err = os.Stat(dir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, 0755); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func canJSONMarshal(contentType string, kind reflect.Kind) bool {
|
||||
return IsJSONType(contentType) && (kind == reflect.Struct || kind == reflect.Map)
|
||||
}
|
||||
|
||||
func functionName(i interface{}) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
||||
}
|
||||
|
||||
func getBuffer() *bytes.Buffer {
|
||||
return bufPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
func putBuffer(buf *bytes.Buffer) {
|
||||
if buf != nil {
|
||||
buf.Reset()
|
||||
bufPool.Put(buf)
|
||||
}
|
||||
}
|
||||
365
vendor/github.com/go-resty/resty/client_test.go
generated
vendored
Normal file
365
vendor/github.com/go-resty/resty/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestClientBasicAuth(t *testing.T) {
|
||||
ts := createAuthServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetBasicAuth("myuser", "basicauth").
|
||||
SetHostURL(ts.URL).
|
||||
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
|
||||
resp, err := c.R().
|
||||
SetResult(&AuthSuccess{}).
|
||||
Post("/login")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
|
||||
t.Logf("Result Success: %q", resp.Result().(*AuthSuccess))
|
||||
logResponse(t, resp)
|
||||
}
|
||||
|
||||
func TestClientAuthToken(t *testing.T) {
|
||||
ts := createAuthServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
|
||||
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF").
|
||||
SetHostURL(ts.URL + "/")
|
||||
|
||||
resp, err := c.R().Get("/profile")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
}
|
||||
|
||||
func TestOnAfterMiddleware(t *testing.T) {
|
||||
ts := createGenServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.OnAfterResponse(func(c *Client, res *Response) error {
|
||||
t.Logf("Request sent at: %v", res.Request.Time)
|
||||
t.Logf("Response Recevied at: %v", res.ReceivedAt())
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
resp, err := c.R().
|
||||
SetBody("OnAfterResponse: This is plain text body to server").
|
||||
Put(ts.URL + "/plaintext")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
assertEqual(t, "TestPut: plain text response", resp.String())
|
||||
}
|
||||
|
||||
func TestClientRedirectPolicy(t *testing.T) {
|
||||
ts := createRedirectServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetHTTPMode().
|
||||
SetRedirectPolicy(FlexibleRedirectPolicy(20))
|
||||
|
||||
_, err := c.R().Get(ts.URL + "/redirect-1")
|
||||
|
||||
assertEqual(t, "Get /redirect-21: Stopped after 20 redirects", err.Error())
|
||||
}
|
||||
|
||||
func TestClientTimeout(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetHTTPMode().
|
||||
SetTimeout(time.Duration(time.Second * 3))
|
||||
|
||||
_, err := c.R().Get(ts.URL + "/set-timeout-test")
|
||||
assertEqual(t, true, strings.Contains(strings.ToLower(err.Error()), "timeout"))
|
||||
}
|
||||
|
||||
func TestClientTimeoutWithinThreshold(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetHTTPMode().
|
||||
SetTimeout(time.Duration(time.Second * 3))
|
||||
|
||||
resp, err := c.R().Get(ts.URL + "/set-timeout-test-with-sequence")
|
||||
assertError(t, err)
|
||||
|
||||
seq1, _ := strconv.ParseInt(resp.String(), 10, 32)
|
||||
|
||||
resp, err = c.R().Get(ts.URL + "/set-timeout-test-with-sequence")
|
||||
assertError(t, err)
|
||||
|
||||
seq2, _ := strconv.ParseInt(resp.String(), 10, 32)
|
||||
|
||||
assertEqual(t, seq1+1, seq2)
|
||||
}
|
||||
|
||||
func TestClientTimeoutInternalError(t *testing.T) {
|
||||
c := dc()
|
||||
c.SetHTTPMode()
|
||||
c.SetTimeout(time.Duration(time.Second * 1))
|
||||
|
||||
_, _ = c.R().Get("http://localhost:9000/set-timeout-test")
|
||||
}
|
||||
|
||||
func TestClientProxy(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetTimeout(1 * time.Second)
|
||||
c.SetProxy("http://sampleproxy:8888")
|
||||
|
||||
resp, err := c.R().Get(ts.URL)
|
||||
assertEqual(t, true, resp != nil)
|
||||
assertEqual(t, true, err != nil)
|
||||
|
||||
// Error
|
||||
c.SetProxy("//not.a.user@%66%6f%6f.com:8888")
|
||||
|
||||
resp, err = c.R().
|
||||
Get(ts.URL)
|
||||
assertEqual(t, true, err == nil)
|
||||
assertEqual(t, false, resp == nil)
|
||||
}
|
||||
|
||||
func TestSetCertificates(t *testing.T) {
|
||||
DefaultClient = dc()
|
||||
SetCertificates(tls.Certificate{})
|
||||
|
||||
assertEqual(t, 1, len(DefaultClient.transport.TLSClientConfig.Certificates))
|
||||
}
|
||||
|
||||
func TestSetRootCertificate(t *testing.T) {
|
||||
DefaultClient = dc()
|
||||
SetRootCertificate(getTestDataPath() + "/sample-root.pem")
|
||||
|
||||
assertEqual(t, true, DefaultClient.transport.TLSClientConfig.RootCAs != nil)
|
||||
}
|
||||
|
||||
func TestSetRootCertificateNotExists(t *testing.T) {
|
||||
DefaultClient = dc()
|
||||
SetRootCertificate(getTestDataPath() + "/not-exists-sample-root.pem")
|
||||
|
||||
assertEqual(t, true, DefaultClient.transport.TLSClientConfig == nil)
|
||||
}
|
||||
|
||||
func TestOnBeforeRequestModification(t *testing.T) {
|
||||
tc := New()
|
||||
tc.OnBeforeRequest(func(c *Client, r *Request) error {
|
||||
r.SetAuthToken("This is test auth token")
|
||||
return nil
|
||||
})
|
||||
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
resp, err := tc.R().Get(ts.URL + "/")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
assertEqual(t, "200 OK", resp.Status())
|
||||
assertEqual(t, true, resp.Body() != nil)
|
||||
assertEqual(t, "TestGet: text response", resp.String())
|
||||
|
||||
logResponse(t, resp)
|
||||
}
|
||||
|
||||
func TestSetTransport(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
DefaultClient = dc()
|
||||
|
||||
transport := &http.Transport{
|
||||
// somthing like Proxying to httptest.Server, etc...
|
||||
Proxy: func(req *http.Request) (*url.URL, error) {
|
||||
return url.Parse(ts.URL)
|
||||
},
|
||||
}
|
||||
SetTransport(transport)
|
||||
|
||||
assertEqual(t, true, DefaultClient.transport != nil)
|
||||
}
|
||||
|
||||
func TestSetScheme(t *testing.T) {
|
||||
DefaultClient = dc()
|
||||
|
||||
SetScheme("http")
|
||||
|
||||
assertEqual(t, true, DefaultClient.scheme == "http")
|
||||
}
|
||||
|
||||
func TestSetCookieJar(t *testing.T) {
|
||||
DefaultClient = dc()
|
||||
backupJar := DefaultClient.httpClient.Jar
|
||||
|
||||
SetCookieJar(nil)
|
||||
assertEqual(t, true, DefaultClient.httpClient.Jar == nil)
|
||||
|
||||
SetCookieJar(backupJar)
|
||||
assertEqual(t, true, DefaultClient.httpClient.Jar == backupJar)
|
||||
}
|
||||
|
||||
func TestClientOptions(t *testing.T) {
|
||||
SetHTTPMode().SetContentLength(true)
|
||||
assertEqual(t, Mode(), "http")
|
||||
assertEqual(t, DefaultClient.setContentLength, true)
|
||||
|
||||
SetRESTMode()
|
||||
assertEqual(t, Mode(), "rest")
|
||||
|
||||
SetHostURL("http://httpbin.org")
|
||||
assertEqual(t, "http://httpbin.org", DefaultClient.HostURL)
|
||||
|
||||
SetHeader(hdrContentTypeKey, jsonContentType)
|
||||
SetHeaders(map[string]string{
|
||||
hdrUserAgentKey: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) go-resty v0.1",
|
||||
"X-Request-Id": strconv.FormatInt(time.Now().UnixNano(), 10),
|
||||
})
|
||||
assertEqual(t, jsonContentType, DefaultClient.Header.Get(hdrContentTypeKey))
|
||||
|
||||
SetCookie(&http.Cookie{
|
||||
Name: "default-cookie",
|
||||
Value: "This is cookie default-cookie value",
|
||||
Path: "/",
|
||||
Domain: "localhost",
|
||||
MaxAge: 36000,
|
||||
HttpOnly: true,
|
||||
Secure: false,
|
||||
})
|
||||
assertEqual(t, "default-cookie", DefaultClient.Cookies[0].Name)
|
||||
|
||||
var cookies []*http.Cookie
|
||||
cookies = append(cookies, &http.Cookie{
|
||||
Name: "default-cookie-1",
|
||||
Value: "This is default-cookie 1 value",
|
||||
Path: "/",
|
||||
})
|
||||
cookies = append(cookies, &http.Cookie{
|
||||
Name: "default-cookie-2",
|
||||
Value: "This is default-cookie 2 value",
|
||||
Path: "/",
|
||||
})
|
||||
SetCookies(cookies)
|
||||
assertEqual(t, "default-cookie-1", DefaultClient.Cookies[1].Name)
|
||||
assertEqual(t, "default-cookie-2", DefaultClient.Cookies[2].Name)
|
||||
|
||||
SetQueryParam("test_param_1", "Param_1")
|
||||
SetQueryParams(map[string]string{"test_param_2": "Param_2", "test_param_3": "Param_3"})
|
||||
assertEqual(t, "Param_3", DefaultClient.QueryParam.Get("test_param_3"))
|
||||
|
||||
rTime := strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||
SetFormData(map[string]string{"r_time": rTime})
|
||||
assertEqual(t, rTime, DefaultClient.FormData.Get("r_time"))
|
||||
|
||||
SetBasicAuth("myuser", "mypass")
|
||||
assertEqual(t, "myuser", DefaultClient.UserInfo.Username)
|
||||
|
||||
SetAuthToken("AC75BD37F019E08FBC594900518B4F7E")
|
||||
assertEqual(t, "AC75BD37F019E08FBC594900518B4F7E", DefaultClient.Token)
|
||||
|
||||
SetDisableWarn(true)
|
||||
assertEqual(t, DefaultClient.DisableWarn, true)
|
||||
|
||||
SetRetryCount(3)
|
||||
assertEqual(t, 3, DefaultClient.RetryCount)
|
||||
|
||||
rwt := time.Duration(1000) * time.Millisecond
|
||||
SetRetryWaitTime(rwt)
|
||||
assertEqual(t, rwt, DefaultClient.RetryWaitTime)
|
||||
|
||||
mrwt := time.Duration(2) * time.Second
|
||||
SetRetryMaxWaitTime(mrwt)
|
||||
assertEqual(t, mrwt, DefaultClient.RetryMaxWaitTime)
|
||||
|
||||
err := &AuthError{}
|
||||
SetError(err)
|
||||
if reflect.TypeOf(err) == DefaultClient.Error {
|
||||
t.Error("SetError failed")
|
||||
}
|
||||
|
||||
SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
assertEqual(t, true, DefaultClient.transport.TLSClientConfig.InsecureSkipVerify)
|
||||
|
||||
OnBeforeRequest(func(c *Client, r *Request) error {
|
||||
c.Log.Println("I'm in Request middleware")
|
||||
return nil // if it success
|
||||
})
|
||||
OnAfterResponse(func(c *Client, r *Response) error {
|
||||
c.Log.Println("I'm in Response middleware")
|
||||
return nil // if it success
|
||||
})
|
||||
|
||||
SetTimeout(time.Duration(5 * time.Second))
|
||||
SetRedirectPolicy(FlexibleRedirectPolicy(10), func(req *http.Request, via []*http.Request) error {
|
||||
return errors.New("sample test redirect")
|
||||
})
|
||||
SetContentLength(true)
|
||||
|
||||
SetDebug(true)
|
||||
assertEqual(t, DefaultClient.Debug, true)
|
||||
|
||||
SetAllowGetMethodPayload(true)
|
||||
assertEqual(t, DefaultClient.AllowGetMethodPayload, true)
|
||||
|
||||
SetScheme("http")
|
||||
assertEqual(t, DefaultClient.scheme, "http")
|
||||
|
||||
SetCloseConnection(true)
|
||||
assertEqual(t, DefaultClient.closeConnection, true)
|
||||
|
||||
SetLogger(ioutil.Discard)
|
||||
}
|
||||
|
||||
func TestClientPreRequestHook(t *testing.T) {
|
||||
SetPreRequestHook(func(c *Client, r *Request) error {
|
||||
c.Log.Println("I'm in Pre-Request Hook")
|
||||
return nil
|
||||
})
|
||||
|
||||
SetPreRequestHook(func(c *Client, r *Request) error {
|
||||
c.Log.Println("I'm Overwriting existing Pre-Request Hook")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestClientAllowsGetMethodPayload(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetAllowGetMethodPayload(true)
|
||||
c.SetPreRequestHook(func(*Client, *Request) error { return nil }) // for coverage
|
||||
|
||||
payload := "test-payload"
|
||||
resp, err := c.R().SetBody(payload).Get(ts.URL + "/get-method-payload-test")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
assertEqual(t, payload, resp.String())
|
||||
}
|
||||
15
vendor/github.com/go-resty/resty/context17_test.go
generated
vendored
Normal file
15
vendor/github.com/go-resty/resty/context17_test.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// +build !go1.8
|
||||
|
||||
// Copyright (c) 2015-2016 Jeevanandam M (jeeva@myjeeva.com)
|
||||
// 2016 Andrew Grigorev (https://github.com/ei-grad)
|
||||
// All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import "strings"
|
||||
|
||||
func errIsContextCanceled(err error) bool {
|
||||
return strings.Contains(err.Error(), "request canceled")
|
||||
}
|
||||
22
vendor/github.com/go-resty/resty/context18_test.go
generated
vendored
Normal file
22
vendor/github.com/go-resty/resty/context18_test.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// +build go1.8
|
||||
|
||||
// Copyright (c) 2015-2016 Jeevanandam M (jeeva@myjeeva.com)
|
||||
// 2016 Andrew Grigorev (https://github.com/ei-grad)
|
||||
// All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func errIsContextCanceled(err error) bool {
|
||||
ue, ok := err.(*url.Error)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return ue.Err == context.Canceled
|
||||
}
|
||||
199
vendor/github.com/go-resty/resty/context_test.go
generated
vendored
Normal file
199
vendor/github.com/go-resty/resty/context_test.go
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
// +build go1.7
|
||||
|
||||
// Copyright (c) 2015-2016 Jeevanandam M (jeeva@myjeeva.com)
|
||||
// 2016 Andrew Grigorev (https://github.com/ei-grad)
|
||||
// All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSetContext(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
resp, err := R().
|
||||
SetContext(context.Background()).
|
||||
Get(ts.URL + "/")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
assertEqual(t, "200 OK", resp.Status())
|
||||
assertEqual(t, true, resp.Body() != nil)
|
||||
assertEqual(t, "TestGet: text response", resp.String())
|
||||
|
||||
logResponse(t, resp)
|
||||
}
|
||||
|
||||
func TestSetContextWithError(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
resp, err := dcr().
|
||||
SetContext(context.Background()).
|
||||
Get(ts.URL + "/mypage")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusBadRequest, resp.StatusCode())
|
||||
assertEqual(t, "", resp.String())
|
||||
|
||||
logResponse(t, resp)
|
||||
}
|
||||
|
||||
func TestSetContextCancel(t *testing.T) {
|
||||
ch := make(chan struct{})
|
||||
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
ch <- struct{}{} // tell test request is finished
|
||||
}()
|
||||
t.Logf("Server: %v %v", r.Method, r.URL.Path)
|
||||
ch <- struct{}{}
|
||||
<-ch // wait for client to finish request
|
||||
n, err := w.Write([]byte("TestSetContextCancel: response"))
|
||||
// FIXME? test server doesn't handle request cancellation
|
||||
t.Logf("Server: wrote %d bytes", n)
|
||||
t.Logf("Server: err is %v ", err)
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
<-ch // wait for server to start request handling
|
||||
cancel()
|
||||
}()
|
||||
|
||||
_, err := R().
|
||||
SetContext(ctx).
|
||||
Get(ts.URL + "/")
|
||||
|
||||
ch <- struct{}{} // tell server to continue request handling
|
||||
|
||||
<-ch // wait for server to finish request handling
|
||||
|
||||
t.Logf("Error: %v", err)
|
||||
if !errIsContextCanceled(err) {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetContextCancelRetry(t *testing.T) {
|
||||
reqCount := 0
|
||||
ch := make(chan struct{})
|
||||
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
|
||||
reqCount++
|
||||
defer func() {
|
||||
ch <- struct{}{} // tell test request is finished
|
||||
}()
|
||||
t.Logf("Server: %v %v", r.Method, r.URL.Path)
|
||||
ch <- struct{}{}
|
||||
<-ch // wait for client to finish request
|
||||
n, err := w.Write([]byte("TestSetContextCancel: response"))
|
||||
// FIXME? test server doesn't handle request cancellation
|
||||
t.Logf("Server: wrote %d bytes", n)
|
||||
t.Logf("Server: err is %v ", err)
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
<-ch // wait for server to start request handling
|
||||
cancel()
|
||||
}()
|
||||
|
||||
c := dc()
|
||||
c.SetHTTPMode().
|
||||
SetTimeout(time.Duration(time.Second * 3)).
|
||||
SetRetryCount(3)
|
||||
|
||||
_, err := c.R().
|
||||
SetContext(ctx).
|
||||
Get(ts.URL + "/")
|
||||
|
||||
ch <- struct{}{} // tell server to continue request handling
|
||||
|
||||
<-ch // wait for server to finish request handling
|
||||
|
||||
t.Logf("Error: %v", err)
|
||||
if !errIsContextCanceled(err) {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if reqCount != 1 {
|
||||
t.Errorf("Request was retried %d times instead of 1", reqCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetContextCancelWithError(t *testing.T) {
|
||||
ch := make(chan struct{})
|
||||
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
ch <- struct{}{} // tell test request is finished
|
||||
}()
|
||||
t.Logf("Server: %v %v", r.Method, r.URL.Path)
|
||||
t.Log("Server: sending StatusBadRequest response")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
ch <- struct{}{}
|
||||
<-ch // wait for client to finish request
|
||||
n, err := w.Write([]byte("TestSetContextCancelWithError: response"))
|
||||
// FIXME? test server doesn't handle request cancellation
|
||||
t.Logf("Server: wrote %d bytes", n)
|
||||
t.Logf("Server: err is %v ", err)
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
<-ch // wait for server to start request handling
|
||||
cancel()
|
||||
}()
|
||||
|
||||
_, err := R().
|
||||
SetContext(ctx).
|
||||
Get(ts.URL + "/")
|
||||
|
||||
ch <- struct{}{} // tell server to continue request handling
|
||||
|
||||
<-ch // wait for server to finish request handling
|
||||
|
||||
t.Logf("Error: %v", err)
|
||||
if !errIsContextCanceled(err) {
|
||||
t.Errorf("Got unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientRetryWithSetContext(t *testing.T) {
|
||||
var attemptctx int32
|
||||
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Logf("Method: %v", r.Method)
|
||||
t.Logf("Path: %v", r.URL.Path)
|
||||
attp := atomic.AddInt32(&attemptctx, 1)
|
||||
if attp <= 3 {
|
||||
time.Sleep(time.Second * 2)
|
||||
}
|
||||
_, _ = w.Write([]byte("TestClientRetry page"))
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetHTTPMode().
|
||||
SetTimeout(time.Duration(time.Second * 1)).
|
||||
SetRetryCount(3)
|
||||
|
||||
_, err := c.R().
|
||||
SetContext(context.Background()).
|
||||
Get(ts.URL + "/")
|
||||
|
||||
assertEqual(t, true, strings.HasPrefix(err.Error(), "Get "+ts.URL+"/"))
|
||||
}
|
||||
283
vendor/github.com/go-resty/resty/default.go
generated
vendored
Normal file
283
vendor/github.com/go-resty/resty/default.go
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
// DefaultClient of resty
|
||||
var DefaultClient *Client
|
||||
|
||||
// New method creates a new go-resty client
|
||||
func New() *Client {
|
||||
cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||
|
||||
c := &Client{
|
||||
HostURL: "",
|
||||
QueryParam: url.Values{},
|
||||
FormData: url.Values{},
|
||||
Header: http.Header{},
|
||||
UserInfo: nil,
|
||||
Token: "",
|
||||
Cookies: make([]*http.Cookie, 0),
|
||||
Debug: false,
|
||||
Log: getLogger(os.Stderr),
|
||||
RetryCount: 0,
|
||||
RetryWaitTime: defaultWaitTime,
|
||||
RetryMaxWaitTime: defaultMaxWaitTime,
|
||||
JSONMarshal: json.Marshal,
|
||||
JSONUnmarshal: json.Unmarshal,
|
||||
httpClient: &http.Client{Jar: cookieJar},
|
||||
transport: &http.Transport{},
|
||||
}
|
||||
|
||||
c.httpClient.Transport = c.transport
|
||||
|
||||
// Default redirect policy
|
||||
c.SetRedirectPolicy(NoRedirectPolicy())
|
||||
|
||||
// default before request middlewares
|
||||
c.beforeRequest = []func(*Client, *Request) error{
|
||||
parseRequestURL,
|
||||
parseRequestHeader,
|
||||
parseRequestBody,
|
||||
createHTTPRequest,
|
||||
addCredentials,
|
||||
requestLogger,
|
||||
}
|
||||
|
||||
// user defined request middlewares
|
||||
c.udBeforeRequest = []func(*Client, *Request) error{}
|
||||
|
||||
// default after response middlewares
|
||||
c.afterResponse = []func(*Client, *Response) error{
|
||||
responseLogger,
|
||||
parseResponseBody,
|
||||
saveResponseIntoFile,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// R creates a new resty request object, it is used form a HTTP/RESTful request
|
||||
// such as GET, POST, PUT, DELETE, HEAD, PATCH and OPTIONS.
|
||||
func R() *Request {
|
||||
return DefaultClient.R()
|
||||
}
|
||||
|
||||
// SetHostURL sets Host URL. See `Client.SetHostURL for more information.
|
||||
func SetHostURL(url string) *Client {
|
||||
return DefaultClient.SetHostURL(url)
|
||||
}
|
||||
|
||||
// SetHeader sets single header. See `Client.SetHeader` for more information.
|
||||
func SetHeader(header, value string) *Client {
|
||||
return DefaultClient.SetHeader(header, value)
|
||||
}
|
||||
|
||||
// SetHeaders sets multiple headers. See `Client.SetHeaders` for more information.
|
||||
func SetHeaders(headers map[string]string) *Client {
|
||||
return DefaultClient.SetHeaders(headers)
|
||||
}
|
||||
|
||||
// SetCookieJar sets custom http.CookieJar. See `Client.SetCookieJar` for more information.
|
||||
func SetCookieJar(jar http.CookieJar) *Client {
|
||||
return DefaultClient.SetCookieJar(jar)
|
||||
}
|
||||
|
||||
// SetCookie sets single cookie object. See `Client.SetCookie` for more information.
|
||||
func SetCookie(hc *http.Cookie) *Client {
|
||||
return DefaultClient.SetCookie(hc)
|
||||
}
|
||||
|
||||
// SetCookies sets multiple cookie object. See `Client.SetCookies` for more information.
|
||||
func SetCookies(cs []*http.Cookie) *Client {
|
||||
return DefaultClient.SetCookies(cs)
|
||||
}
|
||||
|
||||
// SetQueryParam method sets single parameter and its value. See `Client.SetQueryParam` for more information.
|
||||
func SetQueryParam(param, value string) *Client {
|
||||
return DefaultClient.SetQueryParam(param, value)
|
||||
}
|
||||
|
||||
// SetQueryParams method sets multiple parameters and its value. See `Client.SetQueryParams` for more information.
|
||||
func SetQueryParams(params map[string]string) *Client {
|
||||
return DefaultClient.SetQueryParams(params)
|
||||
}
|
||||
|
||||
// SetFormData method sets Form parameters and its values. See `Client.SetFormData` for more information.
|
||||
func SetFormData(data map[string]string) *Client {
|
||||
return DefaultClient.SetFormData(data)
|
||||
}
|
||||
|
||||
// SetBasicAuth method sets the basic authentication header. See `Client.SetBasicAuth` for more information.
|
||||
func SetBasicAuth(username, password string) *Client {
|
||||
return DefaultClient.SetBasicAuth(username, password)
|
||||
}
|
||||
|
||||
// SetAuthToken method sets bearer auth token header. See `Client.SetAuthToken` for more information.
|
||||
func SetAuthToken(token string) *Client {
|
||||
return DefaultClient.SetAuthToken(token)
|
||||
}
|
||||
|
||||
// OnBeforeRequest method sets request middleware. See `Client.OnBeforeRequest` for more information.
|
||||
func OnBeforeRequest(m func(*Client, *Request) error) *Client {
|
||||
return DefaultClient.OnBeforeRequest(m)
|
||||
}
|
||||
|
||||
// OnAfterResponse method sets response middleware. See `Client.OnAfterResponse` for more information.
|
||||
func OnAfterResponse(m func(*Client, *Response) error) *Client {
|
||||
return DefaultClient.OnAfterResponse(m)
|
||||
}
|
||||
|
||||
// SetPreRequestHook method sets the pre-request hook. See `Client.SetPreRequestHook` for more information.
|
||||
func SetPreRequestHook(h func(*Client, *Request) error) *Client {
|
||||
return DefaultClient.SetPreRequestHook(h)
|
||||
}
|
||||
|
||||
// SetDebug method enables the debug mode. See `Client.SetDebug` for more information.
|
||||
func SetDebug(d bool) *Client {
|
||||
return DefaultClient.SetDebug(d)
|
||||
}
|
||||
|
||||
// SetAllowGetMethodPayload method allows the GET method with payload. See `Client.SetAllowGetMethodPayload` for more information.
|
||||
func SetAllowGetMethodPayload(a bool) *Client {
|
||||
return DefaultClient.SetAllowGetMethodPayload(a)
|
||||
}
|
||||
|
||||
// SetRetryCount method sets the retry count. See `Client.SetRetryCount` for more information.
|
||||
func SetRetryCount(count int) *Client {
|
||||
return DefaultClient.SetRetryCount(count)
|
||||
}
|
||||
|
||||
// SetRetryWaitTime method sets the retry wait time. See `Client.SetRetryWaitTime` for more information.
|
||||
func SetRetryWaitTime(waitTime time.Duration) *Client {
|
||||
return DefaultClient.SetRetryWaitTime(waitTime)
|
||||
}
|
||||
|
||||
// SetRetryMaxWaitTime method sets the retry max wait time. See `Client.SetRetryMaxWaitTime` for more information.
|
||||
func SetRetryMaxWaitTime(maxWaitTime time.Duration) *Client {
|
||||
return DefaultClient.SetRetryMaxWaitTime(maxWaitTime)
|
||||
}
|
||||
|
||||
// AddRetryCondition method appends check function for retry. See `Client.AddRetryCondition` for more information.
|
||||
func AddRetryCondition(condition RetryConditionFunc) *Client {
|
||||
return DefaultClient.AddRetryCondition(condition)
|
||||
}
|
||||
|
||||
// SetDisableWarn method disables warning comes from `go-resty` client. See `Client.SetDisableWarn` for more information.
|
||||
func SetDisableWarn(d bool) *Client {
|
||||
return DefaultClient.SetDisableWarn(d)
|
||||
}
|
||||
|
||||
// SetLogger method sets given writer for logging. See `Client.SetLogger` for more information.
|
||||
func SetLogger(w io.Writer) *Client {
|
||||
return DefaultClient.SetLogger(w)
|
||||
}
|
||||
|
||||
// SetContentLength method enables `Content-Length` value. See `Client.SetContentLength` for more information.
|
||||
func SetContentLength(l bool) *Client {
|
||||
return DefaultClient.SetContentLength(l)
|
||||
}
|
||||
|
||||
// SetError method is to register the global or client common `Error` object. See `Client.SetError` for more information.
|
||||
func SetError(err interface{}) *Client {
|
||||
return DefaultClient.SetError(err)
|
||||
}
|
||||
|
||||
// SetRedirectPolicy method sets the client redirect poilicy. See `Client.SetRedirectPolicy` for more information.
|
||||
func SetRedirectPolicy(policies ...interface{}) *Client {
|
||||
return DefaultClient.SetRedirectPolicy(policies...)
|
||||
}
|
||||
|
||||
// SetHTTPMode method sets go-resty mode into HTTP. See `Client.SetMode` for more information.
|
||||
func SetHTTPMode() *Client {
|
||||
return DefaultClient.SetHTTPMode()
|
||||
}
|
||||
|
||||
// SetRESTMode method sets go-resty mode into RESTful. See `Client.SetMode` for more information.
|
||||
func SetRESTMode() *Client {
|
||||
return DefaultClient.SetRESTMode()
|
||||
}
|
||||
|
||||
// Mode method returns the current client mode. See `Client.Mode` for more information.
|
||||
func Mode() string {
|
||||
return DefaultClient.Mode()
|
||||
}
|
||||
|
||||
// SetTLSClientConfig method sets TLSClientConfig for underling client Transport. See `Client.SetTLSClientConfig` for more information.
|
||||
func SetTLSClientConfig(config *tls.Config) *Client {
|
||||
return DefaultClient.SetTLSClientConfig(config)
|
||||
}
|
||||
|
||||
// SetTimeout method sets timeout for request. See `Client.SetTimeout` for more information.
|
||||
func SetTimeout(timeout time.Duration) *Client {
|
||||
return DefaultClient.SetTimeout(timeout)
|
||||
}
|
||||
|
||||
// SetProxy method sets Proxy for request. See `Client.SetProxy` for more information.
|
||||
func SetProxy(proxyURL string) *Client {
|
||||
return DefaultClient.SetProxy(proxyURL)
|
||||
}
|
||||
|
||||
// RemoveProxy method removes the proxy configuration. See `Client.RemoveProxy` for more information.
|
||||
func RemoveProxy() *Client {
|
||||
return DefaultClient.RemoveProxy()
|
||||
}
|
||||
|
||||
// SetCertificates method helps to set client certificates into resty conveniently.
|
||||
// See `Client.SetCertificates` for more information and example.
|
||||
func SetCertificates(certs ...tls.Certificate) *Client {
|
||||
return DefaultClient.SetCertificates(certs...)
|
||||
}
|
||||
|
||||
// SetRootCertificate method helps to add one or more root certificates into resty client.
|
||||
// See `Client.SetRootCertificate` for more information.
|
||||
func SetRootCertificate(pemFilePath string) *Client {
|
||||
return DefaultClient.SetRootCertificate(pemFilePath)
|
||||
}
|
||||
|
||||
// SetOutputDirectory method sets output directory. See `Client.SetOutputDirectory` for more information.
|
||||
func SetOutputDirectory(dirPath string) *Client {
|
||||
return DefaultClient.SetOutputDirectory(dirPath)
|
||||
}
|
||||
|
||||
// SetTransport method sets custom *http.Transport in the resty client.
|
||||
// See `Client.SetTransport` for more information.
|
||||
func SetTransport(transport *http.Transport) *Client {
|
||||
return DefaultClient.SetTransport(transport)
|
||||
}
|
||||
|
||||
// SetScheme method sets custom scheme in the resty client.
|
||||
// See `Client.SetScheme` for more information.
|
||||
func SetScheme(scheme string) *Client {
|
||||
return DefaultClient.SetScheme(scheme)
|
||||
}
|
||||
|
||||
// SetCloseConnection method sets close connection value in the resty client.
|
||||
// See `Client.SetCloseConnection` for more information.
|
||||
func SetCloseConnection(close bool) *Client {
|
||||
return DefaultClient.SetCloseConnection(close)
|
||||
}
|
||||
|
||||
// IsProxySet method returns the true if proxy is set on client otherwise false.
|
||||
// See `Client.IsProxySet` for more information.
|
||||
func IsProxySet() bool {
|
||||
return DefaultClient.IsProxySet()
|
||||
}
|
||||
|
||||
func init() {
|
||||
DefaultClient = New()
|
||||
}
|
||||
217
vendor/github.com/go-resty/resty/example_test.go
generated
vendored
Normal file
217
vendor/github.com/go-resty/resty/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M. (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty_test
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/go-resty/resty"
|
||||
)
|
||||
|
||||
type DropboxError struct {
|
||||
Error string
|
||||
}
|
||||
type AuthSuccess struct {
|
||||
/* variables */
|
||||
}
|
||||
type AuthError struct {
|
||||
/* variables */
|
||||
}
|
||||
type Article struct {
|
||||
Title string
|
||||
Content string
|
||||
Author string
|
||||
Tags []string
|
||||
}
|
||||
type Error struct {
|
||||
/* variables */
|
||||
}
|
||||
|
||||
//
|
||||
// Package Level examples
|
||||
//
|
||||
|
||||
func Example_get() {
|
||||
resp, err := resty.R().Get("http://httpbin.org/get")
|
||||
|
||||
fmt.Printf("\nError: %v", err)
|
||||
fmt.Printf("\nResponse Status Code: %v", resp.StatusCode())
|
||||
fmt.Printf("\nResponse Status: %v", resp.Status())
|
||||
fmt.Printf("\nResponse Body: %v", resp)
|
||||
fmt.Printf("\nResponse Time: %v", resp.Time())
|
||||
fmt.Printf("\nResponse Recevied At: %v", resp.ReceivedAt())
|
||||
}
|
||||
|
||||
func Example_enhancedGet() {
|
||||
resp, err := resty.R().
|
||||
SetQueryParams(map[string]string{
|
||||
"page_no": "1",
|
||||
"limit": "20",
|
||||
"sort": "name",
|
||||
"order": "asc",
|
||||
"random": strconv.FormatInt(time.Now().Unix(), 10),
|
||||
}).
|
||||
SetHeader("Accept", "application/json").
|
||||
SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
|
||||
Get("/search_result")
|
||||
|
||||
printOutput(resp, err)
|
||||
}
|
||||
|
||||
func Example_post() {
|
||||
// POST JSON string
|
||||
// No need to set content type, if you have client level setting
|
||||
resp, err := resty.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(`{"username":"testuser", "password":"testpass"}`).
|
||||
SetResult(AuthSuccess{}). // or SetResult(&AuthSuccess{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
printOutput(resp, err)
|
||||
|
||||
// POST []byte array
|
||||
// No need to set content type, if you have client level setting
|
||||
resp1, err1 := resty.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody([]byte(`{"username":"testuser", "password":"testpass"}`)).
|
||||
SetResult(AuthSuccess{}). // or SetResult(&AuthSuccess{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
printOutput(resp1, err1)
|
||||
|
||||
// POST Struct, default is JSON content type. No need to set one
|
||||
resp2, err2 := resty.R().
|
||||
SetBody(resty.User{Username: "testuser", Password: "testpass"}).
|
||||
SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}).
|
||||
SetError(&AuthError{}). // or SetError(AuthError{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
printOutput(resp2, err2)
|
||||
|
||||
// POST Map, default is JSON content type. No need to set one
|
||||
resp3, err3 := resty.R().
|
||||
SetBody(map[string]interface{}{"username": "testuser", "password": "testpass"}).
|
||||
SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}).
|
||||
SetError(&AuthError{}). // or SetError(AuthError{}).
|
||||
Post("https://myapp.com/login")
|
||||
|
||||
printOutput(resp3, err3)
|
||||
}
|
||||
|
||||
func Example_dropboxUpload() {
|
||||
// For example: upload file to Dropbox
|
||||
// POST of raw bytes for file upload.
|
||||
file, _ := os.Open("/Users/jeeva/mydocument.pdf")
|
||||
fileBytes, _ := ioutil.ReadAll(file)
|
||||
|
||||
// See we are not setting content-type header, since go-resty automatically detects Content-Type for you
|
||||
resp, err := resty.R().
|
||||
SetBody(fileBytes). // resty autodetects content type
|
||||
SetContentLength(true). // Dropbox expects this value
|
||||
SetAuthToken("<your-auth-token>").
|
||||
SetError(DropboxError{}).
|
||||
Post("https://content.dropboxapi.com/1/files_put/auto/resty/mydocument.pdf") // you can use PUT method too dropbox supports it
|
||||
|
||||
// Output print
|
||||
fmt.Printf("\nError: %v\n", err)
|
||||
fmt.Printf("Time: %v\n", resp.Time())
|
||||
fmt.Printf("Body: %v\n", resp)
|
||||
}
|
||||
|
||||
func Example_put() {
|
||||
// Just one sample of PUT, refer POST for more combination
|
||||
// request goes as JSON content type
|
||||
// No need to set auth token, error, if you have client level settings
|
||||
resp, err := resty.R().
|
||||
SetBody(Article{
|
||||
Title: "go-resty",
|
||||
Content: "This is my article content, oh ya!",
|
||||
Author: "Jeevanandam M",
|
||||
Tags: []string{"article", "sample", "resty"},
|
||||
}).
|
||||
SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
|
||||
SetError(&Error{}). // or SetError(Error{}).
|
||||
Put("https://myapp.com/article/1234")
|
||||
|
||||
printOutput(resp, err)
|
||||
}
|
||||
|
||||
func Example_clientCertificates() {
|
||||
// Parsing public/private key pair from a pair of files. The files must contain PEM encoded data.
|
||||
cert, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")
|
||||
if err != nil {
|
||||
log.Fatalf("ERROR client certificate: %s", err)
|
||||
}
|
||||
|
||||
resty.SetCertificates(cert)
|
||||
}
|
||||
|
||||
func Example_customRootCertificate() {
|
||||
resty.SetRootCertificate("/path/to/root/pemFile.pem")
|
||||
}
|
||||
|
||||
//
|
||||
// top level method examples
|
||||
//
|
||||
|
||||
func ExampleNew() {
|
||||
// Creating client1
|
||||
client1 := resty.New()
|
||||
resp1, err1 := client1.R().Get("http://httpbin.org/get")
|
||||
fmt.Println(resp1, err1)
|
||||
|
||||
// Creating client2
|
||||
client2 := resty.New()
|
||||
resp2, err2 := client2.R().Get("http://httpbin.org/get")
|
||||
fmt.Println(resp2, err2)
|
||||
}
|
||||
|
||||
//
|
||||
// Client object methods
|
||||
//
|
||||
|
||||
func ExampleClient_SetCertificates() {
|
||||
// Parsing public/private key pair from a pair of files. The files must contain PEM encoded data.
|
||||
cert, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")
|
||||
if err != nil {
|
||||
log.Fatalf("ERROR client certificate: %s", err)
|
||||
}
|
||||
|
||||
resty.SetCertificates(cert)
|
||||
}
|
||||
|
||||
//
|
||||
// Resty Socks5 Proxy request
|
||||
//
|
||||
|
||||
func Example_socks5Proxy() {
|
||||
// create a dailer
|
||||
dialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9150", nil, proxy.Direct)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to obtain proxy dialer: %v\n", err)
|
||||
}
|
||||
|
||||
// create a transport
|
||||
ptransport := &http.Transport{Dial: dialer.Dial}
|
||||
|
||||
// set transport into resty
|
||||
resty.SetTransport(ptransport)
|
||||
|
||||
resp, err := resty.R().Get("http://check.torproject.org")
|
||||
fmt.Println(err, resp)
|
||||
}
|
||||
|
||||
func printOutput(resp *resty.Response, err error) {
|
||||
fmt.Println(resp, err)
|
||||
}
|
||||
414
vendor/github.com/go-resty/resty/middleware.go
generated
vendored
Normal file
414
vendor/github.com/go-resty/resty/middleware.go
generated
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//
|
||||
// Request Middleware(s)
|
||||
//
|
||||
|
||||
func parseRequestURL(c *Client, r *Request) error {
|
||||
// Parsing request URL
|
||||
reqURL, err := url.Parse(r.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If Request.Url is relative path then added c.HostUrl into
|
||||
// the request URL otherwise Request.Url will be used as-is
|
||||
if !reqURL.IsAbs() {
|
||||
if !strings.HasPrefix(r.URL, "/") {
|
||||
r.URL = "/" + r.URL
|
||||
}
|
||||
|
||||
reqURL, err = url.Parse(c.HostURL + r.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Adding Query Param
|
||||
query := reqURL.Query()
|
||||
for k, v := range c.QueryParam {
|
||||
for _, iv := range v {
|
||||
query.Add(k, iv)
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range r.QueryParam {
|
||||
// remove query param from client level by key
|
||||
// since overrides happens for that key in the request
|
||||
query.Del(k)
|
||||
|
||||
for _, iv := range v {
|
||||
query.Add(k, iv)
|
||||
}
|
||||
}
|
||||
|
||||
reqURL.RawQuery = query.Encode()
|
||||
r.URL = reqURL.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRequestHeader(c *Client, r *Request) error {
|
||||
hdr := http.Header{}
|
||||
for k := range c.Header {
|
||||
hdr.Set(k, c.Header.Get(k))
|
||||
}
|
||||
for k := range r.Header {
|
||||
hdr.Set(k, r.Header.Get(k))
|
||||
}
|
||||
|
||||
if IsStringEmpty(hdr.Get(hdrUserAgentKey)) {
|
||||
hdr.Set(hdrUserAgentKey, fmt.Sprintf(hdrUserAgentValue, Version))
|
||||
}
|
||||
|
||||
if IsStringEmpty(hdr.Get(hdrAcceptKey)) && !IsStringEmpty(hdr.Get(hdrContentTypeKey)) {
|
||||
hdr.Set(hdrAcceptKey, hdr.Get(hdrContentTypeKey))
|
||||
}
|
||||
|
||||
r.Header = hdr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRequestBody(c *Client, r *Request) (err error) {
|
||||
if isPayloadSupported(r.Method, c.AllowGetMethodPayload) {
|
||||
// Handling Multipart
|
||||
if r.isMultiPart && !(r.Method == MethodPatch) {
|
||||
if err = handleMultipart(c, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
goto CL
|
||||
}
|
||||
|
||||
// Handling Form Data
|
||||
if len(c.FormData) > 0 || len(r.FormData) > 0 {
|
||||
handleFormData(c, r)
|
||||
|
||||
goto CL
|
||||
}
|
||||
|
||||
// Handling Request body
|
||||
if r.Body != nil {
|
||||
handleContentType(c, r)
|
||||
|
||||
if err = handleRequestBody(c, r); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.Header.Del(hdrContentTypeKey)
|
||||
}
|
||||
|
||||
CL:
|
||||
// by default resty won't set content length, you can if you want to :)
|
||||
if (c.setContentLength || r.setContentLength) && r.bodyBuf != nil {
|
||||
r.Header.Set(hdrContentLengthKey, fmt.Sprintf("%d", r.bodyBuf.Len()))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func createHTTPRequest(c *Client, r *Request) (err error) {
|
||||
if r.bodyBuf == nil {
|
||||
r.RawRequest, err = http.NewRequest(r.Method, r.URL, nil)
|
||||
} else {
|
||||
r.RawRequest, err = http.NewRequest(r.Method, r.URL, r.bodyBuf)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Assign close connection option
|
||||
r.RawRequest.Close = c.closeConnection
|
||||
|
||||
// Add headers into http request
|
||||
r.RawRequest.Header = r.Header
|
||||
|
||||
// Add cookies into http request
|
||||
for _, cookie := range c.Cookies {
|
||||
r.RawRequest.AddCookie(cookie)
|
||||
}
|
||||
|
||||
// it's for non-http scheme option
|
||||
if r.RawRequest.URL != nil && r.RawRequest.URL.Scheme == "" {
|
||||
r.RawRequest.URL.Scheme = c.scheme
|
||||
r.RawRequest.URL.Host = r.URL
|
||||
}
|
||||
|
||||
// Use context if it was specified
|
||||
r.addContextIfAvailable()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func addCredentials(c *Client, r *Request) error {
|
||||
var isBasicAuth bool
|
||||
// Basic Auth
|
||||
if r.UserInfo != nil { // takes precedence
|
||||
r.RawRequest.SetBasicAuth(r.UserInfo.Username, r.UserInfo.Password)
|
||||
isBasicAuth = true
|
||||
} else if c.UserInfo != nil {
|
||||
r.RawRequest.SetBasicAuth(c.UserInfo.Username, c.UserInfo.Password)
|
||||
isBasicAuth = true
|
||||
}
|
||||
|
||||
if !c.DisableWarn {
|
||||
if isBasicAuth && !strings.HasPrefix(r.URL, "https") {
|
||||
c.Log.Println("WARNING - Using Basic Auth in HTTP mode is not secure.")
|
||||
}
|
||||
}
|
||||
|
||||
// Token Auth
|
||||
if !IsStringEmpty(r.Token) { // takes precedence
|
||||
r.RawRequest.Header.Set(hdrAuthorizationKey, "Bearer "+r.Token)
|
||||
} else if !IsStringEmpty(c.Token) {
|
||||
r.RawRequest.Header.Set(hdrAuthorizationKey, "Bearer "+c.Token)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func requestLogger(c *Client, r *Request) error {
|
||||
if c.Debug {
|
||||
rr := r.RawRequest
|
||||
c.Log.Println()
|
||||
c.disableLogPrefix()
|
||||
c.Log.Println("---------------------- REQUEST LOG -----------------------")
|
||||
c.Log.Printf("%s %s %s\n", r.Method, rr.URL.RequestURI(), rr.Proto)
|
||||
c.Log.Printf("HOST : %s", rr.URL.Host)
|
||||
c.Log.Println("HEADERS:")
|
||||
for h, v := range rr.Header {
|
||||
c.Log.Printf("%25s: %v", h, strings.Join(v, ", "))
|
||||
}
|
||||
c.Log.Printf("BODY :\n%v", r.fmtBodyString())
|
||||
c.Log.Println("----------------------------------------------------------")
|
||||
c.enableLogPrefix()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
// Response Middleware(s)
|
||||
//
|
||||
|
||||
func responseLogger(c *Client, res *Response) error {
|
||||
if c.Debug {
|
||||
c.Log.Println()
|
||||
c.disableLogPrefix()
|
||||
c.Log.Println("---------------------- RESPONSE LOG -----------------------")
|
||||
c.Log.Printf("STATUS : %s", res.Status())
|
||||
c.Log.Printf("RECEIVED AT : %v", res.ReceivedAt())
|
||||
c.Log.Printf("RESPONSE TIME : %v", res.Time())
|
||||
c.Log.Println("HEADERS:")
|
||||
for h, v := range res.Header() {
|
||||
c.Log.Printf("%30s: %v", h, strings.Join(v, ", "))
|
||||
}
|
||||
if res.Request.isSaveResponse {
|
||||
c.Log.Printf("BODY :\n***** RESPONSE WRITTEN INTO FILE *****")
|
||||
} else {
|
||||
c.Log.Printf("BODY :\n%v", res.fmtBodyString())
|
||||
}
|
||||
c.Log.Println("----------------------------------------------------------")
|
||||
c.enableLogPrefix()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseResponseBody(c *Client, res *Response) (err error) {
|
||||
// Handles only JSON or XML content type
|
||||
ct := res.Header().Get(hdrContentTypeKey)
|
||||
if IsJSONType(ct) || IsXMLType(ct) {
|
||||
// Considered as Result
|
||||
if res.StatusCode() > 199 && res.StatusCode() < 300 {
|
||||
if res.Request.Result != nil {
|
||||
err = Unmarshalc(c, ct, res.body, res.Request.Result)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Considered as Error
|
||||
if res.StatusCode() > 399 {
|
||||
// global error interface
|
||||
if res.Request.Error == nil && c.Error != nil {
|
||||
res.Request.Error = reflect.New(c.Error).Interface()
|
||||
}
|
||||
|
||||
if res.Request.Error != nil {
|
||||
err = Unmarshalc(c, ct, res.body, res.Request.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func handleMultipart(c *Client, r *Request) (err error) {
|
||||
r.bodyBuf = getBuffer()
|
||||
w := multipart.NewWriter(r.bodyBuf)
|
||||
|
||||
for k, v := range c.FormData {
|
||||
for _, iv := range v {
|
||||
if err = w.WriteField(k, iv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range r.FormData {
|
||||
for _, iv := range v {
|
||||
if strings.HasPrefix(k, "@") { // file
|
||||
err = addFile(w, k[1:], iv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else { // form value
|
||||
if err = w.WriteField(k, iv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #21 - adding io.Reader support
|
||||
if len(r.multipartFiles) > 0 {
|
||||
for _, f := range r.multipartFiles {
|
||||
err = addFileReader(w, f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Header.Set(hdrContentTypeKey, w.FormDataContentType())
|
||||
err = w.Close()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func handleFormData(c *Client, r *Request) {
|
||||
formData := url.Values{}
|
||||
|
||||
for k, v := range c.FormData {
|
||||
for _, iv := range v {
|
||||
formData.Add(k, iv)
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range r.FormData {
|
||||
// remove form data field from client level by key
|
||||
// since overrides happens for that key in the request
|
||||
formData.Del(k)
|
||||
|
||||
for _, iv := range v {
|
||||
formData.Add(k, iv)
|
||||
}
|
||||
}
|
||||
|
||||
r.bodyBuf = bytes.NewBuffer([]byte(formData.Encode()))
|
||||
r.Header.Set(hdrContentTypeKey, formContentType)
|
||||
r.isFormData = true
|
||||
}
|
||||
|
||||
func handleContentType(c *Client, r *Request) {
|
||||
contentType := r.Header.Get(hdrContentTypeKey)
|
||||
if IsStringEmpty(contentType) {
|
||||
contentType = DetectContentType(r.Body)
|
||||
r.Header.Set(hdrContentTypeKey, contentType)
|
||||
}
|
||||
}
|
||||
|
||||
func handleRequestBody(c *Client, r *Request) (err error) {
|
||||
var bodyBytes []byte
|
||||
contentType := r.Header.Get(hdrContentTypeKey)
|
||||
kind := kindOf(r.Body)
|
||||
r.bodyBuf = nil
|
||||
|
||||
if reader, ok := r.Body.(io.Reader); ok {
|
||||
r.bodyBuf = getBuffer()
|
||||
_, err = r.bodyBuf.ReadFrom(reader)
|
||||
} else if b, ok := r.Body.([]byte); ok {
|
||||
bodyBytes = b
|
||||
} else if s, ok := r.Body.(string); ok {
|
||||
bodyBytes = []byte(s)
|
||||
} else if IsJSONType(contentType) &&
|
||||
(kind == reflect.Struct || kind == reflect.Map || kind == reflect.Slice) {
|
||||
bodyBytes, err = c.JSONMarshal(r.Body)
|
||||
} else if IsXMLType(contentType) && (kind == reflect.Struct) {
|
||||
bodyBytes, err = xml.Marshal(r.Body)
|
||||
}
|
||||
|
||||
if bodyBytes == nil && r.bodyBuf == nil {
|
||||
err = errors.New("Unsupported 'Body' type/value")
|
||||
}
|
||||
|
||||
// if any errors during body bytes handling, return it
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// []byte into Buffer
|
||||
if bodyBytes != nil && r.bodyBuf == nil {
|
||||
r.bodyBuf = bytes.NewBuffer(bodyBytes)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func saveResponseIntoFile(c *Client, res *Response) error {
|
||||
if res.Request.isSaveResponse {
|
||||
file := ""
|
||||
|
||||
if len(c.outputDirectory) > 0 && !filepath.IsAbs(res.Request.outputFile) {
|
||||
file += c.outputDirectory + string(filepath.Separator)
|
||||
}
|
||||
|
||||
file = filepath.Clean(file + res.Request.outputFile)
|
||||
if err := createDirectory(filepath.Dir(file)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outFile, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = outFile.Close()
|
||||
}()
|
||||
|
||||
// io.Copy reads maximum 32kb size, it is perfect for large file download too
|
||||
defer func() {
|
||||
_ = res.RawResponse.Body.Close()
|
||||
}()
|
||||
written, err := io.Copy(outFile, res.RawResponse.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.size = written
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
99
vendor/github.com/go-resty/resty/redirect.go
generated
vendored
Normal file
99
vendor/github.com/go-resty/resty/redirect.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// RedirectPolicy to regulate the redirects in the resty client.
|
||||
// Objects implementing the RedirectPolicy interface can be registered as
|
||||
//
|
||||
// Apply function should return nil to continue the redirect jounery, otherwise
|
||||
// return error to stop the redirect.
|
||||
RedirectPolicy interface {
|
||||
Apply(req *http.Request, via []*http.Request) error
|
||||
}
|
||||
|
||||
// The RedirectPolicyFunc type is an adapter to allow the use of ordinary functions as RedirectPolicy.
|
||||
// If f is a function with the appropriate signature, RedirectPolicyFunc(f) is a RedirectPolicy object that calls f.
|
||||
RedirectPolicyFunc func(*http.Request, []*http.Request) error
|
||||
)
|
||||
|
||||
// Apply calls f(req, via).
|
||||
func (f RedirectPolicyFunc) Apply(req *http.Request, via []*http.Request) error {
|
||||
return f(req, via)
|
||||
}
|
||||
|
||||
// NoRedirectPolicy is used to disable redirects in the HTTP client
|
||||
// resty.SetRedirectPolicy(NoRedirectPolicy())
|
||||
func NoRedirectPolicy() RedirectPolicy {
|
||||
return RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||
return errors.New("Auto redirect is disabled")
|
||||
})
|
||||
}
|
||||
|
||||
// FlexibleRedirectPolicy is convenient method to create No of redirect policy for HTTP client.
|
||||
// resty.SetRedirectPolicy(FlexibleRedirectPolicy(20))
|
||||
func FlexibleRedirectPolicy(noOfRedirect int) RedirectPolicy {
|
||||
return RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||
if len(via) >= noOfRedirect {
|
||||
return fmt.Errorf("Stopped after %d redirects", noOfRedirect)
|
||||
}
|
||||
|
||||
checkHostAndAddHeaders(req, via[0])
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DomainCheckRedirectPolicy is convenient method to define domain name redirect rule in resty client.
|
||||
// Redirect is allowed for only mentioned host in the policy.
|
||||
// resty.SetRedirectPolicy(DomainCheckRedirectPolicy("host1.com", "host2.org", "host3.net"))
|
||||
func DomainCheckRedirectPolicy(hostnames ...string) RedirectPolicy {
|
||||
hosts := make(map[string]bool)
|
||||
for _, h := range hostnames {
|
||||
hosts[strings.ToLower(h)] = true
|
||||
}
|
||||
|
||||
fn := RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||
if ok := hosts[getHostname(req.URL.Host)]; !ok {
|
||||
return errors.New("Redirect is not allowed as per DomainCheckRedirectPolicy")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func getHostname(host string) (hostname string) {
|
||||
if strings.Index(host, ":") > 0 {
|
||||
host, _, _ = net.SplitHostPort(host)
|
||||
}
|
||||
hostname = strings.ToLower(host)
|
||||
return
|
||||
}
|
||||
|
||||
// By default Golang will not redirect request headers
|
||||
// after go throughing various discussion commments from thread
|
||||
// https://github.com/golang/go/issues/4800
|
||||
// go-resty will add all the headers during a redirect for the same host
|
||||
func checkHostAndAddHeaders(cur *http.Request, pre *http.Request) {
|
||||
curHostname := getHostname(cur.URL.Host)
|
||||
preHostname := getHostname(pre.URL.Host)
|
||||
if strings.EqualFold(curHostname, preHostname) {
|
||||
for key, val := range pre.Header {
|
||||
cur.Header[key] = val
|
||||
}
|
||||
} else { // only library User-Agent header is added
|
||||
cur.Header.Set(hdrUserAgentKey, fmt.Sprintf(hdrUserAgentValue, Version))
|
||||
}
|
||||
}
|
||||
496
vendor/github.com/go-resty/resty/request.go
generated
vendored
Normal file
496
vendor/github.com/go-resty/resty/request.go
generated
vendored
Normal file
@@ -0,0 +1,496 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SRVRecord holds the data to query the SRV record for the following service
|
||||
type SRVRecord struct {
|
||||
Service string
|
||||
Domain string
|
||||
}
|
||||
|
||||
// SetHeader method is to set a single header field and its value in the current request.
|
||||
// Example: To set `Content-Type` and `Accept` as `application/json`.
|
||||
// resty.R().
|
||||
// SetHeader("Content-Type", "application/json").
|
||||
// SetHeader("Accept", "application/json")
|
||||
//
|
||||
// Also you can override header value, which was set at client instance level.
|
||||
//
|
||||
func (r *Request) SetHeader(header, value string) *Request {
|
||||
r.Header.Set(header, value)
|
||||
return r
|
||||
}
|
||||
|
||||
// SetHeaders method sets multiple headers field and its values at one go in the current request.
|
||||
// Example: To set `Content-Type` and `Accept` as `application/json`
|
||||
//
|
||||
// resty.R().
|
||||
// SetHeaders(map[string]string{
|
||||
// "Content-Type": "application/json",
|
||||
// "Accept": "application/json",
|
||||
// })
|
||||
// Also you can override header value, which was set at client instance level.
|
||||
//
|
||||
func (r *Request) SetHeaders(headers map[string]string) *Request {
|
||||
for h, v := range headers {
|
||||
r.SetHeader(h, v)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetQueryParam method sets single parameter and its value in the current request.
|
||||
// It will be formed as query string for the request.
|
||||
// Example: `search=kitchen%20papers&size=large` in the URL after `?` mark.
|
||||
// resty.R().
|
||||
// SetQueryParam("search", "kitchen papers").
|
||||
// SetQueryParam("size", "large")
|
||||
// Also you can override query params value, which was set at client instance level
|
||||
//
|
||||
func (r *Request) SetQueryParam(param, value string) *Request {
|
||||
r.QueryParam.Set(param, value)
|
||||
return r
|
||||
}
|
||||
|
||||
// SetQueryParams method sets multiple parameters and its values at one go in the current request.
|
||||
// It will be formed as query string for the request.
|
||||
// Example: `search=kitchen%20papers&size=large` in the URL after `?` mark.
|
||||
// resty.R().
|
||||
// SetQueryParams(map[string]string{
|
||||
// "search": "kitchen papers",
|
||||
// "size": "large",
|
||||
// })
|
||||
// Also you can override query params value, which was set at client instance level
|
||||
//
|
||||
func (r *Request) SetQueryParams(params map[string]string) *Request {
|
||||
for p, v := range params {
|
||||
r.SetQueryParam(p, v)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetMultiValueQueryParams method appends multiple parameters with multi-value
|
||||
// at one go in the current request. It will be formed as query string for the request.
|
||||
// Example: `status=pending&status=approved&status=open` in the URL after `?` mark.
|
||||
// resty.R().
|
||||
// SetMultiValueQueryParams(url.Values{
|
||||
// "status": []string{"pending", "approved", "open"},
|
||||
// })
|
||||
// Also you can override query params value, which was set at client instance level
|
||||
//
|
||||
func (r *Request) SetMultiValueQueryParams(params url.Values) *Request {
|
||||
for p, v := range params {
|
||||
for _, pv := range v {
|
||||
r.QueryParam.Add(p, pv)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetQueryString method provides ability to use string as an input to set URL query string for the request.
|
||||
//
|
||||
// Using String as an input
|
||||
// resty.R().
|
||||
// SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more")
|
||||
//
|
||||
func (r *Request) SetQueryString(query string) *Request {
|
||||
values, err := url.ParseQuery(strings.TrimSpace(query))
|
||||
if err == nil {
|
||||
for k := range values {
|
||||
r.QueryParam.Add(k, values.Get(k))
|
||||
}
|
||||
} else {
|
||||
r.client.Log.Printf("ERROR [%v]", err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFormData method sets Form parameters and their values in the current request.
|
||||
// It's applicable only HTTP method `POST` and `PUT` and requests content type would be set as
|
||||
// `application/x-www-form-urlencoded`.
|
||||
// resty.R().
|
||||
// SetFormData(map[string]string{
|
||||
// "access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
|
||||
// "user_id": "3455454545",
|
||||
// })
|
||||
// Also you can override form data value, which was set at client instance level
|
||||
//
|
||||
func (r *Request) SetFormData(data map[string]string) *Request {
|
||||
for k, v := range data {
|
||||
r.FormData.Set(k, v)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetMultiValueFormData method appends multiple form parameters with multi-value
|
||||
// at one go in the current request.
|
||||
// resty.R().
|
||||
// SetMultiValueFormData(url.Values{
|
||||
// "search_criteria": []string{"book", "glass", "pencil"},
|
||||
// })
|
||||
// Also you can override form data value, which was set at client instance level
|
||||
//
|
||||
func (r *Request) SetMultiValueFormData(params url.Values) *Request {
|
||||
for k, v := range params {
|
||||
for _, kv := range v {
|
||||
r.FormData.Add(k, kv)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetBody method sets the request body for the request. It supports various realtime need easy.
|
||||
// We can say its quite handy or powerful. Supported request body data types is `string`, `[]byte`,
|
||||
// `struct` and `map`. Body value can be pointer or non-pointer. Automatic marshalling
|
||||
// for JSON and XML content type, if it is `struct` or `map`.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// Struct as a body input, based on content type, it will be marshalled.
|
||||
// resty.R().
|
||||
// SetBody(User{
|
||||
// Username: "jeeva@myjeeva.com",
|
||||
// Password: "welcome2resty",
|
||||
// })
|
||||
//
|
||||
// Map as a body input, based on content type, it will be marshalled.
|
||||
// resty.R().
|
||||
// SetBody(map[string]interface{}{
|
||||
// "username": "jeeva@myjeeva.com",
|
||||
// "password": "welcome2resty",
|
||||
// "address": &Address{
|
||||
// Address1: "1111 This is my street",
|
||||
// Address2: "Apt 201",
|
||||
// City: "My City",
|
||||
// State: "My State",
|
||||
// ZipCode: 00000,
|
||||
// },
|
||||
// })
|
||||
//
|
||||
// String as a body input. Suitable for any need as a string input.
|
||||
// resty.R().
|
||||
// SetBody(`{
|
||||
// "username": "jeeva@getrightcare.com",
|
||||
// "password": "admin"
|
||||
// }`)
|
||||
//
|
||||
// []byte as a body input. Suitable for raw request such as file upload, serialize & deserialize, etc.
|
||||
// resty.R().
|
||||
// SetBody([]byte("This is my raw request, sent as-is"))
|
||||
//
|
||||
func (r *Request) SetBody(body interface{}) *Request {
|
||||
r.Body = body
|
||||
return r
|
||||
}
|
||||
|
||||
// SetResult method is to register the response `Result` object for automatic unmarshalling in the RESTful mode
|
||||
// if response status code is between 200 and 299 and content type either JSON or XML.
|
||||
//
|
||||
// Note: Result object can be pointer or non-pointer.
|
||||
// resty.R().SetResult(&AuthToken{})
|
||||
// // OR
|
||||
// resty.R().SetResult(AuthToken{})
|
||||
//
|
||||
// Accessing a result value
|
||||
// response.Result().(*AuthToken)
|
||||
//
|
||||
func (r *Request) SetResult(res interface{}) *Request {
|
||||
r.Result = getPointer(res)
|
||||
return r
|
||||
}
|
||||
|
||||
// SetError method is to register the request `Error` object for automatic unmarshalling in the RESTful mode
|
||||
// if response status code is greater than 399 and content type either JSON or XML.
|
||||
//
|
||||
// Note: Error object can be pointer or non-pointer.
|
||||
// resty.R().SetError(&AuthError{})
|
||||
// // OR
|
||||
// resty.R().SetError(AuthError{})
|
||||
//
|
||||
// Accessing a error value
|
||||
// response.Error().(*AuthError)
|
||||
//
|
||||
func (r *Request) SetError(err interface{}) *Request {
|
||||
r.Error = getPointer(err)
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFile method is to set single file field name and its path for multipart upload.
|
||||
// resty.R().
|
||||
// SetFile("my_file", "/Users/jeeva/Gas Bill - Sep.pdf")
|
||||
//
|
||||
func (r *Request) SetFile(param, filePath string) *Request {
|
||||
r.isMultiPart = true
|
||||
r.FormData.Set("@"+param, filePath)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFiles method is to set multiple file field name and its path for multipart upload.
|
||||
// resty.R().
|
||||
// SetFiles(map[string]string{
|
||||
// "my_file1": "/Users/jeeva/Gas Bill - Sep.pdf",
|
||||
// "my_file2": "/Users/jeeva/Electricity Bill - Sep.pdf",
|
||||
// "my_file3": "/Users/jeeva/Water Bill - Sep.pdf",
|
||||
// })
|
||||
//
|
||||
func (r *Request) SetFiles(files map[string]string) *Request {
|
||||
r.isMultiPart = true
|
||||
|
||||
for f, fp := range files {
|
||||
r.FormData.Set("@"+f, fp)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFileReader method is to set single file using io.Reader for multipart upload.
|
||||
// resty.R().
|
||||
// SetFileReader("profile_img", "my-profile-img.png", bytes.NewReader(profileImgBytes)).
|
||||
// SetFileReader("notes", "user-notes.txt", bytes.NewReader(notesBytes))
|
||||
//
|
||||
func (r *Request) SetFileReader(param, fileName string, reader io.Reader) *Request {
|
||||
r.isMultiPart = true
|
||||
|
||||
r.multipartFiles = append(r.multipartFiles, &File{
|
||||
Name: fileName,
|
||||
ParamName: param,
|
||||
Reader: reader,
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetContentLength method sets the HTTP header `Content-Length` value for current request.
|
||||
// By default go-resty won't set `Content-Length`. Also you have an option to enable for every
|
||||
// request. See `resty.SetContentLength`
|
||||
// resty.R().SetContentLength(true)
|
||||
//
|
||||
func (r *Request) SetContentLength(l bool) *Request {
|
||||
r.setContentLength = true
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetBasicAuth method sets the basic authentication header in the current HTTP request.
|
||||
// For Header example:
|
||||
// Authorization: Basic <base64-encoded-value>
|
||||
//
|
||||
// To set the header for username "go-resty" and password "welcome"
|
||||
// resty.R().SetBasicAuth("go-resty", "welcome")
|
||||
//
|
||||
// This method overrides the credentials set by method `resty.SetBasicAuth`.
|
||||
//
|
||||
func (r *Request) SetBasicAuth(username, password string) *Request {
|
||||
r.UserInfo = &User{Username: username, Password: password}
|
||||
return r
|
||||
}
|
||||
|
||||
// SetAuthToken method sets bearer auth token header in the current HTTP request. Header example:
|
||||
// Authorization: Bearer <auth-token-value-comes-here>
|
||||
//
|
||||
// Example: To set auth token BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F
|
||||
//
|
||||
// resty.R().SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")
|
||||
//
|
||||
// This method overrides the Auth token set by method `resty.SetAuthToken`.
|
||||
//
|
||||
func (r *Request) SetAuthToken(token string) *Request {
|
||||
r.Token = token
|
||||
return r
|
||||
}
|
||||
|
||||
// SetOutput method sets the output file for current HTTP request. Current HTTP response will be
|
||||
// saved into given file. It is similar to `curl -o` flag. Absolute path or relative path can be used.
|
||||
// If is it relative path then output file goes under the output directory, as mentioned
|
||||
// in the `Client.SetOutputDirectory`.
|
||||
// resty.R().
|
||||
// SetOutput("/Users/jeeva/Downloads/ReplyWithHeader-v5.1-beta.zip").
|
||||
// Get("http://bit.ly/1LouEKr")
|
||||
//
|
||||
// Note: In this scenario `Response.Body` might be nil.
|
||||
func (r *Request) SetOutput(file string) *Request {
|
||||
r.outputFile = file
|
||||
r.isSaveResponse = true
|
||||
return r
|
||||
}
|
||||
|
||||
// SetSRV method sets the details to query the service SRV record and execute the
|
||||
// request.
|
||||
// resty.R().
|
||||
// SetSRV(SRVRecord{"web", "testservice.com"}).
|
||||
// Get("/get")
|
||||
func (r *Request) SetSRV(srv *SRVRecord) *Request {
|
||||
r.SRV = srv
|
||||
return r
|
||||
}
|
||||
|
||||
//
|
||||
// HTTP verb method starts here
|
||||
//
|
||||
|
||||
// Get method does GET HTTP request. It's defined in section 4.3.1 of RFC7231.
|
||||
func (r *Request) Get(url string) (*Response, error) {
|
||||
return r.Execute(MethodGet, url)
|
||||
}
|
||||
|
||||
// Head method does HEAD HTTP request. It's defined in section 4.3.2 of RFC7231.
|
||||
func (r *Request) Head(url string) (*Response, error) {
|
||||
return r.Execute(MethodHead, url)
|
||||
}
|
||||
|
||||
// Post method does POST HTTP request. It's defined in section 4.3.3 of RFC7231.
|
||||
func (r *Request) Post(url string) (*Response, error) {
|
||||
return r.Execute(MethodPost, url)
|
||||
}
|
||||
|
||||
// Put method does PUT HTTP request. It's defined in section 4.3.4 of RFC7231.
|
||||
func (r *Request) Put(url string) (*Response, error) {
|
||||
return r.Execute(MethodPut, url)
|
||||
}
|
||||
|
||||
// Delete method does DELETE HTTP request. It's defined in section 4.3.5 of RFC7231.
|
||||
func (r *Request) Delete(url string) (*Response, error) {
|
||||
return r.Execute(MethodDelete, url)
|
||||
}
|
||||
|
||||
// Options method does OPTIONS HTTP request. It's defined in section 4.3.7 of RFC7231.
|
||||
func (r *Request) Options(url string) (*Response, error) {
|
||||
return r.Execute(MethodOptions, url)
|
||||
}
|
||||
|
||||
// Patch method does PATCH HTTP request. It's defined in section 2 of RFC5789.
|
||||
func (r *Request) Patch(url string) (*Response, error) {
|
||||
return r.Execute(MethodPatch, url)
|
||||
}
|
||||
|
||||
// Execute method performs the HTTP request with given HTTP method and URL
|
||||
// for current `Request`.
|
||||
// resp, err := resty.R().Execute(resty.GET, "http://httpbin.org/get")
|
||||
//
|
||||
func (r *Request) Execute(method, url string) (*Response, error) {
|
||||
var addrs []*net.SRV
|
||||
var err error
|
||||
|
||||
if r.isMultiPart && !(method == MethodPost || method == MethodPut) {
|
||||
return nil, fmt.Errorf("Multipart content is not allowed in HTTP verb [%v]", method)
|
||||
}
|
||||
|
||||
if r.SRV != nil {
|
||||
_, addrs, err = net.LookupSRV(r.SRV.Service, "tcp", r.SRV.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
r.Method = method
|
||||
r.URL = r.selectAddr(addrs, url, 0)
|
||||
|
||||
if r.client.RetryCount == 0 {
|
||||
return r.client.execute(r)
|
||||
}
|
||||
|
||||
var resp *Response
|
||||
attempt := 0
|
||||
_ = Backoff(
|
||||
func() (*Response, error) {
|
||||
attempt++
|
||||
|
||||
r.URL = r.selectAddr(addrs, url, attempt)
|
||||
|
||||
resp, err = r.client.execute(r)
|
||||
if err != nil {
|
||||
r.client.Log.Printf("ERROR [%v] Attempt [%v]", err, attempt)
|
||||
if r.isContextCancelledIfAvailable() {
|
||||
// stop Backoff from retrying request if request has been
|
||||
// canceled by context
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
},
|
||||
Retries(r.client.RetryCount),
|
||||
WaitTime(r.client.RetryWaitTime),
|
||||
MaxWaitTime(r.client.RetryMaxWaitTime),
|
||||
RetryConditions(r.client.RetryConditions),
|
||||
)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (r *Request) fmtBodyString() (body string) {
|
||||
body = "***** NO CONTENT *****"
|
||||
if isPayloadSupported(r.Method, r.client.AllowGetMethodPayload) {
|
||||
// multipart or form-data
|
||||
if r.isMultiPart || r.isFormData {
|
||||
body = string(r.bodyBuf.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// request body data
|
||||
if r.Body == nil {
|
||||
return
|
||||
}
|
||||
var prtBodyBytes []byte
|
||||
var err error
|
||||
|
||||
contentType := r.Header.Get(hdrContentTypeKey)
|
||||
kind := kindOf(r.Body)
|
||||
if canJSONMarshal(contentType, kind) {
|
||||
prtBodyBytes, err = json.MarshalIndent(&r.Body, "", " ")
|
||||
} else if IsXMLType(contentType) && (kind == reflect.Struct) {
|
||||
prtBodyBytes, err = xml.MarshalIndent(&r.Body, "", " ")
|
||||
} else if b, ok := r.Body.(string); ok {
|
||||
if IsJSONType(contentType) {
|
||||
bodyBytes := []byte(b)
|
||||
out := getBuffer()
|
||||
defer putBuffer(out)
|
||||
if err = json.Indent(out, bodyBytes, "", " "); err == nil {
|
||||
prtBodyBytes = out.Bytes()
|
||||
}
|
||||
} else {
|
||||
body = b
|
||||
return
|
||||
}
|
||||
} else if b, ok := r.Body.([]byte); ok {
|
||||
body = base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
if prtBodyBytes != nil && err == nil {
|
||||
body = string(prtBodyBytes)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Request) selectAddr(addrs []*net.SRV, path string, attempt int) string {
|
||||
if addrs == nil {
|
||||
return path
|
||||
}
|
||||
|
||||
idx := attempt % len(addrs)
|
||||
domain := strings.TrimRight(addrs[idx].Target, ".")
|
||||
path = strings.TrimLeft(path, "/")
|
||||
|
||||
return fmt.Sprintf("%s://%s:%d/%s", r.client.scheme, domain, addrs[idx].Port, path)
|
||||
}
|
||||
55
vendor/github.com/go-resty/resty/request16.go
generated
vendored
Normal file
55
vendor/github.com/go-resty/resty/request16.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// +build !go1.7
|
||||
|
||||
// Copyright (c) 2015-2016 Jeevanandam M (jeeva@myjeeva.com)
|
||||
// 2016 Andrew Grigorev (https://github.com/ei-grad)
|
||||
// All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Request type is used to compose and send individual request from client
|
||||
// go-resty is provide option override client level settings such as
|
||||
// Auth Token, Basic Auth credentials, Header, Query Param, Form Data, Error object
|
||||
// and also you can add more options for that particular request
|
||||
//
|
||||
type Request struct {
|
||||
URL string
|
||||
Method string
|
||||
QueryParam url.Values
|
||||
FormData url.Values
|
||||
Header http.Header
|
||||
UserInfo *User
|
||||
Token string
|
||||
Body interface{}
|
||||
Result interface{}
|
||||
Error interface{}
|
||||
Time time.Time
|
||||
RawRequest *http.Request
|
||||
SRV *SRVRecord
|
||||
|
||||
client *Client
|
||||
bodyBuf *bytes.Buffer
|
||||
isMultiPart bool
|
||||
isFormData bool
|
||||
setContentLength bool
|
||||
isSaveResponse bool
|
||||
outputFile string
|
||||
multipartFiles []*File
|
||||
}
|
||||
|
||||
func (r *Request) addContextIfAvailable() {
|
||||
// nothing to do for golang<1.7
|
||||
}
|
||||
|
||||
func (r *Request) isContextCancelledIfAvailable() bool {
|
||||
// just always return false golang<1.7
|
||||
return false
|
||||
}
|
||||
72
vendor/github.com/go-resty/resty/request17.go
generated
vendored
Normal file
72
vendor/github.com/go-resty/resty/request17.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// +build go1.7 go1.8
|
||||
|
||||
// Copyright (c) 2015-2016 Jeevanandam M (jeeva@myjeeva.com)
|
||||
// 2016 Andrew Grigorev (https://github.com/ei-grad)
|
||||
// All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Request type is used to compose and send individual request from client
|
||||
// go-resty is provide option override client level settings such as
|
||||
// Auth Token, Basic Auth credentials, Header, Query Param, Form Data, Error object
|
||||
// and also you can add more options for that particular request
|
||||
//
|
||||
type Request struct {
|
||||
URL string
|
||||
Method string
|
||||
QueryParam url.Values
|
||||
FormData url.Values
|
||||
Header http.Header
|
||||
UserInfo *User
|
||||
Token string
|
||||
Body interface{}
|
||||
Result interface{}
|
||||
Error interface{}
|
||||
Time time.Time
|
||||
RawRequest *http.Request
|
||||
SRV *SRVRecord
|
||||
|
||||
client *Client
|
||||
bodyBuf *bytes.Buffer
|
||||
isMultiPart bool
|
||||
isFormData bool
|
||||
setContentLength bool
|
||||
isSaveResponse bool
|
||||
outputFile string
|
||||
multipartFiles []*File
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// SetContext method sets the context.Context for current Request. It allows
|
||||
// to interrupt the request execution if ctx.Done() channel is closed.
|
||||
// See https://blog.golang.org/context article and the "context" package
|
||||
// documentation.
|
||||
func (r *Request) SetContext(ctx context.Context) *Request {
|
||||
r.ctx = ctx
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Request) addContextIfAvailable() {
|
||||
if r.ctx != nil {
|
||||
r.RawRequest = r.RawRequest.WithContext(r.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Request) isContextCancelledIfAvailable() bool {
|
||||
if r.ctx != nil {
|
||||
if r.ctx.Err() != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
125
vendor/github.com/go-resty/resty/response.go
generated
vendored
Normal file
125
vendor/github.com/go-resty/resty/response.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Response is an object represents executed request and its values.
|
||||
type Response struct {
|
||||
Request *Request
|
||||
RawResponse *http.Response
|
||||
|
||||
body []byte
|
||||
size int64
|
||||
receivedAt time.Time
|
||||
}
|
||||
|
||||
// Body method returns HTTP response as []byte array for the executed request.
|
||||
// Note: `Response.Body` might be nil, if `Request.SetOutput` is used.
|
||||
func (r *Response) Body() []byte {
|
||||
if r.RawResponse == nil {
|
||||
return []byte{}
|
||||
}
|
||||
return r.body
|
||||
}
|
||||
|
||||
// Status method returns the HTTP status string for the executed request.
|
||||
// Example: 200 OK
|
||||
func (r *Response) Status() string {
|
||||
if r.RawResponse == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return r.RawResponse.Status
|
||||
}
|
||||
|
||||
// StatusCode method returns the HTTP status code for the executed request.
|
||||
// Example: 200
|
||||
func (r *Response) StatusCode() int {
|
||||
if r.RawResponse == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return r.RawResponse.StatusCode
|
||||
}
|
||||
|
||||
// Result method returns the response value as an object if it has one
|
||||
func (r *Response) Result() interface{} {
|
||||
return r.Request.Result
|
||||
}
|
||||
|
||||
// Error method returns the error object if it has one
|
||||
func (r *Response) Error() interface{} {
|
||||
return r.Request.Error
|
||||
}
|
||||
|
||||
// Header method returns the response headers
|
||||
func (r *Response) Header() http.Header {
|
||||
if r.RawResponse == nil {
|
||||
return http.Header{}
|
||||
}
|
||||
|
||||
return r.RawResponse.Header
|
||||
}
|
||||
|
||||
// Cookies method to access all the response cookies
|
||||
func (r *Response) Cookies() []*http.Cookie {
|
||||
if r.RawResponse == nil {
|
||||
return make([]*http.Cookie, 0)
|
||||
}
|
||||
|
||||
return r.RawResponse.Cookies()
|
||||
}
|
||||
|
||||
// String method returns the body of the server response as String.
|
||||
func (r *Response) String() string {
|
||||
if r.body == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(r.body))
|
||||
}
|
||||
|
||||
// Time method returns the time of HTTP response time that from request we sent and received a request.
|
||||
// See `response.ReceivedAt` to know when client recevied response and see `response.Request.Time` to know
|
||||
// when client sent a request.
|
||||
func (r *Response) Time() time.Duration {
|
||||
return r.receivedAt.Sub(r.Request.Time)
|
||||
}
|
||||
|
||||
// ReceivedAt method returns when response got recevied from server for the request.
|
||||
func (r *Response) ReceivedAt() time.Time {
|
||||
return r.receivedAt
|
||||
}
|
||||
|
||||
// Size method returns the HTTP response size in bytes. Ya, you can relay on HTTP `Content-Length` header,
|
||||
// however it won't be good for chucked transfer/compressed response. Since Resty calculates response size
|
||||
// at the client end. You will get actual size of the http response.
|
||||
func (r *Response) Size() int64 {
|
||||
return r.size
|
||||
}
|
||||
|
||||
func (r *Response) fmtBodyString() string {
|
||||
bodyStr := "***** NO CONTENT *****"
|
||||
if r.body != nil {
|
||||
ct := r.Header().Get(hdrContentTypeKey)
|
||||
if IsJSONType(ct) {
|
||||
out := getBuffer()
|
||||
defer putBuffer(out)
|
||||
if err := json.Indent(out, r.body, "", " "); err == nil {
|
||||
bodyStr = string(out.Bytes())
|
||||
}
|
||||
} else {
|
||||
bodyStr = r.String()
|
||||
}
|
||||
}
|
||||
|
||||
return bodyStr
|
||||
}
|
||||
9
vendor/github.com/go-resty/resty/resty.go
generated
vendored
Normal file
9
vendor/github.com/go-resty/resty/resty.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package resty provides simple HTTP and REST client for Go inspired by Ruby rest-client.
|
||||
package resty
|
||||
|
||||
// Version # of resty
|
||||
const Version = "0.13"
|
||||
1639
vendor/github.com/go-resty/resty/resty_test.go
generated
vendored
Normal file
1639
vendor/github.com/go-resty/resty/resty_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
114
vendor/github.com/go-resty/resty/retry.go
generated
vendored
Normal file
114
vendor/github.com/go-resty/resty/retry.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMaxRetries = 3
|
||||
defaultWaitTime = time.Duration(100) * time.Millisecond
|
||||
defaultMaxWaitTime = time.Duration(2000) * time.Millisecond
|
||||
)
|
||||
|
||||
type (
|
||||
// Option is to create convenient retry options like wait time, max retries, etc.
|
||||
Option func(*Options)
|
||||
|
||||
// RetryConditionFunc type is for retry condition function
|
||||
RetryConditionFunc func(*Response) (bool, error)
|
||||
|
||||
// Options to hold go-resty retry values
|
||||
Options struct {
|
||||
maxRetries int
|
||||
waitTime time.Duration
|
||||
maxWaitTime time.Duration
|
||||
retryConditions []RetryConditionFunc
|
||||
}
|
||||
)
|
||||
|
||||
// Retries sets the max number of retries
|
||||
func Retries(value int) Option {
|
||||
return func(o *Options) {
|
||||
o.maxRetries = value
|
||||
}
|
||||
}
|
||||
|
||||
// WaitTime sets the default wait time to sleep between requests
|
||||
func WaitTime(value time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.waitTime = value
|
||||
}
|
||||
}
|
||||
|
||||
// MaxWaitTime sets the max wait time to sleep between requests
|
||||
func MaxWaitTime(value time.Duration) Option {
|
||||
return func(o *Options) {
|
||||
o.maxWaitTime = value
|
||||
}
|
||||
}
|
||||
|
||||
// RetryConditions sets the conditions that will be checked for retry.
|
||||
func RetryConditions(conditions []RetryConditionFunc) Option {
|
||||
return func(o *Options) {
|
||||
o.retryConditions = conditions
|
||||
}
|
||||
}
|
||||
|
||||
// Backoff retries with increasing timeout duration up until X amount of retries
|
||||
// (Default is 3 attempts, Override with option Retries(n))
|
||||
func Backoff(operation func() (*Response, error), options ...Option) error {
|
||||
// Defaults
|
||||
opts := Options{
|
||||
maxRetries: defaultMaxRetries,
|
||||
waitTime: defaultWaitTime,
|
||||
maxWaitTime: defaultMaxWaitTime,
|
||||
retryConditions: []RetryConditionFunc{},
|
||||
}
|
||||
|
||||
for _, o := range options {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
var (
|
||||
resp *Response
|
||||
err error
|
||||
)
|
||||
base := float64(opts.waitTime) // Time to wait between each attempt
|
||||
capLevel := float64(opts.maxWaitTime) // Maximum amount of wait time for the retry
|
||||
for attempt := 0; attempt < opts.maxRetries; attempt++ {
|
||||
resp, err = operation()
|
||||
|
||||
var needsRetry bool
|
||||
var conditionErr error
|
||||
for _, condition := range opts.retryConditions {
|
||||
needsRetry, conditionErr = condition(resp)
|
||||
if needsRetry || conditionErr != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If the operation returned no error, there was no condition satisfied and
|
||||
// there was no error caused by the conditional functions.
|
||||
if err == nil && !needsRetry && conditionErr == nil {
|
||||
return nil
|
||||
}
|
||||
// Adding capped exponential backup with jitter
|
||||
// See the following article...
|
||||
// http://www.awsarchitectureblog.com/2015/03/backoff.html
|
||||
temp := math.Min(capLevel, base*math.Exp2(float64(attempt)))
|
||||
sleepDuration := time.Duration(int(temp/2) + rand.Intn(int(temp/2)))
|
||||
|
||||
if sleepDuration < opts.waitTime {
|
||||
sleepDuration = opts.waitTime
|
||||
}
|
||||
time.Sleep(sleepDuration)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
270
vendor/github.com/go-resty/resty/retry_test.go
generated
vendored
Normal file
270
vendor/github.com/go-resty/resty/retry_test.go
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
// Copyright (c) 2015-2017 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
|
||||
// resty source code and usage is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package resty
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBackoffSuccess(t *testing.T) {
|
||||
attempts := 3
|
||||
externalCounter := 0
|
||||
retryErr := Backoff(func() (*Response, error) {
|
||||
externalCounter++
|
||||
if externalCounter < attempts {
|
||||
return nil, errors.New("not yet got the number we're after")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
assertError(t, retryErr)
|
||||
assertEqual(t, externalCounter, attempts)
|
||||
}
|
||||
|
||||
func TestBackoffTenAttemptsSuccess(t *testing.T) {
|
||||
attempts := 10
|
||||
externalCounter := 0
|
||||
retryErr := Backoff(func() (*Response, error) {
|
||||
externalCounter++
|
||||
if externalCounter < attempts {
|
||||
return nil, errors.New("not yet got the number we're after")
|
||||
}
|
||||
return nil, nil
|
||||
}, Retries(attempts), WaitTime(5), MaxWaitTime(500))
|
||||
|
||||
assertError(t, retryErr)
|
||||
assertEqual(t, externalCounter, attempts)
|
||||
}
|
||||
|
||||
// Check to make sure the conditional of the retry condition is being used
|
||||
func TestConditionalBackoffCondition(t *testing.T) {
|
||||
attempts := 3
|
||||
counter := 0
|
||||
check := RetryConditionFunc(func(*Response) (bool, error) {
|
||||
return attempts != counter, nil
|
||||
})
|
||||
retryErr := Backoff(func() (*Response, error) {
|
||||
counter++
|
||||
return nil, nil
|
||||
}, RetryConditions([]RetryConditionFunc{check}))
|
||||
|
||||
assertError(t, retryErr)
|
||||
assertEqual(t, counter, attempts)
|
||||
}
|
||||
|
||||
// Check to make sure that errors in the conditional cause a retry
|
||||
func TestConditionalBackoffConditionError(t *testing.T) {
|
||||
attempts := 3
|
||||
counter := 0
|
||||
check := RetryConditionFunc(func(*Response) (bool, error) {
|
||||
if attempts != counter {
|
||||
return false, errors.New("Attempts not equal Counter")
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
|
||||
retryErr := Backoff(func() (*Response, error) {
|
||||
counter++
|
||||
return nil, nil
|
||||
}, RetryConditions([]RetryConditionFunc{check}))
|
||||
|
||||
assertError(t, retryErr)
|
||||
assertEqual(t, counter, attempts)
|
||||
}
|
||||
|
||||
// Check to make sure that if the conditional is false we don't retry
|
||||
func TestConditionalBackoffConditionNonExecution(t *testing.T) {
|
||||
attempts := 3
|
||||
counter := 0
|
||||
|
||||
retryErr := Backoff(func() (*Response, error) {
|
||||
counter++
|
||||
return nil, nil
|
||||
}, RetryConditions([]RetryConditionFunc{filler}))
|
||||
|
||||
assertError(t, retryErr)
|
||||
assertNotEqual(t, counter, attempts)
|
||||
}
|
||||
|
||||
// Check to make sure the functions added to add conditionals work
|
||||
func TestConditionalGet(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
attemptCount := 1
|
||||
externalCounter := 0
|
||||
|
||||
// This check should pass on first run, and let the response through
|
||||
check := RetryConditionFunc(func(*Response) (bool, error) {
|
||||
externalCounter++
|
||||
if attemptCount != externalCounter {
|
||||
return false, errors.New("Attempts not equal Counter")
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
|
||||
client := dc().AddRetryCondition(check).SetRetryCount(1)
|
||||
resp, err := client.R().
|
||||
SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)).
|
||||
Get(ts.URL + "/")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
assertEqual(t, "200 OK", resp.Status())
|
||||
assertEqual(t, true, resp.Body() != nil)
|
||||
assertEqual(t, "TestGet: text response", resp.String())
|
||||
assertEqual(t, externalCounter, attemptCount)
|
||||
|
||||
logResponse(t, resp)
|
||||
}
|
||||
|
||||
// Check to make sure the package Function works.
|
||||
func TestConditionalGetDefaultClient(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
attemptCount := 1
|
||||
externalCounter := 0
|
||||
|
||||
// This check should pass on first run, and let the response through
|
||||
check := RetryConditionFunc(func(*Response) (bool, error) {
|
||||
externalCounter++
|
||||
if attemptCount != externalCounter {
|
||||
return false, errors.New("Attempts not equal Counter")
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
|
||||
// Clear the default client.
|
||||
_ = dc()
|
||||
// Proceed to check.
|
||||
client := AddRetryCondition(check).SetRetryCount(1)
|
||||
resp, err := client.R().
|
||||
SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)).
|
||||
Get(ts.URL + "/")
|
||||
|
||||
assertError(t, err)
|
||||
assertEqual(t, http.StatusOK, resp.StatusCode())
|
||||
assertEqual(t, "200 OK", resp.Status())
|
||||
assertEqual(t, true, resp.Body() != nil)
|
||||
assertEqual(t, "TestGet: text response", resp.String())
|
||||
assertEqual(t, externalCounter, attemptCount)
|
||||
|
||||
logResponse(t, resp)
|
||||
}
|
||||
|
||||
func TestClientRetryGet(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
c := dc()
|
||||
c.SetHTTPMode().
|
||||
SetTimeout(time.Duration(time.Second * 3)).
|
||||
SetRetryCount(3)
|
||||
|
||||
resp, err := c.R().Get(ts.URL + "/set-retrycount-test")
|
||||
assertEqual(t, "", resp.Status())
|
||||
assertEqual(t, 0, resp.StatusCode())
|
||||
assertEqual(t, 0, len(resp.Cookies()))
|
||||
assertEqual(t, true, resp.Body() != nil)
|
||||
assertEqual(t, 0, len(resp.Header()))
|
||||
|
||||
assertEqual(t, true, strings.HasPrefix(err.Error(), "Get "+ts.URL+"/set-retrycount-test"))
|
||||
}
|
||||
|
||||
func TestClientRetryWait(t *testing.T) {
|
||||
ts := createGetServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
attempt := 0
|
||||
|
||||
retryCount := 5
|
||||
retryIntervals := make([]uint64, retryCount)
|
||||
|
||||
// Set retry wait times that do not intersect with default ones
|
||||
retryWaitTime := time.Duration(3) * time.Second
|
||||
retryMaxWaitTime := time.Duration(9) * time.Second
|
||||
|
||||
c := dc()
|
||||
c.SetHTTPMode().
|
||||
SetRetryCount(retryCount).
|
||||
SetRetryWaitTime(retryWaitTime).
|
||||
SetRetryMaxWaitTime(retryMaxWaitTime).
|
||||
AddRetryCondition(
|
||||
func(r *Response) (bool, error) {
|
||||
timeSlept, _ := strconv.ParseUint(string(r.Body()), 10, 64)
|
||||
retryIntervals[attempt] = timeSlept
|
||||
attempt++
|
||||
return true, nil
|
||||
},
|
||||
)
|
||||
_, _ = c.R().Get(ts.URL + "/set-retrywaittime-test")
|
||||
|
||||
// 5 attempts were made
|
||||
assertEqual(t, attempt, 5)
|
||||
|
||||
// Initial attempt has 0 time slept since last request
|
||||
assertEqual(t, retryIntervals[0], uint64(0))
|
||||
|
||||
for i := 1; i < len(retryIntervals); i++ {
|
||||
slept := time.Duration(retryIntervals[i])
|
||||
// Ensure that client has slept some duration between
|
||||
// waitTime and maxWaitTime for consequent requests
|
||||
if slept < retryWaitTime || slept > retryMaxWaitTime {
|
||||
t.Errorf("Client has slept %f seconds before retry %d", slept.Seconds(), i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientRetryPost(t *testing.T) {
|
||||
ts := createPostServer(t)
|
||||
defer ts.Close()
|
||||
|
||||
usersmap := map[string]interface{}{
|
||||
"user1": map[string]interface{}{"FirstName": "firstname1", "LastName": "lastname1", "ZipCode": "10001"},
|
||||
}
|
||||
|
||||
var users []map[string]interface{}
|
||||
users = append(users, usersmap)
|
||||
|
||||
c := dc()
|
||||
c.SetRetryCount(3)
|
||||
c.AddRetryCondition(RetryConditionFunc(func(r *Response) (bool, error) {
|
||||
if r.StatusCode() >= http.StatusInternalServerError {
|
||||
return false, errors.New("error")
|
||||
}
|
||||
return true, nil
|
||||
}))
|
||||
|
||||
resp, _ := c.R().
|
||||
SetBody(&users).
|
||||
Post(ts.URL + "/usersmap?status=500")
|
||||
|
||||
if resp != nil {
|
||||
if resp.StatusCode() == http.StatusInternalServerError {
|
||||
t.Logf("Got response body: %s", string(resp.body))
|
||||
var usersResponse []map[string]interface{}
|
||||
err := json.Unmarshal(resp.body, &usersResponse)
|
||||
assertError(t, err)
|
||||
|
||||
if !reflect.DeepEqual(users, usersResponse) {
|
||||
t.Errorf("Expected request body to be echoed back as response body. Instead got: %s", string(resp.body))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
t.Errorf("Got unexpected response code: %d with body: %s", resp.StatusCode(), string(resp.body))
|
||||
}
|
||||
}
|
||||
|
||||
func filler(*Response) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
BIN
vendor/github.com/go-resty/resty/test-data/test-img.png
generated
vendored
Normal file
BIN
vendor/github.com/go-resty/resty/test-data/test-img.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
3
vendor/github.com/go-resty/resty/test-data/text-file.txt
generated
vendored
Normal file
3
vendor/github.com/go-resty/resty/test-data/text-file.txt
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
THIS IS TEXT FILE FOR MULTIPART UPLOAD TEST :)
|
||||
|
||||
- go-resty
|
||||
Reference in New Issue
Block a user