Source File
request.go
Belonging Package
github.com/go-resty/resty/v2
// Copyright (c) 2015-2023 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 (
)
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Request struct and methods
//_______________________________________________________________________
// Request struct is used to compose and fire individual request from
// resty client. Request provides an options to override client level
// settings and also an options for the request composition.
type Request struct {
URL string
Method string
Token string
AuthScheme string
QueryParam url.Values
FormData url.Values
PathParams map[string]string
RawPathParams map[string]string
Header http.Header
Time time.Time
Body interface{}
Result interface{}
Error interface{}
RawRequest *http.Request
SRV *SRVRecord
UserInfo *User
Cookies []*http.Cookie
Debug bool
// Attempt is to represent the request attempt made during a Resty
// request execution flow, including retry count.
//
// Since v2.4.0
Attempt int
isMultiPart bool
isFormData bool
setContentLength bool
isSaveResponse bool
notParseResponse bool
jsonEscapeHTML bool
trace bool
outputFile string
fallbackContentType string
forceContentType string
ctx context.Context
values map[string]interface{}
client *Client
bodyBuf *bytes.Buffer
clientTrace *clientTrace
log Logger
multipartFiles []*File
multipartFields []*MultipartField
retryConditions []RetryConditionFunc
}
// Context method returns the Context if its already set in request
// otherwise it creates new one using `context.Background()`.
func ( *Request) () context.Context {
if .ctx == nil {
return context.Background()
}
return .ctx
}
// 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 ( *Request) ( context.Context) *Request {
.ctx =
return
}
// SetHeader method is to set a single header field and its value in the current request.
//
// For Example: To set `Content-Type` and `Accept` as `application/json`.
//
// client.R().
// SetHeader("Content-Type", "application/json").
// SetHeader("Accept", "application/json")
//
// Also you can override header value, which was set at client instance level.
func ( *Request) (, string) *Request {
.Header.Set(, )
return
}
// SetHeaders method sets multiple headers field and its values at one go in the current request.
//
// For Example: To set `Content-Type` and `Accept` as `application/json`
//
// client.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 ( *Request) ( map[string]string) *Request {
for , := range {
.SetHeader(, )
}
return
}
// SetHeaderMultiValues sets multiple headers fields and its values is list of strings at one go in the current request.
//
// For Example: To set `Accept` as `text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8`
//
// client.R().
// SetHeaderMultiValues(map[string][]string{
// "Accept": []string{"text/html", "application/xhtml+xml", "application/xml;q=0.9", "image/webp", "*/*;q=0.8"},
// })
//
// Also you can override header value, which was set at client instance level.
func ( *Request) ( map[string][]string) *Request {
for , := range {
.SetHeader(, strings.Join(, ", "))
}
return
}
// SetHeaderVerbatim method is to set a single header field and its value verbatim in the current request.
//
// For Example: To set `all_lowercase` and `UPPERCASE` as `available`.
//
// client.R().
// SetHeaderVerbatim("all_lowercase", "available").
// SetHeaderVerbatim("UPPERCASE", "available")
//
// Also you can override header value, which was set at client instance level.
//
// Since v2.6.0
func ( *Request) (, string) *Request {
.Header[] = []string{}
return
}
// SetQueryParam method sets single parameter and its value in the current request.
// It will be formed as query string for the request.
//
// For Example: `search=kitchen%20papers&size=large` in the URL after `?` mark.
//
// client.R().
// SetQueryParam("search", "kitchen papers").
// SetQueryParam("size", "large")
//
// Also you can override query params value, which was set at client instance level.
func ( *Request) (, string) *Request {
.QueryParam.Set(, )
return
}
// 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.
//
// For Example: `search=kitchen%20papers&size=large` in the URL after `?` mark.
//
// client.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 ( *Request) ( map[string]string) *Request {
for , := range {
.SetQueryParam(, )
}
return
}
// SetQueryParamsFromValues method appends multiple parameters with multi-value
// (`url.Values`) at one go in the current request. It will be formed as
// query string for the request.
//
// For Example: `status=pending&status=approved&status=open` in the URL after `?` mark.
//
// client.R().
// SetQueryParamsFromValues(url.Values{
// "status": []string{"pending", "approved", "open"},
// })
//
// Also you can override query params value, which was set at client instance level.
func ( *Request) ( url.Values) *Request {
for , := range {
for , := range {
.QueryParam.Add(, )
}
}
return
}
// SetQueryString method provides ability to use string as an input to set URL query string for the request.
//
// Using String as an input
//
// client.R().
// SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more")
func ( *Request) ( string) *Request {
, := url.ParseQuery(strings.TrimSpace())
if == nil {
for , := range {
for , := range {
.QueryParam.Add(, )
}
}
} else {
.log.Errorf("%v", )
}
return
}
// 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`.
//
// client.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 ( *Request) ( map[string]string) *Request {
for , := range {
.FormData.Set(, )
}
return
}
// SetFormDataFromValues method appends multiple form parameters with multi-value
// (`url.Values`) at one go in the current request.
//
// client.R().
// SetFormDataFromValues(url.Values{
// "search_criteria": []string{"book", "glass", "pencil"},
// })
//
// Also you can override form data value, which was set at client instance level.
func ( *Request) ( url.Values) *Request {
for , := range {
for , := range {
.FormData.Add(, )
}
}
return
}
// SetBody method sets the request body for the request. It supports various realtime needs as easy.
// We can say its quite handy or powerful. Supported request body data types is `string`,
// `[]byte`, `struct`, `map`, `slice` and `io.Reader`. Body value can be pointer or non-pointer.
// Automatic marshalling for JSON and XML content type, if it is `struct`, `map`, or `slice`.
//
// Note: `io.Reader` is processed as bufferless mode while sending request.
//
// For Example: Struct as a body input, based on content type, it will be marshalled.
//
// client.R().
// SetBody(User{
// Username: "jeeva@myjeeva.com",
// Password: "welcome2resty",
// })
//
// Map as a body input, based on content type, it will be marshalled.
//
// client.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.
//
// client.R().
// SetBody(`{
// "username": "jeeva@getrightcare.com",
// "password": "admin"
// }`)
//
// []byte as a body input. Suitable for raw request such as file upload, serialize & deserialize, etc.
//
// client.R().
// SetBody([]byte("This is my raw request, sent as-is"))
func ( *Request) ( interface{}) *Request {
.Body =
return
}
// SetResult method is to register the response `Result` object for automatic unmarshalling for the request,
// 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.
//
// client.R().SetResult(&AuthToken{})
// // OR
// client.R().SetResult(AuthToken{})
//
// Accessing a result value from response instance.
//
// response.Result().(*AuthToken)
func ( *Request) ( interface{}) *Request {
if != nil {
.Result = getPointer()
}
return
}
// SetError method is to register the request `Error` object for automatic unmarshalling for the request,
// if response status code is greater than 399 and content type either JSON or XML.
//
// Note: Error object can be pointer or non-pointer.
//
// client.R().SetError(&AuthError{})
// // OR
// client.R().SetError(AuthError{})
//
// Accessing a error value from response instance.
//
// response.Error().(*AuthError)
func ( *Request) ( interface{}) *Request {
.Error = getPointer()
return
}
// SetFile method is to set single file field name and its path for multipart upload.
//
// client.R().
// SetFile("my_file", "/Users/jeeva/Gas Bill - Sep.pdf")
func ( *Request) (, string) *Request {
.isMultiPart = true
.FormData.Set("@"+, )
return
}
// SetFiles method is to set multiple file field name and its path for multipart upload.
//
// client.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 ( *Request) ( map[string]string) *Request {
.isMultiPart = true
for , := range {
.FormData.Set("@"+, )
}
return
}
// SetFileReader method is to set single file using io.Reader for multipart upload.
//
// client.R().
// SetFileReader("profile_img", "my-profile-img.png", bytes.NewReader(profileImgBytes)).
// SetFileReader("notes", "user-notes.txt", bytes.NewReader(notesBytes))
func ( *Request) (, string, io.Reader) *Request {
.isMultiPart = true
.multipartFiles = append(.multipartFiles, &File{
Name: ,
ParamName: ,
Reader: ,
})
return
}
// SetMultipartFormData method allows simple form data to be attached to the request as `multipart:form-data`
func ( *Request) ( map[string]string) *Request {
for , := range {
= .SetMultipartField(, "", "", strings.NewReader())
}
return
}
// SetMultipartField method is to set custom data using io.Reader for multipart upload.
func ( *Request) (, , string, io.Reader) *Request {
.isMultiPart = true
.multipartFields = append(.multipartFields, &MultipartField{
Param: ,
FileName: ,
ContentType: ,
Reader: ,
})
return
}
// SetMultipartFields method is to set multiple data fields using io.Reader for multipart upload.
//
// For Example:
//
// client.R().SetMultipartFields(
// &resty.MultipartField{
// Param: "uploadManifest1",
// FileName: "upload-file-1.json",
// ContentType: "application/json",
// Reader: strings.NewReader(`{"input": {"name": "Uploaded document 1", "_filename" : ["file1.txt"]}}`),
// },
// &resty.MultipartField{
// Param: "uploadManifest2",
// FileName: "upload-file-2.json",
// ContentType: "application/json",
// Reader: strings.NewReader(`{"input": {"name": "Uploaded document 2", "_filename" : ["file2.txt"]}}`),
// })
//
// If you have slice already, then simply call-
//
// client.R().SetMultipartFields(fields...)
func ( *Request) ( ...*MultipartField) *Request {
.isMultiPart = true
.multipartFields = append(.multipartFields, ...)
return
}
// SetContentLength method sets the HTTP header `Content-Length` value for current request.
// By default Resty won't set `Content-Length`. Also you have an option to enable for every
// request.
//
// See `Client.SetContentLength`
//
// client.R().SetContentLength(true)
func ( *Request) ( bool) *Request {
.setContentLength =
return
}
// SetBasicAuth method sets the basic authentication header in the current HTTP request.
//
// For Example:
//
// Authorization: Basic <base64-encoded-value>
//
// To set the header for username "go-resty" and password "welcome"
//
// client.R().SetBasicAuth("go-resty", "welcome")
//
// This method overrides the credentials set by method `Client.SetBasicAuth`.
func ( *Request) (, string) *Request {
.UserInfo = &User{Username: , Password: }
return
}
// SetAuthToken method sets the auth token header(Default Scheme: Bearer) in the current HTTP request. Header example:
//
// Authorization: Bearer <auth-token-value-comes-here>
//
// For Example: To set auth token BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F
//
// client.R().SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")
//
// This method overrides the Auth token set by method `Client.SetAuthToken`.
func ( *Request) ( string) *Request {
.Token =
return
}
// SetAuthScheme method sets the auth token scheme type in the HTTP request. For Example:
//
// Authorization: <auth-scheme-value-set-here> <auth-token-value>
//
// For Example: To set the scheme to use OAuth
//
// client.R().SetAuthScheme("OAuth")
//
// This auth header scheme gets added to all the request raised from this client instance.
// Also it can be overridden or set one at the request level is supported.
//
// Information about Auth schemes can be found in RFC7235 which is linked to below along with the page containing
// the currently defined official authentication schemes:
//
// https://tools.ietf.org/html/rfc7235
// https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml#authschemes
//
// This method overrides the Authorization scheme set by method `Client.SetAuthScheme`.
func ( *Request) ( string) *Request {
.AuthScheme =
return
}
// SetDigestAuth method sets the Digest Access auth scheme for the HTTP request. If a server responds with 401 and sends
// a Digest challenge in the WWW-Authenticate Header, the request will be resent with the appropriate Authorization Header.
//
// For Example: To set the Digest scheme with username "Mufasa" and password "Circle Of Life"
//
// client.R().SetDigestAuth("Mufasa", "Circle Of Life")
//
// Information about Digest Access Authentication can be found in RFC7616:
//
// https://datatracker.ietf.org/doc/html/rfc7616
//
// This method overrides the username and password set by method `Client.SetDigestAuth`.
func ( *Request) (, string) *Request {
:= .client.httpClient.Transport
.client.OnBeforeRequest(func( *Client, *Request) error {
.httpClient.Transport = &digestTransport{
digestCredentials: digestCredentials{, },
transport: ,
}
return nil
})
.client.OnAfterResponse(func( *Client, *Response) error {
.httpClient.Transport =
return nil
})
return
}
// 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`.
//
// client.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 ( *Request) ( string) *Request {
.outputFile =
.isSaveResponse = true
return
}
// SetSRV method sets the details to query the service SRV record and execute the
// request.
//
// client.R().
// SetSRV(SRVRecord{"web", "testservice.com"}).
// Get("/get")
func ( *Request) ( *SRVRecord) *Request {
.SRV =
return
}
// SetDoNotParseResponse method instructs `Resty` not to parse the response body automatically.
// Resty exposes the raw response body as `io.ReadCloser`. Also do not forget to close the body,
// otherwise you might get into connection leaks, no connection reuse.
//
// Note: Response middlewares are not applicable, if you use this option. Basically you have
// taken over the control of response parsing from `Resty`.
func ( *Request) ( bool) *Request {
.notParseResponse =
return
}
// SetPathParam method sets single URL path key-value pair in the
// Resty current request instance.
//
// client.R().SetPathParam("userId", "sample@sample.com")
//
// Result:
// URL - /v1/users/{userId}/details
// Composed URL - /v1/users/sample@sample.com/details
//
// client.R().SetPathParam("path", "groups/developers")
//
// Result:
// URL - /v1/users/{userId}/details
// Composed URL - /v1/users/groups%2Fdevelopers/details
//
// It replaces the value of the key while composing the request URL.
// The values will be escaped using `url.PathEscape` function.
//
// Also you can override Path Params value, which was set at client instance
// level.
func ( *Request) (, string) *Request {
.PathParams[] =
return
}
// SetPathParams method sets multiple URL path key-value pairs at one go in the
// Resty current request instance.
//
// client.R().SetPathParams(map[string]string{
// "userId": "sample@sample.com",
// "subAccountId": "100002",
// "path": "groups/developers",
// })
//
// Result:
// URL - /v1/users/{userId}/{subAccountId}/{path}/details
// Composed URL - /v1/users/sample@sample.com/100002/groups%2Fdevelopers/details
//
// It replaces the value of the key while composing request URL.
// The value will be used as it is and will not be escaped.
//
// Also you can override Path Params value, which was set at client instance
// level.
func ( *Request) ( map[string]string) *Request {
for , := range {
.SetPathParam(, )
}
return
}
// SetRawPathParam method sets single URL path key-value pair in the
// Resty current request instance.
//
// client.R().SetPathParam("userId", "sample@sample.com")
//
// Result:
// URL - /v1/users/{userId}/details
// Composed URL - /v1/users/sample@sample.com/details
//
// client.R().SetPathParam("path", "groups/developers")
//
// Result:
// URL - /v1/users/{userId}/details
// Composed URL - /v1/users/groups/developers/details
//
// It replaces the value of the key while composing the request URL.
// The value will be used as it is and will not be escaped.
//
// Also you can override Path Params value, which was set at client instance
// level.
//
// Since v2.8.0
func ( *Request) (, string) *Request {
.RawPathParams[] =
return
}
// SetRawPathParams method sets multiple URL path key-value pairs at one go in the
// Resty current request instance.
//
// client.R().SetPathParams(map[string]string{
// "userId": "sample@sample.com",
// "subAccountId": "100002",
// "path": "groups/developers",
// })
//
// Result:
// URL - /v1/users/{userId}/{subAccountId}/{path}/details
// Composed URL - /v1/users/sample@sample.com/100002/groups/developers/details
//
// It replaces the value of the key while composing request URL.
// The values will be used as they are and will not be escaped.
//
// Also you can override Path Params value, which was set at client instance
// level.
//
// Since v2.8.0
func ( *Request) ( map[string]string) *Request {
for , := range {
.SetRawPathParam(, )
}
return
}
// ExpectContentType method allows to provide fallback `Content-Type` for automatic unmarshalling
// when `Content-Type` response header is unavailable.
func ( *Request) ( string) *Request {
.fallbackContentType =
return
}
// ForceContentType method provides a strong sense of response `Content-Type` for automatic unmarshalling.
// Resty gives this a higher priority than the `Content-Type` response header. This means that if both
// `Request.ForceContentType` is set and the response `Content-Type` is available, `ForceContentType` will win.
func ( *Request) ( string) *Request {
.forceContentType =
return
}
// SetJSONEscapeHTML method is to enable/disable the HTML escape on JSON marshal.
//
// Note: This option only applicable to standard JSON Marshaller.
func ( *Request) ( bool) *Request {
.jsonEscapeHTML =
return
}
// SetCookie method appends a single cookie in the current request instance.
//
// client.R().SetCookie(&http.Cookie{
// Name:"go-resty",
// Value:"This is cookie value",
// })
//
// Note: Method appends the Cookie value into existing Cookie if already existing.
//
// Since v2.1.0
func ( *Request) ( *http.Cookie) *Request {
.Cookies = append(.Cookies, )
return
}
// SetCookies method sets an array of cookies in the current request instance.
//
// cookies := []*http.Cookie{
// &http.Cookie{
// Name:"go-resty-1",
// Value:"This is cookie 1 value",
// },
// &http.Cookie{
// Name:"go-resty-2",
// Value:"This is cookie 2 value",
// },
// }
//
// // Setting a cookies into resty's current request
// client.R().SetCookies(cookies)
//
// Note: Method appends the Cookie value into existing Cookie if already existing.
//
// Since v2.1.0
func ( *Request) ( []*http.Cookie) *Request {
.Cookies = append(.Cookies, ...)
return
}
// SetLogger method sets given writer for logging Resty request and response details.
// By default, requests and responses inherit their logger from the client.
//
// Compliant to interface `resty.Logger`.
func ( *Request) ( Logger) *Request {
.log =
return
}
// SetDebug method enables the debug mode on current request Resty request, It logs
// the details current 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.
//
// client.R().SetDebug(true)
func ( *Request) ( bool) *Request {
.Debug =
return
}
// AddRetryCondition method adds a retry condition function to the request's
// 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.
//
// Note: These retry conditions are checked before all retry conditions of the client.
//
// Since v2.7.0
func ( *Request) ( RetryConditionFunc) *Request {
.retryConditions = append(.retryConditions, )
return
}
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// HTTP request tracing
//_______________________________________________________________________
// EnableTrace method enables trace for the current request
// using `httptrace.ClientTrace` and provides insights.
//
// client := resty.New()
//
// resp, err := client.R().EnableTrace().Get("https://httpbin.org/get")
// fmt.Println("Error:", err)
// fmt.Println("Trace Info:", resp.Request.TraceInfo())
//
// See `Client.EnableTrace` available too to get trace info for all requests.
//
// Since v2.0.0
func ( *Request) () *Request {
.trace = true
return
}
// TraceInfo method returns the trace info for the request.
// If either the Client or Request EnableTrace function has not been called
// prior to the request being made, an empty TraceInfo object will be returned.
//
// Since v2.0.0
func ( *Request) () TraceInfo {
:= .clientTrace
if == nil {
return TraceInfo{}
}
:= TraceInfo{
DNSLookup: .dnsDone.Sub(.dnsStart),
TLSHandshake: .tlsHandshakeDone.Sub(.tlsHandshakeStart),
ServerTime: .gotFirstResponseByte.Sub(.gotConn),
IsConnReused: .gotConnInfo.Reused,
IsConnWasIdle: .gotConnInfo.WasIdle,
ConnIdleTime: .gotConnInfo.IdleTime,
RequestAttempt: .Attempt,
}
// Calculate the total time accordingly,
// when connection is reused
if .gotConnInfo.Reused {
.TotalTime = .endTime.Sub(.getConn)
} else {
.TotalTime = .endTime.Sub(.dnsStart)
}
// Only calculate on successful connections
if !.connectDone.IsZero() {
.TCPConnTime = .connectDone.Sub(.dnsDone)
}
// Only calculate on successful connections
if !.gotConn.IsZero() {
.ConnTime = .gotConn.Sub(.getConn)
}
// Only calculate on successful connections
if !.gotFirstResponseByte.IsZero() {
.ResponseTime = .endTime.Sub(.gotFirstResponseByte)
}
// Capture remote address info when connection is non-nil
if .gotConnInfo.Conn != nil {
.RemoteAddr = .gotConnInfo.Conn.RemoteAddr()
}
return
}
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// HTTP verb method starts here
//_______________________________________________________________________
// Get method does GET HTTP request. It's defined in section 4.3.1 of RFC7231.
func ( *Request) ( string) (*Response, error) {
return .Execute(MethodGet, )
}
// Head method does HEAD HTTP request. It's defined in section 4.3.2 of RFC7231.
func ( *Request) ( string) (*Response, error) {
return .Execute(MethodHead, )
}
// Post method does POST HTTP request. It's defined in section 4.3.3 of RFC7231.
func ( *Request) ( string) (*Response, error) {
return .Execute(MethodPost, )
}
// Put method does PUT HTTP request. It's defined in section 4.3.4 of RFC7231.
func ( *Request) ( string) (*Response, error) {
return .Execute(MethodPut, )
}
// Delete method does DELETE HTTP request. It's defined in section 4.3.5 of RFC7231.
func ( *Request) ( string) (*Response, error) {
return .Execute(MethodDelete, )
}
// Options method does OPTIONS HTTP request. It's defined in section 4.3.7 of RFC7231.
func ( *Request) ( string) (*Response, error) {
return .Execute(MethodOptions, )
}
// Patch method does PATCH HTTP request. It's defined in section 2 of RFC5789.
func ( *Request) ( string) (*Response, error) {
return .Execute(MethodPatch, )
}
// Send method performs the HTTP request using the method and URL already defined
// for current `Request`.
//
// req := client.R()
// req.Method = resty.GET
// req.URL = "http://httpbin.org/get"
// resp, err := req.Send()
func ( *Request) () (*Response, error) {
return .Execute(.Method, .URL)
}
// Execute method performs the HTTP request with given HTTP method and URL
// for current `Request`.
//
// resp, err := client.R().Execute(resty.GET, "http://httpbin.org/get")
func ( *Request) (, string) (*Response, error) {
var []*net.SRV
var *Response
var error
defer func() {
if := recover(); != nil {
if , := .(error); {
.client.onPanicHooks(, )
} else {
.client.onPanicHooks(, fmt.Errorf("panic %v", ))
}
panic()
}
}()
if .isMultiPart && !( == MethodPost || == MethodPut || == MethodPatch) {
// No OnError hook here since this is a request validation error
:= fmt.Errorf("multipart content is not allowed in HTTP verb [%v]", )
.client.onInvalidHooks(, )
return nil,
}
if .SRV != nil {
_, , = net.LookupSRV(.SRV.Service, "tcp", .SRV.Domain)
if != nil {
.client.onErrorHooks(, nil, )
return nil,
}
}
.Method =
.URL = .selectAddr(, , 0)
if .client.RetryCount == 0 {
.Attempt = 1
, = .client.execute()
.client.onErrorHooks(, , unwrapNoRetryErr())
return , unwrapNoRetryErr()
}
= Backoff(
func() (*Response, error) {
.Attempt++
.URL = .selectAddr(, , .Attempt)
, = .client.execute()
if != nil {
.log.Warnf("%v, Attempt %v", , .Attempt)
}
return ,
},
Retries(.client.RetryCount),
WaitTime(.client.RetryWaitTime),
MaxWaitTime(.client.RetryMaxWaitTime),
RetryConditions(append(.retryConditions, .client.RetryConditions...)),
RetryHooks(.client.RetryHooks),
ResetMultipartReaders(.client.RetryResetReaders),
)
if != nil {
.log.Errorf("%v", )
}
.client.onErrorHooks(, , unwrapNoRetryErr())
return , unwrapNoRetryErr()
}
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// SRVRecord struct
//_______________________________________________________________________
// SRVRecord struct holds the data to query the SRV record for the
// following service.
type SRVRecord struct {
Service string
Domain string
}
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Request Unexported methods
//_______________________________________________________________________
func ( *Request) ( int64) ( string) {
= "***** NO CONTENT *****"
if !isPayloadSupported(.Method, .client.AllowGetMethodPayload) {
return
}
if , := .Body.(io.Reader); {
= "***** BODY IS io.Reader *****"
return
}
// multipart or form-data
if .isMultiPart || .isFormData {
:= int64(.bodyBuf.Len())
if > {
= fmt.Sprintf("***** REQUEST TOO LARGE (size - %d) *****", )
return
}
= .bodyBuf.String()
return
}
// request body data
if .Body == nil {
return
}
var []byte
var error
:= .Header.Get(hdrContentTypeKey)
:= kindOf(.Body)
if canJSONMarshal(, ) {
, = noescapeJSONMarshalIndent(&.Body)
} else if IsXMLType() && ( == reflect.Struct) {
, = xml.MarshalIndent(&.Body, "", " ")
} else if , := .Body.(string); {
if IsJSONType() {
:= []byte()
:= acquireBuffer()
defer releaseBuffer()
if = json.Indent(, , "", " "); == nil {
= .Bytes()
}
} else {
=
}
} else if , := .Body.([]byte); {
= fmt.Sprintf("***** BODY IS byte(s) (size - %d) *****", len())
return
}
if != nil && == nil {
= string()
}
if len() > 0 {
:= int64(len([]byte()))
if > {
= fmt.Sprintf("***** REQUEST TOO LARGE (size - %d) *****", )
}
}
return
}
func ( *Request) ( []*net.SRV, string, int) string {
if == nil {
return
}
:= % len()
:= strings.TrimRight([].Target, ".")
= strings.TrimLeft(, "/")
return fmt.Sprintf("%s://%s:%d/%s", .client.scheme, , [].Port, )
}
func ( *Request) () {
if .values == nil {
.values = make(map[string]interface{})
}
}
var noescapeJSONMarshal = func( interface{}) (*bytes.Buffer, error) {
:= acquireBuffer()
:= json.NewEncoder()
.SetEscapeHTML(false)
if := .Encode(); != nil {
releaseBuffer()
return nil,
}
return , nil
}
var noescapeJSONMarshalIndent = func( interface{}) ([]byte, error) {
:= acquireBuffer()
defer releaseBuffer()
:= json.NewEncoder()
.SetEscapeHTML(false)
.SetIndent("", " ")
if := .Encode(); != nil {
return nil,
}
return .Bytes(), nil
}
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |