FastRouter/router.go
2025-04-26 15:40:32 -05:00

490 lines
14 KiB
Go

package router
import (
"fmt"
"github.com/valyala/fasthttp"
)
// Ctx is an alias for fasthttp.RequestCtx for shorter, cleaner code
type Ctx = *fasthttp.RequestCtx
// Handler is an interface for handling HTTP requests with path parameters.
type Handler interface {
Serve(ctx Ctx, params []string)
}
// Middleware wraps a handler with additional functionality.
type Middleware func(Handler) Handler
// node represents a segment in the URL path and its handling logic.
type node struct {
segment string // the path segment this node matches
handler Handler // handler for this path, if it's an endpoint
children []*node // child nodes for subsequent path segments
isDynamic bool // true for param segments like [id]
isWildcard bool // true for catch-all segments like *filepath
maxParams uint8 // maximum number of parameters in paths under this node
}
// Router routes HTTP requests by method and path.
// It supports static paths, path parameters, wildcards, and middleware.
type Router struct {
get *node
post *node
put *node
patch *node
delete *node
middleware []Middleware // Global middleware
}
// Group represents a route group with a path prefix and shared middleware.
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{},
}
}
// ServeHTTP implements the Handler interface for fasthttp
func (r *Router) ServeHTTP(ctx *fasthttp.RequestCtx) {
path := string(ctx.Path())
method := string(ctx.Method())
handler, params, found := r.Lookup(method, path)
if !found {
ctx.SetStatusCode(fasthttp.StatusNotFound)
return
}
handler.Serve(ctx, params)
}
// Handler returns a fasthttp request handler
func (r *Router) Handler() fasthttp.RequestHandler {
return r.ServeHTTP
}
// simpleHandler implements the Handler interface
type simpleHandler struct {
fn func(ctx Ctx, params []string)
}
// Serve executes the handler function with params
func (h *simpleHandler) Serve(ctx Ctx, params []string) {
h.fn(ctx, params)
}
// Use adds middleware to the router's global middleware stack.
func (r *Router) Use(middleware ...Middleware) *Router {
r.middleware = append(r.middleware, middleware...)
return r
}
// Group creates a new route group with the given path prefix.
func (r *Router) Group(prefix string) *Group {
return &Group{
router: r,
prefix: prefix,
middleware: []Middleware{},
}
}
// Use adds middleware to the group's middleware stack.
func (g *Group) Use(middleware ...Middleware) *Group {
g.middleware = append(g.middleware, middleware...)
return g
}
// Group creates a nested group with an additional prefix.
func (g *Group) Group(prefix string) *Group {
return &Group{
router: g.router,
prefix: g.prefix + prefix,
middleware: append([]Middleware{}, g.middleware...),
}
}
// applyMiddleware wraps a handler with middleware in reverse order.
func applyMiddleware(handler Handler, middleware []Middleware) Handler {
h := handler
for i := len(middleware) - 1; i >= 0; i-- {
h = middleware[i](h)
}
return h
}
// HandlerFunc is a function that handles HTTP requests with parameters.
type HandlerFunc func(ctx Ctx, params []string)
// Handle registers a handler for the given method and path.
func (r *Router) Handle(method, path string, handler HandlerFunc) error {
root := r.methodNode(method)
if root == nil {
return fmt.Errorf("unsupported method: %s", method)
}
return r.addRoute(root, path, &simpleHandler{fn: handler}, r.middleware)
}
// methodNode returns the root node for the given HTTP method.
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 handler for GET requests at the given path.
func (r *Router) Get(path string, handler HandlerFunc) error {
return r.Handle("GET", path, handler)
}
// Post registers a handler for POST requests at the given path.
func (r *Router) Post(path string, handler HandlerFunc) error {
return r.Handle("POST", path, handler)
}
// Put registers a handler for PUT requests at the given path.
func (r *Router) Put(path string, handler HandlerFunc) error {
return r.Handle("PUT", path, handler)
}
// Patch registers a handler for PATCH requests at the given path.
func (r *Router) Patch(path string, handler HandlerFunc) error {
return r.Handle("PATCH", path, handler)
}
// Delete registers a handler for DELETE requests at the given path.
func (r *Router) Delete(path string, handler HandlerFunc) error {
return r.Handle("DELETE", path, handler)
}
// buildGroupMiddleware returns combined middleware for the group
func (g *Group) buildGroupMiddleware() []Middleware {
middleware := append([]Middleware{}, g.router.middleware...)
return append(middleware, g.middleware...)
}
// Handle registers a handler for the given method and path.
func (g *Group) Handle(method, path string, handler HandlerFunc) error {
root := g.router.methodNode(method)
if root == nil {
return fmt.Errorf("unsupported method: %s", method)
}
fullPath := g.prefix + path
return g.router.addRoute(root, fullPath, &simpleHandler{fn: handler}, g.buildGroupMiddleware())
}
// Get registers a handler for GET requests at the given path.
func (g *Group) Get(path string, handler HandlerFunc) error {
return g.Handle("GET", path, handler)
}
// Post registers a handler for POST requests at the given path.
func (g *Group) Post(path string, handler HandlerFunc) error {
return g.Handle("POST", path, handler)
}
// Put registers a handler for PUT requests at the given path.
func (g *Group) Put(path string, handler HandlerFunc) error {
return g.Handle("PUT", path, handler)
}
// Patch registers a handler for PATCH requests at the given path.
func (g *Group) Patch(path string, handler HandlerFunc) error {
return g.Handle("PATCH", path, handler)
}
// Delete registers a handler for DELETE requests at the given path.
func (g *Group) Delete(path string, handler HandlerFunc) error {
return g.Handle("DELETE", path, handler)
}
// WithMiddleware applies specific middleware to the next route registration.
func (r *Router) WithMiddleware(middleware ...Middleware) *MiddlewareRouter {
return &MiddlewareRouter{
router: r,
middleware: middleware,
}
}
// WithMiddleware applies specific middleware to the next route registration.
func (g *Group) WithMiddleware(middleware ...Middleware) *MiddlewareGroup {
return &MiddlewareGroup{
group: g,
middleware: middleware,
}
}
// MiddlewareRouter handles route registration with specific middleware.
type MiddlewareRouter struct {
router *Router
middleware []Middleware
}
// MiddlewareGroup handles group route registration with specific middleware.
type MiddlewareGroup struct {
group *Group
middleware []Middleware
}
// buildMiddleware returns combined middleware for the middleware router
func (mr *MiddlewareRouter) buildMiddleware() []Middleware {
middleware := append([]Middleware{}, mr.router.middleware...)
return append(middleware, mr.middleware...)
}
// Handle registers a handler for the given method and path.
func (mr *MiddlewareRouter) Handle(method, path string, handler HandlerFunc) error {
root := mr.router.methodNode(method)
if root == nil {
return fmt.Errorf("unsupported method: %s", method)
}
return mr.router.addRoute(root, path, &simpleHandler{fn: handler}, mr.buildMiddleware())
}
// Get registers a handler for GET requests with specific middleware.
func (mr *MiddlewareRouter) Get(path string, handler HandlerFunc) error {
return mr.Handle("GET", path, handler)
}
// Post registers a handler for POST requests with specific middleware.
func (mr *MiddlewareRouter) Post(path string, handler HandlerFunc) error {
return mr.Handle("POST", path, handler)
}
// Put registers a handler for PUT requests with specific middleware.
func (mr *MiddlewareRouter) Put(path string, handler HandlerFunc) error {
return mr.Handle("PUT", path, handler)
}
// Patch registers a handler for PATCH requests with specific middleware.
func (mr *MiddlewareRouter) Patch(path string, handler HandlerFunc) error {
return mr.Handle("PATCH", path, handler)
}
// Delete registers a handler for DELETE requests with specific middleware.
func (mr *MiddlewareRouter) Delete(path string, handler HandlerFunc) error {
return mr.Handle("DELETE", path, handler)
}
// buildMiddleware returns combined middleware for the middleware group
func (mg *MiddlewareGroup) buildMiddleware() []Middleware {
middleware := append([]Middleware{}, mg.group.router.middleware...)
middleware = append(middleware, mg.group.middleware...)
return append(middleware, mg.middleware...)
}
// Handle registers a handler for the given method and path.
func (mg *MiddlewareGroup) Handle(method, path string, handler HandlerFunc) error {
root := mg.group.router.methodNode(method)
if root == nil {
return fmt.Errorf("unsupported method: %s", method)
}
fullPath := mg.group.prefix + path
return mg.group.router.addRoute(root, fullPath, &simpleHandler{fn: handler}, mg.buildMiddleware())
}
// Get registers a handler for GET requests with specific middleware.
func (mg *MiddlewareGroup) Get(path string, handler HandlerFunc) error {
return mg.Handle("GET", path, handler)
}
// Post registers a handler for POST requests with specific middleware.
func (mg *MiddlewareGroup) Post(path string, handler HandlerFunc) error {
return mg.Handle("POST", path, handler)
}
// Put registers a handler for PUT requests with specific middleware.
func (mg *MiddlewareGroup) Put(path string, handler HandlerFunc) error {
return mg.Handle("PUT", path, handler)
}
// Patch registers a handler for PATCH requests with specific middleware.
func (mg *MiddlewareGroup) Patch(path string, handler HandlerFunc) error {
return mg.Handle("PATCH", path, handler)
}
// Delete registers a handler for DELETE requests with specific middleware.
func (mg *MiddlewareGroup) Delete(path string, handler HandlerFunc) error {
return mg.Handle("DELETE", path, handler)
}
// StandardHandler adapts a standard fasthttp.RequestHandler to the router's HandlerFunc
func StandardHandler(handler fasthttp.RequestHandler) HandlerFunc {
return func(ctx Ctx, _ []string) {
handler(ctx)
}
}
// readSegment extracts the next path segment starting at the given position.
// Returns the segment, the position after it, and whether there are more segments.
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 prefix tree with middleware.
func (r *Router) addRoute(root *node, path string, handler Handler, middleware []Middleware) error {
wrappedHandler := applyMiddleware(handler, middleware)
if path == "/" {
root.handler = wrappedHandler
return nil
}
current := root
pos := 0
var lastWildcard bool
paramsCount := uint8(0)
for {
segment, newPos, hasMore := readSegment(path, pos)
if segment == "" {
break
}
isDynamic := len(segment) > 2 && segment[0] == '[' && segment[len(segment)-1] == ']'
isWildcard := len(segment) > 0 && segment[0] == '*'
if isWildcard {
if lastWildcard {
return fmt.Errorf("wildcard must be the last segment in the path")
}
if hasMore {
return fmt.Errorf("wildcard must be the last segment in the path")
}
lastWildcard = true
}
if isDynamic || isWildcard {
paramsCount++
}
var child *node
for _, n := range current.children {
if n.segment == segment {
child = n
break
}
}
if child == nil {
child = &node{
segment: segment,
isDynamic: isDynamic,
isWildcard: isWildcard,
}
current.children = append(current.children, child)
}
if child.maxParams < paramsCount {
child.maxParams = paramsCount
}
current = child
pos = newPos
}
current.handler = wrappedHandler
return nil
}
// Lookup finds a handler matching the given method and path.
// Returns the handler, any captured parameters, and whether a match was found.
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, []string{}, root.handler != nil
}
params := make([]string, 0, root.maxParams)
h, found := match(root, path, 0, &params)
if !found {
return nil, nil, false
}
return h, params, true
}
// match recursively traverses the prefix tree to find a matching handler.
// It populates params with any captured path parameters or wildcard matches.
func match(current *node, path string, start int, params *[]string) (Handler, bool) {
// Check for wildcard children first
for _, child := range current.children {
if child.isWildcard {
remaining := path[start:]
if len(remaining) > 0 && remaining[0] == '/' {
remaining = remaining[1:]
}
*params = append(*params, remaining)
return child.handler, child.handler != nil
}
}
// Read current segment
segment, pos, hasMore := readSegment(path, start)
if segment == "" {
return current.handler, current.handler != nil
}
// Try to match children
for _, child := range current.children {
if child.segment == segment || child.isDynamic {
if child.isDynamic {
*params = append(*params, segment)
}
if !hasMore {
return child.handler, child.handler != nil
}
if h, found := match(child, path, pos, params); found {
return h, true
}
}
}
return nil, false
}