1// Copyright (c) 2015-2018 Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
2// resty source code and usage is governed by a MIT style
3// license that can be found in the LICENSE file.
4
5package resty
6
7import (
8	"bytes"
9	"compress/gzip"
10	"crypto/tls"
11	"crypto/x509"
12	"errors"
13	"fmt"
14	"io"
15	"io/ioutil"
16	"log"
17	"net/http"
18	"net/url"
19	"reflect"
20	"regexp"
21	"strings"
22	"sync"
23	"time"
24)
25
26const (
27	// MethodGet HTTP method
28	MethodGet = "GET"
29
30	// MethodPost HTTP method
31	MethodPost = "POST"
32
33	// MethodPut HTTP method
34	MethodPut = "PUT"
35
36	// MethodDelete HTTP method
37	MethodDelete = "DELETE"
38
39	// MethodPatch HTTP method
40	MethodPatch = "PATCH"
41
42	// MethodHead HTTP method
43	MethodHead = "HEAD"
44
45	// MethodOptions HTTP method
46	MethodOptions = "OPTIONS"
47)
48
49var (
50	hdrUserAgentKey       = http.CanonicalHeaderKey("User-Agent")
51	hdrAcceptKey          = http.CanonicalHeaderKey("Accept")
52	hdrContentTypeKey     = http.CanonicalHeaderKey("Content-Type")
53	hdrContentLengthKey   = http.CanonicalHeaderKey("Content-Length")
54	hdrContentEncodingKey = http.CanonicalHeaderKey("Content-Encoding")
55	hdrAuthorizationKey   = http.CanonicalHeaderKey("Authorization")
56
57	plainTextType   = "text/plain; charset=utf-8"
58	jsonContentType = "application/json; charset=utf-8"
59	formContentType = "application/x-www-form-urlencoded"
60
61	jsonCheck = regexp.MustCompile(`(?i:(application|text)/(json|.*\+json)(;|$))`)
62	xmlCheck  = regexp.MustCompile(`(?i:(application|text)/(xml|.*\+xml)(;|$))`)
63
64	hdrUserAgentValue = "go-resty/%s (https://github.com/go-resty/resty)"
65	bufPool           = &sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}
66)
67
68// Client type is used for HTTP/RESTful global values
69// for all request raised from the client
70type Client struct {
71	HostURL               string
72	QueryParam            url.Values
73	FormData              url.Values
74	Header                http.Header
75	UserInfo              *User
76	Token                 string
77	Cookies               []*http.Cookie
78	Error                 reflect.Type
79	Debug                 bool
80	DisableWarn           bool
81	AllowGetMethodPayload bool
82	Log                   *log.Logger
83	RetryCount            int
84	RetryWaitTime         time.Duration
85	RetryMaxWaitTime      time.Duration
86	RetryConditions       []RetryConditionFunc
87	JSONMarshal           func(v interface{}) ([]byte, error)
88	JSONUnmarshal         func(data []byte, v interface{}) error
89
90	jsonEscapeHTML     bool
91	httpClient         *http.Client
92	setContentLength   bool
93	isHTTPMode         bool
94	outputDirectory    string
95	scheme             string
96	proxyURL           *url.URL
97	closeConnection    bool
98	notParseResponse   bool
99	debugBodySizeLimit int64
100	logPrefix          string
101	pathParams         map[string]string
102	beforeRequest      []func(*Client, *Request) error
103	udBeforeRequest    []func(*Client, *Request) error
104	preReqHook         func(*Client, *Request) error
105	afterResponse      []func(*Client, *Response) error
106}
107
108// User type is to hold an username and password information
109type User struct {
110	Username, Password string
111}
112
113//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
114// Client methods
115//___________________________________
116
117// SetHostURL method is to set Host URL in the client instance. It will be used with request
118// raised from this client with relative URL
119//		// Setting HTTP address
120//		resty.SetHostURL("http://myjeeva.com")
121//
122//		// Setting HTTPS address
123//		resty.SetHostURL("https://myjeeva.com")
124//
125func (c *Client) SetHostURL(url string) *Client {
126	c.HostURL = strings.TrimRight(url, "/")
127	return c
128}
129
130// SetHeader method sets a single header field and its value in the client instance.
131// These headers will be applied to all requests raised from this client instance.
132// Also it can be overridden at request level header options, see `resty.R().SetHeader`
133// or `resty.R().SetHeaders`.
134//
135// Example: To set `Content-Type` and `Accept` as `application/json`
136//
137// 		resty.
138// 			SetHeader("Content-Type", "application/json").
139// 			SetHeader("Accept", "application/json")
140//
141func (c *Client) SetHeader(header, value string) *Client {
142	c.Header.Set(header, value)
143	return c
144}
145
146// SetHeaders method sets multiple headers field and its values at one go in the client instance.
147// These headers will be applied to all requests raised from this client instance. Also it can be
148// overridden at request level headers options, see `resty.R().SetHeaders` or `resty.R().SetHeader`.
149//
150// Example: To set `Content-Type` and `Accept` as `application/json`
151//
152// 		resty.SetHeaders(map[string]string{
153//				"Content-Type": "application/json",
154//				"Accept": "application/json",
155//			})
156//
157func (c *Client) SetHeaders(headers map[string]string) *Client {
158	for h, v := range headers {
159		c.Header.Set(h, v)
160	}
161
162	return c
163}
164
165// SetCookieJar method sets custom http.CookieJar in the resty client. Its way to override default.
166// Example: sometimes we don't want to save cookies in api contacting, we can remove the default
167// CookieJar in resty client.
168//
169//		resty.SetCookieJar(nil)
170//
171func (c *Client) SetCookieJar(jar http.CookieJar) *Client {
172	c.httpClient.Jar = jar
173	return c
174}
175
176// SetCookie method appends a single cookie in the client instance.
177// These cookies will be added to all the request raised from this client instance.
178// 		resty.SetCookie(&http.Cookie{
179// 					Name:"go-resty",
180//					Value:"This is cookie value",
181//					Path: "/",
182// 					Domain: "sample.com",
183// 					MaxAge: 36000,
184// 					HttpOnly: true,
185//					Secure: false,
186// 				})
187//
188func (c *Client) SetCookie(hc *http.Cookie) *Client {
189	c.Cookies = append(c.Cookies, hc)
190	return c
191}
192
193// SetCookies method sets an array of cookies in the client instance.
194// These cookies will be added to all the request raised from this client instance.
195// 		cookies := make([]*http.Cookie, 0)
196//
197//		cookies = append(cookies, &http.Cookie{
198// 					Name:"go-resty-1",
199//					Value:"This is cookie 1 value",
200//					Path: "/",
201// 					Domain: "sample.com",
202// 					MaxAge: 36000,
203// 					HttpOnly: true,
204//					Secure: false,
205// 				})
206//
207//		cookies = append(cookies, &http.Cookie{
208// 					Name:"go-resty-2",
209//					Value:"This is cookie 2 value",
210//					Path: "/",
211// 					Domain: "sample.com",
212// 					MaxAge: 36000,
213// 					HttpOnly: true,
214//					Secure: false,
215// 				})
216//
217//		// Setting a cookies into resty
218// 		resty.SetCookies(cookies)
219//
220func (c *Client) SetCookies(cs []*http.Cookie) *Client {
221	c.Cookies = append(c.Cookies, cs...)
222	return c
223}
224
225// SetQueryParam method sets single parameter and its value in the client instance.
226// It will be formed as query string for the request. For example: `search=kitchen%20papers&size=large`
227// in the URL after `?` mark. These query params will be added to all the request raised from
228// this client instance. Also it can be overridden at request level Query Param options,
229// see `resty.R().SetQueryParam` or `resty.R().SetQueryParams`.
230// 		resty.
231//			SetQueryParam("search", "kitchen papers").
232//			SetQueryParam("size", "large")
233//
234func (c *Client) SetQueryParam(param, value string) *Client {
235	c.QueryParam.Set(param, value)
236	return c
237}
238
239// SetQueryParams method sets multiple parameters and their values at one go in the client instance.
240// It will be formed as query string for the request. For example: `search=kitchen%20papers&size=large`
241// in the URL after `?` mark. These query params will be added to all the request raised from this
242// client instance. Also it can be overridden at request level Query Param options,
243// see `resty.R().SetQueryParams` or `resty.R().SetQueryParam`.
244// 		resty.SetQueryParams(map[string]string{
245//				"search": "kitchen papers",
246//				"size": "large",
247//			})
248//
249func (c *Client) SetQueryParams(params map[string]string) *Client {
250	for p, v := range params {
251		c.SetQueryParam(p, v)
252	}
253
254	return c
255}
256
257// SetFormData method sets Form parameters and their values in the client instance.
258// It's applicable only HTTP method `POST` and `PUT` and requets content type would be set as
259// `application/x-www-form-urlencoded`. These form data will be added to all the request raised from
260// this client instance. Also it can be overridden at request level form data, see `resty.R().SetFormData`.
261// 		resty.SetFormData(map[string]string{
262//				"access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
263//				"user_id": "3455454545",
264//			})
265//
266func (c *Client) SetFormData(data map[string]string) *Client {
267	for k, v := range data {
268		c.FormData.Set(k, v)
269	}
270
271	return c
272}
273
274// SetBasicAuth method sets the basic authentication header in the HTTP request. Example:
275//		Authorization: Basic <base64-encoded-value>
276//
277// Example: To set the header for username "go-resty" and password "welcome"
278// 		resty.SetBasicAuth("go-resty", "welcome")
279//
280// This basic auth information gets added to all the request rasied from this client instance.
281// Also it can be overridden or set one at the request level is supported, see `resty.R().SetBasicAuth`.
282//
283func (c *Client) SetBasicAuth(username, password string) *Client {
284	c.UserInfo = &User{Username: username, Password: password}
285	return c
286}
287
288// SetAuthToken method sets bearer auth token header in the HTTP request. Example:
289// 		Authorization: Bearer <auth-token-value-comes-here>
290//
291// Example: To set auth token BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F
292//
293// 		resty.SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")
294//
295// This bearer auth token gets added to all the request rasied from this client instance.
296// Also it can be overridden or set one at the request level is supported, see `resty.R().SetAuthToken`.
297//
298func (c *Client) SetAuthToken(token string) *Client {
299	c.Token = token
300	return c
301}
302
303// R method creates a request instance, its used for Get, Post, Put, Delete, Patch, Head and Options.
304func (c *Client) R() *Request {
305	r := &Request{
306		QueryParam: url.Values{},
307		FormData:   url.Values{},
308		Header:     http.Header{},
309
310		client:          c,
311		multipartFiles:  []*File{},
312		multipartFields: []*multipartField{},
313		pathParams:      map[string]string{},
314		jsonEscapeHTML:  true,
315	}
316
317	return r
318}
319
320// NewRequest is an alias for R(). Creates a request instance, its used for
321// Get, Post, Put, Delete, Patch, Head and Options.
322func (c *Client) NewRequest() *Request {
323	return c.R()
324}
325
326// OnBeforeRequest method appends request middleware into the before request chain.
327// Its gets applied after default `go-resty` request middlewares and before request
328// been sent from `go-resty` to host server.
329// 		resty.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {
330//				// Now you have access to Client and Request instance
331//				// manipulate it as per your need
332//
333//				return nil 	// if its success otherwise return error
334//			})
335//
336func (c *Client) OnBeforeRequest(m func(*Client, *Request) error) *Client {
337	c.udBeforeRequest = append(c.udBeforeRequest, m)
338	return c
339}
340
341// OnAfterResponse method appends response middleware into the after response chain.
342// Once we receive response from host server, default `go-resty` response middleware
343// gets applied and then user assigened response middlewares applied.
344// 		resty.OnAfterResponse(func(c *resty.Client, r *resty.Response) error {
345//				// Now you have access to Client and Response instance
346//				// manipulate it as per your need
347//
348//				return nil 	// if its success otherwise return error
349//			})
350//
351func (c *Client) OnAfterResponse(m func(*Client, *Response) error) *Client {
352	c.afterResponse = append(c.afterResponse, m)
353	return c
354}
355
356// SetPreRequestHook method sets the given pre-request function into resty client.
357// It is called right before the request is fired.
358//
359// Note: Only one pre-request hook can be registered. Use `resty.OnBeforeRequest` for mutilple.
360func (c *Client) SetPreRequestHook(h func(*Client, *Request) error) *Client {
361	if c.preReqHook != nil {
362		c.Log.Printf("Overwriting an existing pre-request hook: %s", functionName(h))
363	}
364	c.preReqHook = h
365	return c
366}
367
368// SetDebug method enables the debug mode on `go-resty` client. Client logs details of every request and response.
369// For `Request` it logs information such as HTTP verb, Relative URL path, Host, Headers, Body if it has one.
370// For `Response` it logs information such as Status, Response Time, Headers, Body if it has one.
371//		resty.SetDebug(true)
372//
373func (c *Client) SetDebug(d bool) *Client {
374	c.Debug = d
375	return c
376}
377
378// SetDebugBodyLimit sets the maximum size for which the response body will be logged in debug mode.
379//		resty.SetDebugBodyLimit(1000000)
380//
381func (c *Client) SetDebugBodyLimit(sl int64) *Client {
382	c.debugBodySizeLimit = sl
383	return c
384}
385
386// SetDisableWarn method disables the warning message on `go-resty` client.
387// For example: go-resty warns the user when BasicAuth used on HTTP mode.
388//		resty.SetDisableWarn(true)
389//
390func (c *Client) SetDisableWarn(d bool) *Client {
391	c.DisableWarn = d
392	return c
393}
394
395// SetAllowGetMethodPayload method allows the GET method with payload on `go-resty` client.
396// For example: go-resty allows the user sends request with a payload on HTTP GET method.
397//		resty.SetAllowGetMethodPayload(true)
398//
399func (c *Client) SetAllowGetMethodPayload(a bool) *Client {
400	c.AllowGetMethodPayload = a
401	return c
402}
403
404// SetLogger method sets given writer for logging go-resty request and response details.
405// Default is os.Stderr
406// 		file, _ := os.OpenFile("/Users/jeeva/go-resty.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
407//
408//		resty.SetLogger(file)
409//
410func (c *Client) SetLogger(w io.Writer) *Client {
411	c.Log = getLogger(w)
412	return c
413}
414
415// SetContentLength method enables the HTTP header `Content-Length` value for every request.
416// By default go-resty won't set `Content-Length`.
417// 		resty.SetContentLength(true)
418//
419// Also you have an option to enable for particular request. See `resty.R().SetContentLength`
420//
421func (c *Client) SetContentLength(l bool) *Client {
422	c.setContentLength = l
423	return c
424}
425
426// SetTimeout method sets timeout for request raised from client.
427//		resty.SetTimeout(time.Duration(1 * time.Minute))
428//
429func (c *Client) SetTimeout(timeout time.Duration) *Client {
430	c.httpClient.Timeout = timeout
431	return c
432}
433
434// SetError method is to register the global or client common `Error` object into go-resty.
435// It is used for automatic unmarshalling if response status code is greater than 399 and
436// content type either JSON or XML. Can be pointer or non-pointer.
437// 		resty.SetError(&Error{})
438//		// OR
439//		resty.SetError(Error{})
440//
441func (c *Client) SetError(err interface{}) *Client {
442	c.Error = typeOf(err)
443	return c
444}
445
446// SetRedirectPolicy method sets the client redirect poilicy. go-resty provides ready to use
447// redirect policies. Wanna create one for yourself refer `redirect.go`.
448//
449//		resty.SetRedirectPolicy(FlexibleRedirectPolicy(20))
450//
451// 		// Need multiple redirect policies together
452//		resty.SetRedirectPolicy(FlexibleRedirectPolicy(20), DomainCheckRedirectPolicy("host1.com", "host2.net"))
453//
454func (c *Client) SetRedirectPolicy(policies ...interface{}) *Client {
455	for _, p := range policies {
456		if _, ok := p.(RedirectPolicy); !ok {
457			c.Log.Printf("ERORR: %v does not implement resty.RedirectPolicy (missing Apply method)",
458				functionName(p))
459		}
460	}
461
462	c.httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
463		for _, p := range policies {
464			if err := p.(RedirectPolicy).Apply(req, via); err != nil {
465				return err
466			}
467		}
468		return nil // looks good, go ahead
469	}
470
471	return c
472}
473
474// SetRetryCount method enables retry on `go-resty` client and allows you
475// to set no. of retry count. Resty uses a Backoff mechanism.
476func (c *Client) SetRetryCount(count int) *Client {
477	c.RetryCount = count
478	return c
479}
480
481// SetRetryWaitTime method sets default wait time to sleep before retrying
482// request.
483// Default is 100 milliseconds.
484func (c *Client) SetRetryWaitTime(waitTime time.Duration) *Client {
485	c.RetryWaitTime = waitTime
486	return c
487}
488
489// SetRetryMaxWaitTime method sets max wait time to sleep before retrying
490// request.
491// Default is 2 seconds.
492func (c *Client) SetRetryMaxWaitTime(maxWaitTime time.Duration) *Client {
493	c.RetryMaxWaitTime = maxWaitTime
494	return c
495}
496
497// AddRetryCondition method adds a retry condition function to array of functions
498// that are checked to determine if the request is retried. The request will
499// retry if any of the functions return true and error is nil.
500func (c *Client) AddRetryCondition(condition RetryConditionFunc) *Client {
501	c.RetryConditions = append(c.RetryConditions, condition)
502	return c
503}
504
505// SetHTTPMode method sets go-resty mode to 'http'
506func (c *Client) SetHTTPMode() *Client {
507	return c.SetMode("http")
508}
509
510// SetRESTMode method sets go-resty mode to 'rest'
511func (c *Client) SetRESTMode() *Client {
512	return c.SetMode("rest")
513}
514
515// SetMode method sets go-resty client mode to given value such as 'http' & 'rest'.
516//	'rest':
517//		- No Redirect
518//		- Automatic response unmarshal if it is JSON or XML
519//	'http':
520//		- Up to 10 Redirects
521//		- No automatic unmarshall. Response will be treated as `response.String()`
522//
523// If you want more redirects, use FlexibleRedirectPolicy
524//		resty.SetRedirectPolicy(FlexibleRedirectPolicy(20))
525//
526func (c *Client) SetMode(mode string) *Client {
527	// HTTP
528	if mode == "http" {
529		c.isHTTPMode = true
530		c.SetRedirectPolicy(FlexibleRedirectPolicy(10))
531		c.afterResponse = []func(*Client, *Response) error{
532			responseLogger,
533			saveResponseIntoFile,
534		}
535		return c
536	}
537
538	// RESTful
539	c.isHTTPMode = false
540	c.SetRedirectPolicy(NoRedirectPolicy())
541	c.afterResponse = []func(*Client, *Response) error{
542		responseLogger,
543		parseResponseBody,
544		saveResponseIntoFile,
545	}
546	return c
547}
548
549// Mode method returns the current client mode. Typically its a "http" or "rest".
550// Default is "rest"
551func (c *Client) Mode() string {
552	if c.isHTTPMode {
553		return "http"
554	}
555	return "rest"
556}
557
558// SetTLSClientConfig method sets TLSClientConfig for underling client Transport.
559//
560// Example:
561// 		// One can set custom root-certificate. Refer: http://golang.org/pkg/crypto/tls/#example_Dial
562//		resty.SetTLSClientConfig(&tls.Config{ RootCAs: roots })
563//
564// 		// or One can disable security check (https)
565//		resty.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })
566// Note: This method overwrites existing `TLSClientConfig`.
567//
568func (c *Client) SetTLSClientConfig(config *tls.Config) *Client {
569	transport, err := c.getTransport()
570	if err != nil {
571		c.Log.Printf("ERROR %v", err)
572		return c
573	}
574	transport.TLSClientConfig = config
575	return c
576}
577
578// SetProxy method sets the Proxy URL and Port for resty client.
579//		resty.SetProxy("http://proxyserver:8888")
580//
581// Alternatives: At request level proxy, see `Request.SetProxy`.  OR Without this `SetProxy` method,
582// you can also set Proxy via environment variable. By default `Go` uses setting from `HTTP_PROXY`.
583//
584func (c *Client) SetProxy(proxyURL string) *Client {
585	transport, err := c.getTransport()
586	if err != nil {
587		c.Log.Printf("ERROR %v", err)
588		return c
589	}
590
591	if pURL, err := url.Parse(proxyURL); err == nil {
592		c.proxyURL = pURL
593		transport.Proxy = http.ProxyURL(c.proxyURL)
594	} else {
595		c.Log.Printf("ERROR %v", err)
596		c.RemoveProxy()
597	}
598	return c
599}
600
601// RemoveProxy method removes the proxy configuration from resty client
602//		resty.RemoveProxy()
603//
604func (c *Client) RemoveProxy() *Client {
605	transport, err := c.getTransport()
606	if err != nil {
607		c.Log.Printf("ERROR %v", err)
608		return c
609	}
610	c.proxyURL = nil
611	transport.Proxy = nil
612	return c
613}
614
615// SetCertificates method helps to set client certificates into resty conveniently.
616//
617func (c *Client) SetCertificates(certs ...tls.Certificate) *Client {
618	config, err := c.getTLSConfig()
619	if err != nil {
620		c.Log.Printf("ERROR %v", err)
621		return c
622	}
623	config.Certificates = append(config.Certificates, certs...)
624	return c
625}
626
627// SetRootCertificate method helps to add one or more root certificates into resty client
628// 		resty.SetRootCertificate("/path/to/root/pemFile.pem")
629//
630func (c *Client) SetRootCertificate(pemFilePath string) *Client {
631	rootPemData, err := ioutil.ReadFile(pemFilePath)
632	if err != nil {
633		c.Log.Printf("ERROR %v", err)
634		return c
635	}
636
637	config, err := c.getTLSConfig()
638	if err != nil {
639		c.Log.Printf("ERROR %v", err)
640		return c
641	}
642	if config.RootCAs == nil {
643		config.RootCAs = x509.NewCertPool()
644	}
645
646	config.RootCAs.AppendCertsFromPEM(rootPemData)
647
648	return c
649}
650
651// SetOutputDirectory method sets output directory for saving HTTP response into file.
652// If the output directory not exists then resty creates one. This setting is optional one,
653// if you're planning using absoule path in `Request.SetOutput` and can used together.
654// 		resty.SetOutputDirectory("/save/http/response/here")
655//
656func (c *Client) SetOutputDirectory(dirPath string) *Client {
657	c.outputDirectory = dirPath
658	return c
659}
660
661// SetTransport method sets custom `*http.Transport` or any `http.RoundTripper`
662// compatible interface implementation in the resty client.
663//
664// NOTE:
665//
666// - If transport is not type of `*http.Transport` then you may not be able to
667// take advantage of some of the `resty` client settings.
668//
669// - It overwrites the resty client transport instance and it's configurations.
670//
671//		transport := &http.Transport{
672//			// somthing like Proxying to httptest.Server, etc...
673//			Proxy: func(req *http.Request) (*url.URL, error) {
674//				return url.Parse(server.URL)
675//			},
676//		}
677//
678//		resty.SetTransport(transport)
679//
680func (c *Client) SetTransport(transport http.RoundTripper) *Client {
681	if transport != nil {
682		c.httpClient.Transport = transport
683	}
684	return c
685}
686
687// SetScheme method sets custom scheme in the resty client. It's way to override default.
688// 		resty.SetScheme("http")
689//
690func (c *Client) SetScheme(scheme string) *Client {
691	if !IsStringEmpty(scheme) {
692		c.scheme = scheme
693	}
694
695	return c
696}
697
698// SetCloseConnection method sets variable `Close` in http request struct with the given
699// value. More info: https://golang.org/src/net/http/request.go
700func (c *Client) SetCloseConnection(close bool) *Client {
701	c.closeConnection = close
702	return c
703}
704
705// SetDoNotParseResponse method instructs `Resty` not to parse the response body automatically.
706// Resty exposes the raw response body as `io.ReadCloser`. Also do not forget to close the body,
707// otherwise you might get into connection leaks, no connection reuse.
708//
709// Please Note: Response middlewares are not applicable, if you use this option. Basically you have
710// taken over the control of response parsing from `Resty`.
711func (c *Client) SetDoNotParseResponse(parse bool) *Client {
712	c.notParseResponse = parse
713	return c
714}
715
716// SetLogPrefix method sets the Resty logger prefix value.
717func (c *Client) SetLogPrefix(prefix string) *Client {
718	c.logPrefix = prefix
719	c.Log.SetPrefix(prefix)
720	return c
721}
722
723// SetPathParams method sets multiple URL path key-value pairs at one go in the
724// resty client instance.
725// 		resty.SetPathParams(map[string]string{
726// 		   "userId": "sample@sample.com",
727// 		   "subAccountId": "100002",
728// 		})
729//
730// 		Result:
731// 		   URL - /v1/users/{userId}/{subAccountId}/details
732// 		   Composed URL - /v1/users/sample@sample.com/100002/details
733// It replace the value of the key while composing request URL. Also it can be
734// overridden at request level Path Params options, see `Request.SetPathParams`.
735func (c *Client) SetPathParams(params map[string]string) *Client {
736	for p, v := range params {
737		c.pathParams[p] = v
738	}
739	return c
740}
741
742// SetJSONEscapeHTML method is to enable/disable the HTML escape on JSON marshal.
743//
744// NOTE: This option only applicable to standard JSON Marshaller.
745func (c *Client) SetJSONEscapeHTML(b bool) *Client {
746	c.jsonEscapeHTML = b
747	return c
748}
749
750// IsProxySet method returns the true if proxy is set on client otherwise false.
751func (c *Client) IsProxySet() bool {
752	return c.proxyURL != nil
753}
754
755// GetClient method returns the current http.Client used by the resty client.
756func (c *Client) GetClient() *http.Client {
757	return c.httpClient
758}
759
760//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
761// Client Unexported methods
762//___________________________________
763
764// executes the given `Request` object and returns response
765func (c *Client) execute(req *Request) (*Response, error) {
766	defer releaseBuffer(req.bodyBuf)
767	// Apply Request middleware
768	var err error
769
770	// user defined on before request methods
771	// to modify the *resty.Request object
772	for _, f := range c.udBeforeRequest {
773		if err = f(c, req); err != nil {
774			return nil, err
775		}
776	}
777
778	// resty middlewares
779	for _, f := range c.beforeRequest {
780		if err = f(c, req); err != nil {
781			return nil, err
782		}
783	}
784
785	// call pre-request if defined
786	if c.preReqHook != nil {
787		if err = c.preReqHook(c, req); err != nil {
788			return nil, err
789		}
790	}
791
792	if hostHeader := req.Header.Get("Host"); hostHeader != "" {
793		req.RawRequest.Host = hostHeader
794	}
795
796	req.Time = time.Now()
797	resp, err := c.httpClient.Do(req.RawRequest)
798
799	response := &Response{
800		Request:     req,
801		RawResponse: resp,
802		receivedAt:  time.Now(),
803	}
804
805	if err != nil || req.notParseResponse || c.notParseResponse {
806		return response, err
807	}
808
809	if !req.isSaveResponse {
810		defer closeq(resp.Body)
811		body := resp.Body
812
813		// GitHub #142
814		if strings.EqualFold(resp.Header.Get(hdrContentEncodingKey), "gzip") && resp.ContentLength > 0 {
815			if _, ok := body.(*gzip.Reader); !ok {
816				body, err = gzip.NewReader(body)
817				if err != nil {
818					return response, err
819				}
820				defer closeq(body)
821			}
822		}
823
824		if response.body, err = ioutil.ReadAll(body); err != nil {
825			return response, err
826		}
827
828		response.size = int64(len(response.body))
829	}
830
831	// Apply Response middleware
832	for _, f := range c.afterResponse {
833		if err = f(c, response); err != nil {
834			break
835		}
836	}
837
838	return response, err
839}
840
841// enables a log prefix
842func (c *Client) enableLogPrefix() {
843	c.Log.SetFlags(log.LstdFlags)
844	c.Log.SetPrefix(c.logPrefix)
845}
846
847// disables a log prefix
848func (c *Client) disableLogPrefix() {
849	c.Log.SetFlags(0)
850	c.Log.SetPrefix("")
851}
852
853// getting TLS client config if not exists then create one
854func (c *Client) getTLSConfig() (*tls.Config, error) {
855	transport, err := c.getTransport()
856	if err != nil {
857		return nil, err
858	}
859	if transport.TLSClientConfig == nil {
860		transport.TLSClientConfig = &tls.Config{}
861	}
862	return transport.TLSClientConfig, nil
863}
864
865// returns `*http.Transport` currently in use or error
866// in case currently used `transport` is not an `*http.Transport`
867func (c *Client) getTransport() (*http.Transport, error) {
868	if c.httpClient.Transport == nil {
869		c.SetTransport(new(http.Transport))
870	}
871
872	if transport, ok := c.httpClient.Transport.(*http.Transport); ok {
873		return transport, nil
874	}
875	return nil, errors.New("current transport is not an *http.Transport instance")
876}
877
878//
879// File
880//
881
882// File represent file information for multipart request
883type File struct {
884	Name      string
885	ParamName string
886	io.Reader
887}
888
889// String returns string value of current file details
890func (f *File) String() string {
891	return fmt.Sprintf("ParamName: %v; FileName: %v", f.ParamName, f.Name)
892}
893
894// multipartField represent custom data part for multipart request
895type multipartField struct {
896	Param       string
897	FileName    string
898	ContentType string
899	io.Reader
900}
901