ntext"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"sync"

	"github.com/valyala/fasthttp"

	"github.com/gofiber/fiber/v3"
	"github.com/gofiber/fiber/v3/addon/retry"
)

const boundary = "FiberFormBoundary"

// RequestHook is a function invoked before the request is sent.
// It receives a Client and a Request, allowing you to modify the Request or Client data.
type RequestHook func(*Client, *Request) error

// ResponseHook is a function invoked after a response is received.
// It receives a Client, Respons

don/retry` package.
type RetryConfig = retry.Config

// addMissingPort appends the appropriate port number to the given address if it doesn't have one.
// If isTLS is true, it uses port 443; otherwise, it uses port 80.
func addMissingPort(addr string, isTL

e, and Request, allowing you to modify the Response data
// or perform actions based on the response.
type ResponseHook func(*Client, *Response, *Request) error

// RetryConfig is an alias for the `retry.Config` type from the `ad


x509/pkix"
	"encoding/pem"
	"errors"
	"fmt"
	"math/big"
	"net"
	"time"
)

var errAppendCACert = errors.New("failed to append CA certificate to certificate pool")

// GetTLSConfigs generates TLS configurations for a test server and client that
// trust each other using an in-memory certificate authority.
func GetTLSConfigs() (serverTLSConf, clientTLSConf *tls.Config, err error) { //nolint:nonamedreturns // gocritic unnamedResult prefers naming server and client TLS configurations along with the error
	// set

tring{"Amsterdam"},
			StreetAddress: []string{"Huidenstraat"},
			PostalCode:    []string{"1011 AA"},
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(10, 0, 0),
		IsCA:                  true,
		ExtKeyUsage:

 up our CA certificate
	ca := &x509.Certificate{
		SerialNumber: big.NewInt(2021),
		Subject: pkix.Name{
			Organization:  []string{"Fiber"},
			Country:       []string{"NL"},
			Province:      []string{""},
			Locality:      []s


e binder

import (
	"errors"

	"github.com/gofiber/utils/v2"
)

var ErrMsgPackNotConfigured = errors.New("binder: msgpack is not configured, please check docs: https://docs.gofiber.io/next/guide/advance-format#msgpack")

// MsgPackBinding is the MsgPack binder for MsgPack request body.
type MsgPackBinding struct {
	MsgPackDecoder utils.MsgPackUnmarshal
}

// Name returns the binding name.
func (*MsgPackBinding) Name() string {
	return "msgpack"
}

// Bind parses the request body as MsgPack and returns the r

gpackMarshal returns an error to signal that a MsgPack marshaler must
// be configured before MsgPack support can be used.
func UnimplementedMsgpackMarshal(_ any) ([]byte, error) {
	return nil, ErrMsgPackNotConfigured
}

// UnimplementedMsgpackUnmarshal re

esult.
func (b *MsgPackBinding) Bind(body []byte, out any) error {
	return b.MsgPackDecoder(body, out)
}

// Reset resets the MsgPackBinding binder.
func (b *MsgPackBinding) Reset() {
	b.MsgPackDecoder = nil
}

// UnimplementedMs


ng: true,
	}
	require.Equal(t, "query", b.Name())

	type Post struct {
		Title string `query:"title"`
	}

	type User struct {
		Name  string   `query:"name"`
		Names []string `query:"names"`
		Posts []Post   `query:"posts"`
		Age   int      `query:"age"`
	}
	var user User

	req := fasthttp.AcquireRequest()
	req.URI().SetQueryString("name=john&names=john,doe&age=42&posts[0][title]=post1&posts[1][title]=post2&posts[2][title]=post3")

	t.Cleanup(func() {
		fasthttp.ReleaseRequest(req)
	})

	err := b.Bind(req,

	require.Equal(t, "post3", user.Posts[2].Title)
	require.Contains(t, user.Names, "john")
	require.Contains(t, user.Names, "doe")

	b.Reset()
	require.False(t, b.EnableSplitting)
}

func Benchmark_QueryBinder_Bind(b *testing.B) {
	b.ReportAllocs()

	binder

&user)

	require.NoError(t, err)
	require.Equal(t, "john", user.Name)
	require.Equal(t, 42, user.Age)
	require.Len(t, user.Posts, 3)
	require.Equal(t, "post1", user.Posts[0].Title)
	require.Equal(t, "post2", user.Posts[1].Title)


	"github.com/gofiber/utils/v2"
	"github.com/valyala/fasthttp"
)

const MIMEMultipartForm string = "multipart/form-data"

var (
	formMapPool = sync.Pool{
		New: func() any {
			return make(map[string][]string)
		},
	}
	formFileMapPool = sync.Pool{
		New: func() any {
			return make(map[string][]*multipart.FileHeader)
		},
	}
)

// FormBinding is the form binder for form request body.
type FormBinding struct {
	EnableSplitting bool
	MaxBodySize     int
}

// Name returns the binding name.
func (*FormBinding)

.ContentType())) == MIMEMultipartForm {
		return b.bindMultipart(req, out)
	}

	data := acquireFormMap()
	defer releaseFormMap(data)

	for key, val := range req.PostArgs().All() {
		k := utils.UnsafeString(key)
		v := utils.UnsafeString(val)
		if err := fo

 Name() string {
	return "form"
}

// Bind parses the request body and returns the result.
func (b *FormBinding) Bind(req *fasthttp.Request, out any) error {
	// Handle multipart form
	if FilterFlags(utils.UnsafeString(req.Header


ar dummy2 struct{ f string }
	require.False(t, equalFieldType(&dummy2, reflect.String, "f", "query"))

	var user struct {
		Name    string
		Address string `query:"address"`
		Age     int    `query:"AGE"`
	}
	require.True(t, equalFieldType(&user, reflect.String, "name", "query"))
	require.True(t, equalFieldType(&user, reflect.String, "Name", "query"))
	require.True(t, equalFieldType(&user, reflect.String, "address", "query"))
	require.True(t, equalFieldType(&user, reflect.String, "Address", "query"))
	requi

     int    `query:"AGE"`
		} `query:"user"`
	}

	require.True(t, equalFieldType(&user2, reflect.String, "user.name", "query"))
	require.True(t, equalFieldType(&user2, reflect.String, "user.Name", "query"))
	require.True(t, equalFieldType(&user2, reflect.S

re.True(t, equalFieldType(&user, reflect.Int, "AGE", "query"))
	require.True(t, equalFieldType(&user, reflect.Int, "age", "query"))

	var user2 struct {
		User struct {
			Name    string
			Address string `query:"address"`
			Age


rserType require two element, type and converter for register.
// Use ParserType with BodyParser for parsing custom type in form data.
type ParserType struct {
	CustomType any
	Converter  func(string) reflect.Value
}

var (
	decoderPoolMu sync.RWMutex
	// decoderPoolMap helps to improve binders
	decoderPoolMap = map[string]*sync.Pool{}
	// tags is used to classify parser's pool
	tags = []string{bindingHeader, bindingRespHeader, bindingCookie, bindingQuery, bindingForm, bindingURI}
)

func getDecoderPool(tag

}

// SetParserDecoder allow globally change the option of form decoder, update decoderPool
func SetParserDecoder(parserConfig ParserConfig) {
	decoderPoolMu.Lock()
	defer decoderPoolMu.Unlock()

	for _, tag := range tags {
		decoderPoolMap[tag] = &sync.Po

 string) *sync.Pool {
	decoderPoolMu.RLock()
	pool := decoderPoolMap[tag]
	if pool == nil {
		decoderPoolMu.RUnlock()
		panic(fmt.Sprintf("decoder pool not initialized for tag %q", tag))
	}
	decoderPoolMu.RUnlock()

	return pool


	"testing"

	"github.com/stretchr/testify/require"
	"github.com/valyala/fasthttp"
)

func Test_RespHeaderBinder_Bind(t *testing.T) {
	t.Parallel()

	b := &RespHeaderBinding{
		EnableSplitting: true,
	}
	require.Equal(t, "respHeader", b.Name())

	type User struct {
		Name  string   `respHeader:"name"`
		Posts []string `respHeader:"posts"`
		Age   int      `respHeader:"age"`
	}
	var user User

	resp := fasthttp.AcquireResponse()
	resp.Header.Set("name", "john")
	resp.Header.Set("age", "42")
	resp.Header.Set("

[]string{"post1", "post2", "post3"}, user.Posts)

	b.Reset()
	require.False(t, b.EnableSplitting)
}

func Benchmark_RespHeaderBinder_Bind(b *testing.B) {
	b.ReportAllocs()

	binder := &RespHeaderBinding{
		EnableSplitting: true,
	}

	type User struct {
		N

posts", "post1,post2,post3")

	t.Cleanup(func() {
		fasthttp.ReleaseResponse(resp)
	})

	err := b.Bind(resp, &user)

	require.NoError(t, err)
	require.Equal(t, "john", user.Name)
	require.Equal(t, 42, user.Age)
	require.Equal(t,


	"github.com/stretchr/testify/require"
)

func Test_XMLBinding_Bind(t *testing.T) {
	t.Parallel()

	b := &XMLBinding{
		XMLDecoder: xml.Unmarshal,
	}
	require.Equal(t, "xml", b.Name())

	type Posts struct {
		XMLName xml.Name `xml:"post"`
		Title   string   `xml:"title"`
	}

	type User struct {
		Name   string  `xml:"name"`
		Ignore string  `xml:"-"`
		Posts  []Posts `xml:"posts>post"`
		Age    int     `xml:"age"`
	}

	user := new(User)
	err := b.Bind([]byte(`
		<user>
			<name>john</name>
			<age>42</age

me)
	require.Equal(t, 42, user.Age)
	require.Empty(t, user.Ignore)

	require.Len(t, user.Posts, 2)
	require.Equal(t, "post1", user.Posts[0].Title)
	require.Equal(t, "post2", user.Posts[1].Title)

	b.Reset()
	require.Nil(t, b.XMLDecoder)
}

func Test_XMLBin

>
			<ignore>ignore</ignore>
			<posts>
				<post>
					<title>post1</title>
				</post>
				<post>
					<title>post2</title>
				</post>
			</posts>
		</user>
	`), user)
	require.NoError(t, err)
	require.Equal(t, "john", user.Na


"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestConfigDefault_NoConfig(t *testing.T) {
	t.Parallel()
	cfg := configDefault()
	require.Equal(t, DefaultConfig, cfg)
}

func TestConfigDefault_Custom(t *testing.T) {
	t.Parallel()
	custom := Config{
		InitialInterval: 2 * time.Second,
		MaxBackoffTime:  64 * time.Second,
		Multiplier:      3.0,
		MaxRetryCount:   5,
		currentInterval: 2 * time.Second,
	}
	cfg := configDefault(custom)
	require.Equal(t, custom, cfg)
}

func TestConfigDefault_

	cfg := configDefault(Config{InitialInterval: 5 * time.Second})

	require.Equal(t, 5*time.Second, cfg.currentInterval)
	require.Equal(t, 5*time.Second, cfg.InitialInterval)
}

func TestConfigDefault_CustomCurrentInterval(t *testing.T) {
	t.Parallel()

	cf

PartialAndNegative(t *testing.T) {
	t.Parallel()
	cfg := configDefault(Config{Multiplier: -1, MaxRetryCount: 0})
	require.Equal(t, DefaultConfig, cfg)
}

func TestConfigDefault_CustomInitialInterval(t *testing.T) {
	t.Parallel()


	"time"
)

// ExponentialBackoff is a retry mechanism for retrying some calls.
type ExponentialBackoff struct {
	// InitialInterval is the initial time interval for backoff algorithm.
	InitialInterval time.Duration

	// MaxBackoffTime is the maximum time duration for backoff algorithm. It limits
	// the maximum sleep time.
	MaxBackoffTime time.Duration

	// Multiplier is a multiplier number of the backoff algorithm.
	Multiplier float64

	// MaxRetryCount is the maximum number of retry count.
	MaxRetryCount

 {
	cfg := configDefault(config...)
	return &ExponentialBackoff{
		InitialInterval: cfg.InitialInterval,
		MaxBackoffTime:  cfg.MaxBackoffTime,
		Multiplier:      cfg.Multiplier,
		MaxRetryCount:   cfg.MaxRetryCount,
		currentInterval: cfg.currentInterval,

int

	// currentInterval tracks the current sleep time.
	currentInterval time.Duration
}

// NewExponentialBackoff creates a ExponentialBackoff with default values.
func NewExponentialBackoff(config ...Config) *ExponentialBackoff


be nil", func() {
			NewWithClient(nil)
		})
	})
}

func Test_New_With_HostClient(t *testing.T) {
	t.Parallel()

	t.Run("with valid host client", func(t *testing.T) {
		t.Parallel()

		hc := &fasthttp.HostClient{Addr: "example.com:80"}
		client := NewWithHostClient(hc)

		require.NotNil(t, client)
		require.Equal(t, hc, client.HostClient())
		require.Nil(t, client.FasthttpClient())
	})

	t.Run("with nil host client", func(t *testing.T) {
		t.Parallel()

		require.PanicsWithValue(t, "fasthttp.HostClient must

]fasthttp.BalancingClient{
				&fasthttp.HostClient{Addr: "example.com:80"},
			},
		}

		client := NewWithLBClient(lb)

		require.NotNil(t, client)
		require.Equal(t, lb, client.LBClient())
		require.Nil(t, client.FasthttpClient())
		require.Nil(t, client

 not be nil", func() {
			NewWithHostClient(nil)
		})
	})
}

func Test_New_With_LBClient(t *testing.T) {
	t.Parallel()

	t.Run("with valid lb client", func(t *testing.T) {
		t.Parallel()

		lb := &fasthttp.LBClient{
			Clients: [


http.com/hola")
	url2 := []byte("http://fasthttp.com/make/fasthttp")
	url3 := []byte("http://fasthttp.com/make/fasthttp/great")
	cj := &CookieJar{}

	c1 := &fasthttp.Cookie{}
	c1.SetKey("k")
	c1.SetValue("v")
	c1.SetPath("/make/")

	c2 := &fasthttp.Cookie{}
	c2.SetKey("kk")
	c2.SetValue("vv")
	c2.SetPath("/make/fasthttp")

	c3 := &fasthttp.Cookie{}
	c3.SetKey("kkk")
	c3.SetValue("vvv")
	c3.SetPath("/make/fasthttp/great")

	uri := fasthttp.AcquireURI()
	require.NoError(t, uri.Parse(nil, url))

	uri1 := fasth

tp.AcquireURI()
	require.NoError(t, uri3.Parse(nil, url3))

	cj.Set(uri1, c1, c2, c3)

	cookies := cj.Get(uri1)
	require.Len(t, cookies, 1)
	for _, cookie := range cookies {
		require.True(t, bytes.HasPrefix(uri1.Path(), cookie.Path()))
	}

	cookies = cj.G

ttp.AcquireURI()
	require.NoError(t, uri1.Parse(nil, url1))

	uri11 := fasthttp.AcquireURI()
	require.NoError(t, uri11.Parse(nil, url11))

	uri2 := fasthttp.AcquireURI()
	require.NoError(t, uri2.Parse(nil, url2))

	uri3 := fastht


der

import (
	"github.com/gofiber/utils/v2"
	"github.com/valyala/fasthttp"
)

// QueryBinding is the query binder for query request body.
type QueryBinding struct {
	EnableSplitting bool
}

// Name returns the binding name.
func (*QueryBinding) Name() string {
	return bindingQuery
}

// Bind parses the request query and returns the result.
func (b *QueryBinding) Bind(reqCtx *fasthttp.Request, out any) error {
	data := make(map[string][]string)
	for key, val := range reqCtx.URI().QueryArgs().All() {
		k :=

l)
		if err := formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, true); err != nil {
			return err
		}
	}

	return parse(b.Name(), out, data)
}

// Reset resets the QueryBinding binder.
func (b *QueryBinding) Reset() {
	b.EnableSplitting = false

utils.UnsafeString(key)
		v := utils.UnsafeString(va


mport (
	"github.com/gofiber/utils/v2"
	"github.com/valyala/fasthttp"
)

// RespHeaderBinding is the respHeader binder for response header.
type RespHeaderBinding struct {
	EnableSplitting bool
}

// Name returns the binding name.
func (*RespHeaderBinding) Name() string {
	return bindingRespHeader
}

// Bind parses the response header and returns the result.
func (b *RespHeaderBinding) Bind(resp *fasthttp.Response, out any) error {
	data := make(map[string][]string)

	for key, val := range resp.Header.All()

eString(val)
		if err := formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false); err != nil {
			return err
		}
	}

	return parse(b.Name(), out, data)
}

// Reset resets the RespHeaderBinding binder.
func (b *RespHeaderBinding) Reset() {
	b.En

 {
		k := utils.UnsafeString(key)
		v := utils.Unsaf


e binder

import (
	"errors"

	"github.com/gofiber/utils/v2"
)

// CBORBinding is the CBOR binder for CBOR request body.
type CBORBinding struct {
	CBORDecoder utils.CBORUnmarshal
}

// Name returns the binding name.
func (*CBORBinding) Name() string {
	return "cbor"
}

// Bind parses the request body as CBOR and returns the result.
func (b *CBORBinding) Bind(body []byte, out any) error {
	return b.CBORDecoder(body, out)
}

// Reset resets the CBORBinding binder.
func (b *CBORBinding) Reset() {
	b.CBORDecod

iber.io/next/guide/advance-format#cbor")

// UnimplementedCborMarshal returns an error to signal that a CBOR marshaler
// must be configured before CBOR support can be used.
func UnimplementedCborMarshal(_ any) ([]byte, error) {
	return nil, ErrUnimplement

er = nil
}

// ErrUnimplementedCBOR signals that CBOR support must be explicitly configured
// before it can be used.
var ErrUnimplementedCBOR = errors.New("binder: must explicitly set up CBOR, please check docs: https://docs.gof


	"testing"

	"github.com/stretchr/testify/require"
)

func Test_URIBinding_Bind(t *testing.T) {
	t.Parallel()

	b := &URIBinding{}
	require.Equal(t, "uri", b.Name())

	type User struct {
		Name  string   `uri:"name"`
		Posts []string `uri:"posts"`
		Age   int      `uri:"age"`
	}
	var user User

	paramsKey := []string{"name", "age", "posts"}
	paramsVals := []string{"john", "42", "post1,post2,post3"}
	paramsFunc := func(key string, _ ...string) string {
		for i, k := range paramsKey {
			if k == key {
				ret

}, user.Posts)

	b.Reset()
}

func Benchmark_URIBinding_Bind(b *testing.B) {
	b.ReportAllocs()

	binder := &URIBinding{}

	type User struct {
		Name  string   `uri:"name"`
		Posts []string `uri:"posts"`
		Age   int      `uri:"age"`
	}
	var user User

	para

urn paramsVals[i]
			}
		}

		return ""
	}

	err := b.Bind(paramsKey, paramsFunc, &user)
	require.NoError(t, err)
	require.Equal(t, "john", user.Name)
	require.Equal(t, 42, user.Age)
	require.Equal(t, []string{"post1,post2,post3"


uire"
)

func Test_ExponentialBackoff_Retry(t *testing.T) {
	t.Parallel()
	tests := []struct {
		expErr     error
		expBackoff *ExponentialBackoff
		f          func() error
		name       string
	}{
		{
			name:       "With default values - successful",
			expBackoff: NewExponentialBackoff(),
			f: func() error {
				return nil
			},
		},
		{
			name: "Successful function",
			expBackoff: &ExponentialBackoff{
				InitialInterval: 1 * time.Millisecond,
				MaxBackoffTime:  100 * time.Millisecond,
				Multiplie

00 * time.Millisecond,
				Multiplier:      2.0,
				MaxRetryCount:   5,
			},
			f: func() error {
				return errors.New("failed function")
			},
			expErr: errors.New("failed function"),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testi

r:      2.0,
				MaxRetryCount:   5,
			},
			f: func() error {
				return nil
			},
		},
		{
			name: "Unsuccessful function",
			expBackoff: &ExponentialBackoff{
				InitialInterval: 2 * time.Millisecond,
				MaxBackoffTime:  1


, fasthttp.HostClient, or fasthttp.LBClient implementations.
//
// Settings configured on the client are shared across every request and may be
// overridden per request when needed.
// Client is safe for concurrent request execution after configuration is
// complete. Concurrent configuration changes require external synchronization.
type Client struct {
	logger    log.CommonLogger
	transport httpClientTransport

	header  *Header
	params  *QueryParam
	cookies *Cookie
	path    *PathParam

	jsonMarshal   uti

g          *RetryConfig
	baseURL              string
	userAgent            string
	referer              string
	userRequestHooks     []RequestHook
	builtinRequestHooks  []RequestHook
	userResponseHooks    []ResponseHook
	builtinResponseHooks []ResponseHook

ls.JSONMarshal
	jsonUnmarshal utils.JSONUnmarshal
	xmlMarshal    utils.XMLMarshal
	xmlUnmarshal  utils.XMLUnmarshal
	cborMarshal   utils.CBORMarshal
	cborUnmarshal utils.CBORUnmarshal

	cookieJar            *CookieJar
	retryConfi


hamaton/msgpack/v3"
	"github.com/stretchr/testify/require"
)

func Test_Msgpack_Binding_Bind(t *testing.T) {
	t.Parallel()

	b := &MsgPackBinding{
		MsgPackDecoder: msgpack.Unmarshal,
	}
	require.Equal(t, "msgpack", b.Name())

	type Post struct {
		Title string `msgpack:"title"`
	}

	type User struct {
		Name  string `msgpack:"name"`
		Posts []Post `msgpack:"posts"`
		Age   int    `msgpack:"age"`
	}
	var user User

	// Prepare msgpack data
	input := map[string]any{
		"name": "john",
		"age":  42,
		"posts":

"john", user.Name)
	require.Equal(t, 42, user.Age)
	require.Len(t, user.Posts, 3)
	require.Equal(t, "post1", user.Posts[0].Title)
	require.Equal(t, "post2", user.Posts[1].Title)
	require.Equal(t, "post3", user.Posts[2].Title)

	b.Reset()
	require.Nil(t, b.

 []map[string]any{
			{"title": "post1"},
			{"title": "post2"},
			{"title": "post3"},
		},
	}
	data, err := msgpack.Marshal(input)
	require.NoError(t, err)

	err = b.Bind(data, &user)
	require.NoError(t, err)
	require.Equal(t,


 &CookieJar{}
	},
}

// AcquireCookieJar returns an empty CookieJar object from the pool.
func AcquireCookieJar() *CookieJar {
	jar, ok := cookieJarPool.Get().(*CookieJar)
	if !ok {
		panic(errCookieJarTypeAssertion)
	}

	return jar
}

// ReleaseCookieJar returns a CookieJar object to the pool.
func ReleaseCookieJar(c *CookieJar) {
	c.Release()
	cookieJarPool.Put(c)
}

// CookieJar manages cookie storage for the client.
// CookieJar is safe for concurrent use, except Release. Release must not run
// concurr

	// accepted Domain attribute.
	// If release logic is re-enabled for these entries, iterate as storedCookie
	// values and call fasthttp.ReleaseCookie(stored.cookie) on the wrapped cookie.
	hostCookies map[string][]storedCookie
	mu          sync.Mutex
}

ently with other methods, and the jar must not be used after Release.
type CookieJar struct {
	// hostCookies stores wrapped cookies keyed by storage scope:
	// host-only cookies use the request host, while domain cookies use the


[]struct {
		name string
		want string
		args args
	}{
		{
			name: "do anything",
			args: args{
				addr: "example.com:1234",
			},
			want: "example.com:1234",
		},
		{
			name: "add 80 port",
			args: args{
				addr: "example.com",
			},
			want: "example.com:80",
		},
		{
			name: "add 443 port",
			args: args{
				addr:  "example.com",
				isTLS: true,
			},
			want: "example.com:443",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			require.Equal(t, tt.want

endString(c.Hostname())
	})

	app.Get("/return-error", func(_ fiber.Ctx) error {
		return errors.New("the request is error")
	})

	app.Get("/redirect", func(c fiber.Ctx) error {
		return c.Redirect().Status(fiber.StatusFound).To("/normal")
	})

	app.Get("/

, addMissingPort(tt.args.addr, tt.args.isTLS))
		})
	}
}

func Test_Exec_Func(t *testing.T) {
	t.Parallel()
	ln := fasthttputil.NewInmemoryListener()
	app := fiber.New()

	app.Get("/normal", func(c fiber.Ctx) error {
		return c.S


cationJSON   = "application/json"
	applicationCBOR   = "application/cbor"
	applicationXML    = "application/xml"
	applicationForm   = "application/x-www-form-urlencoded"
	multipartFormData = "multipart/form-data"

	letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)

// unsafeRandString returns a random string of length n.
// An error is returned if the random source fails.
func unsafeRandString(n int) (string, error) {
	inputLength := byte(len(letterBytes))

	// Compute the lar

 make([]byte, n)
	buf := make([]byte, n)

	// Read n raw bytes in one shot
	if _, err := rand.Read(buf); err != nil {
		return "", fmt.Errorf("rand.Read failed: %w", err)
	}

	for i, b := range buf {
		// Reject values ≥ maxLength
		for b >= maxLength {

gest multiple of inputLength ≤ 256 to avoid modulo bias.
	// Any byte ≥ max will be rejected and re‑read.
	maxLength := byte(256 - (256 % int(inputLength))) //nolint:gosec // G115: integer overflow conversion int -> byte

	out :=


llel()
		got, err := unsafeRandString(32)
		require.NoError(t, err)
		for i := 0; i < len(got); i++ {
			require.Contains(t, letterBytes, string(got[i]))
		}
	})
}

func Test_Parser_Request_URL(t *testing.T) {
	t.Parallel()

	t.Run("client baseurl should be set", func(t *testing.T) {
		t.Parallel()
		client := New().SetBaseURL("http://example.com/api")
		req := AcquireRequest().SetURL("")

		err := parserRequestURL(client, req)
		require.NoError(t, err)
		require.Equal(t, "http://example.com/api", req.RawRe

rror(t, err)
		require.Equal(t, "http://example.com/api", req.RawRequest.URI().String())
	})

	t.Run("the request url will override baseurl with protocol", func(t *testing.T) {
		t.Parallel()
		client := New().SetBaseURL("http://example.com/api")
		req :=

quest.URI().String())
	})

	t.Run("request url should be set", func(t *testing.T) {
		t.Parallel()
		client := New()
		req := AcquireRequest().SetURL("http://example.com/api")

		err := parserRequestURL(client, req)
		require.NoE


x.Value(key))

	ctx = context.WithValue(ctx, key, "string")
	req.SetContext(ctx)
	ctx = req.Context()

	v, ok := ctx.Value(key).(string)
	require.True(t, ok)
	require.Equal(t, "string", v)
}

func Test_Request_Header(t *testing.T) {
	t.Parallel()

	t.Run("add header", func(t *testing.T) {
		t.Parallel()
		req := AcquireRequest()
		req.AddHeader("foo", "bar").AddHeader("foo", "fiber")

		res := req.Header("foo")
		require.Len(t, res, 2)
		require.Equal(t, "bar", res[0])
		require.Equal(t, "fiber", res[1])
	}

)
	})

	t.Run("add headers", func(t *testing.T) {
		t.Parallel()
		req := AcquireRequest()
		req.SetHeader("foo", "bar").
			AddHeaders(map[string][]string{
				"foo": {"fiber", "buaa"},
				"bar": {"foo"},
			})

		res := req.Header("foo")
		require.Len(t

)

	t.Run("set header", func(t *testing.T) {
		t.Parallel()
		req := AcquireRequest()
		req.AddHeader("foo", "bar").SetHeader("foo", "fiber")

		res := req.Header("foo")
		require.Len(t, res, 1)
		require.Equal(t, "fiber", res[0]


"http://example")

		require.NoError(t, err)
		require.Equal(t, "OK", resp.Status())
		resp.Close()
	})

	t.Run("fail", func(t *testing.T) {
		t.Parallel()

		server := setupApp()
		defer server.stop()

		client := New().SetDial(server.dial())

		resp, err := AcquireRequest().
			SetClient(client).
			Get("http://example/fail")

		require.NoError(t, err)
		require.Equal(t, "Proxy Authentication Required", resp.Status())
		resp.Close()
	})
}

func Test_Response_Status_Code(t *testing.T) {
	t.Parallel()

	set

tus(407)
			})
		})

		return server
	}

	t.Run("success", func(t *testing.T) {
		t.Parallel()

		server := setupApp()
		defer server.stop()

		client := New().SetDial(server.dial())

		resp, err := AcquireRequest().
			SetClient(client).
			Get("http://ex

upApp := func() *testServer {
		server := startTestServer(t, func(app *fiber.App) {
			app.Get("/", func(c fiber.Ctx) error {
				return c.SendString("foo")
			})
			app.Get("/fail", func(c fiber.Ctx) error {
				return c.SendSta


nt *fasthttp.LBClient
}

func (l *lbBalancingClient) DoDeadline(req *fasthttp.Request, resp *fasthttp.Response, deadline time.Time) error {
	if l.client == nil {
		return nil
	}
	return l.client.DoDeadline(req, resp, deadline)
}

func (*lbBalancingClient) PendingRequests() int { return 0 }

func (l *lbBalancingClient) LBClient() *fasthttp.LBClient { return l.client }

type stubRedirectCall struct {
	err      error
	status   *int
	location *string
}

func ptrInt(v int) *int { return &v }

func ptrString(v st

(s.calls) == 0 {
		resp.Reset()
		resp.Header.SetStatusCode(fasthttp.StatusOK)
		return nil
	}

	call := s.calls[0]
	s.calls = s.calls[1:]

	resp.Reset()
	if call.status != nil {
		resp.Header.SetStatusCode(*call.status)
	}
	if call.location != nil {
		res

ring) *string { return &v }

type stubRedirectClient struct {
	calls     []stubRedirectCall
	callCount int
}

func (s *stubRedirectClient) Do(req *fasthttp.Request, resp *fasthttp.Response) error {
	_ = req
	s.callCount++
	if len


ag       = "${"
	endTag         = "}"
	paramSeparator = ":"
)

// Buffer is the render buffer exposed to template functions.
// The template renderer only requires Write, WriteByte, and WriteString, but
// the wider bytebufferpool-compatible surface is kept so existing custom
// logger tags that use methods such as Len, Bytes, Set, or String continue to
// compile when switching to the shared template renderer.
type Buffer interface {
	Len() int
	ReadFrom(r io.Reader) (int64, error)
	WriteTo(w io.Writer) (i

, D any] func(output Buffer, ctx C, data *D, extraParam string) (int, error)

// Template is a precompiled log template.
type Template[C, D any] struct {
	fixedParts [][]byte
	funcChain  []Func[C, D]
}

// Build parses format once and returns a reusable te

nt64, error)
	Bytes() []byte
	Write(p []byte) (int, error)
	WriteByte(c byte) error
	WriteString(s string) (int, error)
	Set(p []byte)
	SetString(s string)
	String() string
}

// Func renders one dynamic template tag.
type Func[C


kage redact provides common helpers for masking sensitive values in log
// output. It is internal because the redaction policy is not part of Fiber's
// public API.
package redact

const (
	// MinLength is the inclusive lower bound on input length for which the
	// redacted form keeps a leading clear-text prefix. Values shorter than
	// this are fully masked. Eight bytes is enough that a 4-byte prefix
	// never reveals more than half of a typical opaque token.
	MinLength = 8
	// PrefixLength is the number o

 returns a redacted form of value. Empty input is returned unchanged.
// Inputs shorter than MinLength are fully masked. Inputs at least MinLength
// long return the leading PrefixLength bytes followed by Mask. Operations are
// byte-wise; callers must not

f leading bytes kept when the input is at
	// least MinLength long.
	PrefixLength = 4
	// Mask is appended after the visible prefix (or returned alone when the
	// input is too short to keep a prefix).
	Mask = "****"
)

// Prefix


"time"
)

// Config defines the config for addon.
type Config struct {
	// InitialInterval defines the initial time interval for backoff algorithm.
	//
	// Optional. Default: 1 * time.Second
	InitialInterval time.Duration

	// MaxBackoffTime defines maximum time duration for backoff algorithm. When
	// the algorithm is reached this time, rest of the retries will be maximum
	// 32 seconds.
	//
	// Optional. Default: 32 * time.Second
	MaxBackoffTime time.Duration

	// Multiplier defines multiplier number of t

urrent waiting time.
	//
	// Optional. Default: 1 * time.Second
	currentInterval time.Duration
}

// DefaultConfig is the default config for retry.
var DefaultConfig = Config{
	InitialInterval: 1 * time.Second,
	MaxBackoffTime:  32 * time.Second,
	Multipli

he backoff algorithm.
	//
	// Optional. Default: 2.0
	Multiplier float64

	// MaxRetryCount defines maximum retry count for the backoff algorithm.
	//
	// Optional. Default: 10
	MaxRetryCount int

	// currentInterval tracks the c


er/fiber/v3"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/valyala/fasthttp/fasthttputil"
)

type testServer struct {
	app *fiber.App
	ch  chan struct{}
	ln  *fasthttputil.InmemoryListener
	tb  testing.TB
}

func startTestServer(tb testing.TB, beforeStarting func(app *fiber.App)) *testServer {
	tb.Helper()

	ln := fasthttputil.NewInmemoryListener()
	app := fiber.New(fiber.Config{
		CBOREncoder: cbor.Marshal,
		CBORDecoder: cbor.Unmarshal,
	})

	if beforeStarting !

ch:  ch,
		ln:  ln,
		tb:  tb,
	}
}

func (ts *testServer) stop() {
	ts.tb.Helper()

	if err := ts.app.Shutdown(); err != nil {
		ts.tb.Fatal(err)
	}

	select {
	case <-ts.ch:
	case <-time.After(time.Second):
		ts.tb.Fatalf("timeout when waiting for server

= nil {
		beforeStarting(app)
	}

	ch := make(chan struct{})
	go func() {
		err := app.Listener(ln, fiber.ListenConfig{DisableStartupMessage: true})
		assert.NoError(tb, err)

		close(ch)
	}()

	return &testServer{
		app: app,


	"runtime"
	"runtime/metrics"
	"strconv"
	"testing"
)

// BenchmarkRequestHeapScan measures how much heap memory the GC needs to scan
// when a batch of requests is created and released.
func BenchmarkRequestHeapScan(b *testing.B) {
	samples := []metrics.Sample{
		{Name: "/gc/scan/heap:bytes"},
		{Name: "/gc/scan/total:bytes"},
	}

	b.ReportAllocs()
	b.StopTimer()
	b.ResetTimer()

	const batchSize = 512
	var totalScanHeap, totalScanAll uint64
	for i := 0; i < b.N; i++ {
		reqs := make([]*Request, batchSize)

()

		b.StartTimer()
		for j := range reqs {
			req := AcquireRequest()
			req.SetHeader("X-Benchmark", "value")
			req.SetCookie("session", strconv.Itoa(j))
			req.SetPathParam("id", strconv.Itoa(j))
			req.SetParam("page", strconv.Itoa(j))
			reqs[j] = r

		// revive:disable-next-line:call-to-gc // Ensure consistent heap state before measuring scan metrics
		runtime.GC()
		metrics.Read(samples)
		startScanHeap := samples[0].Value.Uint64()
		startScanAll := samples[1].Value.Uint64


onst defaultRedirectLimit = 16

var (
	// Pre-allocated byte slice for http/https scheme comparison
	httpScheme  = []byte("http")
	httpsScheme = []byte("https")
)

// httpClientTransport unifies the operations exposed by the Fiber client across
// the fasthttp.Client, fasthttp.HostClient, and fasthttp.LBClient adapters so
// helper logic can treat the concrete transports uniformly.
type httpClientTransport interface {
	Do(req *fasthttp.Request, resp *fasthttp.Response) error
	DoTimeout(req *fasthttp.Request

loseIdleConnections()
	TLSConfig() *tls.Config
	SetTLSConfig(config *tls.Config)
	SetDial(dial fasthttp.DialFunc)
	Client() any
	StreamResponseBody() bool
	SetStreamResponseBody(enable bool)
}

// standardClientTransport adapts fasthttp.Client to the httpC

, resp *fasthttp.Response, timeout time.Duration) error
	DoDeadline(req *fasthttp.Request, resp *fasthttp.Response, deadline time.Time) error
	DoRedirects(req *fasthttp.Request, resp *fasthttp.Response, maxRedirects int) error
	C


ute(t *testing.T) {
	t.Parallel()

	tmpl, err := Build[string, testData]("a ${tag} ${param:name} z", map[string]Func[string, testData]{
		"tag": func(output Buffer, ctx string, data *testData, _ string) (int, error) {
			return output.WriteString(ctx + data.value)
		},
		"param:": func(output Buffer, _ string, _ *testData, extraParam string) (int, error) {
			return output.WriteString(extraParam)
		},
	})
	require.NoError(t, err)

	buf := bytebufferpool.Get()
	defer bytebufferpool.Put(buf)

	err = tmpl.Exec

stData]

	buf := bytebufferpool.Get()
	defer bytebufferpool.Put(buf)

	require.NoError(t, tmpl.Execute(buf, "ctx", &testData{}))
	require.Equal(t, 0, buf.Len())

	parts, funcs := tmpl.Chains()
	require.Nil(t, parts)
	require.Nil(t, funcs)
}

func Test_Temp

ute(buf, "ctx-", &testData{value: "data"})
	require.NoError(t, err)
	require.Equal(t, "a ctx-data name z", buf.String())
}

func Test_Template_NilTemplateExecuteIsNoop(t *testing.T) {
	t.Parallel()

	var tmpl *Template[string, te


ored in the request.

	body    any
	header  Header
	params  QueryParam
	cookies Cookie
	path    PathParam

	client *Client

	formData FormData

	RawRequest *fasthttp.Request
	url        string
	method     string
	userAgent  string
	boundary   string
	referer    string
	files      []*File

	timeout      time.Duration
	maxRedirects int

	bodyType bodyType

	isPathNormalizingDisabled bool
}

// Method returns the HTTP method set in the Request.
func (r *Request) Method() string {
	return r.method
}

// SetMeth

set in the Request.
func (r *Request) URL() string {
	return r.url
}

// SetURL sets the URL for the Request.
func (r *Request) SetURL(url string) *Request {
	r.url = url
	return r
}

// Client returns the Client instance associated with this Request.
func

od sets the HTTP method for the Request.
// It is recommended to use the specialized methods (e.g., Get, Post) instead.
func (r *Request) SetMethod(method string) *Request {
	r.method = method
	return r
}

// URL returns the URL


e logtemplate

import (
	"errors"
	"strconv"
)

// ErrUnknownTag indicates that the template references a tag that has no
// registered renderer. The format may be a bare ${tag} or a parametric
// ${tag:param}; in both cases the unmatched name is reported via
// UnknownTagError so callers can extract it programmatically.
var ErrUnknownTag = errors.New("logtemplate: unknown tag")

// UnknownTagError is the typed error returned when a template references an
// unknown tag. Tag is the offending tag including a

 a bare tag was
// referenced but a parametric base of the same name is registered.
type UnknownTagError struct {
	Tag   string
	Param string
	Hint  string
}

func (e *UnknownTagError) Error() string {
	msg := ErrUnknownTag.Error() + ": " + strconv.Quote(e

ny parametric suffix
// (without the surrounding "${" / "}"). Param is the parameter portion when
// the tag was parametric, or the empty string for bare tags. Hint is an
// optional human-readable suggestion — currently set when


oding overhead. Unlike the standard storage interface,
// this storage works directly with Go types for maximum speed.
//
// # Safety Considerations
//
// This storage automatically performs defensive copying for:
//   - String keys: Copied to prevent corruption from pooled buffers
//   - []byte values: Copied on both Set and Get to prevent external mutation
//
// For other types (structs, ints, etc.), Go's value semantics provide natural
// protection. However, if storing pointers or slices of non-byte typ

age memory

import (
	"sync"
	"time"

	"github.com/gofiber/utils/v2"
)

// Storage stores arbitrary values in memory for use in tests and benchmarks.
// Storage is safe for concurrent use.
type Storage struct {
	data map[string]item // data
	mu   sync.RWMu

es,
// callers are responsible for not mutating the underlying data.
//
// This storage is primarily used internally by middleware for performance-
// critical operations where the stored data types are known and controlled.
pack


Package loggertest provides shared helpers for the middleware ctx-logger
// integration tests. The helpers consolidate the SetOutput / SetContextTemplate
// / cleanup boilerplate that would otherwise be duplicated across every
// middleware that registers a context tag.
package loggertest

import (
	"bytes"
	"os"
	"testing"

	fiberlog "github.com/gofiber/fiber/v3/log"
)

// CaptureContextLog redirects the default fiberlog output into a fresh buffer
// and configures the given context format. On test cleanup

oss tests.
func CaptureContextLog(tb testing.TB, format string) *bytes.Buffer {
	tb.Helper()

	var buf bytes.Buffer
	fiberlog.SetOutput(&buf)
	fiberlog.MustSetContextTemplate(fiberlog.ContextConfig{Format: format})

	tb.Cleanup(func() {
		fiberlog.MustSetC

, the default
// logger output is restored to os.Stderr and the context template is cleared.
//
// Tests that use this helper must NOT call t.Parallel() because the helper
// mutates package-global default-logger state shared acr


tWithContext(t *testing.T) {
	t.Parallel()
	var (
		testStore = New()
		key       = "john"
		val       = []byte("doe")
	)

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

	err := testStore.SetWithContext(ctx, key, val, 0)
	require.ErrorIs(t, err, context.Canceled)

	keys, err := testStore.Keys()
	require.NoError(t, err)
	require.Nil(t, keys)
}

func Test_Storage_Memory_Set_Override(t *testing.T) {
	t.Parallel()
	var (
		testStore = New()
		key       = "john"
		val       = []byte("doe")

ory_Get(t *testing.T) {
	t.Parallel()
	var (
		testStore = New()
		key       = "john"
		val       = []byte("doe")
	)

	err := testStore.Set(key, val, 0)
	require.NoError(t, err)

	result, err := testStore.Get(key)
	require.NoError(t, err)
	require.Equal(t,

)

	err := testStore.Set(key, val, 0)
	require.NoError(t, err)

	err = testStore.Set(key, val, 0)
	require.NoError(t, err)

	keys, err := testStore.Keys()
	require.NoError(t, err)
	require.Len(t, keys, 1)
}

func Test_Storage_Mem


kage contextvalue

import (
	"context"

	"github.com/valyala/fasthttp"
)

type fiberLocalContext interface {
	Locals(key any, value ...any) any
}

type userValueContext interface {
	UserValue(key any) any
}

type valueContext interface {
	Value(key any) any
}

// Value retrieves a value stored under key from supported context types
// (fiber.Ctx, fiber.CustomCtx, context.Context, and *fasthttp.RequestCtx).
func Value[T any](ctx, key any) (T, bool) {
	// Prefer Value-style lookups before Locals/UserValue whe

		val, ok := typed.Value(key).(T)
		return val, ok
	case valueContext:
		val, ok := typed.Value(key).(T)
		return val, ok
	case fiberLocalContext:
		val, ok := typed.Locals(key).(T)
		return val, ok
	case userValueContext:
		val, ok := typed.UserValue(key)

n a context exposes
	// multiple accessors so Fiber contexts follow context.Value semantics.
	switch typed := ctx.(type) {
	case *fasthttp.RequestCtx:
		val, ok := typed.UserValue(key).(T)
		return val, ok
	case context.Context:


"context"
	"fmt"
	"sync"
	"time"

	"github.com/gofiber/utils/v2"
)

// Storage provides an in-memory implementation of the storage interface for
// testing purposes.
// Storage is safe for concurrent use, except when callers keep using the live
// map returned by Conn. Access to that map requires external synchronization.
type Storage struct {
	db         map[string]Entry
	done       chan struct{}
	gcInterval time.Duration
	mux        sync.RWMutex
}

// Entry represents a value stored in memory along with i

	cfg := configDefault(config...)

	// Create storage
	store := &Storage{
		db:         make(map[string]Entry),
		gcInterval: cfg.GCInterval,
		done:       make(chan struct{}),
	}

	// Start garbage collector
	utils.StartTimeStampUpdater()
	go store.gc()

ts expiration.
type Entry struct {
	data []byte
	// max value is 4294967295 -> Sun Feb 07 2106 06:28:15 GMT+0000
	expiry uint32
}

// New creates a new memory storage.
func New(config ...Config) *Storage {
	// Set default config


 of a request. It provides access to the response data.
type Response struct {
	client  *Client
	request *Request

	RawResponse *fasthttp.Response
	cookie      []*fasthttp.Cookie
}

// setClient sets the client instance in the response. The client object is used by core functionalities.
func (r *Response) setClient(c *Client) {
	r.client = c
}

// setRequest sets the request object in the response. The request is released when Response.Close is called.
func (r *Response) setRequest(req *Request) {
	r.reques

 request.
func (r *Response) StatusCode() int {
	return r.RawResponse.StatusCode()
}

// Protocol returns the HTTP protocol used for the request.
func (r *Response) Protocol() string {
	return string(r.RawResponse.Header.Protocol())
}

// Header returns th

t = req
}

// Status returns the HTTP status message of the executed request.
func (r *Response) Status() string {
	return string(r.RawResponse.Header.StatusMessage())
}

// StatusCode returns the HTTP status code of the executed


	"testing"
	"time"

	"github.com/gofiber/utils/v2"
	"github.com/stretchr/testify/require"
)

// go test -run Test_Memory -v -race
func Test_Memory(t *testing.T) {
	t.Parallel()
	store := New()
	var (
		key     = "john-internal"
		val any = []byte("doe")
		exp     = 1 * time.Second
	)

	// Set key with value
	store.Set(key, val, 0)
	result := store.Get(key)
	require.Equal(t, val, result)

	// Get non-existing key
	result = store.Get("empty")
	require.Nil(t, result)

	// Set key with value and ttl
	store.Set(

t, val, result)

	// Delete key
	store.Delete(key)
	result = store.Get(key)
	require.Nil(t, result)

	// Reset all keys
	store.Set("john-reset", val, 0)
	store.Set("doe-reset", val, 0)
	store.Reset()

	// Check if all keys are deleted
	result = store.Get("

key, val, exp)
	require.Eventually(t, func() bool {
		return store.Get(key) == nil
	}, 3*time.Second, 10*time.Millisecond)

	// Set key with value and no expiration
	store.Set(key, val, 0)
	result = store.Get(key)
	require.Equal(