Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feature: add initial support for hooks #1777

Merged
merged 30 commits into from Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b0933d1
Add initial support for hooks.
efectn Feb 14, 2022
b43788d
release ctx, mutex.
efectn Feb 17, 2022
76a2e45
Add unit tests.
efectn Feb 18, 2022
c61eac9
add comment lines.
efectn Feb 18, 2022
124671b
update
efectn Feb 18, 2022
7292511
update
efectn Feb 18, 2022
649bb6c
Merge branch 'gofiber:master' into add-hooks-support
efectn Feb 19, 2022
2faecaa
remove unnecessary code.
efectn Feb 19, 2022
2e18ea0
Merge branch 'add-hooks-support' of https://github.com/efectn/fiber i…
efectn Feb 19, 2022
429ee79
fix race condition.
efectn Feb 19, 2022
8a04452
fix gosec.
efectn Feb 20, 2022
936a260
skip error handling for onshutdown and onresponse.
efectn Feb 20, 2022
495e722
update
efectn Feb 20, 2022
2d8dce2
separate hooks from app.go
efectn Feb 21, 2022
ca72c2c
make hooks field private, hook struct public and Hooks() func.
efectn Feb 23, 2022
7dd783b
remove onreq and onres because of they can be done by middlewares.
efectn Feb 25, 2022
5b6073a
OnGroupName method.
efectn Feb 25, 2022
a9605c3
Update hooks.go
efectn Feb 26, 2022
57de317
handle errors for name and groupname
efectn Feb 28, 2022
401e4c6
Merge branch 'add-hooks-support' of https://github.com/efectn/fiber i…
efectn Feb 28, 2022
bf99b34
fix tests.
efectn Feb 28, 2022
02fa494
Update app.go
hi019 Mar 2, 2022
1e4138e
use struct fields instead of map
efectn Mar 2, 2022
8efce6f
Merge branch 'add-hooks-support' of https://github.com/efectn/fiber i…
efectn Mar 2, 2022
4bb277e
add multi-handler.
efectn Mar 2, 2022
fecaa73
add onGroup, make prefix field public on Group struct.
efectn Mar 2, 2022
3dbf49b
Update hooks.go
efectn Mar 5, 2022
0223ed1
add newhooks method.
efectn Mar 7, 2022
8d9f0dc
✨ feature: add initial support for hooks
ReneWerner87 Mar 8, 2022
34592bc
remove ctx from hooks.
efectn Mar 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 39 additions & 18 deletions app.go
Expand Up @@ -111,9 +111,13 @@ type App struct {
getBytes func(s string) (b []byte)
// Converts byte slice to a string
getString func(b []byte) string

// Mounted and main apps
appList map[string]*App
// Hooks
hooks Hooks
// latest route & group
hi019 marked this conversation as resolved.
Show resolved Hide resolved
latestRoute *Route
latestGroup *Group
}

// Config is a struct holding the server settings.
Expand Down Expand Up @@ -423,14 +427,6 @@ const (
DefaultCompressedFileSuffix = ".fiber.gz"
)

// Variables for Name & GetRoute
var latestRoute struct {
route *Route
mu sync.Mutex
}

var latestGroup Group

// DefaultErrorHandler that process return errors from handlers
var DefaultErrorHandler = func(c *Ctx, err error) error {
code := StatusInternalServerError
Expand Down Expand Up @@ -461,11 +457,20 @@ func New(config ...Config) *App {
},
},
// Create config
config: Config{},
getBytes: utils.UnsafeBytes,
getString: utils.UnsafeString,
appList: make(map[string]*App),
config: Config{},
getBytes: utils.UnsafeBytes,
getString: utils.UnsafeString,
appList: make(map[string]*App),
latestRoute: &Route{},
latestGroup: &Group{},
}

// Define hooks
app.hooks = Hooks{
efectn marked this conversation as resolved.
Show resolved Hide resolved
app: app,
hookList: make(map[string][]HookHandler),
}

// Override config if provided
if len(config) > 0 {
app.config = config[0]
Expand Down Expand Up @@ -569,13 +574,18 @@ func (app *App) Mount(prefix string, fiber *App) Router {

// Assign name to specific route.
func (app *App) Name(name string) Router {
latestRoute.mu.Lock()
if strings.HasPrefix(latestRoute.route.path, latestGroup.prefix) {
latestRoute.route.Name = latestGroup.name + name
app.mutex.Lock()
if strings.HasPrefix(app.latestRoute.path, app.latestGroup.prefix) {
app.latestRoute.Name = app.latestGroup.name + name
} else {
latestRoute.route.Name = name
app.latestRoute.Name = name
}
latestRoute.mu.Unlock()

if err := app.hooks.executeOnNameHooks(*app.latestRoute); err != nil {
panic(err)
}
app.mutex.Unlock()

return app
}

Expand Down Expand Up @@ -859,6 +869,8 @@ func (app *App) HandlersCount() uint32 {
//
// Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
func (app *App) Shutdown() error {
defer app.hooks.executeOnShutdownHooks()

app.mutex.Lock()
defer app.mutex.Unlock()
if app.server == nil {
Expand All @@ -872,6 +884,11 @@ func (app *App) Server() *fasthttp.Server {
return app.server
}

// Hooks returns the hook struct to register hooks.
func (app *App) Hooks() *Hooks {
return &app.hooks
}

// Test is used for internal debugging by passing a *http.Request.
// Timeout is optional and defaults to 1s, -1 will disable it completely.
func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, err error) {
Expand Down Expand Up @@ -1038,6 +1055,10 @@ 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 {
if err := app.hooks.executeOnListenHooks(); err != nil {
panic(err)
}

app.mutex.Lock()
app.buildTree()
app.mutex.Unlock()
Expand Down
12 changes: 9 additions & 3 deletions group.go
Expand Up @@ -46,13 +46,19 @@ func (grp *Group) Mount(prefix string, fiber *App) Router {

// Assign name to specific route.
func (grp *Group) Name(name string) Router {
if strings.HasPrefix(grp.prefix, latestGroup.prefix) {
grp.name = latestGroup.name + name
grp.app.mutex.Lock()
if strings.HasPrefix(grp.prefix, grp.app.latestGroup.prefix) {
grp.name = grp.app.latestGroup.name + name
} else {
grp.name = name
}

latestGroup = *grp
grp.app.latestGroup = grp

if err := grp.app.hooks.executeOnGroupNameHooks(*grp.app.latestGroup); err != nil {
panic(err)
}
grp.app.mutex.Unlock()

return grp
}
Expand Down
118 changes: 118 additions & 0 deletions hooks.go
@@ -0,0 +1,118 @@
package fiber

import (
"github.com/valyala/fasthttp"
)

// Handler defines a function to create hooks for Fiber.
type HookHandler = func(*Ctx, Map) error

type Hooks struct {
app *App
hookList map[string][]HookHandler
}

// OnRoute is a hook to execute user functions on each route registeration.
// Also you can get route properties by "route" key of map.
func (h *Hooks) OnRoute(handler ...HookHandler) {
h.app.mutex.Lock()
h.hookList["onRoute"] = append(h.hookList["onRoute"], handler...)
h.app.mutex.Unlock()
}

// OnName is a hook to execute user functions on each route naming.
// Also you can get route properties by "route" key of map.
//
// WARN: OnName only works with naming routes, not groups.
func (h *Hooks) OnName(handler ...HookHandler) {
h.app.mutex.Lock()
h.hookList["onName"] = append(h.hookList["onName"], handler...)
h.app.mutex.Unlock()
}

// OnGroupName is a hook to execute user functions on each group naming.
// Also you can get group properties by "group" key of map.
//
// WARN: OnGroupName only works with naming groups, not routes.
func (h *Hooks) OnGroupName(handler ...HookHandler) {
h.app.mutex.Lock()
h.hookList["onGroupName"] = append(h.hookList["onGroupName"], handler...)
h.app.mutex.Unlock()
}

// OnListen is a hook to execute user functions on Listen, ListenTLS, Listener.
func (h *Hooks) OnListen(handler ...HookHandler) {
h.app.mutex.Lock()
h.hookList["onListen"] = append(h.hookList["onListen"], handler...)
h.app.mutex.Unlock()
}

// OnShutdown is a hook to execute user functions after Shutdown.
func (h *Hooks) OnShutdown(handler ...HookHandler) {
h.app.mutex.Lock()
h.hookList["onShutdown"] = append(h.hookList["onShutdown"], handler...)
h.app.mutex.Unlock()
}

func (h *Hooks) executeOnRouteHooks(route Route) error {
for _, v := range h.hookList["onRoute"] {
ctx := h.app.AcquireCtx(&fasthttp.RequestCtx{})
defer h.app.ReleaseCtx(ctx)

if err := v(ctx, Map{"route": route}); err != nil {
return err
}
}

return nil
}

func (h *Hooks) executeOnNameHooks(route Route) error {
for _, v := range h.hookList["onName"] {
ctx := h.app.AcquireCtx(&fasthttp.RequestCtx{})
defer h.app.ReleaseCtx(ctx)

if err := v(ctx, Map{"route": route}); err != nil {
return err
}
}

return nil
}

func (h *Hooks) executeOnGroupNameHooks(group Group) error {
for _, v := range h.hookList["onGroupName"] {
ctx := h.app.AcquireCtx(&fasthttp.RequestCtx{})
defer h.app.ReleaseCtx(ctx)

if err := v(ctx, Map{"group": group}); err != nil {
return err
}
}

return nil
}

func (h *Hooks) executeOnListenHooks() error {
for _, v := range h.hookList["onListen"] {
ctx := h.app.AcquireCtx(&fasthttp.RequestCtx{})
defer h.app.ReleaseCtx(ctx)

if err := v(ctx, Map{}); err != nil {
return err
}
}

return nil
}

func (h *Hooks) executeOnShutdownHooks() {
if len(h.hookList["onShutdown"]) > 0 {
for _, v := range h.hookList["onShutdown"] {
ctx := h.app.AcquireCtx(&fasthttp.RequestCtx{})
defer h.app.ReleaseCtx(ctx)

_ = v(ctx, Map{})
}
}
}