mirror of https://github.com/gofiber/fiber.git
Merge 1070d75d31
into 75281bd874
commit
3a86aaf4f6
198
app.go
198
app.go
|
@ -35,7 +35,7 @@ import (
|
|||
const Version = "3.0.0-beta.4"
|
||||
|
||||
// Handler defines a function to serve HTTP requests.
|
||||
type Handler = func(Ctx) error
|
||||
type Handler[TCtx CtxGeneric[TCtx]] = func(ctx TCtx) error
|
||||
|
||||
// Map is a shortcut for map[string]any, useful for JSON returns
|
||||
type Map map[string]any
|
||||
|
@ -78,7 +78,7 @@ type Storage interface {
|
|||
// return c.Status(code).SendString(err.Error())
|
||||
// }
|
||||
// app := fiber.New(cfg)
|
||||
type ErrorHandler = func(Ctx, error) error
|
||||
type ErrorHandler[TCtx CtxGeneric[TCtx]] = func(TCtx, error) error
|
||||
|
||||
// Error represents an error that occurred while handling a request.
|
||||
type Error struct {
|
||||
|
@ -87,7 +87,7 @@ type Error struct {
|
|||
}
|
||||
|
||||
// App denotes the Fiber application.
|
||||
type App struct {
|
||||
type App[TCtx CtxGeneric[TCtx]] struct {
|
||||
// Ctx pool
|
||||
pool sync.Pool
|
||||
// Fasthttp server
|
||||
|
@ -97,19 +97,19 @@ type App struct {
|
|||
// Converts byte slice to a string
|
||||
getString func(b []byte) string
|
||||
// Hooks
|
||||
hooks *Hooks
|
||||
hooks *Hooks[TCtx]
|
||||
// Latest route & group
|
||||
latestRoute *Route
|
||||
latestRoute *Route[TCtx]
|
||||
// newCtxFunc
|
||||
newCtxFunc func(app *App) CustomCtx
|
||||
newCtxFunc func(app *App[TCtx]) CustomCtx[TCtx]
|
||||
// TLS handler
|
||||
tlsHandler *TLSHandler
|
||||
// Mount fields
|
||||
mountFields *mountFields
|
||||
mountFields *mountFields[TCtx]
|
||||
// Route stack divided by HTTP methods
|
||||
stack [][]*Route
|
||||
stack [][]*Route[TCtx]
|
||||
// Route stack divided by HTTP methods and route prefixes
|
||||
treeStack []map[int][]*Route
|
||||
treeStack []map[int][]*Route[TCtx]
|
||||
// custom binders
|
||||
customBinders []CustomBinder
|
||||
// customConstraints is a list of external constraints
|
||||
|
@ -117,9 +117,9 @@ type App struct {
|
|||
// sendfiles stores configurations for handling ctx.SendFile operations
|
||||
sendfiles []*sendFileStore
|
||||
// App config
|
||||
config Config
|
||||
config Config[TCtx]
|
||||
// Indicates if the value was explicitly configured
|
||||
configured Config
|
||||
configured Config[TCtx]
|
||||
// sendfilesMutex is a mutex used for sendfile operations
|
||||
sendfilesMutex sync.RWMutex
|
||||
mutex sync.Mutex
|
||||
|
@ -132,7 +132,7 @@ type App struct {
|
|||
}
|
||||
|
||||
// Config is a struct holding the server settings.
|
||||
type Config struct { //nolint:govet // Aligning the struct fields is not necessary. betteralign:ignore
|
||||
type Config[TCtx CtxGeneric[TCtx]] struct { //nolint:govet // Aligning the struct fields is not necessary. betteralign:ignore
|
||||
// Enables the "Server: value" HTTP header.
|
||||
//
|
||||
// Default: ""
|
||||
|
@ -250,7 +250,7 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
|
|||
// ErrorHandler is executed when an error is returned from fiber.Handler.
|
||||
//
|
||||
// Default: DefaultErrorHandler
|
||||
ErrorHandler ErrorHandler `json:"-"`
|
||||
ErrorHandler ErrorHandler[TCtx] `json:"-"`
|
||||
|
||||
// When set to true, disables keep-alive connections.
|
||||
// The server will close incoming connections after sending the first response to client.
|
||||
|
@ -482,7 +482,7 @@ var DefaultMethods = []string{
|
|||
}
|
||||
|
||||
// DefaultErrorHandler that process return errors from handlers
|
||||
func DefaultErrorHandler(c Ctx, err error) error {
|
||||
func DefaultErrorHandler[TCtx CtxGeneric[TCtx]](c TCtx, err error) error {
|
||||
code := StatusInternalServerError
|
||||
var e *Error
|
||||
if errors.As(err, &e) {
|
||||
|
@ -502,14 +502,54 @@ func DefaultErrorHandler(c Ctx, err error) error {
|
|||
// Prefork: true,
|
||||
// ServerHeader: "Fiber",
|
||||
// })
|
||||
func New(config ...Config) *App {
|
||||
func New(config ...Config[*DefaultCtx]) *App[*DefaultCtx] {
|
||||
app := newApp[*DefaultCtx](config...)
|
||||
|
||||
// Init app
|
||||
app.init()
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// NewWithCustomCtx creates a new Fiber named instance with a custom context.
|
||||
//
|
||||
// app := fiber.NewWithCustomCtx(func(app *fiber.App) fiber.CustomCtx[MyCustomCtx] {
|
||||
// return &MyCustomCtx{
|
||||
// DefaultCtx: *fiber.NewDefaultCtx(app),
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// You can pass optional configuration options by passing a Config struct:
|
||||
//
|
||||
// app := fiber.NewWithCustomCtx(func(app *fiber.App) fiber.CustomCtx[MyCustomCtx] {
|
||||
// return &MyCustomCtx{
|
||||
// DefaultCtx: *fiber.NewDefaultCtx(app),
|
||||
// }
|
||||
// }, fiber.Config{
|
||||
// Prefork: true,
|
||||
// ServerHeader: "Fiber",
|
||||
// })
|
||||
func NewWithCustomCtx[TCtx CtxGeneric[TCtx]](newCtxFunc func(app *App[TCtx]) CustomCtx[TCtx], config ...Config[TCtx]) *App[TCtx] {
|
||||
app := newApp[TCtx](config...)
|
||||
|
||||
// Set newCtxFunc
|
||||
app.newCtxFunc = newCtxFunc
|
||||
|
||||
// Init app
|
||||
app.init()
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// newApp creates a new Fiber named instance.
|
||||
func newApp[TCtx CtxGeneric[TCtx]](config ...Config[TCtx]) *App[TCtx] {
|
||||
// Create a new app
|
||||
app := &App{
|
||||
app := &App[TCtx]{
|
||||
// Create config
|
||||
config: Config{},
|
||||
config: Config[TCtx]{},
|
||||
getBytes: utils.UnsafeBytes,
|
||||
getString: utils.UnsafeString,
|
||||
latestRoute: &Route{},
|
||||
latestRoute: &Route[TCtx]{},
|
||||
customBinders: []CustomBinder{},
|
||||
sendfiles: []*sendFileStore{},
|
||||
}
|
||||
|
@ -522,7 +562,7 @@ func New(config ...Config) *App {
|
|||
}
|
||||
|
||||
// Define hooks
|
||||
app.hooks = newHooks(app)
|
||||
app.hooks = newHooks[TCtx](app)
|
||||
|
||||
// Define mountFields
|
||||
app.mountFields = newMountFields(app)
|
||||
|
@ -561,7 +601,7 @@ func New(config ...Config) *App {
|
|||
}
|
||||
|
||||
if app.config.ErrorHandler == nil {
|
||||
app.config.ErrorHandler = DefaultErrorHandler
|
||||
app.config.ErrorHandler = DefaultErrorHandler[TCtx]
|
||||
}
|
||||
|
||||
if app.config.JSONEncoder == nil {
|
||||
|
@ -592,21 +632,18 @@ func New(config ...Config) *App {
|
|||
}
|
||||
|
||||
// Create router stack
|
||||
app.stack = make([][]*Route, len(app.config.RequestMethods))
|
||||
app.treeStack = make([]map[int][]*Route, len(app.config.RequestMethods))
|
||||
app.stack = make([][]*Route[TCtx], len(app.config.RequestMethods))
|
||||
app.treeStack = make([]map[int][]*Route[TCtx], len(app.config.RequestMethods))
|
||||
|
||||
// Override colors
|
||||
app.config.ColorScheme = defaultColors(app.config.ColorScheme)
|
||||
|
||||
// Init app
|
||||
app.init()
|
||||
|
||||
// Return app
|
||||
return app
|
||||
}
|
||||
|
||||
// Adds an ip address to TrustProxyConfig.ranges or TrustProxyConfig.ips based on whether it is an IP range or not
|
||||
func (app *App) handleTrustedProxy(ipAddress string) {
|
||||
func (app *App[TCtx]) handleTrustedProxy(ipAddress string) {
|
||||
if strings.Contains(ipAddress, "/") {
|
||||
_, ipNet, err := net.ParseCIDR(ipAddress)
|
||||
if err != nil {
|
||||
|
@ -624,29 +661,19 @@ func (app *App) handleTrustedProxy(ipAddress string) {
|
|||
}
|
||||
}
|
||||
|
||||
// NewCtxFunc allows to customize ctx methods as we want.
|
||||
// Note: It doesn't allow adding new methods, only customizing exist methods.
|
||||
func (app *App) NewCtxFunc(function func(app *App) CustomCtx) {
|
||||
app.newCtxFunc = function
|
||||
|
||||
if app.server != nil {
|
||||
app.server.Handler = app.customRequestHandler
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterCustomConstraint allows to register custom constraint.
|
||||
func (app *App) RegisterCustomConstraint(constraint CustomConstraint) {
|
||||
func (app *App[TCtx]) RegisterCustomConstraint(constraint CustomConstraint) {
|
||||
app.customConstraints = append(app.customConstraints, constraint)
|
||||
}
|
||||
|
||||
// RegisterCustomBinder Allows to register custom binders to use as Bind().Custom("name").
|
||||
// They should be compatible with CustomBinder interface.
|
||||
func (app *App) RegisterCustomBinder(binder CustomBinder) {
|
||||
func (app *App[TCtx]) RegisterCustomBinder(binder CustomBinder) {
|
||||
app.customBinders = append(app.customBinders, binder)
|
||||
}
|
||||
|
||||
// SetTLSHandler Can be used to set ClientHelloInfo when using TLS with Listener.
|
||||
func (app *App) SetTLSHandler(tlsHandler *TLSHandler) {
|
||||
func (app *App[TCtx]) SetTLSHandler(tlsHandler *TLSHandler) {
|
||||
// Attach the tlsHandler to the config
|
||||
app.mutex.Lock()
|
||||
app.tlsHandler = tlsHandler
|
||||
|
@ -654,7 +681,7 @@ func (app *App) SetTLSHandler(tlsHandler *TLSHandler) {
|
|||
}
|
||||
|
||||
// Name Assign name to specific route.
|
||||
func (app *App) Name(name string) Router {
|
||||
func (app *App[TCtx]) Name(name string) Router[TCtx] {
|
||||
app.mutex.Lock()
|
||||
defer app.mutex.Unlock()
|
||||
|
||||
|
@ -680,7 +707,7 @@ func (app *App) Name(name string) Router {
|
|||
}
|
||||
|
||||
// GetRoute Get route by name
|
||||
func (app *App) GetRoute(name string) Route {
|
||||
func (app *App[TCtx]) GetRoute(name string) Route[TCtx] {
|
||||
for _, routes := range app.stack {
|
||||
for _, route := range routes {
|
||||
if route.Name == name {
|
||||
|
@ -689,12 +716,12 @@ func (app *App) GetRoute(name string) Route {
|
|||
}
|
||||
}
|
||||
|
||||
return Route{}
|
||||
return Route[TCtx]{}
|
||||
}
|
||||
|
||||
// GetRoutes Get all routes. When filterUseOption equal to true, it will filter the routes registered by the middleware.
|
||||
func (app *App) GetRoutes(filterUseOption ...bool) []Route {
|
||||
var rs []Route
|
||||
func (app *App[TCtx]) GetRoutes(filterUseOption ...bool) []Route[TCtx] {
|
||||
var rs []Route[TCtx]
|
||||
var filterUse bool
|
||||
if len(filterUseOption) != 0 {
|
||||
filterUse = filterUseOption[0]
|
||||
|
@ -731,21 +758,21 @@ func (app *App) GetRoutes(filterUseOption ...bool) []Route {
|
|||
// app.Use("/mounted-path", subApp)
|
||||
//
|
||||
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
|
||||
func (app *App) Use(args ...any) Router {
|
||||
func (app *App[TCtx]) Use(args ...any) Router[TCtx] {
|
||||
var prefix string
|
||||
var subApp *App
|
||||
var subApp *App[TCtx]
|
||||
var prefixes []string
|
||||
var handlers []Handler
|
||||
var handlers []Handler[TCtx]
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch arg := args[i].(type) {
|
||||
case string:
|
||||
prefix = arg
|
||||
case *App:
|
||||
case *App[TCtx]:
|
||||
subApp = arg
|
||||
case []string:
|
||||
prefixes = arg
|
||||
case Handler:
|
||||
case Handler[TCtx]:
|
||||
handlers = append(handlers, arg)
|
||||
default:
|
||||
panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
|
||||
|
@ -770,66 +797,66 @@ func (app *App) Use(args ...any) Router {
|
|||
|
||||
// Get registers a route for GET methods that requests a representation
|
||||
// of the specified resource. Requests using GET should only retrieve data.
|
||||
func (app *App) Get(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Get(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodGet}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Head registers a route for HEAD methods that asks for a response identical
|
||||
// to that of a GET request, but without the response body.
|
||||
func (app *App) Head(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Head(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodHead}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Post registers a route for POST methods that is used to submit an entity to the
|
||||
// specified resource, often causing a change in state or side effects on the server.
|
||||
func (app *App) Post(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Post(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodPost}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Put registers a route for PUT methods that replaces all current representations
|
||||
// of the target resource with the request payload.
|
||||
func (app *App) Put(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Put(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodPut}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Delete registers a route for DELETE methods that deletes the specified resource.
|
||||
func (app *App) Delete(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Delete(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodDelete}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Connect registers a route for CONNECT methods that establishes a tunnel to the
|
||||
// server identified by the target resource.
|
||||
func (app *App) Connect(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Connect(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodConnect}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Options registers a route for OPTIONS methods that is used to describe the
|
||||
// communication options for the target resource.
|
||||
func (app *App) Options(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Options(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodOptions}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Trace registers a route for TRACE methods that performs a message loop-back
|
||||
// test along the path to the target resource.
|
||||
func (app *App) Trace(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Trace(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodTrace}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Patch registers a route for PATCH methods that is used to apply partial
|
||||
// modifications to a resource.
|
||||
func (app *App) Patch(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Patch(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add([]string{MethodPatch}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Add allows you to specify multiple HTTP methods to register a route.
|
||||
func (app *App) Add(methods []string, path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) Add(methods []string, path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
app.register(methods, path, nil, append([]Handler{handler}, handlers...)...)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// All will register the handler on all HTTP methods
|
||||
func (app *App) All(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (app *App[TCtx]) All(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return app.Add(app.config.RequestMethods, path, handler, handlers...)
|
||||
}
|
||||
|
||||
|
@ -837,8 +864,8 @@ func (app *App) All(path string, handler Handler, handlers ...Handler) Router {
|
|||
//
|
||||
// api := app.Group("/api")
|
||||
// api.Get("/users", handler)
|
||||
func (app *App) Group(prefix string, handlers ...Handler) Router {
|
||||
grp := &Group{Prefix: prefix, app: app}
|
||||
func (app *App[TCtx]) Group(prefix string, handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
grp := &Group[TCtx]{Prefix: prefix, app: app}
|
||||
if len(handlers) > 0 {
|
||||
app.register([]string{methodUse}, prefix, grp, handlers...)
|
||||
}
|
||||
|
@ -851,9 +878,9 @@ func (app *App) Group(prefix string, handlers ...Handler) Router {
|
|||
|
||||
// Route is used to define routes with a common prefix inside the common function.
|
||||
// Uses Group method to define new sub-router.
|
||||
func (app *App) Route(path string) Register {
|
||||
func (app *App[TCtx]) Route(path string) Register[TCtx] {
|
||||
// Create new route
|
||||
route := &Registering{app: app, path: path}
|
||||
route := &Registering[TCtx]{app: app, path: path}
|
||||
|
||||
return route
|
||||
}
|
||||
|
@ -876,28 +903,25 @@ func NewError(code int, message ...string) *Error {
|
|||
}
|
||||
|
||||
// Config returns the app config as value ( read-only ).
|
||||
func (app *App) Config() Config {
|
||||
func (app *App[TCtx]) Config() Config[TCtx] {
|
||||
return app.config
|
||||
}
|
||||
|
||||
// Handler returns the server handler.
|
||||
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
|
||||
func (app *App[TCtx]) 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
|
||||
// prepare the server for the start
|
||||
app.startupProcess()
|
||||
|
||||
if app.newCtxFunc != nil {
|
||||
return app.customRequestHandler
|
||||
}
|
||||
return app.defaultRequestHandler
|
||||
return app.requestHandler
|
||||
}
|
||||
|
||||
// Stack returns the raw router stack.
|
||||
func (app *App) Stack() [][]*Route {
|
||||
func (app *App[TCtx]) Stack() [][]*Route[TCtx] {
|
||||
return app.stack
|
||||
}
|
||||
|
||||
// HandlersCount returns the amount of registered handlers.
|
||||
func (app *App) HandlersCount() uint32 {
|
||||
func (app *App[TCtx]) HandlersCount() uint32 {
|
||||
return app.handlersCount
|
||||
}
|
||||
|
||||
|
@ -914,7 +938,7 @@ func (app *App) HandlersCount() uint32 {
|
|||
// app.Shutdown()
|
||||
//
|
||||
// Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
|
||||
func (app *App) Shutdown() error {
|
||||
func (app *App[TCtx]) Shutdown() error {
|
||||
return app.ShutdownWithContext(context.Background())
|
||||
}
|
||||
|
||||
|
@ -925,7 +949,7 @@ func (app *App) Shutdown() error {
|
|||
// Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return.
|
||||
//
|
||||
// ShutdownWithTimeout does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
|
||||
func (app *App) ShutdownWithTimeout(timeout time.Duration) error {
|
||||
func (app *App[TCtx]) ShutdownWithTimeout(timeout time.Duration) error {
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancelFunc()
|
||||
return app.ShutdownWithContext(ctx)
|
||||
|
@ -936,7 +960,7 @@ func (app *App) ShutdownWithTimeout(timeout time.Duration) error {
|
|||
// Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return.
|
||||
//
|
||||
// ShutdownWithContext does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
|
||||
func (app *App) ShutdownWithContext(ctx context.Context) error {
|
||||
func (app *App[TCtx]) ShutdownWithContext(ctx context.Context) error {
|
||||
app.mutex.Lock()
|
||||
defer app.mutex.Unlock()
|
||||
|
||||
|
@ -955,12 +979,12 @@ func (app *App) ShutdownWithContext(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Server returns the underlying fasthttp server
|
||||
func (app *App) Server() *fasthttp.Server {
|
||||
func (app *App[TCtx]) Server() *fasthttp.Server {
|
||||
return app.server
|
||||
}
|
||||
|
||||
// Hooks returns the hook struct to register hooks.
|
||||
func (app *App) Hooks() *Hooks {
|
||||
func (app *App[TCtx]) Hooks() *Hooks[TCtx] {
|
||||
return app.hooks
|
||||
}
|
||||
|
||||
|
@ -983,7 +1007,7 @@ type TestConfig struct {
|
|||
// Test is used for internal debugging by passing a *http.Request.
|
||||
// Config is optional and defaults to a 1s error on timeout,
|
||||
// 0 timeout will disable it completely.
|
||||
func (app *App) Test(req *http.Request, config ...TestConfig) (*http.Response, error) {
|
||||
func (app *App[TCtx]) Test(req *http.Request, config ...TestConfig) (*http.Response, error) {
|
||||
// Default config
|
||||
cfg := TestConfig{
|
||||
Timeout: time.Second,
|
||||
|
@ -1071,7 +1095,7 @@ type disableLogger struct{}
|
|||
func (*disableLogger) Printf(string, ...any) {
|
||||
}
|
||||
|
||||
func (app *App) init() *App {
|
||||
func (app *App[TCtx]) init() *App[TCtx] {
|
||||
// lock application
|
||||
app.mutex.Lock()
|
||||
|
||||
|
@ -1090,11 +1114,7 @@ func (app *App) init() *App {
|
|||
}
|
||||
|
||||
// fasthttp server settings
|
||||
if app.newCtxFunc != nil {
|
||||
app.server.Handler = app.customRequestHandler
|
||||
} else {
|
||||
app.server.Handler = app.defaultRequestHandler
|
||||
}
|
||||
app.server.Handler = app.requestHandler
|
||||
app.server.Name = app.config.ServerHeader
|
||||
app.server.Concurrency = app.config.Concurrency
|
||||
app.server.NoDefaultDate = app.config.DisableDefaultDate
|
||||
|
@ -1123,9 +1143,9 @@ func (app *App) init() *App {
|
|||
// sub fibers by their prefixes and if it finds a match, it uses that
|
||||
// error handler. Otherwise it uses the configured error handler for
|
||||
// the app, which if not set is the DefaultErrorHandler.
|
||||
func (app *App) ErrorHandler(ctx Ctx, err error) error {
|
||||
func (app *App[TCtx]) ErrorHandler(ctx TCtx, err error) error {
|
||||
var (
|
||||
mountedErrHandler ErrorHandler
|
||||
mountedErrHandler ErrorHandler[TCtx]
|
||||
mountedPrefixParts int
|
||||
)
|
||||
|
||||
|
@ -1152,7 +1172,7 @@ func (app *App) ErrorHandler(ctx Ctx, err error) error {
|
|||
// serverErrorHandler is a wrapper around the application's error handler method
|
||||
// user for the fasthttp server configuration. It maps a set of fasthttp errors to fiber
|
||||
// errors before calling the application's error handler method.
|
||||
func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) {
|
||||
func (app *App[TCtx]) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) {
|
||||
// Acquire Ctx with fasthttp request from pool
|
||||
c := app.AcquireCtx(fctx)
|
||||
defer app.ReleaseCtx(c)
|
||||
|
@ -1187,7 +1207,7 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) {
|
|||
}
|
||||
|
||||
// startupProcess Is the method which executes all the necessary processes just before the start of the server.
|
||||
func (app *App) startupProcess() *App {
|
||||
func (app *App[TCtx]) startupProcess() *App[TCtx] {
|
||||
app.mutex.Lock()
|
||||
defer app.mutex.Unlock()
|
||||
|
||||
|
@ -1200,7 +1220,7 @@ func (app *App) startupProcess() *App {
|
|||
}
|
||||
|
||||
// Run onListen hooks. If they return an error, panic.
|
||||
func (app *App) runOnListenHooks(listenData ListenData) {
|
||||
func (app *App[TCtx]) runOnListenHooks(listenData ListenData) {
|
||||
if err := app.hooks.executeOnListenHooks(listenData); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
19
ctx.go
19
ctx.go
|
@ -50,10 +50,11 @@ const userContextKey contextKey = 0 // __local_user_context__
|
|||
// generation tool `go install github.com/vburenin/ifacemaker@975a95966976eeb2d4365a7fb236e274c54da64c`
|
||||
// https://github.com/vburenin/ifacemaker/blob/975a95966976eeb2d4365a7fb236e274c54da64c/ifacemaker.go#L14-L30
|
||||
//
|
||||
//go:generate ifacemaker --file ctx.go --struct DefaultCtx --iface Ctx --pkg fiber --output ctx_interface_gen.go --not-exported true --iface-comment "Ctx represents the Context which hold the HTTP request and response.\nIt has methods for the request query string, parameters, body, HTTP headers and so on."
|
||||
//go:generate ifacemaker --file ctx.go --struct DefaultCtx --iface CtxGeneric --pkg fiber --output ctx_interface.go --not-exported true --iface-comment "Ctx represents the Context which hold the HTTP request and response.\nIt has methods for the request query string, parameters, body, HTTP headers and so on."
|
||||
//go:generate go run ctx_interface_gen.go
|
||||
type DefaultCtx struct {
|
||||
app *App // Reference to *App
|
||||
route *Route // Reference to *Route
|
||||
app *App[*DefaultCtx] // Reference to *App
|
||||
route *Route[*DefaultCtx] // Reference to *Route
|
||||
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
|
||||
bind *Bind // Default bind reference
|
||||
redirect *Redirect // Default redirect reference
|
||||
|
@ -73,6 +74,8 @@ type DefaultCtx struct {
|
|||
matched bool // Non use route matched
|
||||
}
|
||||
|
||||
type Ctx = CtxGeneric[*DefaultCtx]
|
||||
|
||||
// SendFile defines configuration options when to transfer file with SendFile.
|
||||
type SendFile struct {
|
||||
// FS is the file system to serve the static files from.
|
||||
|
@ -224,7 +227,7 @@ func (c *DefaultCtx) AcceptsLanguages(offers ...string) string {
|
|||
}
|
||||
|
||||
// App returns the *App reference to the instance of the Fiber application
|
||||
func (c *DefaultCtx) App() *App {
|
||||
func (c *DefaultCtx) App() *App[*DefaultCtx] {
|
||||
return c.app
|
||||
}
|
||||
|
||||
|
@ -1045,6 +1048,7 @@ func (c *DefaultCtx) Next() error {
|
|||
}
|
||||
|
||||
// Continue handler stack
|
||||
// TODO: reduce this with generics
|
||||
if c.app.newCtxFunc != nil {
|
||||
_, err := c.app.nextCustom(c)
|
||||
return err
|
||||
|
@ -1060,6 +1064,7 @@ func (c *DefaultCtx) RestartRouting() error {
|
|||
var err error
|
||||
|
||||
c.indexRoute = -1
|
||||
// TODO: reduce this with generics
|
||||
if c.app.newCtxFunc != nil {
|
||||
_, err = c.app.nextCustom(c)
|
||||
} else {
|
||||
|
@ -1744,7 +1749,7 @@ func (c *DefaultCtx) Stale() bool {
|
|||
|
||||
// Status sets the HTTP status for the response.
|
||||
// This method is chainable.
|
||||
func (c *DefaultCtx) Status(status int) Ctx {
|
||||
func (c *DefaultCtx) Status(status int) *DefaultCtx {
|
||||
c.fasthttp.Response.SetStatusCode(status)
|
||||
return c
|
||||
}
|
||||
|
@ -1789,7 +1794,7 @@ func (c *DefaultCtx) String() string {
|
|||
}
|
||||
|
||||
// Type sets the Content-Type HTTP header to the MIME type specified by the file extension.
|
||||
func (c *DefaultCtx) Type(extension string, charset ...string) Ctx {
|
||||
func (c *DefaultCtx) Type(extension string, charset ...string) *DefaultCtx {
|
||||
if len(charset) > 0 {
|
||||
c.fasthttp.Response.Header.SetContentType(utils.GetMIME(extension) + "; charset=" + charset[0])
|
||||
} else {
|
||||
|
@ -1987,7 +1992,7 @@ func (c *DefaultCtx) setMatched(matched bool) {
|
|||
c.matched = matched
|
||||
}
|
||||
|
||||
func (c *DefaultCtx) setRoute(route *Route) {
|
||||
func (c *DefaultCtx) setRoute(route *Route[*DefaultCtx]) {
|
||||
c.route = route
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// ⚡️ 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 (
|
||||
"errors"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type CustomCtx[T any] interface {
|
||||
CtxGeneric[T]
|
||||
|
||||
// Reset is a method to reset context fields by given request when to use server handlers.
|
||||
Reset(fctx *fasthttp.RequestCtx)
|
||||
|
||||
// Methods to use with next stack.
|
||||
getMethodINT() int
|
||||
getIndexRoute() int
|
||||
getTreePath() string
|
||||
getDetectionPath() string
|
||||
getPathOriginal() string
|
||||
getValues() *[maxParams]string
|
||||
getMatched() bool
|
||||
setIndexHandler(handler int)
|
||||
setIndexRoute(route int)
|
||||
setMatched(matched bool)
|
||||
setRoute(route *Route[T])
|
||||
}
|
||||
|
||||
func NewDefaultCtx[TCtx *DefaultCtx](app *App[*DefaultCtx]) TCtx {
|
||||
// return ctx
|
||||
ctx := &DefaultCtx{
|
||||
// Set app reference
|
||||
app: app,
|
||||
}
|
||||
ctx.req = &DefaultReq{ctx: ctx}
|
||||
ctx.res = &DefaultRes{ctx: ctx}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (app *App[TCtx]) newCtx() CtxGeneric[TCtx] {
|
||||
var c CtxGeneric[TCtx]
|
||||
|
||||
// TODO: fix this with generics ?
|
||||
if app.newCtxFunc != nil {
|
||||
c = app.newCtxFunc(app)
|
||||
} else {
|
||||
c = NewDefaultCtx(app)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// AcquireCtx retrieves a new Ctx from the pool.
|
||||
func (app *App[TCtx]) AcquireCtx(fctx *fasthttp.RequestCtx) TCtx {
|
||||
ctx, ok := app.pool.Get().(TCtx)
|
||||
|
||||
if !ok {
|
||||
panic(errors.New("failed to type-assert to Ctx"))
|
||||
}
|
||||
ctx.Reset(fctx)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// ReleaseCtx releases the ctx back into the pool.
|
||||
func (app *App[TCtx]) ReleaseCtx(c TCtx) {
|
||||
c.release()
|
||||
app.pool.Put(c)
|
||||
}
|
395
ctx_interface.go
395
ctx_interface.go
|
@ -1,21 +1,350 @@
|
|||
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
||||
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
||||
// 📌 API Documentation: https://docs.gofiber.io
|
||||
// Code generated by ifacemaker; DO NOT EDIT.
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type CustomCtx interface {
|
||||
Ctx
|
||||
|
||||
// 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 CtxGeneric[T any] interface {
|
||||
// Accepts checks if the specified extensions or content types are acceptable.
|
||||
Accepts(offers ...string) string
|
||||
// AcceptsCharsets checks if the specified charset is acceptable.
|
||||
AcceptsCharsets(offers ...string) string
|
||||
// AcceptsEncodings checks if the specified encoding is acceptable.
|
||||
AcceptsEncodings(offers ...string) string
|
||||
// AcceptsLanguages checks if the specified language is acceptable.
|
||||
AcceptsLanguages(offers ...string) string
|
||||
// App returns the *App[T] reference to the instance of the Fiber application
|
||||
App() *App[T]
|
||||
// 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.
|
||||
Append(field string, values ...string)
|
||||
// Attachment sets the HTTP response Content-Disposition header field to attachment.
|
||||
Attachment(filename ...string)
|
||||
// BaseURL returns (protocol + host + base path).
|
||||
BaseURL() string
|
||||
// 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.
|
||||
BodyRaw() []byte
|
||||
tryDecodeBodyInOrder(originalBody *[]byte, encodings []string) ([]byte, uint8, error)
|
||||
// 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.
|
||||
Body() []byte
|
||||
// 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.
|
||||
ClearCookie(key ...string)
|
||||
// RequestCtx returns *fasthttp.RequestCtx that carries a deadline
|
||||
// a cancellation signal, and other values across API boundaries.
|
||||
RequestCtx() *fasthttp.RequestCtx
|
||||
// Context returns a context implementation that was set by
|
||||
// user earlier or returns a non-nil, empty context,if it was not set earlier.
|
||||
Context() context.Context
|
||||
// SetContext sets a context implementation by user.
|
||||
SetContext(ctx context.Context)
|
||||
// Cookie sets a cookie by passing a cookie struct.
|
||||
Cookie(cookie *Cookie)
|
||||
// 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.
|
||||
Cookies(key string, defaultValue ...string) string
|
||||
// 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.
|
||||
Download(file string, filename ...string) error
|
||||
// Request return the *fasthttp.Request object
|
||||
// This allows you to use all fasthttp request methods
|
||||
// https://godoc.org/github.com/valyala/fasthttp#Request
|
||||
Request() *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
|
||||
Response() *fasthttp.Response
|
||||
// Format performs content-negotiation on the Accept HTTP header.
|
||||
// It uses Accepts to select a proper format and calls the matching
|
||||
// user-provided handler function.
|
||||
// If no accepted format is found, and a format with MediaType "default" is given,
|
||||
// that default handler is called. If no format is found and no default is given,
|
||||
// StatusNotAcceptable is sent.
|
||||
Format(handlers ...ResFmt) error
|
||||
// AutoFormat performs content-negotiation on the Accept HTTP header.
|
||||
// It uses Accepts to select a proper format.
|
||||
// The supported content types are text/html, text/plain, application/json, and application/xml.
|
||||
// For more flexible content negotiation, use Format.
|
||||
// If the header is not specified or there is no proper format, text/plain is used.
|
||||
AutoFormat(body any) error
|
||||
// FormFile returns the first file by key from a MultipartForm.
|
||||
FormFile(key string) (*multipart.FileHeader, error)
|
||||
// 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.
|
||||
FormValue(key string, defaultValue ...string) string
|
||||
// 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
|
||||
Fresh() bool
|
||||
// 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.
|
||||
Get(key string, defaultValue ...string) string
|
||||
// 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.
|
||||
GetRespHeader(key string, defaultValue ...string) string
|
||||
// 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.
|
||||
GetRespHeaders() map[string][]string
|
||||
// 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.
|
||||
GetReqHeaders() map[string][]string
|
||||
// Host contains the host derived from the X-Forwarded-Host or Host HTTP header.
|
||||
// Returned value is only valid within the handler. Do not store any references.
|
||||
// In a network context, `Host` refers to the combination of a hostname and potentially a port number used for connecting,
|
||||
// while `Hostname` refers specifically to the name assigned to a device on a network, excluding any port information.
|
||||
// Example: URL: https://example.com:8080 -> Host: example.com:8080
|
||||
// Make copies or use the Immutable setting instead.
|
||||
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
Host() string
|
||||
// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method.
|
||||
// Returned value is only valid within the handler. Do not store any references.
|
||||
// Example: URL: https://example.com:8080 -> Hostname: example.com
|
||||
// Make copies or use the Immutable setting instead.
|
||||
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
Hostname() string
|
||||
// Port returns the remote port of the request.
|
||||
Port() string
|
||||
// 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.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
IP() 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.
|
||||
extractIPsFromHeader(header string) []string
|
||||
// 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.
|
||||
extractIPFromHeader(header string) string
|
||||
// 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.
|
||||
IPs() []string
|
||||
// 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
|
||||
Is(extension string) bool
|
||||
// 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.
|
||||
// If the ctype parameter is given, this method will set the
|
||||
// Content-Type header equal to ctype. If ctype is not given,
|
||||
// The Content-Type header will be set to application/json.
|
||||
JSON(data any, ctype ...string) error
|
||||
// CBOR converts any interface or string to CBOR encoded bytes.
|
||||
// If the ctype parameter is given, this method will set the
|
||||
// Content-Type header equal to ctype. If ctype is not given,
|
||||
// The Content-Type header will be set to application/cbor.
|
||||
CBOR(data any, ctype ...string) error
|
||||
// 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.
|
||||
JSONP(data any, callback ...string) error
|
||||
// XML converts any interface or string to XML.
|
||||
// This method also sets the content header to application/xml.
|
||||
XML(data any) error
|
||||
// Links joins the links followed by the property to populate the response's Link HTTP header field.
|
||||
Links(link ...string)
|
||||
// Locals makes it possible to pass any values under keys scoped to the request
|
||||
// and therefore available to all following routes that match the request.
|
||||
//
|
||||
// All the values are removed from ctx after returning from the top
|
||||
// RequestHandler. Additionally, Close method is called on each value
|
||||
// implementing io.Closer before removing the value from ctx.
|
||||
Locals(key any, value ...any) any
|
||||
// Location sets the response Location HTTP header to the specified path parameter.
|
||||
Location(path string)
|
||||
// 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.
|
||||
Method(override ...string) string
|
||||
// MultipartForm parse form entries from binary.
|
||||
// This returns a map[string][]string, so given a key the value will be a string slice.
|
||||
MultipartForm() (*multipart.Form, error)
|
||||
// ClientHelloInfo return CHI from context
|
||||
ClientHelloInfo() *tls.ClientHelloInfo
|
||||
// Next executes the next method in the stack that matches the current route.
|
||||
Next() error
|
||||
// RestartRouting instead of going to the next handler. This may be useful after
|
||||
// changing the request path. Note that handlers might be executed again.
|
||||
RestartRouting() error
|
||||
// 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.
|
||||
OriginalURL() string
|
||||
// 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.
|
||||
Params(key string, defaultValue ...string) string
|
||||
// Path returns the path part of the request URL.
|
||||
// Optionally, you could override the path.
|
||||
// Make copies or use the Immutable setting to use the value outside the Handler.
|
||||
Path(override ...string) string
|
||||
// Scheme contains the request protocol string: http or https for TLS requests.
|
||||
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
Scheme() string
|
||||
// Protocol returns the HTTP protocol of request: HTTP/1.1 and HTTP/2.
|
||||
Protocol() string
|
||||
// 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.
|
||||
Query(key string, defaultValue ...string) string
|
||||
// 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"
|
||||
Queries() map[string]string
|
||||
// Range returns a struct containing the type and a slice of ranges.
|
||||
Range(size int) (Range, error)
|
||||
// Redirect returns the Redirect reference.
|
||||
// Use Redirect().Status() to set custom redirection status code.
|
||||
// If status is not specified, status defaults to 302 Found.
|
||||
// You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection.
|
||||
Redirect() *Redirect
|
||||
// ViewBind Add vars to default view var map binding to template engine.
|
||||
// Variables are read by the Render method and may be overwritten.
|
||||
ViewBind(vars Map) error
|
||||
// getLocationFromRoute get URL location from route using parameters
|
||||
getLocationFromRoute(route Route, params Map) (string, error)
|
||||
// GetRouteURL generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831"
|
||||
GetRouteURL(routeName string, params Map) (string, error)
|
||||
// Render a template with data and sends a text/html response.
|
||||
// We support the following engines: https://github.com/gofiber/template
|
||||
Render(name string, bind any, layouts ...string) error
|
||||
renderExtensions(bind any)
|
||||
// Req returns a convenience type whose API is limited to operations
|
||||
// on the incoming request.
|
||||
Req() Req
|
||||
// Res returns a convenience type whose API is limited to operations
|
||||
// on the outgoing response.
|
||||
Res() Res
|
||||
// Route returns the matched Route struct.
|
||||
Route() *Route
|
||||
// SaveFile saves any multipart file to disk.
|
||||
SaveFile(fileheader *multipart.FileHeader, path string) error
|
||||
// SaveFileToStorage saves any multipart file to an external storage system.
|
||||
SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error
|
||||
// Secure returns whether a secure connection was established.
|
||||
Secure() bool
|
||||
// Send sets the HTTP response body without copying it.
|
||||
// From this point onward the body argument must not be changed.
|
||||
Send(body []byte) error
|
||||
// SendFile transfers the file from the specified path.
|
||||
// By default, the file is not compressed. To enable compression, set SendFile.Compress to true.
|
||||
// The Content-Type response HTTP header field is set based on the file's extension.
|
||||
// If the file extension is missing or invalid, the Content-Type is detected from the file's format.
|
||||
SendFile(file string, config ...SendFile) error
|
||||
// SendStatus sets the HTTP status code and if the response body is empty,
|
||||
// it sets the correct status message in the body.
|
||||
SendStatus(status int) error
|
||||
// SendString sets the HTTP response body for string types.
|
||||
// This means no type assertion, recommended for faster performance
|
||||
SendString(body string) error
|
||||
// SendStream sets response body stream and optional body size.
|
||||
SendStream(stream io.Reader, size ...int) error
|
||||
// SendStreamWriter sets response body stream writer
|
||||
SendStreamWriter(streamWriter func(*bufio.Writer)) error
|
||||
// Set sets the response's HTTP header field to the specified key, value.
|
||||
Set(key, val string)
|
||||
setCanonical(key, val string)
|
||||
// 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.
|
||||
Subdomains(offset ...int) []string
|
||||
// Stale is not implemented yet, pull requests are welcome!
|
||||
Stale() bool
|
||||
// Status sets the HTTP status for the response.
|
||||
// This method is chainable.
|
||||
Status(status int) T
|
||||
// String returns unique string representation of the ctx.
|
||||
//
|
||||
// The returned value may be useful for logging.
|
||||
String() string
|
||||
// Type sets the Content-Type HTTP header to the MIME type specified by the file extension.
|
||||
Type(extension string, charset ...string) T
|
||||
// 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.
|
||||
Vary(fields ...string)
|
||||
// Write appends p into response body.
|
||||
Write(p []byte) (int, error)
|
||||
// Writef appends f & a into response body writer.
|
||||
Writef(f string, a ...any) (int, error)
|
||||
// WriteString appends s to response body.
|
||||
WriteString(s string) (int, error)
|
||||
// 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).
|
||||
XHR() bool
|
||||
// configDependentPaths set paths for route recognition and prepared paths for the user,
|
||||
// here the features for caseSensitive, decoded paths, strict paths are evaluated
|
||||
configDependentPaths()
|
||||
// IsProxyTrusted checks trustworthiness of remote ip.
|
||||
// If Config.TrustProxy false, it returns true
|
||||
// IsProxyTrusted can check remote ip by proxy ranges and ip map.
|
||||
IsProxyTrusted() bool
|
||||
// IsFromLocal will return true if request came from local.
|
||||
IsFromLocal() bool
|
||||
// Bind You can bind body, cookie, headers etc. into the map, map slice, struct easily by using Binding method.
|
||||
// It gives custom binding support, detailed binding options and more.
|
||||
// Replacement of: BodyParser, ParamsParser, GetReqHeaders, GetRespHeaders, AllParams, QueryParser, ReqHeaderParser
|
||||
Bind() *Bind
|
||||
// Reset is a method to reset context fields by given request when to use server handlers.
|
||||
Reset(fctx *fasthttp.RequestCtx)
|
||||
|
||||
// Release is a method to reset context fields when to use ReleaseCtx()
|
||||
release()
|
||||
getBody() []byte
|
||||
// Methods to use with next stack.
|
||||
getMethodInt() int
|
||||
getIndexRoute() int
|
||||
|
@ -27,47 +356,11 @@ type CustomCtx interface {
|
|||
setIndexHandler(handler int)
|
||||
setIndexRoute(route int)
|
||||
setMatched(matched bool)
|
||||
setRoute(route *Route)
|
||||
}
|
||||
|
||||
func NewDefaultCtx(app *App) *DefaultCtx {
|
||||
// return ctx
|
||||
ctx := &DefaultCtx{
|
||||
// Set app reference
|
||||
app: app,
|
||||
}
|
||||
ctx.req = &DefaultReq{ctx: ctx}
|
||||
ctx.res = &DefaultRes{ctx: ctx}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (app *App) newCtx() Ctx {
|
||||
var c Ctx
|
||||
|
||||
if app.newCtxFunc != nil {
|
||||
c = app.newCtxFunc(app)
|
||||
} else {
|
||||
c = NewDefaultCtx(app)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// AcquireCtx retrieves a new Ctx from the pool.
|
||||
func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) Ctx {
|
||||
ctx, ok := app.pool.Get().(Ctx)
|
||||
|
||||
if !ok {
|
||||
panic(errors.New("failed to type-assert to Ctx"))
|
||||
}
|
||||
ctx.Reset(fctx)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// ReleaseCtx releases the ctx back into the pool.
|
||||
func (app *App) ReleaseCtx(c Ctx) {
|
||||
c.release()
|
||||
app.pool.Put(c)
|
||||
setRoute(route *Route[T])
|
||||
// Drop closes the underlying connection without sending any response headers or body.
|
||||
// This can be useful for silently terminating client connections, such as in DDoS mitigation
|
||||
// or when blocking access to sensitive endpoints.
|
||||
Drop() error
|
||||
// End immediately flushes the current response and closes the underlying connection.
|
||||
End() error
|
||||
}
|
||||
|
|
|
@ -1,365 +1,78 @@
|
|||
// Code generated by ifacemaker; DO NOT EDIT.
|
||||
//go:build ignore
|
||||
|
||||
package fiber
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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 interface {
|
||||
// Accepts checks if the specified extensions or content types are acceptable.
|
||||
Accepts(offers ...string) string
|
||||
// AcceptsCharsets checks if the specified charset is acceptable.
|
||||
AcceptsCharsets(offers ...string) string
|
||||
// AcceptsEncodings checks if the specified encoding is acceptable.
|
||||
AcceptsEncodings(offers ...string) string
|
||||
// AcceptsLanguages checks if the specified language is acceptable.
|
||||
AcceptsLanguages(offers ...string) string
|
||||
// App returns the *App reference to the instance of the Fiber application
|
||||
App() *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.
|
||||
Append(field string, values ...string)
|
||||
// Attachment sets the HTTP response Content-Disposition header field to attachment.
|
||||
Attachment(filename ...string)
|
||||
// BaseURL returns (protocol + host + base path).
|
||||
BaseURL() string
|
||||
// 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.
|
||||
BodyRaw() []byte
|
||||
tryDecodeBodyInOrder(originalBody *[]byte, encodings []string) ([]byte, uint8, error)
|
||||
// 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.
|
||||
Body() []byte
|
||||
// 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.
|
||||
ClearCookie(key ...string)
|
||||
// RequestCtx returns *fasthttp.RequestCtx that carries a deadline
|
||||
// a cancellation signal, and other values across API boundaries.
|
||||
RequestCtx() *fasthttp.RequestCtx
|
||||
// Context returns a context implementation that was set by
|
||||
// user earlier or returns a non-nil, empty context,if it was not set earlier.
|
||||
Context() context.Context
|
||||
// SetContext sets a context implementation by user.
|
||||
SetContext(ctx context.Context)
|
||||
// Cookie sets a cookie by passing a cookie struct.
|
||||
Cookie(cookie *Cookie)
|
||||
// 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.
|
||||
Cookies(key string, defaultValue ...string) string
|
||||
// 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.
|
||||
Download(file string, filename ...string) error
|
||||
// Request return the *fasthttp.Request object
|
||||
// This allows you to use all fasthttp request methods
|
||||
// https://godoc.org/github.com/valyala/fasthttp#Request
|
||||
Request() *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
|
||||
Response() *fasthttp.Response
|
||||
// Format performs content-negotiation on the Accept HTTP header.
|
||||
// It uses Accepts to select a proper format and calls the matching
|
||||
// user-provided handler function.
|
||||
// If no accepted format is found, and a format with MediaType "default" is given,
|
||||
// that default handler is called. If no format is found and no default is given,
|
||||
// StatusNotAcceptable is sent.
|
||||
Format(handlers ...ResFmt) error
|
||||
// AutoFormat performs content-negotiation on the Accept HTTP header.
|
||||
// It uses Accepts to select a proper format.
|
||||
// The supported content types are text/html, text/plain, application/json, and application/xml.
|
||||
// For more flexible content negotiation, use Format.
|
||||
// If the header is not specified or there is no proper format, text/plain is used.
|
||||
AutoFormat(body any) error
|
||||
// FormFile returns the first file by key from a MultipartForm.
|
||||
FormFile(key string) (*multipart.FileHeader, error)
|
||||
// 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.
|
||||
FormValue(key string, defaultValue ...string) string
|
||||
// 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
|
||||
Fresh() bool
|
||||
// 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.
|
||||
Get(key string, defaultValue ...string) string
|
||||
// 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.
|
||||
GetRespHeader(key string, defaultValue ...string) string
|
||||
// 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.
|
||||
GetRespHeaders() map[string][]string
|
||||
// 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.
|
||||
GetReqHeaders() map[string][]string
|
||||
// Host contains the host derived from the X-Forwarded-Host or Host HTTP header.
|
||||
// Returned value is only valid within the handler. Do not store any references.
|
||||
// In a network context, `Host` refers to the combination of a hostname and potentially a port number used for connecting,
|
||||
// while `Hostname` refers specifically to the name assigned to a device on a network, excluding any port information.
|
||||
// Example: URL: https://example.com:8080 -> Host: example.com:8080
|
||||
// Make copies or use the Immutable setting instead.
|
||||
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
Host() string
|
||||
// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method.
|
||||
// Returned value is only valid within the handler. Do not store any references.
|
||||
// Example: URL: https://example.com:8080 -> Hostname: example.com
|
||||
// Make copies or use the Immutable setting instead.
|
||||
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
Hostname() string
|
||||
// Port returns the remote port of the request.
|
||||
Port() string
|
||||
// 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.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
IP() 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.
|
||||
extractIPsFromHeader(header string) []string
|
||||
// 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.
|
||||
extractIPFromHeader(header string) string
|
||||
// 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.
|
||||
IPs() []string
|
||||
// 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
|
||||
Is(extension string) bool
|
||||
// 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.
|
||||
// If the ctype parameter is given, this method will set the
|
||||
// Content-Type header equal to ctype. If ctype is not given,
|
||||
// The Content-Type header will be set to application/json.
|
||||
JSON(data any, ctype ...string) error
|
||||
// CBOR converts any interface or string to CBOR encoded bytes.
|
||||
// If the ctype parameter is given, this method will set the
|
||||
// Content-Type header equal to ctype. If ctype is not given,
|
||||
// The Content-Type header will be set to application/cbor.
|
||||
CBOR(data any, ctype ...string) error
|
||||
// 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.
|
||||
JSONP(data any, callback ...string) error
|
||||
// XML converts any interface or string to XML.
|
||||
// This method also sets the content header to application/xml.
|
||||
XML(data any) error
|
||||
// Links joins the links followed by the property to populate the response's Link HTTP header field.
|
||||
Links(link ...string)
|
||||
// Locals makes it possible to pass any values under keys scoped to the request
|
||||
// and therefore available to all following routes that match the request.
|
||||
//
|
||||
// All the values are removed from ctx after returning from the top
|
||||
// RequestHandler. Additionally, Close method is called on each value
|
||||
// implementing io.Closer before removing the value from ctx.
|
||||
Locals(key any, value ...any) any
|
||||
// Location sets the response Location HTTP header to the specified path parameter.
|
||||
Location(path string)
|
||||
// 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.
|
||||
Method(override ...string) string
|
||||
// MultipartForm parse form entries from binary.
|
||||
// This returns a map[string][]string, so given a key the value will be a string slice.
|
||||
MultipartForm() (*multipart.Form, error)
|
||||
// ClientHelloInfo return CHI from context
|
||||
ClientHelloInfo() *tls.ClientHelloInfo
|
||||
// Next executes the next method in the stack that matches the current route.
|
||||
Next() error
|
||||
// RestartRouting instead of going to the next handler. This may be useful after
|
||||
// changing the request path. Note that handlers might be executed again.
|
||||
RestartRouting() error
|
||||
// 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.
|
||||
OriginalURL() string
|
||||
// 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.
|
||||
Params(key string, defaultValue ...string) string
|
||||
// Path returns the path part of the request URL.
|
||||
// Optionally, you could override the path.
|
||||
Path(override ...string) string
|
||||
// Scheme contains the request protocol string: http or https for TLS requests.
|
||||
// Please use Config.TrustProxy to prevent header spoofing, in case when your app is behind the proxy.
|
||||
Scheme() string
|
||||
// Protocol returns the HTTP protocol of request: HTTP/1.1 and HTTP/2.
|
||||
Protocol() string
|
||||
// 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.
|
||||
Query(key string, defaultValue ...string) string
|
||||
// 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"
|
||||
Queries() map[string]string
|
||||
// Range returns a struct containing the type and a slice of ranges.
|
||||
Range(size int) (Range, error)
|
||||
// Redirect returns the Redirect reference.
|
||||
// Use Redirect().Status() to set custom redirection status code.
|
||||
// If status is not specified, status defaults to 302 Found.
|
||||
// You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection.
|
||||
Redirect() *Redirect
|
||||
// ViewBind Add vars to default view var map binding to template engine.
|
||||
// Variables are read by the Render method and may be overwritten.
|
||||
ViewBind(vars Map) error
|
||||
// getLocationFromRoute get URL location from route using parameters
|
||||
getLocationFromRoute(route Route, params Map) (string, error)
|
||||
// GetRouteURL generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831"
|
||||
GetRouteURL(routeName string, params Map) (string, error)
|
||||
// Render a template with data and sends a text/html response.
|
||||
// We support the following engines: https://github.com/gofiber/template
|
||||
Render(name string, bind any, layouts ...string) error
|
||||
renderExtensions(bind any)
|
||||
// Req returns a convenience type whose API is limited to operations
|
||||
// on the incoming request.
|
||||
Req() Req
|
||||
// Res returns a convenience type whose API is limited to operations
|
||||
// on the outgoing response.
|
||||
Res() Res
|
||||
// Route returns the matched Route struct.
|
||||
Route() *Route
|
||||
// SaveFile saves any multipart file to disk.
|
||||
SaveFile(fileheader *multipart.FileHeader, path string) error
|
||||
// SaveFileToStorage saves any multipart file to an external storage system.
|
||||
SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error
|
||||
// Secure returns whether a secure connection was established.
|
||||
Secure() bool
|
||||
// Send sets the HTTP response body without copying it.
|
||||
// From this point onward the body argument must not be changed.
|
||||
Send(body []byte) error
|
||||
// SendFile transfers the file from the specified path.
|
||||
// By default, the file is not compressed. To enable compression, set SendFile.Compress to true.
|
||||
// The Content-Type response HTTP header field is set based on the file's extension.
|
||||
// If the file extension is missing or invalid, the Content-Type is detected from the file's format.
|
||||
SendFile(file string, config ...SendFile) error
|
||||
// SendStatus sets the HTTP status code and if the response body is empty,
|
||||
// it sets the correct status message in the body.
|
||||
SendStatus(status int) error
|
||||
// SendString sets the HTTP response body for string types.
|
||||
// This means no type assertion, recommended for faster performance
|
||||
SendString(body string) error
|
||||
// SendStream sets response body stream and optional body size.
|
||||
SendStream(stream io.Reader, size ...int) error
|
||||
// SendStreamWriter sets response body stream writer
|
||||
SendStreamWriter(streamWriter func(*bufio.Writer)) error
|
||||
// Set sets the response's HTTP header field to the specified key, value.
|
||||
Set(key, val string)
|
||||
setCanonical(key, val string)
|
||||
// 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.
|
||||
Subdomains(offset ...int) []string
|
||||
// Stale is not implemented yet, pull requests are welcome!
|
||||
Stale() bool
|
||||
// Status sets the HTTP status for the response.
|
||||
// This method is chainable.
|
||||
Status(status int) Ctx
|
||||
// String returns unique string representation of the ctx.
|
||||
//
|
||||
// The returned value may be useful for logging.
|
||||
String() string
|
||||
// Type sets the Content-Type HTTP header to the MIME type specified by the file extension.
|
||||
Type(extension string, charset ...string) Ctx
|
||||
// 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.
|
||||
Vary(fields ...string)
|
||||
// Write appends p into response body.
|
||||
Write(p []byte) (int, error)
|
||||
// Writef appends f & a into response body writer.
|
||||
Writef(f string, a ...any) (int, error)
|
||||
// WriteString appends s to response body.
|
||||
WriteString(s string) (int, error)
|
||||
// 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).
|
||||
XHR() bool
|
||||
// configDependentPaths set paths for route recognition and prepared paths for the user,
|
||||
// here the features for caseSensitive, decoded paths, strict paths are evaluated
|
||||
configDependentPaths()
|
||||
// IsProxyTrusted checks trustworthiness of remote ip.
|
||||
// If Config.TrustProxy false, it returns true
|
||||
// IsProxyTrusted can check remote ip by proxy ranges and ip map.
|
||||
IsProxyTrusted() bool
|
||||
// IsFromLocal will return true if request came from local.
|
||||
IsFromLocal() bool
|
||||
// Bind You can bind body, cookie, headers etc. into the map, map slice, struct easily by using Binding method.
|
||||
// It gives custom binding support, detailed binding options and more.
|
||||
// Replacement of: BodyParser, ParamsParser, GetReqHeaders, GetRespHeaders, AllParams, QueryParser, ReqHeaderParser
|
||||
Bind() *Bind
|
||||
// Reset is a method to reset context fields by given request when to use server handlers.
|
||||
Reset(fctx *fasthttp.RequestCtx)
|
||||
// Release is a method to reset context fields when to use ReleaseCtx()
|
||||
release()
|
||||
getBody() []byte
|
||||
// Methods to use with next stack.
|
||||
getMethodInt() int
|
||||
getIndexRoute() int
|
||||
getTreePathHash() int
|
||||
getDetectionPath() string
|
||||
getPathOriginal() string
|
||||
getValues() *[maxParams]string
|
||||
getMatched() bool
|
||||
setIndexHandler(handler int)
|
||||
setIndexRoute(route int)
|
||||
setMatched(matched bool)
|
||||
setRoute(route *Route)
|
||||
// Drop closes the underlying connection without sending any response headers or body.
|
||||
// This can be useful for silently terminating client connections, such as in DDoS mitigation
|
||||
// or when blocking access to sensitive endpoints.
|
||||
Drop() error
|
||||
// End immediately flushes the current response and closes the underlying connection.
|
||||
End() error
|
||||
func main() {
|
||||
const filename = "ctx_interface.go"
|
||||
|
||||
// 1) read file
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to read file: %w", err))
|
||||
}
|
||||
|
||||
// 2) patch interface
|
||||
patched, err := patchCtxFile(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 3) write patched file
|
||||
if err := os.WriteFile(filename, patched, 0o644); err != nil {
|
||||
panic(fmt.Errorf("failed to write patched file: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// patchCtxFile adjust the Ctx interface in the given file
|
||||
func patchCtxFile(input []byte) ([]byte, error) {
|
||||
// process file line by line
|
||||
in := bytes.NewReader(input)
|
||||
scanner := bufio.NewScanner(in)
|
||||
var outBuf bytes.Buffer
|
||||
|
||||
regexCtx := regexp.MustCompile(`(\*Default)Ctx`)
|
||||
regexApp := regexp.MustCompile(`\*App(\[\w+])?`)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// A) change interface head definition
|
||||
// => "type Ctx interface {" -> "type Ctx[T any] interface {"
|
||||
if strings.HasPrefix(line, "type") {
|
||||
line = strings.Replace(line,
|
||||
"type CtxGeneric interface {",
|
||||
"type CtxGeneric[T any] interface {",
|
||||
1,
|
||||
)
|
||||
} else {
|
||||
// B) replace every use of Ctx with T but only in the function definitions
|
||||
// via regex and boundary word matching
|
||||
// => "func (app *App[TCtx]) newCtx() Ctx {" -> "func (app *App[TCtx]) newCtx() T {"
|
||||
if strings.Contains(line, "Ctx") {
|
||||
line = regexCtx.ReplaceAllString(line, "T")
|
||||
}
|
||||
|
||||
// C) App with generic type
|
||||
if strings.Contains(line, "App") {
|
||||
line = regexApp.ReplaceAllString(line, "*App[T]")
|
||||
}
|
||||
}
|
||||
|
||||
outBuf.WriteString(line + "\n")
|
||||
}
|
||||
if err := scanner.Err(); err != nil && err != io.EOF {
|
||||
return nil, fmt.Errorf("scanner error: %w", err)
|
||||
}
|
||||
|
||||
return outBuf.Bytes(), nil
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ func Test_Ctx_CustomCtx(t *testing.T) {
|
|||
|
||||
app := New()
|
||||
|
||||
app.NewCtxFunc(func(app *App) CustomCtx {
|
||||
app.NewCtxFunc(func(app *App[TCtx]) CustomCtx {
|
||||
return &customCtx{
|
||||
DefaultCtx: *NewDefaultCtx(app),
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func Test_Ctx_CustomCtx_and_Method(t *testing.T) {
|
|||
})
|
||||
|
||||
// Create custom context
|
||||
app.NewCtxFunc(func(app *App) CustomCtx {
|
||||
app.NewCtxFunc(func(app *App[TCtx]) CustomCtx {
|
||||
return &customCtx{
|
||||
DefaultCtx: *NewDefaultCtx(app),
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ func Test_Ctx_App(t *testing.T) {
|
|||
app.config.BodyLimit = 1000
|
||||
c := app.AcquireCtx(&fasthttp.RequestCtx{})
|
||||
|
||||
require.Equal(t, 1000, c.App().config.BodyLimit)
|
||||
require.Equal(t, 1000, app.config.BodyLimit)
|
||||
}
|
||||
|
||||
// go test -run Test_Ctx_Append
|
||||
|
|
|
@ -48,7 +48,7 @@ func main() {
|
|||
The `MountPath` property contains one or more path patterns on which a sub-app was mounted.
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) MountPath() string
|
||||
func (app *App[TCtx]) MountPath() string
|
||||
```
|
||||
|
||||
```go title="Example"
|
||||
|
@ -87,7 +87,7 @@ Mounting order is important for `MountPath`. To get mount paths properly, you sh
|
|||
You can group routes by creating a `*Group` struct.
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) Group(prefix string, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Group(prefix string, handlers ...Handler) Router
|
||||
```
|
||||
|
||||
```go title="Example"
|
||||
|
@ -127,7 +127,7 @@ Returns an instance of a single route, which you can then use to handle HTTP ver
|
|||
Similar to [`Express`](https://expressjs.com/de/api.html#app.route).
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) Route(path string) Register
|
||||
func (app *App[TCtx]) Route(path string) Register
|
||||
```
|
||||
|
||||
<details>
|
||||
|
@ -204,7 +204,7 @@ func main() {
|
|||
This method returns the number of registered handlers.
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) HandlersCount() uint32
|
||||
func (app *App[TCtx]) HandlersCount() uint32
|
||||
```
|
||||
|
||||
### Stack
|
||||
|
@ -212,7 +212,7 @@ func (app *App) HandlersCount() uint32
|
|||
This method returns the original router stack.
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) Stack() [][]*Route
|
||||
func (app *App[TCtx]) Stack() [][]*Route
|
||||
```
|
||||
|
||||
```go title="Example"
|
||||
|
@ -280,7 +280,7 @@ func main() {
|
|||
This method assigns the name to the latest created route.
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) Name(name string) Router
|
||||
func (app *App[TCtx]) Name(name string) Router
|
||||
```
|
||||
|
||||
```go title="Example"
|
||||
|
@ -514,6 +514,8 @@ func (app *App) ErrorHandler(ctx Ctx, err error) error
|
|||
|
||||
## NewCtxFunc
|
||||
|
||||
TODO: remove this section and replace with the new fiber.NewWithCustomCtx ...
|
||||
|
||||
`NewCtxFunc` allows you to customize the `ctx` struct as needed.
|
||||
|
||||
```go title="Signature"
|
||||
|
|
|
@ -103,7 +103,7 @@ app.Listen(":8080", fiber.ListenConfig{
|
|||
|
||||
| Property | Type | Description | Default |
|
||||
|-------------------------------------------------------------------------|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|---------|
|
||||
| <Reference id="beforeservefunc">BeforeServeFunc</Reference> | `func(app *App) error` | Allows customizing and accessing fiber app before serving the app. | `nil` |
|
||||
| <Reference id="beforeservefunc">BeforeServeFunc</Reference> | `func(app *App[TCtx]) error` | Allows customizing and accessing fiber app before serving the app. | `nil` |
|
||||
| <Reference id="certclientfile">CertClientFile</Reference> | `string` | Path of the client certificate. If you want to use mTLS, you must enter this field. | `""` |
|
||||
| <Reference id="certfile">CertFile</Reference> | `string` | Path of the certificate file. If you want to use TLS, you must enter this field. | `""` |
|
||||
| <Reference id="certkeyfile">CertKeyFile</Reference> | `string` | Path of the certificate's private key. If you want to use TLS, you must enter this field. | `""` |
|
||||
|
|
|
@ -9,22 +9,22 @@ Registers a route bound to a specific [HTTP method](https://developer.mozilla.or
|
|||
|
||||
```go title="Signatures"
|
||||
// HTTP methods
|
||||
func (app *App) Get(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Head(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Post(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Put(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Delete(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Connect(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Options(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Trace(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Patch(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Get(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Head(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Post(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Put(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Delete(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Connect(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Options(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Trace(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Patch(path string, handler Handler, handlers ...Handler) Router
|
||||
|
||||
// Add allows you to specify a method as value
|
||||
func (app *App) Add(method, path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Add(method, path string, handler Handler, handlers ...Handler) Router
|
||||
|
||||
// All will register the route on all HTTP methods
|
||||
// Almost the same as app.Use but not bound to prefixes
|
||||
func (app *App) All(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) All(path string, handler Handler, handlers ...Handler) Router
|
||||
```
|
||||
|
||||
```go title="Examples"
|
||||
|
@ -44,13 +44,13 @@ app.Post("/api/register", func(c fiber.Ctx) error {
|
|||
Can be used for middleware packages and prefix catchers. These routes will only match the beginning of each path i.e. `/john` will match `/john/doe`, `/johnnnnn` etc
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) Use(args ...any) Router
|
||||
func (app *App[TCtx]) Use(args ...any) Router
|
||||
|
||||
// Different usage variations
|
||||
func (app *App) Use(handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Use(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Use(paths []string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App) Use(path string, app *App) Router
|
||||
func (app *App[TCtx]) Use(handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Use(path string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Use(paths []string, handler Handler, handlers ...Handler) Router
|
||||
func (app *App[TCtx]) Use(path string, app *App) Router
|
||||
```
|
||||
|
||||
```go title="Examples"
|
||||
|
|
|
@ -76,6 +76,8 @@ We have made several changes to the Fiber app, including:
|
|||
|
||||
### Custom Ctx Interface in Fiber v3
|
||||
|
||||
TODO - Add more details
|
||||
|
||||
Fiber v3 introduces a customizable `Ctx` interface, allowing developers to extend and modify the context to fit their needs. This feature provides greater flexibility and control over request handling.
|
||||
|
||||
#### Idea Behind Custom Ctx Classes
|
||||
|
@ -84,10 +86,12 @@ The idea behind custom `Ctx` classes is to give developers the ability to extend
|
|||
|
||||
#### NewCtxFunc
|
||||
|
||||
TODO change example
|
||||
|
||||
The `NewCtxFunc` method allows you to customize the `Ctx` struct as needed.
|
||||
|
||||
```go title="Signature"
|
||||
func (app *App) NewCtxFunc(function func(app *App) CustomCtx)
|
||||
func (app *App[TCtx]) NewCtxFunc(function func(app *App) CustomCtx)
|
||||
```
|
||||
|
||||
<details>
|
||||
|
|
2
go.sum
2
go.sum
|
@ -34,8 +34,6 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ
|
|||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
48
group.go
48
group.go
|
@ -10,9 +10,9 @@ import (
|
|||
)
|
||||
|
||||
// Group struct
|
||||
type Group struct {
|
||||
app *App
|
||||
parentGroup *Group
|
||||
type Group[TCtx CtxGeneric[TCtx]] struct {
|
||||
app *App[TCtx]
|
||||
parentGroup *Group[TCtx]
|
||||
name string
|
||||
|
||||
Prefix string
|
||||
|
@ -23,7 +23,7 @@ type Group struct {
|
|||
//
|
||||
// If this method is used before any route added to group, it'll set group name and OnGroupNameHook will be used.
|
||||
// Otherwise, it'll set route name and OnName hook will be used.
|
||||
func (grp *Group) Name(name string) Router {
|
||||
func (grp *Group[TCtx]) Name(name string) Router[TCtx] {
|
||||
if grp.anyRouteDefined {
|
||||
grp.app.Name(name)
|
||||
|
||||
|
@ -66,21 +66,21 @@ func (grp *Group) Name(name string) Router {
|
|||
// app.Use("/mounted-path", subApp)
|
||||
//
|
||||
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
|
||||
func (grp *Group) Use(args ...any) Router {
|
||||
var subApp *App
|
||||
func (grp *Group[TCtx]) Use(args ...any) Router[TCtx] {
|
||||
var subApp *App[TCtx]
|
||||
var prefix string
|
||||
var prefixes []string
|
||||
var handlers []Handler
|
||||
var handlers []Handler[TCtx]
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch arg := args[i].(type) {
|
||||
case string:
|
||||
prefix = arg
|
||||
case *App:
|
||||
case *App[TCtx]:
|
||||
subApp = arg
|
||||
case []string:
|
||||
prefixes = arg
|
||||
case Handler:
|
||||
case Handler[TCtx]:
|
||||
handlers = append(handlers, arg)
|
||||
default:
|
||||
panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
|
||||
|
@ -109,59 +109,59 @@ func (grp *Group) Use(args ...any) Router {
|
|||
|
||||
// Get registers a route for GET methods that requests a representation
|
||||
// of the specified resource. Requests using GET should only retrieve data.
|
||||
func (grp *Group) Get(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Get(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodGet}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Head registers a route for HEAD methods that asks for a response identical
|
||||
// to that of a GET request, but without the response body.
|
||||
func (grp *Group) Head(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Head(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodHead}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Post registers a route for POST methods that is used to submit an entity to the
|
||||
// specified resource, often causing a change in state or side effects on the server.
|
||||
func (grp *Group) Post(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Post(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodPost}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Put registers a route for PUT methods that replaces all current representations
|
||||
// of the target resource with the request payload.
|
||||
func (grp *Group) Put(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Put(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodPut}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Delete registers a route for DELETE methods that deletes the specified resource.
|
||||
func (grp *Group) Delete(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Delete(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodDelete}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Connect registers a route for CONNECT methods that establishes a tunnel to the
|
||||
// server identified by the target resource.
|
||||
func (grp *Group) Connect(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Connect(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodConnect}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Options registers a route for OPTIONS methods that is used to describe the
|
||||
// communication options for the target resource.
|
||||
func (grp *Group) Options(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Options(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodOptions}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Trace registers a route for TRACE methods that performs a message loop-back
|
||||
// test along the path to the target resource.
|
||||
func (grp *Group) Trace(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Trace(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodTrace}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Patch registers a route for PATCH methods that is used to apply partial
|
||||
// modifications to a resource.
|
||||
func (grp *Group) Patch(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Patch(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
return grp.Add([]string{MethodPatch}, path, handler, handlers...)
|
||||
}
|
||||
|
||||
// Add allows you to specify multiple HTTP methods to register a route.
|
||||
func (grp *Group) Add(methods []string, path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Add(methods []string, path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
grp.app.register(methods, getGroupPath(grp.Prefix, path), grp, append([]Handler{handler}, handlers...)...)
|
||||
if !grp.anyRouteDefined {
|
||||
grp.anyRouteDefined = true
|
||||
|
@ -171,7 +171,7 @@ func (grp *Group) Add(methods []string, path string, handler Handler, handlers .
|
|||
}
|
||||
|
||||
// All will register the handler on all HTTP methods
|
||||
func (grp *Group) All(path string, handler Handler, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) All(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
_ = grp.Add(grp.app.config.RequestMethods, path, handler, handlers...)
|
||||
return grp
|
||||
}
|
||||
|
@ -180,14 +180,14 @@ func (grp *Group) All(path string, handler Handler, handlers ...Handler) Router
|
|||
//
|
||||
// api := app.Group("/api")
|
||||
// api.Get("/users", handler)
|
||||
func (grp *Group) Group(prefix string, handlers ...Handler) Router {
|
||||
func (grp *Group[TCtx]) Group(prefix string, handlers ...Handler[TCtx]) Router[TCtx] {
|
||||
prefix = getGroupPath(grp.Prefix, prefix)
|
||||
if len(handlers) > 0 {
|
||||
grp.app.register([]string{methodUse}, prefix, grp, handlers...)
|
||||
}
|
||||
|
||||
// Create new group
|
||||
newGrp := &Group{Prefix: prefix, app: grp.app, parentGroup: grp}
|
||||
newGrp := &Group[TCtx]{Prefix: prefix, app: grp.app, parentGroup: grp}
|
||||
if err := grp.app.hooks.executeOnGroupHooks(*newGrp); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -197,9 +197,9 @@ func (grp *Group) Group(prefix string, handlers ...Handler) Router {
|
|||
|
||||
// Route is used to define routes with a common prefix inside the common function.
|
||||
// Uses Group method to define new sub-router.
|
||||
func (grp *Group) Route(path string) Register {
|
||||
func (grp *Group[TCtx]) Route(path string) Register[TCtx] {
|
||||
// Create new group
|
||||
register := &Registering{app: grp.app, path: getGroupPath(grp.Prefix, path)}
|
||||
register := &Registering[TCtx]{app: grp.app, path: getGroupPath(grp.Prefix, path)}
|
||||
|
||||
return register
|
||||
}
|
||||
|
|
25
helpers.go
25
helpers.go
|
@ -94,7 +94,7 @@ func readContent(rf io.ReaderFrom, name string) (int64, error) {
|
|||
}
|
||||
|
||||
// quoteString escape special characters in a given string
|
||||
func (app *App) quoteString(raw string) string {
|
||||
func (app *App[TCtx]) quoteString(raw string) string {
|
||||
bb := bytebufferpool.Get()
|
||||
quoted := app.getString(fasthttp.AppendQuotedArg(bb.B, app.getBytes(raw)))
|
||||
bytebufferpool.Put(bb)
|
||||
|
@ -102,7 +102,7 @@ func (app *App) quoteString(raw string) string {
|
|||
}
|
||||
|
||||
// Scan stack if other methods match the request
|
||||
func (app *App) methodExist(c *DefaultCtx) bool {
|
||||
func (app *App[TCtx]) methodExist(c *DefaultCtx) bool {
|
||||
var exists bool
|
||||
|
||||
methods := app.config.RequestMethods
|
||||
|
@ -114,9 +114,9 @@ func (app *App) methodExist(c *DefaultCtx) bool {
|
|||
// Reset stack index
|
||||
c.setIndexRoute(-1)
|
||||
|
||||
tree, ok := c.App().treeStack[i][c.treePathHash]
|
||||
tree, ok := app.treeStack[i][c.treePathHash]
|
||||
if !ok {
|
||||
tree = c.App().treeStack[i][0]
|
||||
tree = app.treeStack[i][0]
|
||||
}
|
||||
// Get stack length
|
||||
lenr := len(tree) - 1
|
||||
|
@ -147,8 +147,9 @@ func (app *App) methodExist(c *DefaultCtx) bool {
|
|||
}
|
||||
|
||||
// Scan stack if other methods match the request
|
||||
func (app *App) methodExistCustom(c CustomCtx) bool {
|
||||
func (app *App[TCtx]) methodExistCustom(c CustomCtx[TCtx]) bool {
|
||||
var exists bool
|
||||
|
||||
methods := app.config.RequestMethods
|
||||
for i := 0; i < len(methods); i++ {
|
||||
// Skip original method
|
||||
|
@ -158,9 +159,9 @@ func (app *App) methodExistCustom(c CustomCtx) bool {
|
|||
// Reset stack index
|
||||
c.setIndexRoute(-1)
|
||||
|
||||
tree, ok := c.App().treeStack[i][c.getTreePathHash()]
|
||||
tree, ok := app.treeStack[i][c.getTreePathHash()]
|
||||
if !ok {
|
||||
tree = c.App().treeStack[i][0]
|
||||
tree = app.treeStack[i][0]
|
||||
}
|
||||
// Get stack length
|
||||
lenr := len(tree) - 1
|
||||
|
@ -191,9 +192,9 @@ func (app *App) methodExistCustom(c CustomCtx) bool {
|
|||
}
|
||||
|
||||
// uniqueRouteStack drop all not unique routes from the slice
|
||||
func uniqueRouteStack(stack []*Route) []*Route {
|
||||
var unique []*Route
|
||||
m := make(map[*Route]struct{})
|
||||
func uniqueRouteStack[TCtx CtxGeneric[TCtx]](stack []*Route[TCtx]) []*Route[TCtx] {
|
||||
var unique []*Route[TCtx]
|
||||
m := make(map[*Route[TCtx]]struct{})
|
||||
for _, v := range stack {
|
||||
if _, ok := m[v]; !ok {
|
||||
m[v] = struct{}{}
|
||||
|
@ -540,7 +541,7 @@ func matchEtag(s, etag string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (app *App) isEtagStale(etag string, noneMatchBytes []byte) bool {
|
||||
func (app *App[TCtx]) isEtagStale(etag string, noneMatchBytes []byte) bool {
|
||||
var start, end int
|
||||
|
||||
// Adapted from:
|
||||
|
@ -650,7 +651,7 @@ func getBytesImmutable(s string) []byte {
|
|||
}
|
||||
|
||||
// HTTP methods and their unique INTs
|
||||
func (app *App) methodInt(s string) int {
|
||||
func (app *App[TCtx]) methodInt(s string) int {
|
||||
// For better performance
|
||||
if len(app.configured.RequestMethods) == 0 {
|
||||
switch s {
|
||||
|
|
78
hooks.go
78
hooks.go
|
@ -6,32 +6,32 @@ import (
|
|||
|
||||
// OnRouteHandler Handlers define a function to create hooks for Fiber.
|
||||
type (
|
||||
OnRouteHandler = func(Route) error
|
||||
OnNameHandler = OnRouteHandler
|
||||
OnGroupHandler = func(Group) error
|
||||
OnGroupNameHandler = OnGroupHandler
|
||||
OnListenHandler = func(ListenData) error
|
||||
OnPreShutdownHandler = func() error
|
||||
OnPostShutdownHandler = func(error) error
|
||||
OnForkHandler = func(int) error
|
||||
OnMountHandler = func(*App) error
|
||||
OnRouteHandler [TCtx CtxGeneric[TCtx]] = func(Route[TCtx]) error
|
||||
OnNameHandler [TCtx CtxGeneric[TCtx]] = OnRouteHandler[TCtx]
|
||||
OnGroupHandler [TCtx CtxGeneric[TCtx]] = func(Group[TCtx]) error
|
||||
OnGroupNameHandler [TCtx CtxGeneric[TCtx]] = OnGroupHandler[TCtx]
|
||||
OnListenHandler = func(ListenData) error
|
||||
OnPreShutdownHandler = func() error
|
||||
OnPostShutdownHandler = func(error)error
|
||||
OnForkHandler = func(int) error
|
||||
OnMountHandler[TCtx CtxGeneric[TCtx]] = func(*App[TCtx]) error
|
||||
)
|
||||
|
||||
// Hooks is a struct to use it with App.
|
||||
type Hooks struct {
|
||||
type Hooks[TCtx CtxGeneric[TCtx]] struct {
|
||||
// Embed app
|
||||
app *App
|
||||
app *App[TCtx]
|
||||
|
||||
// Hooks
|
||||
onRoute []OnRouteHandler
|
||||
onName []OnNameHandler
|
||||
onGroup []OnGroupHandler
|
||||
onGroupName []OnGroupNameHandler
|
||||
onRoute []OnRouteHandler[TCtx]
|
||||
onName []OnNameHandler[TCtx]
|
||||
onGroup []OnGroupHandler[TCtx]
|
||||
onGroupName []OnGroupNameHandler[TCtx]
|
||||
onListen []OnListenHandler
|
||||
onPreShutdown []OnPreShutdownHandler
|
||||
onPostShutdown []OnPostShutdownHandler
|
||||
onFork []OnForkHandler
|
||||
onMount []OnMountHandler
|
||||
onMount []OnMountHandler[TCtx]
|
||||
}
|
||||
|
||||
// ListenData is a struct to use it with OnListenHandler
|
||||
|
@ -41,24 +41,24 @@ type ListenData struct {
|
|||
TLS bool
|
||||
}
|
||||
|
||||
func newHooks(app *App) *Hooks {
|
||||
return &Hooks{
|
||||
func newHooks[TCtx CtxGeneric[TCtx]](app *App[TCtx]) *Hooks[TCtx] {
|
||||
return &Hooks[TCtx]{
|
||||
app: app,
|
||||
onRoute: make([]OnRouteHandler, 0),
|
||||
onGroup: make([]OnGroupHandler, 0),
|
||||
onGroupName: make([]OnGroupNameHandler, 0),
|
||||
onName: make([]OnNameHandler, 0),
|
||||
onRoute: make([]OnRouteHandler[TCtx], 0),
|
||||
onGroup: make([]OnGroupHandler[TCtx], 0),
|
||||
onGroupName: make([]OnGroupNameHandler[TCtx], 0),
|
||||
onName: make([]OnNameHandler[TCtx], 0),
|
||||
onListen: make([]OnListenHandler, 0),
|
||||
onPreShutdown: make([]OnPreShutdownHandler, 0),
|
||||
onPostShutdown: make([]OnPostShutdownHandler, 0),
|
||||
onFork: make([]OnForkHandler, 0),
|
||||
onMount: make([]OnMountHandler, 0),
|
||||
onMount: make([]OnMountHandler[TCtx], 0),
|
||||
}
|
||||
}
|
||||
|
||||
// OnRoute is a hook to execute user functions on each route registration.
|
||||
// Also you can get route properties by route parameter.
|
||||
func (h *Hooks) OnRoute(handler ...OnRouteHandler) {
|
||||
func (h *Hooks[TCtx]) OnRoute(handler ...OnRouteHandler[TCtx]) {
|
||||
h.app.mutex.Lock()
|
||||
h.onRoute = append(h.onRoute, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
|
@ -68,7 +68,7 @@ func (h *Hooks) OnRoute(handler ...OnRouteHandler) {
|
|||
// Also you can get route properties by route parameter.
|
||||
//
|
||||
// WARN: OnName only works with naming routes, not groups.
|
||||
func (h *Hooks) OnName(handler ...OnNameHandler) {
|
||||
func (h *Hooks[TCtx]) OnName(handler ...OnNameHandler[TCtx]) {
|
||||
h.app.mutex.Lock()
|
||||
h.onName = append(h.onName, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
|
@ -76,7 +76,7 @@ func (h *Hooks) OnName(handler ...OnNameHandler) {
|
|||
|
||||
// OnGroup is a hook to execute user functions on each group registration.
|
||||
// Also you can get group properties by group parameter.
|
||||
func (h *Hooks) OnGroup(handler ...OnGroupHandler) {
|
||||
func (h *Hooks[TCtx]) OnGroup(handler ...OnGroupHandler[TCtx]) {
|
||||
h.app.mutex.Lock()
|
||||
h.onGroup = append(h.onGroup, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
|
@ -86,14 +86,14 @@ func (h *Hooks) OnGroup(handler ...OnGroupHandler) {
|
|||
// Also you can get group properties by group parameter.
|
||||
//
|
||||
// WARN: OnGroupName only works with naming groups, not routes.
|
||||
func (h *Hooks) OnGroupName(handler ...OnGroupNameHandler) {
|
||||
func (h *Hooks[TCtx]) OnGroupName(handler ...OnGroupNameHandler[TCtx]) {
|
||||
h.app.mutex.Lock()
|
||||
h.onGroupName = append(h.onGroupName, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
}
|
||||
|
||||
// OnListen is a hook to execute user functions on Listen, ListenTLS, Listener.
|
||||
func (h *Hooks) OnListen(handler ...OnListenHandler) {
|
||||
func (h *Hooks[TCtx]) OnListen(handler ...OnListenHandler) {
|
||||
h.app.mutex.Lock()
|
||||
h.onListen = append(h.onListen, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
|
@ -107,14 +107,14 @@ func (h *Hooks) OnPreShutdown(handler ...OnPreShutdownHandler) {
|
|||
}
|
||||
|
||||
// OnPostShutdown is a hook to execute user functions after Shutdown.
|
||||
func (h *Hooks) OnPostShutdown(handler ...OnPostShutdownHandler) {
|
||||
func (h *Hooks[TCtx]) OnPostShutdown(handler ...OnPostShutdownHandler) {
|
||||
h.app.mutex.Lock()
|
||||
h.onPostShutdown = append(h.onPostShutdown, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
}
|
||||
|
||||
// OnFork is a hook to execute user function after fork process.
|
||||
func (h *Hooks) OnFork(handler ...OnForkHandler) {
|
||||
func (h *Hooks[TCtx]) OnFork(handler ...OnForkHandler) {
|
||||
h.app.mutex.Lock()
|
||||
h.onFork = append(h.onFork, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
|
@ -123,13 +123,13 @@ func (h *Hooks) OnFork(handler ...OnForkHandler) {
|
|||
// OnMount is a hook to execute user function after mounting process.
|
||||
// The mount event is fired when sub-app is mounted on a parent app. The parent app is passed as a parameter.
|
||||
// It works for app and group mounting.
|
||||
func (h *Hooks) OnMount(handler ...OnMountHandler) {
|
||||
func (h *Hooks[TCtx]) OnMount(handler ...OnMountHandler[TCtx]) {
|
||||
h.app.mutex.Lock()
|
||||
h.onMount = append(h.onMount, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnRouteHooks(route Route) error {
|
||||
func (h *Hooks[TCtx]) executeOnRouteHooks(route Route[TCtx]) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
route.path = h.app.mountFields.mountPath + route.path
|
||||
|
@ -145,7 +145,7 @@ func (h *Hooks) executeOnRouteHooks(route Route) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnNameHooks(route Route) error {
|
||||
func (h *Hooks[TCtx]) executeOnNameHooks(route Route[TCtx]) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
route.path = h.app.mountFields.mountPath + route.path
|
||||
|
@ -161,7 +161,7 @@ func (h *Hooks) executeOnNameHooks(route Route) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnGroupHooks(group Group) error {
|
||||
func (h *Hooks[TCtx]) executeOnGroupHooks(group Group[TCtx]) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
group.Prefix = h.app.mountFields.mountPath + group.Prefix
|
||||
|
@ -176,7 +176,7 @@ func (h *Hooks) executeOnGroupHooks(group Group) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnGroupNameHooks(group Group) error {
|
||||
func (h *Hooks[TCtx]) executeOnGroupNameHooks(group Group[TCtx]) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
group.Prefix = h.app.mountFields.mountPath + group.Prefix
|
||||
|
@ -191,7 +191,7 @@ func (h *Hooks) executeOnGroupNameHooks(group Group) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnListenHooks(listenData ListenData) error {
|
||||
func (h *Hooks[TCtx]) executeOnListenHooks(listenData ListenData) error {
|
||||
for _, v := range h.onListen {
|
||||
if err := v(listenData); err != nil {
|
||||
return err
|
||||
|
@ -201,7 +201,7 @@ func (h *Hooks) executeOnListenHooks(listenData ListenData) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnPreShutdownHooks() {
|
||||
func (h *Hooks[TCtx]) executeOnPreShutdownHooks() {
|
||||
for _, v := range h.onPreShutdown {
|
||||
if err := v(); err != nil {
|
||||
log.Errorf("failed to call pre shutdown hook: %v", err)
|
||||
|
@ -217,7 +217,7 @@ func (h *Hooks) executeOnPostShutdownHooks(err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnForkHooks(pid int) {
|
||||
func (h *Hooks[TCtx]) executeOnForkHooks(pid int) {
|
||||
for _, v := range h.onFork {
|
||||
if err := v(pid); err != nil {
|
||||
log.Errorf("failed to call fork hook: %v", err)
|
||||
|
@ -225,7 +225,7 @@ func (h *Hooks) executeOnForkHooks(pid int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnMountHooks(app *App) error {
|
||||
func (h *Hooks[TCtx]) executeOnMountHooks(app *App[TCtx]) error {
|
||||
for _, v := range h.onMount {
|
||||
if err := v(app); err != nil {
|
||||
return err
|
||||
|
|
26
listen.go
26
listen.go
|
@ -39,7 +39,7 @@ const (
|
|||
)
|
||||
|
||||
// ListenConfig is a struct to customize startup of Fiber.
|
||||
type ListenConfig struct {
|
||||
type ListenConfig[TCtx CtxGeneric[TCtx]] struct {
|
||||
// GracefulContext is a field to shutdown Fiber by given context gracefully.
|
||||
//
|
||||
// Default: nil
|
||||
|
@ -58,7 +58,7 @@ type ListenConfig struct {
|
|||
// BeforeServeFunc allows customizing and accessing fiber app before serving the app.
|
||||
//
|
||||
// Default: nil
|
||||
BeforeServeFunc func(app *App) error `json:"before_serve_func"`
|
||||
BeforeServeFunc func(app *App[TCtx]) error `json:"before_serve_func"`
|
||||
|
||||
// AutoCertManager manages TLS certificates automatically using the ACME protocol,
|
||||
// Enables integration with Let's Encrypt or other ACME-compatible providers.
|
||||
|
@ -120,9 +120,9 @@ type ListenConfig struct {
|
|||
}
|
||||
|
||||
// listenConfigDefault is a function to set default values of ListenConfig.
|
||||
func listenConfigDefault(config ...ListenConfig) ListenConfig {
|
||||
func listenConfigDefault[TCtx CtxGeneric[TCtx]](config ...ListenConfig[TCtx]) ListenConfig[TCtx] {
|
||||
if len(config) < 1 {
|
||||
return ListenConfig{
|
||||
return ListenConfig[TCtx]{
|
||||
TLSMinVersion: tls.VersionTLS12,
|
||||
ListenerNetwork: NetworkTCP4,
|
||||
ShutdownTimeout: 10 * time.Second,
|
||||
|
@ -151,8 +151,8 @@ func listenConfigDefault(config ...ListenConfig) ListenConfig {
|
|||
// app.Listen(":8080")
|
||||
// app.Listen("127.0.0.1:8080")
|
||||
// app.Listen(":8080", ListenConfig{EnablePrefork: true})
|
||||
func (app *App) Listen(addr string, config ...ListenConfig) error {
|
||||
cfg := listenConfigDefault(config...)
|
||||
func (app *App[TCtx]) Listen(addr string, config ...ListenConfig[TCtx]) error {
|
||||
cfg := listenConfigDefault[TCtx](config...)
|
||||
|
||||
// Configure TLS
|
||||
var tlsConfig *tls.Config
|
||||
|
@ -238,7 +238,7 @@ func (app *App) Listen(addr string, config ...ListenConfig) error {
|
|||
|
||||
// Listener serves HTTP requests from the given listener.
|
||||
// You should enter custom ListenConfig to customize startup. (prefork, startup message, graceful shutdown...)
|
||||
func (app *App) Listener(ln net.Listener, config ...ListenConfig) error {
|
||||
func (app *App[TCtx]) Listener(ln net.Listener, config ...ListenConfig[TCtx]) error {
|
||||
cfg := listenConfigDefault(config...)
|
||||
|
||||
// Graceful shutdown
|
||||
|
@ -274,7 +274,7 @@ func (app *App) Listener(ln net.Listener, config ...ListenConfig) error {
|
|||
}
|
||||
|
||||
// Create listener function.
|
||||
func (*App) createListener(addr string, tlsConfig *tls.Config, cfg ListenConfig) (net.Listener, error) {
|
||||
func (*App[TCtx]) createListener(addr string, tlsConfig *tls.Config, cfg ListenConfig[TCtx]) (net.Listener, error) {
|
||||
var listener net.Listener
|
||||
var err error
|
||||
|
||||
|
@ -297,7 +297,7 @@ func (*App) createListener(addr string, tlsConfig *tls.Config, cfg ListenConfig)
|
|||
return listener, nil
|
||||
}
|
||||
|
||||
func (app *App) printMessages(cfg ListenConfig, ln net.Listener) {
|
||||
func (app *App[TCtx]) printMessages(cfg ListenConfig[TCtx], ln net.Listener) {
|
||||
// Print startup message
|
||||
if !cfg.DisableStartupMessage {
|
||||
app.startupMessage(ln.Addr().String(), getTLSConfig(ln) != nil, "", cfg)
|
||||
|
@ -310,7 +310,7 @@ func (app *App) printMessages(cfg ListenConfig, ln net.Listener) {
|
|||
}
|
||||
|
||||
// prepareListenData create an slice of ListenData
|
||||
func (*App) prepareListenData(addr string, isTLS bool, cfg ListenConfig) ListenData { //revive:disable-line:flag-parameter // Accepting a bool param named isTLS if fine here
|
||||
func (*App[TCtx]) prepareListenData(addr string, isTLS bool, cfg ListenConfig[TCtx]) ListenData { //revive:disable-line:flag-parameter // Accepting a bool param named isTLS if fine here
|
||||
host, port := parseAddr(addr)
|
||||
if host == "" {
|
||||
if cfg.ListenerNetwork == NetworkTCP6 {
|
||||
|
@ -328,7 +328,7 @@ func (*App) prepareListenData(addr string, isTLS bool, cfg ListenConfig) ListenD
|
|||
}
|
||||
|
||||
// startupMessage prepares the startup message with the handler number, port, address and other information
|
||||
func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenConfig) { //nolint:revive // Accepting a bool param named isTLS if fine here
|
||||
func (app *App[TCtx]) startupMessage(addr string, isTLS bool, pids string, cfg ListenConfig[TCtx]) { //nolint:revive // Accepting a bool param named isTLS if fine here
|
||||
// ignore child processes
|
||||
if IsChild() {
|
||||
return
|
||||
|
@ -436,7 +436,7 @@ func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenC
|
|||
// method | path | name | handlers
|
||||
// GET | / | routeName | github.com/gofiber/fiber/v3.emptyHandler
|
||||
// HEAD | / | | github.com/gofiber/fiber/v3.emptyHandler
|
||||
func (app *App) printRoutesMessage() {
|
||||
func (app *App[TCtx]) printRoutesMessage() {
|
||||
// ignore child processes
|
||||
if IsChild() {
|
||||
return
|
||||
|
@ -481,7 +481,7 @@ func (app *App) printRoutesMessage() {
|
|||
}
|
||||
|
||||
// shutdown goroutine
|
||||
func (app *App) gracefulShutdown(ctx context.Context, cfg ListenConfig) {
|
||||
func (app *App[TCtx]) gracefulShutdown(ctx context.Context, cfg ListenConfig[TCtx]) {
|
||||
<-ctx.Done()
|
||||
|
||||
var err error
|
||||
|
|
|
@ -399,9 +399,9 @@ func Test_Listen_BeforeServeFunc(t *testing.T) {
|
|||
}()
|
||||
|
||||
wantErr := errors.New("test")
|
||||
require.ErrorIs(t, app.Listen(":0", ListenConfig{
|
||||
require.ErrorIs(t, app.Listen(":0", ListenConfig[*DefaultCtx]{
|
||||
DisableStartupMessage: true,
|
||||
BeforeServeFunc: func(fiber *App) error {
|
||||
BeforeServeFunc: func(fiber *App[*DefaultCtx]) error {
|
||||
handlers = fiber.HandlersCount()
|
||||
|
||||
return wantErr
|
||||
|
|
34
mount.go
34
mount.go
|
@ -13,9 +13,9 @@ import (
|
|||
)
|
||||
|
||||
// Put fields related to mounting.
|
||||
type mountFields struct {
|
||||
type mountFields[TCtx CtxGeneric[TCtx]] struct {
|
||||
// Mounted and main apps
|
||||
appList map[string]*App
|
||||
appList map[string]*App[TCtx]
|
||||
// Prefix of app if it was mounted
|
||||
mountPath string
|
||||
// Ordered keys of apps (sorted by key length for Render)
|
||||
|
@ -27,9 +27,9 @@ type mountFields struct {
|
|||
}
|
||||
|
||||
// Create empty mountFields instance
|
||||
func newMountFields(app *App) *mountFields {
|
||||
return &mountFields{
|
||||
appList: map[string]*App{"": app},
|
||||
func newMountFields[TCtx CtxGeneric[TCtx]](app *App[TCtx]) *mountFields[TCtx] {
|
||||
return &mountFields[TCtx]{
|
||||
appList: map[string]*App[TCtx]{"": app},
|
||||
appListKeys: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func newMountFields(app *App) *mountFields {
|
|||
// compose them as a single service using Mount. The fiber's error handler and
|
||||
// any of the fiber's sub apps are added to the application's error handlers
|
||||
// to be invoked on errors that happen within the prefix route.
|
||||
func (app *App) mount(prefix string, subApp *App) Router {
|
||||
func (app *App[TCtx]) mount(prefix string, subApp *App[TCtx]) Router[TCtx] {
|
||||
prefix = utils.TrimRight(prefix, '/')
|
||||
if prefix == "" {
|
||||
prefix = "/"
|
||||
|
@ -54,7 +54,7 @@ func (app *App) mount(prefix string, subApp *App) Router {
|
|||
}
|
||||
|
||||
// register mounted group
|
||||
mountGroup := &Group{Prefix: prefix, app: subApp}
|
||||
mountGroup := &Group[TCtx]{Prefix: prefix, app: subApp}
|
||||
app.register([]string{methodUse}, prefix, mountGroup)
|
||||
|
||||
// Execute onMount hooks
|
||||
|
@ -68,7 +68,7 @@ func (app *App) mount(prefix string, subApp *App) Router {
|
|||
// Mount attaches another app instance as a sub-router along a routing path.
|
||||
// It's very useful to split up a large API as many independent routers and
|
||||
// compose them as a single service using Mount.
|
||||
func (grp *Group) mount(prefix string, subApp *App) Router {
|
||||
func (grp *Group[TCtx]) mount(prefix string, subApp *App[TCtx]) Router[TCtx] {
|
||||
groupPath := getGroupPath(grp.Prefix, prefix)
|
||||
groupPath = utils.TrimRight(groupPath, '/')
|
||||
if groupPath == "" {
|
||||
|
@ -84,7 +84,7 @@ func (grp *Group) mount(prefix string, subApp *App) Router {
|
|||
}
|
||||
|
||||
// register mounted group
|
||||
mountGroup := &Group{Prefix: groupPath, app: subApp}
|
||||
mountGroup := &Group[TCtx]{Prefix: groupPath, app: subApp}
|
||||
grp.app.register([]string{methodUse}, groupPath, mountGroup)
|
||||
|
||||
// Execute onMount hooks
|
||||
|
@ -96,17 +96,17 @@ func (grp *Group) mount(prefix string, subApp *App) Router {
|
|||
}
|
||||
|
||||
// The MountPath property contains one or more path patterns on which a sub-app was mounted.
|
||||
func (app *App) MountPath() string {
|
||||
func (app *App[TCtx]) MountPath() string {
|
||||
return app.mountFields.mountPath
|
||||
}
|
||||
|
||||
// hasMountedApps Checks if there are any mounted apps in the current application.
|
||||
func (app *App) hasMountedApps() bool {
|
||||
func (app *App[TCtx]) hasMountedApps() bool {
|
||||
return len(app.mountFields.appList) > 1
|
||||
}
|
||||
|
||||
// mountStartupProcess Handles the startup process of mounted apps by appending sub-app routes, generating app list keys, and processing sub-app routes.
|
||||
func (app *App) mountStartupProcess() {
|
||||
func (app *App[TCtx]) mountStartupProcess() {
|
||||
if app.hasMountedApps() {
|
||||
// add routes of sub-apps
|
||||
app.mountFields.subAppsProcessed.Do(func() {
|
||||
|
@ -121,7 +121,7 @@ func (app *App) mountStartupProcess() {
|
|||
}
|
||||
|
||||
// generateAppListKeys generates app list keys for Render, should work after appendSubAppLists
|
||||
func (app *App) generateAppListKeys() {
|
||||
func (app *App[TCtx]) generateAppListKeys() {
|
||||
for key := range app.mountFields.appList {
|
||||
app.mountFields.appListKeys = append(app.mountFields.appListKeys, key)
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ func (app *App) generateAppListKeys() {
|
|||
}
|
||||
|
||||
// appendSubAppLists supports nested for sub apps
|
||||
func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) {
|
||||
func (app *App[TCtx]) appendSubAppLists(appList map[string]*App[TCtx], parent ...string) {
|
||||
// Optimize: Cache parent prefix
|
||||
parentPrefix := ""
|
||||
if len(parent) > 0 {
|
||||
|
@ -161,7 +161,7 @@ func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) {
|
|||
}
|
||||
|
||||
// processSubAppsRoutes adds routes of sub-apps recursively when the server is started
|
||||
func (app *App) processSubAppsRoutes() {
|
||||
func (app *App[TCtx]) processSubAppsRoutes() {
|
||||
for prefix, subApp := range app.mountFields.appList {
|
||||
// skip real app
|
||||
if prefix == "" {
|
||||
|
@ -194,7 +194,7 @@ func (app *App) processSubAppsRoutes() {
|
|||
}
|
||||
|
||||
// Create a slice to hold the sub-app's routes
|
||||
subRoutes := make([]*Route, len(route.group.app.stack[m]))
|
||||
subRoutes := make([]*Route[TCtx], len(route.group.app.stack[m]))
|
||||
|
||||
// Iterate over the sub-app's routes
|
||||
for j, subAppRoute := range route.group.app.stack[m] {
|
||||
|
@ -209,7 +209,7 @@ func (app *App) processSubAppsRoutes() {
|
|||
}
|
||||
|
||||
// Insert the sub-app's routes into the parent app's stack
|
||||
newStack := make([]*Route, len(app.stack[m])+len(subRoutes)-1)
|
||||
newStack := make([]*Route[TCtx], len(app.stack[m])+len(subRoutes)-1)
|
||||
copy(newStack[:i], app.stack[m][:i])
|
||||
copy(newStack[i:i+len(subRoutes)], subRoutes)
|
||||
copy(newStack[i+len(subRoutes):], app.stack[m][i+1:])
|
||||
|
|
|
@ -35,7 +35,7 @@ func IsChild() bool {
|
|||
}
|
||||
|
||||
// prefork manages child processes to make use of the OS REUSEPORT or REUSEADDR feature
|
||||
func (app *App) prefork(addr string, tlsConfig *tls.Config, cfg ListenConfig) error {
|
||||
func (app *App[TCtx]) prefork(addr string, tlsConfig *tls.Config, cfg ListenConfig[TCtx]) error {
|
||||
var ln net.Listener
|
||||
var err error
|
||||
|
||||
|
|
58
register.go
58
register.go
|
@ -5,28 +5,28 @@
|
|||
package fiber
|
||||
|
||||
// Register defines all router handle interface generate by Route().
|
||||
type Register interface {
|
||||
All(handler Handler, handlers ...Handler) Register
|
||||
Get(handler Handler, handlers ...Handler) Register
|
||||
Head(handler Handler, handlers ...Handler) Register
|
||||
Post(handler Handler, handlers ...Handler) Register
|
||||
Put(handler Handler, handlers ...Handler) Register
|
||||
Delete(handler Handler, handlers ...Handler) Register
|
||||
Connect(handler Handler, handlers ...Handler) Register
|
||||
Options(handler Handler, handlers ...Handler) Register
|
||||
Trace(handler Handler, handlers ...Handler) Register
|
||||
Patch(handler Handler, handlers ...Handler) Register
|
||||
type Register[TCtx CtxGeneric[TCtx]] interface {
|
||||
All(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Get(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Head(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Post(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Put(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Delete(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Connect(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Options(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Trace(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
Patch(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
|
||||
Add(methods []string, handler Handler, handlers ...Handler) Register
|
||||
Add(methods []string, handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx]
|
||||
|
||||
Route(path string) Register
|
||||
Route(path string) Register[TCtx]
|
||||
}
|
||||
|
||||
var _ (Register) = (*Registering)(nil)
|
||||
var _ Register[*DefaultCtx] = (*Registering[*DefaultCtx])(nil)
|
||||
|
||||
// Registering struct
|
||||
type Registering struct {
|
||||
app *App
|
||||
type Registering[TCtx CtxGeneric[TCtx]] struct {
|
||||
app *App[TCtx]
|
||||
|
||||
path string
|
||||
}
|
||||
|
@ -45,76 +45,76 @@ type Registering struct {
|
|||
// })
|
||||
//
|
||||
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
|
||||
func (r *Registering) All(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) All(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
r.app.register([]string{methodUse}, r.path, nil, append([]Handler{handler}, handlers...)...)
|
||||
return r
|
||||
}
|
||||
|
||||
// Get registers a route for GET methods that requests a representation
|
||||
// of the specified resource. Requests using GET should only retrieve data.
|
||||
func (r *Registering) Get(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Get(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
r.app.Add([]string{MethodGet}, r.path, handler, handlers...)
|
||||
return r
|
||||
}
|
||||
|
||||
// Head registers a route for HEAD methods that asks for a response identical
|
||||
// to that of a GET request, but without the response body.
|
||||
func (r *Registering) Head(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Head(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodHead}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Post registers a route for POST methods that is used to submit an entity to the
|
||||
// specified resource, often causing a change in state or side effects on the server.
|
||||
func (r *Registering) Post(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Post(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodPost}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Put registers a route for PUT methods that replaces all current representations
|
||||
// of the target resource with the request payload.
|
||||
func (r *Registering) Put(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Put(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodPut}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Delete registers a route for DELETE methods that deletes the specified resource.
|
||||
func (r *Registering) Delete(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Delete(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodDelete}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Connect registers a route for CONNECT methods that establishes a tunnel to the
|
||||
// server identified by the target resource.
|
||||
func (r *Registering) Connect(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Connect(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodConnect}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Options registers a route for OPTIONS methods that is used to describe the
|
||||
// communication options for the target resource.
|
||||
func (r *Registering) Options(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Options(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodOptions}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Trace registers a route for TRACE methods that performs a message loop-back
|
||||
// test along the r.Path to the target resource.
|
||||
func (r *Registering) Trace(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Trace(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodTrace}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Patch registers a route for PATCH methods that is used to apply partial
|
||||
// modifications to a resource.
|
||||
func (r *Registering) Patch(handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Patch(handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
return r.Add([]string{MethodPatch}, handler, handlers...)
|
||||
}
|
||||
|
||||
// Add allows you to specify multiple HTTP methods to register a route.
|
||||
func (r *Registering) Add(methods []string, handler Handler, handlers ...Handler) Register {
|
||||
func (r *Registering[TCtx]) Add(methods []string, handler Handler[TCtx], handlers ...Handler[TCtx]) Register[TCtx] {
|
||||
r.app.register(methods, r.path, nil, append([]Handler{handler}, handlers...)...)
|
||||
return r
|
||||
}
|
||||
|
||||
// Route returns a new Register instance whose route path takes
|
||||
// the path in the current instance as its prefix.
|
||||
func (r *Registering) Route(path string) Register {
|
||||
func (r *Registering[TCtx]) Route(path string) Register[TCtx] {
|
||||
// Create new group
|
||||
route := &Registering{app: r.app, path: getGroupPath(r.path, path)}
|
||||
route := &Registering[TCtx]{app: r.app, path: getGroupPath(r.path, path)}
|
||||
|
||||
return route
|
||||
}
|
||||
|
|
171
router.go
171
router.go
|
@ -6,9 +6,7 @@ package fiber
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
|
@ -17,33 +15,33 @@ import (
|
|||
)
|
||||
|
||||
// Router defines all router handle interface, including app and group router.
|
||||
type Router interface {
|
||||
Use(args ...any) Router
|
||||
type Router[TCtx CtxGeneric[TCtx]] interface {
|
||||
Use(args ...any) Router[TCtx]
|
||||
|
||||
Get(path string, handler Handler, handlers ...Handler) Router
|
||||
Head(path string, handler Handler, handlers ...Handler) Router
|
||||
Post(path string, handler Handler, handlers ...Handler) Router
|
||||
Put(path string, handler Handler, handlers ...Handler) Router
|
||||
Delete(path string, handler Handler, handlers ...Handler) Router
|
||||
Connect(path string, handler Handler, handlers ...Handler) Router
|
||||
Options(path string, handler Handler, handlers ...Handler) Router
|
||||
Trace(path string, handler Handler, handlers ...Handler) Router
|
||||
Patch(path string, handler Handler, handlers ...Handler) Router
|
||||
Get(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Head(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Post(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Put(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Delete(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Connect(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Options(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Trace(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
Patch(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
|
||||
Add(methods []string, path string, handler Handler, handlers ...Handler) Router
|
||||
All(path string, handler Handler, handlers ...Handler) Router
|
||||
Add(methods []string, path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
All(path string, handler Handler[TCtx], handlers ...Handler[TCtx]) Router[TCtx]
|
||||
|
||||
Group(prefix string, handlers ...Handler) Router
|
||||
Group(prefix string, handlers ...Handler[TCtx]) Router[TCtx]
|
||||
|
||||
Route(path string) Register
|
||||
Route(path string) Register[TCtx]
|
||||
|
||||
Name(name string) Router
|
||||
Name(name string) Router[TCtx]
|
||||
}
|
||||
|
||||
// Route is a struct that holds all metadata for each registered handler.
|
||||
type Route struct {
|
||||
type Route[TCtx CtxGeneric[TCtx]] struct {
|
||||
// ### important: always keep in sync with the copy method "app.copyRoute" ###
|
||||
group *Group // Group instance. used for routes in groups
|
||||
group *Group[TCtx] // Group instance. used for routes in groups
|
||||
|
||||
path string // Prettified path
|
||||
|
||||
|
@ -51,10 +49,10 @@ type Route struct {
|
|||
Method string `json:"method"` // HTTP method
|
||||
Name string `json:"name"` // Route's name
|
||||
//nolint:revive // Having both a Path (uppercase) and a path (lowercase) is fine
|
||||
Path string `json:"path"` // Original registered route path
|
||||
Params []string `json:"params"` // Case-sensitive param keys
|
||||
Handlers []Handler `json:"-"` // Ctx handlers
|
||||
routeParser routeParser // Parameter parser
|
||||
Path string `json:"path"` // Original registered route path
|
||||
Params []string `json:"params"` // Case-sensitive param keys
|
||||
Handlers []Handler[TCtx] `json:"-"` // Ctx handlers
|
||||
routeParser routeParser // Parameter parser
|
||||
// Data for routing
|
||||
pos uint32 // Position in stack -> important for the sort of the matched routes
|
||||
use bool // USE matches path prefixes
|
||||
|
@ -63,7 +61,7 @@ type Route struct {
|
|||
root bool // Path equals '/'
|
||||
}
|
||||
|
||||
func (r *Route) match(detectionPath, path string, params *[maxParams]string) bool {
|
||||
func (r *Route[TCtx]) match(detectionPath, path string, params *[maxParams]string) bool {
|
||||
// root detectionPath check
|
||||
if r.root && len(detectionPath) == 1 && detectionPath[0] == '/' {
|
||||
return true
|
||||
|
@ -108,7 +106,7 @@ func (r *Route) match(detectionPath, path string, params *[maxParams]string) boo
|
|||
return false
|
||||
}
|
||||
|
||||
func (app *App) nextCustom(c CustomCtx) (bool, error) { //nolint:unparam // bool param might be useful for testing
|
||||
func (app *App[TCtx]) next(c TCtx) (bool, error) { //nolint:unparam // bool param might be useful for testing
|
||||
// Get stack length
|
||||
tree, ok := app.treeStack[c.getMethodInt()][c.getTreePathHash()]
|
||||
if !ok {
|
||||
|
@ -146,7 +144,7 @@ func (app *App) nextCustom(c CustomCtx) (bool, error) { //nolint:unparam // bool
|
|||
}
|
||||
|
||||
// If c.Next() does not match, return 404
|
||||
err := NewError(StatusNotFound, "Cannot "+c.Method()+" "+c.getPathOriginal())
|
||||
err := NewError(StatusNotFound, "Cannot "+c.Method()+" "+html.EscapeString(c.getPathOriginal()))
|
||||
|
||||
// If no match, scan stack again if other methods match the request
|
||||
// Moved from app.handler because middleware may break the route chain
|
||||
|
@ -156,98 +154,9 @@ func (app *App) nextCustom(c CustomCtx) (bool, error) { //nolint:unparam // bool
|
|||
return false, err
|
||||
}
|
||||
|
||||
func (app *App) next(c *DefaultCtx) (bool, error) {
|
||||
// Get stack length
|
||||
tree, ok := app.treeStack[c.methodInt][c.treePathHash]
|
||||
if !ok {
|
||||
tree = app.treeStack[c.methodInt][0]
|
||||
}
|
||||
lenTree := len(tree) - 1
|
||||
|
||||
// Loop over the route stack starting from previous index
|
||||
for c.indexRoute < lenTree {
|
||||
// Increment route index
|
||||
c.indexRoute++
|
||||
|
||||
// Get *Route
|
||||
route := tree[c.indexRoute]
|
||||
|
||||
var match bool
|
||||
var err error
|
||||
// skip for mounted apps
|
||||
if route.mount {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if it matches the request path
|
||||
match = route.match(utils.UnsafeString(c.detectionPath), utils.UnsafeString(c.path), &c.values)
|
||||
if !match {
|
||||
// No match, next route
|
||||
continue
|
||||
}
|
||||
// Pass route reference and param values
|
||||
c.route = route
|
||||
|
||||
// Non use handler matched
|
||||
if !c.matched && !route.use {
|
||||
c.matched = true
|
||||
}
|
||||
|
||||
// Execute first handler of route
|
||||
c.indexHandler = 0
|
||||
if len(route.Handlers) > 0 {
|
||||
err = route.Handlers[0](c)
|
||||
}
|
||||
return match, err // Stop scanning the stack
|
||||
}
|
||||
|
||||
// If c.Next() does not match, return 404
|
||||
err := NewError(StatusNotFound, "Cannot "+c.Method()+" "+html.EscapeString(c.pathOriginal))
|
||||
if !c.matched && app.methodExist(c) {
|
||||
// If no match, scan stack again if other methods match the request
|
||||
// Moved from app.handler because middleware may break the route chain
|
||||
err = ErrMethodNotAllowed
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (app *App) defaultRequestHandler(rctx *fasthttp.RequestCtx) {
|
||||
func (app *App[TCtx]) requestHandler(rctx *fasthttp.RequestCtx) {
|
||||
// Acquire DefaultCtx from the pool
|
||||
ctx, ok := app.AcquireCtx(rctx).(*DefaultCtx)
|
||||
if !ok {
|
||||
panic(errors.New("requestHandler: failed to type-assert to *DefaultCtx"))
|
||||
}
|
||||
|
||||
defer app.ReleaseCtx(ctx)
|
||||
|
||||
// Check if the HTTP method is valid
|
||||
if ctx.methodInt == -1 {
|
||||
_ = ctx.SendStatus(StatusNotImplemented) //nolint:errcheck // Always return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Optional: Check flash messages
|
||||
rawHeaders := ctx.Request().Header.RawHeaders()
|
||||
if len(rawHeaders) > 0 && bytes.Contains(rawHeaders, []byte(FlashCookieName)) {
|
||||
ctx.Redirect().parseAndClearFlashMessages()
|
||||
}
|
||||
|
||||
// Attempt to match a route and execute the chain
|
||||
_, err := app.next(ctx)
|
||||
if err != nil {
|
||||
if catch := ctx.App().ErrorHandler(ctx, err); catch != nil {
|
||||
_ = ctx.SendStatus(StatusInternalServerError) //nolint:errcheck // Always return nil
|
||||
}
|
||||
// TODO: Do we need to return here?
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) customRequestHandler(rctx *fasthttp.RequestCtx) {
|
||||
// Acquire CustomCtx from the pool
|
||||
ctx, ok := app.AcquireCtx(rctx).(CustomCtx)
|
||||
if !ok {
|
||||
panic(errors.New("requestHandler: failed to type-assert to CustomCtx"))
|
||||
}
|
||||
ctx := app.AcquireCtx(rctx)
|
||||
|
||||
defer app.ReleaseCtx(ctx)
|
||||
|
||||
|
@ -264,16 +173,16 @@ func (app *App) customRequestHandler(rctx *fasthttp.RequestCtx) {
|
|||
}
|
||||
|
||||
// Attempt to match a route and execute the chain
|
||||
_, err := app.nextCustom(ctx)
|
||||
_, err := app.next(ctx)
|
||||
if err != nil {
|
||||
if catch := ctx.App().ErrorHandler(ctx, err); catch != nil {
|
||||
if catch := app.ErrorHandler(ctx, err); catch != nil {
|
||||
_ = ctx.SendStatus(StatusInternalServerError) //nolint:errcheck // Always return nil
|
||||
}
|
||||
// TODO: Do we need to return here?
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) addPrefixToRoute(prefix string, route *Route) *Route {
|
||||
func (app *App[TCtx]) addPrefixToRoute(prefix string, route *Route[TCtx]) *Route[TCtx] {
|
||||
prefixedPath := getGroupPath(prefix, route.Path)
|
||||
prettyPath := prefixedPath
|
||||
// Case-sensitive routing, all to lowercase
|
||||
|
@ -294,8 +203,8 @@ func (app *App) addPrefixToRoute(prefix string, route *Route) *Route {
|
|||
return route
|
||||
}
|
||||
|
||||
func (*App) copyRoute(route *Route) *Route {
|
||||
return &Route{
|
||||
func (*App[TCtx]) copyRoute(route *Route[TCtx]) *Route[TCtx] {
|
||||
return &Route[TCtx]{
|
||||
// Router booleans
|
||||
use: route.use,
|
||||
mount: route.mount,
|
||||
|
@ -318,7 +227,7 @@ func (*App) copyRoute(route *Route) *Route {
|
|||
}
|
||||
}
|
||||
|
||||
func (app *App) register(methods []string, pathRaw string, group *Group, handlers ...Handler) {
|
||||
func (app *App[TCtx]) register(methods []string, pathRaw string, group *Group[TCtx], handlers ...Handler[TCtx]) {
|
||||
// A regular route requires at least one ctx handler
|
||||
if len(handlers) == 0 && group == nil {
|
||||
panic(fmt.Sprintf("missing handler/middleware in route: %s\n", pathRaw))
|
||||
|
@ -361,7 +270,7 @@ func (app *App) register(methods []string, pathRaw string, group *Group, handler
|
|||
isStar := pathClean == "/*"
|
||||
isRoot := pathClean == "/"
|
||||
|
||||
route := Route{
|
||||
route := Route[TCtx]{
|
||||
use: isUse,
|
||||
mount: isMount,
|
||||
star: isStar,
|
||||
|
@ -395,7 +304,7 @@ func (app *App) register(methods []string, pathRaw string, group *Group, handler
|
|||
}
|
||||
}
|
||||
|
||||
func (app *App) addRoute(method string, route *Route, isMounted ...bool) {
|
||||
func (app *App[TCtx]) addRoute(method string, route *Route[TCtx], isMounted ...bool) {
|
||||
app.mutex.Lock()
|
||||
defer app.mutex.Unlock()
|
||||
|
||||
|
@ -431,7 +340,7 @@ func (app *App) addRoute(method string, route *Route, isMounted ...bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// BuildTree rebuilds the prefix tree from the previously registered routes.
|
||||
// RebuildTree BuildTree rebuilds the prefix tree from the previously registered routes.
|
||||
// This method is useful when you want to register routes dynamically after the app has started.
|
||||
// It is not recommended to use this method on production environments because rebuilding
|
||||
// the tree is performance-intensive and not thread-safe in runtime. Since building the tree
|
||||
|
@ -439,7 +348,7 @@ func (app *App) addRoute(method string, route *Route, isMounted ...bool) {
|
|||
// routeTree is being safely changed, as it would add a great deal of overhead in the request.
|
||||
// Latest benchmark results showed a degradation from 82.79 ns/op to 94.48 ns/op and can be found in:
|
||||
// https://github.com/gofiber/fiber/issues/2769#issuecomment-2227385283
|
||||
func (app *App) RebuildTree() *App {
|
||||
func (app *App[TCtx]) RebuildTree() *App[TCtx] {
|
||||
app.mutex.Lock()
|
||||
defer app.mutex.Unlock()
|
||||
|
||||
|
@ -447,14 +356,14 @@ func (app *App) RebuildTree() *App {
|
|||
}
|
||||
|
||||
// buildTree build the prefix tree from the previously registered routes
|
||||
func (app *App) buildTree() *App {
|
||||
func (app *App[TCtx]) buildTree() *App[TCtx] {
|
||||
if !app.routesRefreshed {
|
||||
return app
|
||||
}
|
||||
|
||||
// loop all the methods and stacks and create the prefix tree
|
||||
for m := range app.config.RequestMethods {
|
||||
tsMap := make(map[int][]*Route)
|
||||
tsMap := make(map[int][]*Route[TCtx])
|
||||
for _, route := range app.stack[m] {
|
||||
treePathHash := 0
|
||||
if len(route.routeParser.segs) > 0 && len(route.routeParser.segs[0].Const) >= maxDetectionPaths {
|
||||
|
@ -469,7 +378,7 @@ func (app *App) buildTree() *App {
|
|||
for treePart := range tsMap {
|
||||
if treePart != 0 {
|
||||
// merge global tree routes in current tree stack
|
||||
tsMap[treePart] = uniqueRouteStack(append(tsMap[treePart], tsMap[0]...))
|
||||
tsMap[treePart] = uniqueRouteStack[TCtx](append(tsMap[treePart], tsMap[0]...))
|
||||
}
|
||||
// sort tree slices with the positions
|
||||
slc := tsMap[treePart]
|
||||
|
|
|
@ -442,7 +442,7 @@ func Test_App_Rebuild_Tree(t *testing.T) {
|
|||
///////////////// BENCHMARKS /////////////////
|
||||
//////////////////////////////////////////////
|
||||
|
||||
func registerDummyRoutes(app *App) {
|
||||
func registerDummyRoutes(app *App[TCtx]) {
|
||||
h := func(_ Ctx) error {
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue