436 lines
10 KiB
Go
436 lines
10 KiB
Go
package router
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
type Ctx = *fasthttp.RequestCtx
|
|
|
|
// Handler is a request handler with parameters.
|
|
type Handler func(ctx Ctx, params []string)
|
|
|
|
func (h Handler) Serve(ctx Ctx, params []string) {
|
|
h(ctx, params)
|
|
}
|
|
|
|
type Middleware func(Handler) Handler
|
|
|
|
type node struct {
|
|
segment string
|
|
handler Handler
|
|
children []*node
|
|
isDynamic bool
|
|
isWildcard bool
|
|
maxParams uint8
|
|
}
|
|
|
|
type Router struct {
|
|
get *node
|
|
post *node
|
|
put *node
|
|
patch *node
|
|
delete *node
|
|
middleware []Middleware
|
|
paramsBuffer []string // Pre-allocated buffer for parameters
|
|
}
|
|
|
|
type Group struct {
|
|
router *Router
|
|
prefix string
|
|
middleware []Middleware
|
|
}
|
|
|
|
// New creates a new Router instance.
|
|
func New() *Router {
|
|
return &Router{
|
|
get: &node{},
|
|
post: &node{},
|
|
put: &node{},
|
|
patch: &node{},
|
|
delete: &node{},
|
|
middleware: []Middleware{},
|
|
paramsBuffer: make([]string, 64),
|
|
}
|
|
}
|
|
|
|
// ServeHTTP implements the Handler interface for fasthttp
|
|
func (r *Router) ServeHTTP(ctx *fasthttp.RequestCtx) {
|
|
path := string(ctx.Path())
|
|
method := string(ctx.Method())
|
|
|
|
h, params, found := r.Lookup(method, path)
|
|
if !found {
|
|
ctx.SetStatusCode(fasthttp.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
h(ctx, params)
|
|
}
|
|
|
|
// Handler returns a fasthttp request handler
|
|
func (r *Router) Handler() fasthttp.RequestHandler {
|
|
return r.ServeHTTP
|
|
}
|
|
|
|
// Use adds middleware to the router.
|
|
func (r *Router) Use(mw ...Middleware) *Router {
|
|
r.middleware = append(r.middleware, mw...)
|
|
return r
|
|
}
|
|
|
|
// Group creates a new route group.
|
|
func (r *Router) Group(prefix string) *Group {
|
|
return &Group{router: r, prefix: prefix, middleware: []Middleware{}}
|
|
}
|
|
|
|
// Use adds middleware to the group.
|
|
func (g *Group) Use(mw ...Middleware) *Group {
|
|
g.middleware = append(g.middleware, mw...)
|
|
return g
|
|
}
|
|
|
|
// Group creates a nested group.
|
|
func (g *Group) Group(prefix string) *Group {
|
|
return &Group{
|
|
router: g.router,
|
|
prefix: g.prefix + prefix,
|
|
middleware: append([]Middleware{}, g.middleware...),
|
|
}
|
|
}
|
|
|
|
// applyMiddleware applies middleware in reverse order.
|
|
func applyMiddleware(h Handler, mw []Middleware) Handler {
|
|
for i := len(mw) - 1; i >= 0; i-- {
|
|
h = mw[i](h)
|
|
}
|
|
return h
|
|
}
|
|
|
|
// Handle registers a handler for the given method and path.
|
|
func (r *Router) Handle(method, path string, h Handler) error {
|
|
root := r.methodNode(method)
|
|
if root == nil {
|
|
return fmt.Errorf("unsupported method: %s", method)
|
|
}
|
|
return r.addRoute(root, path, h, r.middleware)
|
|
}
|
|
|
|
func (r *Router) methodNode(method string) *node {
|
|
switch method {
|
|
case "GET":
|
|
return r.get
|
|
case "POST":
|
|
return r.post
|
|
case "PUT":
|
|
return r.put
|
|
case "PATCH":
|
|
return r.patch
|
|
case "DELETE":
|
|
return r.delete
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Get registers a GET handler.
|
|
func (r *Router) Get(path string, h Handler) error {
|
|
return r.Handle("GET", path, h)
|
|
}
|
|
|
|
// Post registers a POST handler.
|
|
func (r *Router) Post(path string, h Handler) error {
|
|
return r.Handle("POST", path, h)
|
|
}
|
|
|
|
// Put registers a PUT handler.
|
|
func (r *Router) Put(path string, h Handler) error {
|
|
return r.Handle("PUT", path, h)
|
|
}
|
|
|
|
// Patch registers a PATCH handler.
|
|
func (r *Router) Patch(path string, h Handler) error {
|
|
return r.Handle("PATCH", path, h)
|
|
}
|
|
|
|
// Delete registers a DELETE handler.
|
|
func (r *Router) Delete(path string, h Handler) error {
|
|
return r.Handle("DELETE", path, h)
|
|
}
|
|
|
|
func (g *Group) buildGroupMiddleware() []Middleware {
|
|
mw := append([]Middleware{}, g.router.middleware...)
|
|
return append(mw, g.middleware...)
|
|
}
|
|
|
|
// Handle registers a handler in the group.
|
|
func (g *Group) Handle(method, path string, h Handler) error {
|
|
root := g.router.methodNode(method)
|
|
if root == nil {
|
|
return fmt.Errorf("unsupported method: %s", method)
|
|
}
|
|
return g.router.addRoute(root, g.prefix+path, h, g.buildGroupMiddleware())
|
|
}
|
|
|
|
// Get registers a GET handler in the group.
|
|
func (g *Group) Get(path string, h Handler) error {
|
|
return g.Handle("GET", path, h)
|
|
}
|
|
|
|
// Post registers a POST handler in the group.
|
|
func (g *Group) Post(path string, h Handler) error {
|
|
return g.Handle("POST", path, h)
|
|
}
|
|
|
|
// Put registers a PUT handler in the group.
|
|
func (g *Group) Put(path string, h Handler) error {
|
|
return g.Handle("PUT", path, h)
|
|
}
|
|
|
|
// Patch registers a PATCH handler in the group.
|
|
func (g *Group) Patch(path string, h Handler) error {
|
|
return g.Handle("PATCH", path, h)
|
|
}
|
|
|
|
// Delete registers a DELETE handler in the group.
|
|
func (g *Group) Delete(path string, h Handler) error {
|
|
return g.Handle("DELETE", path, h)
|
|
}
|
|
|
|
// WithMiddleware applies specific middleware for next registration.
|
|
func (r *Router) WithMiddleware(mw ...Middleware) *MiddlewareRouter {
|
|
return &MiddlewareRouter{router: r, middleware: mw}
|
|
}
|
|
|
|
// WithMiddleware applies specific middleware for next group route.
|
|
func (g *Group) WithMiddleware(mw ...Middleware) *MiddlewareGroup {
|
|
return &MiddlewareGroup{group: g, middleware: mw}
|
|
}
|
|
|
|
type MiddlewareRouter struct {
|
|
router *Router
|
|
middleware []Middleware
|
|
}
|
|
|
|
type MiddlewareGroup struct {
|
|
group *Group
|
|
middleware []Middleware
|
|
}
|
|
|
|
func (mr *MiddlewareRouter) buildMiddleware() []Middleware {
|
|
mw := append([]Middleware{}, mr.router.middleware...)
|
|
return append(mw, mr.middleware...)
|
|
}
|
|
|
|
// Handle registers a handler with middleware router.
|
|
func (mr *MiddlewareRouter) Handle(method, path string, h Handler) error {
|
|
root := mr.router.methodNode(method)
|
|
if root == nil {
|
|
return fmt.Errorf("unsupported method: %s", method)
|
|
}
|
|
return mr.router.addRoute(root, path, h, mr.buildMiddleware())
|
|
}
|
|
|
|
// Get registers a GET handler with middleware router.
|
|
func (mr *MiddlewareRouter) Get(path string, h Handler) error {
|
|
return mr.Handle("GET", path, h)
|
|
}
|
|
|
|
// Post registers a POST handler with middleware router.
|
|
func (mr *MiddlewareRouter) Post(path string, h Handler) error {
|
|
return mr.Handle("POST", path, h)
|
|
}
|
|
|
|
// Put registers a PUT handler with middleware router.
|
|
func (mr *MiddlewareRouter) Put(path string, h Handler) error {
|
|
return mr.Handle("PUT", path, h)
|
|
}
|
|
|
|
// Patch registers a PATCH handler with middleware router.
|
|
func (mr *MiddlewareRouter) Patch(path string, h Handler) error {
|
|
return mr.Handle("PATCH", path, h)
|
|
}
|
|
|
|
// Delete registers a DELETE handler with middleware router.
|
|
func (mr *MiddlewareRouter) Delete(path string, h Handler) error {
|
|
return mr.Handle("DELETE", path, h)
|
|
}
|
|
|
|
func (mg *MiddlewareGroup) buildMiddleware() []Middleware {
|
|
mw := append([]Middleware{}, mg.group.router.middleware...)
|
|
mw = append(mw, mg.group.middleware...)
|
|
return append(mw, mg.middleware...)
|
|
}
|
|
|
|
// Handle registers a handler with middleware group.
|
|
func (mg *MiddlewareGroup) Handle(method, path string, h Handler) error {
|
|
root := mg.group.router.methodNode(method)
|
|
if root == nil {
|
|
return fmt.Errorf("unsupported method: %s", method)
|
|
}
|
|
return mg.group.router.addRoute(root, mg.group.prefix+path, h, mg.buildMiddleware())
|
|
}
|
|
|
|
// Get registers a GET handler with middleware group.
|
|
func (mg *MiddlewareGroup) Get(path string, h Handler) error {
|
|
return mg.Handle("GET", path, h)
|
|
}
|
|
|
|
// Post registers a POST handler with middleware group.
|
|
func (mg *MiddlewareGroup) Post(path string, h Handler) error {
|
|
return mg.Handle("POST", path, h)
|
|
}
|
|
|
|
// Put registers a PUT handler with middleware group.
|
|
func (mg *MiddlewareGroup) Put(path string, h Handler) error {
|
|
return mg.Handle("PUT", path, h)
|
|
}
|
|
|
|
// Patch registers a PATCH handler with middleware group.
|
|
func (mg *MiddlewareGroup) Patch(path string, h Handler) error {
|
|
return mg.Handle("PATCH", path, h)
|
|
}
|
|
|
|
// Delete registers a DELETE handler with middleware group.
|
|
func (mg *MiddlewareGroup) Delete(path string, h Handler) error {
|
|
return mg.Handle("DELETE", path, h)
|
|
}
|
|
|
|
// StandardHandler adapts a standard fasthttp.RequestHandler to the router's Handler
|
|
func StandardHandler(handler fasthttp.RequestHandler) Handler {
|
|
return func(ctx Ctx, _ []string) {
|
|
handler(ctx)
|
|
}
|
|
}
|
|
|
|
// readSegment extracts the next path segment.
|
|
func readSegment(path string, start int) (segment string, end int, hasMore bool) {
|
|
if start >= len(path) {
|
|
return "", start, false
|
|
}
|
|
if path[start] == '/' {
|
|
start++
|
|
}
|
|
if start >= len(path) {
|
|
return "", start, false
|
|
}
|
|
end = start
|
|
for end < len(path) && path[end] != '/' {
|
|
end++
|
|
}
|
|
return path[start:end], end, end < len(path)
|
|
}
|
|
|
|
// addRoute adds a new route to the trie.
|
|
func (r *Router) addRoute(root *node, path string, h Handler, mw []Middleware) error {
|
|
h = applyMiddleware(h, mw)
|
|
if path == "/" {
|
|
root.handler = h
|
|
return nil
|
|
}
|
|
current := root
|
|
pos := 0
|
|
lastWC := false
|
|
count := uint8(0)
|
|
for {
|
|
seg, newPos, more := readSegment(path, pos)
|
|
if seg == "" {
|
|
break
|
|
}
|
|
isDyn := len(seg) > 2 && seg[0] == '[' && seg[len(seg)-1] == ']'
|
|
isWC := len(seg) > 0 && seg[0] == '*'
|
|
if isWC {
|
|
if lastWC || more {
|
|
return fmt.Errorf("wildcard must be the last segment in the path")
|
|
}
|
|
lastWC = true
|
|
}
|
|
if isDyn || isWC {
|
|
count++
|
|
}
|
|
var child *node
|
|
for _, c := range current.children {
|
|
if c.segment == seg {
|
|
child = c
|
|
break
|
|
}
|
|
}
|
|
if child == nil {
|
|
child = &node{segment: seg, isDynamic: isDyn, isWildcard: isWC}
|
|
current.children = append(current.children, child)
|
|
}
|
|
if child.maxParams < count {
|
|
child.maxParams = count
|
|
}
|
|
current = child
|
|
pos = newPos
|
|
}
|
|
current.handler = h
|
|
return nil
|
|
}
|
|
|
|
// Lookup finds a handler matching method and path.
|
|
func (r *Router) Lookup(method, path string) (Handler, []string, bool) {
|
|
root := r.methodNode(method)
|
|
if root == nil {
|
|
return nil, nil, false
|
|
}
|
|
if path == "/" {
|
|
return root.handler, nil, root.handler != nil
|
|
}
|
|
|
|
buffer := r.paramsBuffer
|
|
if cap(buffer) < int(root.maxParams) {
|
|
buffer = make([]string, root.maxParams)
|
|
r.paramsBuffer = buffer
|
|
}
|
|
buffer = buffer[:0]
|
|
|
|
h, paramCount, found := match(root, path, 0, &buffer)
|
|
if !found {
|
|
return nil, nil, false
|
|
}
|
|
|
|
return h, buffer[:paramCount], true
|
|
}
|
|
|
|
// match traverses the trie to find a handler.
|
|
func match(current *node, path string, start int, params *[]string) (Handler, int, bool) {
|
|
paramCount := 0
|
|
|
|
for _, c := range current.children {
|
|
if c.isWildcard {
|
|
rem := path[start:]
|
|
if len(rem) > 0 && rem[0] == '/' {
|
|
rem = rem[1:]
|
|
}
|
|
*params = append(*params, rem)
|
|
return c.handler, 1, c.handler != nil
|
|
}
|
|
}
|
|
|
|
seg, pos, more := readSegment(path, start)
|
|
if seg == "" {
|
|
return current.handler, 0, current.handler != nil
|
|
}
|
|
|
|
for _, c := range current.children {
|
|
if c.segment == seg || c.isDynamic {
|
|
if c.isDynamic {
|
|
*params = append(*params, seg)
|
|
paramCount++
|
|
}
|
|
if !more {
|
|
return c.handler, paramCount, c.handler != nil
|
|
}
|
|
h, nestedCount, ok := match(c, path, pos, params)
|
|
if ok {
|
|
return h, paramCount + nestedCount, true
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, 0, false
|
|
}
|