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