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, ¶ms) 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 }