// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
// 🤖 Github Repository: https://github.com/gofiber/fiber
// 📌 API Documentation: https://docs.gofiber.io

package fiber

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	

	
	

	
	
)

const (
	schemeHTTP  = "http"
	schemeHTTPS = "https"
)

// maxParams defines the maximum number of parameters per route.
const maxParams = 30

// Some constants for BodyParser, QueryParser, CookieParser and ReqHeaderParser.
const (
	queryTag     = "query"
	reqHeaderTag = "reqHeader"
	bodyTag      = "form"
	paramsTag    = "params"
	cookieTag    = "cookie"
)

// userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx
const userContextKey = "__local_user_context__"

var (
	// decoderPoolMap helps to improve BodyParser's, QueryParser's, CookieParser's and ReqHeaderParser's performance
	decoderPoolMap = map[string]*sync.Pool{}
	// tags is used to classify parser's pool
	tags = []string{queryTag, bodyTag, reqHeaderTag, paramsTag, cookieTag}
)

func init() {
	for ,  := range tags {
		decoderPoolMap[] = &sync.Pool{New: func() interface{} {
			return decoderBuilder(ParserConfig{
				IgnoreUnknownKeys: true,
				ZeroEmpty:         true,
			})
		}}
	}
}

// SetParserDecoder allow globally change the option of form decoder, update decoderPool
func ( ParserConfig) {
	for ,  := range tags {
		decoderPoolMap[] = &sync.Pool{New: func() interface{} {
			return decoderBuilder()
		}}
	}
}

// Ctx represents the Context which hold the HTTP request and response.
// It has methods for the request query string, parameters, body, HTTP headers and so on.
type Ctx struct {
	app                 *App                 // Reference to *App
	route               *Route               // Reference to *Route
	indexRoute          int                  // Index of the current route
	indexHandler        int                  // Index of the current handler
	method              string               // HTTP method
	methodINT           int                  // HTTP method INT equivalent
	baseURI             string               // HTTP base uri
	path                string               // HTTP path with the modifications by the configuration -> string copy from pathBuffer
	pathBuffer          []byte               // HTTP path buffer
	detectionPath       string               // Route detection path                                  -> string copy from detectionPathBuffer
	detectionPathBuffer []byte               // HTTP detectionPath buffer
	treePath            string               // Path for the search in the tree
	pathOriginal        string               // Original HTTP path
	values              [maxParams]string    // Route parameter values
	fasthttp            *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
	matched             bool                 // Non use route matched
	viewBindMap         sync.Map             // Default view map to bind template engine
}

// TLSHandler object
type TLSHandler struct {
	clientHelloInfo *tls.ClientHelloInfo
}

// GetClientInfo Callback function to set ClientHelloInfo
// Must comply with the method structure of https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/tls/common.go;l=554-563
// Since we overlay the method of the tls config in the listener method
func ( *TLSHandler) ( *tls.ClientHelloInfo) (*tls.Certificate, error) {
	.clientHelloInfo = 
	return nil, nil //nolint:nilnil // Not returning anything useful here is probably fine
}

// Range data for c.Range
type Range struct {
	Type   string
	Ranges []struct {
		Start int
		End   int
	}
}

// Cookie data for c.Cookie
type Cookie struct {
	Name        string    `json:"name"`
	Value       string    `json:"value"`
	Path        string    `json:"path"`
	Domain      string    `json:"domain"`
	MaxAge      int       `json:"max_age"`
	Expires     time.Time `json:"expires"`
	Secure      bool      `json:"secure"`
	HTTPOnly    bool      `json:"http_only"`
	SameSite    string    `json:"same_site"`
	SessionOnly bool      `json:"session_only"`
}

// Views is the interface that wraps the Render function.
type Views interface {
	Load() error
	Render(io.Writer, string, interface{}, ...string) error
}

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

// ParserConfig form decoder config for SetParserDecoder
type ParserConfig struct {
	IgnoreUnknownKeys bool
	SetAliasTag       string
	ParserType        []ParserType
	ZeroEmpty         bool
}

// AcquireCtx retrieves a new Ctx from the pool.
func ( *App) ( *fasthttp.RequestCtx) *Ctx {
	,  := .pool.Get().(*Ctx)
	if ! {
		panic(fmt.Errorf("failed to type-assert to *Ctx"))
	}
	// Set app reference
	.app = 
	// Reset route and handler index
	.indexRoute = -1
	.indexHandler = 0
	// Reset matched flag
	.matched = false
	// Set paths
	.pathOriginal = .getString(.URI().PathOriginal())
	// Set method
	.method = .getString(.Request.Header.Method())
	.methodINT = .methodInt(.method)
	// Attach *fasthttp.RequestCtx to ctx
	.fasthttp = 
	// reset base uri
	.baseURI = ""
	// Prettify path
	.configDependentPaths()
	return 
}

// ReleaseCtx releases the ctx back into the pool.
func ( *App) ( *Ctx) {
	// Reset values
	.route = nil
	.fasthttp = nil
	.viewBindMap = sync.Map{}
	.pool.Put()
}

// Accepts checks if the specified extensions or content types are acceptable.
func ( *Ctx) ( ...string) string {
	return getOffer(.Get(HeaderAccept), acceptsOfferType, ...)
}

// AcceptsCharsets checks if the specified charset is acceptable.
func ( *Ctx) ( ...string) string {
	return getOffer(.Get(HeaderAcceptCharset), acceptsOffer, ...)
}

// AcceptsEncodings checks if the specified encoding is acceptable.
func ( *Ctx) ( ...string) string {
	return getOffer(.Get(HeaderAcceptEncoding), acceptsOffer, ...)
}

// AcceptsLanguages checks if the specified language is acceptable.
func ( *Ctx) ( ...string) string {
	return getOffer(.Get(HeaderAcceptLanguage), acceptsOffer, ...)
}

// App returns the *App reference to the instance of the Fiber application
func ( *Ctx) () *App {
	return .app
}

// Append the specified value to the HTTP response header field.
// If the header is not already set, it creates the header with the specified value.
func ( *Ctx) ( string,  ...string) {
	if len() == 0 {
		return
	}
	 := .app.getString(.fasthttp.Response.Header.Peek())
	 := 
	for ,  := range  {
		if len() == 0 {
			 = 
		} else if  !=  && !strings.HasPrefix(, +",") && !strings.HasSuffix(, " "+) &&
			!strings.Contains(, " "++",") {
			 += ", " + 
		}
	}
	if  !=  {
		.Set(, )
	}
}

// Attachment sets the HTTP response Content-Disposition header field to attachment.
func ( *Ctx) ( ...string) {
	if len() > 0 {
		 := filepath.Base([0])
		.Type(filepath.Ext())

		.setCanonical(HeaderContentDisposition, `attachment; filename="`+.app.quoteString()+`"`)
		return
	}
	.setCanonical(HeaderContentDisposition, "attachment")
}

// BaseURL returns (protocol + host + base path).
func ( *Ctx) () string {
	// TODO: Could be improved: 53.8 ns/op  32 B/op  1 allocs/op
	// Should work like https://codeigniter.com/user_guide/helpers/url_helper.html
	if .baseURI != "" {
		return .baseURI
	}
	.baseURI = .Protocol() + "://" + .Hostname()
	return .baseURI
}

// BodyRaw contains the raw body submitted in a POST request.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func ( *Ctx) () []byte {
	return .fasthttp.Request.Body()
}

func ( *Ctx) (
	 *[]byte,
	 []string,
) ([]byte, uint8, error) {
	var (
		             error
		            []byte
		 uint8
	)

	for ,  := range  {
		++
		switch  {
		case StrGzip:
			,  = .fasthttp.Request.BodyGunzip()
		case StrBr, StrBrotli:
			,  = .fasthttp.Request.BodyUnbrotli()
		case StrDeflate:
			,  = .fasthttp.Request.BodyInflate()
		default:
			--
			if len() == 1 {
				 = .fasthttp.Request.Body()
			}
			return , , nil
		}

		if  != nil {
			return nil, , 
		}

		// Only execute body raw update if it has a next iteration to try to decode
		if  < len()-1 &&  > 0 {
			if  == 0 {
				 := .fasthttp.Request.Body()
				* = make([]byte, len())
				copy(*, )
			}
			.fasthttp.Request.SetBodyRaw()
		}
	}

	return , , nil
}

// Body contains the raw body submitted in a POST request.
// This method will decompress the body if the 'Content-Encoding' header is provided.
// It returns the original (or decompressed) body data which is valid only within the handler.
// Don't store direct references to the returned data.
// If you need to keep the body's data later, make a copy or use the Immutable option.
func ( *Ctx) () []byte {
	var (
		                error
		,  []byte
		     string
		      = []string{"", "", ""}
	)

	// faster than peek
	.Request().Header.VisitAll(func(,  []byte) {
		if .app.getString() == HeaderContentEncoding {
			 = .app.getString()
		}
	})

	// Split and get the encodings list, in order to attend the
	// rule defined at: https://www.rfc-editor.org/rfc/rfc9110#section-8.4-5
	 = getSplicedStrList(, )
	if len() == 0 {
		return .fasthttp.Request.Body()
	}

	var  uint8
	, ,  = .tryDecodeBodyInOrder(&, )

	// Ensure that the body will be the original
	if  != nil &&  > 0 {
		.fasthttp.Request.SetBodyRaw()
	}
	if  != nil {
		return []byte(.Error())
	}

	return 
}

func decoderBuilder( ParserConfig) interface{} {
	 := schema.NewDecoder()
	.IgnoreUnknownKeys(.IgnoreUnknownKeys)
	if .SetAliasTag != "" {
		.SetAliasTag(.SetAliasTag)
	}
	for ,  := range .ParserType {
		.RegisterConverter(reflect.ValueOf(.Customtype).Interface(), .Converter)
	}
	.ZeroEmpty(.ZeroEmpty)
	return 
}

// BodyParser binds the request body to a struct.
// It supports decoding the following content types based on the Content-Type header:
// application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data
// If none of the content types above are matched, it will return a ErrUnprocessableEntity error
func ( *Ctx) ( interface{}) error {
	// Get content-type
	 := utils.ToLower(.app.getString(.fasthttp.Request.Header.ContentType()))

	 = utils.ParseVendorSpecificContentType()

	// Parse body accordingly
	if strings.HasPrefix(, MIMEApplicationJSON) {
		return .app.config.JSONDecoder(.Body(), )
	}
	if strings.HasPrefix(, MIMEApplicationForm) {
		 := make(map[string][]string)
		var  error

		.fasthttp.PostArgs().VisitAll(func(,  []byte) {
			if  != nil {
				return
			}

			 := .app.getString()
			 := .app.getString()

			if strings.Contains(, "[") {
				,  = parseParamSquareBrackets()
			}

			if .app.config.EnableSplittingOnParsers && strings.Contains(, ",") && equalFieldType(, reflect.Slice, , bodyTag) {
				 := strings.Split(, ",")
				for  := 0;  < len(); ++ {
					[] = append([], [])
				}
			} else {
				[] = append([], )
			}
		})

		return .parseToStruct(bodyTag, , )
	}
	if strings.HasPrefix(, MIMEMultipartForm) {
		,  := .fasthttp.MultipartForm()
		if  != nil {
			return 
		}
		return .parseToStruct(bodyTag, , .Value)
	}
	if strings.HasPrefix(, MIMETextXML) || strings.HasPrefix(, MIMEApplicationXML) {
		if  := xml.Unmarshal(.Body(), );  != nil {
			return fmt.Errorf("failed to unmarshal: %w", )
		}
		return nil
	}
	// No suitable content type found
	return ErrUnprocessableEntity
}

// ClearCookie expires a specific cookie by key on the client side.
// If no key is provided it expires all cookies that came with the request.
func ( *Ctx) ( ...string) {
	if len() > 0 {
		for  := range  {
			.fasthttp.Response.Header.DelClientCookie([])
		}
		return
	}
	.fasthttp.Request.Header.VisitAllCookie(func(,  []byte) {
		.fasthttp.Response.Header.DelClientCookieBytes()
	})
}

// Context returns *fasthttp.RequestCtx that carries a deadline
// a cancellation signal, and other values across API boundaries.
func ( *Ctx) () *fasthttp.RequestCtx {
	return .fasthttp
}

// UserContext returns a context implementation that was set by
// user earlier or returns a non-nil, empty context,if it was not set earlier.
func ( *Ctx) () context.Context {
	,  := .fasthttp.UserValue(userContextKey).(context.Context)
	if ! {
		 = context.Background()
		.SetUserContext()
	}

	return 
}

// SetUserContext sets a context implementation by user.
func ( *Ctx) ( context.Context) {
	.fasthttp.SetUserValue(userContextKey, )
}

// Cookie sets a cookie by passing a cookie struct.
func ( *Ctx) ( *Cookie) {
	 := fasthttp.AcquireCookie()
	.SetKey(.Name)
	.SetValue(.Value)
	.SetPath(.Path)
	.SetDomain(.Domain)
	// only set max age and expiry when SessionOnly is false
	// i.e. cookie supposed to last beyond browser session
	// refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_the_lifetime_of_a_cookie
	if !.SessionOnly {
		.SetMaxAge(.MaxAge)
		.SetExpire(.Expires)
	}
	.SetSecure(.Secure)
	.SetHTTPOnly(.HTTPOnly)

	switch utils.ToLower(.SameSite) {
	case CookieSameSiteStrictMode:
		.SetSameSite(fasthttp.CookieSameSiteStrictMode)
	case CookieSameSiteNoneMode:
		.SetSameSite(fasthttp.CookieSameSiteNoneMode)
	case CookieSameSiteDisabled:
		.SetSameSite(fasthttp.CookieSameSiteDisabled)
	default:
		.SetSameSite(fasthttp.CookieSameSiteLaxMode)
	}

	.fasthttp.Response.Header.SetCookie()
	fasthttp.ReleaseCookie()
}

// Cookies are used for getting a cookie value by key.
// Defaults to the empty string "" if the cookie doesn't exist.
// If a default value is given, it will return that value if the cookie doesn't exist.
// The returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting to use the value outside the Handler.
func ( *Ctx) ( string,  ...string) string {
	return defaultString(.app.getString(.fasthttp.Request.Header.Cookie()), )
}

// CookieParser is used to bind cookies to a struct
func ( *Ctx) ( interface{}) error {
	 := make(map[string][]string)
	var  error

	// loop through all cookies
	.fasthttp.Request.Header.VisitAllCookie(func(,  []byte) {
		if  != nil {
			return
		}

		 := .app.getString()
		 := .app.getString()

		if strings.Contains(, "[") {
			,  = parseParamSquareBrackets()
		}

		if .app.config.EnableSplittingOnParsers && strings.Contains(, ",") && equalFieldType(, reflect.Slice, , cookieTag) {
			 := strings.Split(, ",")
			for  := 0;  < len(); ++ {
				[] = append([], [])
			}
		} else {
			[] = append([], )
		}
	})
	if  != nil {
		return 
	}

	return .parseToStruct(cookieTag, , )
}

// Download transfers the file from path as an attachment.
// Typically, browsers will prompt the user for download.
// By default, the Content-Disposition header filename= parameter is the filepath (this typically appears in the browser dialog).
// Override this default with the filename parameter.
func ( *Ctx) ( string,  ...string) error {
	var  string
	if len() > 0 {
		 = [0]
	} else {
		 = filepath.Base()
	}
	.setCanonical(HeaderContentDisposition, `attachment; filename="`+.app.quoteString()+`"`)
	return .SendFile()
}

// Request return the *fasthttp.Request object
// This allows you to use all fasthttp request methods
// https://godoc.org/github.com/valyala/fasthttp#Request
func ( *Ctx) () *fasthttp.Request {
	return &.fasthttp.Request
}

// Response return the *fasthttp.Response object
// This allows you to use all fasthttp response methods
// https://godoc.org/github.com/valyala/fasthttp#Response
func ( *Ctx) () *fasthttp.Response {
	return &.fasthttp.Response
}

// Format performs content-negotiation on the Accept HTTP header.
// It uses Accepts to select a proper format.
// If the header is not specified or there is no proper format, text/plain is used.
func ( *Ctx) ( interface{}) error {
	// Get accepted content type
	 := .Accepts("html", "json", "txt", "xml")
	// Set accepted content type
	.Type()
	// Type convert provided body
	var  string
	switch val := .(type) {
	case string:
		 = 
	case []byte:
		 = .app.getString()
	default:
		 = fmt.Sprintf("%v", )
	}

	// Format based on the accept content type
	switch  {
	case "html":
		return .SendString("<p>" +  + "</p>")
	case "json":
		return .JSON()
	case "txt":
		return .SendString()
	case "xml":
		return .XML()
	}
	return .SendString()
}

// FormFile returns the first file by key from a MultipartForm.
func ( *Ctx) ( string) (*multipart.FileHeader, error) {
	return .fasthttp.FormFile()
}

// FormValue returns the first value by key from a MultipartForm.
// Search is performed in QueryArgs, PostArgs, MultipartForm and FormFile in this particular order.
// Defaults to the empty string "" if the form value doesn't exist.
// If a default value is given, it will return that value if the form value does not exist.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func ( *Ctx) ( string,  ...string) string {
	return defaultString(.app.getString(.fasthttp.FormValue()), )
}

// Fresh returns true when the response is still “fresh” in the client's cache,
// otherwise false is returned to indicate that the client cache is now stale
// and the full response should be sent.
// When a client sends the Cache-Control: no-cache request header to indicate an end-to-end
// reload request, this module will return false to make handling these requests transparent.
// https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L33
func ( *Ctx) () bool {
	// fields
	 := .Get(HeaderIfModifiedSince)
	 := .Get(HeaderIfNoneMatch)

	// unconditional request
	if  == "" &&  == "" {
		return false
	}

	// Always return stale when Cache-Control: no-cache
	// to support end-to-end reload requests
	// https://tools.ietf.org/html/rfc2616#section-14.9.4
	 := .Get(HeaderCacheControl)
	if  != "" && isNoCache() {
		return false
	}

	// if-none-match
	if  != "" &&  != "*" {
		 := .app.getString(.fasthttp.Response.Header.Peek(HeaderETag))
		if  == "" {
			return false
		}
		if .app.isEtagStale(, .app.getBytes()) {
			return false
		}

		if  != "" {
			 := .app.getString(.fasthttp.Response.Header.Peek(HeaderLastModified))
			if  != "" {
				,  := http.ParseTime()
				if  != nil {
					return false
				}
				,  := http.ParseTime()
				if  != nil {
					return false
				}
				return .Before()
			}
		}
	}
	return true
}

// Get returns the HTTP request header specified by field.
// Field names are case-insensitive
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func ( *Ctx) ( string,  ...string) string {
	return defaultString(.app.getString(.fasthttp.Request.Header.Peek()), )
}

// GetRespHeader returns the HTTP response header specified by field.
// Field names are case-insensitive
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func ( *Ctx) ( string,  ...string) string {
	return defaultString(.app.getString(.fasthttp.Response.Header.Peek()), )
}

// GetReqHeaders returns the HTTP request headers.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func ( *Ctx) () map[string][]string {
	 := make(map[string][]string)
	.Request().Header.VisitAll(func(,  []byte) {
		 := .app.getString()
		[] = append([], .app.getString())
	})

	return 
}

// GetRespHeaders returns the HTTP response headers.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func ( *Ctx) () map[string][]string {
	 := make(map[string][]string)
	.Response().Header.VisitAll(func(,  []byte) {
		 := .app.getString()
		[] = append([], .app.getString())
	})

	return 
}

// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
func ( *Ctx) () string {
	if .IsProxyTrusted() {
		if  := .Get(HeaderXForwardedHost); len() > 0 {
			 := strings.Index(, ",")
			if  != -1 {
				return [:]
			}
			return 
		}
	}
	return .app.getString(.fasthttp.Request.URI().Host())
}

// Port returns the remote port of the request.
func ( *Ctx) () string {
	,  := .fasthttp.RemoteAddr().(*net.TCPAddr)
	if ! {
		panic(fmt.Errorf("failed to type-assert to *net.TCPAddr"))
	}
	return strconv.Itoa(.Port)
}

// IP returns the remote IP address of the request.
// If ProxyHeader and IP Validation is configured, it will parse that header and return the first valid IP address.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
func ( *Ctx) () string {
	if .IsProxyTrusted() && len(.app.config.ProxyHeader) > 0 {
		return .extractIPFromHeader(.app.config.ProxyHeader)
	}

	return .fasthttp.RemoteIP().String()
}

// extractIPsFromHeader will return a slice of IPs it found given a header name in the order they appear.
// When IP validation is enabled, any invalid IPs will be omitted.
func ( *Ctx) ( string) []string {
	// TODO: Reuse the c.extractIPFromHeader func somehow in here

	 := .Get()

	// We can't know how many IPs we will return, but we will try to guess with this constant division.
	// Counting ',' makes function slower for about 50ns in general case.
	const  = 8
	 := len() / 
	if  >  {
		 =  // Avoid big allocation on big header
	}

	 := make([]string, 0, )

	 := 0
	 := -1

:
	for {
		var ,  bool

		// Manually splitting string without allocating slice, working with parts directly
		,  = +1, +2

		if  > len() {
			break
		}

		for  < len() && [] != ',' {
			if [] == ':' {
				 = true
			} else if [] == '.' {
				 = true
			}
			++
		}

		for  <  && ([] == ' ' || [] == ',') {
			++
		}

		 := utils.TrimRight([:], ' ')

		if .app.config.EnableIPValidation {
			// Skip validation if IP is clearly not IPv4/IPv6, otherwise validate without allocations
			if (! && !) || ( && !utils.IsIPv6()) || ( && !utils.IsIPv4()) {
				continue 
			}
		}

		 = append(, )
	}

	return 
}

// extractIPFromHeader will attempt to pull the real client IP from the given header when IP validation is enabled.
// currently, it will return the first valid IP address in header.
// when IP validation is disabled, it will simply return the value of the header without any inspection.
// Implementation is almost the same as in extractIPsFromHeader, but without allocation of []string.
func ( *Ctx) ( string) string {
	if .app.config.EnableIPValidation {
		 := .Get()

		 := 0
		 := -1

	:
		for {
			var ,  bool

			// Manually splitting string without allocating slice, working with parts directly
			,  = +1, +2

			if  > len() {
				break
			}

			for  < len() && [] != ',' {
				if [] == ':' {
					 = true
				} else if [] == '.' {
					 = true
				}
				++
			}

			for  <  && [] == ' ' {
				++
			}

			 := utils.TrimRight([:], ' ')

			if .app.config.EnableIPValidation {
				if (! && !) || ( && !utils.IsIPv6()) || ( && !utils.IsIPv4()) {
					continue 
				}
			}

			return 
		}

		return .fasthttp.RemoteIP().String()
	}

	// default behavior if IP validation is not enabled is just to return whatever value is
	// in the proxy header. Even if it is empty or invalid
	return .Get(.app.config.ProxyHeader)
}

// IPs returns a string slice of IP addresses specified in the X-Forwarded-For request header.
// When IP validation is enabled, only valid IPs are returned.
func ( *Ctx) () []string {
	return .extractIPsFromHeader(HeaderXForwardedFor)
}

// Is returns the matching content type,
// if the incoming request's Content-Type HTTP header field matches the MIME type specified by the type parameter
func ( *Ctx) ( string) bool {
	 := utils.GetMIME()
	if  == "" {
		return false
	}

	return strings.HasPrefix(
		utils.TrimLeft(.app.getString(.fasthttp.Request.Header.ContentType()), ' '),
		,
	)
}

// JSON converts any interface or string to JSON.
// Array and slice values encode as JSON arrays,
// except that []byte encodes as a base64-encoded string,
// and a nil slice encodes as the null JSON value.
// This method also sets the content header to application/json.
func ( *Ctx) ( interface{}) error {
	,  := .app.config.JSONEncoder()
	if  != nil {
		return 
	}
	.fasthttp.Response.SetBodyRaw()
	.fasthttp.Response.Header.SetContentType(MIMEApplicationJSON)
	return nil
}

// JSONP sends a JSON response with JSONP support.
// This method is identical to JSON, except that it opts-in to JSONP callback support.
// By default, the callback name is simply callback.
func ( *Ctx) ( interface{},  ...string) error {
	,  := .app.config.JSONEncoder()
	if  != nil {
		return 
	}

	var ,  string

	if len() > 0 {
		 = [0]
	} else {
		 = "callback"
	}

	 =  + "(" + .app.getString() + ");"

	.setCanonical(HeaderXContentTypeOptions, "nosniff")
	.fasthttp.Response.Header.SetContentType(MIMETextJavaScriptCharsetUTF8)
	return .SendString()
}

// XML converts any interface or string to XML.
// This method also sets the content header to application/xml.
func ( *Ctx) ( interface{}) error {
	,  := .app.config.XMLEncoder()
	if  != nil {
		return 
	}
	.fasthttp.Response.SetBodyRaw()
	.fasthttp.Response.Header.SetContentType(MIMEApplicationXML)
	return nil
}

// Links joins the links followed by the property to populate the response's Link HTTP header field.
func ( *Ctx) ( ...string) {
	if len() == 0 {
		return
	}
	 := bytebufferpool.Get()
	for  := range  {
		if %2 == 0 {
			_ = .WriteByte('<')          //nolint:errcheck // This will never fail
			_, _ = .WriteString([]) //nolint:errcheck // This will never fail
			_ = .WriteByte('>')          //nolint:errcheck // This will never fail
		} else {
			_, _ = .WriteString(`; rel="` + [] + `",`) //nolint:errcheck // This will never fail
		}
	}
	.setCanonical(HeaderLink, utils.TrimRight(.app.getString(.Bytes()), ','))
	bytebufferpool.Put()
}

// Locals makes it possible to pass interface{} values under keys scoped to the request
// and therefore available to all following routes that match the request.
func ( *Ctx) ( interface{},  ...interface{}) interface{} {
	if len() == 0 {
		return .fasthttp.UserValue()
	}
	.fasthttp.SetUserValue(, [0])
	return [0]
}

// Location sets the response Location HTTP header to the specified path parameter.
func ( *Ctx) ( string) {
	.setCanonical(HeaderLocation, )
}

// Method returns the HTTP request method for the context, optionally overridden by the provided argument.
// If no override is given or if the provided override is not a valid HTTP method, it returns the current method from the context.
// Otherwise, it updates the context's method and returns the overridden method as a string.
func ( *Ctx) ( ...string) string {
	if len() == 0 {
		// Nothing to override, just return current method from context
		return .method
	}

	 := utils.ToUpper([0])
	 := .app.methodInt()
	if  == -1 {
		// Provided override does not valid HTTP method, no override, return current method
		return .method
	}

	.method = 
	.methodINT = 
	return .method
}

// MultipartForm parse form entries from binary.
// This returns a map[string][]string, so given a key the value will be a string slice.
func ( *Ctx) () (*multipart.Form, error) {
	return .fasthttp.MultipartForm()
}

// ClientHelloInfo return CHI from context
func ( *Ctx) () *tls.ClientHelloInfo {
	if .app.tlsHandler != nil {
		return .app.tlsHandler.clientHelloInfo
	}

	return nil
}

// Next executes the next method in the stack that matches the current route.
func ( *Ctx) () error {
	// Increment handler index
	.indexHandler++
	var  error
	// Did we execute all route handlers?
	if .indexHandler < len(.route.Handlers) {
		// Continue route stack
		 = .route.Handlers[.indexHandler]()
	} else {
		// Continue handler stack
		_,  = .app.next()
	}
	return 
}

// RestartRouting instead of going to the next handler. This may be useful after
// changing the request path. Note that handlers might be executed again.
func ( *Ctx) () error {
	.indexRoute = -1
	,  := .app.next()
	return 
}

// OriginalURL contains the original request URL.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting to use the value outside the Handler.
func ( *Ctx) () string {
	return .app.getString(.fasthttp.Request.Header.RequestURI())
}

// Params is used to get the route parameters.
// Defaults to empty string "" if the param doesn't exist.
// If a default value is given, it will return that value if the param doesn't exist.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting to use the value outside the Handler.
func ( *Ctx) ( string,  ...string) string {
	if  == "*" ||  == "+" {
		 += "1"
	}
	for  := range .route.Params {
		if len() != len(.route.Params[]) {
			continue
		}
		if .route.Params[] ==  || (!.app.config.CaseSensitive && utils.EqualFold(.route.Params[], )) {
			// in case values are not here
			if len(.values) <=  || len(.values[]) == 0 {
				break
			}
			return .values[]
		}
	}
	return defaultString("", )
}

// AllParams Params is used to get all route parameters.
// Using Params method to get params.
func ( *Ctx) () map[string]string {
	 := make(map[string]string, len(.route.Params))
	for ,  := range .route.Params {
		[] = .Params()
	}

	return 
}

// ParamsParser binds the param string to a struct.
func ( *Ctx) ( interface{}) error {
	 := make(map[string][]string, len(.route.Params))
	for ,  := range .route.Params {
		[] = append([], .Params())
	}
	return .parseToStruct(paramsTag, , )
}

// ParamsInt is used to get an integer from the route parameters
// it defaults to zero if the parameter is not found or if the
// parameter cannot be converted to an integer
// If a default value is given, it will return that value in case the param
// doesn't exist or cannot be converted to an integer
func ( *Ctx) ( string,  ...int) (int, error) {
	// Use Atoi to convert the param to an int or return zero and an error
	,  := strconv.Atoi(.Params())
	if  != nil {
		if len() > 0 {
			return [0], nil
		}
		return 0, fmt.Errorf("failed to convert: %w", )
	}

	return , nil
}

// Path returns the path part of the request URL.
// Optionally, you could override the path.
func ( *Ctx) ( ...string) string {
	if len() != 0 && .path != [0] {
		// Set new path to context
		.pathOriginal = [0]

		// Set new path to request context
		.fasthttp.Request.URI().SetPath(.pathOriginal)
		// Prettify path
		.configDependentPaths()
	}
	return .path
}

// Protocol contains the request protocol string: http or https for TLS requests.
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
func ( *Ctx) () string {
	if .fasthttp.IsTLS() {
		return schemeHTTPS
	}
	if !.IsProxyTrusted() {
		return schemeHTTP
	}

	 := schemeHTTP
	const  = 12
	.fasthttp.Request.Header.VisitAll(func(,  []byte) {
		if len() <  {
			return // Neither "X-Forwarded-" nor "X-Url-Scheme"
		}
		switch {
		case bytes.HasPrefix(, []byte("X-Forwarded-")):
			if bytes.Equal(, []byte(HeaderXForwardedProto)) ||
				bytes.Equal(, []byte(HeaderXForwardedProtocol)) {
				 := .app.getString()
				 := strings.Index(, ",")
				if  != -1 {
					 = [:]
				} else {
					 = 
				}
			} else if bytes.Equal(, []byte(HeaderXForwardedSsl)) && bytes.Equal(, []byte("on")) {
				 = schemeHTTPS
			}

		case bytes.Equal(, []byte(HeaderXUrlScheme)):
			 = .app.getString()
		}
	})
	return 
}

// Query returns the query string parameter in the url.
// Defaults to empty string "" if the query doesn't exist.
// If a default value is given, it will return that value if the query doesn't exist.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting to use the value outside the Handler.
func ( *Ctx) ( string,  ...string) string {
	return defaultString(.app.getString(.fasthttp.QueryArgs().Peek()), )
}

// Queries returns a map of query parameters and their values.
//
// GET /?name=alex&wanna_cake=2&id=
// Queries()["name"] == "alex"
// Queries()["wanna_cake"] == "2"
// Queries()["id"] == ""
//
// GET /?field1=value1&field1=value2&field2=value3
// Queries()["field1"] == "value2"
// Queries()["field2"] == "value3"
//
// GET /?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3
// Queries()["list_a"] == "3"
// Queries()["list_b[]"] == "3"
// Queries()["list_c"] == "1,2,3"
//
// GET /api/search?filters.author.name=John&filters.category.name=Technology&filters[customer][name]=Alice&filters[status]=pending
// Queries()["filters.author.name"] == "John"
// Queries()["filters.category.name"] == "Technology"
// Queries()["filters[customer][name]"] == "Alice"
// Queries()["filters[status]"] == "pending"
func ( *Ctx) () map[string]string {
	 := make(map[string]string, .Context().QueryArgs().Len())
	.Context().QueryArgs().VisitAll(func(,  []byte) {
		[.app.getString()] = .app.getString()
	})
	return 
}

// QueryInt returns integer value of key string parameter in the url.
// Default to empty or invalid key is 0.
//
//	GET /?name=alex&wanna_cake=2&id=
//	QueryInt("wanna_cake", 1) == 2
//	QueryInt("name", 1) == 1
//	QueryInt("id", 1) == 1
//	QueryInt("id") == 0
func ( *Ctx) ( string,  ...int) int {
	// Use Atoi to convert the param to an int or return zero and an error
	,  := strconv.Atoi(.app.getString(.fasthttp.QueryArgs().Peek()))
	if  != nil {
		if len() > 0 {
			return [0]
		}
		return 0
	}

	return 
}

// QueryBool returns bool value of key string parameter in the url.
// Default to empty or invalid key is true.
//
//	Get /?name=alex&want_pizza=false&id=
//	QueryBool("want_pizza") == false
//	QueryBool("want_pizza", true) == false
//	QueryBool("name") == false
//	QueryBool("name", true) == true
//	QueryBool("id") == false
//	QueryBool("id", true) == true
func ( *Ctx) ( string,  ...bool) bool {
	,  := strconv.ParseBool(.app.getString(.fasthttp.QueryArgs().Peek()))
	if  != nil {
		if len() > 0 {
			return [0]
		}
		return false
	}
	return 
}

// QueryFloat returns float64 value of key string parameter in the url.
// Default to empty or invalid key is 0.
//
//	GET /?name=alex&amount=32.23&id=
//	QueryFloat("amount") = 32.23
//	QueryFloat("amount", 3) = 32.23
//	QueryFloat("name", 1) = 1
//	QueryFloat("name") = 0
//	QueryFloat("id", 3) = 3
func ( *Ctx) ( string,  ...float64) float64 {
	// use strconv.ParseFloat to convert the param to a float or return zero and an error.
	,  := strconv.ParseFloat(.app.getString(.fasthttp.QueryArgs().Peek()), 64)
	if  != nil {
		if len() > 0 {
			return [0]
		}
		return 0
	}
	return 
}

// QueryParser binds the query string to a struct.
func ( *Ctx) ( interface{}) error {
	 := make(map[string][]string)
	var  error

	.fasthttp.QueryArgs().VisitAll(func(,  []byte) {
		if  != nil {
			return
		}

		 := .app.getString()
		 := .app.getString()

		if strings.Contains(, "[") {
			,  = parseParamSquareBrackets()
		}

		if .app.config.EnableSplittingOnParsers && strings.Contains(, ",") && equalFieldType(, reflect.Slice, , queryTag) {
			 := strings.Split(, ",")
			for  := 0;  < len(); ++ {
				[] = append([], [])
			}
		} else {
			[] = append([], )
		}
	})

	if  != nil {
		return 
	}

	return .parseToStruct(queryTag, , )
}

func parseParamSquareBrackets( string) (string, error) {
	 := bytebufferpool.Get()
	defer bytebufferpool.Put()

	 := []byte()

	for ,  := range  {
		if  == '[' && [+1] != ']' {
			if  := .WriteByte('.');  != nil {
				return "", fmt.Errorf("failed to write: %w", )
			}
		}

		if  == '[' ||  == ']' {
			continue
		}

		if  := .WriteByte();  != nil {
			return "", fmt.Errorf("failed to write: %w", )
		}
	}

	return .String(), nil
}

// ReqHeaderParser binds the request header strings to a struct.
func ( *Ctx) ( interface{}) error {
	 := make(map[string][]string)
	.fasthttp.Request.Header.VisitAll(func(,  []byte) {
		 := .app.getString()
		 := .app.getString()

		if .app.config.EnableSplittingOnParsers && strings.Contains(, ",") && equalFieldType(, reflect.Slice, , reqHeaderTag) {
			 := strings.Split(, ",")
			for  := 0;  < len(); ++ {
				[] = append([], [])
			}
		} else {
			[] = append([], )
		}
	})

	return .parseToStruct(reqHeaderTag, , )
}

func (*Ctx) ( string,  interface{},  map[string][]string) error {
	// Get decoder from pool
	,  := decoderPoolMap[].Get().(*schema.Decoder)
	if ! {
		panic(fmt.Errorf("failed to type-assert to *schema.Decoder"))
	}
	defer decoderPoolMap[].Put()

	// Set alias tag
	.SetAliasTag()

	if  := .Decode(, );  != nil {
		return fmt.Errorf("failed to decode: %w", )
	}

	return nil
}

func equalFieldType( interface{},  reflect.Kind, ,  string) bool {
	// Get type of interface
	 := reflect.TypeOf().Elem()
	 = utils.ToLower()
	// Must be a struct to match a field
	if .Kind() != reflect.Struct {
		return false
	}
	// Copy interface to an value to be used
	 := reflect.ValueOf().Elem()
	// Loop over each field
	for  := 0;  < .NumField(); ++ {
		// Get field value data
		 := .Field()
		// Can this field be changed?
		if !.CanSet() {
			continue
		}
		// Get field key data
		 := .Field()
		// Get type of field key
		 := .Kind()
		// Does the field type equals input?
		if  !=  {
			continue
		}
		// Get tag from field if exist
		 := .Tag.Get()
		if  == "" {
			 = .Name
		} else {
			 = strings.Split(, ",")[0]
		}
		// Compare field/tag with provided key
		if utils.ToLower() ==  {
			return true
		}
	}
	return false
}

var (
	ErrRangeMalformed     = errors.New("range: malformed range header string")
	ErrRangeUnsatisfiable = errors.New("range: unsatisfiable range")
)

// Range returns a struct containing the type and a slice of ranges.
func ( *Ctx) ( int) (Range, error) {
	var  Range
	 := .Get(HeaderRange)
	if  == "" || !strings.Contains(, "=") {
		return , ErrRangeMalformed
	}
	 := strings.Split(, "=")
	const  = 2
	if len() !=  {
		return , ErrRangeMalformed
	}
	.Type = [0]
	 := strings.Split([1], ",")
	for  := 0;  < len(); ++ {
		 := strings.Split([], "-")
		if len() == 1 {
			return , ErrRangeMalformed
		}
		,  := strconv.Atoi([0])
		,  := strconv.Atoi([1])
		if  != nil { // -nnn
			 =  - 
			 =  - 1
		} else if  != nil { // nnn-
			 =  - 1
		}
		if  > -1 { // limit last-byte-pos to current length
			 =  - 1
		}
		if  >  ||  < 0 {
			continue
		}
		.Ranges = append(.Ranges, struct {
			 int
			   int
		}{
			,
			,
		})
	}
	if len(.Ranges) < 1 {
		return , ErrRangeUnsatisfiable
	}

	return , nil
}

// Redirect to the URL derived from the specified path, with specified status.
// If status is not specified, status defaults to 302 Found.
func ( *Ctx) ( string,  ...int) error {
	.setCanonical(HeaderLocation, )
	if len() > 0 {
		.Status([0])
	} else {
		.Status(StatusFound)
	}
	return nil
}

// Bind Add vars to default view var map binding to template engine.
// Variables are read by the Render method and may be overwritten.
func ( *Ctx) ( Map) error {
	// init viewBindMap - lazy map
	for ,  := range  {
		.viewBindMap.Store(, )
	}
	return nil
}

// getLocationFromRoute get URL location from route using parameters
func ( *Ctx) ( Route,  Map) (string, error) {
	 := bytebufferpool.Get()
	for ,  := range .routeParser.segs {
		if !.IsParam {
			,  := .WriteString(.Const)
			if  != nil {
				return "", fmt.Errorf("failed to write string: %w", )
			}
			continue
		}

		for ,  := range  {
			 :=  == .ParamName || (!.app.config.CaseSensitive && utils.EqualFold(, .ParamName))
			 := .IsGreedy && len() == 1 && isInCharset([0], greedyParameters)
			if  ||  {
				,  := .WriteString(utils.ToString())
				if  != nil {
					return "", fmt.Errorf("failed to write string: %w", )
				}
			}
		}
	}
	 := .String()
	// release buffer
	bytebufferpool.Put()
	return , nil
}

// GetRouteURL generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831"
func ( *Ctx) ( string,  Map) (string, error) {
	return .getLocationFromRoute(.App().GetRoute(), )
}

// RedirectToRoute to the Route registered in the app with appropriate parameters
// If status is not specified, status defaults to 302 Found.
// If you want to send queries to route, you must add "queries" key typed as map[string]string to params.
func ( *Ctx) ( string,  Map,  ...int) error {
	,  := .getLocationFromRoute(.App().GetRoute(), )
	if  != nil {
		return 
	}

	// Check queries
	if ,  := ["queries"].(map[string]string);  {
		 := bytebufferpool.Get()
		defer bytebufferpool.Put()

		 := 1
		for ,  := range  {
			_, _ = .WriteString( + "=" + ) //nolint:errcheck // This will never fail

			if  != len() {
				_, _ = .WriteString("&") //nolint:errcheck // This will never fail
			}
			++
		}

		return .Redirect(+"?"+.String(), ...)
	}
	return .Redirect(, ...)
}

// RedirectBack to the URL to referer
// If status is not specified, status defaults to 302 Found.
func ( *Ctx) ( string,  ...int) error {
	 := .Get(HeaderReferer)
	if  == "" {
		 = 
	}
	return .Redirect(, ...)
}

// Render a template with data and sends a text/html response.
// We support the following engines: html, amber, handlebars, mustache, pug
func ( *Ctx) ( string,  interface{},  ...string) error {
	// Get new buffer from pool
	 := bytebufferpool.Get()
	defer bytebufferpool.Put()

	// Initialize empty bind map if bind is nil
	if  == nil {
		 = make(Map)
	}

	// Pass-locals-to-views, bind, appListKeys
	.renderExtensions()

	var  bool
	for  := len(.app.mountFields.appListKeys) - 1;  >= 0; -- {
		 := .app.mountFields.appListKeys[]
		 := .app.mountFields.appList[]
		if  == "" || strings.Contains(.OriginalURL(), ) {
			if len() == 0 && .config.ViewsLayout != "" {
				 = []string{
					.config.ViewsLayout,
				}
			}

			// Render template from Views
			if .config.Views != nil {
				if  := .config.Views.Render(, , , ...);  != nil {
					return fmt.Errorf("failed to render: %w", )
				}

				 = true
				break
			}
		}
	}

	if ! {
		// Render raw template using 'name' as filepath if no engine is set
		var  *template.Template
		if ,  := readContent(, );  != nil {
			return 
		}
		// Parse template
		,  := template.New("").Parse(.app.getString(.Bytes()))
		if  != nil {
			return fmt.Errorf("failed to parse: %w", )
		}
		.Reset()
		// Render template
		if  := .Execute(, );  != nil {
			return fmt.Errorf("failed to execute: %w", )
		}
	}

	// Set Content-Type to text/html
	.fasthttp.Response.Header.SetContentType(MIMETextHTMLCharsetUTF8)
	// Set rendered template to body
	.fasthttp.Response.SetBody(.Bytes())

	return nil
}

func ( *Ctx) ( interface{}) {
	if ,  := .(Map);  {
		// Bind view map
		.viewBindMap.Range(func(,  interface{}) bool {
			,  := .(string)
			if ! {
				return true
			}
			if ,  := []; ! {
				[] = 
			}
			return true
		})

		// Check if the PassLocalsToViews option is enabled (by default it is disabled)
		if .app.config.PassLocalsToViews {
			// Loop through each local and set it in the map
			.fasthttp.VisitUserValues(func( []byte,  interface{}) {
				// check if bindMap doesn't contain the key
				if ,  := [.app.getString()]; ! {
					// Set the key and value in the bindMap
					[.app.getString()] = 
				}
			})
		}
	}

	if len(.app.mountFields.appListKeys) == 0 {
		.app.generateAppListKeys()
	}
}

// Route returns the matched Route struct.
func ( *Ctx) () *Route {
	if .route == nil {
		// Fallback for fasthttp error handler
		return &Route{
			path:     .pathOriginal,
			Path:     .pathOriginal,
			Method:   .method,
			Handlers: make([]Handler, 0),
			Params:   make([]string, 0),
		}
	}
	return .route
}

// SaveFile saves any multipart file to disk.
func (*Ctx) ( *multipart.FileHeader,  string) error {
	return fasthttp.SaveMultipartFile(, )
}

// SaveFileToStorage saves any multipart file to an external storage system.
func (*Ctx) ( *multipart.FileHeader,  string,  Storage) error {
	,  := .Open()
	if  != nil {
		return fmt.Errorf("failed to open: %w", )
	}

	,  := io.ReadAll()
	if  != nil {
		return fmt.Errorf("failed to read: %w", )
	}

	if  := .Set(, , 0);  != nil {
		return fmt.Errorf("failed to store: %w", )
	}

	return nil
}

// Secure returns whether a secure connection was established.
func ( *Ctx) () bool {
	return .Protocol() == schemeHTTPS
}

// Send sets the HTTP response body without copying it.
// From this point onward the body argument must not be changed.
func ( *Ctx) ( []byte) error {
	// Write response body
	.fasthttp.Response.SetBodyRaw()
	return nil
}

var (
	sendFileOnce    sync.Once
	sendFileFS      *fasthttp.FS
	sendFileHandler fasthttp.RequestHandler
)

// SendFile transfers the file from the given path.
// The file is not compressed by default, enable this by passing a 'true' argument
// Sets the Content-Type response HTTP header field based on the filenames extension.
func ( *Ctx) ( string,  ...bool) error {
	// Save the filename, we will need it in the error message if the file isn't found
	 := 

	// https://github.com/valyala/fasthttp/blob/c7576cc10cabfc9c993317a2d3f8355497bea156/fs.go#L129-L134
	sendFileOnce.Do(func() {
		const  = 10 * time.Second
		sendFileFS = &fasthttp.FS{
			Root:                 "",
			AllowEmptyRoot:       true,
			GenerateIndexPages:   false,
			AcceptByteRange:      true,
			Compress:             true,
			CompressedFileSuffix: .app.config.CompressedFileSuffix,
			CacheDuration:        ,
			IndexNames:           []string{"index.html"},
			PathNotFound: func( *fasthttp.RequestCtx) {
				.Response.SetStatusCode(StatusNotFound)
			},
		}
		sendFileHandler = sendFileFS.NewRequestHandler()
	})

	// Keep original path for mutable params
	.pathOriginal = utils.CopyString(.pathOriginal)
	// Disable compression
	if len() == 0 || ![0] {
		// https://github.com/valyala/fasthttp/blob/7cc6f4c513f9e0d3686142e0a1a5aa2f76b3194a/fs.go#L55
		.fasthttp.Request.Header.Del(HeaderAcceptEncoding)
	}
	// copy of https://github.com/valyala/fasthttp/blob/7cc6f4c513f9e0d3686142e0a1a5aa2f76b3194a/fs.go#L103-L121 with small adjustments
	if len() == 0 || !filepath.IsAbs() {
		// extend relative path to absolute path
		 := len() > 0 && ([len()-1] == '/' || [len()-1] == '\\')

		var  error
		 = filepath.FromSlash()
		if ,  = filepath.Abs();  != nil {
			return fmt.Errorf("failed to determine abs file path: %w", )
		}
		if  {
			 += "/"
		}
	}
	// convert the path to forward slashes regardless the OS in order to set the URI properly
	// the handler will convert back to OS path separator before opening the file
	 = filepath.ToSlash()

	// Restore the original requested URL
	 := utils.CopyString(.OriginalURL())
	defer .fasthttp.Request.SetRequestURI()
	// Set new URI for fileHandler
	.fasthttp.Request.SetRequestURI()
	// Save status code
	 := .fasthttp.Response.StatusCode()
	// Serve file
	sendFileHandler(.fasthttp)
	// Get the status code which is set by fasthttp
	 := .fasthttp.Response.StatusCode()
	// Set the status code set by the user if it is different from the fasthttp status code and 200
	if  !=  &&  != StatusOK {
		.Status()
	}
	// Check for error
	if  != StatusNotFound &&  == StatusNotFound {
		return NewError(StatusNotFound, fmt.Sprintf("sendfile: file %s not found", ))
	}
	return nil
}

// SendStatus sets the HTTP status code and if the response body is empty,
// it sets the correct status message in the body.
func ( *Ctx) ( int) error {
	.Status()

	// Only set status body when there is no response body
	if len(.fasthttp.Response.Body()) == 0 {
		return .SendString(utils.StatusMessage())
	}

	return nil
}

// SendString sets the HTTP response body for string types.
// This means no type assertion, recommended for faster performance
func ( *Ctx) ( string) error {
	.fasthttp.Response.SetBodyString()

	return nil
}

// SendStream sets response body stream and optional body size.
func ( *Ctx) ( io.Reader,  ...int) error {
	if len() > 0 && [0] >= 0 {
		.fasthttp.Response.SetBodyStream(, [0])
	} else {
		.fasthttp.Response.SetBodyStream(, -1)
	}

	return nil
}

// Set sets the response's HTTP header field to the specified key, value.
func ( *Ctx) (,  string) {
	.fasthttp.Response.Header.Set(, )
}

func ( *Ctx) (,  string) {
	.fasthttp.Response.Header.SetCanonical(.app.getBytes(), .app.getBytes())
}

// Subdomains returns a string slice of subdomains in the domain name of the request.
// The subdomain offset, which defaults to 2, is used for determining the beginning of the subdomain segments.
func ( *Ctx) ( ...int) []string {
	 := 2
	if len() > 0 {
		 = [0]
	}
	 := strings.Split(.Hostname(), ".")
	 := len() - 
	// Check index to avoid slice bounds out of range panic
	if  < 0 {
		 = len()
	}
	 = [:]
	return 
}

// Stale is not implemented yet, pull requests are welcome!
func ( *Ctx) () bool {
	return !.Fresh()
}

// Status sets the HTTP status for the response.
// This method is chainable.
func ( *Ctx) ( int) *Ctx {
	.fasthttp.Response.SetStatusCode()
	return 
}

// String returns unique string representation of the ctx.
//
// The returned value may be useful for logging.
func ( *Ctx) () string {
	return fmt.Sprintf(
		"#%016X - %s <-> %s - %s %s",
		.fasthttp.ID(),
		.fasthttp.LocalAddr(),
		.fasthttp.RemoteAddr(),
		.fasthttp.Request.Header.Method(),
		.fasthttp.URI().FullURI(),
	)
}

// Type sets the Content-Type HTTP header to the MIME type specified by the file extension.
func ( *Ctx) ( string,  ...string) *Ctx {
	if len() > 0 {
		.fasthttp.Response.Header.SetContentType(utils.GetMIME() + "; charset=" + [0])
	} else {
		.fasthttp.Response.Header.SetContentType(utils.GetMIME())
	}
	return 
}

// Vary adds the given header field to the Vary response header.
// This will append the header, if not already listed, otherwise leaves it listed in the current location.
func ( *Ctx) ( ...string) {
	.Append(HeaderVary, ...)
}

// Write appends p into response body.
func ( *Ctx) ( []byte) (int, error) {
	.fasthttp.Response.AppendBody()
	return len(), nil
}

// Writef appends f & a into response body writer.
func ( *Ctx) ( string,  ...interface{}) (int, error) {
	//nolint:wrapcheck // This must not be wrapped
	return fmt.Fprintf(.fasthttp.Response.BodyWriter(), , ...)
}

// WriteString appends s to response body.
func ( *Ctx) ( string) (int, error) {
	.fasthttp.Response.AppendBodyString()
	return len(), nil
}

// XHR returns a Boolean property, that is true, if the request's X-Requested-With header field is XMLHttpRequest,
// indicating that the request was issued by a client library (such as jQuery).
func ( *Ctx) () bool {
	return utils.EqualFoldBytes(.app.getBytes(.Get(HeaderXRequestedWith)), []byte("xmlhttprequest"))
}

// configDependentPaths set paths for route recognition and prepared paths for the user,
// here the features for caseSensitive, decoded paths, strict paths are evaluated
func ( *Ctx) () {
	.pathBuffer = append(.pathBuffer[0:0], .pathOriginal...)
	// If UnescapePath enabled, we decode the path and save it for the framework user
	if .app.config.UnescapePath {
		.pathBuffer = fasthttp.AppendUnquotedArg(.pathBuffer[:0], .pathBuffer)
	}
	.path = .app.getString(.pathBuffer)

	// another path is specified which is for routing recognition only
	// use the path that was changed by the previous configuration flags
	.detectionPathBuffer = append(.detectionPathBuffer[0:0], .pathBuffer...)
	// If CaseSensitive is disabled, we lowercase the original path
	if !.app.config.CaseSensitive {
		.detectionPathBuffer = utils.ToLowerBytes(.detectionPathBuffer)
	}
	// If StrictRouting is disabled, we strip all trailing slashes
	if !.app.config.StrictRouting && len(.detectionPathBuffer) > 1 && .detectionPathBuffer[len(.detectionPathBuffer)-1] == '/' {
		.detectionPathBuffer = utils.TrimRightBytes(.detectionPathBuffer, '/')
	}
	.detectionPath = .app.getString(.detectionPathBuffer)

	// Define the path for dividing routes into areas for fast tree detection, so that fewer routes need to be traversed,
	// since the first three characters area select a list of routes
	.treePath = .treePath[0:0]
	const  = 3
	if len(.detectionPath) >=  {
		.treePath = .detectionPath[:]
	}
}

func ( *Ctx) () bool {
	if !.app.config.EnableTrustedProxyCheck {
		return true
	}

	 := .fasthttp.RemoteIP()

	if ,  := .app.config.trustedProxiesMap[.String()];  {
		return true
	}

	for ,  := range .app.config.trustedProxyRanges {
		if .Contains() {
			return true
		}
	}

	return false
}

var localHosts = [...]string{"127.0.0.1", "::1"}

// IsLocalHost will return true if address is a localhost address.
func (*Ctx) ( string) bool {
	for ,  := range localHosts {
		if  ==  {
			return true
		}
	}
	return false
}

// IsFromLocal will return true if request came from local.
func ( *Ctx) () bool {
	return .isLocalHost(.fasthttp.RemoteIP().String())
}