Full Code of go-resty/resty for AI

v3 0fadf8088bd3 cached
51 files
634.8 KB
181.3k tokens
1073 symbols
1 requests
Download .txt
Showing preview only (657K chars total). Download the full file or copy to clipboard to get everything.
Repository: go-resty/resty
Branch: v3
Commit: 0fadf8088bd3
Files: 51
Total size: 634.8 KB

Directory structure:
gitextract_43qx_89h/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── ci.yml
│       └── label-actions.yml
├── .gitignore
├── .testdata/
│   ├── cert.pem
│   ├── key.pem
│   ├── sample-root.pem
│   └── text-file.txt
├── BUILD.bazel
├── LICENSE
├── README.md
├── WORKSPACE
├── benchmark_test.go
├── cert_watcher_test.go
├── circuit_breaker.go
├── circuit_breaker_test.go
├── client.go
├── client_test.go
├── context_test.go
├── curl.go
├── curl_test.go
├── debug.go
├── digest.go
├── digest_test.go
├── go.mod
├── go.sum
├── hedging.go
├── hedging_test.go
├── load_balancer.go
├── load_balancer_test.go
├── middleware.go
├── middleware_test.go
├── multipart.go
├── multipart_test.go
├── redirect.go
├── request.go
├── request_test.go
├── response.go
├── resty.go
├── resty_test.go
├── retry.go
├── retry_test.go
├── sse.go
├── sse_test.go
├── stream.go
├── stream_test.go
├── trace.go
├── transport_dial.go
├── transport_dial_wasm.go
├── util.go
└── util_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: [jeevatkm]
custom: ["https://www.paypal.com/donate/?cmd=_donations&business=QWMZG74FW4QYC&lc=US&item_name=Resty+Library+for+Go&currency_code=USD"]


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches:
      - v3
      - v2
    paths-ignore:
      - '**.md'
      - '**.bazel'
      - 'WORKSPACE'
  pull_request:
    branches:
      - main
      - v3
      - v2
    paths-ignore:
      - '**.md'
      - '**.bazel'
      - 'WORKSPACE'

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  build:
    name: Build
    strategy:
      matrix:
        go: [ 'stable', '1.23.x' ]
        os: [ ubuntu-latest ]

    runs-on: ${{ matrix.os }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go }}
          cache: true
          cache-dependency-path: go.sum

      - name: Format
        run: diff -u <(echo -n) <(go fmt $(go list ./...))

      - name: Test
        run: go run gotest.tools/gotestsum@latest -f testname -- ./... -race -count=1 -coverprofile=coverage.txt -covermode=atomic -coverpkg=./... -shuffle=on

      - name: Upload coverage to Codecov
        if: ${{ matrix.os == 'ubuntu-latest' && matrix.go == 'stable' }}
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: ./coverage.txt
          flags: unittests


================================================
FILE: .github/workflows/label-actions.yml
================================================
name: 'Label'

on:
  pull_request:
    types: [labeled]
    paths-ignore:
      - '**.md'
      - '**.bazel'
      - 'WORKSPACE'

jobs:
  build:
    strategy:
      matrix:
        go: [ 'stable', '1.23.x' ]
        os: [ ubuntu-latest ]

    name: Run Build
    if: ${{ github.event.label.name == 'run-build' }}
    runs-on: ${{ matrix.os }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go }}
          cache: true
          cache-dependency-path: go.sum

      - name: Format
        run: diff -u <(echo -n) <(go fmt $(go list ./...))

      - name: Test
        run: go run gotest.tools/gotestsum@latest -f testname -- ./... -race -count=1 -coverprofile=coverage.txt -covermode=atomic -coverpkg=./... -shuffle=on

      - name: Upload coverage to Codecov
        if: ${{ matrix.os == 'ubuntu-latest' && matrix.go == 'stable' }}
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: ./coverage.txt
          flags: unittests


================================================
FILE: .gitignore
================================================
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof

coverage.out
coverage.txt

# Exclude IDE folders
.idea/*
.vscode/*


================================================
FILE: .testdata/cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIC+jCCAeKgAwIBAgIRAJce5ewsoW44j0qvSABmq7owDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yNTAxMDQwNzA3MTNaFw0yNjAxMDQwNzA3
MTNaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCYkTN1g/0Z3KkS3w0lX9yhZkwiA0obXCeFs7hpRP0p4WlW3uADyXQ5
h2MaYx8OCA7oGU7/dWOPhtE3rgFEz7IwLxcP5d02ukLGlFD69D6KLyTXwCFmvOWQ
5fbOq4s73WTNDfYSTYNzeujDCjeu/Bk0OVhdxbyZdyrpdm+UBfH8uIDoGeCRXnji
nqG9HNOQx6r/S6FqC5j/7PrVl1i66WlqRzKEJB94uejfujrHq8RjQm/wzEutU5df
C39zEEEx75qQt7Jc0asm1AqAKSq34xn4rVajWrBZ/WudUUizHfaBDP61uPFvPyKW
JDvTSdeoM9TPX0y0cjo6AwSrdLl7flrRAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAdHvPQe3EJ4/X6K/bklJUhIfM
KBauH8VMBfri7xLawleKssm7GdiFivSA0g1pArkl8SALBlPqhrx7rwlyyivLTZaR
VFvXaQ9eU0zGnSnDnKVz6CX/zn3TKfcgZPEBclayh0ldm7A8xSJWaWbRZ+s9e9x1
XcQTn2KkMZfBDMnGEWQ3KZrClvO5ZfkqSiyzEm9+eF0m0E7ujTyfSVMsPdyldA6U
pHG8omQTyOzJl2I4z7DlS0AEsL0TJHV4iKr9rDei2xQz/wtful5qU/taYp2Y6zMH
8ytnDldJhmcCwmvtqvK5p6CbkatE7TFyw2CxQJHnQef+Y4W94sSZWg9CGRKDIQ==
-----END CERTIFICATE-----


================================================
FILE: .testdata/key.pem
================================================
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCYkTN1g/0Z3KkS
3w0lX9yhZkwiA0obXCeFs7hpRP0p4WlW3uADyXQ5h2MaYx8OCA7oGU7/dWOPhtE3
rgFEz7IwLxcP5d02ukLGlFD69D6KLyTXwCFmvOWQ5fbOq4s73WTNDfYSTYNzeujD
Cjeu/Bk0OVhdxbyZdyrpdm+UBfH8uIDoGeCRXnjinqG9HNOQx6r/S6FqC5j/7PrV
l1i66WlqRzKEJB94uejfujrHq8RjQm/wzEutU5dfC39zEEEx75qQt7Jc0asm1AqA
KSq34xn4rVajWrBZ/WudUUizHfaBDP61uPFvPyKWJDvTSdeoM9TPX0y0cjo6AwSr
dLl7flrRAgMBAAECggEAJPTPNUEilxgncGXNZmdBJ2uDN536XoRFIpL1MbK/bFyo
yp00QFaVK7ZK4EJwbFKxYbF3vFOwKT0sAsPIlOWGsTtG59fzbOVTdYzJzPBLEef3
kbd9n8hUB3RdA5T0Ji0r1Kv0FlzmYZu9NDmOYXm5lTfq2tQiKj5+i4zf3EhQZLng
4wVxBT7yQUQcstJv5K1L6HVzunSYtbHx8ZVxmw+tJ4lMCK23KPlvncZZTT8chWdT
3GOp5nYIHk9E5jQnBnj7p73sxZUCZlb8uhLtdcgAXc4scptEVO+7n5zOaXIv40Oz
yfkESgHcZWAMDvnkxdySHlD38Z2LIKDGbqR6O9wcwQKBgQDBO6fFPXO41nsxdVCB
nhCgL2hsGjaxJzGBLaOJNVMMFRASN3Yqvs4N1Hn7lawRI/FRRffxjLkZfNGEBSF2
OipdvX19Oe2hCZxvwHPoe5sb/Dh6KE7If1hRLOCXg/8E7ADBtAp94dam1WF4Kh6N
Va6+n2YKif2rqye1YtRoUU46iQKBgQDKH/eMcMRUe9IySxHLogidOUwa0X7WrxF/
PkXGpPbHQtMOJF5cVzh+L+foUKXNM60lgmCH0438GKU7kirC/dVtD/bwE598/XFZ
vnjPV7Adf9vBz9NN8cS/4uEfQYbvTRmrnrQK+ZhOe8hmwjapxqdWrVHNUtvx18vL
qBwR4YjsCQKBgCycMx1MFJ1FludSKCXkcf4pM7hRTPMVE065VJnmn6eYbT9nYnZ3
2mZC+W5lnXXPkHSs7JLtZAZIVK5f6Nu8je9aQdBZQUz+RQlfquKvNp39WqSJDbcn
/yGudKNGK+fc/Ee74vgw3Tdi57+wKaGDeHY1on8oYFHzj5VGnbb/nknRAoGBAK2Z
hyQ4NmfZcU+A6mfbY0qmS5c9F5OMCZsgAQ374XiDDIK4+dKVlw/KVYRSwBTerXfp
4r7GFMzQ3hmsEM4o9YYWkCDiubjAdPp/fYOX7MtpZXWw6euoGzQzyObvgNVHgyTD
yh8jAI1oA1c+t3RaCp+HfRq8b+vnTEI+wN0auF8BAoGBAJmw+GgHCZGpw2XPNu+X
8kuVGbQYAjTOXhBM4WzZyhfH1TWKLGn7C9YixhE2AW0UWKDvy+6OqPhe8q3KVms3
8YZ1W+vbUNEZNGE0XrB5ZMXfePiqisCz0jgP9OAuT+ii4aI3MAm3zgCEC6UTMvLq
gNBu3Tcy6udxnUf7czzJDRtE
-----END PRIVATE KEY-----


================================================
FILE: .testdata/sample-root.pem
================================================
-----BEGIN CERTIFICATE-----
MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7
qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g
K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI
KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n
ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB
BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY
/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/
zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
-----END CERTIFICATE-----


================================================
FILE: .testdata/text-file.txt
================================================
 THIS IS TEXT FILE FOR MULTIPART UPLOAD TEST :)

- go-resty


================================================
FILE: BUILD.bazel
================================================
load("@bazel_gazelle//:def.bzl", "gazelle")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

# gazelle:prefix resty.dev/v3
# gazelle:go_naming_convention import_alias
gazelle(name = "gazelle")

go_library(
    name = "resty",
    srcs = [
        "circuit_breaker.go",
        "client.go",
        "curl.go",
        "debug.go",
        "digest.go",
        "hedging.go",
        "load_balancer.go",
        "middleware.go",
        "multipart.go",
        "redirect.go",
        "request.go",
        "response.go",
        "resty.go",
        "retry.go",
        "sse.go",
        "stream.go",
        "trace.go",
        "transport_dial.go",
        "transport_dial_wasm.go",
        "util.go",
    ],
    importpath = "resty.dev/v3",
    visibility = ["//visibility:public"],
    deps = ["@org_golang_x_net//publicsuffix:go_default_library"],
)

go_test(
    name = "resty_test",
    srcs = [
        "benchmark_test.go",
        "cert_watcher_test.go",
        "client_test.go",
        "context_test.go",
        "curl_test.go",
        "digest_test.go",
        "hedging_test.go",
        "load_balancer_test.go",
        "middleware_test.go",
        "multipart_test.go",
        "request_test.go",
        "resty_test.go",
        "retry_test.go",
        "sse_test.go",
        "util_test.go",
    ],
    data = glob([".testdata/*"]),
    embed = [":resty"],
)

alias(
    name = "go_default_library",
    actual = ":resty",
    visibility = ["//visibility:public"],
)


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015-present Jeevanandam M., https://myjeeva.com <jeeva@myjeeva.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<p align="center">
  <img src="https://resty.dev/svg/resty-logo.svg" width="175" alt="Resty Logo">
</p>
<p align="center"><strong>Simple HTTP, REST, and SSE client library for Go</strong></p>

<p align="center" style="margin-top:3rem"><a href="https://github.com/go-resty/resty/actions/workflows/ci.yml?query=branch%3Av3" target="_blank"><img src="https://github.com/go-resty/resty/actions/workflows/ci.yml/badge.svg?branch=v3" alt="Resty Build Status">
</a><a href="https://app.codecov.io/gh/go-resty/resty/tree/v3" target="_blank"><img src="https://codecov.io/gh/go-resty/resty/branch/v3/graph/badge.svg" alt="Resty Code Coverage">
</a><a href="https://goreportcard.com/report/resty.dev/v3" target="_blank"><img src="https://goreportcard.com/badge/resty.dev/v3" alt="Go Report Card">
</a><a href="https://pkg.go.dev/resty.dev/v3" target="_blank"><img src="https://pkg.go.dev/badge/resty.dev/v3" alt="Resty GoDoc">
</a><a href="LICENSE"><img src="https://img.shields.io/github/license/go-resty/resty.svg" alt="License"></a> <a href="https://github.com/avelino/awesome-go" target="_blank"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Go"></a></p>
<p align="center" style="margin-bottom:1rem"><a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fgo-resty%2Fresty?ref=badge_shield&amp;issueType=license" alt="FOSSA Status"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fgo-resty%2Fresty.svg?type=shield&amp;issueType=license"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fgo-resty%2Fresty?ref=badge_shield&amp;issueType=security" alt="FOSSA Status"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fgo-resty%2Fresty.svg?type=shield&amp;issueType=security"></a></p>


## Documentation

Go to https://resty.dev and refer to godoc.

## Minimum Go Version

Use `go1.23` and above.

## Support & Donate

* Sponsor via [GitHub](https://github.com/sponsors/jeevatkm)
* Donate via [PayPal](https://www.paypal.com/donate/?cmd=_donations&business=QWMZG74FW4QYC&lc=US&item_name=Resty+Library+for+Go&currency_code=USD)

## Versioning

Resty releases versions according to [Semantic Versioning](http://semver.org)

  * Resty v3 provides Go Vanity URL `resty.dev/v3`.
  * Resty v2 migrated away from `gopkg.in` service, `github.com/go-resty/resty/v2`.
  * Resty fully adapted to `go mod` capabilities since `v1.10.0` release.
  * Resty v1 series was using `gopkg.in` to provide versioning. `gopkg.in/resty.vX` points to appropriate tagged versions; `X` denotes version series number and it's a stable release for production use. For e.g. `gopkg.in/resty.v0`.

## Contribution

I would welcome your contribution!

* If you find any improvement or issue you want to fix, feel free to send a pull request.
* The pull requests must include test cases for feature/fix/enhancement with patch coverage of 100%.
* I have done my best to bring pretty good coverage. I would request contributors to do the same for their contribution.

I always look forward to hearing feedback, appreciation, and real-world usage stories from Resty users on [GitHub Discussions](https://github.com/go-resty/resty/discussions). It means a lot to me.

## Creator

[Jeevanandam M.](https://github.com/jeevatkm) (jeeva@myjeeva.com)


## Contributors

Have a look on [Contributors](https://github.com/go-resty/resty/graphs/contributors) page.

## License Info

Resty released under MIT [LICENSE](LICENSE).

Resty [Documentation](https://github.com/go-resty/docs) and website released under Apache-2.0 [LICENSE](https://github.com/go-resty/docs/blob/main/LICENSE).


================================================
FILE: WORKSPACE
================================================
workspace(name = "resty")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "io_bazel_rules_go",
    sha256 = "80a98277ad1311dacd837f9b16db62887702e9f1d1c4c9f796d0121a46c8e184",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.46.0/rules_go-v0.46.0.zip",
        "https://github.com/bazelbuild/rules_go/releases/download/v0.46.0/rules_go-v0.46.0.zip",
    ],
)

http_archive(
    name = "bazel_gazelle",
    sha256 = "62ca106be173579c0a167deb23358fdfe71ffa1e4cfdddf5582af26520f1c66f",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz",
        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz",
    ],
)

load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")

go_rules_dependencies()

go_register_toolchains(version = "1.21")

load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")

gazelle_dependencies()


================================================
FILE: benchmark_test.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"bytes"
	"strings"
	"testing"
	"time"
)

func Benchmark_parseRequestURL_PathParams(b *testing.B) {
	c := New().SetPathParams(map[string]string{
		"foo": "1",
		"bar": "2",
	}).SetPathRawParams(map[string]string{
		"foo": "3",
		"xyz": "4",
	})
	r := c.R().SetPathParams(map[string]string{
		"foo": "5",
		"qwe": "6",
	}).SetPathRawParams(map[string]string{
		"foo": "7",
		"asd": "8",
	})
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		r.URL = "https://example.com/{foo}/{bar}/{xyz}/{qwe}/{asd}"
		if err := parseRequestURL(c, r); err != nil {
			b.Errorf("parseRequestURL() error = %v", err)
		}
	}
}

func Benchmark_parseRequestURL_QueryParams(b *testing.B) {
	c := New().SetQueryParams(map[string]string{
		"foo": "1",
		"bar": "2",
	})
	r := c.R().SetQueryParams(map[string]string{
		"foo": "5",
		"qwe": "6",
	})
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		r.URL = "https://example.com/"
		if err := parseRequestURL(c, r); err != nil {
			b.Errorf("parseRequestURL() error = %v", err)
		}
	}
}

func Benchmark_parseRequestHeader(b *testing.B) {
	c := New()
	r := c.R()
	c.SetHeaders(map[string]string{
		"foo": "1", // ignored, because of the same header in the request
		"bar": "2",
	})
	r.SetHeaders(map[string]string{
		"foo": "3",
		"xyz": "4",
	})
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestHeader(c, r); err != nil {
			b.Errorf("parseRequestHeader() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_string(b *testing.B) {
	c := New()
	r := c.R()
	r.SetBody("foo")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_byte(b *testing.B) {
	c := New()
	r := c.R()
	r.SetBody([]byte("foo"))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_reader(b *testing.B) {
	c := New()
	r := c.R()
	r.SetBody(bytes.NewBufferString("foo"))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_struct(b *testing.B) {
	type FooBar struct {
		Foo string `json:"foo"`
		Bar string `json:"bar"`
	}
	c := New()
	r := c.R()
	r.SetBody(FooBar{Foo: "1", Bar: "2"}).SetHeader(hdrContentTypeKey, jsonContentType)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_struct_xml(b *testing.B) {
	type FooBar struct {
		Foo string `xml:"foo"`
		Bar string `xml:"bar"`
	}
	c := New()
	r := c.R()
	r.SetBody(FooBar{Foo: "1", Bar: "2"}).SetHeader(hdrContentTypeKey, "text/xml")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_map(b *testing.B) {
	c := New()
	r := c.R()
	r.SetBody(map[string]string{
		"foo": "1",
		"bar": "2",
	}).SetHeader(hdrContentTypeKey, jsonContentType)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_slice(b *testing.B) {
	c := New()
	r := c.R()
	r.SetBody([]string{"1", "2"}).SetHeader(hdrContentTypeKey, jsonContentType)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_FormData(b *testing.B) {
	c := New()
	r := c.R()
	c.SetFormData(map[string]string{"foo": "1", "bar": "2"})
	r.SetFormData(map[string]string{"foo": "3", "baz": "4"})
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

func Benchmark_parseRequestBody_MultiPart(b *testing.B) {
	c := New()
	r := c.R()
	c.SetFormData(map[string]string{"foo": "1", "bar": "2"})
	r.SetFormData(map[string]string{"foo": "3", "baz": "4"}).
		SetMultipartFormData(map[string]string{"foo": "5", "xyz": "6"}).
		SetFileReader("qwe", "qwe.txt", strings.NewReader("7")).
		SetMultipartFields(
			&MultipartField{
				Name:        "sdj",
				ContentType: "text/plain",
				Reader:      strings.NewReader("8"),
			},
		).
		SetMethod(MethodPost)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := parseRequestBody(c, r); err != nil {
			b.Errorf("parseRequestBody() error = %v", err)
		}
	}
}

// benchmarkStringer implements fmt.Stringer for benchmarking
type benchmarkStringer struct {
	value string
}

func (s benchmarkStringer) String() string {
	return s.value
}

// Tier 1: most common URL types
func Benchmark_formatAnyToString_string(b *testing.B) {
	v := "hello world"
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_int(b *testing.B) {
	v := 12345
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_bool(b *testing.B) {
	v := true
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_int64(b *testing.B) {
	v := int64(9223372036854775807)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_stringSlice(b *testing.B) {
	v := []string{"a", "b", "c"}
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

// Tier 2: common stdlib types
func Benchmark_formatAnyToString_time(b *testing.B) {
	v := time.Date(2024, 6, 15, 10, 30, 0, 0, time.UTC)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_byteSlice(b *testing.B) {
	v := []byte("binary data")
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_float64(b *testing.B) {
	v := 3.14159265359
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

// Tier 3: less common integers (signed)
func Benchmark_formatAnyToString_int32(b *testing.B) {
	v := int32(2147483647)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_int16(b *testing.B) {
	v := int16(32767)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_int8(b *testing.B) {
	v := int8(127)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

// Tier 4: less common integers (unsigned)
func Benchmark_formatAnyToString_uint64(b *testing.B) {
	v := uint64(18446744073709551615)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_uint32(b *testing.B) {
	v := uint32(4294967295)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_uint16(b *testing.B) {
	v := uint16(65535)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_uint8(b *testing.B) {
	v := uint8(255)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_uint(b *testing.B) {
	v := uint(12345)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

// Tier 5: rare types and fallbacks
func Benchmark_formatAnyToString_float32(b *testing.B) {
	v := float32(3.14)
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_stringer(b *testing.B) {
	v := benchmarkStringer{value: "custom value"}
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}

func Benchmark_formatAnyToString_default(b *testing.B) {
	v := struct{ Name string }{Name: "test"}
	for i := 0; i < b.N; i++ {
		_ = formatAnyToString(v)
	}
}


================================================
FILE: cert_watcher_test.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

type certPaths struct {
	RootCAKey  string
	RootCACert string
	TLSKey     string
	TLSCert    string
}

func TestClient_SetRootCertificateWatcher(t *testing.T) {
	// For this test, we want to:
	// - Generate root CA
	// - Generate TLS cert signed with root CA
	// - Start a Test HTTPS server
	// - Create a Resty client with SetRootCertificateWatcher and SetClientRootCertificateWatcher
	// - Send multiple requests and re-generate the certs periodically to reproduce renewal

	certDir := t.TempDir()
	paths := certPaths{
		RootCAKey:  filepath.Join(certDir, "root-ca.key"),
		RootCACert: filepath.Join(certDir, "root-ca.crt"),
		TLSKey:     filepath.Join(certDir, "tls.key"),
		TLSCert:    filepath.Join(certDir, "tls.crt"),
	}

	generateCerts(t, paths)

	ts := createTestTLSServer(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	}, paths.TLSCert, paths.TLSKey)
	defer ts.Close()

	poolingInterval := 100 * time.Millisecond

	client := NewWithTransportSettings(&TransportSettings{
		// Make sure that TLS handshake happens for all request
		// (otherwise, test may succeed because 1st TLS session is re-used)
		DisableKeepAlives: true,
	}).SetRootCertificatesWatcher(
		&CertWatcherOptions{PoolInterval: poolingInterval},
		paths.RootCACert,
	).SetClientRootCertificatesWatcher(
		&CertWatcherOptions{PoolInterval: poolingInterval},
		paths.RootCACert,
	).SetDebug(false)

	url := strings.Replace(ts.URL, "127.0.0.1", "localhost", 1)
	t.Log("Test URL:", url)

	t.Run("Cert Watcher should handle certs rotation", func(t *testing.T) {
		for i := 0; i < 5; i++ {
			res, err := client.R().Get(url)
			if err != nil {
				t.Fatal(err)
			}

			assertEqual(t, res.StatusCode(), http.StatusOK)

			if i%2 == 1 {
				// Re-generate certs to simulate renewal scenario
				generateCerts(t, paths)
				time.Sleep(50 * time.Millisecond)
			}

		}
	})

	t.Run("Cert Watcher should recover on failure", func(t *testing.T) {
		// Delete root cert and re-create it to ensure that cert watcher is able to recover

		// Re-generate certs to invalidate existing cert
		generateCerts(t, paths)
		// Delete root cert so that Cert Watcher will fail
		err := os.RemoveAll(paths.RootCACert)
		assertNil(t, err)

		// Reset TLS config to ensure that previous root cert is not re-used
		tr, err := client.HTTPTransport()
		assertNil(t, err)
		tr.TLSClientConfig = nil
		client.SetTransport(tr)

		time.Sleep(50 * time.Millisecond)

		_, err = client.R().Get(url)
		// We expect an error since root cert has been deleted
		assertNotNil(t, err)

		// Re-generate certs. We except cert watcher to reload the new root cert.
		generateCerts(t, paths)
		time.Sleep(50 * time.Millisecond)
		_, err = client.R().Get(url)
		assertNil(t, err)
	})

	err := client.Close()
	assertNil(t, err)
}

func generateCerts(t *testing.T, paths certPaths) {
	rootKey, rootCert, err := generateRootCA(paths.RootCAKey, paths.RootCACert)
	if err != nil {
		t.Fatal(err)
	}

	if err := generateTLSCert(paths.TLSKey, paths.TLSCert, rootKey, rootCert); err != nil {
		t.Fatal(err)
	}
}

// Generate a Root Certificate Authority (CA)
func generateRootCA(keyPath, certPath string) (*rsa.PrivateKey, []byte, error) {
	// Generate the key for the Root CA
	rootKey, err := generateKey()
	if err != nil {
		return nil, nil, err
	}

	// Define the maximum value you want for the random big integer
	max := new(big.Int).Lsh(big.NewInt(1), 256) // Example: 256 bits

	// Generate a random big.Int
	randomBigInt, err := rand.Int(rand.Reader, max)
	if err != nil {
		return nil, nil, err
	}

	// Create the root certificate template
	rootCertTemplate := &x509.Certificate{
		SerialNumber: randomBigInt,
		Subject: pkix.Name{
			Organization: []string{"YourOrg"},
			Country:      []string{"US"},
			Province:     []string{"State"},
			Locality:     []string{"City"},
			CommonName:   "YourRootCA",
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().Add(time.Hour * 10),
		KeyUsage:              x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
		IsCA:                  true,
		BasicConstraintsValid: true,
	}

	// Self-sign the root certificate
	rootCert, err := x509.CreateCertificate(rand.Reader, rootCertTemplate, rootCertTemplate, &rootKey.PublicKey, rootKey)
	if err != nil {
		return nil, nil, err
	}

	// Save the Root CA key and certificate
	if err := savePEMKey(keyPath, rootKey); err != nil {
		return nil, nil, err
	}
	if err := savePEMCert(certPath, rootCert); err != nil {
		return nil, nil, err
	}

	return rootKey, rootCert, nil
}

// Generate a TLS Certificate signed by the Root CA
func generateTLSCert(keyPath, certPath string, rootKey *rsa.PrivateKey, rootCert []byte) error {
	// Generate a key for the server
	serverKey, err := generateKey()
	if err != nil {
		return err
	}

	// Parse the Root CA certificate
	parsedRootCert, err := x509.ParseCertificate(rootCert)
	if err != nil {
		return err
	}

	// Create the server certificate template
	serverCertTemplate := &x509.Certificate{
		SerialNumber: big.NewInt(2),
		Subject: pkix.Name{
			Organization: []string{"YourOrg"},
			CommonName:   "localhost",
		},
		NotBefore:   time.Now(),
		NotAfter:    time.Now().Add(time.Hour * 10),
		KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
		DNSNames:    []string{"localhost"},
	}

	// Sign the server certificate with the Root CA
	serverCert, err := x509.CreateCertificate(rand.Reader, serverCertTemplate, parsedRootCert, &serverKey.PublicKey, rootKey)
	if err != nil {
		return err
	}

	// Save the server key and certificate
	if err := savePEMKey(keyPath, serverKey); err != nil {
		return err
	}
	if err := savePEMCert(certPath, serverCert); err != nil {
		return err
	}

	return nil
}

func generateKey() (*rsa.PrivateKey, error) {
	return rsa.GenerateKey(rand.Reader, 2048)
}

func savePEMKey(fileName string, key *rsa.PrivateKey) error {
	keyFile, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer keyFile.Close()

	privateKeyPEM := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(key),
	}

	return pem.Encode(keyFile, privateKeyPEM)
}

func savePEMCert(fileName string, cert []byte) error {
	certFile, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer certFile.Close()

	certPEM := &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: cert,
	}

	return pem.Encode(certFile, certPEM)
}


================================================
FILE: circuit_breaker.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"errors"
	"net/http"
	"sync"
	"sync/atomic"
	"time"
)

// ErrCircuitBreakerOpen is returned when the circuit breaker is open.
var ErrCircuitBreakerOpen = errors.New("resty: circuit breaker open")

type (
	// CircuitBreakerTriggerHook type is for reacting to circuit breaker trigger hooks.
	CircuitBreakerTriggerHook func(*Request, error)

	// CircuitBreakerStateChangeHook type is for reacting to circuit breaker state change hooks.
	CircuitBreakerStateChangeHook func(oldState, newState CircuitBreakerState)

	// CircuitBreakerState type represents the state of the circuit breaker.
	CircuitBreakerState uint32
)

// group is an interface for types that can be combined and inverted
type group[T any] interface {
	op(T) T
	empty() T
	inverse() T
}

// totalAndFailures tracks total requests and failures
type totalAndFailures struct {
	total    int
	failures int
}

func (tf totalAndFailures) op(g totalAndFailures) totalAndFailures {
	tf.total += g.total
	tf.failures += g.failures
	return tf
}

func (tf totalAndFailures) empty() totalAndFailures {
	return totalAndFailures{}
}

func (tf totalAndFailures) inverse() totalAndFailures {
	tf.total = -tf.total
	tf.failures = -tf.failures
	return tf
}

// slidingWindow implements a time-based sliding window for tracking values
type slidingWindow[G group[G]] struct {
	mutex     sync.RWMutex
	total     G
	values    []G
	idx       int
	lastStart time.Time
	interval  time.Duration
}

func newSlidingWindow[G group[G]](empty func() G, interval time.Duration, buckets int) *slidingWindow[G] {
	return &slidingWindow[G]{
		total:     empty(),
		values:    make([]G, buckets),
		idx:       0,
		lastStart: time.Now(),
		interval:  interval,
	}
}

func (sw *slidingWindow[G]) Add(val G) {
	sw.mutex.Lock()
	defer sw.mutex.Unlock()

	now := time.Now()
	elapsed := now.Sub(sw.lastStart)
	bucketDuration := sw.interval / time.Duration(len(sw.values))

	// Advance window if needed
	if elapsed >= bucketDuration {
		bucketsToAdvance := int(elapsed / bucketDuration)
		if bucketsToAdvance >= len(sw.values) {
			// Reset all buckets
			for i := range sw.values {
				sw.values[i] = sw.total.empty()
			}
			sw.total = sw.total.empty()
			sw.idx = 0
		} else {
			// Remove old buckets
			for i := 0; i < bucketsToAdvance; i++ {
				sw.idx = (sw.idx + 1) % len(sw.values)
				sw.total = sw.total.op(sw.values[sw.idx].inverse())
				sw.values[sw.idx] = sw.total.empty()
			}
		}
		sw.lastStart = now
	}

	// Add to current bucket
	sw.values[sw.idx] = sw.values[sw.idx].op(val)
	sw.total = sw.total.op(val)
}

func (sw *slidingWindow[G]) Get() G {
	sw.mutex.RLock()
	defer sw.mutex.RUnlock()
	return sw.total
}

func (sw *slidingWindow[G]) SetInterval(interval time.Duration) {
	sw.mutex.Lock()
	defer sw.mutex.Unlock()
	sw.interval = interval
}

const (
	// CircuitBreakerStateClosed represents the closed state of the circuit breaker.
	CircuitBreakerStateClosed CircuitBreakerState = iota

	// CircuitBreakerStateOpen represents the open state of the circuit breaker.
	CircuitBreakerStateOpen

	// CircuitBreakerStateHalfOpen represents the half-open state of the circuit breaker.
	CircuitBreakerStateHalfOpen
)

// CircuitBreaker struct implements a state machine to monitor and manage the
// states of circuit breakers. The three states are:
//   - Closed: requests are allowed
//   - Open: requests are blocked
//   - Half-Open: a single request is allowed to determine
//
// Transitions
//   - To Closed State: when the success count reaches the success threshold.
//   - To Open State: when the failure count reaches the failure threshold.
//   - Half-Open Check: when the specified timeout reaches, a single request is allowed
//     to determine the transition state; if failed, it goes back to the open state.
//
// Use [NewCircuitBreakerWithCount] or [NewCircuitBreakerWithRatio] to create a new [CircuitBreaker]
// instance accordingly.
type CircuitBreaker struct {
	lock         *sync.RWMutex
	policies     []CircuitBreakerPolicy
	resetTimeout time.Duration
	state        atomic.Value // CircuitBreakerState
	sw           *slidingWindow[totalAndFailures]

	// Hooks
	triggerHooks     []CircuitBreakerTriggerHook
	stateChangeHooks []CircuitBreakerStateChangeHook

	// Count-based
	failureThreshold uint64
	successThreshold uint64

	// Ratio-based
	isRatioBased bool
	failureRatio float64 // Threshold, e.g., 0.5 for 50% failure
	minRequests  uint64  // Minimum number of requests to consider failure ratio
}

// NewCircuitBreakerWithCount method creates a new [CircuitBreaker] instance with Count settings.
//
// The default settings are:
//   - Policies: CircuitBreaker5xxPolicy
func NewCircuitBreakerWithCount(failureThreshold uint64, successThreshold uint64,
	resetTimeout time.Duration, policies ...CircuitBreakerPolicy) *CircuitBreaker {
	cb := newCircuitBreaker(resetTimeout, policies...)
	cb.failureThreshold = failureThreshold
	cb.successThreshold = successThreshold
	return cb
}

// NewCircuitBreakerWithRatio method creates a new [CircuitBreaker] instance with Ratio settings.
//
// The default settings are:
//   - Policies: CircuitBreaker5xxPolicy
func NewCircuitBreakerWithRatio(failureRatio float64, minRequests uint64,
	resetTimeout time.Duration, policies ...CircuitBreakerPolicy) *CircuitBreaker {
	cb := newCircuitBreaker(resetTimeout, policies...)
	cb.failureRatio = failureRatio
	cb.minRequests = minRequests
	cb.isRatioBased = true
	return cb
}

func newCircuitBreaker(resetTimeout time.Duration, policies ...CircuitBreakerPolicy) *CircuitBreaker {
	cb := &CircuitBreaker{
		lock:         &sync.RWMutex{},
		resetTimeout: resetTimeout,
		policies:     []CircuitBreakerPolicy{CircuitBreaker5xxPolicy},
	}
	cb.state.Store(CircuitBreakerStateClosed)
	cb.sw = newSlidingWindow(
		func() totalAndFailures { return totalAndFailures{} },
		resetTimeout,
		10,
	)
	if len(policies) > 0 {
		cb.policies = policies
	}
	return cb
}

// OnTrigger method adds a [CircuitBreakerTriggerHook] to the [CircuitBreaker] instance.
func (cb *CircuitBreaker) OnTrigger(hooks ...CircuitBreakerTriggerHook) *CircuitBreaker {
	cb.lock.Lock()
	defer cb.lock.Unlock()
	cb.triggerHooks = append(cb.triggerHooks, hooks...)
	return cb
}

// onTriggerHooks method executes all registered trigger hooks.
func (cb *CircuitBreaker) onTriggerHooks(req *Request, err error) {
	cb.lock.RLock()
	defer cb.lock.RUnlock()
	for _, h := range cb.triggerHooks {
		h(req, err)
	}
}

// OnStateChange method adds a [CircuitBreakerStateChangeHook] to the [CircuitBreaker] instance.
func (cb *CircuitBreaker) OnStateChange(hooks ...CircuitBreakerStateChangeHook) *CircuitBreaker {
	cb.lock.Lock()
	defer cb.lock.Unlock()
	cb.stateChangeHooks = append(cb.stateChangeHooks, hooks...)
	return cb
}

// onStateChangeHooks method executes all registered state change hooks.
func (cb *CircuitBreaker) onStateChangeHooks(oldState, newState CircuitBreakerState) {
	cb.lock.RLock()
	defer cb.lock.RUnlock()
	for _, h := range cb.stateChangeHooks {
		h(oldState, newState)
	}
}

// CircuitBreakerPolicy is a function type that determines whether a response should
// trip the [CircuitBreaker].
type CircuitBreakerPolicy func(resp *http.Response) bool

// CircuitBreaker5xxPolicy is a [CircuitBreakerPolicy] that trips the [CircuitBreaker] if
// the response status code is 500 or greater.
func CircuitBreaker5xxPolicy(resp *http.Response) bool {
	return resp.StatusCode > 499
}

func (cb *CircuitBreaker) getState() CircuitBreakerState {
	return cb.state.Load().(CircuitBreakerState)
}

func (cb *CircuitBreaker) allow() error {
	if cb.getState() == CircuitBreakerStateOpen {
		return ErrCircuitBreakerOpen
	}

	return nil
}

func (cb *CircuitBreaker) applyPolicies(resp *http.Response) {
	failed := false
	for _, policy := range cb.policies {
		if policy(resp) {
			failed = true
			break
		}
	}

	if failed {
		cb.sw.Add(totalAndFailures{total: 1, failures: 1})

		switch cb.getState() {
		case CircuitBreakerStateClosed:
			tf := cb.sw.Get()

			if cb.isRatioBased {
				if tf.total >= int(cb.minRequests) {
					currentFailureRatio := float64(tf.failures) / float64(tf.total)
					if currentFailureRatio >= cb.failureRatio {
						cb.open()
					}
				}
			} else {
				if tf.failures >= int(cb.failureThreshold) {
					cb.open()
				}
			}
		case CircuitBreakerStateHalfOpen:
			cb.open()
		}

		return
	}

	cb.sw.Add(totalAndFailures{total: 1, failures: 0})

	switch cb.getState() {
	case CircuitBreakerStateClosed:
		return
	case CircuitBreakerStateHalfOpen:
		tf := cb.sw.Get()
		if tf.total-tf.failures >= int(cb.successThreshold) {
			cb.changeState(CircuitBreakerStateClosed)
		}
	}
}

func (cb *CircuitBreaker) open() {
	cb.changeState(CircuitBreakerStateOpen)
	go func() {
		time.Sleep(cb.resetTimeout)
		cb.changeState(CircuitBreakerStateHalfOpen)
	}()
}

func (cb *CircuitBreaker) changeState(state CircuitBreakerState) {
	oldState := cb.getState()
	cb.sw = newSlidingWindow(
		func() totalAndFailures { return totalAndFailures{} },
		cb.resetTimeout,
		10,
	)
	cb.state.Store(state)
	if oldState != state {
		cb.onStateChangeHooks(oldState, state)
	}
}


================================================
FILE: circuit_breaker_test.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"net/http"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

var _ CircuitBreakerPolicy = CircuitBreaker5xxPolicy

func TestCircuitBreakerCountBased(t *testing.T) {
	ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
		t.Logf("Method: %v", r.Method)
		t.Logf("Path: %v", r.URL.Path)

		switch r.URL.Path {
		case "/200":
			w.WriteHeader(http.StatusOK)
			return
		case "/500":
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	})
	defer ts.Close()

	failThreshold := uint64(2)
	successThreshold := uint64(1)
	resetTimeout := 100 * time.Millisecond

	cb := NewCircuitBreakerWithCount(failThreshold, successThreshold, resetTimeout)

	c := dcnl().SetCircuitBreaker(cb)

	for i := uint64(0); i < failThreshold; i++ {
		_, err := c.R().Get(ts.URL + "/500")
		assertNil(t, err)
	}
	resp, err := c.R().Get(ts.URL + "/500")
	assertErrorIs(t, ErrCircuitBreakerOpen, err)
	assertNil(t, resp)
	assertEqual(t, CircuitBreakerStateOpen, c.circuitBreaker.getState(), "expected open state after reaching failure threshold")

	time.Sleep(resetTimeout + 50*time.Millisecond)
	assertEqual(t, CircuitBreakerStateHalfOpen, c.circuitBreaker.getState(), "expected half-open state")

	_, err = c.R().Get(ts.URL + "/500")
	assertError(t, err)
	assertEqual(t, CircuitBreakerStateOpen, c.circuitBreaker.getState(), "expected open state after failure in half-open")

	time.Sleep(resetTimeout + 50*time.Millisecond)
	assertEqual(t, CircuitBreakerStateHalfOpen, c.circuitBreaker.getState(), "expected half-open state")

	for i := uint64(0); i < successThreshold; i++ {
		_, err := c.R().Get(ts.URL + "/200")
		assertNil(t, err)
	}
	assertEqual(t, CircuitBreakerStateClosed, c.circuitBreaker.getState(), "expected closed state after success threshold")

	resp, err = c.R().Get(ts.URL + "/200")
	assertNil(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())

	_, err = c.R().Get(ts.URL + "/500")
	assertError(t, err)
	assertEqual(t, 1, c.circuitBreaker.sw.Get().failures, "expected failure count to be 1 after single failure in closed state")

	time.Sleep(resetTimeout)

	_, err = c.R().Get(ts.URL + "/500")
	assertError(t, err)
	assertEqual(t, 1, c.circuitBreaker.sw.Get().failures, "expected failure count to be 1 after single failure in closed state")
}

func TestCircuitBreaker5xxPolicy(t *testing.T) {
	res1 := CircuitBreaker5xxPolicy(&http.Response{StatusCode: 500})
	assertTrue(t, res1, "expected true for 5xx status code")

	res2 := CircuitBreaker5xxPolicy(&http.Response{StatusCode: 200})
	assertFalse(t, res2, "expected false for non-5xx status code")
}

func TestCircuitBreakerCountBasedOpensAndAllow(t *testing.T) {
	cb := NewCircuitBreakerWithCount(2, 1, 20*time.Millisecond)
	fail := &http.Response{StatusCode: 500}

	// expected allow when state is closed
	err1 := cb.allow()
	assertNil(t, err1)
	assertEqual(t, 0, cb.sw.Get().failures, "expected allow when no failures initially")

	// expected still closed after 1 failure
	cb.applyPolicies(fail)
	err2 := cb.allow()
	assertNil(t, err2)
	assertEqual(t, 1, cb.sw.Get().failures, "expected still closed after 1 failure")

	// expected open after reaching failure threshold
	cb.applyPolicies(fail)
	err3 := cb.allow()
	assertErrorIs(t, ErrCircuitBreakerOpen, err3, "expected open after reaching failure threshold")

	// time.Sleep to half-open state
	time.Sleep(25 * time.Millisecond)
	assertEqual(t, CircuitBreakerStateHalfOpen, cb.getState(), "expected half-open state after reset timeout")

	// expected still half-open after a failure
	cb.applyPolicies(fail)
	assertEqual(t, CircuitBreakerStateOpen, cb.getState(), "expected open state after failure in half-open")

	// expected open state on allow
	err4 := cb.allow()
	assertErrorIs(t, ErrCircuitBreakerOpen, err4, "expected open state on allow after failure in half-open")
}

func TestCircuitBreakerCountBasedHalfOpenToClosedOnSuccess(t *testing.T) {
	cb := NewCircuitBreakerWithCount(1, 1, 30*time.Millisecond)
	fail := &http.Response{StatusCode: 500}
	ok := &http.Response{StatusCode: 200}

	// expected open after failing threshold
	cb.applyPolicies(fail)
	err1 := cb.allow()
	assertErrorIs(t, ErrCircuitBreakerOpen, err1, "expected open after failing threshold")

	// wait for resetTimeout to transition to half-open
	deadline := time.Now().Add(200 * time.Millisecond)
	for time.Now().Before(deadline) {
		if cb.getState() == CircuitBreakerStateHalfOpen {
			break
		}
		time.Sleep(5 * time.Millisecond)
	}
	// expected half-open state after reset timeout
	assertEqual(t, CircuitBreakerStateHalfOpen, cb.getState(), "expected half-open state after reset timeout")

	// on success in half-open, should move to closed
	cb.applyPolicies(ok)
	assertEqual(t, CircuitBreakerStateClosed, cb.getState(), "expected closed state after success in half-open")

	// expected allow when closed
	err := cb.allow()
	assertNil(t, err)
}

func TestCircuitBreakerRatioBasedOpenToClosed(t *testing.T) {
	cb := NewCircuitBreakerWithRatio(0.5, 2, 20*time.Millisecond)
	fail := &http.Response{StatusCode: 500}
	ok := &http.Response{StatusCode: 200}

	// two failures should open (2/2 = 1.0 >= 0.5)
	cb.applyPolicies(fail)
	err1 := cb.allow()
	assertNil(t, err1)
	if err1 == ErrCircuitBreakerOpen {
		t.Errorf("expected still closed after 1 failure (minRequests not met)")
	}

	// expected open after failures exceed ratio threshold
	cb.applyPolicies(fail)
	err2 := cb.allow()
	assertErrorIs(t, ErrCircuitBreakerOpen, err2, "expected open after failures exceed ratio threshold")

	time.Sleep(25 * time.Millisecond)

	// expected half-open state after reset timeout
	assertEqual(t, CircuitBreakerStateHalfOpen, cb.getState(), "expected half-open state after reset timeout")

	// on success in half-open, should move to closed
	cb.applyPolicies(ok)
	assertEqual(t, CircuitBreakerStateClosed, cb.getState(), "expected closed state after success in half-open")
}

func TestCircuitBreakerNewStateAndPolicies(t *testing.T) {
	cb := NewCircuitBreakerWithCount(3, 2, 10*time.Millisecond, CircuitBreaker5xxPolicy)
	assertEqual(t, CircuitBreakerStateClosed, cb.getState())
	assertEqual(t, uint64(3), cb.failureThreshold)
	assertEqual(t, uint64(2), cb.successThreshold)
	assertEqual(t, 10*time.Millisecond, cb.resetTimeout)
	assertEqual(t, 1, len(cb.policies))
}

func TestCircuitBreakerChangeStateClearsCounts(t *testing.T) {
	cb := NewCircuitBreakerWithCount(2, 1, 10*time.Millisecond)
	fail := &http.Response{StatusCode: 500}

	cb.applyPolicies(fail)
	assertEqual(t, 1, cb.sw.Get().failures)

	cb.changeState(CircuitBreakerStateHalfOpen)
	assertEqual(t, CircuitBreakerStateHalfOpen, cb.getState())
	assertEqual(t, 0, cb.sw.Get().failures)
	assertEqual(t, 0, cb.sw.Get().total)
}

func TestCircuitBreakerAllowDuringHalfOpen(t *testing.T) {
	cb := NewCircuitBreakerWithCount(1, 1, 20*time.Millisecond)
	fail := &http.Response{StatusCode: 500}

	cb.applyPolicies(fail) // opens
	assertErrorIs(t, ErrCircuitBreakerOpen, cb.allow(), "expected open state")

	time.Sleep(25 * time.Millisecond) // wait to transition to half-open
	assertEqual(t, CircuitBreakerStateHalfOpen, cb.getState(), "expected half-open state")
	assertNil(t, cb.allow())
}

func TestCircuitBreakerOnTriggerHooks(t *testing.T) {
	cb := NewCircuitBreakerWithCount(1, 1, 10*time.Millisecond)

	called := false
	var gotErr error
	cb.OnTrigger(func(r *Request, e error) {
		called = true
		gotErr = e
	})

	cb.onTriggerHooks(nil, ErrCircuitBreakerOpen)

	assertTrue(t, called, "expected onTrigger hook to be called")
	assertEqual(t, ErrCircuitBreakerOpen, gotErr, "expected error to be passed to onTrigger hook")
}

func TestCircuitBreakerOnStateChangeHooks(t *testing.T) {
	cb := NewCircuitBreakerWithCount(1, 1, 10*time.Millisecond)

	called := false
	var oldState, newState CircuitBreakerState
	cb.OnStateChange(func(o, n CircuitBreakerState) {
		called = true
		oldState = o
		newState = n
	})

	cb.onStateChangeHooks(CircuitBreakerStateClosed, CircuitBreakerStateOpen)

	assertTrue(t, called)
	assertEqual(t, CircuitBreakerStateClosed, oldState, "expected old state to be passed to onStateChange hook")
	assertEqual(t, CircuitBreakerStateOpen, newState, "expected new state to be passed to onStateChange hook")
}

func TestCircuitBreakerMultipleHooksAreCalled(t *testing.T) {
	cb := NewCircuitBreakerWithCount(1, 1, 10*time.Millisecond)

	triggerCount := 0
	cb.OnTrigger(func(_ *Request, _ error) { triggerCount++ })
	cb.OnTrigger(func(_ *Request, _ error) { triggerCount++ })

	cb.onTriggerHooks(nil, ErrCircuitBreakerOpen)
	assertEqual(t, 2, triggerCount, "expected both trigger hooks to be called")

	stateCount := 0
	cb.OnStateChange(func(_, _ CircuitBreakerState) { stateCount++ })
	cb.OnStateChange(func(_, _ CircuitBreakerState) { stateCount++ })

	cb.onStateChangeHooks(CircuitBreakerStateClosed, CircuitBreakerStateHalfOpen)
	assertEqual(t, 2, stateCount, "expected both state change hooks to be called")
}

func TestCircuitBreakerConcurrentOnTriggerRegistration(t *testing.T) {
	cb := NewCircuitBreakerWithCount(1, 1, 10*time.Millisecond)
	var wg sync.WaitGroup
	var cnt int32
	n := 100

	wg.Add(n)
	for i := 0; i < n; i++ {
		go func() {
			cb.OnTrigger(func(_ *Request, _ error) {
				atomic.AddInt32(&cnt, 1)
			})
			wg.Done()
		}()
	}
	wg.Wait()

	cb.onTriggerHooks(nil, ErrCircuitBreakerOpen)
	got := atomic.LoadInt32(&cnt)
	assertEqual(t, int32(n), got, "expected N hooks executed")
}

func TestCircuitBreakerConcurrentOnStateChangeRegistration(t *testing.T) {
	cb := NewCircuitBreakerWithCount(1, 1, 10*time.Millisecond)
	var wg sync.WaitGroup
	var cnt int32
	n := 100

	wg.Add(n)
	for i := 0; i < n; i++ {
		go func() {
			cb.OnStateChange(func(_, _ CircuitBreakerState) {
				atomic.AddInt32(&cnt, 1)
			})
			wg.Done()
		}()
	}
	wg.Wait()

	cb.onStateChangeHooks(CircuitBreakerStateClosed, CircuitBreakerStateOpen)
	got := atomic.LoadInt32(&cnt)
	assertEqual(t, int32(n), got, "expected N state change hooks executed")
}

func TestCircuitBreakerSlidingWindow1SetInterval(t *testing.T) {
	cb := NewCircuitBreakerWithCount(2, 1, 100*time.Millisecond)

	// Verify initial interval
	assertEqual(t, 100*time.Millisecond, cb.sw.interval, "initial interval mismatch")

	// Change interval to a longer duration
	cb.sw.SetInterval(200 * time.Millisecond)

	// Verify interval was changed
	assertEqual(t, 200*time.Millisecond, cb.sw.interval, "interval not updated correctly")
}

func TestCircuitBreakerSlidingWindow2SetInterval(t *testing.T) {
	sw := newSlidingWindow(func() totalAndFailures { return totalAndFailures{} }, 100*time.Millisecond, 5)
	assertEqual(t, 100*time.Millisecond, sw.interval, "initial interval mismatch")

	sw.SetInterval(250 * time.Millisecond)
	assertEqual(t, 250*time.Millisecond, sw.interval, "interval not updated correctly")
}

func TestCircuitBreakerSlidingWindowConcurrentAddGet(t *testing.T) {
	sw := newSlidingWindow(func() totalAndFailures { return totalAndFailures{} }, 200*time.Millisecond, 10)

	var wg sync.WaitGroup
	n := 200
	wg.Add(n)
	for i := 0; i < n; i++ {
		go func() {
			sw.Add(totalAndFailures{total: 1, failures: 0})
			wg.Done()
		}()
	}
	wg.Wait()

	got := sw.Get()
	assertEqual(t, n, got.total, "concurrent adds: expected total count mismatch")
}

func TestCircuitBreakerTotalAndFailuresOperations(t *testing.T) {
	a := totalAndFailures{total: 2, failures: 1}
	b := totalAndFailures{total: 3, failures: 2}

	c := a.op(b)
	assertEqual(t, 5, c.total, "op result incorrect, want total 5")
	assertEqual(t, 3, c.failures, "op result incorrect, want failures 3")

	inv := c.inverse()
	assertEqual(t, -5, inv.total, "inverse result incorrect, want total -5")
	assertEqual(t, -3, inv.failures, "inverse result incorrect, want failures -3")

	empty := c.empty()
	assertEqual(t, 0, empty.total, "empty result incorrect, want total 0")
	assertEqual(t, 0, empty.failures, "empty result incorrect, want failures 0")
}

func TestCircuitBreakerSlidingWindowResetWhenElapsedExceedsBuckets(t *testing.T) {
	interval := 100 * time.Millisecond
	sw := newSlidingWindow(func() totalAndFailures { return totalAndFailures{} }, interval, 4)

	// Pre-populate total and buckets to non-zero values
	sw.values[0] = totalAndFailures{total: 5, failures: 2}
	sw.values[1] = totalAndFailures{total: 3, failures: 1}
	sw.total = sw.values[0].op(sw.values[1]).op(sw.total)

	// Force lastStart far in the past so bucketsToAdvance >= len(values) path is taken
	sw.lastStart = sw.lastStart.Add(-time.Duration(10) * interval)

	// Add a new value; should reset buckets and only this value remains
	sw.Add(totalAndFailures{total: 1, failures: 1})

	got := sw.Get()
	assertEqual(t, 1, got.total, "after reset expected total=1")
	assertEqual(t, 1, got.failures, "after reset expected failures=1")
	assertEqual(t, 0, sw.idx, "expected idx reset to 0")
}


================================================
FILE: client.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"bytes"
	"context"
	"crypto/tls"
	"crypto/x509"
	"errors"
	"io"
	"maps"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"slices"
	"strings"
	"sync"
	"time"
)

const (
	// MethodGet HTTP method
	MethodGet = "GET"

	// MethodPost HTTP method
	MethodPost = "POST"

	// MethodPut HTTP method
	MethodPut = "PUT"

	// MethodDelete HTTP method
	MethodDelete = "DELETE"

	// MethodPatch HTTP method
	MethodPatch = "PATCH"

	// MethodHead HTTP method
	MethodHead = "HEAD"

	// MethodOptions HTTP method
	MethodOptions = "OPTIONS"

	// MethodTrace HTTP method
	MethodTrace = "TRACE"
)

const (
	defaultWatcherPoolingInterval = 24 * time.Hour
)

var (
	ErrNotHttpTransportType       = errors.New("resty: not a http.Transport type")
	ErrUnsupportedRequestBodyKind = errors.New("resty: unsupported request body kind")

	hdrUserAgentKey       = http.CanonicalHeaderKey("User-Agent")
	hdrAcceptKey          = http.CanonicalHeaderKey("Accept")
	hdrAcceptEncodingKey  = http.CanonicalHeaderKey("Accept-Encoding")
	hdrContentTypeKey     = http.CanonicalHeaderKey("Content-Type")
	hdrContentLengthKey   = http.CanonicalHeaderKey("Content-Length")
	hdrContentEncodingKey = http.CanonicalHeaderKey("Content-Encoding")
	hdrContentDisposition = http.CanonicalHeaderKey("Content-Disposition")
	hdrAuthorizationKey   = http.CanonicalHeaderKey("Authorization")
	hdrWwwAuthenticateKey = http.CanonicalHeaderKey("WWW-Authenticate")
	hdrRetryAfterKey      = http.CanonicalHeaderKey("Retry-After")
	hdrCookieKey          = http.CanonicalHeaderKey("Cookie")

	plainTextType   = "text/plain; charset=utf-8"
	jsonContentType = "application/json"
	formContentType = "application/x-www-form-urlencoded"

	jsonKey = "json"
	xmlKey  = "xml"

	defaultAuthScheme = "Bearer"

	hdrUserAgentValue = "go-resty/" + Version + " (https://resty.dev)"
	bufPool           = &sync.Pool{New: func() any { return &bytes.Buffer{} }}
)

type (
	// RequestMiddleware type is for request middleware, called before a request is sent
	RequestMiddleware func(*Client, *Request) error

	// ResponseMiddleware type is for response middleware, called after a response has been received
	ResponseMiddleware func(*Client, *Response) error

	// ErrorHook type is for reacting to request errors, called after all retries were attempted
	ErrorHook func(*Request, error)

	// SuccessHook type is for reacting to request success
	SuccessHook func(*Client, *Response)

	// CloseHook type is for reacting to client closing
	CloseHook func()

	// RequestFunc type is for extended manipulation of the Request instance
	RequestFunc func(*Request) *Request

	// TLSClientConfiger interface is to configure TLS Client configuration on custom transport
	// implemented using [http.RoundTripper]
	TLSClientConfiger interface {
		TLSClientConfig() *tls.Config
		SetTLSClientConfig(*tls.Config) error
	}
)

// TransportSettings struct is used to define custom dialer and transport
// values for the Resty client. Please refer to individual
// struct fields to know the default values.
//
// Also, refer to https://pkg.go.dev/net/http#Transport for more details.
type TransportSettings struct {
	// DialerTimeout, default value is `30` seconds.
	DialerTimeout time.Duration

	// DialerKeepAlive, default value is `30` seconds.
	DialerKeepAlive time.Duration

	// IdleConnTimeout, default value is `90` seconds.
	IdleConnTimeout time.Duration

	// TLSHandshakeTimeout, default value is `10` seconds.
	TLSHandshakeTimeout time.Duration

	// ExpectContinueTimeout, default value is `1` seconds.
	ExpectContinueTimeout time.Duration

	// ResponseHeaderTimeout, added to provide ability to
	// set value. No default value in Resty, the Go
	// HTTP client default value applies.
	ResponseHeaderTimeout time.Duration

	// MaxIdleConns, default value is `100`.
	MaxIdleConns int

	// MaxIdleConnsPerHost, default value is `runtime.GOMAXPROCS(0) + 1`.
	MaxIdleConnsPerHost int

	// MaxConnsPerHost, default value is no limit.
	MaxConnsPerHost int

	// DisableKeepAlives, default value is `false`.
	DisableKeepAlives bool

	// MaxResponseHeaderBytes, added to provide ability to
	// set value. No default value in Resty, the Go
	// HTTP client default value applies.
	MaxResponseHeaderBytes int64

	// WriteBufferSize, added to provide ability to
	// set value. No default value in Resty, the Go
	// HTTP client default value applies.
	WriteBufferSize int

	// ReadBufferSize, added to provide ability to
	// set value. No default value in Resty, the Go
	// HTTP client default value applies.
	ReadBufferSize int
}

// Client struct is used to create a Resty client with client-level settings,
// these settings apply to all the requests raised from the client.
//
// Resty also provides an option to override most of the client settings
// at [Request] level.
type Client struct {
	lock                       *sync.RWMutex
	baseURL                    string
	queryParams                url.Values
	formData                   url.Values
	pathParams                 map[string]string
	header                     http.Header
	credentials                *credentials
	authToken                  string
	authScheme                 string
	cookies                    []*http.Cookie
	errorType                  reflect.Type
	debug                      bool
	disableWarn                bool
	isMethodGetAllowPayload    bool
	isMethodDeleteAllowPayload bool
	timeout                    time.Duration
	retryCount                 int
	retryWaitTime              time.Duration
	retryMaxWaitTime           time.Duration
	retryConditions            []RetryConditionFunc
	retryHooks                 []RetryHookFunc
	retryDelayStrategy         RetryDelayStrategyFunc
	isRetryDefaultConditions   bool
	isRetryAllowNonIdempotent  bool
	headerAuthorizationKey     string
	responseBodyLimit          int64
	resBodyUnlimitedReads      bool
	jsonEscapeHTML             bool
	closeConnection            bool
	isResponseDoNotParse       bool
	isTrace                    bool
	debugBodyLimit             int
	responseSaveDirectory      string
	isResponseSaveToFile       bool
	scheme                     string
	log                        Logger
	ctx                        context.Context
	httpClient                 *http.Client
	proxyURL                   *url.URL
	debugLogFormatter          DebugLogFormatterFunc
	debugLogCallback           DebugLogCallbackFunc
	isCurlCmdGenerate          bool
	isCurlCmdDebugLog          bool
	unescapeQueryParams        bool
	loadBalancer               LoadBalancer
	beforeRequest              []RequestMiddleware
	afterResponse              []ResponseMiddleware
	errorHooks                 []ErrorHook
	invalidHooks               []ErrorHook
	panicHooks                 []ErrorHook
	successHooks               []SuccessHook
	closeHooks                 []CloseHook
	contentTypeEncoders        map[string]ContentTypeEncoder
	contentTypeDecoders        map[string]ContentTypeDecoder
	contentDecompresserKeys    []string
	contentDecompressers       map[string]ContentDecompresser
	certWatcherStopChan        chan bool
	circuitBreaker             *CircuitBreaker
	hedging                    *Hedging
}

// CertWatcherOptions allows configuring a watcher that reloads dynamically TLS certs.
type CertWatcherOptions struct {
	// PoolInterval is the frequency at which resty will check if the PEM file needs to be reloaded.
	// Default is 24 hours.
	PoolInterval time.Duration
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Client methods
//___________________________________

// BaseURL method returns the Base URL value from the client instance.
func (c *Client) BaseURL() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.baseURL
}

// SetBaseURL method sets the Base URL in the client instance. It will be used with a request
// raised from this client with a relative URL
//
//	// Setting HTTP address
//	client.SetBaseURL("http://myjeeva.com")
//
//	// Setting HTTPS address
//	client.SetBaseURL("https://myjeeva.com")
func (c *Client) SetBaseURL(url string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.baseURL = strings.TrimRight(url, "/")
	return c
}

// LoadBalancer method returns the request load balancer instance from the client
// instance. Otherwise returns nil.
func (c *Client) LoadBalancer() LoadBalancer {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.loadBalancer
}

// SetLoadBalancer method is used to set the new request load balancer into the client.
func (c *Client) SetLoadBalancer(b LoadBalancer) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.loadBalancer = b
	return c
}

// Header method returns the headers from the client instance.
func (c *Client) Header() http.Header {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.header
}

// SetHeader method sets a single header and its value in the client instance.
// These headers will be applied to all requests raised from the client instance.
// Also, it can be overridden by request-level header options.
//
// For Example: To set `Content-Type` and `Accept` as `application/json`
//
//	client.
//		SetHeader("Content-Type", "application/json").
//		SetHeader("Accept", "application/json")
//
// See [Request.SetHeader] or [Request.SetHeaders].
func (c *Client) SetHeader(header, value string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.header.Set(header, value)
	return c
}

// SetHeaderAny method sets a single header field and its value in the client instance
// for all requests raised from the client.
//
// It is similar to [Client.SetHeader] but accepts any type as the value and converts
// it to a string using predefined formatting rules (integers, bools, time.Time, etc.).
//
// For Example: To set `X-Request-Id` with an integer value
//
//	client.SetHeaderAny("X-Request-Id", 12345)
//
// See [Request.SetHeaderAny] or [Client.SetHeader].
func (c *Client) SetHeaderAny(header string, value any) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	strVal := formatAnyToString(value)
	c.header.Set(header, strVal)
	return c
}

// SetHeaders method sets multiple headers and their values at one go, and
// these headers will be applied to all requests raised from the client instance.
// Also, it can be overridden at request-level headers options.
//
// For Example: To set `Content-Type` and `Accept` as `application/json`
//
//	client.SetHeaders(map[string]string{
//		"Content-Type": "application/json",
//		"Accept": "application/json",
//	})
//
// See [Request.SetHeaders] or [Request.SetHeader].
func (c *Client) SetHeaders(headers map[string]string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	for h, v := range headers {
		c.header.Set(h, v)
	}
	return c
}

// SetHeaderVerbatim method is used to set the HTTP header key and value verbatim in the current request.
// It is typically helpful for legacy applications or servers that require HTTP headers in a certain way
//
// For Example: To set header key as `all_lowercase`, `UPPERCASE`, and `x-cloud-trace-id`
//
//	client.
//		SetHeaderVerbatim("all_lowercase", "available").
//		SetHeaderVerbatim("UPPERCASE", "available").
//		SetHeaderVerbatim("x-cloud-trace-id", "798e94019e5fc4d57fbb8901eb4c6cae")
//
// See [Request.SetHeaderVerbatim].
func (c *Client) SetHeaderVerbatim(header, value string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.header[header] = []string{value}
	return c
}

// SetHeaderVerbatimAny method sets the HTTP header key and value verbatim in the client instance
// for all requests raised from the client.
//
// It is similar to [Client.SetHeaderVerbatim] but accepts any type as the value and converts
// it to a string using predefined formatting rules (integers, bools, time.Time, etc.).
//
// For Example: To set header key as `x-trace-id` with an integer value
//
//	client.SetHeaderVerbatimAny("x-trace-id", 798940)
//
// See [Request.SetHeaderVerbatimAny] or [Client.SetHeaderVerbatim].
func (c *Client) SetHeaderVerbatimAny(header string, value any) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	strVal := formatAnyToString(value)
	c.header[header] = []string{strVal}
	return c
}

// Context method returns the [context.Context] from the client instance.
func (c *Client) Context() context.Context {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.ctx
}

// SetContext method sets the given [context.Context] in the client instance and
// it gets added to [Request] raised from this instance.
func (c *Client) SetContext(ctx context.Context) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.ctx = ctx
	return c
}

// CookieJar method returns the HTTP cookie jar instance from the underlying Go HTTP Client.
func (c *Client) CookieJar() http.CookieJar {
	return c.Client().Jar
}

// SetCookieJar method sets custom [http.CookieJar] in the resty client. It's a way to override the default.
//
// For Example, sometimes we don't want to save cookies in API mode so that we can remove the default
// CookieJar in resty client.
//
//	client.SetCookieJar(nil)
func (c *Client) SetCookieJar(jar http.CookieJar) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.httpClient.Jar = jar
	return c
}

// Cookies method returns all cookies registered in the client instance.
func (c *Client) Cookies() []*http.Cookie {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.cookies
}

// SetCookie method appends a single cookie to the client instance.
// These cookies will be added to all the requests from this client instance.
//
//	client.SetCookie(&http.Cookie{
//		Name:"go-resty",
//		Value:"This is cookie value",
//	})
func (c *Client) SetCookie(hc *http.Cookie) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.cookies = append(c.cookies, hc)
	return c
}

// SetCookies method sets an array of cookies in the client instance.
// These cookies will be added to all the requests from this client instance.
//
//	cookies := []*http.Cookie{
//		&http.Cookie{
//			Name:"go-resty-1",
//			Value:"This is cookie 1 value",
//		},
//		&http.Cookie{
//			Name:"go-resty-2",
//			Value:"This is cookie 2 value",
//		},
//	}
//
//	// Setting a cookies into resty
//	client.SetCookies(cookies)
func (c *Client) SetCookies(cs []*http.Cookie) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.cookies = append(c.cookies, cs...)
	return c
}

// QueryParams method returns all query parameters and their values from the client instance.
func (c *Client) QueryParams() url.Values {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.queryParams
}

// SetQueryParam method sets a single parameter and its value in the client instance.
// It will be formed as a query string for the request.
//
//	For Example: `search=kitchen%20papers&size=large`
//
// In the URL after the `?` mark. These query params will be added to all the requests raised from
// this client instance. Also, it can be overridden at the request level.
//
// See [Request.SetQueryParam] or [Request.SetQueryParams].
//
//	client.
//		SetQueryParam("search", "kitchen papers").
//		SetQueryParam("size", "large")
func (c *Client) SetQueryParam(param, value string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.queryParams.Set(param, value)
	return c
}

// SetQueryParamAny method sets a single query parameter and its value in the client instance.
// It will be formed as a query string for the request.
//
// It is similar to [Client.SetQueryParam] but accepts any type as the value and converts
// it to a string using predefined formatting rules (integers, bools, time.Time, etc.).
//
// For Example: To set `page` and `active` query parameters
//
//	client.
//		SetQueryParamAny("page", 5).
//		SetQueryParamAny("active", true)
//
// See [Request.SetQueryParamAny] or [Client.SetQueryParam].
func (c *Client) SetQueryParamAny(param string, value any) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	strVal := formatAnyToString(value)
	c.queryParams.Set(param, strVal)
	return c
}

// SetQueryParams method sets multiple parameters and their values at one go in the client instance.
// It will be formed as a query string for the request.
//
//	For Example: `search=kitchen%20papers&size=large`
//
// In the URL after the `?` mark. These query params will be added to all the requests raised from this
// client instance. Also, it can be overridden at the request level.
//
// See [Request.SetQueryParams] or [Request.SetQueryParam].
//
//	client.SetQueryParams(map[string]string{
//		"search": "kitchen papers",
//		"size": "large",
//	})
func (c *Client) SetQueryParams(params map[string]string) *Client {
	// Do not lock here since there is potential deadlock.
	for p, v := range params {
		c.SetQueryParam(p, v)
	}
	return c
}

// FormData method returns the form parameters and their values from the client instance.
func (c *Client) FormData() url.Values {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.formData
}

// SetFormData method sets Form parameters and their values in the client instance.
// The request content type would be set as `application/x-www-form-urlencoded`.
// The client-level form data gets added to all the requests. Also, it can be
// overridden at the request level.
//
// See [Request.SetFormData].
//
//	client.SetFormData(map[string]string{
//		"access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
//		"user_id": "3455454545",
//	})
func (c *Client) SetFormData(data map[string]string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	for k, v := range data {
		c.formData.Set(k, v)
	}
	return c
}

// SetBasicAuth method sets the basic authentication header in the HTTP request. For Example:
//
//	Authorization: Basic <base64-encoded-value>
//
// For Example: To set the header for username "go-resty" and password "welcome"
//
//	client.SetBasicAuth("go-resty", "welcome")
//
// This basic auth information is added to all requests from this client instance.
// It can also be overridden at the request level.
//
// See [Request.SetBasicAuth].
func (c *Client) SetBasicAuth(username, password string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.credentials = &credentials{Username: username, Password: password}
	return c
}

// AuthToken method returns the auth token value registered in the client instance.
func (c *Client) AuthToken() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.authToken
}

// HeaderAuthorizationKey method returns the HTTP header name for Authorization from the client instance.
func (c *Client) HeaderAuthorizationKey() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.headerAuthorizationKey
}

// SetHeaderAuthorizationKey method sets the given HTTP header name for Authorization in the client instance.
//
// It can be overridden at the request level; see [Request.SetHeaderAuthorizationKey].
//
//	client.SetHeaderAuthorizationKey("X-Custom-Authorization")
func (c *Client) SetHeaderAuthorizationKey(k string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.headerAuthorizationKey = k
	return c
}

// SetAuthToken method sets the auth token of the `Authorization` header for all HTTP requests.
// The default auth scheme is `Bearer`; it can be customized with the method [Client.SetAuthScheme]. For Example:
//
//	Authorization: <auth-scheme> <auth-token-value>
//
// For Example: To set auth token BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F
//
//	client.SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")
//
// This auth token gets added to all the requests raised from this client instance.
// Also, it can be overridden at the request level.
//
// See [Request.SetAuthToken].
func (c *Client) SetAuthToken(token string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.authToken = token
	return c
}

// AuthScheme method returns the auth scheme name set in the client instance.
//
// See [Client.SetAuthScheme], [Request.SetAuthScheme].
func (c *Client) AuthScheme() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.authScheme
}

// SetAuthScheme method sets the auth scheme type in the HTTP request. For Example:
//
//	Authorization: <auth-scheme-value> <auth-token-value>
//
// For Example: To set the scheme to use OAuth
//
//	client.SetAuthScheme("OAuth")
//
// This auth scheme gets added to all the requests raised from this client instance.
// Also, it can be overridden at the request level.
//
// Information about auth schemes can be found in [RFC 7235], IANA [HTTP Auth schemes].
//
// See [Request.SetAuthScheme].
//
// [RFC 7235]: https://tools.ietf.org/html/rfc7235
// [HTTP Auth schemes]: https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml#authschemes
func (c *Client) SetAuthScheme(scheme string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.authScheme = scheme
	return c
}

// SetDigestAuth method sets the Digest Auth transport with provided credentials in the client.
// If a server responds with 401 and sends a Digest challenge in the header `WWW-Authenticate`,
// the request will be resent with the appropriate digest `Authorization` header.
//
// For Example: To set the Digest scheme with user "Mufasa" and password "Circle Of Life"
//
//	client.SetDigestAuth("Mufasa", "Circle Of Life")
//
// Information about Digest Access Authentication can be found in [RFC 7616].
//
// NOTE:
//   - On the QOP `auth-int` scenario, the request body is read into memory to
//     compute the body hash that increases memory usage.
//   - Create a dedicated client instance to use digest auth,
//     as it does digest auth for all the requests raised by the client.
//
// [RFC 7616]: https://datatracker.ietf.org/doc/html/rfc7616
func (c *Client) SetDigestAuth(username, password string) *Client {
	dt := &digestTransport{
		credentials: &credentials{username, password},
		transport:   c.Transport(),
	}
	c.SetTransport(dt)
	return c
}

// R method creates a new request instance; it's used for Get, Post, Put, Delete, Patch, Head, Options, etc.
func (c *Client) R() *Request {
	c.lock.RLock()
	defer c.lock.RUnlock()
	r := &Request{
		QueryParams:                  url.Values{},
		FormData:                     url.Values{},
		Header:                       http.Header{},
		Cookies:                      make([]*http.Cookie, 0),
		PathParams:                   make(map[string]string),
		Timeout:                      c.timeout,
		IsDebug:                      c.debug,
		IsTrace:                      c.isTrace,
		IsResponseSaveToFile:         c.isResponseSaveToFile,
		AuthScheme:                   c.authScheme,
		AuthToken:                    c.authToken,
		RetryCount:                   c.retryCount,
		RetryWaitTime:                c.retryWaitTime,
		RetryMaxWaitTime:             c.retryMaxWaitTime,
		RetryDelayStrategy:           c.retryDelayStrategy,
		IsRetryDefaultConditions:     c.isRetryDefaultConditions,
		IsCloseConnection:            c.closeConnection,
		IsResponseDoNotParse:         c.isResponseDoNotParse,
		DebugBodyLimit:               c.debugBodyLimit,
		ResponseBodyLimit:            c.responseBodyLimit,
		IsResponseBodyUnlimitedReads: c.resBodyUnlimitedReads,
		IsMethodGetAllowPayload:      c.isMethodGetAllowPayload,
		IsMethodDeleteAllowPayload:   c.isMethodDeleteAllowPayload,
		IsRetryAllowNonIdempotent:    c.isRetryAllowNonIdempotent,
		HeaderAuthorizationKey:       c.headerAuthorizationKey,

		mu:                  new(sync.Mutex),
		client:              c,
		baseURL:             c.baseURL,
		multipartFields:     make([]*MultipartField, 0),
		jsonEscapeHTML:      c.jsonEscapeHTML,
		log:                 c.log,
		isCurlCmdGenerate:   c.isCurlCmdGenerate,
		isCurlCmdDebugLog:   c.isCurlCmdDebugLog,
		unescapeQueryParams: c.unescapeQueryParams,
		credentials:         c.credentials,
	}

	if c.ctx != nil {
		r.ctx = context.WithoutCancel(c.ctx) // refer to godoc for more info about this function
	}

	return r
}

// NewRequest method is an alias for method `R()`.
func (c *Client) NewRequest() *Request {
	return c.R()
}

// SetRequestMiddlewares method allows Resty users to override the default request
// middlewares sequence
//
//	client.SetRequestMiddlewares(
//		Custom1RequestMiddleware,
//		Custom2RequestMiddleware,
//		resty.PrepareRequestMiddleware, // after this, `Request.RawRequest` instance is available
//		Custom3RequestMiddleware,
//		Custom4RequestMiddleware,
//	)
//
// See, [Client.AddRequestMiddleware]
//
// NOTE:
//   - It overwrites the existing request middleware list.
//   - Be sure to include Resty request middlewares in the request chain at the appropriate spot.
func (c *Client) SetRequestMiddlewares(middlewares ...RequestMiddleware) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.beforeRequest = middlewares
	return c
}

// SetResponseMiddlewares method allows Resty users to override the default response
// middlewares sequence
//
//	client.SetResponseMiddlewares(
//		Custom1ResponseMiddleware,
//		Custom2ResponseMiddleware,
//		resty.AutoParseResponseMiddleware, // before this, the body is not read except on the debug flow
//		Custom3ResponseMiddleware,
//		resty.SaveToFileResponseMiddleware, // See, Request.SetOutputFileName, Request.SetSaveResponse
//		Custom4ResponseMiddleware,
//		Custom5ResponseMiddleware,
//	)
//
// See, [Client.AddResponseMiddleware]
//
// NOTE:
//   - It overwrites the existing response middleware list.
//   - Be sure to include Resty response middlewares in the response chain at the appropriate spot.
func (c *Client) SetResponseMiddlewares(middlewares ...ResponseMiddleware) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.afterResponse = middlewares
	return c
}

func (c *Client) requestMiddlewares() []RequestMiddleware {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.beforeRequest
}

// AddRequestMiddleware method appends a request middleware to the before request chain.
// After all requests, middlewares are applied, and the request is sent to the host server.
//
//	client.AddRequestMiddleware(func(c *resty.Client, r *resty.Request) error {
//		// Now you have access to the Client and Request instance
//		// manipulate it as per your need
//
//		return nil 	// if its successful otherwise return error
//	})
func (c *Client) AddRequestMiddleware(m RequestMiddleware) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	idx := len(c.beforeRequest) - 1
	c.beforeRequest = slices.Insert(c.beforeRequest, idx, m)
	return c
}

func (c *Client) responseMiddlewares() []ResponseMiddleware {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.afterResponse
}

// AddResponseMiddleware method appends response middleware to the after-response chain.
// All the response middlewares are applied; once we receive a response
// from the host server.
//
//	client.AddResponseMiddleware(func(c *resty.Client, r *resty.Response) error {
//		// Now you have access to the Client and Response instance
//		// Also, you could access request via Response.Request i.e., r.Request
//		// manipulate it as per your need
//
//		return nil 	// if its successful otherwise return error
//	})
func (c *Client) AddResponseMiddleware(m ResponseMiddleware) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.afterResponse = append(c.afterResponse, m)
	return c
}

// OnError method adds a callback that will be run whenever a request execution fails.
// This is called after all retries have been attempted (if any).
// If there was a response from the server, the error will be wrapped in [ResponseError]
// which has the last response received from the server.
//
//	client.OnError(func(req *resty.Request, err error) {
//		if v, ok := err.(*resty.ResponseError); ok {
//			// Do something with v.Response
//		}
//		// Log the error, increment a metric, etc...
//	})
//
// Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
// callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
//
// NOTE:
//   - Do not use [Client] setter methods within OnError hooks; deadlock will happen.
func (c *Client) OnError(hooks ...ErrorHook) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.errorHooks = append(c.errorHooks, hooks...)
	return c
}

// OnSuccess method adds a callback that will be run whenever a request execution
// succeeds.  This is called after all retries have been attempted (if any).
//
// Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
// callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
//
// NOTE:
//   - Do not use [Client] setter methods within OnSuccess hooks; deadlock will happen.
func (c *Client) OnSuccess(hooks ...SuccessHook) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.successHooks = append(c.successHooks, hooks...)
	return c
}

// OnInvalid method adds a callback that will be run whenever a request execution
// fails before it starts because the request is invalid.
//
// Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
// callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
//
// NOTE:
//   - Do not use [Client] setter methods within OnInvalid hooks; deadlock will happen.
func (c *Client) OnInvalid(hooks ...ErrorHook) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.invalidHooks = append(c.invalidHooks, hooks...)
	return c
}

// OnPanic method adds a callback that will be run whenever a request execution
// panics.
//
// Out of the [Client.OnSuccess], [Client.OnError], [Client.OnInvalid], [Client.OnPanic]
// callbacks, exactly one set will be invoked for each call to [Request.Execute] that completes.
//
// If an [Client.OnSuccess], [Client.OnError], or [Client.OnInvalid] callback panics,
// then exactly one rule can be violated.
//
// NOTE:
//   - Do not use [Client] setter methods within OnPanic hooks; deadlock will happen.
func (c *Client) OnPanic(hooks ...ErrorHook) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.panicHooks = append(c.panicHooks, hooks...)
	return c
}

// OnClose method adds a callback that will be run whenever the client is closed.
// The hooks are executed in the order they were registered.
func (c *Client) OnClose(hooks ...CloseHook) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.closeHooks = append(c.closeHooks, hooks...)
	return c
}

// ContentTypeEncoders method returns all the registered content type encoders.
func (c *Client) ContentTypeEncoders() map[string]ContentTypeEncoder {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.contentTypeEncoders
}

// AddContentTypeEncoder method adds the user-provided Content-Type encoder into a client.
//
// NOTE: It overwrites the encoder function if the given Content-Type key already exists.
func (c *Client) AddContentTypeEncoder(ct string, e ContentTypeEncoder) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.contentTypeEncoders[strings.ToLower(ct)] = e
	return c
}

func (c *Client) inferContentTypeEncoder(ct ...string) (ContentTypeEncoder, bool) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	for _, v := range ct {
		if d, f := c.contentTypeEncoders[v]; f {
			return d, f
		}
	}
	return nil, false
}

// ContentTypeDecoders method returns all the registered content type decoders.
func (c *Client) ContentTypeDecoders() map[string]ContentTypeDecoder {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.contentTypeDecoders
}

// AddContentTypeDecoder method adds the user-provided Content-Type decoder into a client.
//
// NOTE: It overwrites the decoder function if the given Content-Type key already exists.
func (c *Client) AddContentTypeDecoder(ct string, d ContentTypeDecoder) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.contentTypeDecoders[strings.ToLower(ct)] = d
	return c
}

func (c *Client) inferContentTypeDecoder(ct ...string) (ContentTypeDecoder, bool) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	for _, v := range ct {
		if d, f := c.contentTypeDecoders[v]; f {
			return d, f
		}
	}
	return nil, false
}

// ContentDecompressers method returns all the registered content-encoding Decompressers.
func (c *Client) ContentDecompressers() map[string]ContentDecompresser {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.contentDecompressers
}

// AddContentDecompresser method adds the user-provided Content-Encoding ([RFC 9110]) Decompresser
// and directive into a client.
//
// NOTE: It overwrites the Decompresser function if the given Content-Encoding directive already exists.
//
// [RFC 9110]: https://datatracker.ietf.org/doc/html/rfc9110
func (c *Client) AddContentDecompresser(k string, d ContentDecompresser) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	lk := strings.ToLower(k)
	if !slices.Contains(c.contentDecompresserKeys, lk) {
		c.contentDecompresserKeys = slices.Insert(c.contentDecompresserKeys, 0, lk)
	}
	c.contentDecompressers[lk] = d
	return c
}

// ContentDecompresserKeys method returns all the registered content-encoding Decompressers
// keys as comma-separated string.
func (c *Client) ContentDecompresserKeys() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return strings.Join(c.contentDecompresserKeys, ", ")
}

// SetContentDecompresserKeys method sets given Content-Encoding ([RFC 9110]) directives into the client instance.
//
// It checks the given Content-Encoding exists in the [ContentDecompresser] list before assigning it,
// if it does not exist, it will skip that directive.
//
// Use this method to overwrite the default order. If a new content Decompresser is added,
// that directive will be the first.
//
// [RFC 9110]: https://datatracker.ietf.org/doc/html/rfc9110
func (c *Client) SetContentDecompresserKeys(keys []string) *Client {
	result := make([]string, 0)
	decoders := c.ContentDecompressers()
	for _, k := range keys {
		k = strings.ToLower(k)
		if _, f := decoders[k]; f {
			result = append(result, k)
		}
	}

	c.lock.Lock()
	defer c.lock.Unlock()
	c.contentDecompresserKeys = result
	return c
}

// SetCircuitBreaker method sets the Circuit Breaker instance into the client.
// It is used to prevent the client from sending requests that are likely to fail.
// For Example: To use the default Circuit Breaker:
//
//	client.SetCircuitBreaker(NewCircuitBreaker())
func (c *Client) SetCircuitBreaker(b *CircuitBreaker) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.circuitBreaker = b
	return c
}

// IsDebug method returns `true` if the client is in debug mode; otherwise, it is `false`.
func (c *Client) IsDebug() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.debug
}

// SetDebug method is used to turn on/off the debug mode on the Resty client instance. It logs details
// of every request and response when enabled.
//
//	client.SetDebug(true)
//
// Also, it can be enabled at the request level for a particular request; see [Request.SetDebug].
//   - For [Request], it logs information such as HTTP verb, Relative URL path,
//     Host, Headers, and Body if it has one.
//   - For [Response], it logs information such as Status, Response Time, Headers,
//     and Body if it has one.
func (c *Client) SetDebug(d bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.debug = d
	return c
}

// DebugBodyLimit method returns the debug body limit value set on the client instance
func (c *Client) DebugBodyLimit() int {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.debugBodyLimit
}

// SetDebugBodyLimit sets the maximum size in bytes for which the response and
// request body will be logged in debug mode.
//
//	client.SetDebugBodyLimit(1000000)
func (c *Client) SetDebugBodyLimit(sl int) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.debugBodyLimit = sl
	return c
}

func (c *Client) debugLogCallbackFunc() DebugLogCallbackFunc {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.debugLogCallback
}

// OnDebugLog method sets the debug log callback function to the client instance.
// Registered callback gets called before the Resty logs the information.
func (c *Client) OnDebugLog(dlc DebugLogCallbackFunc) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	if c.debugLogCallback != nil {
		c.log.Warnf("Overwriting an existing on-debug-log callback from=%s to=%s",
			functionName(c.debugLogCallback), functionName(dlc))
	}
	c.debugLogCallback = dlc
	return c
}

func (c *Client) debugLogFormatterFunc() DebugLogFormatterFunc {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.debugLogFormatter
}

// SetDebugLogFormatter method sets the Resty debug log formatter to the client instance.
func (c *Client) SetDebugLogFormatter(df DebugLogFormatterFunc) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.debugLogFormatter = df
	return c
}

// IsDisableWarn method returns `true` if the warning message is disabled; otherwise, it is `false`.
func (c *Client) IsDisableWarn() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.disableWarn
}

// SetLoggerWarnLevel method disables the warning log message on the Resty client.
//
// For example, Resty warns users when BasicAuth is used in non-TLS mode.
//
//	client.SetLoggerWarnLevel(true)
func (c *Client) SetLoggerWarnLevel(d bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.disableWarn = d
	return c
}

// IsMethodGetAllowPayload method returns `true` if the client is enabled to allow
// payload with GET method; otherwise, it is `false`.
func (c *Client) IsMethodGetAllowPayload() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.isMethodGetAllowPayload
}

// SetMethodGetAllowPayload method allows the GET method with payload on the Resty client.
// By default, Resty does not allow.
//
//	client.SetMethodGetAllowPayload(true)
//
// It can be overridden at the request level. See [Request.SetMethodGetAllowPayload]
func (c *Client) SetMethodGetAllowPayload(allow bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isMethodGetAllowPayload = allow
	return c
}

// IsMethodDeleteAllowPayload method returns `true` if the client is enabled to allow
// payload with DELETE method; otherwise, it is `false`.
//
// More info, refer to GH#881
func (c *Client) IsMethodDeleteAllowPayload() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.isMethodDeleteAllowPayload
}

// SetMethodDeleteAllowPayload method allows the DELETE method with payload on the Resty client.
// By default, Resty does not allow.
//
//	client.SetMethodDeleteAllowPayload(true)
//
// More info, refer to GH#881
//
// It can be overridden at the request level. See [Request.SetMethodDeleteAllowPayload]
func (c *Client) SetMethodDeleteAllowPayload(allow bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isMethodDeleteAllowPayload = allow
	return c
}

// Logger method returns the logger instance used by the client instance.
func (c *Client) Logger() Logger {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.log
}

// SetLogger method sets given writer for logging Resty request and response details.
//
// Compliant to interface [resty.Logger]
func (c *Client) SetLogger(l Logger) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.log = l
	return c
}

// Timeout method returns the timeout duration value from the client
func (c *Client) Timeout() time.Duration {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.timeout
}

// SetTimeout method is used to set a timeout for a request raised by the client.
//
//	client.SetTimeout(1 * time.Minute)
//
// It can be overridden at the request level. See [Request.SetTimeout]
//
// NOTE: Resty uses [context.WithTimeout] on the request, it does not use [http.Client].Timeout
func (c *Client) SetTimeout(timeout time.Duration) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.timeout = timeout
	return c
}

// ResultError method returns the global or client common `ResultError` object
// type registered in the client instance.
func (c *Client) ResultError() reflect.Type {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.errorType
}

// SetResultError method registers the global or client common `ResultError`
// object type into the client instance. It is used for automatic unmarshalling if
// the response status code is greater than 399 and the content type is JSON or XML.
// It can be a pointer or a non-pointer.
//
//	client.SetResultError(&LoginErrorResponse{})
//	// OR
//	client.SetResultError(LoginErrorResponse{})
func (c *Client) SetResultError(v any) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.errorType = inferType(v)
	return c
}

func (c *Client) newErrorInterface() any {
	e := c.ResultError()
	if e == nil {
		return e
	}
	return reflect.New(e).Interface()
}

// SetRedirectPolicy method sets the redirect policy for the client. Resty provides ready-to-use
// redirect policies. Wanna create one for yourself, refer to `redirect.go`.
//
//	client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20))
//
//	// Need multiple redirect policies together
//	client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20), resty.DomainCheckRedirectPolicy("host1.com", "host2.net"))
//
// NOTE: It overwrites the previous redirect policies in the client instance.
func (c *Client) SetRedirectPolicy(policies ...RedirectPolicy) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		for _, p := range policies {
			if err := p.Apply(req, via); err != nil {
				return err
			}
		}
		return nil // looks good, go ahead
	}
	return c
}

// RetryCount method returns the retry count value from the client instance.
func (c *Client) RetryCount() int {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.retryCount
}

// SetRetryCount method enables retry on Resty client and allows you
// to set no. of retry count.
//
//	first attempt + retry count = total attempts
//
// See [Request.SetRetryDelayStrategy]
//
// NOTE:
//   - By default, Resty only does retry on idempotent HTTP verb, [RFC 9110 Section 9.2.2], [RFC 9110 Section 18.2]
//
// [RFC 9110 Section 9.2.2]: https://datatracker.ietf.org/doc/html/rfc9110.html#name-idempotent-methods
// [RFC 9110 Section 18.2]: https://datatracker.ietf.org/doc/html/rfc9110.html#name-method-registration
func (c *Client) SetRetryCount(count int) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.retryCount = count
	return c
}

// RetryWaitTime method returns the retry wait time that is used to sleep before
// retrying the request.
func (c *Client) RetryWaitTime() time.Duration {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.retryWaitTime
}

// SetRetryWaitTime method sets the default wait time for sleep before retrying
//
// Default is 100 milliseconds.
func (c *Client) SetRetryWaitTime(waitTime time.Duration) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.retryWaitTime = waitTime
	return c
}

// RetryMaxWaitTime method returns the retry max wait time that is used to sleep
// before retrying the request.
func (c *Client) RetryMaxWaitTime() time.Duration {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.retryMaxWaitTime
}

// SetRetryMaxWaitTime method sets the max wait time for sleep before retrying
//
// Default is 2 seconds.
func (c *Client) SetRetryMaxWaitTime(maxWaitTime time.Duration) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.retryMaxWaitTime = maxWaitTime
	return c
}

// RetryDelayStrategy method returns the retry delay strategy function;
// otherwise, it is nil.
//
// See [Client.SetRetryDelayStrategy]
func (c *Client) RetryDelayStrategy() RetryDelayStrategyFunc {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.retryDelayStrategy
}

// SetRetryDelayStrategy method used to set the custom Retry delay strategy
// into Resty client, it is used to get wait time before each retry.
// It can be overridden at request level, see [Request.SetRetryDelayStrategy]
//
// By default, Resty employs the capped exponential backoff with a jitter delay strategy.
func (c *Client) SetRetryDelayStrategy(rs RetryDelayStrategyFunc) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.retryDelayStrategy = rs
	return c
}

// IsRetryDefaultConditions method returns true if Resty's default retry conditions
// are enabled otherwise false
//
// Default value is `true`
func (c *Client) IsRetryDefaultConditions() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.isRetryDefaultConditions
}

// SetRetryDefaultConditions method is used to enable/disable the Resty's default
// retry conditions
//
// It can be overridden at request level, see [Request.SetRetryDefaultConditions]
func (c *Client) SetRetryDefaultConditions(b bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isRetryDefaultConditions = b
	return c
}

// IsRetryAllowNonIdempotent method returns true if the client is enabled to allow
// non-idempotent HTTP methods retry; otherwise, it is `false`
//
// Default value is `false`
func (c *Client) IsRetryAllowNonIdempotent() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.isRetryAllowNonIdempotent
}

// SetRetryAllowNonIdempotent method is used to enable/disable non-idempotent HTTP
// methods retry. By default, Resty only allows idempotent HTTP methods, see
// [RFC 9110 Section 9.2.2], [RFC 9110 Section 18.2]
//
// It can be overridden at request level, see [Request.SetRetryAllowNonIdempotent]
//
// [RFC 9110 Section 9.2.2]: https://datatracker.ietf.org/doc/html/rfc9110.html#name-idempotent-methods
// [RFC 9110 Section 18.2]: https://datatracker.ietf.org/doc/html/rfc9110.html#name-method-registration
func (c *Client) SetRetryAllowNonIdempotent(b bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isRetryAllowNonIdempotent = b
	return c
}

// RetryConditions method returns all the retry condition functions.
func (c *Client) RetryConditions() []RetryConditionFunc {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.retryConditions
}

// AddRetryConditions method adds one or more retry condition functions into the request.
// These retry conditions are executed to determine if the request can be retried.
// The request will retry if any functions return `true`, otherwise return `false`.
//
// NOTE:
//   - Retry conditions are executed on each retry attempt.
//   - Default retry conditions are executed first.
//   - Client-level retry conditions are applied to all requests.
//   - Request-level retry conditions are executed before client-level retry conditions.
//     See [Request.AddRetryConditions], [Request.SetRetryConditions]
//   - Once a retry condition returns true, the remaining retry conditions are not executed.
//   - Retry conditions are executed in the order in which they are added.
func (c *Client) AddRetryConditions(conditions ...RetryConditionFunc) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.retryConditions = append(c.retryConditions, conditions...)
	return c
}

// RetryHooks method returns all the retry hook functions.
func (c *Client) RetryHooks() []RetryHookFunc {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.retryHooks
}

// AddRetryHooks method adds one or more side-effecting retry hooks to an array
// of hooks that will be executed on each retry.
//
// NOTE:
//   - Retry hooks are executed on each retry attempt.
//   - The request-level retry hooks are executed first before client-level hooks.
//     See [Request.AddRetryHooks], [Request.SetRetryHooks]
//   - Retry hooks are executed in the order in which they are added.
func (c *Client) AddRetryHooks(hooks ...RetryHookFunc) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.retryHooks = append(c.retryHooks, hooks...)
	return c
}

// isHedgingEnabled method returns true if hedging is enabled.
func (c *Client) isHedgingEnabled() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.hedging != nil
}

// Hedging method returns the hedging configuration of the client.
// If nil is returned, it means hedging is disabled.
func (c *Client) Hedging() *Hedging {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.hedging
}

// SetHedging method sets the hedging instance into client. If nil is passed, it disables hedging.
//
// See [NewHedging] for more details about the Hedging configuration.
func (c *Client) SetHedging(h *Hedging) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()

	// if nil is passed, we disable hedging
	// by reverting the transport instance
	if h == nil {
		if ht, ok := c.httpClient.Transport.(*Hedging); ok {
			c.httpClient.Transport = ht.transport
			c.hedging = h
		}
		return c
	}

	// enable hedging if its not already enabled

	currentTransport := c.httpClient.Transport
	if currentTransport == nil {
		currentTransport = createTransport(nil, nil)
	}

	// If current transport is already a Hedging instance, unwrap it
	// to avoid double-wrapping (e.g., when SetHedging is called multiple times)
	if hedging, ok := currentTransport.(*Hedging); ok {
		currentTransport = hedging.transport
	}

	// Disable retry by default when hedging is enabled.
	// Users can re-enable retry if they want it as a fallback mechanism.
	if c.retryCount > 0 {
		c.log.Warnf("Disabling retry (count: %d) as hedging is now enabled."+
			" You can re-enable retry with SetRetryCount() if you really want it as a fallback."+
			" otherwise, hedging and retry requests can overwhelm the server.", c.retryCount)
		c.retryCount = 0
	}

	h.transport = currentTransport
	c.httpClient.Transport = h
	c.hedging = h

	return c
}

// TLSClientConfig method returns the [tls.Config] from underlying client transport
// otherwise returns nil
func (c *Client) TLSClientConfig() *tls.Config {
	cfg, err := c.tlsConfig()
	if err != nil {
		c.Logger().Errorf("%v", err)
	}
	return cfg
}

// SetTLSClientConfig method sets TLSClientConfig for underlying client Transport.
//
// Values supported by https://pkg.go.dev/crypto/tls#Config can be configured.
//
//	// Disable SSL cert verification for local development
//	client.SetTLSClientConfig(&tls.Config{
//		InsecureSkipVerify: true
//	})
//
// NOTE: This method overwrites existing [http.Transport.TLSClientConfig]
func (c *Client) SetTLSClientConfig(tlsConfig *tls.Config) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()

	// TLSClientConfiger interface handling
	if tc, ok := c.httpClient.Transport.(TLSClientConfiger); ok {
		if err := tc.SetTLSClientConfig(tlsConfig); err != nil {
			c.log.Errorf("%v", err)
		}
		return c
	}

	// default standard transport handling
	transport, ok := c.httpClient.Transport.(*http.Transport)
	if !ok {
		c.log.Errorf("SetTLSClientConfig: %v", ErrNotHttpTransportType)
		return c
	}
	transport.TLSClientConfig = tlsConfig

	return c
}

// ProxyURL method returns the proxy URL if set otherwise nil.
func (c *Client) ProxyURL() *url.URL {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.proxyURL
}

// SetProxy method sets the Proxy URL and Port for the Resty client.
//
//	// HTTP/HTTPS proxy
//	client.SetProxy("http://proxyserver:8888")
//
//	// SOCKS5 Proxy
//	client.SetProxy("socks5://127.0.0.1:1080")
//
// OR you could also set Proxy via environment variable, refer to [http.ProxyFromEnvironment]
func (c *Client) SetProxy(proxyURL string) *Client {
	transport, err := c.HTTPTransport()
	if err != nil {
		c.Logger().Errorf("%v", err)
		return c
	}

	pURL, err := url.Parse(proxyURL)
	if err != nil {
		c.Logger().Errorf("%v", err)
		return c
	}

	c.lock.Lock()
	c.proxyURL = pURL
	transport.Proxy = http.ProxyURL(c.proxyURL)
	c.lock.Unlock()
	return c
}

// RemoveProxy method removes the proxy configuration from the Resty client
//
//	client.RemoveProxy()
func (c *Client) RemoveProxy() *Client {
	transport, err := c.HTTPTransport()
	if err != nil {
		c.Logger().Errorf("%v", err)
		return c
	}

	c.lock.Lock()
	defer c.lock.Unlock()
	c.proxyURL = nil
	transport.Proxy = nil
	return c
}

// SetCertificateFromFile method helps to set client certificates into Resty
// from cert and key files to perform SSL client authentication
//
//	client.SetCertificateFromFile("certs/client.pem", "certs/client.key")
func (c *Client) SetCertificateFromFile(certFilePath, certKeyFilePath string) *Client {
	cert, err := tls.LoadX509KeyPair(certFilePath, certKeyFilePath)
	if err != nil {
		c.Logger().Errorf("client certificate/key parsing error: %v", err)
		return c
	}
	c.SetCertificates(cert)
	return c
}

// SetCertificateFromString method helps to set client certificates into Resty
// from string to perform SSL client authentication
//
//	myClientCertStr := `-----BEGIN CERTIFICATE-----
//	... cert content ...
//	-----END CERTIFICATE-----`
//
//	myClientCertKeyStr := `-----BEGIN PRIVATE KEY-----
//	... cert key content ...
//	-----END PRIVATE KEY-----`
//
//	client.SetCertificateFromString(myClientCertStr, myClientCertKeyStr)
func (c *Client) SetCertificateFromString(certStr, certKeyStr string) *Client {
	cert, err := tls.X509KeyPair([]byte(certStr), []byte(certKeyStr))
	if err != nil {
		c.Logger().Errorf("client certificate/key parsing error: %v", err)
		return c
	}
	c.SetCertificates(cert)
	return c
}

// SetCertificates method helps to conveniently set a slice of client certificates
// into Resty to perform SSL client authentication
//
//	cert, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")
//	if err != nil {
//		log.Printf("ERROR client certificate/key parsing error: %v", err)
//		return
//	}
//
//	client.SetCertificates(cert)
func (c *Client) SetCertificates(certs ...tls.Certificate) *Client {
	config, err := c.tlsConfig()
	if err != nil {
		c.Logger().Errorf("%v", err)
		return c
	}

	c.lock.Lock()
	defer c.lock.Unlock()
	config.Certificates = append(config.Certificates, certs...)
	return c
}

// SetRootCertificates method helps to add one or more root certificate files
// into the Resty client
//
//	// one pem file path
//	client.SetRootCertificates("/path/to/root/pemFile.pem")
//
//	// one or more pem file path(s)
//	client.SetRootCertificates(
//	    "/path/to/root/pemFile1.pem",
//	    "/path/to/root/pemFile2.pem"
//	    "/path/to/root/pemFile3.pem"
//	)
//
//	// if you happen to have string slices
//	client.SetRootCertificates(certs...)
func (c *Client) SetRootCertificates(pemFilePaths ...string) *Client {
	for _, fp := range pemFilePaths {
		rootPemData, err := os.ReadFile(fp)
		if err != nil {
			c.Logger().Errorf("%v", err)
			return c
		}
		c.handleCAs("root", rootPemData)
	}
	return c
}

// SetRootCertificatesWatcher method enables dynamic reloading of one or more root certificate files.
// It is designed for scenarios involving long-running Resty clients where certificates may be renewed.
//
//	client.SetRootCertificatesWatcher(
//		&resty.CertWatcherOptions{
//			PoolInterval: 24 * time.Hour,
//		},
//		"root-ca.pem",
//	)
func (c *Client) SetRootCertificatesWatcher(options *CertWatcherOptions, pemFilePaths ...string) *Client {
	c.SetRootCertificates(pemFilePaths...)
	for _, fp := range pemFilePaths {
		c.initCertWatcher(fp, "root", options)
	}
	return c
}

// SetRootCertificateFromString method helps to add root certificate from the string
// into the Resty client
//
//	myRootCertStr := `-----BEGIN CERTIFICATE-----
//	... cert content ...
//	-----END CERTIFICATE-----`
//
//	client.SetRootCertificateFromString(myRootCertStr)
func (c *Client) SetRootCertificateFromString(pemCerts string) *Client {
	c.handleCAs("root", []byte(pemCerts))
	return c
}

// SetClientRootCertificates method helps to add one or more client root
// certificate files into the Resty client
//
//	// one pem file path
//	client.SetClientRootCertificates("/path/to/client-root/pemFile.pem")
//
//	// one or more pem file path(s)
//	client.SetClientRootCertificates(
//	    "/path/to/client-root/pemFile1.pem",
//	    "/path/to/client-root/pemFile2.pem"
//	    "/path/to/client-root/pemFile3.pem"
//	)
//
//	// if you happen to have string slices
//	client.SetClientRootCertificates(certs...)
func (c *Client) SetClientRootCertificates(pemFilePaths ...string) *Client {
	for _, fp := range pemFilePaths {
		pemData, err := os.ReadFile(fp)
		if err != nil {
			c.Logger().Errorf("%v", err)
			return c
		}
		c.handleCAs("client-root", pemData)
	}
	return c
}

// SetClientRootCertificatesWatcher method enables dynamic reloading of one or more client root certificate files.
// It is designed for scenarios involving long-running Resty clients where certificates may be renewed.
//
//	client.SetClientRootCertificatesWatcher(
//		&resty.CertWatcherOptions{
//			PoolInterval: 24 * time.Hour,
//		},
//		"client-root-ca.pem",
//	)
func (c *Client) SetClientRootCertificatesWatcher(options *CertWatcherOptions, pemFilePaths ...string) *Client {
	c.SetClientRootCertificates(pemFilePaths...)
	for _, fp := range pemFilePaths {
		c.initCertWatcher(fp, "client-root", options)
	}
	return c
}

// SetClientRootCertificateFromString method helps to add a client root certificate
// from the string into the Resty client
//
//	myClientRootCertStr := `-----BEGIN CERTIFICATE-----
//	... cert content ...
//	-----END CERTIFICATE-----`
//
//	client.SetClientRootCertificateFromString(myClientRootCertStr)
func (c *Client) SetClientRootCertificateFromString(pemCerts string) *Client {
	c.handleCAs("client-root", []byte(pemCerts))
	return c
}

func (c *Client) handleCAs(scope string, permCerts []byte) {
	config, err := c.tlsConfig()
	if err != nil {
		c.Logger().Errorf("%v", err)
		return
	}

	c.lock.Lock()
	defer c.lock.Unlock()
	switch scope {
	case "root":
		if config.RootCAs == nil {
			config.RootCAs = x509.NewCertPool()
		}
		config.RootCAs.AppendCertsFromPEM(permCerts)
	case "client-root":
		if config.ClientCAs == nil {
			config.ClientCAs = x509.NewCertPool()
		}
		config.ClientCAs.AppendCertsFromPEM(permCerts)
	}
}

func (c *Client) initCertWatcher(pemFilePath, scope string, options *CertWatcherOptions) {
	tickerDuration := defaultWatcherPoolingInterval
	if options != nil && options.PoolInterval > 0 {
		tickerDuration = options.PoolInterval
	}

	go func() {
		ticker := time.NewTicker(tickerDuration)
		st, err := os.Stat(pemFilePath)
		if err != nil {
			c.Logger().Errorf("%v", err)
			return
		}

		modTime := st.ModTime().UTC()

		for {
			select {
			case <-c.certWatcherStopChan:
				ticker.Stop()
				return
			case <-ticker.C:

				c.debugf("Checking if cert %s has changed...", pemFilePath)

				st, err = os.Stat(pemFilePath)
				if err != nil {
					c.Logger().Errorf("%v", err)
					continue
				}
				newModTime := st.ModTime().UTC()

				if modTime.Equal(newModTime) {
					c.debugf("Cert %s hasn't changed.", pemFilePath)
					continue
				}

				modTime = newModTime

				c.debugf("Reloading cert %s ...", pemFilePath)

				switch scope {
				case "root":
					c.SetRootCertificates(pemFilePath)
				case "client-root":
					c.SetClientRootCertificates(pemFilePath)
				}

				c.debugf("Cert %s reloaded.", pemFilePath)
			}
		}
	}()
}

// ResponseSaveDirectory method returns the output directory value from the client.
func (c *Client) ResponseSaveDirectory() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.responseSaveDirectory
}

// SetResponseSaveDirectory method sets the output directory for saving HTTP responses in a file.
// Resty creates one if the output directory does not exist. This setting is optional,
// if you plan to use the absolute path in [Request.SetResponseSaveFileName] and can used together.
//
//	client.SetResponseSaveDirectory("/save/http/response/here")
func (c *Client) SetResponseSaveDirectory(dirPath string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.responseSaveDirectory = dirPath
	return c
}

// IsResponseSaveToFile method returns true if the save response is set to true; otherwise, false
func (c *Client) IsResponseSaveToFile() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.isResponseSaveToFile
}

// SetResponseSaveToFile method used to enable the save response option at the client level for
// all requests
//
//	client.SetResponseSaveToFile(true)
//
// Resty determines the save filename in the following order -
//   - [Request.SetResponseSaveFileName]
//   - Content-Disposition header
//   - Request URL using [path.Base]
//   - Request URL hostname if path is empty or "/"
//
// It can be overridden at request level, see [Request.SetResponseSaveToFile]
func (c *Client) SetResponseSaveToFile(save bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isResponseSaveToFile = save
	return c
}

// HTTPTransport method does type assertion and returns [http.Transport]
// from the client instance, if type assertion fails it returns an error
func (c *Client) HTTPTransport() (*http.Transport, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if transport, ok := c.httpClient.Transport.(*http.Transport); ok {
		return transport, nil
	}
	return nil, ErrNotHttpTransportType
}

// Transport method returns underlying client transport referance as-is
// i.e., [http.RoundTripper]
func (c *Client) Transport() http.RoundTripper {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.httpClient.Transport
}

// SetTransport method sets custom [http.Transport] or any [http.RoundTripper]
// compatible interface implementation in the Resty client.
//
//	transport := &http.Transport{
//		// something like Proxying to httptest.Server, etc...
//		Proxy: func(req *http.Request) (*url.URL, error) {
//			return url.Parse(server.URL)
//		},
//	}
//	client.SetTransport(transport)
//
// NOTE:
//   - If transport is not the type of [http.Transport], you may lose the
//     ability to set a few Resty client settings. However, if you implement
//     [TLSClientConfiger] interface, then TLS client config is possible to set.
//   - It overwrites the Resty client transport instance and its configurations.
func (c *Client) SetTransport(transport http.RoundTripper) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	if transport != nil {
		c.httpClient.Transport = transport
	}
	return c
}

// Scheme method returns custom scheme value from the client.
//
//	scheme := client.Scheme()
func (c *Client) Scheme() string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.scheme
}

// SetScheme method sets a custom scheme for the Resty client. It's a way to override the default.
//
//	client.SetScheme("http")
func (c *Client) SetScheme(scheme string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	if !isStringEmpty(scheme) {
		c.scheme = strings.TrimSpace(scheme)
	}
	return c
}

// SetCloseConnection method sets variable `Close` in HTTP request struct with the given
// value. More info: https://golang.org/src/net/http/request.go
//
// It can be overridden at the request level, see [Request.SetCloseConnection]
func (c *Client) SetCloseConnection(close bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.closeConnection = close
	return c
}

// SetResponseDoNotParse method instructs Resty not to parse the response body automatically.
//
// Resty exposes the raw response body as [io.ReadCloser]. If you use it, do not
// forget to close the body, otherwise, you might get into connection leaks, and connection
// reuse may not happen.
//
// NOTE: The default [Response] middlewares are not executed when using this option. User
// takes over the control of handling response body from Resty.
func (c *Client) SetResponseDoNotParse(notParse bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isResponseDoNotParse = notParse
	return c
}

// PathParams method returns the path parameters from the client.
//
//	pathParams := client.PathParams()
func (c *Client) PathParams() map[string]string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.pathParams
}

// SetPathParam method sets a single URL path key-value pair in the
// Resty client instance.
//
//	client.SetPathParam("userId", "sample@sample.com")
//
//	Result:
//	   URL - /v1/users/{userId}/details
//	   Composed URL - /v1/users/sample@sample.com/details
//
// It replaces the value of the key while composing the request URL.
// The value will be escaped using [url.PathEscape] function.
//
// It can be overridden at the request level,
// see [Request.SetPathParam] or [Request.SetPathParams]
func (c *Client) SetPathParam(param, value string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.pathParams[param] = url.PathEscape(value)
	return c
}

// SetPathParamAny method sets a single URL path key-value pair in the
// Resty client instance.
//
// It is similar to [Client.SetPathParam] but accepts any type as the value and converts
// it to a string using predefined formatting rules (integers, bools, time.Time, etc.).
//
//	client.SetPathParamAny("userId", 12345)
//
//	Result:
//	   URL - /v1/users/{userId}/details
//	   Composed URL - /v1/users/12345/details
//
// It replaces the value of the key while composing the request URL.
// The value will be escaped using [url.PathEscape] function.
//
// It can be overridden at the request level,
// see [Request.SetPathParamAny] or [Request.SetPathParams]
func (c *Client) SetPathParamAny(param string, value any) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	strVal := formatAnyToString(value)
	c.pathParams[param] = url.PathEscape(strVal)
	return c
}

// SetPathParams method sets multiple URL path key-value pairs at one go in the
// Resty client instance.
//
//	client.SetPathParams(map[string]string{
//		"userId":       "sample@sample.com",
//		"subAccountId": "100002",
//		"path":         "groups/developers",
//	})
//
//	Result:
//	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
//	   Composed URL - /v1/users/sample@sample.com/100002/groups%2Fdevelopers/details
//
// It replaces the value of the key while composing the request URL.
// The values will be escaped using [url.PathEscape] function.
//
// It can be overridden at the request level,
// see [Request.SetPathParam] or [Request.SetPathParams]
func (c *Client) SetPathParams(params map[string]string) *Client {
	for p, v := range params {
		c.SetPathParam(p, v)
	}
	return c
}

// SetPathRawParam method sets a single URL path key-value pair in the
// Resty client instance without path escape.
//
//	client.SetPathRawParam("path", "groups/developers")
//
//	Result:
//		URL - /v1/users/{path}/details
//		Composed URL - /v1/users/groups/developers/details
//
// It replaces the value of the key while composing the request URL.
// The value will be used as-is, no path escape applied.
//
// It can be overridden at the request level,
// see [Request.SetPathRawParam] or [Request.SetPathRawParams]
func (c *Client) SetPathRawParam(param, value string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.pathParams[param] = value
	return c
}

// SetPathRawParamAny method sets a single URL path key-value pair in the
// Resty client instance without path escape.
//
// It is similar to [Client.SetPathRawParam] but accepts any type as the value and converts
// it to a string using predefined formatting rules (integers, bools, time.Time, etc.).
//
//	client.SetPathRawParamAny("userId", 12345)
//
//	Result:
//	   URL - /v1/users/{userId}/details
//	   Composed URL - /v1/users/12345/details
//
// It replaces the value of the key while composing the request URL.
// The value will be used as-is, no path escape applied.
//
// It can be overridden at the request level,
// see [Request.SetPathRawParamAny] or [Request.SetPathRawParams]
func (c *Client) SetPathRawParamAny(param string, value any) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	strVal := formatAnyToString(value)
	c.pathParams[param] = strVal
	return c
}

// SetPathRawParams method sets multiple URL path key-value pairs at one go in the
// Resty client instance without path escape.
//
//	client.SetPathRawParams(map[string]string{
//		"userId":       "sample@sample.com",
//		"subAccountId": "100002",
//		"path":         "groups/developers",
//	})
//
//	Result:
//	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
//	   Composed URL - /v1/users/sample@sample.com/100002/groups/developers/details
//
// It replaces the value of the key while composing the request URL.
// The value will be used as-is, no path escape applied.
//
// It can be overridden at the request level,
// see [Request.SetPathRawParam] or [Request.SetPathRawParams]
func (c *Client) SetPathRawParams(params map[string]string) *Client {
	for p, v := range params {
		c.SetPathRawParam(p, v)
	}
	return c
}

// SetJSONEscapeHTML method enables or disables the HTML escape on JSON marshal.
// By default, escape HTML is `true`.
//
// NOTE: This option only applies to the standard JSON Marshaller used by Resty.
//
// It can be overridden at the request level, see [Request.SetJSONEscapeHTML]
func (c *Client) SetJSONEscapeHTML(b bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.jsonEscapeHTML = b
	return c
}

// ResponseBodyLimit method returns the value max body size limit in bytes from
// the client instance.
func (c *Client) ResponseBodyLimit() int64 {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.responseBodyLimit
}

// SetResponseBodyLimit method sets a maximum body size limit in bytes on response,
// avoid reading too much data to memory.
//
// Client will return [resty.ErrResponseBodyTooLarge] if the body size of the body
// in the uncompressed response is larger than the limit.
// Body size limit will not be enforced in the following cases:
//   - ResponseBodyLimit <= 0, which is the default behavior.
//   - [Request.SetResponseSaveFileName] is called to save response data to the file.
//   - "DoNotParseResponse" is set for client or request.
//
// It can be overridden at the request level; see [Request.SetResponseBodyLimit]
func (c *Client) SetResponseBodyLimit(v int64) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.responseBodyLimit = v
	return c
}

// IsTrace method returns true if the trace is enabled on the client instance; otherwise, it returns false.
func (c *Client) IsTrace() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.isTrace
}

// SetTrace method is used to turn on/off the trace capability in the Resty client instance.
// It provides an insight into the request lifecycle using [httptrace.ClientTrace].
//
//	client := resty.New().SetTrace(true)
//
//	resp, err := client.R().Get("https://httpbin.org/get")
//	fmt.Println("error:", err)
//	fmt.Println("Trace Info:", resp.Request.TraceInfo())
//
// The method [Request.SetTrace] is also available to get trace info for a single request.
func (c *Client) SetTrace(t bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isTrace = t
	return c
}

// SetCurlCmdGenerate method is used to turn on/off the generate curl command at the
// client instance level.
//
// By default, Resty does not log the curl command in the debug log since it has the potential
// to leak sensitive data unless explicitly enabled via [Client.SetCurlCmdDebugLog] or
// [Request.SetCurlCmdDebugLog].
//
// NOTE: Use with care.
//   - Potential to leak sensitive data from [Request] and [Response] in the debug log
//     when the debug log option is enabled.
//   - Additional memory usage since the request body was reread.
//   - curl body is not generated for [io.Reader] and multipart request flow.
//
// It can be overridden at the request level; see [Request.SetCurlCmdGenerate]
func (c *Client) SetCurlCmdGenerate(b bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isCurlCmdGenerate = b
	return c
}

// SetCurlCmdDebugLog method enables the curl command to be logged in the debug log.
//
// It can be overridden at the request level; see [Request.SetCurlCmdDebugLog]
func (c *Client) SetCurlCmdDebugLog(b bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isCurlCmdDebugLog = b
	return c
}

// SetQueryParamsUnescape method sets the choice of unescape query parameters for the request URL.
// To prevent broken URL, Resty replaces space (" ") with "+" in the query parameters.
//
// See [Request.SetQueryParamsUnescape]
//
// NOTE: Request failure is possible due to non-standard usage of Unescaped Query Parameters.
func (c *Client) SetQueryParamsUnescape(unescape bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.unescapeQueryParams = unescape
	return c
}

// ResponseBodyUnlimitedReads method returns true if enabled. Otherwise, it returns false
func (c *Client) ResponseBodyUnlimitedReads() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.resBodyUnlimitedReads
}

// SetResponseBodyUnlimitedReads method is to turn on/off the response body in memory
// that provides an ability to do unlimited reads.
//
// It can be overridden at the request level; see [Request.SetResponseBodyUnlimitedReads]
//
// Unlimited reads are possible in a few scenarios, even without enabling it.
//   - When debug mode is enabled
//
// NOTE: Use with care
//   - Turning on this feature keeps the response body in memory, which might cause additional memory usage.
func (c *Client) SetResponseBodyUnlimitedReads(b bool) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.resBodyUnlimitedReads = b
	return c
}

// IsProxySet method returns the true is proxy is set from the Resty client; otherwise
// false. By default, the proxy is set from the environment variable; refer to [http.ProxyFromEnvironment].
func (c *Client) IsProxySet() bool {
	return c.ProxyURL() != nil
}

// Client method returns the underlying Go [http.Client] used by the Resty.
func (c *Client) Client() *http.Client {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.httpClient
}

// Clone method returns a clone of the original client.
//
// NOTE: Use with care:
//   - Interface values are not deeply cloned. Thus, both the original and the
//     clone will use the same value.
//   - It is not safe for concurrent use. You should only use this method
//     when you are sure that any other concurrent process is not using the client
//     or client instance is protected by a mutex.
func (c *Client) Clone(ctx context.Context) *Client {
	cc := new(Client)
	// dereference the pointer and copy the value
	*cc = *c

	cc.ctx = ctx
	cc.queryParams = cloneURLValues(c.queryParams)
	cc.formData = cloneURLValues(c.formData)
	cc.header = c.header.Clone()
	cc.pathParams = maps.Clone(c.pathParams)

	if c.credentials != nil {
		cc.credentials = c.credentials.Clone()
	}

	cc.contentTypeEncoders = maps.Clone(c.contentTypeEncoders)
	cc.contentTypeDecoders = maps.Clone(c.contentTypeDecoders)
	cc.contentDecompressers = maps.Clone(c.contentDecompressers)
	copy(cc.contentDecompresserKeys, c.contentDecompresserKeys)

	if c.proxyURL != nil {
		cc.proxyURL, _ = url.Parse(c.proxyURL.String())
	}
	// clone cookies
	if l := len(c.cookies); l > 0 {
		cc.cookies = make([]*http.Cookie, 0, l)
		for _, cookie := range c.cookies {
			cc.cookies = append(cc.cookies, cloneCookie(cookie))
		}
	}

	// certain values need to be reset
	cc.lock = &sync.RWMutex{}
	return cc
}

// Close method performs cleanup and closure activities on the client instance
func (c *Client) Close() error {
	// Execute close hooks first
	c.onCloseHooks()

	if c.LoadBalancer() != nil {
		silently(c.LoadBalancer().Close())
	}
	close(c.certWatcherStopChan)

	return nil
}

func (c *Client) executeRequestMiddlewares(req *Request) (err error) {
	for _, f := range c.requestMiddlewares() {
		if err = f(c, req); err != nil {
			return err
		}
	}
	return nil
}

// Executes method executes the given `Request` object and returns
// response or error.
func (c *Client) execute(req *Request) (*Response, error) {
	if c.circuitBreaker != nil {
		if err := c.circuitBreaker.allow(); err != nil {
			c.circuitBreaker.onTriggerHooks(req, err)
			return nil, err
		}
	}

	if err := c.executeRequestMiddlewares(req); err != nil {
		return nil, err
	}

	if hostHeader := req.Header.Get("Host"); hostHeader != "" {
		req.RawRequest.Host = hostHeader
	}

	prepareRequestDebugInfo(c, req)

	req.StartTime = time.Now()
	resp, err := c.Client().Do(req.withTimeout())
	// Cancel multipart context for io.Copy to stop reading/writing further
	if req.isMultiPart && req.multipartCancelFunc != nil {
		req.multipartCancelFunc()
	}

	response := &Response{Request: req, RawResponse: resp}
	response.setReceivedAt()
	if err != nil {
		return response, err
	}
	if req.isMultiPart && req.multipartErrChan != nil {
		// read all multipart errors from channel
		for err = range req.multipartErrChan {
			response.CascadeError = wrapErrors(err, response.CascadeError)
		}
	}

	if resp != nil {
		if c.circuitBreaker != nil {
			c.circuitBreaker.applyPolicies(resp)
		}

		response.Body = resp.Body
		if err = response.wrapContentDecompresser(); err != nil {
			return response, response.wrapError(err, false)
		}

		response.wrapLimitReadCloser()

		if !req.IsResponseDoNotParse {
			if req.IsResponseBodyUnlimitedReads || req.IsDebug {
				response.wrapCopyReadCloser()

				if err = response.readAll(); err != nil {
					return response, response.wrapError(err, false)
				}
			}
		}
	}

	debugLogger(c, response)

	// Apply Response middleware
	for _, f := range c.responseMiddlewares() {
		if err = f(c, response); err != nil {
			response.CascadeError = wrapErrors(err, response.CascadeError)
		}
	}

	return response, response.wrapError(nil, false)
}

// getting TLS client config if not exists then create one
func (c *Client) tlsConfig() (*tls.Config, error) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if tc, ok := c.httpClient.Transport.(TLSClientConfiger); ok {
		return tc.TLSClientConfig(), nil
	}

	transport, ok := c.httpClient.Transport.(*http.Transport)
	if !ok {
		return nil, ErrNotHttpTransportType
	}

	if transport.TLSClientConfig == nil {
		transport.TLSClientConfig = &tls.Config{}
	}
	return transport.TLSClientConfig, nil
}

// just an internal helper method
func (c *Client) outputLogTo(w io.Writer) *Client {
	c.Logger().(*logger).l.SetOutput(w)
	return c
}

// ResponseError is a wrapper that includes the server response with an error.
// Neither the err nor the response should be nil.
type ResponseError struct {
	Response *Response
	Err      error
}

func (e *ResponseError) Error() string {
	return e.Err.Error()
}

func (e *ResponseError) Unwrap() error {
	return e.Err
}

// Helper to run errorHooks hooks.
// It wraps the error in a [ResponseError] if the resp is not nil
// so hooks can access it.
func (c *Client) onErrorHooks(req *Request, res *Response, err error) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	if err != nil {
		if res != nil { // wrap with ResponseError
			err = &ResponseError{Response: res, Err: err}
		}
		for _, h := range c.errorHooks {
			h(req, err)
		}
	} else {
		for _, h := range c.successHooks {
			h(c, res)
		}
	}
}

// Helper to run panicHooks hooks.
func (c *Client) onPanicHooks(req *Request, err error) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	for _, h := range c.panicHooks {
		h(req, err)
	}
}

// Helper to run invalidHooks hooks.
func (c *Client) onInvalidHooks(req *Request, err error) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	for _, h := range c.invalidHooks {
		h(req, err)
	}
}

// Helper to run closeHooks hooks.
func (c *Client) onCloseHooks() {
	c.lock.RLock()
	defer c.lock.RUnlock()
	for _, h := range c.closeHooks {
		h()
	}
}

func (c *Client) debugf(format string, v ...any) {
	if c.IsDebug() {
		c.Logger().Debugf(format, v...)
	}
}


================================================
FILE: client_test.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"bytes"
	"compress/gzip"
	"compress/lzw"
	"context"
	cryprand "crypto/rand"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"math"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)

func TestClientBasicAuth(t *testing.T) {
	ts := createAuthServer(t)
	defer ts.Close()

	c := dcnl()
	c.SetBasicAuth("myuser", "basicauth").
		SetBaseURL(ts.URL).
		SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})

	resp, err := c.R().
		SetResult(&AuthSuccess{}).
		Post("/login")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())

	t.Logf("Result Success: %q", resp.Result().(*AuthSuccess))
	logResponse(t, resp)
}

func TestClientAuthToken(t *testing.T) {
	ts := createAuthServer(t)
	defer ts.Close()

	c := dcnl()
	c.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
		SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF").
		SetBaseURL(ts.URL + "/")

	resp, err := c.R().Get("/profile")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())
}

func TestClientAuthScheme(t *testing.T) {
	ts := createAuthServer(t)
	defer ts.Close()

	c := dcnl()
	// Ensure default Bearer
	c.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
		SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF").
		SetBaseURL(ts.URL + "/")

	resp, err := c.R().Get("/profile")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())

	// Ensure setting the scheme works as well
	c.SetAuthScheme("Bearer")
	assertEqual(t, "Bearer", c.AuthScheme())

	resp2, err2 := c.R().Get("/profile")
	assertError(t, err2)
	assertEqual(t, http.StatusOK, resp2.StatusCode())

}

func TestClientResponseMiddleware(t *testing.T) {
	ts := createGenericServer(t)
	defer ts.Close()

	c := dcnl()
	c.AddResponseMiddleware(func(c *Client, res *Response) error {
		t.Logf("Request sent at: %v", res.Request.StartTime)
		t.Logf("Response Received at: %v", res.ReceivedAt())

		return nil
	})

	resp, err := c.R().
		SetBody("ResponseMiddleware: This is plain text body to server").
		Put(ts.URL + "/plaintext")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())
	assertEqual(t, "TestPut: plain text response", resp.String())
}

func TestClientRedirectPolicy(t *testing.T) {
	ts := createRedirectServer(t)
	defer ts.Close()

	c := dcnl().SetRedirectPolicy(RedirectFlexiblePolicy(20), RedirectDomainCheckPolicy("127.0.0.1"))
	res, err := c.R().
		SetHeader("Name1", "Value1").
		SetHeader("Name2", "Value2").
		SetHeader("Name3", "Value3").
		Get(ts.URL + "/redirect-1")

	assertTrue(t, err.Error() == "Get \"/redirect-21\": resty: stopped after 20 redirects")

	redirects := res.RedirectHistory()
	assertEqual(t, 20, len(redirects))

	finalReq := redirects[0]
	assertEqual(t, 307, finalReq.StatusCode)
	assertEqual(t, ts.URL+"/redirect-20", finalReq.URL)

	c.SetRedirectPolicy(RedirectNoPolicy())
	res, err = c.R().Get(ts.URL + "/redirect-1")
	assertNil(t, err)
	assertEqual(t, http.StatusTemporaryRedirect, res.StatusCode())
	assertEqual(t, `<a href="/redirect-2">Temporary Redirect</a>.`, res.String())
}

func TestClientTimeout(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	c := dcnl().SetTimeout(200 * time.Millisecond)
	_, err := c.R().Get(ts.URL + "/set-timeout-test")
	assertErrorIs(t, context.DeadlineExceeded, err)
}

func TestClientTimeoutWithinThreshold(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	c := dcnl().SetTimeout(200 * time.Millisecond)

	resp, err := c.R().Get(ts.URL + "/set-timeout-test-with-sequence")
	assertError(t, err)

	seq1, _ := strconv.ParseInt(resp.String(), 10, 32)

	resp, err = c.R().Get(ts.URL + "/set-timeout-test-with-sequence")
	assertError(t, err)

	seq2, _ := strconv.ParseInt(resp.String(), 10, 32)

	assertEqual(t, seq1+1, seq2)
}

func TestClientTimeoutInternalError(t *testing.T) {
	c := dcnl().SetTimeout(time.Second * 1)
	_, _ = c.R().Get("http://localhost:9000/set-timeout-test")
}

func TestClientProxy(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	c := dcnl()
	c.SetTimeout(1 * time.Second)
	c.SetProxy("http://sampleproxy:8888")

	resp, err := c.R().Get(ts.URL)
	assertNotNil(t, resp)
	assertNotNil(t, err)

	// error
	c.SetProxy("//not.a.user@%66%6f%6f.com:8888")

	resp, err = c.R().
		Get(ts.URL)
	assertNotNil(t, err)
	assertNotNil(t, resp)
}

func TestClientSetCertificates(t *testing.T) {
	certFile := filepath.Join(getTestDataPath(), "cert.pem")
	keyFile := filepath.Join(getTestDataPath(), "key.pem")

	t.Run("client cert from file", func(t *testing.T) {
		c := dcnl()
		c.SetCertificateFromFile(certFile, keyFile)
		assertEqual(t, 1, len(c.TLSClientConfig().Certificates))
	})

	t.Run("error-client cert from file", func(t *testing.T) {
		c := dcnl()
		c.SetCertificateFromFile(certFile+"no", keyFile+"no")
		assertEqual(t, 0, len(c.TLSClientConfig().Certificates))
	})

	t.Run("client cert from string", func(t *testing.T) {
		certPemData, _ := os.ReadFile(certFile)
		keyPemData, _ := os.ReadFile(keyFile)
		c := dcnl()
		c.SetCertificateFromString(string(certPemData), string(keyPemData))
		assertEqual(t, 1, len(c.TLSClientConfig().Certificates))
	})

	t.Run("error-client cert from string", func(t *testing.T) {
		c := dcnl()
		c.SetCertificateFromString(string("empty"), string("empty"))
		assertEqual(t, 0, len(c.TLSClientConfig().Certificates))
	})
}

func TestClientSetRootCertificate(t *testing.T) {
	t.Run("root cert", func(t *testing.T) {
		client := dcnl()
		client.SetRootCertificates(filepath.Join(getTestDataPath(), "sample-root.pem"))

		transport, err := client.HTTPTransport()

		assertNil(t, err)
		assertNotNil(t, transport.TLSClientConfig.RootCAs)
	})

	t.Run("root cert not exists", func(t *testing.T) {
		client := dcnl()
		client.SetRootCertificates(filepath.Join(getTestDataPath(), "not-exists-sample-root.pem"))

		transport, err := client.HTTPTransport()

		assertNil(t, err)
		assertNil(t, transport.TLSClientConfig)
	})

	t.Run("root cert from string", func(t *testing.T) {
		client := dcnl()
		rootPemData, err := os.ReadFile(filepath.Join(getTestDataPath(), "sample-root.pem"))
		assertNil(t, err)

		client.SetRootCertificateFromString(string(rootPemData))

		transport, err := client.HTTPTransport()

		assertNil(t, err)
		assertNotNil(t, transport.TLSClientConfig.RootCAs)
	})
}

type CustomRoundTripper1 struct{}

// RoundTrip just for test
func (rt *CustomRoundTripper1) RoundTrip(_ *http.Request) (*http.Response, error) {
	return &http.Response{}, nil
}

func TestClientCACertificateFromStringErrorTls(t *testing.T) {
	t.Run("root cert string", func(t *testing.T) {
		client := NewWithClient(&http.Client{})
		client.outputLogTo(io.Discard)

		rootPemData, err := os.ReadFile(filepath.Join(getTestDataPath(), "sample-root.pem"))
		assertNil(t, err)
		rt := &CustomRoundTripper1{}
		client.SetTransport(rt)
		transport, err := client.HTTPTransport()

		client.SetRootCertificateFromString(string(rootPemData))

		assertNotNil(t, rt)
		assertNotNil(t, err)
		assertNil(t, transport)
	})

	t.Run("client cert string", func(t *testing.T) {
		client := NewWithClient(&http.Client{})
		client.outputLogTo(io.Discard)

		rootPemData, err := os.ReadFile(filepath.Join(getTestDataPath(), "sample-root.pem"))
		assertNil(t, err)
		rt := &CustomRoundTripper1{}
		client.SetTransport(rt)
		transport, err := client.HTTPTransport()

		client.SetClientRootCertificateFromString(string(rootPemData))

		assertNotNil(t, rt)
		assertNotNil(t, err)
		assertNil(t, transport)
	})
}

// CustomRoundTripper2 just for test
type CustomRoundTripper2 struct {
	http.RoundTripper
	TLSClientConfiger
	tlsConfig *tls.Config
	returnErr bool
}

// RoundTrip just for test
func (rt *CustomRoundTripper2) RoundTrip(_ *http.Request) (*http.Response, error) {
	if rt.returnErr {
		return nil, errors.New("test req mock error")
	}
	return &http.Response{}, nil
}

func (rt *CustomRoundTripper2) TLSClientConfig() *tls.Config {
	return rt.tlsConfig
}
func (rt *CustomRoundTripper2) SetTLSClientConfig(tlsConfig *tls.Config) error {
	if rt.returnErr {
		return errors.New("test mock error")
	}
	rt.tlsConfig = tlsConfig
	return nil
}

func TestClientTLSConfigerInterface(t *testing.T) {

	t.Run("assert transport and custom roundtripper", func(t *testing.T) {
		c := dcnl()

		assertNotNil(t, c.Transport())
		assertEqual(t, "http.Transport", inferType(c.Transport()).String())

		ct := &CustomRoundTripper2{}
		c.SetTransport(ct)
		assertNotNil(t, c.Transport())
		assertEqual(t, "resty.CustomRoundTripper2", inferType(c.Transport()).String())
	})

	t.Run("get and set tls config", func(t *testing.T) {
		c := dcnl()

		ct := &CustomRoundTripper2{}
		c.SetTransport(ct)

		tlsConfig := &tls.Config{InsecureSkipVerify: true}
		c.SetTLSClientConfig(tlsConfig)
		assertEqual(t, tlsConfig, c.TLSClientConfig())
	})

	t.Run("get tls config error", func(t *testing.T) {
		c := dcnl()

		ct := &CustomRoundTripper1{}
		c.SetTransport(ct)
		assertNil(t, c.TLSClientConfig())
	})

	t.Run("set tls config error", func(t *testing.T) {
		c := dcnl()

		ct := &CustomRoundTripper2{returnErr: true}
		c.SetTransport(ct)

		tlsConfig := &tls.Config{InsecureSkipVerify: true}
		c.SetTLSClientConfig(tlsConfig)
		assertNil(t, c.TLSClientConfig())
	})
}

func TestClientSetClientRootCertificate(t *testing.T) {
	client := dcnl()
	client.SetClientRootCertificates(filepath.Join(getTestDataPath(), "sample-root.pem"))

	transport, err := client.HTTPTransport()

	assertNil(t, err)
	assertNotNil(t, transport.TLSClientConfig.ClientCAs)
}

func TestClientSetClientRootCertificateNotExists(t *testing.T) {
	client := dcnl()
	client.SetClientRootCertificates(filepath.Join(getTestDataPath(), "not-exists-sample-root.pem"))

	transport, err := client.HTTPTransport()

	assertNil(t, err)
	assertNil(t, transport.TLSClientConfig)
}

func TestClientSetClientRootCertificateWatcher(t *testing.T) {
	t.Run("Cert exists", func(t *testing.T) {
		client := dcnl()
		client.SetClientRootCertificatesWatcher(
			&CertWatcherOptions{PoolInterval: time.Second * 1},
			filepath.Join(getTestDataPath(), "sample-root.pem"),
		)

		transport, err := client.HTTPTransport()

		assertNil(t, err)
		assertNotNil(t, transport.TLSClientConfig.ClientCAs)
	})

	t.Run("Cert does not exist", func(t *testing.T) {
		client := dcnl()
		client.SetClientRootCertificatesWatcher(nil, filepath.Join(getTestDataPath(), "not-exists-sample-root.pem"))

		transport, err := client.HTTPTransport()

		assertNil(t, err)
		assertNil(t, transport.TLSClientConfig)
	})
}

func TestClientSetClientRootCertificateFromString(t *testing.T) {
	client := dcnl()
	rootPemData, err := os.ReadFile(filepath.Join(getTestDataPath(), "sample-root.pem"))
	assertNil(t, err)

	client.SetClientRootCertificateFromString(string(rootPemData))

	transport, err := client.HTTPTransport()

	assertNil(t, err)
	assertNotNil(t, transport.TLSClientConfig.ClientCAs)
}

func TestClientRequestMiddlewareModification(t *testing.T) {
	tc := dcnl()
	tc.AddRequestMiddleware(func(c *Client, r *Request) error {
		r.SetAuthToken("This is test auth token")
		return nil
	})

	ts := createGetServer(t)
	defer ts.Close()

	resp, err := tc.R().Get(ts.URL + "/")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())
	assertEqual(t, "200 OK", resp.Status())
	assertEqual(t, "TestGet: text response", resp.String())

	logResponse(t, resp)
}

func TestClientSetHeaderVerbatim(t *testing.T) {
	ts := createPostServer(t)
	defer ts.Close()

	c := dcnl().
		SetHeaderVerbatim("header-lowercase", "value_lowercase").
		SetHeader("header-lowercase", "value_standard")

	//lint:ignore SA1008 valid one, so ignore this!
	unConventionHdrValue := strings.Join(c.Header()["header-lowercase"], "")
	assertEqual(t, "value_lowercase", unConventionHdrValue)
	assertEqual(t, "value_standard", c.Header().Get("Header-Lowercase"))
}

func TestClientSetHeaderAny(t *testing.T) {
	c := dcnl().
		SetHeaderAny("X-Int-Value", 42).
		SetHeaderAny("X-String-Value", "hello")

	assertEqual(t, "42", c.Header().Get("X-Int-Value"))
	assertEqual(t, "hello", c.Header().Get("X-String-Value"))
}

func TestClientSetHeaderVerbatimAny(t *testing.T) {
	c := dcnl().
		SetHeaderVerbatimAny("header-lowercase", 123)

	//lint:ignore SA1008 valid one, so ignore this!
	unConventionHdrValue := strings.Join(c.Header()["header-lowercase"], "")
	assertEqual(t, "123", unConventionHdrValue)
}

func TestClientSetQueryParamAny(t *testing.T) {
	c := dcnl().
		SetQueryParamAny("page", 5).
		SetQueryParamAny("active", true)

	assertEqual(t, "5", c.QueryParams().Get("page"))
	assertEqual(t, "true", c.QueryParams().Get("active"))
}

func TestClientSetPathParamAny(t *testing.T) {
	c := dcnl().
		SetPathParamAny("userId", 42).
		SetPathParamAny("name", "john doe")

	assertEqual(t, "42", c.PathParams()["userId"])
	assertEqual(t, "john%20doe", c.PathParams()["name"])
}

func TestClientSetRawPathParamAny(t *testing.T) {
	c := dcnl().
		SetPathRawParamAny("userId", 42).
		SetPathRawParamAny("name", "john doe")

	assertEqual(t, "42", c.PathParams()["userId"])
	assertEqual(t, "john doe", c.PathParams()["name"])
}

func TestClientSetTransport(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()
	client := dcnl()

	transport := &http.Transport{
		// something like Proxying to httptest.Server, etc...
		Proxy: func(req *http.Request) (*url.URL, error) {
			return url.Parse(ts.URL)
		},
	}
	client.SetTransport(transport)
	transportInUse, err := client.HTTPTransport()

	assertNil(t, err)
	assertTrue(t, transport == transportInUse, "HTTP Transport should be of same type")
}

func TestClientSetScheme(t *testing.T) {
	client := dcnl()

	client.SetScheme("http")

	assertEqual(t, "http", client.scheme, "Scheme should be 'http'")
}

func TestClientSetCookieJar(t *testing.T) {
	client := dcnl()
	backupJar := client.httpClient.Jar

	client.SetCookieJar(nil)
	assertNil(t, client.httpClient.Jar, "CookieJar should be nil")

	client.SetCookieJar(backupJar)
	assertTrue(t, client.httpClient.Jar == backupJar, "CookieJar should be set back to original jar")
}

// This test methods exist for test coverage purpose
// to validate the getter and setter
func TestClientSettingsCoverage(t *testing.T) {
	c := dcnl()

	assertNotNil(t, c.CookieJar())
	assertNotNil(t, c.ContentTypeEncoders())
	assertNotNil(t, c.ContentTypeDecoders())
	assertFalse(t, c.IsDebug())
	assertEqual(t, math.MaxInt32, c.DebugBodyLimit())
	assertNotNil(t, c.Logger())
	assertEqual(t, 0, c.RetryCount())
	assertEqual(t, time.Millisecond*100, c.RetryWaitTime())
	assertEqual(t, time.Second*2, c.RetryMaxWaitTime())
	assertFalse(t, c.IsTrace())
	assertEqual(t, 0, len(c.RetryConditions()))

	authToken := "sample auth token value"
	c.SetAuthToken(authToken)
	assertEqual(t, authToken, c.AuthToken())

	customAuthHeader := "X-Custom-Authorization"
	c.SetHeaderAuthorizationKey(customAuthHeader)
	assertEqual(t, customAuthHeader, c.HeaderAuthorizationKey())

	c.SetCloseConnection(true)

	c.SetDebug(false)

	assertTrue(t, c.IsRetryDefaultConditions())
	c.SetRetryDefaultConditions(false)
	assertFalse(t, c.IsRetryDefaultConditions())
	c.SetRetryDefaultConditions(true)
	assertTrue(t, c.IsRetryDefaultConditions())

	nr := nopReader{}
	n, err1 := nr.Read(nil)
	assertEqual(t, 0, n)
	assertEqual(t, io.EOF, err1)
	b, err1 := nr.ReadByte()
	assertEqual(t, byte(0), b)
	assertEqual(t, io.EOF, err1)

	// [Start] Custom Transport scenario
	ct := dcnl()
	ct.SetTransport(&CustomRoundTripper1{})
	_, err := ct.HTTPTransport()
	assertNotNil(t, err)
	assertEqual(t, ErrNotHttpTransportType, err)

	ct.SetProxy("http://localhost:8080")
	ct.RemoveProxy()

	ct.SetCertificates(tls.Certificate{})
	ct.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
	ct.SetRootCertificateFromString("root cert")

	ct.outputLogTo(io.Discard)
	// [End] Custom Transport scenario

	// Response - for now stay here
	resp := &Response{Request: &Request{}}
	s := resp.fmtBodyString(0)
	assertEqual(t, "***** NO CONTENT *****", s)
}

func TestContentLengthWhenBodyIsNil(t *testing.T) {
	client := dcnl()

	fnPreRequestMiddleware1 := func(c *Client, r *Request) error {
		// validate
		assertEqual(t, int64(0), r.contentLength)
		assertEqual(t, int64(0), r.RawRequest.ContentLength)
		return nil
	}
	client.SetRequestMiddlewares(
		MiddlewareRequestCreate,
		fnPreRequestMiddleware1,
	)

	client.R().SetBody(nil).Get("http://localhost")
}

func TestClientPreRequestMiddlewares(t *testing.T) {
	client := dcnl()

	fnPreRequestMiddleware1 := func(c *Client, r *Request) error {
		c.Logger().Debugf("I'm in Pre-Request Hook")
		return nil
	}

	fnPreRequestMiddleware2 := func(c *Client, r *Request) error {
		c.Logger().Debugf("I'm Overwriting existing Pre-Request Hook")

		// Reading Request `N` no of times
		for i := 0; i < 5; i++ {
			b, _ := r.RawRequest.GetBody()
			rb, _ := io.ReadAll(b)
			c.Logger().Debugf("%s %v", string(rb), len(rb))
			assertTrue(t, len(rb) >= 45)
		}
		return nil
	}

	client.SetRequestMiddlewares(
		MiddlewareRequestCreate,
		fnPreRequestMiddleware1,
		fnPreRequestMiddleware2,
	)

	ts := createPostServer(t)
	defer ts.Close()

	// Regular bodybuf use case
	resp, _ := client.R().
		SetBody(map[string]any{"username": "testuser", "password": "testpass"}).
		Post(ts.URL + "/login")
	assertEqual(t, http.StatusOK, resp.StatusCode())
	assertEqual(t, `{ "id": "success", "message": "login successful" }`, resp.String())

	// io.Reader body use case
	resp, _ = client.R().
		SetHeader(hdrContentTypeKey, jsonContentType).
		SetBody(bytes.NewReader([]byte(`{"username":"testuser", "password":"testpass"}`))).
		Post(ts.URL + "/login")
	assertEqual(t, http.StatusOK, resp.StatusCode())
	assertEqual(t, `{ "id": "success", "message": "login successful" }`, resp.String())
}

func TestClientPreRequestMiddlewareError(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	c := dcnl()
	fnPreRequestMiddleware1 := func(c *Client, r *Request) error {
		return errors.New("error from PreRequestMiddleware")
	}
	c.SetRequestMiddlewares(
		MiddlewareRequestCreate,
		fnPreRequestMiddleware1,
	)

	resp, err := c.R().Get(ts.URL)
	assertNotNil(t, err)
	assertEqual(t, "error from PreRequestMiddleware", err.Error())
	assertNil(t, resp)
}

func TestClientAllowMethodGetPayload(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	t.Run("method GET allow string payload at client level", func(t *testing.T) {
		c := dcnl()
		c.SetMethodGetAllowPayload(true)
		assertTrue(t, c.IsMethodGetAllowPayload())

		payload := "test-payload"
		resp, err := c.R().SetBody(payload).Get(ts.URL + "/get-method-payload-test")

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode(), "Status code should be 200 OK")
		assertEqual(t, payload, resp.String(), "Response payload should be same as request payload")
	})

	t.Run("method GET allow io.Reader payload at client level", func(t *testing.T) {
		c := dcnl()
		c.SetMethodGetAllowPayload(true)
		assertTrue(t, c.IsMethodGetAllowPayload())

		payload := "test-payload"
		body := bytes.NewReader([]byte(payload))
		resp, err := c.R().SetBody(body).Get(ts.URL + "/get-method-payload-test")

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, payload, resp.String(), "Response payload should be same as request payload")
	})

	t.Run("method GET disallow payload at client level", func(t *testing.T) {
		c := dcnl()
		c.SetMethodGetAllowPayload(false)
		assertFalse(t, c.IsMethodGetAllowPayload())

		payload := bytes.NewReader([]byte("test-payload"))
		resp, err := c.R().SetBody(payload).Get(ts.URL + "/get-method-payload-test")

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, "", resp.String())
	})
}

func TestClientAllowMethodDeletePayload(t *testing.T) {
	ts := createGenericServer(t)
	defer ts.Close()

	t.Run("method DELETE allow string payload at client level", func(t *testing.T) {
		c := dcnl().SetBaseURL(ts.URL)

		c.SetMethodDeleteAllowPayload(true)
		assertTrue(t, c.IsMethodDeleteAllowPayload())

		payload := "test-payload"
		resp, err := c.R().SetBody(payload).Delete("/delete")

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, payload, resp.String())
	})

	t.Run("method DELETE allow io.Reader payload at client level", func(t *testing.T) {
		c := dcnl().SetBaseURL(ts.URL)

		c.SetMethodDeleteAllowPayload(true)
		assertTrue(t, c.IsMethodDeleteAllowPayload())

		payload := "test-payload"
		body := bytes.NewReader([]byte(payload))
		resp, err := c.R().SetBody(body).Delete("/delete")

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, payload, resp.String())
	})

	t.Run("method DELETE disallow payload at client level", func(t *testing.T) {
		c := dcnl().SetBaseURL(ts.URL)

		c.SetMethodDeleteAllowPayload(false)
		assertFalse(t, c.IsMethodDeleteAllowPayload())

		payload := bytes.NewReader([]byte("test-payload"))
		resp, err := c.R().SetBody(payload).Delete("/delete")

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, "", resp.String(), "Response payload should be empty")
	})
}

func TestClientRoundTripper(t *testing.T) {
	c := NewWithClient(&http.Client{})
	c.outputLogTo(io.Discard)

	rt := &CustomRoundTripper2{}
	c.SetTransport(rt)

	ct, err := c.HTTPTransport()
	assertNotNil(t, err)
	assertNil(t, ct)
	assertEqual(t, ErrNotHttpTransportType, err)
}

func TestClientNewRequest(t *testing.T) {
	c := New()
	request := c.NewRequest()
	assertNotNil(t, request)
}

func TestClientDebugBodySizeLimit(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	c, lb := dcldb()
	c.SetDebugBodyLimit(30)

	testcases := []struct{ url, want string }{
		// Text, does not exceed limit.
		{url: ts.URL, want: "TestGet: text response"},
		// Empty response.
		{url: ts.URL + "/no-content", want: "***** NO CONTENT *****"},
		// JSON, does not exceed limit.
		{url: ts.URL + "/json", want: "{\n   \"TestGet\": \"JSON response\"\n}"},
		// Invalid JSON, does not exceed limit.
		{url: ts.URL + "/json-invalid", want: "DebugLog: Response.fmtBodyString: invalid character 'T' looking for beginning of value"},
		// Text, exceeds limit.
		{url: ts.URL + "/long-text", want: "RESPONSE TOO LARGE"},
		// JSON, exceeds limit.
		{url: ts.URL + "/long-json", want: "RESPONSE TOO LARGE"},
	}
	for _, tc := range testcases {
		_, err := c.R().Get(tc.url)
		if tc.want != "" {
			assertError(t, err)
			debugLog := lb.String()
			if !strings.Contains(debugLog, tc.want) {
				t.Errorf("Expected logs to contain [%v], got [\n%v]", tc.want, debugLog)
			}
			lb.Reset()
		}
	}
}

func TestGzipCompress(t *testing.T) {
	ts := createGenericServer(t)
	defer ts.Close()

	c := dcnl()
	testcases := []struct{ url, want string }{
		{ts.URL + "/gzip-test", "This is Gzip response testing"},
		{ts.URL + "/gzip-test-gziped-empty-body", ""},
		{ts.URL + "/gzip-test-no-gziped-body", ""},
	}
	for _, tc := range testcases {
		resp, err := c.R().Get(tc.url)

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, "200 OK", resp.Status())
		assertEqual(t, tc.want, resp.String())

		logResponse(t, resp)
	}
}

func TestDeflateCompress(t *testing.T) {
	ts := createGenericServer(t)
	defer ts.Close()

	c := dcnl()
	testcases := []struct{ url, want string }{
		{ts.URL + "/deflate-test", "This is Deflate response testing"},
		{ts.URL + "/deflate-test-empty-body", ""},
		{ts.URL + "/deflate-test-no-body", ""},
	}
	for _, tc := range testcases {
		resp, err := c.R().Get(tc.url)

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, "200 OK", resp.Status())
		assertEqual(t, tc.want, resp.String())

		logResponse(t, resp)
	}
}

type lzwReader struct {
	s io.ReadCloser
	r io.ReadCloser
}

func (l *lzwReader) Read(p []byte) (n int, err error) {
	return l.r.Read(p)
}

func (l *lzwReader) Close() error {
	closeq(l.r)
	closeq(l.s)
	return nil
}

func TestLzwCompress(t *testing.T) {
	ts := createGenericServer(t)
	defer ts.Close()

	c := dcnl()

	// Not found scenario
	_, err := c.R().Get(ts.URL + "/lzw-test")
	assertNotNil(t, err)
	assertEqual(t, ErrContentDecompresserNotFound, err)

	// Register LZW content decoder
	c.AddContentDecompresser("ComPreSs", func(r io.ReadCloser) (io.ReadCloser, error) {
		l := &lzwReader{
			s: r,
			r: lzw.NewReader(r, lzw.LSB, 8),
		}
		return l, nil
	})
	c.SetContentDecompresserKeys([]string{"compress"})

	testcases := []struct{ url, want string }{
		{ts.URL + "/lzw-test", "This is LZW response testing"},
		{ts.URL + "/lzw-test-empty-body", ""},
		{ts.URL + "/lzw-test-no-body", ""},
	}
	for _, tc := range testcases {
		resp, err := c.R().Get(tc.url)

		assertError(t, err)
		assertEqual(t, http.StatusOK, resp.StatusCode())
		assertEqual(t, "200 OK", resp.Status())
		assertEqual(t, tc.want, resp.String())

		logResponse(t, resp)
	}
}

func TestClientLogCallbacks(t *testing.T) {
	ts := createAuthServer(t)
	defer ts.Close()

	c, lb := dcldb()

	c.OnDebugLog(func(dl *DebugLog) {
		// request
		// masking authorization header
		dl.Request.Header.Set("Authorization", "Bearer *******************************")

		// response
		dl.Response.Header.Add("X-Debug-Response-Log", "Modified :)")
		dl.Response.Body += "\nModified the response body content"
	})

	c.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
		SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF")

	resp, err := c.R().
		SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF-Request").
		Get(ts.URL + "/profile")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())

	// Validating debug log updates
	logInfo := lb.String()
	assertTrue(t, strings.Contains(logInfo, "Bearer *******************************"))
	assertTrue(t, strings.Contains(logInfo, "X-Debug-Response-Log"))
	assertTrue(t, strings.Contains(logInfo, "Modified the response body content"))

	// overwrite scenario
	c.OnDebugLog(func(dl *DebugLog) {
		// overwrite debug log
	})
	resp, err = c.R().
		SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF-Request").
		Get(ts.URL + "/profile")
	assertNil(t, err)
	assertNotNil(t, resp)
	assertEqual(t, int64(66), resp.Size())
	assertTrue(t, strings.Contains(lb.String(), "Overwriting an existing on-debug-log callback from=resty.dev/v3.TestClientLogCallbacks.func1 to=resty.dev/v3.TestClientLogCallbacks.func2"))
}

func TestDebugLogSimultaneously(t *testing.T) {
	ts := createGetServer(t)

	c := dcnl().
		SetDebug(true).
		SetBaseURL(ts.URL)

	t.Cleanup(ts.Close)
	for i := 0; i < 50; i++ {
		t.Run(fmt.Sprint(i), func(t *testing.T) {
			t.Parallel()
			resp, err := c.R().
				SetBody([]int{1, 2, 3}).
				SetHeader(hdrContentTypeKey, "application/json; charset=utf-8").
				Post("/")

			assertError(t, err)
			assertEqual(t, http.StatusOK, resp.StatusCode())
		})
	}
}

func TestCustomTransportSettings(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	customTransportSettings := &TransportSettings{
		DialerTimeout:          30 * time.Second,
		DialerKeepAlive:        15 * time.Second,
		IdleConnTimeout:        120 * time.Second,
		TLSHandshakeTimeout:    20 * time.Second,
		ExpectContinueTimeout:  1 * time.Second,
		MaxIdleConns:           50,
		MaxIdleConnsPerHost:    3,
		MaxConnsPerHost:        100,
		ResponseHeaderTimeout:  10 * time.Second,
		MaxResponseHeaderBytes: 1 << 10,
		WriteBufferSize:        2 << 10,
		ReadBufferSize:         2 << 10,
	}
	client := NewWithTransportSettings(customTransportSettings)
	client.SetBaseURL(ts.URL)

	resp, err := client.R().Get("/")
	assertNil(t, err)
	assertEqual(t, "TestGet: text response", resp.String())
}

func TestDefaultDialerTransportSettings(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	t.Run("transport-default", func(t *testing.T) {
		client := NewWithTransportSettings(nil)
		client.SetBaseURL(ts.URL)

		resp, err := client.R().Get("/")
		assertNil(t, err)
		assertEqual(t, "TestGet: text response", resp.String())
	})

	t.Run("dialer-transport-default", func(t *testing.T) {
		client := NewWithDialerAndTransportSettings(nil, nil)
		client.SetBaseURL(ts.URL)

		resp, err := client.R().Get("/")
		assertNil(t, err)
		assertEqual(t, "TestGet: text response", resp.String())
	})
}

func TestNewWithDialer(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	dialer := &net.Dialer{
		Timeout:   15 * time.Second,
		KeepAlive: 15 * time.Second,
	}
	client := NewWithDialer(dialer)
	client.SetBaseURL(ts.URL)

	resp, err := client.R().Get("/")
	assertNil(t, err)
	assertEqual(t, "TestGet: text response", resp.String())
}

func TestNewWithLocalAddr(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	localAddress, _ := net.ResolveTCPAddr("tcp", "127.0.0.1")
	client := NewWithLocalAddr(localAddress)
	client.SetBaseURL(ts.URL)

	resp, err := client.R().Get("/")
	assertNil(t, err)
	assertEqual(t, "TestGet: text response", resp.String())
}

func TestClientOnResponseFailure(t *testing.T) {
	tests := []struct {
		name        string
		setup       func(*Client)
		isError     bool
		hasResponse bool
		panics      bool
	}{
		{
			name: "successful_request",
		},
		{
			name: "http_status_failure",
			setup: func(client *Client) {
				client.SetAuthToken("BAD")
			},
		},
		{
			name: "before_request_failure",
			setup: func(client *Client) {
				client.AddRequestMiddleware(func(client *Client, request *Request) error {
					return fmt.Errorf("before request")
				})
			},
			isError: true,
		},
		{
			name: "before_request_failure_retry",
			setup: func(client *Client) {
				client.SetRetryCount(3).AddRequestMiddleware(func(client *Client, request *Request) error {
					return fmt.Errorf("before request")
				})
			},
			isError: true,
		},
		{
			name: "after_response_failure",
			setup: func(client *Client) {
				client.AddResponseMiddleware(func(client *Client, response *Response) error {
					return fmt.Errorf("after response")
				})
			},
			isError:     true,
			hasResponse: true,
		},
		{
			name: "after_response_failure_retry",
			setup: func(client *Client) {
				client.SetRetryCount(3).AddResponseMiddleware(func(client *Client, response *Response) error {
					return fmt.Errorf("after response")
				})
			},
			isError:     true,
			hasResponse: true,
		},
		{
			name: "panic with error",
			setup: func(client *Client) {
				client.AddRequestMiddleware(func(client *Client, request *Request) error {
					panic(fmt.Errorf("before request"))
				})
			},
			isError:     false,
			hasResponse: false,
			panics:      true,
		},
		{
			name: "panic with string",
			setup: func(client *Client) {
				client.AddRequestMiddleware(func(client *Client, request *Request) error {
					panic("before request")
				})
			},
			isError:     false,
			hasResponse: false,
			panics:      true,
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()
			ts := createAuthServer(t)
			defer ts.Close()

			var assertErrorHook = func(r *Request, err error) {
				assertNotNil(t, r)
				v, ok := err.(*ResponseError)
				assertEqual(t, test.hasResponse, ok)
				if ok {
					assertNotNil(t, v.Response)
					assertNotNil(t, v.Err)
				}
			}
			var errorHook1, errorHook2, successHook1, successHook2, panicHook1, panicHook2 int
			defer func() {
				if rec := recover(); rec != nil {
					assertTrue(t, test.panics, "expected to panic")
					assertEqual(t, 0, errorHook1)
					assertEqual(t, 0, successHook1)
					assertEqual(t, 1, panicHook1)
					assertEqual(t, 1, panicHook2)
				}
			}()
			c := dcnl().
				SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
				SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF").
				SetRetryCount(0).
				SetRetryMaxWaitTime(time.Microsecond).
				AddRetryConditions(func(response *Response, err error) bool {
					if err != nil {
						return true
					}
					return response.IsStatusFailure()
				}).
				OnError(func(r *Request, err error) {
					assertErrorHook(r, err)
					errorHook1++
				}).
				OnError(func(r *Request, err error) {
					assertErrorHook(r, err)
					errorHook2++
				}).
				OnPanic(func(r *Request, err error) {
					assertErrorHook(r, err)
					panicHook1++
				}).
				OnPanic(func(r *Request, err error) {
					assertErrorHook(r, err)
					panicHook2++
				}).
				OnSuccess(func(c *Client, resp *Response) {
					assertNotNil(t, c)
					assertNotNil(t, resp)
					successHook1++
				}).
				OnSuccess(func(c *Client, resp *Response) {
					assertNotNil(t, c)
					assertNotNil(t, resp)
					successHook2++
				})
			if test.setup != nil {
				test.setup(c)
			}
			_, err := c.R().Get(ts.URL + "/profile")
			if test.isError {
				assertNotNil(t, err)
				assertEqual(t, 1, errorHook1)
				assertEqual(t, 1, errorHook2)
				assertEqual(t, 0, successHook1)
				assertEqual(t, 0, panicHook1)
			} else {
				assertError(t, err)
				assertEqual(t, 0, errorHook1)
				assertEqual(t, 1, successHook1)
				assertEqual(t, 1, successHook2)
				assertEqual(t, 0, panicHook1)
			}
		})
	}
}

func TestResponseError(t *testing.T) {
	err := errors.New("error message")
	re := &ResponseError{
		Response: &Response{},
		Err:      err,
	}
	assertNotNil(t, re.Unwrap())
	assertEqual(t, err.Error(), re.Error())
}

func TestHostURLForGH318AndGH407(t *testing.T) {
	ts := createPostServer(t)
	defer ts.Close()

	targetURL, _ := url.Parse(ts.URL)
	t.Log("ts.URL:", ts.URL)
	t.Log("targetURL.Host:", targetURL.Host)
	// Sample output
	// ts.URL: http://127.0.0.1:55967
	// targetURL.Host: 127.0.0.1:55967

	// Unable use the local http test server for this
	// use case testing
	//
	// using `targetURL.Host` value or test case yield to ERROR
	// "parse "127.0.0.1:55967": first path segment in URL cannot contain colon"

	// test the functionality with httpbin.org locally
	// will figure out later

	c := dcnl()
	// c.SetScheme("http")
	// c.SetHostURL(targetURL.Host + "/")

	// t.Log("with leading `/`")
	// resp, err := c.R().Post("/login")
	// assertNil(t, err)
	// assertNotNil(t, resp)

	// t.Log("\nwithout leading `/`")
	// resp, err = c.R().Post("login")
	// assertNil(t, err)
	// assertNotNil(t, resp)

	t.Log("with leading `/` on request & with trailing `/` on host url")
	c.SetBaseURL(ts.URL + "/")
	resp, err := c.R().
		SetBody(map[string]any{"username": "testuser", "password": "testpass"}).
		Post("/login")
	assertNil(t, err)
	assertNotNil(t, resp)
}

func TestPostRedirectWithBody(t *testing.T) {
	ts := createPostServer(t)
	defer ts.Close()

	mu := sync.Mutex{}
	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))

	c := dcnl().SetBaseURL(ts.URL)

	totalRequests := 4000
	wg := sync.WaitGroup{}
	wg.Add(totalRequests)
	for i := 0; i < totalRequests; i++ {
		if i%50 == 0 {
			time.Sleep(20 * time.Millisecond) // to prevent test server socket exhaustion
		}
		go func() {
			defer wg.Done()
			mu.Lock()
			randNumber := rnd.Int()
			mu.Unlock()
			resp, err := c.R().
				SetBody([]byte(strconv.Itoa(randNumber))).
				Post("/redirect-with-body")
			assertError(t, err)
			assertNotNil(t, resp)
		}()
	}
	wg.Wait()
}

func TestUnixSocket(t *testing.T) {
	unixSocketAddr := createUnixSocketEchoServer(t)
	defer os.Remove(unixSocketAddr)

	// Create a Go's http.Transport so we can set it in resty.
	transport := http.Transport{
		Dial: func(_, _ string) (net.Conn, error) {
			return net.Dial("unix", unixSocketAddr)
		},
	}

	// Create a Resty Client
	client := New()

	// Set the previous transport that we created, set the scheme of the communication to the
	// socket and set the unixSocket as the HostURL.
	client.SetTransport(&transport).SetScheme("http").SetBaseURL(unixSocketAddr)

	// No need to write the host's URL on the request, just the path.
	res, err := client.R().Get("http://localhost/")
	assertNil(t, err)
	assertEqual(t, "Hi resty client from a server running on Unix domain socket!", res.String())

	res, err = client.R().Get("http://localhost/hello")
	assertNil(t, err)
	assertEqual(t, "Hello resty client from a server running on endpoint /hello!", res.String())
}

func TestClientClone(t *testing.T) {
	parent := New()

	// set a non-interface field
	parent.SetBaseURL("http://localhost")
	parent.SetBasicAuth("parent", "")
	parent.SetProxy("http://localhost:8080")

	parent.SetCookie(&http.Cookie{
		Name:  "go-resty-1",
		Value: "This is cookie 1 value",
	})
	parent.SetCookies([]*http.Cookie{
		{
			Name:  "go-resty-2",
			Value: "This is cookie 2 value",
		},
		{
			Name:  "go-resty-3",
			Value: "This is cookie 3 value",
		},
	})

	clone := parent.Clone(context.Background())
	// update value of non-interface type - change will only happen on clone
	clone.SetBaseURL("https://local.host")

	clone.SetBasicAuth("clone", "clone")

	// assert non-interface type
	assertEqual(t, "http://localhost", parent.BaseURL())
	assertEqual(t, "https://local.host", clone.BaseURL())
	assertEqual(t, "parent", parent.credentials.Username)
	assertEqual(t, "clone", clone.credentials.Username)

	// assert interface/pointer type
	assertEqual(t, parent.Client(), clone.Client())

	// assert cookies
	parentCookies := parent.Cookies()
	cloneCookies := clone.Cookies()
	assertEqual(t, len(parentCookies), len(cloneCookies))
	for i := range parentCookies {
		assertEqual(t, parentCookies[i].Name, cloneCookies[i].Name)
		assertEqual(t, parentCookies[i].Value, cloneCookies[i].Value)
	}
}

func TestResponseBodyLimit(t *testing.T) {
	ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
		io.CopyN(w, cryprand.Reader, 100*800)
	})
	defer ts.Close()

	t.Run("client body limit", func(t *testing.T) {
		resBodyLimit := int64(1024)
		c := dcnl().SetResponseBodyLimit(resBodyLimit)
		assertEqual(t, resBodyLimit, c.ResponseBodyLimit())

		resp, err := c.R().Get(ts.URL + "/")
		assertNotNil(t, err)
		assertErrorIs(t, ErrReadExceedsThresholdLimit, err)
		assertTrue(t, resp.Size() == resBodyLimit)
	})

	t.Run("request body limit", func(t *testing.T) {
		resBodyLimit := int64(1024)
		c := dcnl()

		resp, err := c.R().SetResponseBodyLimit(resBodyLimit).Get(ts.URL + "/")
		assertNotNil(t, err)
		assertErrorIs(t, ErrReadExceedsThresholdLimit, err)
		assertTrue(t, resp.Size() == resBodyLimit)
	})

	t.Run("body less than limit", func(t *testing.T) {
		c := dcnl()

		res, err := c.R().SetResponseBodyLimit(800*100 + 10).Get(ts.URL + "/")
		assertNil(t, err)
		assertEqual(t, 800*100, len(res.Bytes()))
		assertEqual(t, int64(800*100), res.Size())
	})

	t.Run("no body limit", func(t *testing.T) {
		c := dcnl()

		res, err := c.R().Get(ts.URL + "/")
		assertNil(t, err)
		assertEqual(t, 800*100, len(res.Bytes()))
		assertEqual(t, int64(800*100), res.Size())
	})

	t.Run("read error", func(t *testing.T) {
		tse := createTestServer(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set(hdrContentEncodingKey, "gzip")
			var buf [1024]byte
			w.Write(buf[:])
		})
		defer tse.Close()

		c := dcnl()

		_, err := c.R().SetResponseBodyLimit(10240).Get(tse.URL + "/")
		assertErrorIs(t, gzip.ErrHeader, err)
	})
}

func TestClient_executeReadAllError(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	ioReadAll = func(_ io.Reader) ([]byte, error) {
		return nil, errors.New("test case error")
	}
	t.Cleanup(func() {
		ioReadAll = io.ReadAll
	})

	c := dcnld()

	resp, err := c.R().
		SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)).
		Get(ts.URL + "/json")

	assertNotNil(t, err)
	assertEqual(t, "test case error", err.Error())
	assertEqual(t, http.StatusOK, resp.StatusCode())
	assertEqual(t, "", resp.String())
}

func TestClientDebugf(t *testing.T) {
	t.Run("Debug mode enabled", func(t *testing.T) {
		var b bytes.Buffer
		c := New().SetLogger(&logger{l: log.New(&b, "", 0)}).SetDebug(true)
		c.debugf("hello")
		assertEqual(t, "DEBUG RESTY hello\n", b.String())
	})

	t.Run("Debug mode disabled", func(t *testing.T) {
		var b bytes.Buffer
		c := New().SetLogger(&logger{l: log.New(&b, "", 0)})
		c.debugf("hello")
		assertEqual(t, "", b.String())
	})
}

func TestClientOnClose(t *testing.T) {
	var hookExecuted bool

	c := dcnl()
	c.OnClose(func() {
		hookExecuted = true
	})

	err := c.Close()
	assertNil(t, err)
	assertTrue(t, hookExecuted, "OnClose hook should be executed")
}

func TestClientOnCloseMultipleHooks(t *testing.T) {
	var executionOrder []string

	c := dcnl()
	c.OnClose(func() {
		executionOrder = append(executionOrder, "first")
	})
	c.OnClose(func() {
		executionOrder = append(executionOrder, "second")
	})
	c.OnClose(func() {
		executionOrder = append(executionOrder, "third")
	})

	err := c.Close()
	assertNil(t, err)
	assertEqual(t, []string{"first", "second", "third"}, executionOrder)
}

func TestClientHedgingMutualExclusionWithRetry(t *testing.T) {
	c := dcnl()

	// Set retry first
	c.SetRetryCount(2)
	assertEqual(t, 2, c.RetryCount())

	// Enable hedging should disable retry by default
	h := NewHedging().
		SetDelay(50 * time.Millisecond).
		SetMaxRequest(3).
		SetMaxRequestPerSecond(0)
	c.SetHedging(h)
	assertEqual(t, 0, c.RetryCount())

	// But user can re-enable retry as fallback
	c.SetRetryCount(1)
	assertEqual(t, 1, c.RetryCount())
	assertEqual(t, true, c.isHedgingEnabled())

	// Disable hedging
	c.SetHedging(nil)
	assertEqual(t, false, c.isHedgingEnabled())
	assertEqual(t, 1, c.RetryCount()) // Retry count should remain
}


================================================
FILE: context_test.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// 2016 Andrew Grigorev (https://github.com/ei-grad)
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"context"
	"errors"
	"net/http"
	"sync/atomic"
	"testing"
	"time"
)

func TestClientSetContext(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	c := dcnl()

	assertNil(t, c.Context())

	c.SetContext(context.Background())

	resp, err := c.R().Get(ts.URL + "/")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())
	assertEqual(t, "200 OK", resp.Status())
	assertEqual(t, "TestGet: text response", resp.String())

	logResponse(t, resp)
}

func TestRequestSetContext(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	resp, err := dcnl().R().
		SetContext(context.Background()).
		Get(ts.URL + "/")

	assertError(t, err)
	assertEqual(t, http.StatusOK, resp.StatusCode())
	assertEqual(t, "200 OK", resp.Status())
	assertEqual(t, "TestGet: text response", resp.String())

	logResponse(t, resp)
}

func TestSetContextWithError(t *testing.T) {
	ts := createGetServer(t)
	defer ts.Close()

	resp, err := dcnlr().
		SetContext(context.Background()).
		Get(ts.URL + "/mypage")

	assertError(t, err)
	assertEqual(t, http.StatusBadRequest, resp.StatusCode(), "expected bad request status code")
	assertEqual(t, "", resp.String(), "expected empty response body on bad request")

	logResponse(t, resp)
}

func TestSetContextCancel(t *testing.T) {
	ch := make(chan struct{})
	ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			ch <- struct{}{} // tell test request is finished
		}()
		t.Logf("Server: %v %v", r.Method, r.URL.Path)
		ch <- struct{}{}
		<-ch // wait for client to finish request
		n, err := w.Write([]byte("TestSetContextCancel: response"))
		// FIXME? test server doesn't handle request cancellation
		t.Logf("Server: wrote %d bytes", n)
		t.Logf("Server: err is %v ", err)
	})
	defer ts.Close()

	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		<-ch // wait for server to start request handling
		cancel()
	}()

	_, err := dcnl().R().
		SetContext(ctx).
		Get(ts.URL + "/")

	ch <- struct{}{} // tell server to continue request handling

	<-ch // wait for server to finish request handling

	t.Logf("Error: %v", err)
	if !errIsContextCanceled(err) {
		t.Errorf("Got unexpected error: %v", err)
	}
}

func TestSetContextCancelRetry(t *testing.T) {
	reqCount := 0
	ch := make(chan struct{})
	ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
		reqCount++
		defer func() {
			ch <- struct{}{} // tell test request is finished
		}()
		t.Logf("Server: %v %v", r.Method, r.URL.Path)
		ch <- struct{}{}
		<-ch // wait for client to finish request
		n, err := w.Write([]byte("TestSetContextCancel: response"))
		// FIXME? test server doesn't handle request cancellation
		t.Logf("Server: wrote %d bytes", n)
		t.Logf("Server: err is %v ", err)
	})
	defer ts.Close()

	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		<-ch // wait for server to start request handling
		cancel()
	}()

	c := dcnl().
		SetTimeout(time.Second * 3).
		SetRetryCount(3)

	_, err := c.R().
		SetContext(ctx).
		Get(ts.URL + "/")

	ch <- struct{}{} // tell server to continue request handling

	<-ch // wait for server to finish request handling

	t.Logf("Error: %v", err)
	if !errIsContextCanceled(err) {
		t.Errorf("Got unexpected error: %v", err)
	}

	if reqCount != 1 {
		t.Errorf("Request was retried %d times instead of 1", reqCount)
	}
}

func TestSetContextCancelWithError(t *testing.T) {
	ch := make(chan struct{})
	ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			ch <- struct{}{} // tell test request is finished
		}()
		t.Logf("Server: %v %v", r.Method, r.URL.Path)
		t.Log("Server: sending StatusBadRequest response")
		w.WriteHeader(http.StatusBadRequest)
		ch <- struct{}{}
		<-ch // wait for client to finish request
		n, err := w.Write([]byte("TestSetContextCancelWithError: response"))
		// FIXME? test server doesn't handle request cancellation
		t.Logf("Server: wrote %d bytes", n)
		t.Logf("Server: err is %v ", err)
	})
	defer ts.Close()

	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		<-ch // wait for server to start request handling
		cancel()
	}()

	_, err := dcnl().R().
		SetContext(ctx).
		Get(ts.URL + "/")

	ch <- struct{}{} // tell server to continue request handling

	<-ch // wait for server to finish request handling

	t.Logf("Error: %v", err)
	if !errIsContextCanceled(err) {
		t.Errorf("Got unexpected error: %v", err)
	}
}

func TestClientRetryWithSetContext(t *testing.T) {
	var attempt int32
	ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
		t.Logf("Method: %v", r.Method)
		t.Logf("Path: %v", r.URL.Path)
		if atomic.AddInt32(&attempt, 1) <= 4 {
			time.Sleep(100 * time.Millisecond)
		}
		_, _ = w.Write([]byte("TestClientRetry page"))
	})
	defer ts.Close()

	c := dcnl().
		SetTimeout(50 * time.Millisecond).
		SetRetryCount(3)

	_, err := c.R().
		SetContext(context.Background()).
		Get(ts.URL + "/")

	assertNotNil(t, ts)
	assertNotNil(t, err)
	assertErrorIs(t, context.DeadlineExceeded, err, "expected context deadline exceeded error")
}

func TestRequestContext(t *testing.T) {
	client := dcnl()
	r := client.NewRequest()
	assertNotNil(t, r.Context(), "expected default context to be non-nil")

	r.SetContext(context.Background())
	assertNotNil(t, r.Context(), "expected context to be set")
}

func errIsContextCanceled(err error) bool {
	return errors.Is(err, context.Canceled)
}


================================================
FILE: curl.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"bytes"
	"io"
	"net/http"
	"regexp"

	"net/url"
	"strings"
)

func buildCurlCmd(req *Request) string {
	// generate curl raw headers
	var curl = "curl -X " + req.Method + " "
	headers := dumpCurlHeaders(req.RawRequest)
	for _, kv := range *headers {
		curl += "-H " + cmdQuote(kv[0]+": "+kv[1]) + " "
	}

	// generate curl cookies
	if cookieJar := req.client.CookieJar(); cookieJar != nil {
		if cookies := cookieJar.Cookies(req.RawRequest.URL); len(cookies) > 0 {
			curl += "-H " + cmdQuote(dumpCurlCookies(cookies)) + " "
		}
	}

	// generate curl body except for io.Reader and multipart request flow
	if req.RawRequest.GetBody != nil {
		body, err := req.RawRequest.GetBody()
		if err == nil {
			buf, _ := io.ReadAll(body)
			curl += "-d " + cmdQuote(string(bytes.TrimRight(buf, "\n"))) + " "
		} else {
			req.log.Errorf("curl: %v", err)
			curl += "-d ''"
		}
	}

	urlString := cmdQuote(req.RawRequest.URL.String())
	if urlString == "''" {
		urlString = "'http://unexecuted-request'"
	}
	curl += urlString
	return curl
}

// dumpCurlCookies dumps cookies to curl format
func dumpCurlCookies(cookies []*http.Cookie) string {
	sb := strings.Builder{}
	sb.WriteString("Cookie: ")
	for _, cookie := range cookies {
		sb.WriteString(cookie.Name + "=" + url.QueryEscape(cookie.Value) + "&")
	}
	return strings.TrimRight(sb.String(), "&")
}

// dumpCurlHeaders dumps headers to curl format
func dumpCurlHeaders(req *http.Request) *[][2]string {
	headers := [][2]string{}
	for k, vs := range req.Header {
		for _, v := range vs {
			headers = append(headers, [2]string{k, v})
		}
	}
	n := len(headers)
	for i := 0; i < n; i++ {
		for j := n - 1; j > i; j-- {
			jj := j - 1
			h1, h2 := headers[j], headers[jj]
			if h1[0] < h2[0] {
				headers[jj], headers[j] = headers[j], headers[jj]
			}
		}
	}
	return &headers
}

var regexCmdQuote = regexp.MustCompile(`[^\w@%+=:,./-]`)

// cmdQuote method to escape arbitrary strings for a safe use as
// command line arguments in the most common POSIX shells.
//
// The original Python package which this work was inspired by can be found
// at https://pypi.python.org/pypi/shellescape.
func cmdQuote(s string) string {
	if len(s) == 0 {
		return "''"
	}

	if regexCmdQuote.MatchString(s) {
		return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'"
	}

	return s
}


================================================
FILE: curl_test.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/http/cookiejar"
	"strings"
	"testing"
)

func TestCurlGenerateUnexecutedRequest(t *testing.T) {
	req := dcnldr().
		SetBody(map[string]string{
			"name": "Resty",
		}).
		SetCookies(
			[]*http.Cookie{
				{Name: "count", Value: "1"},
			},
		).
		SetMethod(MethodPost)

	assertEqual(t, "", req.CurlCmd())

	curlCmdUnexecuted := req.SetCurlCmdGenerate(true).CurlCmd()
	req.SetCurlCmdGenerate(false)

	if !strings.Contains(curlCmdUnexecuted, "Cookie: count=1") ||
		!strings.Contains(curlCmdUnexecuted, "curl -X POST") ||
		!strings.Contains(curlCmdUnexecuted, `-d '{"name":"Resty"}'`) {
		t.Fatal("Incomplete curl:", curlCmdUnexecuted)
	} else {
		t.Log("curlCmdUnexecuted: \n", curlCmdUnexecuted)
	}

}

func TestCurlGenerateExecutedRequest(t *testing.T) {
	ts := createPostServer(t)
	defer ts.Close()

	data := map[string]string{
		"name": "Resty",
	}
	c := dcnl().SetDebug(true)
	req := c.R().
		SetBody(data).
		SetCookies(
			[]*http.Cookie{
				{Name: "count", Value: "1"},
			},
		)

	url := ts.URL + "/curl-cmd-post"
	resp, err := req.
		SetCurlCmdGenerate(true).
		Post(url)
	if err != nil {
		t.Fatal(err)
	}
	curlCmdExecuted := resp.Request.CurlCmd()

	c.SetCurlCmdGenerate(false)
	req.SetCurlCmdGenerate(false)
	if !strings.Contains(curlCmdExecuted, "Cookie: count=1") ||
		!strings.Contains(curlCmdExecuted, "curl -X POST") ||
		!strings.Contains(curlCmdExecuted, `-d '{"name":"Resty"}'`) ||
		!strings.Contains(curlCmdExecuted, url) {
		t.Fatal("Incomplete curl:", curlCmdExecuted)
	} else {
		t.Log("curlCmdExecuted: \n", curlCmdExecuted)
	}
}

func TestCurlCmdDebugMode(t *testing.T) {
	ts := createPostServer(t)
	defer ts.Close()

	c, logBuf := dcldb()
	c.SetCurlCmdGenerate(true).
		SetCurlCmdDebugLog(true)

	// Build request
	req := c.R().
		SetBody(map[string]string{
			"name": "Resty",
		}).
		SetCookies(
			[]*http.Cookie{
				{Name: "count", Value: "1"},
			},
		).
		SetCurlCmdDebugLog(true)

	// Execute request: set debug mode
	url := ts.URL + "/curl-cmd-post"
	_, err := req.SetDebug(true).Post(url)
	if err != nil {
		t.Fatal(err)
	}

	c.SetCurlCmdGenerate(false)
	req.SetCurlCmdGenerate(false)

	// test logContent curl cmd
	logContent := logBuf.String()
	if !strings.Contains(logContent, "Cookie: count=1") ||
		!strings.Contains(logContent, `-d '{"name":"Resty"}'`) {
		t.Fatal("Incomplete debug curl info:", logContent)
	}
}

func TestCurl_buildCurlCmd(t *testing.T) {
	tests := []struct {
		name     string
		method   string
		url      string
		headers  map[string]string
		body     string
		cookies  []*http.Cookie
		expected string
	}{
		{
			name:     "With Headers",
			method:   "GET",
			url:      "http://example.com",
			headers:  map[string]string{"Content-Type": "application/json", "Authorization": "Bearer token"},
			expected: "curl -X GET -H 'Authorization: Bearer token' -H 'Content-Type: application/json' http://example.com",
		},
		{
			name:     "With Body",
			method:   "POST",
			url:      "http://example.com",
			headers:  map[string]string{"Content-Type": "application/json"},
			body:     `{"key":"value"}`,
			expected: "curl -X POST -H 'Content-Type: application/json' -d '{\"key\":\"value\"}' http://example.com",
		},
		{
			name:     "With Empty Body",
			method:   "POST",
			url:      "http://example.com",
			headers:  map[string]string{"Content-Type": "application/json"},
			expected: "curl -X POST -H 'Content-Type: application/json' http://example.com",
		},
		{
			name:     "With Query Params",
			method:   "GET",
			url:      "http://example.com?param1=value1&param2=value2",
			expected: "curl -X GET 'http://example.com?param1=value1&param2=value2'",
		},
		{
			name:     "With Special Characters in URL",
			method:   "GET",
			url:      "http://example.com/path with spaces",
			expected: "curl -X GET http://example.com/path%20with%20spaces",
		},
		{
			name:     "With Cookies",
			method:   "GET",
			url:      "http://example.com",
			cookies:  []*http.Cookie{{Name: "session_id", Value: "abc123"}},
			expected: "curl -X GET -H 'Cookie: session_id=abc123' http://example.com",
		},
		{
			name:     "Without Cookies",
			method:   "GET",
			url:      "http://example.com",
			expected: "curl -X GET http://example.com",
		},
		{
			name:     "With Multiple Cookies",
			method:   "GET",
			url:      "http://example.com",
			cookies:  []*http.Cookie{{Name: "session_id", Value: "abc123"}, {Name: "user_id", Value: "user456"}},
			expected: "curl -X GET -H 'Cookie: session_id=abc123&user_id=user456' http://example.com",
		},
		{
			name:     "With Empty Cookie Jar",
			method:   "GET",
			url:      "http://example.com",
			expected: "curl -X GET http://example.com",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := dcnl()
			req := c.R().SetMethod(tt.method).SetURL(tt.url)

			if !isStringEmpty(tt.body) {
				req.SetBody(bytes.NewBufferString(tt.body))
			}

			for k, v := range tt.headers {
				req.SetHeader(k, v)
			}

			err := createRawRequest(c, req)
			assertNil(t, err)

			if len(tt.cookies) > 0 {
				cookieJar, _ := cookiejar.New(nil)
				cookieJar.SetCookies(req.RawRequest.URL, tt.cookies)
				c.SetCookieJar(cookieJar)
			}

			curlCmd := buildCurlCmd(req)
			assertEqual(t, tt.expected, curlCmd)
		})
	}
}

func TestCurlRequestGetBodyError(t *testing.T) {
	c := dcnl().
		SetDebug(true).
		SetRequestMiddlewares(
			MiddlewareRequestCreate,
			func(_ *Client, r *Request) error {
				r.RawRequest.GetBody = func() (io.ReadCloser, error) {
					return nil, errors.New("test case error")
				}
				return nil
			},
		)

	req := c.R().
		SetBody(map[string]string{
			"name": "Resty",
		}).
		SetCookies(
			[]*http.Cookie{
				{Name: "count", Value: "1"},
			},
		).
		SetMethod(MethodPost)

	assertEqual(t, "", req.CurlCmd())

	curlCmdUnexecuted := req.SetCurlCmdGenerate(true).CurlCmd()
	req.SetCurlCmdGenerate(false)

	if !strings.Contains(curlCmdUnexecuted, "Cookie: count=1") ||
		!strings.Contains(curlCmdUnexecuted, "curl -X POST") ||
		!strings.Contains(curlCmdUnexecuted, `-d ''`) {
		t.Fatal("Incomplete curl:", curlCmdUnexecuted)
	} else {
		t.Log("curlCmdUnexecuted: \n", curlCmdUnexecuted)
	}
}

func TestCurlRequestMiddlewaresError(t *testing.T) {
	errMsg := "middleware error"
	c := dcnl().SetDebug(true).
		SetRequestMiddlewares(
			func(c *Client, r *Request) error {
				return errors.New(errMsg)
			},
			MiddlewareRequestCreate,
		)

	curlCmdUnexecuted := c.R().SetCurlCmdGenerate(true).CurlCmd()
	assertEqual(t, "", curlCmdUnexecuted)
}

func TestCurlMiscTestCoverage(t *testing.T) {
	cookieStr := dumpCurlCookies([]*http.Cookie{
		{Name: "count", Value: "1"},
	})
	assertEqual(t, "Cookie: count=1", cookieStr)
}


================================================
FILE: debug.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"fmt"
	"net/http"
	"time"
)

type (
	// DebugLogCallbackFunc function type is for request and response debug log callback purposes.
	// It gets called before Resty logs it
	DebugLogCallbackFunc func(*DebugLog)

	// DebugLogFormatterFunc function type is used to implement debug log formatting.
	// See out of the box [DebugLogStringFormatter], [DebugLogJSONFormatter]
	DebugLogFormatterFunc func(*DebugLog) string

	// DebugLog struct is used to collect details from Resty request and response
	// for debug logging callback purposes.
	DebugLog struct {
		Request   *DebugLogRequest  `json:"request"`
		Response  *DebugLogResponse `json:"response"`
		TraceInfo *TraceInfo        `json:"trace_info"`
	}

	// DebugLogRequest type used to capture debug info about the [Request].
	DebugLogRequest struct {
		CorrelationID string      `json:"correlation_id"`
		Host          string      `json:"host"`
		URI           string      `json:"uri"`
		Method        string      `json:"method"`
		Proto         string      `json:"proto"`
		Header        http.Header `json:"header"`
		CurlCmd       string      `json:"curl_cmd"`
		Attempt       int         `json:"attempt"`
		Body          string      `json:"body"`
	}

	// DebugLogResponse type used to capture debug info about the [Response].
	DebugLogResponse struct {
		StatusCode int           `json:"status_code"`
		Status     string        `json:"status"`
		Proto      string        `json:"proto"`
		ReceivedAt time.Time     `json:"received_at"`
		Duration   time.Duration `json:"duration"`
		Size       int64         `json:"size"`
		Header     http.Header   `json:"header"`
		Body       string        `json:"body"`
	}
)

// DebugLogFormatter function formats the given debug log info in human readable
// format.
//
// This is the default debug log formatter in the Resty.
func DebugLogFormatter(dl *DebugLog) string {
	debugLog := "\n==============================================================================\n"

	req := dl.Request
	if len(req.CurlCmd) > 0 {
		debugLog += "~~~ REQUEST(CURL) ~~~\n" +
			fmt.Sprintf("	%v\n", req.CurlCmd)
	}
	debugLog += "~~~ REQUEST ~~~\n" +
		fmt.Sprintf("CORRELATION ID: %s\n", req.CorrelationID) +
		fmt.Sprintf("%s  %s  %s\n", req.Method, req.URI, req.Proto) +
		fmt.Sprintf("HOST   : %s\n", req.Host) +
		fmt.Sprintf("HEADERS:\n%s\n", composeHeaders(req.Header)) +
		fmt.Sprintf("BODY   :\n%v\n", req.Body) +
		fmt.Sprintf("ATTEMPT       : %d\n", req.Attempt) +
		"------------------------------------------------------------------------------\n"

	res := dl.Response
	debugLog += "~~~ RESPONSE ~~~\n" +
		fmt.Sprintf("STATUS       : %s\n", res.Status) +
		fmt.Sprintf("PROTO        : %s\n", res.Proto) +
		fmt.Sprintf("RECEIVED AT  : %v\n", res.ReceivedAt.Format(time.RFC3339Nano)) +
		fmt.Sprintf("DURATION     : %v\n", res.Duration) +
		"HEADERS      :\n" +
		composeHeaders(res.Header) + "\n" +
		fmt.Sprintf("BODY         :\n%v\n", res.Body)
	if dl.TraceInfo != nil {
		debugLog += "------------------------------------------------------------------------------\n"
		debugLog += fmt.Sprintf("%v\n", dl.TraceInfo)
	}
	debugLog += "==============================================================================\n"

	return debugLog
}

// DebugLogJSONFormatter function formats the given debug log info in JSON format.
func DebugLogJSONFormatter(dl *DebugLog) string {
	return toJSON(dl)
}

func debugLogger(c *Client, res *Response) {
	req := res.Request
	if !req.IsDebug {
		return
	}

	rdl := &DebugLogResponse{
		StatusCode: res.StatusCode(),
		Status:     res.Status(),
		Proto:      res.Proto(),
		ReceivedAt: res.ReceivedAt(),
		Duration:   res.Duration(),
		Size:       res.Size(),
		Header:     sanitizeHeaders(res.Header().Clone()),
		Body:       res.fmtBodyString(res.Request.DebugBodyLimit),
	}

	dl := &DebugLog{
		Request:  req.values[debugRequestLogKey].(*DebugLogRequest),
		Response: rdl,
	}

	if res.Request.IsTrace {
		ti := req.TraceInfo()
		dl.TraceInfo = &ti
	}

	dblCallback := c.debugLogCallbackFunc()
	if dblCallback != nil {
		dblCallback(dl)
	}

	formatterFunc := c.debugLogFormatterFunc()
	if formatterFunc != nil {
		debugLog := formatterFunc(dl)
		req.log.Debugf("%s", debugLog)
	}
}

const debugRequestLogKey = "__restyDebugRequestLog"

func prepareRequestDebugInfo(c *Client, r *Request) {
	if !r.IsDebug {
		return
	}

	rr := r.RawRequest
	rh := rr.Header.Clone()
	if c.Client().Jar != nil {
		for _, cookie := range c.Client().Jar.Cookies(r.RawRequest.URL) {
			s := fmt.Sprintf("%s=%s", cookie.Name, cookie.Value)
			if c := rh.Get(hdrCookieKey); isStringEmpty(c) {
				rh.Set(hdrCookieKey, s)
			} else {
				rh.Set(hdrCookieKey, c+"; "+s)
			}
		}
	}

	rdl := &DebugLogRequest{
		CorrelationID: r.CorrelationID,
		Host:          rr.URL.Host,
		URI:           rr.URL.RequestURI(),
		Method:        r.Method,
		Proto:         rr.Proto,
		Header:        sanitizeHeaders(rh),
		Attempt:       r.Attempt,
		Body:          r.fmtBodyString(r.DebugBodyLimit),
	}
	if r.isCurlCmdGenerate && r.isCurlCmdDebugLog {
		rdl.CurlCmd = r.curlCmdString
	}

	r.initValuesMap()
	r.values[debugRequestLogKey] = rdl
}


================================================
FILE: digest.go
================================================
// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.
// 2023 Segev Dagan (https://github.com/segevda)
// 2024 Philipp Wolfer (https://github.com/phw)
// resty source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// SPDX-License-Identifier: MIT

package resty

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"errors"
	"fmt"
	"hash"
	"io"
	"net/http"
	"strconv"
	"strings"
)

var (
	ErrDigestBadChallenge    = errors.New("resty: digest: challenge is bad")
	ErrDigestInvalidCharset  = errors.New("resty: digest: invalid charset")
	ErrDigestAlgNotSupported = errors.New("resty: digest: algorithm is not supported")
	ErrDigestQopNotSupported = errors.New("resty: digest: qop is not supported")
)

// Reference: https://datatracker.ietf.org/doc/html/rfc7616#section-6.1
var digestHashFuncs = map[string]func() hash.Hash{
	"":                 md5.New,
	"MD5":              md5.New,
	"MD5-sess":         md5.New,
	"SHA-256":          sha256.New,
	"SHA-256-sess":     sha256.New,
	"SHA-512":          sha512.New,
	"SHA-512-sess":     sha512.New,
	"SHA-512-256":      sha512.New512_256,
	"SHA-512-256-sess": sha512.New512_256,
}

const (
	qopAuth    = "auth"
	qopAuthInt = "auth-int"
)

type digestTransport struct {
	*credentials
	transport http.RoundTripper
}

func (dt *digestTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	// first request without body for all HTTP verbs
	req1 := dt.cloneReq(req, true)

	// make a request to get the 401 that contains the challenge.
	res, err := dt.transport.RoundTrip(req1)
	if err != nil {
		return nil, err
	}
	if res.StatusCode != http.StatusUnauthorized {
		return res, nil
	}
	_, _ = ioCopy(io.Discard, res.Body)
	closeq(res.Body)

	chaHdrValue := strings.TrimSpace(res.Header.Get(hdrWwwAuthenticateKey))
	if chaHdrValue == "" {
		return nil, ErrDigestBadChallenge
	}

	cha, err := dt.parseChallenge(chaHdrValue)
	if err != nil {
		return nil, err
	}

	// prepare second request
	req2 := dt.cloneReq(req, false)
	cred, err := dt.createCredentials(cha, req2)
	if err != nil {
		return nil, err
	}

	auth, err := cred.digest(cha)
	if err != nil {
		return nil, err
	}

	req2.Header.Set(hdrAuthorizationKey, auth)
	return dt.transport.RoundTrip(req2)
}

func (dt *digestTransport) cloneReq(r *http.Request, first bool) *http.Request {
	r1 := r.Clone(r.Context())
	if first {
		r1.Body = http.NoBody
		r1.ContentLength = 0
		r1.GetBody = nil
	}
	return r1
}

func (dt *digestTransport) parseChallenge(input string) (*digestChallenge, error) {
	const ws = " \n\r\t"
	s := strings.Trim(input, ws)
	if !strings.HasPrefix(s, "Digest ") {
		return nil, ErrDigestBadChallenge
	}

	s = strings.Trim(s[7:], ws)
	c := &digestChallenge{}
	b := strings.Builder{}
	key := ""
	quoted := false
	for _, r := range s {
		switch r {
		case '"':
			quoted = !quoted
		case ',':
			if quoted {
				b.WriteRune(r)
			} else {
				val := strings.Trim(b.String(), ws)
				b.Reset()
				if err := c.setValue(key, val); err != nil {
					return nil, err
				}
				key = ""
			}
		case '=':
			if quoted {
				b.WriteRune(r)
			} else {
				key = strings.Trim(b.String(), ws)
				b.Reset()
			}
		default:
			b.WriteRune(r)
		}
	}

	key = strings.TrimSpace(key)
	if quoted || (key == "" && b.Len() > 0) {
		return nil, ErrDigestBadChallenge
	}

	if key != "" {
		val := strings.Trim(b.String(), ws)
		if err := c.setValue(key, val); err != nil {
			return nil, err
		}
	}

	return c, nil
}

func (dt *digestTransport) createCredentials(cha *digestChallenge, req *http.Request) (*digestCredentials, error) {
	cred := &digestCredentials{
		username:      dt.Username,
		password:      dt.Password,
		uri:           req.URL.RequestURI(),
		method:        req.Method,
		realm:         cha.realm,
		nonce:         cha.nonce,
		nc:            cha.nc,
		algorithm:     cha.algorithm,
		sessAlgorithm: strings.HasSuffix(cha.algorithm, "-sess"),
		opaque:        cha.opaque,
		userHash:      cha.userHash,
	}

	if cha.isQopSupported(qopAuthInt) {
		if err := dt.prepareBody(req); err != nil {
			return nil, fmt.Errorf("resty: digest: failed to prepare body for auth-int: %w", err)
		}
		body, err := req.GetBody()
		if err != nil {
			return nil, fmt.Errorf("resty: digest: failed to get body for auth-int: %w", err)
		}
		if body != http.NoBody {
			defer closeq(body)
			h := newHashFunc(cha.algorithm)
			if _, e
Download .txt
gitextract_43qx_89h/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── ci.yml
│       └── label-actions.yml
├── .gitignore
├── .testdata/
│   ├── cert.pem
│   ├── key.pem
│   ├── sample-root.pem
│   └── text-file.txt
├── BUILD.bazel
├── LICENSE
├── README.md
├── WORKSPACE
├── benchmark_test.go
├── cert_watcher_test.go
├── circuit_breaker.go
├── circuit_breaker_test.go
├── client.go
├── client_test.go
├── context_test.go
├── curl.go
├── curl_test.go
├── debug.go
├── digest.go
├── digest_test.go
├── go.mod
├── go.sum
├── hedging.go
├── hedging_test.go
├── load_balancer.go
├── load_balancer_test.go
├── middleware.go
├── middleware_test.go
├── multipart.go
├── multipart_test.go
├── redirect.go
├── request.go
├── request_test.go
├── response.go
├── resty.go
├── resty_test.go
├── retry.go
├── retry_test.go
├── sse.go
├── sse_test.go
├── stream.go
├── stream_test.go
├── trace.go
├── transport_dial.go
├── transport_dial_wasm.go
├── util.go
└── util_test.go
Download .txt
SYMBOL INDEX (1073 symbols across 37 files)

FILE: benchmark_test.go
  function Benchmark_parseRequestURL_PathParams (line 15) | func Benchmark_parseRequestURL_PathParams(b *testing.B) {
  function Benchmark_parseRequestURL_QueryParams (line 39) | func Benchmark_parseRequestURL_QueryParams(b *testing.B) {
  function Benchmark_parseRequestHeader (line 57) | func Benchmark_parseRequestHeader(b *testing.B) {
  function Benchmark_parseRequestBody_string (line 76) | func Benchmark_parseRequestBody_string(b *testing.B) {
  function Benchmark_parseRequestBody_byte (line 88) | func Benchmark_parseRequestBody_byte(b *testing.B) {
  function Benchmark_parseRequestBody_reader (line 100) | func Benchmark_parseRequestBody_reader(b *testing.B) {
  function Benchmark_parseRequestBody_struct (line 112) | func Benchmark_parseRequestBody_struct(b *testing.B) {
  function Benchmark_parseRequestBody_struct_xml (line 128) | func Benchmark_parseRequestBody_struct_xml(b *testing.B) {
  function Benchmark_parseRequestBody_map (line 144) | func Benchmark_parseRequestBody_map(b *testing.B) {
  function Benchmark_parseRequestBody_slice (line 159) | func Benchmark_parseRequestBody_slice(b *testing.B) {
  function Benchmark_parseRequestBody_FormData (line 171) | func Benchmark_parseRequestBody_FormData(b *testing.B) {
  function Benchmark_parseRequestBody_MultiPart (line 184) | func Benchmark_parseRequestBody_MultiPart(b *testing.B) {
  type benchmarkStringer (line 208) | type benchmarkStringer struct
    method String (line 212) | func (s benchmarkStringer) String() string {
  function Benchmark_formatAnyToString_string (line 217) | func Benchmark_formatAnyToString_string(b *testing.B) {
  function Benchmark_formatAnyToString_int (line 224) | func Benchmark_formatAnyToString_int(b *testing.B) {
  function Benchmark_formatAnyToString_bool (line 231) | func Benchmark_formatAnyToString_bool(b *testing.B) {
  function Benchmark_formatAnyToString_int64 (line 238) | func Benchmark_formatAnyToString_int64(b *testing.B) {
  function Benchmark_formatAnyToString_stringSlice (line 245) | func Benchmark_formatAnyToString_stringSlice(b *testing.B) {
  function Benchmark_formatAnyToString_time (line 253) | func Benchmark_formatAnyToString_time(b *testing.B) {
  function Benchmark_formatAnyToString_byteSlice (line 260) | func Benchmark_formatAnyToString_byteSlice(b *testing.B) {
  function Benchmark_formatAnyToString_float64 (line 267) | func Benchmark_formatAnyToString_float64(b *testing.B) {
  function Benchmark_formatAnyToString_int32 (line 275) | func Benchmark_formatAnyToString_int32(b *testing.B) {
  function Benchmark_formatAnyToString_int16 (line 282) | func Benchmark_formatAnyToString_int16(b *testing.B) {
  function Benchmark_formatAnyToString_int8 (line 289) | func Benchmark_formatAnyToString_int8(b *testing.B) {
  function Benchmark_formatAnyToString_uint64 (line 297) | func Benchmark_formatAnyToString_uint64(b *testing.B) {
  function Benchmark_formatAnyToString_uint32 (line 304) | func Benchmark_formatAnyToString_uint32(b *testing.B) {
  function Benchmark_formatAnyToString_uint16 (line 311) | func Benchmark_formatAnyToString_uint16(b *testing.B) {
  function Benchmark_formatAnyToString_uint8 (line 318) | func Benchmark_formatAnyToString_uint8(b *testing.B) {
  function Benchmark_formatAnyToString_uint (line 325) | func Benchmark_formatAnyToString_uint(b *testing.B) {
  function Benchmark_formatAnyToString_float32 (line 333) | func Benchmark_formatAnyToString_float32(b *testing.B) {
  function Benchmark_formatAnyToString_stringer (line 340) | func Benchmark_formatAnyToString_stringer(b *testing.B) {
  function Benchmark_formatAnyToString_default (line 347) | func Benchmark_formatAnyToString_default(b *testing.B) {

FILE: cert_watcher_test.go
  type certPaths (line 24) | type certPaths struct
  function TestClient_SetRootCertificateWatcher (line 31) | func TestClient_SetRootCertificateWatcher(t *testing.T) {
  function generateCerts (line 121) | func generateCerts(t *testing.T, paths certPaths) {
  function generateRootCA (line 133) | func generateRootCA(keyPath, certPath string) (*rsa.PrivateKey, []byte, ...
  function generateTLSCert (line 184) | func generateTLSCert(keyPath, certPath string, rootKey *rsa.PrivateKey, ...
  function generateKey (line 229) | func generateKey() (*rsa.PrivateKey, error) {
  function savePEMKey (line 233) | func savePEMKey(fileName string, key *rsa.PrivateKey) error {
  function savePEMCert (line 248) | func savePEMCert(fileName string, cert []byte) error {

FILE: circuit_breaker.go
  type CircuitBreakerTriggerHook (line 21) | type CircuitBreakerTriggerHook
  type CircuitBreakerStateChangeHook (line 24) | type CircuitBreakerStateChangeHook
  type CircuitBreakerState (line 27) | type CircuitBreakerState
  type group (line 31) | type group interface
  type totalAndFailures (line 38) | type totalAndFailures struct
    method op (line 43) | func (tf totalAndFailures) op(g totalAndFailures) totalAndFailures {
    method empty (line 49) | func (tf totalAndFailures) empty() totalAndFailures {
    method inverse (line 53) | func (tf totalAndFailures) inverse() totalAndFailures {
  type slidingWindow (line 60) | type slidingWindow struct
  function newSlidingWindow (line 69) | func newSlidingWindow[G group[G]](empty func() G, interval time.Duration...
  method Add (line 79) | func (sw *slidingWindow[G]) Add(val G) {
  method Get (line 113) | func (sw *slidingWindow[G]) Get() G {
  method SetInterval (line 119) | func (sw *slidingWindow[G]) SetInterval(interval time.Duration) {
  constant CircuitBreakerStateClosed (line 127) | CircuitBreakerStateClosed CircuitBreakerState = iota
  constant CircuitBreakerStateOpen (line 130) | CircuitBreakerStateOpen
  constant CircuitBreakerStateHalfOpen (line 133) | CircuitBreakerStateHalfOpen
  type CircuitBreaker (line 150) | type CircuitBreaker struct
    method OnTrigger (line 215) | func (cb *CircuitBreaker) OnTrigger(hooks ...CircuitBreakerTriggerHook...
    method onTriggerHooks (line 223) | func (cb *CircuitBreaker) onTriggerHooks(req *Request, err error) {
    method OnStateChange (line 232) | func (cb *CircuitBreaker) OnStateChange(hooks ...CircuitBreakerStateCh...
    method onStateChangeHooks (line 240) | func (cb *CircuitBreaker) onStateChangeHooks(oldState, newState Circui...
    method getState (line 258) | func (cb *CircuitBreaker) getState() CircuitBreakerState {
    method allow (line 262) | func (cb *CircuitBreaker) allow() error {
    method applyPolicies (line 270) | func (cb *CircuitBreaker) applyPolicies(resp *http.Response) {
    method open (line 318) | func (cb *CircuitBreaker) open() {
    method changeState (line 326) | func (cb *CircuitBreaker) changeState(state CircuitBreakerState) {
  function NewCircuitBreakerWithCount (line 175) | func NewCircuitBreakerWithCount(failureThreshold uint64, successThreshol...
  function NewCircuitBreakerWithRatio (line 187) | func NewCircuitBreakerWithRatio(failureRatio float64, minRequests uint64,
  function newCircuitBreaker (line 196) | func newCircuitBreaker(resetTimeout time.Duration, policies ...CircuitBr...
  type CircuitBreakerPolicy (line 250) | type CircuitBreakerPolicy
  function CircuitBreaker5xxPolicy (line 254) | func CircuitBreaker5xxPolicy(resp *http.Response) bool {

FILE: circuit_breaker_test.go
  function TestCircuitBreakerCountBased (line 18) | func TestCircuitBreakerCountBased(t *testing.T) {
  function TestCircuitBreaker5xxPolicy (line 82) | func TestCircuitBreaker5xxPolicy(t *testing.T) {
  function TestCircuitBreakerCountBasedOpensAndAllow (line 90) | func TestCircuitBreakerCountBasedOpensAndAllow(t *testing.T) {
  function TestCircuitBreakerCountBasedHalfOpenToClosedOnSuccess (line 123) | func TestCircuitBreakerCountBasedHalfOpenToClosedOnSuccess(t *testing.T) {
  function TestCircuitBreakerRatioBasedOpenToClosed (line 153) | func TestCircuitBreakerRatioBasedOpenToClosed(t *testing.T) {
  function TestCircuitBreakerNewStateAndPolicies (line 181) | func TestCircuitBreakerNewStateAndPolicies(t *testing.T) {
  function TestCircuitBreakerChangeStateClearsCounts (line 190) | func TestCircuitBreakerChangeStateClearsCounts(t *testing.T) {
  function TestCircuitBreakerAllowDuringHalfOpen (line 203) | func TestCircuitBreakerAllowDuringHalfOpen(t *testing.T) {
  function TestCircuitBreakerOnTriggerHooks (line 215) | func TestCircuitBreakerOnTriggerHooks(t *testing.T) {
  function TestCircuitBreakerOnStateChangeHooks (line 231) | func TestCircuitBreakerOnStateChangeHooks(t *testing.T) {
  function TestCircuitBreakerMultipleHooksAreCalled (line 249) | func TestCircuitBreakerMultipleHooksAreCalled(t *testing.T) {
  function TestCircuitBreakerConcurrentOnTriggerRegistration (line 267) | func TestCircuitBreakerConcurrentOnTriggerRegistration(t *testing.T) {
  function TestCircuitBreakerConcurrentOnStateChangeRegistration (line 289) | func TestCircuitBreakerConcurrentOnStateChangeRegistration(t *testing.T) {
  function TestCircuitBreakerSlidingWindow1SetInterval (line 311) | func TestCircuitBreakerSlidingWindow1SetInterval(t *testing.T) {
  function TestCircuitBreakerSlidingWindow2SetInterval (line 324) | func TestCircuitBreakerSlidingWindow2SetInterval(t *testing.T) {
  function TestCircuitBreakerSlidingWindowConcurrentAddGet (line 332) | func TestCircuitBreakerSlidingWindowConcurrentAddGet(t *testing.T) {
  function TestCircuitBreakerTotalAndFailuresOperations (line 350) | func TestCircuitBreakerTotalAndFailuresOperations(t *testing.T) {
  function TestCircuitBreakerSlidingWindowResetWhenElapsedExceedsBuckets (line 367) | func TestCircuitBreakerSlidingWindowResetWhenElapsedExceedsBuckets(t *te...

FILE: client.go
  constant MethodGet (line 28) | MethodGet = "GET"
  constant MethodPost (line 31) | MethodPost = "POST"
  constant MethodPut (line 34) | MethodPut = "PUT"
  constant MethodDelete (line 37) | MethodDelete = "DELETE"
  constant MethodPatch (line 40) | MethodPatch = "PATCH"
  constant MethodHead (line 43) | MethodHead = "HEAD"
  constant MethodOptions (line 46) | MethodOptions = "OPTIONS"
  constant MethodTrace (line 49) | MethodTrace = "TRACE"
  constant defaultWatcherPoolingInterval (line 53) | defaultWatcherPoolingInterval = 24 * time.Hour
  type RequestMiddleware (line 87) | type RequestMiddleware
  type ResponseMiddleware (line 90) | type ResponseMiddleware
  type ErrorHook (line 93) | type ErrorHook
  type SuccessHook (line 96) | type SuccessHook
  type CloseHook (line 99) | type CloseHook
  type RequestFunc (line 102) | type RequestFunc
  type TLSClientConfiger (line 106) | type TLSClientConfiger interface
  type TransportSettings (line 117) | type TransportSettings struct
  type Client (line 171) | type Client struct
    method BaseURL (line 245) | func (c *Client) BaseURL() string {
    method SetBaseURL (line 259) | func (c *Client) SetBaseURL(url string) *Client {
    method LoadBalancer (line 268) | func (c *Client) LoadBalancer() LoadBalancer {
    method SetLoadBalancer (line 275) | func (c *Client) SetLoadBalancer(b LoadBalancer) *Client {
    method Header (line 283) | func (c *Client) Header() http.Header {
    method SetHeader (line 300) | func (c *Client) SetHeader(header, value string) *Client {
    method SetHeaderAny (line 318) | func (c *Client) SetHeaderAny(header string, value any) *Client {
    method SetHeaders (line 338) | func (c *Client) SetHeaders(headers map[string]string) *Client {
    method SetHeaderVerbatim (line 358) | func (c *Client) SetHeaderVerbatim(header, value string) *Client {
    method SetHeaderVerbatimAny (line 376) | func (c *Client) SetHeaderVerbatimAny(header string, value any) *Client {
    method Context (line 385) | func (c *Client) Context() context.Context {
    method SetContext (line 393) | func (c *Client) SetContext(ctx context.Context) *Client {
    method CookieJar (line 401) | func (c *Client) CookieJar() http.CookieJar {
    method SetCookieJar (line 411) | func (c *Client) SetCookieJar(jar http.CookieJar) *Client {
    method Cookies (line 419) | func (c *Client) Cookies() []*http.Cookie {
    method SetCookie (line 432) | func (c *Client) SetCookie(hc *http.Cookie) *Client {
    method SetCookies (line 455) | func (c *Client) SetCookies(cs []*http.Cookie) *Client {
    method QueryParams (line 463) | func (c *Client) QueryParams() url.Values {
    method SetQueryParam (line 482) | func (c *Client) SetQueryParam(param, value string) *Client {
    method SetQueryParamAny (line 502) | func (c *Client) SetQueryParamAny(param string, value any) *Client {
    method SetQueryParams (line 524) | func (c *Client) SetQueryParams(params map[string]string) *Client {
    method FormData (line 533) | func (c *Client) FormData() url.Values {
    method SetFormData (line 550) | func (c *Client) SetFormData(data map[string]string) *Client {
    method SetBasicAuth (line 571) | func (c *Client) SetBasicAuth(username, password string) *Client {
    method AuthToken (line 579) | func (c *Client) AuthToken() string {
    method HeaderAuthorizationKey (line 586) | func (c *Client) HeaderAuthorizationKey() string {
    method SetHeaderAuthorizationKey (line 597) | func (c *Client) SetHeaderAuthorizationKey(k string) *Client {
    method SetAuthToken (line 617) | func (c *Client) SetAuthToken(token string) *Client {
    method AuthScheme (line 627) | func (c *Client) AuthScheme() string {
    method SetAuthScheme (line 650) | func (c *Client) SetAuthScheme(scheme string) *Client {
    method SetDigestAuth (line 674) | func (c *Client) SetDigestAuth(username, password string) *Client {
    method R (line 684) | func (c *Client) R() *Request {
    method NewRequest (line 734) | func (c *Client) NewRequest() *Request {
    method SetRequestMiddlewares (line 754) | func (c *Client) SetRequestMiddlewares(middlewares ...RequestMiddlewar...
    method SetResponseMiddlewares (line 779) | func (c *Client) SetResponseMiddlewares(middlewares ...ResponseMiddlew...
    method requestMiddlewares (line 786) | func (c *Client) requestMiddlewares() []RequestMiddleware {
    method AddRequestMiddleware (line 801) | func (c *Client) AddRequestMiddleware(m RequestMiddleware) *Client {
    method responseMiddlewares (line 809) | func (c *Client) responseMiddlewares() []ResponseMiddleware {
    method AddResponseMiddleware (line 826) | func (c *Client) AddResponseMiddleware(m ResponseMiddleware) *Client {
    method OnError (line 850) | func (c *Client) OnError(hooks ...ErrorHook) *Client {
    method OnSuccess (line 865) | func (c *Client) OnSuccess(hooks ...SuccessHook) *Client {
    method OnInvalid (line 880) | func (c *Client) OnInvalid(hooks ...ErrorHook) *Client {
    method OnPanic (line 898) | func (c *Client) OnPanic(hooks ...ErrorHook) *Client {
    method OnClose (line 907) | func (c *Client) OnClose(hooks ...CloseHook) *Client {
    method ContentTypeEncoders (line 915) | func (c *Client) ContentTypeEncoders() map[string]ContentTypeEncoder {
    method AddContentTypeEncoder (line 924) | func (c *Client) AddContentTypeEncoder(ct string, e ContentTypeEncoder...
    method inferContentTypeEncoder (line 931) | func (c *Client) inferContentTypeEncoder(ct ...string) (ContentTypeEnc...
    method ContentTypeDecoders (line 943) | func (c *Client) ContentTypeDecoders() map[string]ContentTypeDecoder {
    method AddContentTypeDecoder (line 952) | func (c *Client) AddContentTypeDecoder(ct string, d ContentTypeDecoder...
    method inferContentTypeDecoder (line 959) | func (c *Client) inferContentTypeDecoder(ct ...string) (ContentTypeDec...
    method ContentDecompressers (line 971) | func (c *Client) ContentDecompressers() map[string]ContentDecompresser {
    method AddContentDecompresser (line 983) | func (c *Client) AddContentDecompresser(k string, d ContentDecompresse...
    method ContentDecompresserKeys (line 996) | func (c *Client) ContentDecompresserKeys() string {
    method SetContentDecompresserKeys (line 1011) | func (c *Client) SetContentDecompresserKeys(keys []string) *Client {
    method SetCircuitBreaker (line 1032) | func (c *Client) SetCircuitBreaker(b *CircuitBreaker) *Client {
    method IsDebug (line 1040) | func (c *Client) IsDebug() bool {
    method SetDebug (line 1056) | func (c *Client) SetDebug(d bool) *Client {
    method DebugBodyLimit (line 1064) | func (c *Client) DebugBodyLimit() int {
    method SetDebugBodyLimit (line 1074) | func (c *Client) SetDebugBodyLimit(sl int) *Client {
    method debugLogCallbackFunc (line 1081) | func (c *Client) debugLogCallbackFunc() DebugLogCallbackFunc {
    method OnDebugLog (line 1089) | func (c *Client) OnDebugLog(dlc DebugLogCallbackFunc) *Client {
    method debugLogFormatterFunc (line 1100) | func (c *Client) debugLogFormatterFunc() DebugLogFormatterFunc {
    method SetDebugLogFormatter (line 1107) | func (c *Client) SetDebugLogFormatter(df DebugLogFormatterFunc) *Client {
    method IsDisableWarn (line 1115) | func (c *Client) IsDisableWarn() bool {
    method SetLoggerWarnLevel (line 1126) | func (c *Client) SetLoggerWarnLevel(d bool) *Client {
    method IsMethodGetAllowPayload (line 1135) | func (c *Client) IsMethodGetAllowPayload() bool {
    method SetMethodGetAllowPayload (line 1147) | func (c *Client) SetMethodGetAllowPayload(allow bool) *Client {
    method IsMethodDeleteAllowPayload (line 1158) | func (c *Client) IsMethodDeleteAllowPayload() bool {
    method SetMethodDeleteAllowPayload (line 1172) | func (c *Client) SetMethodDeleteAllowPayload(allow bool) *Client {
    method Logger (line 1180) | func (c *Client) Logger() Logger {
    method SetLogger (line 1189) | func (c *Client) SetLogger(l Logger) *Client {
    method Timeout (line 1197) | func (c *Client) Timeout() time.Duration {
    method SetTimeout (line 1210) | func (c *Client) SetTimeout(timeout time.Duration) *Client {
    method ResultError (line 1219) | func (c *Client) ResultError() reflect.Type {
    method SetResultError (line 1233) | func (c *Client) SetResultError(v any) *Client {
    method newErrorInterface (line 1240) | func (c *Client) newErrorInterface() any {
    method SetRedirectPolicy (line 1257) | func (c *Client) SetRedirectPolicy(policies ...RedirectPolicy) *Client {
    method RetryCount (line 1272) | func (c *Client) RetryCount() int {
    method SetRetryCount (line 1290) | func (c *Client) SetRetryCount(count int) *Client {
    method RetryWaitTime (line 1299) | func (c *Client) RetryWaitTime() time.Duration {
    method SetRetryWaitTime (line 1308) | func (c *Client) SetRetryWaitTime(waitTime time.Duration) *Client {
    method RetryMaxWaitTime (line 1317) | func (c *Client) RetryMaxWaitTime() time.Duration {
    method SetRetryMaxWaitTime (line 1326) | func (c *Client) SetRetryMaxWaitTime(maxWaitTime time.Duration) *Client {
    method RetryDelayStrategy (line 1337) | func (c *Client) RetryDelayStrategy() RetryDelayStrategyFunc {
    method SetRetryDelayStrategy (line 1348) | func (c *Client) SetRetryDelayStrategy(rs RetryDelayStrategyFunc) *Cli...
    method IsRetryDefaultConditions (line 1359) | func (c *Client) IsRetryDefaultConditions() bool {
    method SetRetryDefaultConditions (line 1369) | func (c *Client) SetRetryDefaultConditions(b bool) *Client {
    method IsRetryAllowNonIdempotent (line 1380) | func (c *Client) IsRetryAllowNonIdempotent() bool {
    method SetRetryAllowNonIdempotent (line 1394) | func (c *Client) SetRetryAllowNonIdempotent(b bool) *Client {
    method RetryConditions (line 1402) | func (c *Client) RetryConditions() []RetryConditionFunc {
    method AddRetryConditions (line 1420) | func (c *Client) AddRetryConditions(conditions ...RetryConditionFunc) ...
    method RetryHooks (line 1428) | func (c *Client) RetryHooks() []RetryHookFunc {
    method AddRetryHooks (line 1442) | func (c *Client) AddRetryHooks(hooks ...RetryHookFunc) *Client {
    method isHedgingEnabled (line 1450) | func (c *Client) isHedgingEnabled() bool {
    method Hedging (line 1458) | func (c *Client) Hedging() *Hedging {
    method SetHedging (line 1467) | func (c *Client) SetHedging(h *Hedging) *Client {
    method TLSClientConfig (line 1512) | func (c *Client) TLSClientConfig() *tls.Config {
    method SetTLSClientConfig (line 1530) | func (c *Client) SetTLSClientConfig(tlsConfig *tls.Config) *Client {
    method ProxyURL (line 1554) | func (c *Client) ProxyURL() *url.URL {
    method SetProxy (line 1569) | func (c *Client) SetProxy(proxyURL string) *Client {
    method RemoveProxy (line 1592) | func (c *Client) RemoveProxy() *Client {
    method SetCertificateFromFile (line 1610) | func (c *Client) SetCertificateFromFile(certFilePath, certKeyFilePath ...
    method SetCertificateFromString (line 1632) | func (c *Client) SetCertificateFromString(certStr, certKeyStr string) ...
    method SetCertificates (line 1652) | func (c *Client) SetCertificates(certs ...tls.Certificate) *Client {
    method SetRootCertificates (line 1680) | func (c *Client) SetRootCertificates(pemFilePaths ...string) *Client {
    method SetRootCertificatesWatcher (line 1701) | func (c *Client) SetRootCertificatesWatcher(options *CertWatcherOption...
    method SetRootCertificateFromString (line 1717) | func (c *Client) SetRootCertificateFromString(pemCerts string) *Client {
    method SetClientRootCertificates (line 1737) | func (c *Client) SetClientRootCertificates(pemFilePaths ...string) *Cl...
    method SetClientRootCertificatesWatcher (line 1758) | func (c *Client) SetClientRootCertificatesWatcher(options *CertWatcher...
    method SetClientRootCertificateFromString (line 1774) | func (c *Client) SetClientRootCertificateFromString(pemCerts string) *...
    method handleCAs (line 1779) | func (c *Client) handleCAs(scope string, permCerts []byte) {
    method initCertWatcher (line 1802) | func (c *Client) initCertWatcher(pemFilePath, scope string, options *C...
    method ResponseSaveDirectory (line 1857) | func (c *Client) ResponseSaveDirectory() string {
    method SetResponseSaveDirectory (line 1868) | func (c *Client) SetResponseSaveDirectory(dirPath string) *Client {
    method IsResponseSaveToFile (line 1876) | func (c *Client) IsResponseSaveToFile() bool {
    method SetResponseSaveToFile (line 1894) | func (c *Client) SetResponseSaveToFile(save bool) *Client {
    method HTTPTransport (line 1903) | func (c *Client) HTTPTransport() (*http.Transport, error) {
    method Transport (line 1914) | func (c *Client) Transport() http.RoundTripper {
    method SetTransport (line 1936) | func (c *Client) SetTransport(transport http.RoundTripper) *Client {
    method Scheme (line 1948) | func (c *Client) Scheme() string {
    method SetScheme (line 1957) | func (c *Client) SetScheme(scheme string) *Client {
    method SetCloseConnection (line 1970) | func (c *Client) SetCloseConnection(close bool) *Client {
    method SetResponseDoNotParse (line 1985) | func (c *Client) SetResponseDoNotParse(notParse bool) *Client {
    method PathParams (line 1995) | func (c *Client) PathParams() map[string]string {
    method SetPathParam (line 2015) | func (c *Client) SetPathParam(param, value string) *Client {
    method SetPathParamAny (line 2039) | func (c *Client) SetPathParamAny(param string, value any) *Client {
    method SetPathParams (line 2065) | func (c *Client) SetPathParams(params map[string]string) *Client {
    method SetPathRawParam (line 2086) | func (c *Client) SetPathRawParam(param, value string) *Client {
    method SetPathRawParamAny (line 2110) | func (c *Client) SetPathRawParamAny(param string, value any) *Client {
    method SetPathRawParams (line 2136) | func (c *Client) SetPathRawParams(params map[string]string) *Client {
    method SetJSONEscapeHTML (line 2149) | func (c *Client) SetJSONEscapeHTML(b bool) *Client {
    method ResponseBodyLimit (line 2158) | func (c *Client) ResponseBodyLimit() int64 {
    method SetResponseBodyLimit (line 2175) | func (c *Client) SetResponseBodyLimit(v int64) *Client {
    method IsTrace (line 2183) | func (c *Client) IsTrace() bool {
    method SetTrace (line 2199) | func (c *Client) SetTrace(t bool) *Client {
    method SetCurlCmdGenerate (line 2220) | func (c *Client) SetCurlCmdGenerate(b bool) *Client {
    method SetCurlCmdDebugLog (line 2230) | func (c *Client) SetCurlCmdDebugLog(b bool) *Client {
    method SetQueryParamsUnescape (line 2243) | func (c *Client) SetQueryParamsUnescape(unescape bool) *Client {
    method ResponseBodyUnlimitedReads (line 2251) | func (c *Client) ResponseBodyUnlimitedReads() bool {
    method SetResponseBodyUnlimitedReads (line 2267) | func (c *Client) SetResponseBodyUnlimitedReads(b bool) *Client {
    method IsProxySet (line 2276) | func (c *Client) IsProxySet() bool {
    method Client (line 2281) | func (c *Client) Client() *http.Client {
    method Clone (line 2295) | func (c *Client) Clone(ctx context.Context) *Client {
    method Close (line 2332) | func (c *Client) Close() error {
    method executeRequestMiddlewares (line 2344) | func (c *Client) executeRequestMiddlewares(req *Request) (err error) {
    method execute (line 2355) | func (c *Client) execute(req *Request) (*Response, error) {
    method tlsConfig (line 2428) | func (c *Client) tlsConfig() (*tls.Config, error) {
    method outputLogTo (line 2448) | func (c *Client) outputLogTo(w io.Writer) *Client {
    method onErrorHooks (line 2471) | func (c *Client) onErrorHooks(req *Request, res *Response, err error) {
    method onPanicHooks (line 2489) | func (c *Client) onPanicHooks(req *Request, err error) {
    method onInvalidHooks (line 2498) | func (c *Client) onInvalidHooks(req *Request, err error) {
    method onCloseHooks (line 2507) | func (c *Client) onCloseHooks() {
    method debugf (line 2515) | func (c *Client) debugf(format string, v ...any) {
  type CertWatcherOptions (line 234) | type CertWatcherOptions struct
  type ResponseError (line 2455) | type ResponseError struct
    method Error (line 2460) | func (e *ResponseError) Error() string {
    method Unwrap (line 2464) | func (e *ResponseError) Unwrap() error {

FILE: client_test.go
  function TestClientBasicAuth (line 33) | func TestClientBasicAuth(t *testing.T) {
  function TestClientAuthToken (line 53) | func TestClientAuthToken(t *testing.T) {
  function TestClientAuthScheme (line 68) | func TestClientAuthScheme(t *testing.T) {
  function TestClientResponseMiddleware (line 93) | func TestClientResponseMiddleware(t *testing.T) {
  function TestClientRedirectPolicy (line 114) | func TestClientRedirectPolicy(t *testing.T) {
  function TestClientTimeout (line 141) | func TestClientTimeout(t *testing.T) {
  function TestClientTimeoutWithinThreshold (line 150) | func TestClientTimeoutWithinThreshold(t *testing.T) {
  function TestClientTimeoutInternalError (line 169) | func TestClientTimeoutInternalError(t *testing.T) {
  function TestClientProxy (line 174) | func TestClientProxy(t *testing.T) {
  function TestClientSetCertificates (line 195) | func TestClientSetCertificates(t *testing.T) {
  function TestClientSetRootCertificate (line 226) | func TestClientSetRootCertificate(t *testing.T) {
  type CustomRoundTripper1 (line 261) | type CustomRoundTripper1 struct
    method RoundTrip (line 264) | func (rt *CustomRoundTripper1) RoundTrip(_ *http.Request) (*http.Respo...
  function TestClientCACertificateFromStringErrorTls (line 268) | func TestClientCACertificateFromStringErrorTls(t *testing.T) {
  type CustomRoundTripper2 (line 305) | type CustomRoundTripper2 struct
    method RoundTrip (line 313) | func (rt *CustomRoundTripper2) RoundTrip(_ *http.Request) (*http.Respo...
    method TLSClientConfig (line 320) | func (rt *CustomRoundTripper2) TLSClientConfig() *tls.Config {
    method SetTLSClientConfig (line 323) | func (rt *CustomRoundTripper2) SetTLSClientConfig(tlsConfig *tls.Confi...
  function TestClientTLSConfigerInterface (line 331) | func TestClientTLSConfigerInterface(t *testing.T) {
  function TestClientSetClientRootCertificate (line 376) | func TestClientSetClientRootCertificate(t *testing.T) {
  function TestClientSetClientRootCertificateNotExists (line 386) | func TestClientSetClientRootCertificateNotExists(t *testing.T) {
  function TestClientSetClientRootCertificateWatcher (line 396) | func TestClientSetClientRootCertificateWatcher(t *testing.T) {
  function TestClientSetClientRootCertificateFromString (line 421) | func TestClientSetClientRootCertificateFromString(t *testing.T) {
  function TestClientRequestMiddlewareModification (line 434) | func TestClientRequestMiddlewareModification(t *testing.T) {
  function TestClientSetHeaderVerbatim (line 454) | func TestClientSetHeaderVerbatim(t *testing.T) {
  function TestClientSetHeaderAny (line 468) | func TestClientSetHeaderAny(t *testing.T) {
  function TestClientSetHeaderVerbatimAny (line 477) | func TestClientSetHeaderVerbatimAny(t *testing.T) {
  function TestClientSetQueryParamAny (line 486) | func TestClientSetQueryParamAny(t *testing.T) {
  function TestClientSetPathParamAny (line 495) | func TestClientSetPathParamAny(t *testing.T) {
  function TestClientSetRawPathParamAny (line 504) | func TestClientSetRawPathParamAny(t *testing.T) {
  function TestClientSetTransport (line 513) | func TestClientSetTransport(t *testing.T) {
  function TestClientSetScheme (line 531) | func TestClientSetScheme(t *testing.T) {
  function TestClientSetCookieJar (line 539) | func TestClientSetCookieJar(t *testing.T) {
  function TestClientSettingsCoverage (line 552) | func TestClientSettingsCoverage(t *testing.T) {
  function TestContentLengthWhenBodyIsNil (line 616) | func TestContentLengthWhenBodyIsNil(t *testing.T) {
  function TestClientPreRequestMiddlewares (line 633) | func TestClientPreRequestMiddlewares(t *testing.T) {
  function TestClientPreRequestMiddlewareError (line 679) | func TestClientPreRequestMiddlewareError(t *testing.T) {
  function TestClientAllowMethodGetPayload (line 698) | func TestClientAllowMethodGetPayload(t *testing.T) {
  function TestClientAllowMethodDeletePayload (line 743) | func TestClientAllowMethodDeletePayload(t *testing.T) {
  function TestClientRoundTripper (line 791) | func TestClientRoundTripper(t *testing.T) {
  function TestClientNewRequest (line 804) | func TestClientNewRequest(t *testing.T) {
  function TestClientDebugBodySizeLimit (line 810) | func TestClientDebugBodySizeLimit(t *testing.T) {
  function TestGzipCompress (line 844) | func TestGzipCompress(t *testing.T) {
  function TestDeflateCompress (line 866) | func TestDeflateCompress(t *testing.T) {
  type lzwReader (line 888) | type lzwReader struct
    method Read (line 893) | func (l *lzwReader) Read(p []byte) (n int, err error) {
    method Close (line 897) | func (l *lzwReader) Close() error {
  function TestLzwCompress (line 903) | func TestLzwCompress(t *testing.T) {
  function TestClientLogCallbacks (line 941) | func TestClientLogCallbacks(t *testing.T) {
  function TestDebugLogSimultaneously (line 986) | func TestDebugLogSimultaneously(t *testing.T) {
  function TestCustomTransportSettings (line 1008) | func TestCustomTransportSettings(t *testing.T) {
  function TestDefaultDialerTransportSettings (line 1034) | func TestDefaultDialerTransportSettings(t *testing.T) {
  function TestNewWithDialer (line 1057) | func TestNewWithDialer(t *testing.T) {
  function TestNewWithLocalAddr (line 1073) | func TestNewWithLocalAddr(t *testing.T) {
  function TestClientOnResponseFailure (line 1086) | func TestClientOnResponseFailure(t *testing.T) {
  function TestResponseError (line 1249) | func TestResponseError(t *testing.T) {
  function TestHostURLForGH318AndGH407 (line 1259) | func TestHostURLForGH318AndGH407(t *testing.T) {
  function TestPostRedirectWithBody (line 1302) | func TestPostRedirectWithBody(t *testing.T) {
  function TestUnixSocket (line 1333) | func TestUnixSocket(t *testing.T) {
  function TestClientClone (line 1361) | func TestClientClone(t *testing.T) {
  function TestResponseBodyLimit (line 1409) | func TestResponseBodyLimit(t *testing.T) {
  function TestClient_executeReadAllError (line 1469) | func TestClient_executeReadAllError(t *testing.T) {
  function TestClientDebugf (line 1492) | func TestClientDebugf(t *testing.T) {
  function TestClientOnClose (line 1508) | func TestClientOnClose(t *testing.T) {
  function TestClientOnCloseMultipleHooks (line 1521) | func TestClientOnCloseMultipleHooks(t *testing.T) {
  function TestClientHedgingMutualExclusionWithRetry (line 1540) | func TestClientHedgingMutualExclusionWithRetry(t *testing.T) {

FILE: context_test.go
  function TestClientSetContext (line 18) | func TestClientSetContext(t *testing.T) {
  function TestRequestSetContext (line 38) | func TestRequestSetContext(t *testing.T) {
  function TestSetContextWithError (line 54) | func TestSetContextWithError(t *testing.T) {
  function TestSetContextCancel (line 69) | func TestSetContextCancel(t *testing.T) {
  function TestSetContextCancelRetry (line 106) | func TestSetContextCancelRetry(t *testing.T) {
  function TestSetContextCancelWithError (line 153) | func TestSetContextCancelWithError(t *testing.T) {
  function TestClientRetryWithSetContext (line 192) | func TestClientRetryWithSetContext(t *testing.T) {
  function TestRequestContext (line 217) | func TestRequestContext(t *testing.T) {
  function errIsContextCanceled (line 226) | func errIsContextCanceled(err error) bool {

FILE: curl.go
  function buildCurlCmd (line 18) | func buildCurlCmd(req *Request) string {
  function dumpCurlCookies (line 54) | func dumpCurlCookies(cookies []*http.Cookie) string {
  function dumpCurlHeaders (line 64) | func dumpCurlHeaders(req *http.Request) *[][2]string {
  function cmdQuote (line 91) | func cmdQuote(s string) string {

FILE: curl_test.go
  function TestCurlGenerateUnexecutedRequest (line 18) | func TestCurlGenerateUnexecutedRequest(t *testing.T) {
  function TestCurlGenerateExecutedRequest (line 45) | func TestCurlGenerateExecutedRequest(t *testing.T) {
  function TestCurlCmdDebugMode (line 82) | func TestCurlCmdDebugMode(t *testing.T) {
  function TestCurl_buildCurlCmd (line 120) | func TestCurl_buildCurlCmd(t *testing.T) {
  function TestCurlRequestGetBodyError (line 219) | func TestCurlRequestGetBodyError(t *testing.T) {
  function TestCurlRequestMiddlewaresError (line 257) | func TestCurlRequestMiddlewaresError(t *testing.T) {
  function TestCurlMiscTestCoverage (line 271) | func TestCurlMiscTestCoverage(t *testing.T) {

FILE: debug.go
  type DebugLogCallbackFunc (line 17) | type DebugLogCallbackFunc
  type DebugLogFormatterFunc (line 21) | type DebugLogFormatterFunc
  type DebugLog (line 25) | type DebugLog struct
  type DebugLogRequest (line 32) | type DebugLogRequest struct
  type DebugLogResponse (line 45) | type DebugLogResponse struct
  function DebugLogFormatter (line 61) | func DebugLogFormatter(dl *DebugLog) string {
  function DebugLogJSONFormatter (line 97) | func DebugLogJSONFormatter(dl *DebugLog) string {
  function debugLogger (line 101) | func debugLogger(c *Client, res *Response) {
  constant debugRequestLogKey (line 140) | debugRequestLogKey = "__restyDebugRequestLog"
  function prepareRequestDebugInfo (line 142) | func prepareRequestDebugInfo(c *Client, r *Request) {

FILE: digest.go
  constant qopAuth (line 47) | qopAuth    = "auth"
  constant qopAuthInt (line 48) | qopAuthInt = "auth-int"
  type digestTransport (line 51) | type digestTransport struct
    method RoundTrip (line 56) | func (dt *digestTransport) RoundTrip(req *http.Request) (*http.Respons...
    method cloneReq (line 97) | func (dt *digestTransport) cloneReq(r *http.Request, first bool) *http...
    method parseChallenge (line 107) | func (dt *digestTransport) parseChallenge(input string) (*digestChalle...
    method createCredentials (line 161) | func (dt *digestTransport) createCredentials(cha *digestChallenge, req...
    method prepareBody (line 197) | func (dt *digestTransport) prepareBody(req *http.Request) error {
  type digestChallenge (line 222) | type digestChallenge struct
    method isQopSupported (line 234) | func (dc *digestChallenge) isQopSupported(qop string) bool {
    method setValue (line 243) | func (dc *digestChallenge) setValue(k, v string) error {
  type digestCredentials (line 279) | type digestCredentials struct
    method parseQop (line 297) | func (dc *digestCredentials) parseQop(cha *digestChallenge) error {
    method h (line 315) | func (dc *digestCredentials) h(data string) string {
    method digest (line 321) | func (dc *digestCredentials) digest(cha *digestChallenge) (string, err...
    method ha1 (line 353) | func (dc *digestCredentials) ha1() string {
    method ha2 (line 362) | func (dc *digestCredentials) ha2() string {
    method String (line 369) | func (dc *digestCredentials) String() string {
  function newHashFunc (line 396) | func newHashFunc(algorithm string) hash.Hash {

FILE: digest_test.go
  type digestServerConfig (line 16) | type digestServerConfig struct
  function defaultDigestServerConf (line 20) | func defaultDigestServerConf() *digestServerConfig {
  function TestClientDigestAuth (line 35) | func TestClientDigestAuth(t *testing.T) {
  function TestClientDigestAuthSession (line 51) | func TestClientDigestAuthSession(t *testing.T) {
  function TestClientDigestAuthErrors (line 69) | func TestClientDigestAuthErrors(t *testing.T) {
  function TestClientDigestAuthWithBody (line 101) | func TestClientDigestAuthWithBody(t *testing.T) {
  function TestClientDigestAuthWithBodyQopAuthInt (line 122) | func TestClientDigestAuthWithBodyQopAuthInt(t *testing.T) {
  function TestClientDigestAuthWithBodyQopAuthIntIoCopyError (line 140) | func TestClientDigestAuthWithBodyQopAuthIntIoCopyError(t *testing.T) {
  function TestClientDigestAuthRoundTripError (line 167) | func TestClientDigestAuthRoundTripError(t *testing.T) {
  function TestClientDigestAuthWithBodyQopAuthIntGetBodyNil (line 185) | func TestClientDigestAuthWithBodyQopAuthIntGetBodyNil(t *testing.T) {
  function TestClientDigestAuthWithGetBodyError (line 210) | func TestClientDigestAuthWithGetBodyError(t *testing.T) {
  function TestClientDigestAuthWithGetBodyNilReadError (line 239) | func TestClientDigestAuthWithGetBodyNilReadError(t *testing.T) {
  function TestClientDigestAuthWithNoBodyQopAuthInt (line 266) | func TestClientDigestAuthWithNoBodyQopAuthInt(t *testing.T) {
  function TestClientDigestAuthNoQop (line 280) | func TestClientDigestAuthNoQop(t *testing.T) {
  function TestClientDigestAuthWithIncorrectNcValue (line 299) | func TestClientDigestAuthWithIncorrectNcValue(t *testing.T) {

FILE: hedging.go
  function NewHedging (line 38) | func NewHedging() *Hedging {
  type Hedging (line 69) | type Hedging struct
    method Delay (line 80) | func (h *Hedging) Delay() time.Duration {
    method SetDelay (line 87) | func (h *Hedging) SetDelay(delay time.Duration) *Hedging {
    method MaxRequest (line 95) | func (h *Hedging) MaxRequest() int {
    method SetMaxRequest (line 102) | func (h *Hedging) SetMaxRequest(count int) *Hedging {
    method MaxRequestPerSecond (line 110) | func (h *Hedging) MaxRequestPerSecond() float64 {
    method SetMaxRequestPerSecond (line 117) | func (h *Hedging) SetMaxRequestPerSecond(count float64) *Hedging {
    method IsNonReadOnlyAllowed (line 127) | func (h *Hedging) IsNonReadOnlyAllowed() bool {
    method SetNonReadOnlyAllowed (line 138) | func (h *Hedging) SetNonReadOnlyAllowed(allow bool) *Hedging {
    method calculateRateDelay (line 150) | func (h *Hedging) calculateRateDelay() {
    method RoundTrip (line 159) | func (ht *Hedging) RoundTrip(req *http.Request) (*http.Response, error) {
  function isReadOnlyMethod (line 256) | func isReadOnlyMethod(method string) bool {

FILE: hedging_test.go
  function createHedgingTestServer (line 19) | func createHedgingTestServer(t *testing.T, attemptCount *int32) *httptes...
  function TestHedgingBasic (line 29) | func TestHedgingBasic(t *testing.T) {
  function TestHedgingSecondWins (line 48) | func TestHedgingSecondWins(t *testing.T) {
  function TestHedgingTimeout (line 80) | func TestHedgingTimeout(t *testing.T) {
  function TestHedgingReadOnlyMethodsOnly (line 126) | func TestHedgingReadOnlyMethodsOnly(t *testing.T) {
  function TestHedgingRateLimit (line 172) | func TestHedgingRateLimit(t *testing.T) {
  function TestHedgingWithRetryFallback (line 196) | func TestHedgingWithRetryFallback(t *testing.T) {
  function TestHedgingDisable (line 223) | func TestHedgingDisable(t *testing.T) {
  function TestHedgingContextCancellation (line 250) | func TestHedgingContextCancellation(t *testing.T) {
  function TestHedgingConfiguration (line 283) | func TestHedgingConfiguration(t *testing.T) {
  function TestHedgingConfigurationViaClient (line 303) | func TestHedgingConfigurationViaClient(t *testing.T) {
  function TestHedgingWithCustomTransport (line 331) | func TestHedgingWithCustomTransport(t *testing.T) {
  function TestHedgingSingleRequest (line 356) | func TestHedgingSingleRequest(t *testing.T) {
  function TestHedgingAllowNonReadOnly (line 374) | func TestHedgingAllowNonReadOnly(t *testing.T) {
  function TestHedgingWithNilTransport (line 408) | func TestHedgingWithNilTransport(t *testing.T) {
  function TestHedgingEnableMultipleTimes (line 428) | func TestHedgingEnableMultipleTimes(t *testing.T) {
  function TestHedgingWrapWithDisabledHedging (line 462) | func TestHedgingWrapWithDisabledHedging(t *testing.T) {
  function TestHedgingRateDelayBetweenRequests (line 481) | func TestHedgingRateDelayBetweenRequests(t *testing.T) {
  function TestHedgingNoDoubleWrap (line 535) | func TestHedgingNoDoubleWrap(t *testing.T) {
  function TestHedgingRoundTripDeadlineExpired (line 567) | func TestHedgingRoundTripDeadlineExpired(t *testing.T) {

FILE: load_balancer.go
  type LoadBalancer (line 24) | type LoadBalancer interface
  type RequestFeedback (line 32) | type RequestFeedback struct
  function NewRoundRobin (line 40) | func NewRoundRobin(baseURLs ...string) (*RoundRobin, error) {
  type RoundRobin (line 56) | type RoundRobin struct
    method NextWithContext (line 64) | func (rr *RoundRobin) NextWithContext(ctx context.Context) (string, er...
    method Feedback (line 84) | func (rr *RoundRobin) Feedback(_ *RequestFeedback) {}
    method Close (line 87) | func (rr *RoundRobin) Close() error { return nil }
    method Refresh (line 90) | func (rr *RoundRobin) Refresh(baseURLs ...string) error {
  type Host (line 109) | type Host struct
    method addWeight (line 128) | func (h *Host) addWeight() {
    method resetWeight (line 132) | func (h *Host) resetWeight(totalWeight int) {
  type HostState (line 136) | type HostState
  constant HostStateInActive (line 140) | HostStateInActive HostState = iota
  constant HostStateActive (line 141) | HostStateActive
  type HostStateChangeFunc (line 145) | type HostStateChangeFunc
  function NewWeightedRoundRobin (line 152) | func NewWeightedRoundRobin(recovery time.Duration, hosts ...*Host) (*Wei...
  type WeightedRoundRobin (line 174) | type WeightedRoundRobin struct
    method NextWithContext (line 189) | func (wrr *WeightedRoundRobin) NextWithContext(ctx context.Context) (s...
    method Feedback (line 224) | func (wrr *WeightedRoundRobin) Feedback(f *RequestFeedback) {
    method Close (line 250) | func (wrr *WeightedRoundRobin) Close() error {
    method Refresh (line 258) | func (wrr *WeightedRoundRobin) Refresh(hosts ...*Host) error {
    method SetOnStateChange (line 289) | func (wrr *WeightedRoundRobin) SetOnStateChange(fn HostStateChangeFunc) {
    method SetRecoveryDuration (line 296) | func (wrr *WeightedRoundRobin) SetRecoveryDuration(d time.Duration) {
    method ticker (line 303) | func (wrr *WeightedRoundRobin) ticker() {
  function NewSRVWeightedRoundRobin (line 325) | func NewSRVWeightedRoundRobin(service, proto, domainName, httpScheme str...
  type SRVWeightedRoundRobin (line 358) | type SRVWeightedRoundRobin struct
    method NextWithContext (line 372) | func (swrr *SRVWeightedRoundRobin) NextWithContext(ctx context.Context...
    method Feedback (line 378) | func (swrr *SRVWeightedRoundRobin) Feedback(f *RequestFeedback) {
    method Close (line 384) | func (swrr *SRVWeightedRoundRobin) Close() error {
    method Refresh (line 393) | func (swrr *SRVWeightedRoundRobin) Refresh() error {
    method SetRefreshDuration (line 412) | func (swrr *SRVWeightedRoundRobin) SetRefreshDuration(d time.Duration) {
    method SetOnStateChange (line 419) | func (swrr *SRVWeightedRoundRobin) SetOnStateChange(fn HostStateChange...
    method SetRecoveryDuration (line 424) | func (swrr *SRVWeightedRoundRobin) SetRecoveryDuration(d time.Duration) {
    method ticker (line 428) | func (swrr *SRVWeightedRoundRobin) ticker() {
  function extractBaseURL (line 434) | func extractBaseURL(u string) (string, error) {

FILE: load_balancer_test.go
  function TestRoundRobin (line 19) | func TestRoundRobin(t *testing.T) {
  function TestRoundRobinNoBaseURLs (line 120) | func TestRoundRobinNoBaseURLs(t *testing.T) {
  function TestWeightedRoundRobin (line 139) | func TestWeightedRoundRobin(t *testing.T) {
  function TestSRVWeightedRoundRobin (line 281) | func TestSRVWeightedRoundRobin(t *testing.T) {
  function TestLoadBalancerRequest (line 471) | func TestLoadBalancerRequest(t *testing.T) {
  function TestLoadBalancerRequestFlowError (line 500) | func TestLoadBalancerRequestFlowError(t *testing.T) {
  function Test_extractBaseURL (line 533) | func Test_extractBaseURL(t *testing.T) {
  function TestLoadBalancerRequestFailures (line 572) | func TestLoadBalancerRequestFailures(t *testing.T) {
  type mockTimeoutErr (line 603) | type mockTimeoutErr struct
    method Error (line 605) | func (e *mockTimeoutErr) Error() string { return "i/o timeout" }
    method Timeout (line 606) | func (e *mockTimeoutErr) Timeout() bool { return true }
  function TestLoadBalancerCoverage (line 608) | func TestLoadBalancerCoverage(t *testing.T) {

FILE: middleware.go
  function MiddlewareRequestCreate (line 37) | func MiddlewareRequestCreate(c *Client, r *Request) (err error) {
  function parseRequestURL (line 60) | func parseRequestURL(c *Client, r *Request) error {
  function parseRequestHeader (line 185) | func parseRequestHeader(c *Client, r *Request) error {
  function parseRequestBody (line 204) | func parseRequestBody(c *Client, r *Request) error {
  function createRawRequest (line 230) | func createRawRequest(c *Client, r *Request) (err error) {
  function addCredentials (line 277) | func addCredentials(c *Client, r *Request) error {
  function handleMultipartFormData (line 326) | func handleMultipartFormData(r *Request) error {
  function handleMultipart (line 341) | func handleMultipart(c *Client, r *Request) error {
  function handleFormData (line 436) | func handleFormData(c *Client, r *Request) {
  function handleRequestBody (line 450) | func handleRequestBody(c *Client, r *Request) error {
  function MiddlewareResponseAutoParse (line 526) | func MiddlewareResponseAutoParse(c *Client, res *Response) (err error) {
  function MiddlewareResponseSaveToFile (line 584) | func MiddlewareResponseSaveToFile(c *Client, res *Response) error {

FILE: middleware_test.go
  function Test_parseRequestURL (line 26) | func Test_parseRequestURL(t *testing.T) {
  function Test_parseRequestHeader (line 356) | func Test_parseRequestHeader(t *testing.T) {
  function TestParseRequestBody (line 454) | func TestParseRequestBody(t *testing.T) {
  function TestMiddlewareSaveToFileErrorCases (line 864) | func TestMiddlewareSaveToFileErrorCases(t *testing.T) {
  function TestMiddlewareSaveToFileCopyError (line 894) | func TestMiddlewareSaveToFileCopyError(t *testing.T) {
  function TestRequestURL_GH797 (line 913) | func TestRequestURL_GH797(t *testing.T) {
  function TestMiddleware_multipartWriteFormData (line 933) | func TestMiddleware_multipartWriteFormData(t *testing.T) {
  function TestMiddleware_multipartWriteField (line 963) | func TestMiddleware_multipartWriteField(t *testing.T) {
  function TestMiddleware_multipartCreatePart (line 994) | func TestMiddleware_multipartCreatePart(t *testing.T) {
  function TestMiddleware_multipartCreatePart_WriteError (line 1028) | func TestMiddleware_multipartCreatePart_WriteError(t *testing.T) {
  function TestMiddlewareCoverage (line 1062) | func TestMiddlewareCoverage(t *testing.T) {

FILE: multipart.go
  function escapeQuotes (line 20) | func escapeQuotes(s string) string {
  type MultipartField (line 26) | type MultipartField struct
    method Clone (line 69) | func (mf *MultipartField) Clone() *MultipartField {
    method resetReader (line 75) | func (mf *MultipartField) resetReader() error {
    method isValues (line 83) | func (mf *MultipartField) isValues() bool {
    method close (line 87) | func (mf *MultipartField) close() {
    method createHeader (line 91) | func (mf *MultipartField) createHeader() textproto.MIMEHeader {
    method openFile (line 107) | func (mf *MultipartField) openFile() error {
    method detectContentType (line 130) | func (mf *MultipartField) detectContentType() error {
    method wrapProgressCallbackIfPresent (line 145) | func (mf *MultipartField) wrapProgressCallbackIfPresent(pw io.Writer) ...
  type MultipartFieldCallbackFunc (line 165) | type MultipartFieldCallbackFunc
  type MultipartFieldProgress (line 169) | type MultipartFieldProgress struct
    method String (line 177) | func (mfp MultipartFieldProgress) String() string {
  type multipartProgressWriter (line 182) | type multipartProgressWriter struct
    method Write (line 188) | func (mpw *multipartProgressWriter) Write(p []byte) (n int, err error) {

FILE: multipart_test.go
  function TestMultipartFormDataAndUpload (line 25) | func TestMultipartFormDataAndUpload(t *testing.T) {
  function TestMultipartFormDataAndUploadMethodPatch (line 59) | func TestMultipartFormDataAndUploadMethodPatch(t *testing.T) {
  function TestMultipartUploadError (line 77) | func TestMultipartUploadError(t *testing.T) {
  function TestMultipartUploadFiles (line 94) | func TestMultipartUploadFiles(t *testing.T) {
  function TestMultipartFilesAndFormDataEmptyGH1046 (line 124) | func TestMultipartFilesAndFormDataEmptyGH1046(t *testing.T) {
  function TestMultipartIoReaderFiles (line 148) | func TestMultipartIoReaderFiles(t *testing.T) {
  function TestMultipartUploadFileNotOnGetOrDelete (line 183) | func TestMultipartUploadFileNotOnGetOrDelete(t *testing.T) {
  function TestMultipartFormData (line 224) | func TestMultipartFormData(t *testing.T) {
  function TestMultipartFormDataFields (line 237) | func TestMultipartFormDataFields(t *testing.T) {
  function TestMultipartField (line 270) | func TestMultipartField(t *testing.T) {
  function TestMultipartFields (line 296) | func TestMultipartFields(t *testing.T) {
  function TestMultipartCustomBoundary (line 341) | func TestMultipartCustomBoundary(t *testing.T) {
  function TestMultipartLargeFile (line 368) | func TestMultipartLargeFile(t *testing.T) {
  function TestMultipartFieldProgressCallback (line 418) | func TestMultipartFieldProgressCallback(t *testing.T) {
  function TestMultipartOrderedFormData (line 489) | func TestMultipartOrderedFormData(t *testing.T) {
  type errorReader (line 551) | type errorReader struct
    method Read (line 553) | func (errorReader) Read(p []byte) (n int, err error) {
  function TestMultipartReaderErrors (line 557) | func TestMultipartReaderErrors(t *testing.T) {
  type mpWriterError (line 602) | type mpWriterError struct
    method Write (line 604) | func (mwe *mpWriterError) Write(p []byte) (int, error) {
  function TestMultipartRequest_Errors (line 608) | func TestMultipartRequest_Errors(t *testing.T) {
  function TestMultipartUploadFailAutoErrorParse (line 624) | func TestMultipartUploadFailAutoErrorParse(t *testing.T) {
  function TestMultipartConcurrentRequests (line 676) | func TestMultipartConcurrentRequests(t *testing.T) {
  type returnValueTestWriter (line 707) | type returnValueTestWriter struct
    method Write (line 710) | func (z *returnValueTestWriter) Write(p []byte) (n int, err error) {
  function TestMultipartCornerCoverage (line 714) | func TestMultipartCornerCoverage(t *testing.T) {

FILE: redirect.go
  type RedirectPolicy (line 22) | type RedirectPolicy interface
  type RedirectPolicyFunc (line 29) | type RedirectPolicyFunc
    method Apply (line 39) | func (f RedirectPolicyFunc) Apply(req *http.Request, via []*http.Reque...
  type RedirectInfo (line 32) | type RedirectInfo struct
  function RedirectNoPolicy (line 46) | func RedirectNoPolicy() RedirectPolicy {
  function RedirectFlexiblePolicy (line 55) | func RedirectFlexiblePolicy(noOfRedirect int) RedirectPolicy {
  function RedirectDomainCheckPolicy (line 69) | func RedirectDomainCheckPolicy(hostnames ...string) RedirectPolicy {
  function getHostname (line 84) | func getHostname(host string) (hostname string) {
  function checkHostAndAddHeaders (line 97) | func checkHostAndAddHeaders(cur *http.Request, pre *http.Request) {

FILE: request.go
  type Request (line 35) | type Request struct
    method SetCorrelationID (line 115) | func (r *Request) SetCorrelationID(id string) *Request {
    method SetMethod (line 121) | func (r *Request) SetMethod(m string) *Request {
    method SetURL (line 127) | func (r *Request) SetURL(url string) *Request {
    method Context (line 137) | func (r *Request) Context() context.Context {
    method SetContext (line 154) | func (r *Request) SetContext(ctx context.Context) *Request {
    method WithContext (line 169) | func (r *Request) WithContext(ctx context.Context) *Request {
    method SetContentType (line 182) | func (r *Request) SetContentType(ct string) *Request {
    method SetHeader (line 196) | func (r *Request) SetHeader(header, value string) *Request {
    method SetHeaderAny (line 213) | func (r *Request) SetHeaderAny(header string, value any) *Request {
    method SetHeaders (line 230) | func (r *Request) SetHeaders(headers map[string]string) *Request {
    method SetHeaderMultiValues (line 247) | func (r *Request) SetHeaderMultiValues(headers map[string][]string) *R...
    method SetHeaderVerbatim (line 265) | func (r *Request) SetHeaderVerbatim(header, value string) *Request {
    method SetHeaderVerbatimAny (line 282) | func (r *Request) SetHeaderVerbatimAny(header string, value any) *Requ...
    method SetQueryParam (line 298) | func (r *Request) SetQueryParam(param, value string) *Request {
    method SetQueryParamAny (line 318) | func (r *Request) SetQueryParamAny(param string, value any) *Request {
    method SetQueryParams (line 336) | func (r *Request) SetQueryParams(params map[string]string) *Request {
    method SetQueryParamsFromValues (line 355) | func (r *Request) SetQueryParamsFromValues(params url.Values) *Request {
    method SetQueryString (line 370) | func (r *Request) SetQueryString(query string) *Request {
    method SetFormData (line 396) | func (r *Request) SetFormData(data map[string]string) *Request {
    method SetFormDataFromValues (line 412) | func (r *Request) SetFormDataFromValues(data url.Values) *Request {
    method SetBody (line 468) | func (r *Request) SetBody(body any) *Request {
    method SetResult (line 497) | func (r *Request) SetResult(v any) *Request {
    method SetResultError (line 518) | func (r *Request) SetResultError(err any) *Request {
    method SetFile (line 530) | func (r *Request) SetFile(fieldName, filePath string) *Request {
    method SetFiles (line 551) | func (r *Request) SetFiles(files map[string]string) *Request {
    method SetFileReader (line 571) | func (r *Request) SetFileReader(fieldName, fileName string, reader io....
    method SetMultipartFormData (line 578) | func (r *Request) SetMultipartFormData(data map[string]string) *Request {
    method SetMultipartOrderedFormData (line 588) | func (r *Request) SetMultipartOrderedFormData(name string, values []st...
    method SetMultipartField (line 601) | func (r *Request) SetMultipartField(fieldName, fileName, contentType s...
    method SetMultipartFields (line 654) | func (r *Request) SetMultipartFields(fields ...*MultipartField) *Reque...
    method SetMultipartBoundary (line 662) | func (r *Request) SetMultipartBoundary(boundary string) *Request {
    method SetContentLength (line 671) | func (r *Request) SetContentLength(v int64) *Request {
    method SetBasicAuth (line 688) | func (r *Request) SetBasicAuth(username, password string) *Request {
    method SetAuthToken (line 702) | func (r *Request) SetAuthToken(authToken string) *Request {
    method SetAuthScheme (line 726) | func (r *Request) SetAuthScheme(scheme string) *Request {
    method SetHeaderAuthorizationKey (line 736) | func (r *Request) SetHeaderAuthorizationKey(k string) *Request {
    method SetResponseSaveFileName (line 756) | func (r *Request) SetResponseSaveFileName(file string) *Request {
    method SetResponseSaveToFile (line 773) | func (r *Request) SetResponseSaveToFile(save bool) *Request {
    method SetCloseConnection (line 782) | func (r *Request) SetCloseConnection(close bool) *Request {
    method SetResponseDoNotParse (line 795) | func (r *Request) SetResponseDoNotParse(notParse bool) *Request {
    method SetResponseBodyLimit (line 811) | func (r *Request) SetResponseBodyLimit(v int64) *Request {
    method SetResponseBodyUnlimitedReads (line 826) | func (r *Request) SetResponseBodyUnlimitedReads(b bool) *Request {
    method SetPathParam (line 850) | func (r *Request) SetPathParam(param, value string) *Request {
    method SetPathParamAny (line 873) | func (r *Request) SetPathParamAny(param string, value any) *Request {
    method SetPathParams (line 896) | func (r *Request) SetPathParams(params map[string]string) *Request {
    method SetPathRawParam (line 922) | func (r *Request) SetPathRawParam(param, value string) *Request {
    method SetPathRawParamAny (line 945) | func (r *Request) SetPathRawParamAny(param string, value any) *Request {
    method SetPathRawParams (line 968) | func (r *Request) SetPathRawParams(params map[string]string) *Request {
    method SetResponseExpectContentType (line 977) | func (r *Request) SetResponseExpectContentType(contentType string) *Re...
    method SetResponseForceContentType (line 988) | func (r *Request) SetResponseForceContentType(contentType string) *Req...
    method SetJSONEscapeHTML (line 999) | func (r *Request) SetJSONEscapeHTML(b bool) *Request {
    method SetCookie (line 1012) | func (r *Request) SetCookie(hc *http.Cookie) *Request {
    method SetCookies (line 1034) | func (r *Request) SetCookies(rs []*http.Cookie) *Request {
    method SetTimeout (line 1046) | func (r *Request) SetTimeout(timeout time.Duration) *Request {
    method SetLogger (line 1057) | func (r *Request) SetLogger(l Logger) *Request {
    method SetDebug (line 1072) | func (r *Request) SetDebug(d bool) *Request {
    method AddRetryConditions (line 1089) | func (r *Request) AddRetryConditions(conditions ...RetryConditionFunc)...
    method SetRetryConditions (line 1101) | func (r *Request) SetRetryConditions(conditions ...RetryConditionFunc)...
    method AddRetryHooks (line 1114) | func (r *Request) AddRetryHooks(hooks ...RetryHookFunc) *Request {
    method SetRetryHooks (line 1124) | func (r *Request) SetRetryHooks(hooks ...RetryHookFunc) *Request {
    method SetRetryCount (line 1142) | func (r *Request) SetRetryCount(count int) *Request {
    method SetRetryWaitTime (line 1150) | func (r *Request) SetRetryWaitTime(waitTime time.Duration) *Request {
    method SetRetryMaxWaitTime (line 1158) | func (r *Request) SetRetryMaxWaitTime(maxWaitTime time.Duration) *Requ...
    method SetRetryDelayStrategy (line 1168) | func (r *Request) SetRetryDelayStrategy(rs RetryDelayStrategyFunc) *Re...
    method SetRetryDefaultConditions (line 1177) | func (r *Request) SetRetryDefaultConditions(b bool) *Request {
    method SetRetryAllowNonIdempotent (line 1190) | func (r *Request) SetRetryAllowNonIdempotent(b bool) *Request {
    method SetTrace (line 1208) | func (r *Request) SetTrace(t bool) *Request {
    method SetCurlCmdGenerate (line 1226) | func (r *Request) SetCurlCmdGenerate(b bool) *Request {
    method SetCurlCmdDebugLog (line 1235) | func (r *Request) SetCurlCmdDebugLog(b bool) *Request {
    method CurlCmd (line 1241) | func (r *Request) CurlCmd() string {
    method generateCurlCommand (line 1245) | func (r *Request) generateCurlCommand() string {
    method SetQueryParamsUnescape (line 1268) | func (r *Request) SetQueryParamsUnescape(unescape bool) *Request {
    method SetMethodGetAllowPayload (line 1279) | func (r *Request) SetMethodGetAllowPayload(allow bool) *Request {
    method SetMethodDeleteAllowPayload (line 1292) | func (r *Request) SetMethodDeleteAllowPayload(allow bool) *Request {
    method TraceInfo (line 1300) | func (r *Request) TraceInfo() TraceInfo {
    method Get (line 1373) | func (r *Request) Get(url string) (*Response, error) {
    method Head (line 1380) | func (r *Request) Head(url string) (*Response, error) {
    method Post (line 1387) | func (r *Request) Post(url string) (*Response, error) {
    method Put (line 1394) | func (r *Request) Put(url string) (*Response, error) {
    method Patch (line 1401) | func (r *Request) Patch(url string) (*Response, error) {
    method Delete (line 1408) | func (r *Request) Delete(url string) (*Response, error) {
    method Options (line 1415) | func (r *Request) Options(url string) (*Response, error) {
    method Trace (line 1422) | func (r *Request) Trace(url string) (*Response, error) {
    method Send (line 1433) | func (r *Request) Send() (*Response, error) {
    method Execute (line 1441) | func (r *Request) Execute(method, url string) (res *Response, err erro...
    method Clone (line 1596) | func (r *Request) Clone(ctx context.Context) *Request {
    method Funcs (line 1682) | func (r *Request) Funcs(funcs ...RequestFunc) *Request {
    method fmtBodyString (line 1689) | func (r *Request) fmtBodyString(sl int) (body string) {
    method initValuesMap (line 1757) | func (r *Request) initValuesMap() {
    method initTraceIfEnabled (line 1763) | func (r *Request) initTraceIfEnabled() {
    method isHeaderExists (line 1770) | func (r *Request) isHeaderExists(k string) bool {
    method isPayloadSupported (line 1775) | func (r *Request) isPayloadSupported() bool {
    method sendLoadBalancerFeedback (line 1796) | func (r *Request) sendLoadBalancerFeedback(res *Response, err error) {
    method resetFileReaders (line 1825) | func (r *Request) resetFileReaders() error {
    method isIdempotent (line 1845) | func (r *Request) isIdempotent() bool {
    method withTimeout (line 1850) | func (r *Request) withTimeout() *http.Request {
  function jsonIndent (line 1862) | func jsonIndent(v []byte) []byte {

FILE: request_test.go
  type AuthSuccess (line 28) | type AuthSuccess struct
  type AuthError (line 33) | type AuthError struct
  function TestGet (line 37) | func TestGet(t *testing.T) {
  function TestGetGH524 (line 54) | func TestGetGH524(t *testing.T) {
  function TestRequestNegativeRetryCount (line 73) | func TestRequestNegativeRetryCount(t *testing.T) {
  function TestGetCustomUserAgent (line 84) | func TestGetCustomUserAgent(t *testing.T) {
  function TestGetClientParamRequestParam (line 102) | func TestGetClientParamRequestParam(t *testing.T) {
  function TestGetRelativePath (line 127) | func TestGetRelativePath(t *testing.T) {
  function TestGet400Error (line 143) | func TestGet400Error(t *testing.T) {
  function TestPostJSONStringSuccess (line 156) | func TestPostJSONStringSuccess(t *testing.T) {
  function TestPostJSONBytesSuccess (line 184) | func TestPostJSONBytesSuccess(t *testing.T) {
  function TestPostJSONBytesIoReader (line 202) | func TestPostJSONBytesIoReader(t *testing.T) {
  function TestPostJSONStructSuccess (line 221) | func TestPostJSONStructSuccess(t *testing.T) {
  function TestPostJSONRPCStructSuccess (line 248) | func TestPostJSONRPCStructSuccess(t *testing.T) {
  function TestPostJSONStructInvalidLogin (line 274) | func TestPostJSONStructInvalidLogin(t *testing.T) {
  function TestPostJSONErrorRFC7807 (line 299) | func TestPostJSONErrorRFC7807(t *testing.T) {
  function TestPostJSONMapSuccess (line 321) | func TestPostJSONMapSuccess(t *testing.T) {
  function TestPostJSONMapInvalidResponseJson (line 341) | func TestPostJSONMapInvalidResponseJson(t *testing.T) {
  type brokenMarshalJSON (line 362) | type brokenMarshalJSON struct
    method MarshalJSON (line 364) | func (b brokenMarshalJSON) MarshalJSON() ([]byte, error) {
  function TestPostJSONMarshalError (line 368) | func TestPostJSONMarshalError(t *testing.T) {
  function TestForceContentTypeForGH276andGH240 (line 388) | func TestForceContentTypeForGH276andGH240(t *testing.T) {
  function TestPostXMLStringSuccess (line 412) | func TestPostXMLStringSuccess(t *testing.T) {
  type brokenMarshalXML (line 432) | type brokenMarshalXML struct
    method MarshalXML (line 434) | func (b brokenMarshalXML) MarshalXML(e *xml.Encoder, start xml.StartEl...
  function TestPostXMLMarshalError (line 438) | func TestPostXMLMarshalError(t *testing.T) {
  function TestPostXMLStringError (line 458) | func TestPostXMLStringError(t *testing.T) {
  function TestPostXMLBytesSuccess (line 474) | func TestPostXMLBytesSuccess(t *testing.T) {
  function TestPostXMLStructSuccess (line 493) | func TestPostXMLStructSuccess(t *testing.T) {
  function TestPostXMLStructInvalidLogin (line 511) | func TestPostXMLStructInvalidLogin(t *testing.T) {
  function TestPostXMLStructInvalidResponseXml (line 532) | func TestPostXMLStructInvalidResponseXml(t *testing.T) {
  function TestPostXMLMapNotSupported (line 550) | func TestPostXMLMapNotSupported(t *testing.T) {
  function TestRequestBasicAuth (line 562) | func TestRequestBasicAuth(t *testing.T) {
  function TestRequestBasicAuthWithBody (line 582) | func TestRequestBasicAuthWithBody(t *testing.T) {
  function TestRequestInsecureBasicAuth (line 603) | func TestRequestInsecureBasicAuth(t *testing.T) {
  function TestRequestBasicAuthFail (line 630) | func TestRequestBasicAuthFail(t *testing.T) {
  function TestRequestAuthToken (line 649) | func TestRequestAuthToken(t *testing.T) {
  function TestRequestAuthScheme (line 665) | func TestRequestAuthScheme(t *testing.T) {
  function TestFormData (line 734) | func TestFormData(t *testing.T) {
  function TestMultiValueFormData (line 753) | func TestMultiValueFormData(t *testing.T) {
  function TestFormDataDisableWarn (line 774) | func TestFormDataDisableWarn(t *testing.T) {
  function TestGetWithCookie (line 794) | func TestGetWithCookie(t *testing.T) {
  function TestGetWithCookies (line 827) | func TestGetWithCookies(t *testing.T) {
  function TestPutPlainString (line 878) | func TestPutPlainString(t *testing.T) {
  function TestPutJSONString (line 891) | func TestPutJSONString(t *testing.T) {
  function TestPutXMLString (line 919) | func TestPutXMLString(t *testing.T) {
  function TestRequestMiddleware (line 933) | func TestRequestMiddleware(t *testing.T) {
  function TestHTTPAutoRedirectUpTo10 (line 957) | func TestHTTPAutoRedirectUpTo10(t *testing.T) {
  function TestHostCheckRedirectPolicy (line 973) | func TestHostCheckRedirectPolicy(t *testing.T) {
  function TestHttpMethods (line 986) | func TestHttpMethods(t *testing.T) {
  function TestSendMethod (line 1024) | func TestSendMethod(t *testing.T) {
  function TestRawFileUploadByBody (line 1094) | func TestRawFileUploadByBody(t *testing.T) {
  function TestProxySetting (line 1111) | func TestProxySetting(t *testing.T) {
  function TestGetClient (line 1136) | func TestGetClient(t *testing.T) {
  function TestIncorrectURL (line 1147) | func TestIncorrectURL(t *testing.T) {
  function TestDetectContentTypeForPointer (line 1159) | func TestDetectContentTypeForPointer(t *testing.T) {
  type ExampleUser (line 1178) | type ExampleUser struct
  function TestDetectContentTypeForPointerWithSlice (line 1184) | func TestDetectContentTypeForPointerWithSlice(t *testing.T) {
  function TestDetectContentTypeForPointerWithSliceMap (line 1206) | func TestDetectContentTypeForPointerWithSliceMap(t *testing.T) {
  function TestDetectContentTypeForSlice (line 1231) | func TestDetectContentTypeForSlice(t *testing.T) {
  function TestMultiParamsQueryString (line 1253) | func TestMultiParamsQueryString(t *testing.T) {
  function TestSetQueryStringTypical (line 1295) | func TestSetQueryStringTypical(t *testing.T) {
  function TestSetHeaderVerbatim (line 1318) | func TestSetHeaderVerbatim(t *testing.T) {
  function TestSetHeaderMultipleValue (line 1331) | func TestSetHeaderMultipleValue(t *testing.T) {
  function TestRequestSetHeaderAny (line 1344) | func TestRequestSetHeaderAny(t *testing.T) {
  function TestRequestSetHeaderVerbatimAny (line 1353) | func TestRequestSetHeaderVerbatimAny(t *testing.T) {
  function TestRequestSetQueryParamAny (line 1361) | func TestRequestSetQueryParamAny(t *testing.T) {
  function TestRequestSetPathParamAny (line 1370) | func TestRequestSetPathParamAny(t *testing.T) {
  function TestRequestSetRawPathParamAny (line 1379) | func TestRequestSetRawPathParamAny(t *testing.T) {
  function TestOutputFileWithBaseDirAndRelativePath (line 1388) | func TestOutputFileWithBaseDirAndRelativePath(t *testing.T) {
  function TestOutputFileWithBaseDirError (line 1413) | func TestOutputFileWithBaseDirError(t *testing.T) {
  function TestOutputPathDirNotExists (line 1420) | func TestOutputPathDirNotExists(t *testing.T) {
  function TestOutputFileAbsPath (line 1438) | func TestOutputFileAbsPath(t *testing.T) {
  function TestRequestSaveResponse (line 1456) | func TestRequestSaveResponse(t *testing.T) {
  function TestContextInternal (line 1508) | func TestContextInternal(t *testing.T) {
  function TestRequestDoNotParseResponse (line 1521) | func TestRequestDoNotParseResponse(t *testing.T) {
  function TestRequestDoNotParseResponseDebugLog (line 1552) | func TestRequestDoNotParseResponseDebugLog(t *testing.T) {
  type noCtTest (line 1589) | type noCtTest struct
  function TestRequestExpectContentTypeTest (line 1593) | func TestRequestExpectContentTypeTest(t *testing.T) {
  function TestGetPathParamAndPathParams (line 1611) | func TestGetPathParamAndPathParams(t *testing.T) {
  function TestReportMethodSupportsPayload (line 1632) | func TestReportMethodSupportsPayload(t *testing.T) {
  function TestRequestQueryStringOrder (line 1645) | func TestRequestQueryStringOrder(t *testing.T) {
  function TestRequestOverridesClientAuthorizationHeader (line 1661) | func TestRequestOverridesClientAuthorizationHeader(t *testing.T) {
  function TestRequestFileUploadAsReader (line 1678) | func TestRequestFileUploadAsReader(t *testing.T) {
  function TestHostHeaderOverride (line 1722) | func TestHostHeaderOverride(t *testing.T) {
  type HTTPErrorResponse (line 1738) | type HTTPErrorResponse struct
  function TestNotFoundWithError (line 1742) | func TestNotFoundWithError(t *testing.T) {
  function TestNotFoundWithoutError (line 1761) | func TestNotFoundWithoutError(t *testing.T) {
  function TestPathParamURLInput (line 1782) | func TestPathParamURLInput(t *testing.T) {
  function TestRawPathParamURLInput (line 1808) | func TestRawPathParamURLInput(t *testing.T) {
  function TestTraceInfo (line 1837) | func TestTraceInfo(t *testing.T) {
  function TestTraceInfoWithoutEnableTrace (line 1942) | func TestTraceInfoWithoutEnableTrace(t *testing.T) {
  function TestTraceInfoOnTimeout (line 1963) | func TestTraceInfoOnTimeout(t *testing.T) {
  function TestTraceInfoOnTimeoutWithSetTimeout (line 1985) | func TestTraceInfoOnTimeoutWithSetTimeout(t *testing.T) {
  function TestDebugLoggerRequestBodyTooLarge (line 2062) | func TestDebugLoggerRequestBodyTooLarge(t *testing.T) {
  function TestPostMapTemporaryRedirect (line 2127) | func TestPostMapTemporaryRedirect(t *testing.T) {
  function TestPostWith204Response (line 2140) | func TestPostWith204Response(t *testing.T) {
  type brokenReadCloser (line 2153) | type brokenReadCloser struct
    method Read (line 2155) | func (b brokenReadCloser) Read(p []byte) (n int, err error) {
    method Close (line 2159) | func (b brokenReadCloser) Close() error {
  function TestPostBodyError (line 2163) | func TestPostBodyError(t *testing.T) {
  function TestSetResultMustNotPanicOnNil (line 2174) | func TestSetResultMustNotPanicOnNil(t *testing.T) {
  function TestRequestClone (line 2183) | func TestRequestClone(t *testing.T) {
  function TestResponseBodyUnlimitedReads (line 2237) | func TestResponseBodyUnlimitedReads(t *testing.T) {
  function TestRequestAllowPayload (line 2270) | func TestRequestAllowPayload(t *testing.T) {
  function TestRequestNoRetryOnNonIdempotentMethod (line 2351) | func TestRequestNoRetryOnNonIdempotentMethod(t *testing.T) {
  function TestRequestContextTimeout (line 2383) | func TestRequestContextTimeout(t *testing.T) {
  function TestRequestPanicContext (line 2424) | func TestRequestPanicContext(t *testing.T) {
  function TestRequestSetResultAndSetOutputFile (line 2437) | func TestRequestSetResultAndSetOutputFile(t *testing.T) {
  function TestRequestBodyContentLengthValidation (line 2466) | func TestRequestBodyContentLengthValidation(t *testing.T) {
  function TestRequestFuncs (line 2495) | func TestRequestFuncs(t *testing.T) {
  function TestHTTPWarnGH970 (line 2529) | func TestHTTPWarnGH970(t *testing.T) {
  function TestRequestSettingsCoverage (line 2568) | func TestRequestSettingsCoverage(t *testing.T) {
  function TestRequestDataRace (line 2629) | func TestRequestDataRace(t *testing.T) {

FILE: response.go
  type Response (line 23) | type Response struct
    method Status (line 41) | func (r *Response) Status() string {
    method StatusCode (line 51) | func (r *Response) StatusCode() int {
    method Proto (line 59) | func (r *Response) Proto() string {
    method Result (line 86) | func (r *Response) Result() any {
    method ResultError (line 110) | func (r *Response) ResultError() any {
    method Header (line 115) | func (r *Response) Header() http.Header {
    method Cookies (line 123) | func (r *Response) Cookies() []*http.Cookie {
    method String (line 137) | func (r *Response) String() string {
    method Bytes (line 149) | func (r *Response) Bytes() []byte {
    method Duration (line 159) | func (r *Response) Duration() time.Duration {
    method ReceivedAt (line 167) | func (r *Response) ReceivedAt() time.Time {
    method Size (line 175) | func (r *Response) Size() int64 {
    method IsStatusSuccess (line 183) | func (r *Response) IsStatusSuccess() bool {
    method IsStatusFailure (line 190) | func (r *Response) IsStatusFailure() bool {
    method RedirectHistory (line 195) | func (r *Response) RedirectHistory() []*RedirectInfo {
    method setReceivedAt (line 214) | func (r *Response) setReceivedAt() {
    method fmtBodyString (line 221) | func (r *Response) fmtBodyString(sl int) string {
    method readIfRequired (line 258) | func (r *Response) readIfRequired() {
    method readAll (line 268) | func (r *Response) readAll() (err error) {
    method wrapLimitReadCloser (line 289) | func (r *Response) wrapLimitReadCloser() {
    method wrapCopyReadCloser (line 299) | func (r *Response) wrapCopyReadCloser() {
    method wrapContentDecompresser (line 312) | func (r *Response) wrapContentDecompresser() error {
    method wrapError (line 339) | func (r *Response) wrapError(err error, preserve bool) error {

FILE: resty.go
  constant Version (line 23) | Version = "3.0.0-beta.6"
  function New (line 26) | func New() *Client {
  function NewWithTransportSettings (line 32) | func NewWithTransportSettings(transportSettings *TransportSettings) *Cli...
  function NewWithClient (line 37) | func NewWithClient(hc *http.Client) *Client {
  function NewWithDialer (line 43) | func NewWithDialer(dialer *net.Dialer) *Client {
  function NewWithLocalAddr (line 48) | func NewWithLocalAddr(localAddr net.Addr) *Client {
  function NewWithDialerAndTransportSettings (line 57) | func NewWithDialerAndTransportSettings(dialer *net.Dialer, transportSett...
  function createTransport (line 68) | func createTransport(dialer *net.Dialer, transportSettings *TransportSet...
  function createCookieJar (line 159) | func createCookieJar() *cookiejar.Jar {
  function createClient (line 164) | func createClient(hc *http.Client) *Client {

FILE: resty_test.go
  function getTestDataPath (line 43) | func getTestDataPath() string {
  function createGetServer (line 48) | func createGetServer(t *testing.T) *httptest.Server {
  function handleLoginEndpoint (line 172) | func handleLoginEndpoint(t *testing.T, w http.ResponseWriter, r *http.Re...
  function handleUsersEndpoint (line 239) | func handleUsersEndpoint(t *testing.T, w http.ResponseWriter, r *http.Re...
  function createPostServer (line 273) | func createPostServer(t *testing.T) *httptest.Server {
  function createFormPostServer (line 356) | func createFormPostServer(t *testing.T) *httptest.Server {
  function createFormPatchServer (line 436) | func createFormPatchServer(t *testing.T) *httptest.Server {
  function createFileUploadServer (line 484) | func createFileUploadServer(t *testing.T) *httptest.Server {
  function createAuthServer (line 525) | func createAuthServer(t *testing.T) *httptest.Server {
  function createAuthServerTLSOptional (line 529) | func createAuthServerTLSOptional(t *testing.T, useTLS bool) *httptest.Se...
  function createGenericServer (line 588) | func createGenericServer(t *testing.T) *httptest.Server {
  function createRedirectServer (line 714) | func createRedirectServer(t *testing.T) *httptest.Server {
  function createUnixSocketEchoServer (line 743) | func createUnixSocketEchoServer(t *testing.T) string {
  function createDigestServer (line 771) | func createDigestServer(t *testing.T, conf *digestServerConfig) *httptes...
  function authorizationHeaderValid (line 827) | func authorizationHeaderValid(t *testing.T, r *http.Request, conf *diges...
  function createTestServer (line 897) | func createTestServer(fn func(w http.ResponseWriter, r *http.Request)) *...
  function createTestTLSServer (line 901) | func createTestTLSServer(fn func(w http.ResponseWriter, r *http.Request)...
  function dcnl (line 916) | func dcnl() *Client {
  function dcnld (line 922) | func dcnld() *Client {
  function dcldb (line 926) | func dcldb() (*Client, *bytes.Buffer) {
  function dcnlr (line 934) | func dcnlr() *Request {
  function dcnldr (line 938) | func dcnldr() *Request {
  function assertNil (line 944) | func assertNil(t *testing.T, v any, failureMsgs ...string) {
  function assertNotNil (line 951) | func assertNotNil(t *testing.T, v any, failureMsgs ...string) {
  function assertType (line 958) | func assertType(t *testing.T, typ, v any, failureMsgs ...string) {
  function assertError (line 965) | func assertError(t *testing.T, err error, failureMsgs ...string) {
  function assertErrorIs (line 972) | func assertErrorIs(t *testing.T, e, g error, failureMsgs ...string) (r b...
  function assertTrue (line 981) | func assertTrue(t *testing.T, g any, failureMsgs ...string) (r bool) {
  function assertFalse (line 990) | func assertFalse(t *testing.T, g any, failureMsgs ...string) (r bool) {
  function assertEqual (line 999) | func assertEqual(t *testing.T, e, g any, failureMsgs ...string) (r bool) {
  function assertNotEqual (line 1008) | func assertNotEqual(t *testing.T, e, g any, failureMsgs ...string) (r bo...
  function equal (line 1019) | func equal(expected, got any) bool {
  function isNil (line 1023) | func isNil(v any) bool {
  function logResponse (line 1037) | func logResponse(t *testing.T, resp *Response) {
  function cleanupFiles (line 1046) | func cleanupFiles(files ...string) {
  function createBinFile (line 1058) | func createBinFile(fileName string, size int64) string {

FILE: retry.go
  constant defaultWaitTime (line 21) | defaultWaitTime    = time.Duration(100) * time.Millisecond
  constant defaultMaxWaitTime (line 22) | defaultMaxWaitTime = time.Duration(2000) * time.Millisecond
  type RetryConditionFunc (line 28) | type RetryConditionFunc
  type RetryHookFunc (line 31) | type RetryHookFunc
  type RetryDelayStrategyFunc (line 35) | type RetryDelayStrategyFunc
  function RetryConstantDelayStrategy (line 39) | func RetryConstantDelayStrategy(delay time.Duration) RetryDelayStrategyF...
  function applyRetryDefaultConditions (line 51) | func applyRetryDefaultConditions(res *Response, err error) bool {
  function newBackoffWithJitter (line 88) | func newBackoffWithJitter(min, max time.Duration) *backoffWithJitter {
  type backoffWithJitter (line 104) | type backoffWithJitter struct
    method NextWaitDuration (line 111) | func (b *backoffWithJitter) NextWaitDuration(c *Client, res *Response,...
    method defaultDelayStrategy (line 135) | func (b *backoffWithJitter) defaultDelayStrategy(attempt int) time.Dur...
    method randDuration (line 144) | func (b *backoffWithJitter) randDuration(center time.Duration) time.Du...
    method balanceMinMax (line 153) | func (b *backoffWithJitter) balanceMinMax(delay time.Duration) time.Du...
  function parseRetryAfterHeader (line 175) | func parseRetryAfterHeader(v string) (time.Duration, bool) {

FILE: retry_test.go
  function TestRetryConditionalGet (line 26) | func TestRetryConditionalGet(t *testing.T) {
  function TestRequestConditionalGet (line 54) | func TestRequestConditionalGet(t *testing.T) {
  function TestClientRetryGetWithTimeout (line 88) | func TestClientRetryGetWithTimeout(t *testing.T) {
  function TestClientRetryWithMinAndMaxWaitTime (line 105) | func TestClientRetryWithMinAndMaxWaitTime(t *testing.T) {
  function TestClientRetryWaitMaxInfinite (line 152) | func TestClientRetryWaitMaxInfinite(t *testing.T) {
  function TestClientRetryWaitMaxMinimum (line 193) | func TestClientRetryWaitMaxMinimum(t *testing.T) {
  function TestClientRetryDelayStrategyFuncError (line 207) | func TestClientRetryDelayStrategyFuncError(t *testing.T) {
  function TestClientRetryDelayStrategyFunc (line 245) | func TestClientRetryDelayStrategyFunc(t *testing.T) {
  function TestRequestRetryDelayStrategyFunc (line 290) | func TestRequestRetryDelayStrategyFunc(t *testing.T) {
  function TestClientRetryDelayStrategyWaitTooShort (line 337) | func TestClientRetryDelayStrategyWaitTooShort(t *testing.T) {
  function TestClientRetryDelayStrategyWaitTooLong (line 382) | func TestClientRetryDelayStrategyWaitTooLong(t *testing.T) {
  function TestClientRetryCancel (line 427) | func TestClientRetryCancel(t *testing.T) {
  function TestClientRetryPost (line 468) | func TestClientRetryPost(t *testing.T) {
  function TestClientRetryErrorRecover (line 506) | func TestClientRetryErrorRecover(t *testing.T) {
  function TestClientRetryCountWithTimeout (line 537) | func TestClientRetryCountWithTimeout(t *testing.T) {
  function TestClientRetryTooManyRequestsAndRecover (line 563) | func TestClientRetryTooManyRequestsAndRecover(t *testing.T) {
  function TestClientRetryHookWithTimeout (line 588) | func TestClientRetryHookWithTimeout(t *testing.T) {
  type failingSeeker (line 626) | type failingSeeker struct
    method Read (line 630) | func (f failingSeeker) Read(b []byte) (n int, err error) {
    method Seek (line 634) | func (f failingSeeker) Seek(offset int64, whence int) (int64, error) {
  function TestResetMultipartReaderSeekStartError (line 642) | func TestResetMultipartReaderSeekStartError(t *testing.T) {
  function TestClientResetMultipartReaders (line 662) | func TestClientResetMultipartReaders(t *testing.T) {
  function TestRequestResetMultipartReaders (line 693) | func TestRequestResetMultipartReaders(t *testing.T) {
  function TestParseRetryAfterHeader (line 724) | func TestParseRetryAfterHeader(t *testing.T) {
  function TestRequestRetryTooManyRequestsHeaderRetryAfter (line 755) | func TestRequestRetryTooManyRequestsHeaderRetryAfter(t *testing.T) {
  function TestRetryDefaultConditions (line 777) | func TestRetryDefaultConditions(t *testing.T) {
  function TestRequestRetryPutIoReadSeekerForBuffer (line 831) | func TestRequestRetryPutIoReadSeekerForBuffer(t *testing.T) {
  function TestRequestRetryPostIoReadSeeker (line 863) | func TestRequestRetryPostIoReadSeeker(t *testing.T) {
  function TestRequestRetryHooks (line 894) | func TestRequestRetryHooks(t *testing.T) {
  function TestRequestSetRetryConditions (line 927) | func TestRequestSetRetryConditions(t *testing.T) {
  function TestRequestRetryQueryParamsGH938 (line 951) | func TestRequestRetryQueryParamsGH938(t *testing.T) {
  function TestRetryConstantDelayStrategyReturnsGivenDelay (line 980) | func TestRetryConstantDelayStrategyReturnsGivenDelay(t *testing.T) {
  function TestRetryConstantDelayStrategyZeroAndNegative (line 989) | func TestRetryConstantDelayStrategyZeroAndNegative(t *testing.T) {
  function TestRetryConstantDelayUsingMinAndMaxWaitTime (line 1004) | func TestRetryConstantDelayUsingMinAndMaxWaitTime(t *testing.T) {
  function TestRetryConstantDelayUsingStrategy (line 1050) | func TestRetryConstantDelayUsingStrategy(t *testing.T) {
  function TestRetryCoverage (line 1095) | func TestRetryCoverage(t *testing.T) {
  function parseTimeSleptFromResponse (line 1115) | func parseTimeSleptFromResponse(v string) uint64 {
  function testStaticTime (line 1120) | func testStaticTime(t *testing.T) {

FILE: sse.go
  type SSEOpenFunc (line 44) | type SSEOpenFunc
  type SSEMessageFunc (line 48) | type SSEMessageFunc
  type SSEErrorFunc (line 52) | type SSEErrorFunc
  type SSERequestFailureFunc (line 56) | type SSERequestFailureFunc
  type SSE (line 59) | type SSE struct
  type SSESource (line 69) | type SSESource struct
    method SetURL (line 133) | func (sse *SSESource) SetURL(url string) *SSESource {
    method SetMethod (line 141) | func (sse *SSESource) SetMethod(method string) *SSESource {
    method SetHeader (line 152) | func (sse *SSESource) SetHeader(header, value string) *SSESource {
    method SetBody (line 163) | func (sse *SSESource) SetBody(body io.Reader) *SSESource {
    method TLSClientConfig (line 184) | func (sse *SSESource) TLSClientConfig() *tls.Config {
    method SetTLSClientConfig (line 202) | func (sse *SSESource) SetTLSClientConfig(tlsConfig *tls.Config) *SSESo...
    method tlsConfig (line 223) | func (sse *SSESource) tlsConfig() (*tls.Config, error) {
    method AddHeader (line 248) | func (sse *SSESource) AddHeader(header, value string) *SSESource {
    method SetRetryCount (line 263) | func (sse *SSESource) SetRetryCount(count int) *SSESource {
    method SetRetryWaitTime (line 278) | func (sse *SSESource) SetRetryWaitTime(waitTime time.Duration) *SSESou...
    method SetRetryMaxWaitTime (line 293) | func (sse *SSESource) SetRetryMaxWaitTime(maxWaitTime time.Duration) *...
    method SetSizeMaxBuffer (line 305) | func (sse *SSESource) SetSizeMaxBuffer(bufSize int) *SSESource {
    method Logger (line 313) | func (sse *SSESource) Logger() Logger {
    method SetLogger (line 322) | func (sse *SSESource) SetLogger(l Logger) *SSESource {
    method outputLogTo (line 330) | func (sse *SSESource) outputLogTo(w io.Writer) *SSESource {
    method OnOpen (line 343) | func (sse *SSESource) OnOpen(ef SSEOpenFunc) *SSESource {
    method OnError (line 360) | func (sse *SSESource) OnError(ef SSEErrorFunc) *SSESource {
    method OnRequestFailure (line 381) | func (sse *SSESource) OnRequestFailure(ef SSERequestFailureFunc) *SSES...
    method OnMessage (line 413) | func (sse *SSESource) OnMessage(ef SSEMessageFunc, result any) *SSESou...
    method AddEventListener (line 440) | func (sse *SSESource) AddEventListener(eventName string, ef SSEMessage...
    method Get (line 469) | func (sse *SSESource) Get() error {
    method Close (line 504) | func (sse *SSESource) Close() {
    method enableConnect (line 510) | func (sse *SSESource) enableConnect() {
    method isClosed (line 516) | func (sse *SSESource) isClosed() bool {
    method triggerOnOpen (line 522) | func (sse *SSESource) triggerOnOpen(hdr http.Header) {
    method triggerOnError (line 530) | func (sse *SSESource) triggerOnError(err error) {
    method triggerOnRequestFailure (line 538) | func (sse *SSESource) triggerOnRequestFailure(err error, res *http.Res...
    method createRequest (line 546) | func (sse *SSESource) createRequest() (*http.Request, error) {
    method connect (line 569) | func (sse *SSESource) connect() (*http.Response, error) {
    method listenStream (line 637) | func (sse *SSESource) listenStream(res *http.Response) error {
    method processEvent (line 669) | func (sse *SSESource) processEvent(scanner *bufio.Scanner) error {
    method handleCallback (line 713) | func (sse *SSESource) handleCallback(e *SSE) {
  type callback (line 90) | type callback struct
  function NewSSESource (line 113) | func NewSSESource() *SSESource {
  function readEventFunc (line 739) | func readEventFunc(scanner *bufio.Scanner) ([]byte, error) {
  function wrapResponse (line 750) | func wrapResponse(res *http.Response, req *http.Request) *Response {
  type rawSSE (line 757) | type rawSSE struct
  function parseEventFunc (line 768) | func parseEventFunc(msg []byte) (*rawSSE, error) {
  function trimHeader (line 801) | func trimHeader(size int, data []byte) []byte {
  function newRawEvent (line 817) | func newRawEvent() *rawSSE {
  function putRawEvent (line 826) | func putRawEvent(e *rawSSE) {

FILE: sse_test.go
  function TestSSESourceSimpleFlow (line 23) | func TestSSESourceSimpleFlow(t *testing.T) {
  function TestSSESourceMultipleEventTypes (line 60) | func TestSSESourceMultipleEventTypes(t *testing.T) {
  function TestSSESourceOverwriteFuncs (line 133) | func TestSSESourceOverwriteFuncs(t *testing.T) {
  function TestSSESourceRetry (line 187) | func TestSSESourceRetry(t *testing.T) {
  function TestSSESourceRetryReusesRequestBody (line 268) | func TestSSESourceRetryReusesRequestBody(t *testing.T) {
  function TestSSESourceTLSConfigerInterface (line 309) | func TestSSESourceTLSConfigerInterface(t *testing.T) {
  function TestSSESourceNoRetryRequired (line 354) | func TestSSESourceNoRetryRequired(t *testing.T) {
  function TestGH1044TrimHeader (line 367) | func TestGH1044TrimHeader(t *testing.T) {
  function TestGH1041RequestFailureWithResponseBody (line 386) | func TestGH1041RequestFailureWithResponseBody(t *testing.T) {
  function TestSSESourceHTTPError (line 411) | func TestSSESourceHTTPError(t *testing.T) {
  function TestSSESourceParseAndReadError (line 423) | func TestSSESourceParseAndReadError(t *testing.T) {
  function TestSSESourceReadError (line 459) | func TestSSESourceReadError(t *testing.T) {
  function TestSSESourceWithDifferentMethods (line 480) | func TestSSESourceWithDifferentMethods(t *testing.T) {
  function TestSSESource_readEventFunc (line 574) | func TestSSESource_readEventFunc(t *testing.T) {
  function TestSSESourceCoverage (line 633) | func TestSSESourceCoverage(t *testing.T) {
  function TestSSESetBody (line 652) | func TestSSESetBody(t *testing.T) {
  function createSSESource (line 668) | func createSSESource(t *testing.T, url string, fn SSEMessageFunc, rt any...
  function createSSETestServer (line 691) | func createSSETestServer(t *testing.T, ticker time.Duration, fn func(io....
  function createMethodVerifyingSSETestServer (line 726) | func createMethodVerifyingSSETestServer(

FILE: stream.go
  type ContentTypeEncoder (line 35) | type ContentTypeEncoder
  type ContentTypeDecoder (line 38) | type ContentTypeDecoder
  type ContentDecompresser (line 46) | type ContentDecompresser
  function encodeJSON (line 49) | func encodeJSON(w io.Writer, v any) error {
  function encodeJSONEscapeHTML (line 53) | func encodeJSONEscapeHTML(w io.Writer, v any, esc bool) error {
  function encodeJSONEscapeHTMLIndent (line 59) | func encodeJSONEscapeHTMLIndent(w io.Writer, v any, esc bool, indent str...
  function decodeJSON (line 66) | func decodeJSON(r io.Reader, v any) error {
  function doDecodeJSON (line 90) | func doDecodeJSON(dec *json.Decoder, v any) error {
  function encodeXML (line 103) | func encodeXML(w io.Writer, v any) error {
  function decodeXML (line 107) | func decodeXML(r io.Reader, v any) error {
  type gzipReaderWrapper (line 132) | type gzipReaderWrapper struct
    method Read (line 191) | func (w *gzipReaderWrapper) Read(p []byte) (n int, err error) {
    method Close (line 202) | func (w *gzipReaderWrapper) Close() error {
  function acquireGzipReader (line 140) | func acquireGzipReader(r io.ReadCloser) (*gzipReaderWrapper, error) {
  function releaseGzipReader (line 171) | func releaseGzipReader(w *gzipReaderWrapper) {
  function decompressGzip (line 186) | func decompressGzip(r io.ReadCloser) (io.ReadCloser, error) {
  type deflateReaderWrapper (line 219) | type deflateReaderWrapper struct
    method Read (line 271) | func (w *deflateReaderWrapper) Read(p []byte) (n int, err error) {
    method Close (line 282) | func (w *deflateReaderWrapper) Close() error {
  function acquireDeflateReader (line 227) | func acquireDeflateReader(r io.ReadCloser) (*deflateReaderWrapper, error) {
  function releaseDeflateReader (line 251) | func releaseDeflateReader(w *deflateReaderWrapper) {
  function decompressDeflate (line 266) | func decompressDeflate(r io.ReadCloser) (io.ReadCloser, error) {
  type resetter (line 294) | type resetter interface
  constant unlimitedRead (line 298) | unlimitedRead = 0
  type limitReadCloser (line 300) | type limitReadCloser struct
    method Read (line 307) | func (l *limitReadCloser) Read(p []byte) (n int, err error) {
    method Close (line 329) | func (l *limitReadCloser) Close() error {
    method Reset (line 336) | func (l *limitReadCloser) Reset() error {
  type copyReadCloser (line 343) | type copyReadCloser struct
    method Read (line 350) | func (r *copyReadCloser) Read(p []byte) (int, error) {
    method Close (line 364) | func (r *copyReadCloser) Close() error {
  type nopReadCloser (line 373) | type nopReadCloser struct
    method Read (line 378) | func (r *nopReadCloser) Read(p []byte) (int, error) {
    method Close (line 386) | func (r *nopReadCloser) Close() error { return nil }
    method Reset (line 389) | func (r *nopReadCloser) Reset() {
  type nopReader (line 403) | type nopReader struct
    method Read (line 405) | func (nopReader) Read([]byte) (int, error) { return 0, io.EOF }
    method ReadByte (line 406) | func (nopReader) ReadByte() (byte, error)  { return 0, io.EOF }
  type gracefulStopReader (line 408) | type gracefulStopReader struct
    method Read (line 413) | func (gsr *gracefulStopReader) Read(p []byte) (n int, err error) {

FILE: stream_test.go
  function TestDecodeJSONWhenResponseBodyIsNull (line 15) | func TestDecodeJSONWhenResponseBodyIsNull(t *testing.T) {
  function TestGetMethodWhenResponseIsNull (line 29) | func TestGetMethodWhenResponseIsNull(t *testing.T) {
  function TestDecodeJSON (line 50) | func TestDecodeJSON(t *testing.T) {
  function TestWrapCopyReadCloser (line 126) | func TestWrapCopyReadCloser(t *testing.T) {
  function TestMultipleJSONObjectsSupport (line 153) | func TestMultipleJSONObjectsSupport(t *testing.T) {
  function TestGzipReaderPanicOnConcurrentCorruptedBody (line 191) | func TestGzipReaderPanicOnConcurrentCorruptedBody(t *testing.T) {
  function TestGzipReaderAcquireAndResetError (line 290) | func TestGzipReaderAcquireAndResetError(t *testing.T) {
  function TestGzipReaderPoolConcurrentAccess (line 325) | func TestGzipReaderPoolConcurrentAccess(t *testing.T) {
  function createGzipValidData (line 360) | func createGzipValidData() []byte {
  function createDeflateValidData (line 368) | func createDeflateValidData() []byte {
  function TestDeflateReaderPanicOnConcurrentCorruptedBody (line 378) | func TestDeflateReaderPanicOnConcurrentCorruptedBody(t *testing.T) {
  function TestDeflateReaderPoolAcquireAndRead (line 474) | func TestDeflateReaderPoolAcquireAndRead(t *testing.T) {
  function TestDeflateReaderPoolConcurrentAccess (line 494) | func TestDeflateReaderPoolConcurrentAccess(t *testing.T) {
  function TestLimitCloserResetterInterface (line 526) | func TestLimitCloserResetterInterface(t *testing.T) {
  function TestDecodeXML (line 546) | func TestDecodeXML(t *testing.T) {
  function TestStreamMisc (line 601) | func TestStreamMisc(t *testing.T) {

FILE: trace.go
  type TraceInfo (line 19) | type TraceInfo struct
    method String (line 64) | func (ti TraceInfo) String() string {
    method JSON (line 84) | func (ti TraceInfo) JSON() string {
    method Clone (line 89) | func (ti TraceInfo) Clone() *TraceInfo {
  type clientTrace (line 98) | type clientTrace struct
    method createContext (line 112) | func (t *clientTrace) createContext(ctx context.Context) context.Conte...

FILE: transport_dial.go
  function transportDialContext (line 15) | func transportDialContext(dialer *net.Dialer) func(context.Context, stri...

FILE: transport_dial_wasm.go
  function transportDialContext (line 15) | func transportDialContext(_ *net.Dialer) func(context.Context, string, s...

FILE: util.go
  type Logger (line 38) | type Logger interface
  function createLogger (line 44) | func createLogger() *logger {
  type logger (line 51) | type logger struct
    method Errorf (line 55) | func (l *logger) Errorf(format string, v ...any) {
    method Warnf (line 59) | func (l *logger) Warnf(format string, v ...any) {
    method Debugf (line 63) | func (l *logger) Debugf(format string, v ...any) {
    method output (line 67) | func (l *logger) output(format string, v ...any) {
  type credentials (line 140) | type credentials struct
    method Clone (line 146) | func (c *credentials) Clone() *credentials {
    method String (line 153) | func (c credentials) String() string {
  function isStringEmpty (line 162) | func isStringEmpty(str string) bool {
  function detectContentType (line 167) | func detectContentType(body any) string {
  function isJSONContentType (line 186) | func isJSONContentType(ct string) bool {
  function isXMLContentType (line 190) | func isXMLContentType(ct string) bool {
  function inferContentTypeMapKey (line 194) | func inferContentTypeMapKey(v string) string {
  function firstNonEmpty (line 203) | func firstNonEmpty(v ...string) string {
  function createDirectory (line 218) | func createDirectory(dir string) (err error) {
  function getPointer (line 229) | func getPointer(v any) any {
  function inferType (line 240) | func inferType(v any) reflect.Type {
  function inferKind (line 244) | func inferKind(v any) reflect.Kind {
  function newInterface (line 248) | func newInterface(v any) any {
  function functionName (line 255) | func functionName(i any) string {
  function acquireBuffer (line 259) | func acquireBuffer() *bytes.Buffer {
  function releaseBuffer (line 269) | func releaseBuffer(buf *bytes.Buffer) {
  function backToBufPool (line 276) | func backToBufPool(buf *bytes.Buffer) {
  function closeq (line 282) | func closeq(v any) {
  function silently (line 288) | func silently(_ ...any) {}
  function isSanitizeHeader (line 296) | func isSanitizeHeader(k string) bool {
  function sanitizeHeaders (line 306) | func sanitizeHeaders(hdr http.Header) http.Header {
  function composeHeaders (line 315) | func composeHeaders(hdr http.Header) string {
  function sortHeaderKeys (line 323) | func sortHeaderKeys(hdr http.Header) []string {
  function wrapErrors (line 332) | func wrapErrors(n error, inner error) error {
  type restyError (line 348) | type restyError struct
    method Error (line 353) | func (e *restyError) Error() string {
    method Unwrap (line 357) | func (e *restyError) Unwrap() error {
  function cloneURLValues (line 362) | func cloneURLValues(v url.Values) url.Values {
  function cloneCookie (line 369) | func cloneCookie(c *http.Cookie) *http.Cookie {
  type invalidRequestError (line 386) | type invalidRequestError struct
    method Error (line 390) | func (ire *invalidRequestError) Error() string {
  function drainBody (line 394) | func drainBody(res *Response) {
  function drainReadCloser (line 400) | func drainReadCloser(body io.ReadCloser) {
  function toJSON (line 407) | func toJSON(v any) string {
  function formatAnyToString (line 416) | func formatAnyToString(value any) string {
  function newGUID (line 498) | func newGUID() string {
  function readRandomUint32 (line 519) | func readRandomUint32() uint32 {
  function readMachineID (line 534) | func readMachineID() []byte {

FILE: util_test.go
  function TestIsJSONContentType (line 22) | func TestIsJSONContentType(t *testing.T) {
  function TestIsXMLContentType (line 56) | func TestIsXMLContentType(t *testing.T) {
  function TestCloneURLValues (line 88) | func TestCloneURLValues(t *testing.T) {
  function TestRestyErrorFuncs (line 100) | func TestRestyErrorFuncs(t *testing.T) {
  function Test_createDirectory (line 117) | func Test_createDirectory(t *testing.T) {
  function TestUtil_readRandomUint32 (line 131) | func TestUtil_readRandomUint32(t *testing.T) {
  function TestUtil_readMachineID (line 149) | func TestUtil_readMachineID(t *testing.T) {
  function TestInMemoryJSONMarshalUnmarshal (line 186) | func TestInMemoryJSONMarshalUnmarshal(t *testing.T) {
  function TestInMemoryXMLMarshalUnmarshal (line 231) | func TestInMemoryXMLMarshalUnmarshal(t *testing.T) {
  function TestInMemoryJSONPost (line 276) | func TestInMemoryJSONPost(t *testing.T) {
  function TestInMemoryXMLPost (line 302) | func TestInMemoryXMLPost(t *testing.T) {
  function TestUtilMiscTestCoverage (line 328) | func TestUtilMiscTestCoverage(t *testing.T) {
  type customStringer (line 347) | type customStringer struct
    method String (line 351) | func (c customStringer) String() string {
  function TestFormatAnyToString (line 355) | func TestFormatAnyToString(t *testing.T) {
Condensed preview — 51 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (713K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 155,
    "preview": "github: [jeevatkm]\ncustom: [\"https://www.paypal.com/donate/?cmd=_donations&business=QWMZG74FW4QYC&lc=US&item_name=Resty+"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1327,
    "preview": "name: CI\n\non:\n  push:\n    branches:\n      - v3\n      - v2\n    paths-ignore:\n      - '**.md'\n      - '**.bazel'\n      - '"
  },
  {
    "path": ".github/workflows/label-actions.yml",
    "chars": 1157,
    "preview": "name: 'Label'\n\non:\n  pull_request:\n    types: [labeled]\n    paths-ignore:\n      - '**.md'\n      - '**.bazel'\n      - 'WO"
  },
  {
    "path": ".gitignore",
    "chars": 334,
    "preview": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture spe"
  },
  {
    "path": ".testdata/cert.pem",
    "chars": 1094,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAJce5ewsoW44j0qvSABmq7owDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzA"
  },
  {
    "path": ".testdata/key.pem",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCYkTN1g/0Z3KkS\n3w0lX9yhZkwiA0obXCeFs7hpRP0"
  },
  {
    "path": ".testdata/sample-root.pem",
    "chars": 1452,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\nMRYwFAYDVQQKEw1HZW9UcnVzdCB"
  },
  {
    "path": ".testdata/text-file.txt",
    "chars": 60,
    "preview": " THIS IS TEXT FILE FOR MULTIPART UPLOAD TEST :)\n\n- go-resty\n"
  },
  {
    "path": "BUILD.bazel",
    "chars": 1495,
    "preview": "load(\"@bazel_gazelle//:def.bzl\", \"gazelle\")\nload(\"@io_bazel_rules_go//go:def.bzl\", \"go_library\", \"go_test\")\n\n# gazelle:p"
  },
  {
    "path": "LICENSE",
    "chars": 1130,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015-present Jeevanandam M., https://myjeeva.com <jeeva@myjeeva.com>\n\nPermission is"
  },
  {
    "path": "README.md",
    "chars": 3603,
    "preview": "<p align=\"center\">\n  <img src=\"https://resty.dev/svg/resty-logo.svg\" width=\"175\" alt=\"Resty Logo\">\n</p>\n<p align=\"center"
  },
  {
    "path": "WORKSPACE",
    "chars": 1086,
    "preview": "workspace(name = \"resty\")\n\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name "
  },
  {
    "path": "benchmark_test.go",
    "chars": 7947,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "cert_watcher_test.go",
    "chars": 6913,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "circuit_breaker.go",
    "chars": 9334,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "circuit_breaker_test.go",
    "chars": 12990,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "client.go",
    "chars": 78803,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "client_test.go",
    "chars": 41489,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "context_test.go",
    "chars": 5744,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// 2016 Andrew Grigorev (https://g"
  },
  {
    "path": "curl.go",
    "chars": 2558,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "curl_test.go",
    "chars": 6970,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "debug.go",
    "chars": 5375,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "digest.go",
    "chars": 9195,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// 2023 Segev Dagan (https://githu"
  },
  {
    "path": "digest_test.go",
    "chars": 9338,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "go.mod",
    "chars": 65,
    "preview": "module resty.dev/v3\n\ngo 1.23.0\n\nrequire golang.org/x/net v0.43.0\n"
  },
  {
    "path": "go.sum",
    "chars": 153,
    "preview": "golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=\ngolang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsI"
  },
  {
    "path": "hedging.go",
    "chars": 7884,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// 2025 Ahmet Demir (https://githu"
  },
  {
    "path": "hedging_test.go",
    "chars": 17003,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "load_balancer.go",
    "chars": 11306,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "load_balancer_test.go",
    "chars": 17822,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "middleware.go",
    "chars": 16582,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "middleware_test.go",
    "chars": 28366,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "multipart.go",
    "chars": 5025,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "multipart_test.go",
    "chars": 20202,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "redirect.go",
    "chars": 3473,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "request.go",
    "chars": 58528,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "request_test.go",
    "chars": 70838,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "response.go",
    "chars": 9162,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "resty.go",
    "chars": 6128,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "resty_test.go",
    "chars": 31605,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "retry.go",
    "chars": 5432,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "retry_test.go",
    "chars": 32081,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "sse.go",
    "chars": 21021,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "sse_test.go",
    "chars": 19426,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "stream.go",
    "chars": 10411,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "stream_test.go",
    "chars": 16541,
    "preview": "package resty\n\nimport (\n\t\"bytes\"\n\t\"compress/flate\"\n\t\"compress/gzip\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\""
  },
  {
    "path": "trace.go",
    "chars": 4869,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "transport_dial.go",
    "chars": 384,
    "preview": "// Copyright 2021 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
  },
  {
    "path": "transport_dial_wasm.go",
    "chars": 378,
    "preview": "// Copyright 2021 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license "
  },
  {
    "path": "util.go",
    "chars": 12620,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  },
  {
    "path": "util_test.go",
    "chars": 11472,
    "preview": "// Copyright (c) 2015-present Jeevanandam M (jeeva@myjeeva.com), All rights reserved.\n// resty source code and usage is "
  }
]

About this extraction

This page contains the full source code of the go-resty/resty GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 51 files (634.8 KB), approximately 181.3k tokens, and a symbol index with 1073 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!