github.com/boomhut/fiber/v2@v2.0.0-20230603160335-b65c856e57d3/app.go (about)

     1  // ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
     2  // 🤖 Github Repository: https://github.com/gofiber/fiber
     3  // 📌 API Documentation: https://docs.gofiber.io
     4  
     5  // Package fiber is an Express inspired web framework built on top of Fasthttp,
     6  // the fastest HTTP engine for Go. Designed to ease things up for fast
     7  // development with zero memory allocation and performance in mind.
     8  package fiber
     9  
    10  import (
    11  	"bufio"
    12  	"context"
    13  	"encoding/json"
    14  	"encoding/xml"
    15  	"errors"
    16  	"fmt"
    17  	"log"
    18  	"net"
    19  	"net/http"
    20  	"net/http/httputil"
    21  	"reflect"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/boomhut/fiber/v2/utils"
    28  
    29  	"github.com/valyala/fasthttp"
    30  )
    31  
    32  // Version of current fiber package
    33  const Version = "2.46.0"
    34  
    35  // Handler defines a function to serve HTTP requests.
    36  type Handler = func(*Ctx) error
    37  
    38  // Map is a shortcut for map[string]interface{}, useful for JSON returns
    39  type Map map[string]interface{}
    40  
    41  // Storage interface for communicating with different database/key-value
    42  // providers
    43  type Storage interface {
    44  	// Get gets the value for the given key.
    45  	// `nil, nil` is returned when the key does not exist
    46  	Get(key string) ([]byte, error)
    47  
    48  	// Set stores the given value for the given key along
    49  	// with an expiration value, 0 means no expiration.
    50  	// Empty key or value will be ignored without an error.
    51  	Set(key string, val []byte, exp time.Duration) error
    52  
    53  	// Delete deletes the value for the given key.
    54  	// It returns no error if the storage does not contain the key,
    55  	Delete(key string) error
    56  
    57  	// Reset resets the storage and delete all keys.
    58  	Reset() error
    59  
    60  	// Close closes the storage and will stop any running garbage
    61  	// collectors and open connections.
    62  	Close() error
    63  }
    64  
    65  // ErrorHandler defines a function that will process all errors
    66  // returned from any handlers in the stack
    67  //
    68  //	cfg := fiber.Config{}
    69  //	cfg.ErrorHandler = func(c *Ctx, err error) error {
    70  //	 code := StatusInternalServerError
    71  //	 var e *fiber.Error
    72  //	 if errors.As(err, &e) {
    73  //	   code = e.Code
    74  //	 }
    75  //	 c.Set(HeaderContentType, MIMETextPlainCharsetUTF8)
    76  //	 return c.Status(code).SendString(err.Error())
    77  //	}
    78  //	app := fiber.New(cfg)
    79  type ErrorHandler = func(*Ctx, error) error
    80  
    81  // Error represents an error that occurred while handling a request.
    82  type Error struct {
    83  	Code    int    `json:"code"`
    84  	Message string `json:"message"`
    85  }
    86  
    87  // App denotes the Fiber application.
    88  type App struct {
    89  	mutex sync.Mutex
    90  	// Route stack divided by HTTP methods
    91  	stack [][]*Route
    92  	// Route stack divided by HTTP methods and route prefixes
    93  	treeStack []map[string][]*Route
    94  	// contains the information if the route stack has been changed to build the optimized tree
    95  	routesRefreshed bool
    96  	// Amount of registered routes
    97  	routesCount uint32
    98  	// Amount of registered handlers
    99  	handlersCount uint32
   100  	// Ctx pool
   101  	pool sync.Pool
   102  	// Fasthttp server
   103  	server *fasthttp.Server
   104  	// App config
   105  	config Config
   106  	// Converts string to a byte slice
   107  	getBytes func(s string) (b []byte)
   108  	// Converts byte slice to a string
   109  	getString func(b []byte) string
   110  	// Hooks
   111  	hooks *Hooks
   112  	// Latest route & group
   113  	latestRoute *Route
   114  	// TLS handler
   115  	tlsHandler *TLSHandler
   116  	// Mount fields
   117  	mountFields *mountFields
   118  	// Indicates if the value was explicitly configured
   119  	configured Config
   120  }
   121  
   122  // Config is a struct holding the server settings.
   123  type Config struct {
   124  	// When set to true, this will spawn multiple Go processes listening on the same port.
   125  	//
   126  	// Default: false
   127  	Prefork bool `json:"prefork"`
   128  
   129  	// Enables the "Server: value" HTTP header.
   130  	//
   131  	// Default: ""
   132  	ServerHeader string `json:"server_header"`
   133  
   134  	// When set to true, the router treats "/foo" and "/foo/" as different.
   135  	// By default this is disabled and both "/foo" and "/foo/" will execute the same handler.
   136  	//
   137  	// Default: false
   138  	StrictRouting bool `json:"strict_routing"`
   139  
   140  	// When set to true, enables case sensitive routing.
   141  	// E.g. "/FoO" and "/foo" are treated as different routes.
   142  	// By default this is disabled and both "/FoO" and "/foo" will execute the same handler.
   143  	//
   144  	// Default: false
   145  	CaseSensitive bool `json:"case_sensitive"`
   146  
   147  	// When set to true, this relinquishes the 0-allocation promise in certain
   148  	// cases in order to access the handler values (e.g. request bodies) in an
   149  	// immutable fashion so that these values are available even if you return
   150  	// from handler.
   151  	//
   152  	// Default: false
   153  	Immutable bool `json:"immutable"`
   154  
   155  	// When set to true, converts all encoded characters in the route back
   156  	// before setting the path for the context, so that the routing,
   157  	// the returning of the current url from the context `ctx.Path()`
   158  	// and the parameters `ctx.Params(%key%)` with decoded characters will work
   159  	//
   160  	// Default: false
   161  	UnescapePath bool `json:"unescape_path"`
   162  
   163  	// Enable or disable ETag header generation, since both weak and strong etags are generated
   164  	// using the same hashing method (CRC-32). Weak ETags are the default when enabled.
   165  	//
   166  	// Default: false
   167  	ETag bool `json:"etag"`
   168  
   169  	// Max body size that the server accepts.
   170  	// -1 will decline any body size
   171  	//
   172  	// Default: 4 * 1024 * 1024
   173  	BodyLimit int `json:"body_limit"`
   174  
   175  	// Maximum number of concurrent connections.
   176  	//
   177  	// Default: 256 * 1024
   178  	Concurrency int `json:"concurrency"`
   179  
   180  	// Views is the interface that wraps the Render function.
   181  	//
   182  	// Default: nil
   183  	Views Views `json:"-"`
   184  
   185  	// Views Layout is the global layout for all template render until override on Render function.
   186  	//
   187  	// Default: ""
   188  	ViewsLayout string `json:"views_layout"`
   189  
   190  	// PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine
   191  	//
   192  	// Default: false
   193  	PassLocalsToViews bool `json:"pass_locals_to_views"`
   194  
   195  	// The amount of time allowed to read the full request including body.
   196  	// It is reset after the request handler has returned.
   197  	// The connection's read deadline is reset when the connection opens.
   198  	//
   199  	// Default: unlimited
   200  	ReadTimeout time.Duration `json:"read_timeout"`
   201  
   202  	// The maximum duration before timing out writes of the response.
   203  	// It is reset after the request handler has returned.
   204  	//
   205  	// Default: unlimited
   206  	WriteTimeout time.Duration `json:"write_timeout"`
   207  
   208  	// The maximum amount of time to wait for the next request when keep-alive is enabled.
   209  	// If IdleTimeout is zero, the value of ReadTimeout is used.
   210  	//
   211  	// Default: unlimited
   212  	IdleTimeout time.Duration `json:"idle_timeout"`
   213  
   214  	// Per-connection buffer size for requests' reading.
   215  	// This also limits the maximum header size.
   216  	// Increase this buffer if your clients send multi-KB RequestURIs
   217  	// and/or multi-KB headers (for example, BIG cookies).
   218  	//
   219  	// Default: 4096
   220  	ReadBufferSize int `json:"read_buffer_size"`
   221  
   222  	// Per-connection buffer size for responses' writing.
   223  	//
   224  	// Default: 4096
   225  	WriteBufferSize int `json:"write_buffer_size"`
   226  
   227  	// CompressedFileSuffix adds suffix to the original file name and
   228  	// tries saving the resulting compressed file under the new file name.
   229  	//
   230  	// Default: ".fiber.gz"
   231  	CompressedFileSuffix string `json:"compressed_file_suffix"`
   232  
   233  	// ProxyHeader will enable c.IP() to return the value of the given header key
   234  	// By default c.IP() will return the Remote IP from the TCP connection
   235  	// This property can be useful if you are behind a load balancer: X-Forwarded-*
   236  	// NOTE: headers are easily spoofed and the detected IP addresses are unreliable.
   237  	//
   238  	// Default: ""
   239  	ProxyHeader string `json:"proxy_header"`
   240  
   241  	// GETOnly rejects all non-GET requests if set to true.
   242  	// This option is useful as anti-DoS protection for servers
   243  	// accepting only GET requests. The request size is limited
   244  	// by ReadBufferSize if GETOnly is set.
   245  	//
   246  	// Default: false
   247  	GETOnly bool `json:"get_only"`
   248  
   249  	// ErrorHandler is executed when an error is returned from fiber.Handler.
   250  	//
   251  	// Default: DefaultErrorHandler
   252  	ErrorHandler ErrorHandler `json:"-"`
   253  
   254  	// When set to true, disables keep-alive connections.
   255  	// The server will close incoming connections after sending the first response to client.
   256  	//
   257  	// Default: false
   258  	DisableKeepalive bool `json:"disable_keepalive"`
   259  
   260  	// When set to true, causes the default date header to be excluded from the response.
   261  	//
   262  	// Default: false
   263  	DisableDefaultDate bool `json:"disable_default_date"`
   264  
   265  	// When set to true, causes the default Content-Type header to be excluded from the response.
   266  	//
   267  	// Default: false
   268  	DisableDefaultContentType bool `json:"disable_default_content_type"`
   269  
   270  	// When set to true, disables header normalization.
   271  	// By default all header names are normalized: conteNT-tYPE -> Content-Type.
   272  	//
   273  	// Default: false
   274  	DisableHeaderNormalizing bool `json:"disable_header_normalizing"`
   275  
   276  	// When set to true, it will not print out the «Fiber» ASCII art and listening address.
   277  	//
   278  	// Default: false
   279  	DisableStartupMessage bool `json:"disable_startup_message"`
   280  
   281  	// This function allows to setup app name for the app
   282  	//
   283  	// Default: nil
   284  	AppName string `json:"app_name"`
   285  
   286  	// StreamRequestBody enables request body streaming,
   287  	// and calls the handler sooner when given body is
   288  	// larger then the current limit.
   289  	StreamRequestBody bool
   290  
   291  	// Will not pre parse Multipart Form data if set to true.
   292  	//
   293  	// This option is useful for servers that desire to treat
   294  	// multipart form data as a binary blob, or choose when to parse the data.
   295  	//
   296  	// Server pre parses multipart form data by default.
   297  	DisablePreParseMultipartForm bool
   298  
   299  	// Aggressively reduces memory usage at the cost of higher CPU usage
   300  	// if set to true.
   301  	//
   302  	// Try enabling this option only if the server consumes too much memory
   303  	// serving mostly idle keep-alive connections. This may reduce memory
   304  	// usage by more than 50%.
   305  	//
   306  	// Default: false
   307  	ReduceMemoryUsage bool `json:"reduce_memory_usage"`
   308  
   309  	// FEATURE: v2.3.x
   310  	// The router executes the same handler by default if StrictRouting or CaseSensitive is disabled.
   311  	// Enabling RedirectFixedPath will change this behavior into a client redirect to the original route path.
   312  	// Using the status code 301 for GET requests and 308 for all other request methods.
   313  	//
   314  	// Default: false
   315  	// RedirectFixedPath bool
   316  
   317  	// When set by an external client of Fiber it will use the provided implementation of a
   318  	// JSONMarshal
   319  	//
   320  	// Allowing for flexibility in using another json library for encoding
   321  	// Default: json.Marshal
   322  	JSONEncoder utils.JSONMarshal `json:"-"`
   323  
   324  	// When set by an external client of Fiber it will use the provided implementation of a
   325  	// JSONUnmarshal
   326  	//
   327  	// Allowing for flexibility in using another json library for decoding
   328  	// Default: json.Unmarshal
   329  	JSONDecoder utils.JSONUnmarshal `json:"-"`
   330  
   331  	// XMLEncoder set by an external client of Fiber it will use the provided implementation of a
   332  	// XMLMarshal
   333  	//
   334  	// Allowing for flexibility in using another XML library for encoding
   335  	// Default: xml.Marshal
   336  	XMLEncoder utils.XMLMarshal `json:"-"`
   337  
   338  	// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only)
   339  	// WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chose.
   340  	//
   341  	// Default: NetworkTCP4
   342  	Network string
   343  
   344  	// If you find yourself behind some sort of proxy, like a load balancer,
   345  	// then certain header information may be sent to you using special X-Forwarded-* headers or the Forwarded header.
   346  	// For example, the Host HTTP header is usually used to return the requested host.
   347  	// But when you’re behind a proxy, the actual host may be stored in an X-Forwarded-Host header.
   348  	//
   349  	// If you are behind a proxy, you should enable TrustedProxyCheck to prevent header spoofing.
   350  	// If you enable EnableTrustedProxyCheck and leave TrustedProxies empty Fiber will skip
   351  	// all headers that could be spoofed.
   352  	// If request ip in TrustedProxies whitelist then:
   353  	//   1. c.Protocol() get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header
   354  	//   2. c.IP() get value from ProxyHeader header.
   355  	//   3. c.Hostname() get value from X-Forwarded-Host header
   356  	// But if request ip NOT in Trusted Proxies whitelist then:
   357  	//   1. c.Protocol() WON't get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header,
   358  	//    will return https in case when tls connection is handled by the app, of http otherwise
   359  	//   2. c.IP() WON'T get value from ProxyHeader header, will return RemoteIP() from fasthttp context
   360  	//   3. c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host()
   361  	//    will be used to get the hostname.
   362  	//
   363  	// Default: false
   364  	EnableTrustedProxyCheck bool `json:"enable_trusted_proxy_check"`
   365  
   366  	// Read EnableTrustedProxyCheck doc.
   367  	//
   368  	// Default: []string
   369  	TrustedProxies     []string `json:"trusted_proxies"`
   370  	trustedProxiesMap  map[string]struct{}
   371  	trustedProxyRanges []*net.IPNet
   372  
   373  	// If set to true, c.IP() and c.IPs() will validate IP addresses before returning them.
   374  	// Also, c.IP() will return only the first valid IP rather than just the raw header
   375  	// WARNING: this has a performance cost associated with it.
   376  	//
   377  	// Default: false
   378  	EnableIPValidation bool `json:"enable_ip_validation"`
   379  
   380  	// If set to true, will print all routes with their method, path and handler.
   381  	// Default: false
   382  	EnablePrintRoutes bool `json:"enable_print_routes"`
   383  
   384  	// You can define custom color scheme. They'll be used for startup message, route list and some middlewares.
   385  	//
   386  	// Optional. Default: DefaultColors
   387  	ColorScheme Colors `json:"color_scheme"`
   388  
   389  	// RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish.
   390  	//
   391  	// Optional. Default: DefaultMethods
   392  	RequestMethods []string
   393  
   394  	Codename string `json:"codename"`
   395  
   396  	CodenameEmoji rune `json:"codename_emoji"`
   397  }
   398  
   399  // Static defines configuration options when defining static assets.
   400  type Static struct {
   401  	// When set to true, the server tries minimizing CPU usage by caching compressed files.
   402  	// This works differently than the github.com/gofiber/compression middleware.
   403  	// Optional. Default value false
   404  	Compress bool `json:"compress"`
   405  
   406  	// When set to true, enables byte range requests.
   407  	// Optional. Default value false
   408  	ByteRange bool `json:"byte_range"`
   409  
   410  	// When set to true, enables directory browsing.
   411  	// Optional. Default value false.
   412  	Browse bool `json:"browse"`
   413  
   414  	// When set to true, enables direct download.
   415  	// Optional. Default value false.
   416  	Download bool `json:"download"`
   417  
   418  	// The name of the index file for serving a directory.
   419  	// Optional. Default value "index.html".
   420  	Index string `json:"index"`
   421  
   422  	// Expiration duration for inactive file handlers.
   423  	// Use a negative time.Duration to disable it.
   424  	//
   425  	// Optional. Default value 10 * time.Second.
   426  	CacheDuration time.Duration `json:"cache_duration"`
   427  
   428  	// The value for the Cache-Control HTTP-header
   429  	// that is set on the file response. MaxAge is defined in seconds.
   430  	//
   431  	// Optional. Default value 0.
   432  	MaxAge int `json:"max_age"`
   433  
   434  	// ModifyResponse defines a function that allows you to alter the response.
   435  	//
   436  	// Optional. Default: nil
   437  	ModifyResponse Handler
   438  
   439  	// Next defines a function to skip this middleware when returned true.
   440  	//
   441  	// Optional. Default: nil
   442  	Next func(c *Ctx) bool
   443  }
   444  
   445  // RouteMessage is some message need to be print when server starts
   446  type RouteMessage struct {
   447  	name     string
   448  	method   string
   449  	path     string
   450  	handlers string
   451  }
   452  
   453  // Default Config values
   454  const (
   455  	DefaultBodyLimit            = 4 * 1024 * 1024
   456  	DefaultConcurrency          = 256 * 1024
   457  	DefaultReadBufferSize       = 4096
   458  	DefaultWriteBufferSize      = 4096
   459  	DefaultCompressedFileSuffix = ".fiber.gz"
   460  )
   461  
   462  // HTTP methods enabled by default
   463  var DefaultMethods = []string{
   464  	MethodGet,
   465  	MethodHead,
   466  	MethodPost,
   467  	MethodPut,
   468  	MethodDelete,
   469  	MethodConnect,
   470  	MethodOptions,
   471  	MethodTrace,
   472  	MethodPatch,
   473  }
   474  
   475  // DefaultErrorHandler that process return errors from handlers
   476  func DefaultErrorHandler(c *Ctx, err error) error {
   477  	code := StatusInternalServerError
   478  	var e *Error
   479  	if errors.As(err, &e) {
   480  		code = e.Code
   481  	}
   482  	c.Set(HeaderContentType, MIMETextPlainCharsetUTF8)
   483  	return c.Status(code).SendString(err.Error())
   484  }
   485  
   486  // New creates a new Fiber named instance.
   487  //
   488  //	app := fiber.New()
   489  //
   490  // You can pass optional configuration options by passing a Config struct:
   491  //
   492  //	app := fiber.New(fiber.Config{
   493  //	    Prefork: true,
   494  //	    ServerHeader: "Fiber",
   495  //	})
   496  func New(config ...Config) *App {
   497  	// Create a new app
   498  	app := &App{
   499  		// Create Ctx pool
   500  		pool: sync.Pool{
   501  			New: func() interface{} {
   502  				return new(Ctx)
   503  			},
   504  		},
   505  		// Create config
   506  		config:      Config{},
   507  		getBytes:    utils.UnsafeBytes,
   508  		getString:   utils.UnsafeString,
   509  		latestRoute: &Route{},
   510  	}
   511  
   512  	// Define hooks
   513  	app.hooks = newHooks(app)
   514  
   515  	// Define mountFields
   516  	app.mountFields = newMountFields(app)
   517  
   518  	// Override config if provided
   519  	if len(config) > 0 {
   520  		app.config = config[0]
   521  	}
   522  
   523  	// Initialize configured before defaults are set
   524  	app.configured = app.config
   525  
   526  	if app.config.ETag {
   527  		if !IsChild() {
   528  			log.Printf("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.\n")
   529  		}
   530  	}
   531  
   532  	// Override default values
   533  	if app.config.BodyLimit == 0 {
   534  		app.config.BodyLimit = DefaultBodyLimit
   535  	}
   536  	if app.config.Concurrency <= 0 {
   537  		app.config.Concurrency = DefaultConcurrency
   538  	}
   539  	if app.config.ReadBufferSize <= 0 {
   540  		app.config.ReadBufferSize = DefaultReadBufferSize
   541  	}
   542  	if app.config.WriteBufferSize <= 0 {
   543  		app.config.WriteBufferSize = DefaultWriteBufferSize
   544  	}
   545  	if app.config.CompressedFileSuffix == "" {
   546  		app.config.CompressedFileSuffix = DefaultCompressedFileSuffix
   547  	}
   548  	if app.config.Immutable {
   549  		app.getBytes, app.getString = getBytesImmutable, getStringImmutable
   550  	}
   551  
   552  	if app.config.ErrorHandler == nil {
   553  		app.config.ErrorHandler = DefaultErrorHandler
   554  	}
   555  
   556  	if app.config.JSONEncoder == nil {
   557  		app.config.JSONEncoder = json.Marshal
   558  	}
   559  	if app.config.JSONDecoder == nil {
   560  		app.config.JSONDecoder = json.Unmarshal
   561  	}
   562  	if app.config.XMLEncoder == nil {
   563  		app.config.XMLEncoder = xml.Marshal
   564  	}
   565  	if app.config.Network == "" {
   566  		app.config.Network = NetworkTCP4
   567  	}
   568  	if len(app.config.RequestMethods) == 0 {
   569  		app.config.RequestMethods = DefaultMethods
   570  	}
   571  
   572  	app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies))
   573  	for _, ipAddress := range app.config.TrustedProxies {
   574  		app.handleTrustedProxy(ipAddress)
   575  	}
   576  
   577  	/// default Codename and Codename Emoji
   578  	if app.config.Codename == "" {
   579  		app.config.Codename = "Dodo"
   580  	}
   581  
   582  	if app.config.CodenameEmoji == 0 {
   583  		defEmojiString := "🐦"
   584  		// string to rune slice
   585  		emojiRune := []rune(defEmojiString)
   586  		app.config.CodenameEmoji = emojiRune[0]
   587  
   588  	}
   589  
   590  	// Create router stack
   591  	app.stack = make([][]*Route, len(app.config.RequestMethods))
   592  	app.treeStack = make([]map[string][]*Route, len(app.config.RequestMethods))
   593  
   594  	// Override colors
   595  	app.config.ColorScheme = defaultColors(app.config.ColorScheme)
   596  
   597  	// Init app
   598  	app.init()
   599  
   600  	// Return app
   601  	return app
   602  }
   603  
   604  // Adds an ip address to trustedProxyRanges or trustedProxiesMap based on whether it is an IP range or not
   605  func (app *App) handleTrustedProxy(ipAddress string) {
   606  	if strings.Contains(ipAddress, "/") {
   607  		_, ipNet, err := net.ParseCIDR(ipAddress)
   608  		if err != nil {
   609  			log.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err)
   610  		} else {
   611  			app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet)
   612  		}
   613  	} else {
   614  		app.config.trustedProxiesMap[ipAddress] = struct{}{}
   615  	}
   616  }
   617  
   618  // SetTLSHandler You can use SetTLSHandler to use ClientHelloInfo when using TLS with Listener.
   619  func (app *App) SetTLSHandler(tlsHandler *TLSHandler) {
   620  	// Attach the tlsHandler to the config
   621  	app.mutex.Lock()
   622  	app.tlsHandler = tlsHandler
   623  	app.mutex.Unlock()
   624  }
   625  
   626  // Name Assign name to specific route.
   627  func (app *App) Name(name string) Router {
   628  	app.mutex.Lock()
   629  
   630  	latestGroup := app.latestRoute.group
   631  	if latestGroup != nil {
   632  		app.latestRoute.Name = latestGroup.name + name
   633  	} else {
   634  		app.latestRoute.Name = name
   635  	}
   636  
   637  	if err := app.hooks.executeOnNameHooks(*app.latestRoute); err != nil {
   638  		panic(err)
   639  	}
   640  	app.mutex.Unlock()
   641  
   642  	return app
   643  }
   644  
   645  // GetRoute Get route by name
   646  func (app *App) GetRoute(name string) Route {
   647  	for _, routes := range app.stack {
   648  		for _, route := range routes {
   649  			if route.Name == name {
   650  				return *route
   651  			}
   652  		}
   653  	}
   654  
   655  	return Route{}
   656  }
   657  
   658  // GetRoutes Get all routes. When filterUseOption equal to true, it will filter the routes registered by the middleware.
   659  func (app *App) GetRoutes(filterUseOption ...bool) []Route {
   660  	var rs []Route
   661  	var filterUse bool
   662  	if len(filterUseOption) != 0 {
   663  		filterUse = filterUseOption[0]
   664  	}
   665  	for _, routes := range app.stack {
   666  		for _, route := range routes {
   667  			if filterUse && route.use {
   668  				continue
   669  			}
   670  			rs = append(rs, *route)
   671  		}
   672  	}
   673  	return rs
   674  }
   675  
   676  // Use registers a middleware route that will match requests
   677  // with the provided prefix (which is optional and defaults to "/").
   678  //
   679  //	app.Use(func(c *fiber.Ctx) error {
   680  //	     return c.Next()
   681  //	})
   682  //	app.Use("/api", func(c *fiber.Ctx) error {
   683  //	     return c.Next()
   684  //	})
   685  //	app.Use("/api", handler, func(c *fiber.Ctx) error {
   686  //	     return c.Next()
   687  //	})
   688  //
   689  // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
   690  func (app *App) Use(args ...interface{}) Router {
   691  	var prefix string
   692  	var prefixes []string
   693  	var handlers []Handler
   694  
   695  	for i := 0; i < len(args); i++ {
   696  		switch arg := args[i].(type) {
   697  		case string:
   698  			prefix = arg
   699  		case []string:
   700  			prefixes = arg
   701  		case Handler:
   702  			handlers = append(handlers, arg)
   703  		default:
   704  			panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
   705  		}
   706  	}
   707  
   708  	if len(prefixes) == 0 {
   709  		prefixes = append(prefixes, prefix)
   710  	}
   711  
   712  	for _, prefix := range prefixes {
   713  		app.register(methodUse, prefix, nil, handlers...)
   714  	}
   715  
   716  	return app
   717  }
   718  
   719  // Get registers a route for GET methods that requests a representation
   720  // of the specified resource. Requests using GET should only retrieve data.
   721  func (app *App) Get(path string, handlers ...Handler) Router {
   722  	return app.Head(path, handlers...).Add(MethodGet, path, handlers...)
   723  }
   724  
   725  // Head registers a route for HEAD methods that asks for a response identical
   726  // to that of a GET request, but without the response body.
   727  func (app *App) Head(path string, handlers ...Handler) Router {
   728  	return app.Add(MethodHead, path, handlers...)
   729  }
   730  
   731  // Post registers a route for POST methods that is used to submit an entity to the
   732  // specified resource, often causing a change in state or side effects on the server.
   733  func (app *App) Post(path string, handlers ...Handler) Router {
   734  	return app.Add(MethodPost, path, handlers...)
   735  }
   736  
   737  // Put registers a route for PUT methods that replaces all current representations
   738  // of the target resource with the request payload.
   739  func (app *App) Put(path string, handlers ...Handler) Router {
   740  	return app.Add(MethodPut, path, handlers...)
   741  }
   742  
   743  // Delete registers a route for DELETE methods that deletes the specified resource.
   744  func (app *App) Delete(path string, handlers ...Handler) Router {
   745  	return app.Add(MethodDelete, path, handlers...)
   746  }
   747  
   748  // Connect registers a route for CONNECT methods that establishes a tunnel to the
   749  // server identified by the target resource.
   750  func (app *App) Connect(path string, handlers ...Handler) Router {
   751  	return app.Add(MethodConnect, path, handlers...)
   752  }
   753  
   754  // Options registers a route for OPTIONS methods that is used to describe the
   755  // communication options for the target resource.
   756  func (app *App) Options(path string, handlers ...Handler) Router {
   757  	return app.Add(MethodOptions, path, handlers...)
   758  }
   759  
   760  // Trace registers a route for TRACE methods that performs a message loop-back
   761  // test along the path to the target resource.
   762  func (app *App) Trace(path string, handlers ...Handler) Router {
   763  	return app.Add(MethodTrace, path, handlers...)
   764  }
   765  
   766  // Patch registers a route for PATCH methods that is used to apply partial
   767  // modifications to a resource.
   768  func (app *App) Patch(path string, handlers ...Handler) Router {
   769  	return app.Add(MethodPatch, path, handlers...)
   770  }
   771  
   772  // Add allows you to specify a HTTP method to register a route
   773  func (app *App) Add(method, path string, handlers ...Handler) Router {
   774  	return app.register(method, path, nil, handlers...)
   775  }
   776  
   777  // Static will create a file server serving static files
   778  func (app *App) Static(prefix, root string, config ...Static) Router {
   779  	return app.registerStatic(prefix, root, config...)
   780  }
   781  
   782  // All will register the handler on all HTTP methods
   783  func (app *App) All(path string, handlers ...Handler) Router {
   784  	for _, method := range app.config.RequestMethods {
   785  		_ = app.Add(method, path, handlers...)
   786  	}
   787  	return app
   788  }
   789  
   790  // Group is used for Routes with common prefix to define a new sub-router with optional middleware.
   791  //
   792  //	api := app.Group("/api")
   793  //	api.Get("/users", handler)
   794  func (app *App) Group(prefix string, handlers ...Handler) Router {
   795  	grp := &Group{Prefix: prefix, app: app}
   796  	if len(handlers) > 0 {
   797  		app.register(methodUse, prefix, grp, handlers...)
   798  	}
   799  	if err := app.hooks.executeOnGroupHooks(*grp); err != nil {
   800  		panic(err)
   801  	}
   802  
   803  	return grp
   804  }
   805  
   806  // Route is used to define routes with a common prefix inside the common function.
   807  // Uses Group method to define new sub-router.
   808  func (app *App) Route(prefix string, fn func(router Router), name ...string) Router {
   809  	// Create new group
   810  	group := app.Group(prefix)
   811  	if len(name) > 0 {
   812  		group.Name(name[0])
   813  	}
   814  
   815  	// Define routes
   816  	fn(group)
   817  
   818  	return group
   819  }
   820  
   821  // Error makes it compatible with the `error` interface.
   822  func (e *Error) Error() string {
   823  	return e.Message
   824  }
   825  
   826  // NewError creates a new Error instance with an optional message
   827  func NewError(code int, message ...string) *Error {
   828  	err := &Error{
   829  		Code:    code,
   830  		Message: utils.StatusMessage(code),
   831  	}
   832  	if len(message) > 0 {
   833  		err.Message = message[0]
   834  	}
   835  	return err
   836  }
   837  
   838  // Config returns the app config as value ( read-only ).
   839  func (app *App) Config() Config {
   840  	return app.config
   841  }
   842  
   843  // Handler returns the server handler.
   844  func (app *App) Handler() fasthttp.RequestHandler { //revive:disable-line:confusing-naming // Having both a Handler() (uppercase) and a handler() (lowercase) is fine. TODO: Use nolint:revive directive instead. See https://github.com/golangci/golangci-lint/issues/3476
   845  	// prepare the server for the start
   846  	app.startupProcess()
   847  	return app.handler
   848  }
   849  
   850  // Stack returns the raw router stack.
   851  func (app *App) Stack() [][]*Route {
   852  	return app.stack
   853  }
   854  
   855  // HandlersCount returns the amount of registered handlers.
   856  func (app *App) HandlersCount() uint32 {
   857  	return app.handlersCount
   858  }
   859  
   860  // Shutdown gracefully shuts down the server without interrupting any active connections.
   861  // Shutdown works by first closing all open listeners and then waiting indefinitely for all connections to return to idle before shutting down.
   862  //
   863  // Make sure the program doesn't exit and waits instead for Shutdown to return.
   864  //
   865  // Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
   866  func (app *App) Shutdown() error {
   867  	return app.ShutdownWithContext(context.Background())
   868  }
   869  
   870  // ShutdownWithTimeout gracefully shuts down the server without interrupting any active connections. However, if the timeout is exceeded,
   871  // ShutdownWithTimeout will forcefully close any active connections.
   872  // ShutdownWithTimeout works by first closing all open listeners and then waiting for all connections to return to idle before shutting down.
   873  //
   874  // Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return.
   875  //
   876  // ShutdownWithTimeout does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
   877  func (app *App) ShutdownWithTimeout(timeout time.Duration) error {
   878  	ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
   879  	defer cancelFunc()
   880  	return app.ShutdownWithContext(ctx)
   881  }
   882  
   883  // ShutdownWithContext shuts down the server including by force if the context's deadline is exceeded.
   884  //
   885  // Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return.
   886  //
   887  // ShutdownWithContext does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
   888  func (app *App) ShutdownWithContext(ctx context.Context) error {
   889  	if app.hooks != nil {
   890  		defer app.hooks.executeOnShutdownHooks()
   891  	}
   892  
   893  	app.mutex.Lock()
   894  	defer app.mutex.Unlock()
   895  	if app.server == nil {
   896  		return fmt.Errorf("shutdown: server is not running")
   897  	}
   898  	return app.server.ShutdownWithContext(ctx)
   899  }
   900  
   901  // Server returns the underlying fasthttp server
   902  func (app *App) Server() *fasthttp.Server {
   903  	return app.server
   904  }
   905  
   906  // Hooks returns the hook struct to register hooks.
   907  func (app *App) Hooks() *Hooks {
   908  	return app.hooks
   909  }
   910  
   911  // Test is used for internal debugging by passing a *http.Request.
   912  // Timeout is optional and defaults to 1s, -1 will disable it completely.
   913  func (app *App) Test(req *http.Request, msTimeout ...int) (*http.Response, error) {
   914  	// Set timeout
   915  	timeout := 1000
   916  	if len(msTimeout) > 0 {
   917  		timeout = msTimeout[0]
   918  	}
   919  
   920  	// Add Content-Length if not provided with body
   921  	if req.Body != http.NoBody && req.Header.Get(HeaderContentLength) == "" {
   922  		req.Header.Add(HeaderContentLength, strconv.FormatInt(req.ContentLength, 10))
   923  	}
   924  
   925  	// Dump raw http request
   926  	dump, err := httputil.DumpRequest(req, true)
   927  	if err != nil {
   928  		return nil, fmt.Errorf("failed to dump request: %w", err)
   929  	}
   930  
   931  	// Create test connection
   932  	conn := new(testConn)
   933  
   934  	// Write raw http request
   935  	if _, err := conn.r.Write(dump); err != nil {
   936  		return nil, fmt.Errorf("failed to write: %w", err)
   937  	}
   938  	// prepare the server for the start
   939  	app.startupProcess()
   940  
   941  	// Serve conn to server
   942  	channel := make(chan error)
   943  	go func() {
   944  		var returned bool
   945  		defer func() {
   946  			if !returned {
   947  				channel <- fmt.Errorf("runtime.Goexit() called in handler or server panic")
   948  			}
   949  		}()
   950  
   951  		channel <- app.server.ServeConn(conn)
   952  		returned = true
   953  	}()
   954  
   955  	// Wait for callback
   956  	if timeout >= 0 {
   957  		// With timeout
   958  		select {
   959  		case err = <-channel:
   960  		case <-time.After(time.Duration(timeout) * time.Millisecond):
   961  			return nil, fmt.Errorf("test: timeout error %vms", timeout)
   962  		}
   963  	} else {
   964  		// Without timeout
   965  		err = <-channel
   966  	}
   967  
   968  	// Check for errors
   969  	if err != nil && !errors.Is(err, fasthttp.ErrGetOnly) {
   970  		return nil, err
   971  	}
   972  
   973  	// Read response
   974  	buffer := bufio.NewReader(&conn.w)
   975  
   976  	// Convert raw http response to *http.Response
   977  	res, err := http.ReadResponse(buffer, req)
   978  	if err != nil {
   979  		return nil, fmt.Errorf("failed to read response: %w", err)
   980  	}
   981  
   982  	return res, nil
   983  }
   984  
   985  type disableLogger struct{}
   986  
   987  func (*disableLogger) Printf(_ string, _ ...interface{}) {
   988  	// fmt.Println(fmt.Sprintf(format, args...))
   989  }
   990  
   991  func (app *App) init() *App {
   992  	// lock application
   993  	app.mutex.Lock()
   994  
   995  	// Only load templates if a view engine is specified
   996  	if app.config.Views != nil {
   997  		if err := app.config.Views.Load(); err != nil {
   998  			log.Printf("[Warning]: failed to load views: %v\n", err)
   999  		}
  1000  	}
  1001  
  1002  	// create fasthttp server
  1003  	app.server = &fasthttp.Server{
  1004  		Logger:       &disableLogger{},
  1005  		LogAllErrors: false,
  1006  		ErrorHandler: app.serverErrorHandler,
  1007  	}
  1008  
  1009  	// fasthttp server settings
  1010  	app.server.Handler = app.handler
  1011  	app.server.Name = app.config.ServerHeader
  1012  	app.server.Concurrency = app.config.Concurrency
  1013  	app.server.NoDefaultDate = app.config.DisableDefaultDate
  1014  	app.server.NoDefaultContentType = app.config.DisableDefaultContentType
  1015  	app.server.DisableHeaderNamesNormalizing = app.config.DisableHeaderNormalizing
  1016  	app.server.DisableKeepalive = app.config.DisableKeepalive
  1017  	app.server.MaxRequestBodySize = app.config.BodyLimit
  1018  	app.server.NoDefaultServerHeader = app.config.ServerHeader == ""
  1019  	app.server.ReadTimeout = app.config.ReadTimeout
  1020  	app.server.WriteTimeout = app.config.WriteTimeout
  1021  	app.server.IdleTimeout = app.config.IdleTimeout
  1022  	app.server.ReadBufferSize = app.config.ReadBufferSize
  1023  	app.server.WriteBufferSize = app.config.WriteBufferSize
  1024  	app.server.GetOnly = app.config.GETOnly
  1025  	app.server.ReduceMemoryUsage = app.config.ReduceMemoryUsage
  1026  	app.server.StreamRequestBody = app.config.StreamRequestBody
  1027  	app.server.DisablePreParseMultipartForm = app.config.DisablePreParseMultipartForm
  1028  
  1029  	// unlock application
  1030  	app.mutex.Unlock()
  1031  	return app
  1032  }
  1033  
  1034  // ErrorHandler is the application's method in charge of finding the
  1035  // appropriate handler for the given request. It searches any mounted
  1036  // sub fibers by their prefixes and if it finds a match, it uses that
  1037  // error handler. Otherwise it uses the configured error handler for
  1038  // the app, which if not set is the DefaultErrorHandler.
  1039  func (app *App) ErrorHandler(ctx *Ctx, err error) error {
  1040  	var (
  1041  		mountedErrHandler  ErrorHandler
  1042  		mountedPrefixParts int
  1043  	)
  1044  
  1045  	for prefix, subApp := range app.mountFields.appList {
  1046  		if prefix != "" && strings.HasPrefix(ctx.path, prefix) {
  1047  			parts := len(strings.Split(prefix, "/"))
  1048  			if mountedPrefixParts <= parts {
  1049  				if subApp.configured.ErrorHandler != nil {
  1050  					mountedErrHandler = subApp.config.ErrorHandler
  1051  				}
  1052  
  1053  				mountedPrefixParts = parts
  1054  			}
  1055  		}
  1056  	}
  1057  
  1058  	if mountedErrHandler != nil {
  1059  		return mountedErrHandler(ctx, err)
  1060  	}
  1061  
  1062  	return app.config.ErrorHandler(ctx, err)
  1063  }
  1064  
  1065  // serverErrorHandler is a wrapper around the application's error handler method
  1066  // user for the fasthttp server configuration. It maps a set of fasthttp errors to fiber
  1067  // errors before calling the application's error handler method.
  1068  func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) {
  1069  	c := app.AcquireCtx(fctx)
  1070  	defer app.ReleaseCtx(c)
  1071  
  1072  	var (
  1073  		errNetOP *net.OpError
  1074  		netErr   net.Error
  1075  	)
  1076  
  1077  	switch {
  1078  	case errors.As(err, new(*fasthttp.ErrSmallBuffer)):
  1079  		err = ErrRequestHeaderFieldsTooLarge
  1080  	case errors.As(err, &errNetOP) && errNetOP.Timeout():
  1081  		err = ErrRequestTimeout
  1082  	case errors.As(err, &netErr):
  1083  		err = ErrBadGateway
  1084  	case errors.Is(err, fasthttp.ErrBodyTooLarge):
  1085  		err = ErrRequestEntityTooLarge
  1086  	case errors.Is(err, fasthttp.ErrGetOnly):
  1087  		err = ErrMethodNotAllowed
  1088  	case strings.Contains(err.Error(), "timeout"):
  1089  		err = ErrRequestTimeout
  1090  	default:
  1091  		err = NewError(StatusBadRequest, err.Error())
  1092  	}
  1093  
  1094  	if catch := app.ErrorHandler(c, err); catch != nil {
  1095  		log.Printf("serverErrorHandler: failed to call ErrorHandler: %v\n", catch)
  1096  		_ = c.SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here
  1097  		return
  1098  	}
  1099  }
  1100  
  1101  // startupProcess Is the method which executes all the necessary processes just before the start of the server.
  1102  func (app *App) startupProcess() *App {
  1103  	if err := app.hooks.executeOnListenHooks(); err != nil {
  1104  		panic(err)
  1105  	}
  1106  
  1107  	app.mutex.Lock()
  1108  	defer app.mutex.Unlock()
  1109  
  1110  	app.mountStartupProcess()
  1111  
  1112  	// build route tree stack
  1113  	app.buildTree()
  1114  
  1115  	return app
  1116  }