client.go raw

   1  // Copyright (c) 2015-2024 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  
   5  package resty
   6  
   7  import (
   8  	"bytes"
   9  	"compress/gzip"
  10  	"crypto/tls"
  11  	"crypto/x509"
  12  	"encoding/json"
  13  	"encoding/xml"
  14  	"errors"
  15  	"fmt"
  16  	"io"
  17  	"math"
  18  	"net/http"
  19  	"net/url"
  20  	"os"
  21  	"reflect"
  22  	"regexp"
  23  	"strings"
  24  	"sync"
  25  	"time"
  26  )
  27  
  28  const (
  29  	// MethodGet HTTP method
  30  	MethodGet = "GET"
  31  
  32  	// MethodPost HTTP method
  33  	MethodPost = "POST"
  34  
  35  	// MethodPut HTTP method
  36  	MethodPut = "PUT"
  37  
  38  	// MethodDelete HTTP method
  39  	MethodDelete = "DELETE"
  40  
  41  	// MethodPatch HTTP method
  42  	MethodPatch = "PATCH"
  43  
  44  	// MethodHead HTTP method
  45  	MethodHead = "HEAD"
  46  
  47  	// MethodOptions HTTP method
  48  	MethodOptions = "OPTIONS"
  49  )
  50  
  51  var (
  52  	hdrUserAgentKey       = http.CanonicalHeaderKey("User-Agent")
  53  	hdrAcceptKey          = http.CanonicalHeaderKey("Accept")
  54  	hdrContentTypeKey     = http.CanonicalHeaderKey("Content-Type")
  55  	hdrContentLengthKey   = http.CanonicalHeaderKey("Content-Length")
  56  	hdrContentEncodingKey = http.CanonicalHeaderKey("Content-Encoding")
  57  	hdrLocationKey        = http.CanonicalHeaderKey("Location")
  58  	hdrAuthorizationKey   = http.CanonicalHeaderKey("Authorization")
  59  	hdrWwwAuthenticateKey = http.CanonicalHeaderKey("WWW-Authenticate")
  60  
  61  	plainTextType   = "text/plain; charset=utf-8"
  62  	jsonContentType = "application/json"
  63  	formContentType = "application/x-www-form-urlencoded"
  64  
  65  	jsonCheck = regexp.MustCompile(`(?i:(application|text)/(.*json.*)(;|$))`)
  66  	xmlCheck  = regexp.MustCompile(`(?i:(application|text)/(.*xml.*)(;|$))`)
  67  
  68  	hdrUserAgentValue = "go-resty/" + Version + " (https://github.com/go-resty/resty)"
  69  	bufPool           = &sync.Pool{New: func() interface{} { return &bytes.Buffer{} }}
  70  )
  71  
  72  type (
  73  	// RequestMiddleware type is for request middleware, called before a request is sent
  74  	RequestMiddleware func(*Client, *Request) error
  75  
  76  	// ResponseMiddleware type is for response middleware, called after a response has been received
  77  	ResponseMiddleware func(*Client, *Response) error
  78  
  79  	// PreRequestHook type is for the request hook, called right before the request is sent
  80  	PreRequestHook func(*Client, *http.Request) error
  81  
  82  	// RequestLogCallback type is for request logs, called before the request is logged
  83  	RequestLogCallback func(*RequestLog) error
  84  
  85  	// ResponseLogCallback type is for response logs, called before the response is logged
  86  	ResponseLogCallback func(*ResponseLog) error
  87  
  88  	// ErrorHook type is for reacting to request errors, called after all retries were attempted
  89  	ErrorHook func(*Request, error)
  90  
  91  	// SuccessHook type is for reacting to request success
  92  	SuccessHook func(*Client, *Response)
  93  )
  94  
  95  // Client struct is used to create a Resty client with client-level settings,
  96  // these settings apply to all the requests raised from the client.
  97  //
  98  // Resty also provides an option to override most of the client settings
  99  // at [Request] level.
 100  type Client struct {
 101  	BaseURL               string
 102  	HostURL               string // Deprecated: use BaseURL instead. To be removed in v3.0.0 release.
 103  	QueryParam            url.Values
 104  	FormData              url.Values
 105  	PathParams            map[string]string
 106  	RawPathParams         map[string]string
 107  	Header                http.Header
 108  	UserInfo              *User
 109  	Token                 string
 110  	AuthScheme            string
 111  	Cookies               []*http.Cookie
 112  	Error                 reflect.Type
 113  	Debug                 bool
 114  	DisableWarn           bool
 115  	AllowGetMethodPayload bool
 116  	RetryCount            int
 117  	RetryWaitTime         time.Duration
 118  	RetryMaxWaitTime      time.Duration
 119  	RetryConditions       []RetryConditionFunc
 120  	RetryHooks            []OnRetryFunc
 121  	RetryAfter            RetryAfterFunc
 122  	RetryResetReaders     bool
 123  	JSONMarshal           func(v interface{}) ([]byte, error)
 124  	JSONUnmarshal         func(data []byte, v interface{}) error
 125  	XMLMarshal            func(v interface{}) ([]byte, error)
 126  	XMLUnmarshal          func(data []byte, v interface{}) error
 127  
 128  	// HeaderAuthorizationKey is used to set/access Request Authorization header
 129  	// value when `SetAuthToken` option is used.
 130  	HeaderAuthorizationKey string
 131  	ResponseBodyLimit      int
 132  
 133  	jsonEscapeHTML      bool
 134  	setContentLength    bool
 135  	closeConnection     bool
 136  	notParseResponse    bool
 137  	trace               bool
 138  	debugBodySizeLimit  int64
 139  	outputDirectory     string
 140  	scheme              string
 141  	log                 Logger
 142  	httpClient          *http.Client
 143  	proxyURL            *url.URL
 144  	beforeRequest       []RequestMiddleware
 145  	udBeforeRequest     []RequestMiddleware
 146  	udBeforeRequestLock *sync.RWMutex
 147  	preReqHook          PreRequestHook
 148  	successHooks        []SuccessHook
 149  	afterResponse       []ResponseMiddleware
 150  	afterResponseLock   *sync.RWMutex
 151  	requestLog          RequestLogCallback
 152  	responseLog         ResponseLogCallback
 153  	errorHooks          []ErrorHook
 154  	invalidHooks        []ErrorHook
 155  	panicHooks          []ErrorHook
 156  	rateLimiter         RateLimiter
 157  	generateCurlOnDebug bool
 158  	unescapeQueryParams bool
 159  }
 160  
 161  // User type is to hold an username and password information
 162  type User struct {
 163  	Username, Password string
 164  }
 165  
 166  //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
 167  // Client methods
 168  //___________________________________
 169  
 170  // SetHostURL method sets the Host URL in the client instance. It will be used with a request
 171  // raised from this client with a relative URL
 172  //
 173  //	// Setting HTTP address
 174  //	client.SetHostURL("http://myjeeva.com")
 175  //
 176  //	// Setting HTTPS address
 177  //	client.SetHostURL("https://myjeeva.com")
 178  //
 179  // Deprecated: use [Client.SetBaseURL] instead. To be removed in the v3.0.0 release.
 180  func (c *Client) SetHostURL(url string) *Client {
 181  	c.SetBaseURL(url)
 182  	return c
 183  }
 184  
 185  // SetBaseURL method sets the Base URL in the client instance. It will be used with a request
 186  // raised from this client with a relative URL
 187  //
 188  //	// Setting HTTP address
 189  //	client.SetBaseURL("http://myjeeva.com")
 190  //
 191  //	// Setting HTTPS address
 192  //	client.SetBaseURL("https://myjeeva.com")
 193  func (c *Client) SetBaseURL(url string) *Client {
 194  	c.BaseURL = strings.TrimRight(url, "/")
 195  	c.HostURL = c.BaseURL
 196  	return c
 197  }
 198  
 199  // SetHeader method sets a single header field and its value in the client instance.
 200  // These headers will be applied to all requests from this client instance.
 201  // Also, it can be overridden by request-level header options.
 202  //
 203  // See [Request.SetHeader] or [Request.SetHeaders].
 204  //
 205  // For Example: To set `Content-Type` and `Accept` as `application/json`
 206  //
 207  //	client.
 208  //		SetHeader("Content-Type", "application/json").
 209  //		SetHeader("Accept", "application/json")
 210  func (c *Client) SetHeader(header, value string) *Client {
 211  	c.Header.Set(header, value)
 212  	return c
 213  }
 214  
 215  // SetHeaders method sets multiple header fields and their values at one go in the client instance.
 216  // These headers will be applied to all requests from this client instance. Also, it can be
 217  // overridden at request level headers options.
 218  //
 219  // See [Request.SetHeaders] or [Request.SetHeader].
 220  //
 221  // For Example: To set `Content-Type` and `Accept` as `application/json`
 222  //
 223  //	client.SetHeaders(map[string]string{
 224  //			"Content-Type": "application/json",
 225  //			"Accept": "application/json",
 226  //		})
 227  func (c *Client) SetHeaders(headers map[string]string) *Client {
 228  	for h, v := range headers {
 229  		c.Header.Set(h, v)
 230  	}
 231  	return c
 232  }
 233  
 234  // SetHeaderVerbatim method sets a single header field and its value verbatim in the current request.
 235  //
 236  // For Example: To set `all_lowercase` and `UPPERCASE` as `available`.
 237  //
 238  //	client.
 239  //		SetHeaderVerbatim("all_lowercase", "available").
 240  //		SetHeaderVerbatim("UPPERCASE", "available")
 241  func (c *Client) SetHeaderVerbatim(header, value string) *Client {
 242  	c.Header[header] = []string{value}
 243  	return c
 244  }
 245  
 246  // SetCookieJar method sets custom [http.CookieJar] in the resty client. It's a way to override the default.
 247  //
 248  // For Example, sometimes we don't want to save cookies in API mode so that we can remove the default
 249  // CookieJar in resty client.
 250  //
 251  //	client.SetCookieJar(nil)
 252  func (c *Client) SetCookieJar(jar http.CookieJar) *Client {
 253  	c.httpClient.Jar = jar
 254  	return c
 255  }
 256  
 257  // SetCookie method appends a single cookie to the client instance.
 258  // These cookies will be added to all the requests from this client instance.
 259  //
 260  //	client.SetCookie(&http.Cookie{
 261  //				Name:"go-resty",
 262  //				Value:"This is cookie value",
 263  //			})
 264  func (c *Client) SetCookie(hc *http.Cookie) *Client {
 265  	c.Cookies = append(c.Cookies, hc)
 266  	return c
 267  }
 268  
 269  // SetCookies method sets an array of cookies in the client instance.
 270  // These cookies will be added to all the requests from this client instance.
 271  //
 272  //	cookies := []*http.Cookie{
 273  //		&http.Cookie{
 274  //			Name:"go-resty-1",
 275  //			Value:"This is cookie 1 value",
 276  //		},
 277  //		&http.Cookie{
 278  //			Name:"go-resty-2",
 279  //			Value:"This is cookie 2 value",
 280  //		},
 281  //	}
 282  //
 283  //	// Setting a cookies into resty
 284  //	client.SetCookies(cookies)
 285  func (c *Client) SetCookies(cs []*http.Cookie) *Client {
 286  	c.Cookies = append(c.Cookies, cs...)
 287  	return c
 288  }
 289  
 290  // SetQueryParam method sets a single parameter and its value in the client instance.
 291  // It will be formed as a query string for the request.
 292  //
 293  //	For Example: `search=kitchen%20papers&size=large`
 294  //
 295  // In the URL after the `?` mark. These query params will be added to all the requests raised from
 296  // this client instance. Also, it can be overridden at the request level.
 297  //
 298  // See [Request.SetQueryParam] or [Request.SetQueryParams].
 299  //
 300  //	client.
 301  //		SetQueryParam("search", "kitchen papers").
 302  //		SetQueryParam("size", "large")
 303  func (c *Client) SetQueryParam(param, value string) *Client {
 304  	c.QueryParam.Set(param, value)
 305  	return c
 306  }
 307  
 308  // SetQueryParams method sets multiple parameters and their values at one go in the client instance.
 309  // It will be formed as a query string for the request.
 310  //
 311  //	For Example: `search=kitchen%20papers&size=large`
 312  //
 313  // In the URL after the `?` mark. These query params will be added to all the requests raised from this
 314  // client instance. Also, it can be overridden at the request level.
 315  //
 316  // See [Request.SetQueryParams] or [Request.SetQueryParam].
 317  //
 318  //	client.SetQueryParams(map[string]string{
 319  //			"search": "kitchen papers",
 320  //			"size": "large",
 321  //		})
 322  func (c *Client) SetQueryParams(params map[string]string) *Client {
 323  	for p, v := range params {
 324  		c.SetQueryParam(p, v)
 325  	}
 326  	return c
 327  }
 328  
 329  // SetUnescapeQueryParams method sets the unescape query parameters choice for request URL.
 330  // To prevent broken URL, resty replaces space (" ") with "+" in the query parameters.
 331  //
 332  // See [Request.SetUnescapeQueryParams]
 333  //
 334  // NOTE: Request failure is possible due to non-standard usage of Unescaped Query Parameters.
 335  func (c *Client) SetUnescapeQueryParams(unescape bool) *Client {
 336  	c.unescapeQueryParams = unescape
 337  	return c
 338  }
 339  
 340  // SetFormData method sets Form parameters and their values in the client instance.
 341  // It applies only to HTTP methods `POST` and `PUT`, and the request content type would be set as
 342  // `application/x-www-form-urlencoded`. These form data will be added to all the requests raised from
 343  // this client instance. Also, it can be overridden at the request level.
 344  //
 345  // See [Request.SetFormData].
 346  //
 347  //	client.SetFormData(map[string]string{
 348  //			"access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
 349  //			"user_id": "3455454545",
 350  //		})
 351  func (c *Client) SetFormData(data map[string]string) *Client {
 352  	for k, v := range data {
 353  		c.FormData.Set(k, v)
 354  	}
 355  	return c
 356  }
 357  
 358  // SetBasicAuth method sets the basic authentication header in the HTTP request. For Example:
 359  //
 360  //	Authorization: Basic <base64-encoded-value>
 361  //
 362  // For Example: To set the header for username "go-resty" and password "welcome"
 363  //
 364  //	client.SetBasicAuth("go-resty", "welcome")
 365  //
 366  // This basic auth information is added to all requests from this client instance.
 367  // It can also be overridden at the request level.
 368  //
 369  // See [Request.SetBasicAuth].
 370  func (c *Client) SetBasicAuth(username, password string) *Client {
 371  	c.UserInfo = &User{Username: username, Password: password}
 372  	return c
 373  }
 374  
 375  // SetAuthToken method sets the auth token of the `Authorization` header for all HTTP requests.
 376  // The default auth scheme is `Bearer`; it can be customized with the method [Client.SetAuthScheme]. For Example:
 377  //
 378  //	Authorization: <auth-scheme> <auth-token-value>
 379  //
 380  // For Example: To set auth token BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F
 381  //
 382  //	client.SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")
 383  //
 384  // This auth token gets added to all the requests raised from this client instance.
 385  // Also, it can be overridden at the request level.
 386  //
 387  // See [Request.SetAuthToken].
 388  func (c *Client) SetAuthToken(token string) *Client {
 389  	c.Token = token
 390  	return c
 391  }
 392  
 393  // SetAuthScheme method sets the auth scheme type in the HTTP request. For Example:
 394  //
 395  //	Authorization: <auth-scheme-value> <auth-token-value>
 396  //
 397  // For Example: To set the scheme to use OAuth
 398  //
 399  //	client.SetAuthScheme("OAuth")
 400  //
 401  // This auth scheme gets added to all the requests raised from this client instance.
 402  // Also, it can be overridden at the request level.
 403  //
 404  // Information about auth schemes can be found in [RFC 7235], IANA [HTTP Auth schemes].
 405  //
 406  // See [Request.SetAuthToken].
 407  //
 408  // [RFC 7235]: https://tools.ietf.org/html/rfc7235
 409  // [HTTP Auth schemes]: https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml#authschemes
 410  func (c *Client) SetAuthScheme(scheme string) *Client {
 411  	c.AuthScheme = scheme
 412  	return c
 413  }
 414  
 415  // SetDigestAuth method sets the Digest Access auth scheme for the client. If a server responds with 401 and sends
 416  // a Digest challenge in the WWW-Authenticate Header, requests will be resent with the appropriate Authorization Header.
 417  //
 418  // For Example: To set the Digest scheme with user "Mufasa" and password "Circle Of Life"
 419  //
 420  //	client.SetDigestAuth("Mufasa", "Circle Of Life")
 421  //
 422  // Information about Digest Access Authentication can be found in [RFC 7616].
 423  //
 424  // See [Request.SetDigestAuth].
 425  //
 426  // [RFC 7616]: https://datatracker.ietf.org/doc/html/rfc7616
 427  func (c *Client) SetDigestAuth(username, password string) *Client {
 428  	oldTransport := c.httpClient.Transport
 429  	c.OnBeforeRequest(func(c *Client, _ *Request) error {
 430  		c.httpClient.Transport = &digestTransport{
 431  			digestCredentials: digestCredentials{username, password},
 432  			transport:         oldTransport,
 433  		}
 434  		return nil
 435  	})
 436  	c.OnAfterResponse(func(c *Client, _ *Response) error {
 437  		c.httpClient.Transport = oldTransport
 438  		return nil
 439  	})
 440  	return c
 441  }
 442  
 443  // R method creates a new request instance; it's used for Get, Post, Put, Delete, Patch, Head, Options, etc.
 444  func (c *Client) R() *Request {
 445  	r := &Request{
 446  		QueryParam:    url.Values{},
 447  		FormData:      url.Values{},
 448  		Header:        http.Header{},
 449  		Cookies:       make([]*http.Cookie, 0),
 450  		PathParams:    map[string]string{},
 451  		RawPathParams: map[string]string{},
 452  		Debug:         c.Debug,
 453  		AuthScheme:    c.AuthScheme,
 454  
 455  		client:              c,
 456  		multipartFiles:      []*File{},
 457  		multipartFields:     []*MultipartField{},
 458  		jsonEscapeHTML:      c.jsonEscapeHTML,
 459  		log:                 c.log,
 460  		responseBodyLimit:   c.ResponseBodyLimit,
 461  		generateCurlOnDebug: c.generateCurlOnDebug,
 462  		unescapeQueryParams: c.unescapeQueryParams,
 463  	}
 464  	return r
 465  }
 466  
 467  // NewRequest method is an alias for method `R()`.
 468  func (c *Client) NewRequest() *Request {
 469  	return c.R()
 470  }
 471  
 472  // OnBeforeRequest method appends a request middleware to the before request chain.
 473  // The user-defined middlewares are applied before the default Resty request middlewares.
 474  // After all middlewares have been applied, the request is sent from Resty to the host server.
 475  //
 476  //	client.OnBeforeRequest(func(c *resty.Client, r *resty.Request) error {
 477  //			// Now you have access to the Client and Request instance
 478  //			// manipulate it as per your need
 479  //
 480  //			return nil 	// if its successful otherwise return error
 481  //		})
 482  func (c *Client) OnBeforeRequest(m RequestMiddleware) *Client {
 483  	c.udBeforeRequestLock.Lock()
 484  	defer c.udBeforeRequestLock.Unlock()
 485  
 486  	c.udBeforeRequest = append(c.udBeforeRequest, m)
 487  
 488  	return c
 489  }
 490  
 491  // OnAfterResponse method appends response middleware to the after-response chain.
 492  // Once we receive a response from the host server, the default Resty response middleware
 493  // gets applied, and then the user-assigned response middleware is applied.
 494  //
 495  //	client.OnAfterResponse(func(c *resty.Client, r *resty.Response) error {
 496  //			// Now you have access to the Client and Response instance
 497  //			// manipulate it as per your need
 498  //
 499  //			return nil 	// if its successful otherwise return error
 500  //		})
 501  func (c *Client) OnAfterResponse(m ResponseMiddleware) *Client {
 502  	c.afterResponseLock.Lock()
 503  	defer c.afterResponseLock.Unlock()
 504  
 505  	c.afterResponse = append(c.afterResponse, m)
 506  
 507  	return c
 508  }
 509  
 510  // OnError method adds a callback that will be run whenever a request execution fails.
 511  // This is called after all retries have been attempted (if any).
 512  // If there was a response from the server, the error will be wrapped in [ResponseError]
 513  // which has the last response received from the server.
 514  //
 515  //	client.OnError(func(req *resty.Request, err error) {
 516  //		if v, ok := err.(*resty.ResponseError); ok {
 517  //			// Do something with v.Response
 518  //		}
 519  //		// Log the error, increment a metric, etc...
 520  //	})
 521  //
 522  // Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
 523  // callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
 524  func (c *Client) OnError(h ErrorHook) *Client {
 525  	c.errorHooks = append(c.errorHooks, h)
 526  	return c
 527  }
 528  
 529  // OnSuccess method adds a callback that will be run whenever a request execution
 530  // succeeds.  This is called after all retries have been attempted (if any).
 531  //
 532  // Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
 533  // callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
 534  func (c *Client) OnSuccess(h SuccessHook) *Client {
 535  	c.successHooks = append(c.successHooks, h)
 536  	return c
 537  }
 538  
 539  // OnInvalid method adds a callback that will be run whenever a request execution
 540  // fails before it starts because the request is invalid.
 541  //
 542  // Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
 543  // callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
 544  func (c *Client) OnInvalid(h ErrorHook) *Client {
 545  	c.invalidHooks = append(c.invalidHooks, h)
 546  	return c
 547  }
 548  
 549  // OnPanic method adds a callback that will be run whenever a request execution
 550  // panics.
 551  //
 552  // Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
 553  // callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
 554  //
 555  // If an [Client.OnSuccess], [Client.OnError], or [Client.OnInvalid] callback panics,
 556  // then exactly one rule can be violated.
 557  func (c *Client) OnPanic(h ErrorHook) *Client {
 558  	c.panicHooks = append(c.panicHooks, h)
 559  	return c
 560  }
 561  
 562  // SetPreRequestHook method sets the given pre-request function into a resty client.
 563  // It is called right before the request is fired.
 564  //
 565  // NOTE: Only one pre-request hook can be registered. Use [Client.OnBeforeRequest] for multiple.
 566  func (c *Client) SetPreRequestHook(h PreRequestHook) *Client {
 567  	if c.preReqHook != nil {
 568  		c.log.Warnf("Overwriting an existing pre-request hook: %s", functionName(h))
 569  	}
 570  	c.preReqHook = h
 571  	return c
 572  }
 573  
 574  // SetDebug method enables the debug mode on the Resty client. The client logs details
 575  // of every request and response.
 576  //
 577  //	client.SetDebug(true)
 578  //
 579  // Also, it can be enabled at the request level for a particular request; see [Request.SetDebug].
 580  //   - For [Request], it logs information such as HTTP verb, Relative URL path,
 581  //     Host, Headers, and Body if it has one.
 582  //   - For [Response], it logs information such as Status, Response Time, Headers,
 583  //     and Body if it has one.
 584  func (c *Client) SetDebug(d bool) *Client {
 585  	c.Debug = d
 586  	return c
 587  }
 588  
 589  // SetDebugBodyLimit sets the maximum size in bytes for which the response and
 590  // request body will be logged in debug mode.
 591  //
 592  //	client.SetDebugBodyLimit(1000000)
 593  func (c *Client) SetDebugBodyLimit(sl int64) *Client {
 594  	c.debugBodySizeLimit = sl
 595  	return c
 596  }
 597  
 598  // OnRequestLog method sets the request log callback to Resty. Registered callback gets
 599  // called before the resty logs the information.
 600  func (c *Client) OnRequestLog(rl RequestLogCallback) *Client {
 601  	if c.requestLog != nil {
 602  		c.log.Warnf("Overwriting an existing on-request-log callback from=%s to=%s",
 603  			functionName(c.requestLog), functionName(rl))
 604  	}
 605  	c.requestLog = rl
 606  	return c
 607  }
 608  
 609  // OnResponseLog method sets the response log callback to Resty. Registered callback gets
 610  // called before the resty logs the information.
 611  func (c *Client) OnResponseLog(rl ResponseLogCallback) *Client {
 612  	if c.responseLog != nil {
 613  		c.log.Warnf("Overwriting an existing on-response-log callback from=%s to=%s",
 614  			functionName(c.responseLog), functionName(rl))
 615  	}
 616  	c.responseLog = rl
 617  	return c
 618  }
 619  
 620  // SetDisableWarn method disables the warning log message on the Resty client.
 621  //
 622  // For example, Resty warns users when BasicAuth is used in non-TLS mode.
 623  //
 624  //	client.SetDisableWarn(true)
 625  func (c *Client) SetDisableWarn(d bool) *Client {
 626  	c.DisableWarn = d
 627  	return c
 628  }
 629  
 630  // SetAllowGetMethodPayload method allows the GET method with payload on the Resty client.
 631  //
 632  // For example, Resty allows the user to send a request with a payload using the HTTP GET method.
 633  //
 634  //	client.SetAllowGetMethodPayload(true)
 635  func (c *Client) SetAllowGetMethodPayload(a bool) *Client {
 636  	c.AllowGetMethodPayload = a
 637  	return c
 638  }
 639  
 640  // SetLogger method sets given writer for logging Resty request and response details.
 641  //
 642  // Compliant to interface [resty.Logger]
 643  func (c *Client) SetLogger(l Logger) *Client {
 644  	c.log = l
 645  	return c
 646  }
 647  
 648  // SetContentLength method enables the HTTP header `Content-Length` value for every request.
 649  // By default, Resty won't set `Content-Length`.
 650  //
 651  //	client.SetContentLength(true)
 652  //
 653  // Also, you have the option to enable a particular request. See [Request.SetContentLength]
 654  func (c *Client) SetContentLength(l bool) *Client {
 655  	c.setContentLength = l
 656  	return c
 657  }
 658  
 659  // SetTimeout method sets the timeout for a request raised by the client.
 660  //
 661  //	client.SetTimeout(time.Duration(1 * time.Minute))
 662  func (c *Client) SetTimeout(timeout time.Duration) *Client {
 663  	c.httpClient.Timeout = timeout
 664  	return c
 665  }
 666  
 667  // SetError method registers the global or client common `Error` object into Resty.
 668  // It is used for automatic unmarshalling if the response status code is greater than 399 and
 669  // content type is JSON or XML. It can be a pointer or a non-pointer.
 670  //
 671  //	client.SetError(&Error{})
 672  //	// OR
 673  //	client.SetError(Error{})
 674  func (c *Client) SetError(err interface{}) *Client {
 675  	c.Error = typeOf(err)
 676  	return c
 677  }
 678  
 679  // SetRedirectPolicy method sets the redirect policy for the client. Resty provides ready-to-use
 680  // redirect policies. Wanna create one for yourself, refer to `redirect.go`.
 681  //
 682  //	client.SetRedirectPolicy(FlexibleRedirectPolicy(20))
 683  //
 684  //	// Need multiple redirect policies together
 685  //	client.SetRedirectPolicy(FlexibleRedirectPolicy(20), DomainCheckRedirectPolicy("host1.com", "host2.net"))
 686  func (c *Client) SetRedirectPolicy(policies ...interface{}) *Client {
 687  	for _, p := range policies {
 688  		if _, ok := p.(RedirectPolicy); !ok {
 689  			c.log.Errorf("%v does not implement resty.RedirectPolicy (missing Apply method)",
 690  				functionName(p))
 691  		}
 692  	}
 693  
 694  	c.httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
 695  		for _, p := range policies {
 696  			if err := p.(RedirectPolicy).Apply(req, via); err != nil {
 697  				return err
 698  			}
 699  		}
 700  		return nil // looks good, go ahead
 701  	}
 702  
 703  	return c
 704  }
 705  
 706  // SetRetryCount method enables retry on Resty client and allows you
 707  // to set no. of retry count. Resty uses a Backoff mechanism.
 708  func (c *Client) SetRetryCount(count int) *Client {
 709  	c.RetryCount = count
 710  	return c
 711  }
 712  
 713  // SetRetryWaitTime method sets the default wait time for sleep before retrying
 714  // request.
 715  //
 716  // Default is 100 milliseconds.
 717  func (c *Client) SetRetryWaitTime(waitTime time.Duration) *Client {
 718  	c.RetryWaitTime = waitTime
 719  	return c
 720  }
 721  
 722  // SetRetryMaxWaitTime method sets the max wait time for sleep before retrying
 723  // request.
 724  //
 725  // Default is 2 seconds.
 726  func (c *Client) SetRetryMaxWaitTime(maxWaitTime time.Duration) *Client {
 727  	c.RetryMaxWaitTime = maxWaitTime
 728  	return c
 729  }
 730  
 731  // SetRetryAfter sets a callback to calculate the wait time between retries.
 732  // Default (nil) implies exponential backoff with jitter
 733  func (c *Client) SetRetryAfter(callback RetryAfterFunc) *Client {
 734  	c.RetryAfter = callback
 735  	return c
 736  }
 737  
 738  // SetJSONMarshaler method sets the JSON marshaler function to marshal the request body.
 739  // By default, Resty uses [encoding/json] package to marshal the request body.
 740  func (c *Client) SetJSONMarshaler(marshaler func(v interface{}) ([]byte, error)) *Client {
 741  	c.JSONMarshal = marshaler
 742  	return c
 743  }
 744  
 745  // SetJSONUnmarshaler method sets the JSON unmarshaler function to unmarshal the response body.
 746  // By default, Resty uses [encoding/json] package to unmarshal the response body.
 747  func (c *Client) SetJSONUnmarshaler(unmarshaler func(data []byte, v interface{}) error) *Client {
 748  	c.JSONUnmarshal = unmarshaler
 749  	return c
 750  }
 751  
 752  // SetXMLMarshaler method sets the XML marshaler function to marshal the request body.
 753  // By default, Resty uses [encoding/xml] package to marshal the request body.
 754  func (c *Client) SetXMLMarshaler(marshaler func(v interface{}) ([]byte, error)) *Client {
 755  	c.XMLMarshal = marshaler
 756  	return c
 757  }
 758  
 759  // SetXMLUnmarshaler method sets the XML unmarshaler function to unmarshal the response body.
 760  // By default, Resty uses [encoding/xml] package to unmarshal the response body.
 761  func (c *Client) SetXMLUnmarshaler(unmarshaler func(data []byte, v interface{}) error) *Client {
 762  	c.XMLUnmarshal = unmarshaler
 763  	return c
 764  }
 765  
 766  // AddRetryCondition method adds a retry condition function to an array of functions
 767  // that are checked to determine if the request is retried. The request will
 768  // retry if any functions return true and the error is nil.
 769  //
 770  // NOTE: These retry conditions are applied on all requests made using this Client.
 771  // For [Request] specific retry conditions, check [Request.AddRetryCondition]
 772  func (c *Client) AddRetryCondition(condition RetryConditionFunc) *Client {
 773  	c.RetryConditions = append(c.RetryConditions, condition)
 774  	return c
 775  }
 776  
 777  // AddRetryAfterErrorCondition adds the basic condition of retrying after encountering
 778  // an error from the HTTP response
 779  func (c *Client) AddRetryAfterErrorCondition() *Client {
 780  	c.AddRetryCondition(func(response *Response, err error) bool {
 781  		return response.IsError()
 782  	})
 783  	return c
 784  }
 785  
 786  // AddRetryHook adds a side-effecting retry hook to an array of hooks
 787  // that will be executed on each retry.
 788  func (c *Client) AddRetryHook(hook OnRetryFunc) *Client {
 789  	c.RetryHooks = append(c.RetryHooks, hook)
 790  	return c
 791  }
 792  
 793  // SetRetryResetReaders method enables the Resty client to seek the start of all
 794  // file readers are given as multipart files if the object implements [io.ReadSeeker].
 795  func (c *Client) SetRetryResetReaders(b bool) *Client {
 796  	c.RetryResetReaders = b
 797  	return c
 798  }
 799  
 800  // SetTLSClientConfig method sets TLSClientConfig for underlying client Transport.
 801  //
 802  // For Example:
 803  //
 804  //	// One can set a custom root certificate. Refer: http://golang.org/pkg/crypto/tls/#example_Dial
 805  //	client.SetTLSClientConfig(&tls.Config{ RootCAs: roots })
 806  //
 807  //	// or One can disable security check (https)
 808  //	client.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })
 809  //
 810  // NOTE: This method overwrites existing [http.Transport.TLSClientConfig]
 811  func (c *Client) SetTLSClientConfig(config *tls.Config) *Client {
 812  	transport, err := c.Transport()
 813  	if err != nil {
 814  		c.log.Errorf("%v", err)
 815  		return c
 816  	}
 817  	transport.TLSClientConfig = config
 818  	return c
 819  }
 820  
 821  // SetProxy method sets the Proxy URL and Port for the Resty client.
 822  //
 823  //	client.SetProxy("http://proxyserver:8888")
 824  //
 825  // OR you could also set Proxy via environment variable, refer to [http.ProxyFromEnvironment]
 826  func (c *Client) SetProxy(proxyURL string) *Client {
 827  	transport, err := c.Transport()
 828  	if err != nil {
 829  		c.log.Errorf("%v", err)
 830  		return c
 831  	}
 832  
 833  	pURL, err := url.Parse(proxyURL)
 834  	if err != nil {
 835  		c.log.Errorf("%v", err)
 836  		return c
 837  	}
 838  
 839  	c.proxyURL = pURL
 840  	transport.Proxy = http.ProxyURL(c.proxyURL)
 841  	return c
 842  }
 843  
 844  // RemoveProxy method removes the proxy configuration from the Resty client
 845  //
 846  //	client.RemoveProxy()
 847  func (c *Client) RemoveProxy() *Client {
 848  	transport, err := c.Transport()
 849  	if err != nil {
 850  		c.log.Errorf("%v", err)
 851  		return c
 852  	}
 853  	c.proxyURL = nil
 854  	transport.Proxy = nil
 855  	return c
 856  }
 857  
 858  // SetCertificates method helps to conveniently set client certificates into Resty.
 859  func (c *Client) SetCertificates(certs ...tls.Certificate) *Client {
 860  	config, err := c.tlsConfig()
 861  	if err != nil {
 862  		c.log.Errorf("%v", err)
 863  		return c
 864  	}
 865  	config.Certificates = append(config.Certificates, certs...)
 866  	return c
 867  }
 868  
 869  // SetRootCertificate method helps to add one or more root certificates into the Resty client
 870  //
 871  //	client.SetRootCertificate("/path/to/root/pemFile.pem")
 872  func (c *Client) SetRootCertificate(pemFilePath string) *Client {
 873  	rootPemData, err := os.ReadFile(pemFilePath)
 874  	if err != nil {
 875  		c.log.Errorf("%v", err)
 876  		return c
 877  	}
 878  	c.handleCAs("root", rootPemData)
 879  	return c
 880  }
 881  
 882  // SetRootCertificateFromString method helps to add one or more root certificates
 883  // into the Resty client
 884  //
 885  //	client.SetRootCertificateFromString("pem certs content")
 886  func (c *Client) SetRootCertificateFromString(pemCerts string) *Client {
 887  	c.handleCAs("root", []byte(pemCerts))
 888  	return c
 889  }
 890  
 891  // SetClientRootCertificate method helps to add one or more client's root
 892  // certificates into the Resty client
 893  //
 894  //	client.SetClientRootCertificate("/path/to/root/pemFile.pem")
 895  func (c *Client) SetClientRootCertificate(pemFilePath string) *Client {
 896  	rootPemData, err := os.ReadFile(pemFilePath)
 897  	if err != nil {
 898  		c.log.Errorf("%v", err)
 899  		return c
 900  	}
 901  	c.handleCAs("client", rootPemData)
 902  	return c
 903  }
 904  
 905  // SetClientRootCertificateFromString method helps to add one or more clients
 906  // root certificates into the Resty client
 907  //
 908  //	client.SetClientRootCertificateFromString("pem certs content")
 909  func (c *Client) SetClientRootCertificateFromString(pemCerts string) *Client {
 910  	c.handleCAs("client", []byte(pemCerts))
 911  	return c
 912  }
 913  
 914  func (c *Client) handleCAs(scope string, permCerts []byte) {
 915  	config, err := c.tlsConfig()
 916  	if err != nil {
 917  		c.log.Errorf("%v", err)
 918  		return
 919  	}
 920  
 921  	switch scope {
 922  	case "root":
 923  		if config.RootCAs == nil {
 924  			config.RootCAs = x509.NewCertPool()
 925  		}
 926  		config.RootCAs.AppendCertsFromPEM(permCerts)
 927  	case "client":
 928  		if config.ClientCAs == nil {
 929  			config.ClientCAs = x509.NewCertPool()
 930  		}
 931  		config.ClientCAs.AppendCertsFromPEM(permCerts)
 932  	}
 933  }
 934  
 935  // SetOutputDirectory method sets the output directory for saving HTTP responses in a file.
 936  // Resty creates one if the output directory does not exist. This setting is optional,
 937  // if you plan to use the absolute path in [Request.SetOutput] and can used together.
 938  //
 939  //	client.SetOutputDirectory("/save/http/response/here")
 940  func (c *Client) SetOutputDirectory(dirPath string) *Client {
 941  	c.outputDirectory = dirPath
 942  	return c
 943  }
 944  
 945  // SetRateLimiter sets an optional [RateLimiter]. If set, the rate limiter will control
 946  // all requests were made by this client.
 947  func (c *Client) SetRateLimiter(rl RateLimiter) *Client {
 948  	c.rateLimiter = rl
 949  	return c
 950  }
 951  
 952  // SetTransport method sets custom [http.Transport] or any [http.RoundTripper]
 953  // compatible interface implementation in the Resty client.
 954  //
 955  //	transport := &http.Transport{
 956  //		// something like Proxying to httptest.Server, etc...
 957  //		Proxy: func(req *http.Request) (*url.URL, error) {
 958  //			return url.Parse(server.URL)
 959  //		},
 960  //	}
 961  //	client.SetTransport(transport)
 962  //
 963  // NOTE:
 964  //   - If transport is not the type of `*http.Transport`, then you may not be able to
 965  //     take advantage of some of the Resty client settings.
 966  //   - It overwrites the Resty client transport instance and its configurations.
 967  func (c *Client) SetTransport(transport http.RoundTripper) *Client {
 968  	if transport != nil {
 969  		c.httpClient.Transport = transport
 970  	}
 971  	return c
 972  }
 973  
 974  // SetScheme method sets a custom scheme for the Resty client. It's a way to override the default.
 975  //
 976  //	client.SetScheme("http")
 977  func (c *Client) SetScheme(scheme string) *Client {
 978  	if !IsStringEmpty(scheme) {
 979  		c.scheme = strings.TrimSpace(scheme)
 980  	}
 981  	return c
 982  }
 983  
 984  // SetCloseConnection method sets variable `Close` in HTTP request struct with the given
 985  // value. More info: https://golang.org/src/net/http/request.go
 986  func (c *Client) SetCloseConnection(close bool) *Client {
 987  	c.closeConnection = close
 988  	return c
 989  }
 990  
 991  // SetDoNotParseResponse method instructs Resty not to parse the response body automatically.
 992  // Resty exposes the raw response body as [io.ReadCloser]. If you use it, do not
 993  // forget to close the body, otherwise, you might get into connection leaks, and connection
 994  // reuse may not happen.
 995  //
 996  // NOTE: [Response] middlewares are not executed using this option. You have
 997  // taken over the control of response parsing from Resty.
 998  func (c *Client) SetDoNotParseResponse(notParse bool) *Client {
 999  	c.notParseResponse = notParse
1000  	return c
1001  }
1002  
1003  // SetPathParam method sets a single URL path key-value pair in the
1004  // Resty client instance.
1005  //
1006  //	client.SetPathParam("userId", "sample@sample.com")
1007  //
1008  //	Result:
1009  //	   URL - /v1/users/{userId}/details
1010  //	   Composed URL - /v1/users/sample@sample.com/details
1011  //
1012  // It replaces the value of the key while composing the request URL.
1013  // The value will be escaped using [url.PathEscape] function.
1014  //
1015  // It can be overridden at the request level,
1016  // see [Request.SetPathParam] or [Request.SetPathParams]
1017  func (c *Client) SetPathParam(param, value string) *Client {
1018  	c.PathParams[param] = value
1019  	return c
1020  }
1021  
1022  // SetPathParams method sets multiple URL path key-value pairs at one go in the
1023  // Resty client instance.
1024  //
1025  //	client.SetPathParams(map[string]string{
1026  //		"userId":       "sample@sample.com",
1027  //		"subAccountId": "100002",
1028  //		"path":         "groups/developers",
1029  //	})
1030  //
1031  //	Result:
1032  //	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
1033  //	   Composed URL - /v1/users/sample@sample.com/100002/groups%2Fdevelopers/details
1034  //
1035  // It replaces the value of the key while composing the request URL.
1036  // The values will be escaped using [url.PathEscape] function.
1037  //
1038  // It can be overridden at the request level,
1039  // see [Request.SetPathParam] or [Request.SetPathParams]
1040  func (c *Client) SetPathParams(params map[string]string) *Client {
1041  	for p, v := range params {
1042  		c.SetPathParam(p, v)
1043  	}
1044  	return c
1045  }
1046  
1047  // SetRawPathParam method sets a single URL path key-value pair in the
1048  // Resty client instance.
1049  //
1050  //	client.SetPathParam("userId", "sample@sample.com")
1051  //
1052  //	Result:
1053  //	   URL - /v1/users/{userId}/details
1054  //	   Composed URL - /v1/users/sample@sample.com/details
1055  //
1056  //	client.SetPathParam("path", "groups/developers")
1057  //
1058  //	Result:
1059  //	   URL - /v1/users/{userId}/details
1060  //	   Composed URL - /v1/users/groups%2Fdevelopers/details
1061  //
1062  // It replaces the value of the key while composing the request URL.
1063  // The value will be used as it is and will not be escaped.
1064  //
1065  // It can be overridden at the request level,
1066  // see [Request.SetRawPathParam] or [Request.SetRawPathParams]
1067  func (c *Client) SetRawPathParam(param, value string) *Client {
1068  	c.RawPathParams[param] = value
1069  	return c
1070  }
1071  
1072  // SetRawPathParams method sets multiple URL path key-value pairs at one go in the
1073  // Resty client instance.
1074  //
1075  //	client.SetPathParams(map[string]string{
1076  //		"userId":       "sample@sample.com",
1077  //		"subAccountId": "100002",
1078  //		"path":         "groups/developers",
1079  //	})
1080  //
1081  //	Result:
1082  //	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
1083  //	   Composed URL - /v1/users/sample@sample.com/100002/groups/developers/details
1084  //
1085  // It replaces the value of the key while composing the request URL.
1086  // The values will be used as they are and will not be escaped.
1087  //
1088  // It can be overridden at the request level,
1089  // see [Request.SetRawPathParam] or [Request.SetRawPathParams]
1090  func (c *Client) SetRawPathParams(params map[string]string) *Client {
1091  	for p, v := range params {
1092  		c.SetRawPathParam(p, v)
1093  	}
1094  	return c
1095  }
1096  
1097  // SetJSONEscapeHTML method enables or disables the HTML escape on JSON marshal.
1098  // By default, escape HTML is false.
1099  //
1100  // NOTE: This option only applies to the standard JSON Marshaller used by Resty.
1101  //
1102  // It can be overridden at the request level, see [Client.SetJSONEscapeHTML]
1103  func (c *Client) SetJSONEscapeHTML(b bool) *Client {
1104  	c.jsonEscapeHTML = b
1105  	return c
1106  }
1107  
1108  // SetResponseBodyLimit method sets a maximum body size limit in bytes on response,
1109  // avoid reading too much data to memory.
1110  //
1111  // Client will return [resty.ErrResponseBodyTooLarge] if the body size of the body
1112  // in the uncompressed response is larger than the limit.
1113  // Body size limit will not be enforced in the following cases:
1114  //   - ResponseBodyLimit <= 0, which is the default behavior.
1115  //   - [Request.SetOutput] is called to save response data to the file.
1116  //   - "DoNotParseResponse" is set for client or request.
1117  //
1118  // It can be overridden at the request level; see [Request.SetResponseBodyLimit]
1119  func (c *Client) SetResponseBodyLimit(v int) *Client {
1120  	c.ResponseBodyLimit = v
1121  	return c
1122  }
1123  
1124  // EnableTrace method enables the Resty client trace for the requests fired from
1125  // the client using [httptrace.ClientTrace] and provides insights.
1126  //
1127  //	client := resty.New().EnableTrace()
1128  //
1129  //	resp, err := client.R().Get("https://httpbin.org/get")
1130  //	fmt.Println("Error:", err)
1131  //	fmt.Println("Trace Info:", resp.Request.TraceInfo())
1132  //
1133  // The method [Request.EnableTrace] is also available to get trace info for a single request.
1134  func (c *Client) EnableTrace() *Client {
1135  	c.trace = true
1136  	return c
1137  }
1138  
1139  // DisableTrace method disables the Resty client trace. Refer to [Client.EnableTrace].
1140  func (c *Client) DisableTrace() *Client {
1141  	c.trace = false
1142  	return c
1143  }
1144  
1145  // EnableGenerateCurlOnDebug method enables the generation of CURL commands in the debug log.
1146  // It works in conjunction with debug mode.
1147  //
1148  // NOTE: Use with care.
1149  //   - Potential to leak sensitive data from [Request] and [Response] in the debug log.
1150  //   - Beware of memory usage since the request body is reread.
1151  func (c *Client) EnableGenerateCurlOnDebug() *Client {
1152  	c.generateCurlOnDebug = true
1153  	return c
1154  }
1155  
1156  // DisableGenerateCurlOnDebug method disables the option set by [Client.EnableGenerateCurlOnDebug].
1157  func (c *Client) DisableGenerateCurlOnDebug() *Client {
1158  	c.generateCurlOnDebug = false
1159  	return c
1160  }
1161  
1162  // IsProxySet method returns the true is proxy is set from the Resty client; otherwise
1163  // false. By default, the proxy is set from the environment variable; refer to [http.ProxyFromEnvironment].
1164  func (c *Client) IsProxySet() bool {
1165  	return c.proxyURL != nil
1166  }
1167  
1168  // GetClient method returns the underlying [http.Client] used by the Resty.
1169  func (c *Client) GetClient() *http.Client {
1170  	return c.httpClient
1171  }
1172  
1173  // Clone returns a clone of the original client.
1174  //
1175  // NOTE: Use with care:
1176  //   - Interface values are not deeply cloned. Thus, both the original and the
1177  //     clone will use the same value.
1178  //   - This function is not safe for concurrent use. You should only use this method
1179  //     when you are sure that any other goroutine is not using the client.
1180  func (c *Client) Clone() *Client {
1181  	// dereference the pointer and copy the value
1182  	cc := *c
1183  
1184  	// lock values should not be copied - thus new values are used.
1185  	cc.afterResponseLock = &sync.RWMutex{}
1186  	cc.udBeforeRequestLock = &sync.RWMutex{}
1187  	return &cc
1188  }
1189  
1190  func (c *Client) executeBefore(req *Request) error {
1191  	// Lock the user-defined pre-request hooks.
1192  	c.udBeforeRequestLock.RLock()
1193  	defer c.udBeforeRequestLock.RUnlock()
1194  
1195  	// Lock the post-request hooks.
1196  	c.afterResponseLock.RLock()
1197  	defer c.afterResponseLock.RUnlock()
1198  
1199  	// Apply Request middleware
1200  	var err error
1201  
1202  	// user defined on before request methods
1203  	// to modify the *resty.Request object
1204  	for _, f := range c.udBeforeRequest {
1205  		if err = f(c, req); err != nil {
1206  			return wrapNoRetryErr(err)
1207  		}
1208  	}
1209  
1210  	// If there is a rate limiter set for this client, the Execute call
1211  	// will return an error if the rate limit is exceeded.
1212  	if req.client.rateLimiter != nil {
1213  		if !req.client.rateLimiter.Allow() {
1214  			return wrapNoRetryErr(ErrRateLimitExceeded)
1215  		}
1216  	}
1217  
1218  	// resty middlewares
1219  	for _, f := range c.beforeRequest {
1220  		if err = f(c, req); err != nil {
1221  			return wrapNoRetryErr(err)
1222  		}
1223  	}
1224  
1225  	if hostHeader := req.Header.Get("Host"); hostHeader != "" {
1226  		req.RawRequest.Host = hostHeader
1227  	}
1228  
1229  	// call pre-request if defined
1230  	if c.preReqHook != nil {
1231  		if err = c.preReqHook(c, req.RawRequest); err != nil {
1232  			return wrapNoRetryErr(err)
1233  		}
1234  	}
1235  
1236  	if err = requestLogger(c, req); err != nil {
1237  		return wrapNoRetryErr(err)
1238  	}
1239  
1240  	return nil
1241  }
1242  
1243  // Executes method executes the given `Request` object and returns
1244  // response or error.
1245  func (c *Client) execute(req *Request) (*Response, error) {
1246  	if err := c.executeBefore(req); err != nil {
1247  		return nil, err
1248  	}
1249  
1250  	req.Time = time.Now()
1251  	resp, err := c.httpClient.Do(req.RawRequest)
1252  
1253  	response := &Response{
1254  		Request:     req,
1255  		RawResponse: resp,
1256  	}
1257  
1258  	if err != nil || req.notParseResponse || c.notParseResponse {
1259  		response.setReceivedAt()
1260  		if logErr := responseLogger(c, response); logErr != nil {
1261  			return response, wrapErrors(logErr, err)
1262  		}
1263  		if err != nil {
1264  			return response, err
1265  		}
1266  		return response, nil
1267  	}
1268  
1269  	if !req.isSaveResponse {
1270  		defer closeq(resp.Body)
1271  		body := resp.Body
1272  
1273  		// GitHub #142 & #187
1274  		if strings.EqualFold(resp.Header.Get(hdrContentEncodingKey), "gzip") && resp.ContentLength != 0 {
1275  			if _, ok := body.(*gzip.Reader); !ok {
1276  				body, err = gzip.NewReader(body)
1277  				if err != nil {
1278  					err = wrapErrors(responseLogger(c, response), err)
1279  					response.setReceivedAt()
1280  					return response, err
1281  				}
1282  				defer closeq(body)
1283  			}
1284  		}
1285  
1286  		if response.body, err = readAllWithLimit(body, req.responseBodyLimit); err != nil {
1287  			err = wrapErrors(responseLogger(c, response), err)
1288  			response.setReceivedAt()
1289  			return response, err
1290  		}
1291  
1292  		response.size = int64(len(response.body))
1293  	}
1294  
1295  	response.setReceivedAt() // after we read the body
1296  
1297  	// Apply Response middleware
1298  	err = responseLogger(c, response)
1299  	if err != nil {
1300  		return response, wrapNoRetryErr(err)
1301  	}
1302  
1303  	for _, f := range c.afterResponse {
1304  		if err = f(c, response); err != nil {
1305  			break
1306  		}
1307  	}
1308  
1309  	return response, wrapNoRetryErr(err)
1310  }
1311  
1312  var ErrResponseBodyTooLarge = errors.New("resty: response body too large")
1313  
1314  // https://github.com/golang/go/issues/51115
1315  // [io.LimitedReader] can only return [io.EOF]
1316  func readAllWithLimit(r io.Reader, maxSize int) ([]byte, error) {
1317  	if maxSize <= 0 {
1318  		return io.ReadAll(r)
1319  	}
1320  
1321  	var buf [512]byte // make buf stack allocated
1322  	result := make([]byte, 0, 512)
1323  	total := 0
1324  	for {
1325  		n, err := r.Read(buf[:])
1326  		total += n
1327  		if total > maxSize {
1328  			return nil, ErrResponseBodyTooLarge
1329  		}
1330  
1331  		if err != nil {
1332  			if err == io.EOF {
1333  				result = append(result, buf[:n]...)
1334  				break
1335  			}
1336  			return nil, err
1337  		}
1338  
1339  		result = append(result, buf[:n]...)
1340  	}
1341  
1342  	return result, nil
1343  }
1344  
1345  // getting TLS client config if not exists then create one
1346  func (c *Client) tlsConfig() (*tls.Config, error) {
1347  	transport, err := c.Transport()
1348  	if err != nil {
1349  		return nil, err
1350  	}
1351  	if transport.TLSClientConfig == nil {
1352  		transport.TLSClientConfig = &tls.Config{}
1353  	}
1354  	return transport.TLSClientConfig, nil
1355  }
1356  
1357  // Transport method returns [http.Transport] currently in use or error
1358  // in case the currently used `transport` is not a [http.Transport].
1359  //
1360  // Since v2.8.0 has become exported method.
1361  func (c *Client) Transport() (*http.Transport, error) {
1362  	if transport, ok := c.httpClient.Transport.(*http.Transport); ok {
1363  		return transport, nil
1364  	}
1365  	return nil, errors.New("current transport is not an *http.Transport instance")
1366  }
1367  
1368  // just an internal helper method
1369  func (c *Client) outputLogTo(w io.Writer) *Client {
1370  	c.log.(*logger).l.SetOutput(w)
1371  	return c
1372  }
1373  
1374  // ResponseError is a wrapper that includes the server response with an error.
1375  // Neither the err nor the response should be nil.
1376  type ResponseError struct {
1377  	Response *Response
1378  	Err      error
1379  }
1380  
1381  func (e *ResponseError) Error() string {
1382  	return e.Err.Error()
1383  }
1384  
1385  func (e *ResponseError) Unwrap() error {
1386  	return e.Err
1387  }
1388  
1389  // Helper to run errorHooks hooks.
1390  // It wraps the error in a [ResponseError] if the resp is not nil
1391  // so hooks can access it.
1392  func (c *Client) onErrorHooks(req *Request, resp *Response, err error) {
1393  	if err != nil {
1394  		if resp != nil { // wrap with ResponseError
1395  			err = &ResponseError{Response: resp, Err: err}
1396  		}
1397  		for _, h := range c.errorHooks {
1398  			h(req, err)
1399  		}
1400  	} else {
1401  		for _, h := range c.successHooks {
1402  			h(c, resp)
1403  		}
1404  	}
1405  }
1406  
1407  // Helper to run panicHooks hooks.
1408  func (c *Client) onPanicHooks(req *Request, err error) {
1409  	for _, h := range c.panicHooks {
1410  		h(req, err)
1411  	}
1412  }
1413  
1414  // Helper to run invalidHooks hooks.
1415  func (c *Client) onInvalidHooks(req *Request, err error) {
1416  	for _, h := range c.invalidHooks {
1417  		h(req, err)
1418  	}
1419  }
1420  
1421  //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
1422  // File struct and its methods
1423  //_______________________________________________________________________
1424  
1425  // File struct represents file information for multipart request
1426  type File struct {
1427  	Name      string
1428  	ParamName string
1429  	io.Reader
1430  }
1431  
1432  // String method returns the string value of current file details
1433  func (f *File) String() string {
1434  	return fmt.Sprintf("ParamName: %v; FileName: %v", f.ParamName, f.Name)
1435  }
1436  
1437  //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
1438  // MultipartField struct
1439  //_______________________________________________________________________
1440  
1441  // MultipartField struct represents the custom data part for a multipart request
1442  type MultipartField struct {
1443  	Param       string
1444  	FileName    string
1445  	ContentType string
1446  	io.Reader
1447  }
1448  
1449  func createClient(hc *http.Client) *Client {
1450  	if hc.Transport == nil {
1451  		hc.Transport = createTransport(nil)
1452  	}
1453  
1454  	c := &Client{ // not setting lang default values
1455  		QueryParam:             url.Values{},
1456  		FormData:               url.Values{},
1457  		Header:                 http.Header{},
1458  		Cookies:                make([]*http.Cookie, 0),
1459  		RetryWaitTime:          defaultWaitTime,
1460  		RetryMaxWaitTime:       defaultMaxWaitTime,
1461  		PathParams:             make(map[string]string),
1462  		RawPathParams:          make(map[string]string),
1463  		JSONMarshal:            json.Marshal,
1464  		JSONUnmarshal:          json.Unmarshal,
1465  		XMLMarshal:             xml.Marshal,
1466  		XMLUnmarshal:           xml.Unmarshal,
1467  		HeaderAuthorizationKey: http.CanonicalHeaderKey("Authorization"),
1468  		AuthScheme:             "Bearer",
1469  
1470  		jsonEscapeHTML:      true,
1471  		httpClient:          hc,
1472  		debugBodySizeLimit:  math.MaxInt32,
1473  		udBeforeRequestLock: &sync.RWMutex{},
1474  		afterResponseLock:   &sync.RWMutex{},
1475  	}
1476  
1477  	// Logger
1478  	c.SetLogger(createLogger())
1479  
1480  	// default before request middlewares
1481  	c.beforeRequest = []RequestMiddleware{
1482  		parseRequestURL,
1483  		parseRequestHeader,
1484  		parseRequestBody,
1485  		createHTTPRequest,
1486  		addCredentials,
1487  		createCurlCmd,
1488  	}
1489  
1490  	// user defined request middlewares
1491  	c.udBeforeRequest = []RequestMiddleware{}
1492  
1493  	// default after response middlewares
1494  	c.afterResponse = []ResponseMiddleware{
1495  		parseResponseBody,
1496  		saveResponseIntoFile,
1497  	}
1498  
1499  	return c
1500  }
1501