Files
amcs/vendor/github.com/uptrace/bunrouter/router.go
Hein 1adf50e3db
CI / build-and-test (push) Failing after 1s
Release / release (push) Failing after 19m26s
fix(go.sum): update ResolveSpec dependency to v1.0.87
2026-06-23 13:17:16 +02:00

180 lines
4.9 KiB
Go

package bunrouter
import (
"net/http"
"net/url"
"strings"
"sync"
)
// Router is the main router structure that implements HTTP request routing.
// It maintains a routing tree and handles incoming HTTP requests.
type Router struct {
config // embedded router configuration
Group // embedded route group
mu sync.Mutex // protects the routing tree
tree node // root node of the routing tree
}
// New creates and returns a new Router instance with the given options.
// Options can include middleware, custom handlers for 404 and 405 responses,
// and other router configurations.
func New(opts ...Option) *Router {
r := &Router{
tree: node{
part: "/",
},
}
r.Group.router = r
r.config.group = &r.Group
r.methodNotAllowedHandler = methodNotAllowedHandler
for _, opt := range opts {
opt.apply(&r.config)
}
// Do it after processing middlewares from the options.
if r.notFoundHandler == nil {
r.notFoundHandler = r.group.wrap(notFoundHandler)
}
return r
}
var _ http.Handler = (*Router)(nil)
// ServeHTTP implements the http.Handler interface.
// It processes the incoming HTTP request and routes it to the appropriate handler.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
_ = r.ServeHTTPError(w, req)
}
// ServeHTTPError is similar to ServeHTTP but also returns any error
// that occurred during request handling.
func (r *Router) ServeHTTPError(w http.ResponseWriter, req *http.Request) error {
handler, params := r.lookup(w, req)
return handler(w, newRequestParams(req, params))
}
// lookup finds the appropriate handler and parameters for the given HTTP request.
// It returns the handler function and parsed route parameters.
func (r *Router) lookup(w http.ResponseWriter, req *http.Request) (HandlerFunc, Params) {
path := req.URL.RawPath
if path == "" {
path = req.URL.Path
}
node, handler, wildcardLen := r.tree.findRoute(req.Method, path)
if node == nil {
if redir := r.redir(req.Method, path); redir != nil {
return redir, Params{}
}
return r.notFoundHandler, Params{}
}
if handler == nil {
if redir := r.redir(req.Method, path); redir != nil {
return redir, Params{}
}
handler = node.handlerMap.notAllowed
}
return handler.fn, Params{
node: node,
handler: handler,
path: path,
wildcardLen: uint16(wildcardLen),
}
}
// redir handles URL redirects for cleaned paths and trailing slash variations.
// It returns a redirect handler if a redirect is needed, nil otherwise.
func (r *Router) redir(method, path string) HandlerFunc {
if path == "/" {
return nil
}
// Path was not found. Try cleaning it up and search again.
if cleanPath := CleanPath(path); cleanPath != path {
if _, handler, _ := r.tree.findRoute(method, cleanPath); handler != nil {
return redirectHandler(cleanPath)
}
}
if strings.HasSuffix(path, "/") {
// Try path without a slash.
cleanPath := path[:len(path)-1]
if _, handler, _ := r.tree.findRoute(method, cleanPath); handler != nil {
return redirectHandler(cleanPath)
}
return nil
}
// Try path with a slash.
cleanPath := path + "/"
if _, handler, _ := r.tree.findRoute(method, cleanPath); handler != nil {
return redirectHandler(cleanPath)
}
return nil
}
//------------------------------------------------------------------------------
// CompatRouter provides compatibility layer for the router.
type CompatRouter struct {
*Router
*CompatGroup
}
// Compat returns a new CompatRouter instance that wraps the current router.
func (r *Router) Compat() *CompatRouter {
return &CompatRouter{
Router: r,
CompatGroup: r.Group.Compat(),
}
}
// VerboseRouter provides a verbose interface to the router.
type VerboseRouter struct {
*Router
*VerboseGroup
}
// Verbose returns a new VerboseRouter instance that wraps the current router.
func (r *Router) Verbose() *VerboseRouter {
return &VerboseRouter{
Router: r,
VerboseGroup: r.Group.Verbose(),
}
}
//------------------------------------------------------------------------------
// redirectHandler creates a handler function that performs HTTP redirects
// to the specified new path while preserving query parameters and fragments.
func redirectHandler(newPath string) HandlerFunc {
return func(w http.ResponseWriter, req Request) error {
newURL := url.URL{
Path: newPath,
RawQuery: req.URL.RawQuery,
Fragment: req.URL.Fragment,
}
http.Redirect(w, req.Request, newURL.String(), http.StatusMovedPermanently)
return nil
}
}
// methodNotAllowedHandler is the default handler for requests with methods
// that are not allowed for the matched route.
func methodNotAllowedHandler(w http.ResponseWriter, r Request) error {
w.WriteHeader(http.StatusMethodNotAllowed)
return nil
}
// notFoundHandler is the default handler for requests that don't match any route.
func notFoundHandler(w http.ResponseWriter, req Request) error {
http.NotFound(w, req.Request)
return nil
}