rewrite 4
This commit is contained in:
parent
de416933e8
commit
2212ea757c
93
README.md
93
README.md
@ -8,7 +8,7 @@ A high-performance router for Go with support for path parameters, wildcards, mi
|
|||||||
- Support for path parameters (`[id]`) and wildcards (`*path`)
|
- Support for path parameters (`[id]`) and wildcards (`*path`)
|
||||||
- Middleware support for request processing pipelines
|
- Middleware support for request processing pipelines
|
||||||
- Route grouping with shared prefixes and middleware
|
- Route grouping with shared prefixes and middleware
|
||||||
- Adapters for net/http and fasthttp (optional)
|
- Implements `http.Handler` for direct integration
|
||||||
- Up to 15x faster than `http.ServeMux` for dynamic routes
|
- Up to 15x faster than `http.ServeMux` for dynamic routes
|
||||||
- Zero allocations for static routes
|
- Zero allocations for static routes
|
||||||
|
|
||||||
@ -27,26 +27,34 @@ go get git.sharkk.net/Go/Router
|
|||||||
r := router.New()
|
r := router.New()
|
||||||
|
|
||||||
// Static routes
|
// Static routes
|
||||||
r.Get("/", router.FuncHandler(func(params []string) {
|
r.Get("/", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
fmt.Println("Root handler")
|
fmt.Fprintf(w, "Root handler")
|
||||||
}))
|
})
|
||||||
|
|
||||||
// Parameter routes
|
// Parameter routes
|
||||||
r.Get("/users/[id]", router.FuncHandler(func(params []string) {
|
r.Get("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
userID := params[0]
|
userID := params[0]
|
||||||
fmt.Printf("User ID: %s\n", userID)
|
fmt.Fprintf(w, "User ID: %s", userID)
|
||||||
}))
|
})
|
||||||
|
|
||||||
// Wildcard routes
|
// Wildcard routes
|
||||||
r.Get("/files/*path", router.FuncHandler(func(params []string) {
|
r.Get("/files/*path", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
filePath := params[0]
|
filePath := params[0]
|
||||||
fmt.Printf("File path: %s\n", filePath)
|
fmt.Fprintf(w, "File path: %s", filePath)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Standard http.HandlerFunc adapter
|
||||||
|
r.Get("/simple", router.StandardHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "Simple handler without params")
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Lookup routes
|
// Lookup routes manually
|
||||||
if handler, params, ok := r.Lookup("GET", "/users/123"); ok {
|
if handler, params, ok := r.Lookup("GET", "/users/123"); ok {
|
||||||
handler.Serve(params)
|
handler.Serve(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use listen and serve directly
|
||||||
|
http.ListenAndServe(":8080", r)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Middleware
|
### Middleware
|
||||||
@ -54,11 +62,13 @@ if handler, params, ok := r.Lookup("GET", "/users/123"); ok {
|
|||||||
```go
|
```go
|
||||||
// Create logging middleware
|
// Create logging middleware
|
||||||
func LoggingMiddleware(next router.Handler) router.Handler {
|
func LoggingMiddleware(next router.Handler) router.Handler {
|
||||||
return router.FuncHandler(func(params []string) {
|
return &router.simpleHandler{
|
||||||
fmt.Println("Request started")
|
fn: func(params []string) {
|
||||||
next.Serve(params)
|
fmt.Println("Request started")
|
||||||
fmt.Println("Request completed")
|
next.Serve(params)
|
||||||
})
|
fmt.Println("Request completed")
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply middleware globally
|
// Apply middleware globally
|
||||||
@ -67,6 +77,8 @@ r.Use(LoggingMiddleware)
|
|||||||
|
|
||||||
// Apply middleware to specific routes
|
// Apply middleware to specific routes
|
||||||
r.WithMiddleware(AuthMiddleware).Get("/admin", adminHandler)
|
r.WithMiddleware(AuthMiddleware).Get("/admin", adminHandler)
|
||||||
|
|
||||||
|
http.ListenAndServe(":8080", r)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Route Groups
|
### Route Groups
|
||||||
@ -87,59 +99,13 @@ v1.Get("/products", listProductsHandler) // matches /api/v1/products
|
|||||||
admin := api.Group("/admin")
|
admin := api.Group("/admin")
|
||||||
admin.Use(AuthMiddleware)
|
admin.Use(AuthMiddleware)
|
||||||
admin.Get("/stats", statsHandler) // matches /api/admin/stats
|
admin.Get("/stats", statsHandler) // matches /api/admin/stats
|
||||||
```
|
|
||||||
|
|
||||||
### HTTP Integration
|
http.ListenAndServe(":8080", r)
|
||||||
|
|
||||||
```go
|
|
||||||
// Standard net/http integration
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Find the matching handler
|
|
||||||
handler, params, found := router.Lookup(r.Method, r.URL.Path)
|
|
||||||
if !found {
|
|
||||||
http.NotFound(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an HTTP-compatible handler
|
|
||||||
httpHandler := router.NewHTTP(w, r, func(w http.ResponseWriter, r *http.Request, params []string) {
|
|
||||||
fmt.Fprintf(w, "Hello, user %s", params[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Execute the handler
|
|
||||||
httpHandler.Serve(params)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### FastHTTP Integration (Optional)
|
|
||||||
|
|
||||||
Build with `-tags fasthttp` to enable FastHTTP support:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// FastHTTP integration
|
|
||||||
fastHandler := func(ctx *fasthttp.RequestCtx) {
|
|
||||||
path := string(ctx.Path())
|
|
||||||
method := string(ctx.Method())
|
|
||||||
|
|
||||||
handler, params, found := router.Lookup(method, path)
|
|
||||||
if !found {
|
|
||||||
ctx.Error("Not found", fasthttp.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a FastHTTP-compatible handler
|
|
||||||
fastHandler := router.NewFastHTTP(ctx, func(ctx *fasthttp.RequestCtx, params []string) {
|
|
||||||
fmt.Fprintf(ctx, "Hello, user %s", params[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Execute the handler
|
|
||||||
fastHandler.Serve(params)
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
Benchmark results comparing our router to the standard `http.ServeMux` on AMD Ryzen 9 7950X:
|
Benchmark results comparing Router to the standard `http.ServeMux`:
|
||||||
|
|
||||||
```
|
```
|
||||||
cpu: AMD Ryzen 9 7950X 16-Core Processor
|
cpu: AMD Ryzen 9 7950X 16-Core Processor
|
||||||
@ -161,7 +127,6 @@ Router: 10.580 ns/op 0 B/op 0 allocs/op
|
|||||||
ServeMux: 178.100 ns/op 56 B/op 3 allocs/op
|
ServeMux: 178.100 ns/op 56 B/op 3 allocs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
Key Performance Points:
|
|
||||||
- Root path lookups are 15x faster
|
- Root path lookups are 15x faster
|
||||||
- Static paths are 4x faster with zero allocations
|
- Static paths are 4x faster with zero allocations
|
||||||
- Dynamic paths are 4.4x faster with fewer allocations
|
- Dynamic paths are 4.4x faster with fewer allocations
|
||||||
|
426
router.go
426
router.go
@ -3,6 +3,7 @@ package router
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler is an interface for handling HTTP requests with path parameters.
|
// Handler is an interface for handling HTTP requests with path parameters.
|
||||||
@ -111,7 +112,7 @@ func (g *Group) Group(prefix string) *Group {
|
|||||||
return &Group{
|
return &Group{
|
||||||
router: g.router,
|
router: g.router,
|
||||||
prefix: g.prefix + prefix,
|
prefix: g.prefix + prefix,
|
||||||
middleware: append([]Middleware{}, g.middleware...),
|
middleware: slices.Clone(g.middleware),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,166 +125,101 @@ func applyMiddleware(handler Handler, middleware []Middleware) Handler {
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get registers a handler for GET requests at the given path.
|
// HandlerFunc is a function that handles HTTP requests with parameters.
|
||||||
func (r *Router) Get(path string, handler http.HandlerFunc) error {
|
type HandlerFunc func(w http.ResponseWriter, r *http.Request, params []string)
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.addRoute(r.get, path, &httpHandler{h: httpHandlerFunc}, r.middleware)
|
// 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, &httpHandler{h: handler}, r.middleware)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get registers a handler with parameters for GET requests at the given path.
|
// methodNode returns the root node for the given HTTP method.
|
||||||
func (r *Router) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
func (r *Router) methodNode(method string) *node {
|
||||||
return r.addRoute(r.get, path, &httpHandler{h: handler}, r.middleware)
|
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.
|
// Post registers a handler for POST requests at the given path.
|
||||||
func (r *Router) Post(path string, handler http.HandlerFunc) error {
|
func (r *Router) Post(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return r.Handle("POST", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.addRoute(r.post, path, &httpHandler{h: httpHandlerFunc}, r.middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post registers a handler with parameters for POST requests at the given path.
|
|
||||||
func (r *Router) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
return r.addRoute(r.post, path, &httpHandler{h: handler}, r.middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put registers a handler for PUT requests at the given path.
|
// Put registers a handler for PUT requests at the given path.
|
||||||
func (r *Router) Put(path string, handler http.HandlerFunc) error {
|
func (r *Router) Put(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return r.Handle("PUT", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.addRoute(r.put, path, &httpHandler{h: httpHandlerFunc}, r.middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put registers a handler with parameters for PUT requests at the given path.
|
|
||||||
func (r *Router) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
return r.addRoute(r.put, path, &httpHandler{h: handler}, r.middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch registers a handler for PATCH requests at the given path.
|
// Patch registers a handler for PATCH requests at the given path.
|
||||||
func (r *Router) Patch(path string, handler http.HandlerFunc) error {
|
func (r *Router) Patch(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return r.Handle("PATCH", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.addRoute(r.patch, path, &httpHandler{h: httpHandlerFunc}, r.middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch registers a handler with parameters for PATCH requests at the given path.
|
|
||||||
func (r *Router) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
return r.addRoute(r.patch, path, &httpHandler{h: handler}, r.middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete registers a handler for DELETE requests at the given path.
|
// Delete registers a handler for DELETE requests at the given path.
|
||||||
func (r *Router) Delete(path string, handler http.HandlerFunc) error {
|
func (r *Router) Delete(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return r.Handle("DELETE", path, handler)
|
||||||
handler(w, r)
|
}
|
||||||
|
|
||||||
|
// buildGroupMiddleware returns combined middleware for the group
|
||||||
|
func (g *Group) buildGroupMiddleware() []Middleware {
|
||||||
|
middleware := slices.Clone(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)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.addRoute(r.delete, path, &httpHandler{h: httpHandlerFunc}, r.middleware)
|
fullPath := g.prefix + path
|
||||||
|
return g.router.addRoute(root, fullPath, &httpHandler{h: handler}, g.buildGroupMiddleware())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete registers a handler with parameters for DELETE requests at the given path.
|
|
||||||
func (r *Router) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
return r.addRoute(r.delete, path, &httpHandler{h: handler}, r.middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group HTTP handler registration methods
|
|
||||||
|
|
||||||
// Get registers a handler for GET requests at the given path.
|
// Get registers a handler for GET requests at the given path.
|
||||||
func (g *Group) Get(path string, handler http.HandlerFunc) error {
|
func (g *Group) Get(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return g.Handle("GET", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.get, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get registers a handler with parameters for GET requests at the given path.
|
|
||||||
func (g *Group) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.get, g.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post registers a handler for POST requests at the given path.
|
// Post registers a handler for POST requests at the given path.
|
||||||
func (g *Group) Post(path string, handler http.HandlerFunc) error {
|
func (g *Group) Post(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return g.Handle("POST", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.post, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post registers a handler with parameters for POST requests at the given path.
|
|
||||||
func (g *Group) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.post, g.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put registers a handler for PUT requests at the given path.
|
// Put registers a handler for PUT requests at the given path.
|
||||||
func (g *Group) Put(path string, handler http.HandlerFunc) error {
|
func (g *Group) Put(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return g.Handle("PUT", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.put, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put registers a handler with parameters for PUT requests at the given path.
|
|
||||||
func (g *Group) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.put, g.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch registers a handler for PATCH requests at the given path.
|
// Patch registers a handler for PATCH requests at the given path.
|
||||||
func (g *Group) Patch(path string, handler http.HandlerFunc) error {
|
func (g *Group) Patch(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return g.Handle("PATCH", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.patch, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch registers a handler with parameters for PATCH requests at the given path.
|
|
||||||
func (g *Group) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.patch, g.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete registers a handler for DELETE requests at the given path.
|
// Delete registers a handler for DELETE requests at the given path.
|
||||||
func (g *Group) Delete(path string, handler http.HandlerFunc) error {
|
func (g *Group) Delete(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return g.Handle("DELETE", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.delete, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete registers a handler with parameters for DELETE requests at the given path.
|
|
||||||
func (g *Group) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, g.router.middleware...)
|
|
||||||
middleware = append(middleware, g.middleware...)
|
|
||||||
return g.router.addRoute(g.router.delete, g.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMiddleware applies specific middleware to the next route registration.
|
// WithMiddleware applies specific middleware to the next route registration.
|
||||||
@ -314,198 +250,95 @@ type MiddlewareGroup struct {
|
|||||||
middleware []Middleware
|
middleware []Middleware
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP method registration functions for MiddlewareRouter
|
// buildMiddleware returns combined middleware for the middleware router
|
||||||
|
func (mr *MiddlewareRouter) buildMiddleware() []Middleware {
|
||||||
// Get registers a handler for GET requests with specific middleware.
|
middleware := slices.Clone(mr.router.middleware)
|
||||||
func (mr *MiddlewareRouter) Get(path string, handler http.HandlerFunc) error {
|
return append(middleware, mr.middleware...)
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.get, path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get registers a handler with parameters for GET requests with specific middleware.
|
// Handle registers a handler for the given method and path.
|
||||||
func (mr *MiddlewareRouter) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
func (mr *MiddlewareRouter) Handle(method, path string, handler HandlerFunc) error {
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
root := mr.router.methodNode(method)
|
||||||
middleware = append(middleware, mr.middleware...)
|
if root == nil {
|
||||||
return mr.router.addRoute(mr.router.get, path, &httpHandler{h: handler}, middleware)
|
return fmt.Errorf("unsupported method: %s", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mr.router.addRoute(root, path, &httpHandler{h: 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.
|
// Post registers a handler for POST requests with specific middleware.
|
||||||
func (mr *MiddlewareRouter) Post(path string, handler http.HandlerFunc) error {
|
func (mr *MiddlewareRouter) Post(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mr.Handle("POST", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.post, path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post registers a handler with parameters for POST requests with specific middleware.
|
|
||||||
func (mr *MiddlewareRouter) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.post, path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put registers a handler for PUT requests with specific middleware.
|
// Put registers a handler for PUT requests with specific middleware.
|
||||||
func (mr *MiddlewareRouter) Put(path string, handler http.HandlerFunc) error {
|
func (mr *MiddlewareRouter) Put(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mr.Handle("PUT", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.put, path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put registers a handler with parameters for PUT requests with specific middleware.
|
|
||||||
func (mr *MiddlewareRouter) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.put, path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch registers a handler for PATCH requests with specific middleware.
|
// Patch registers a handler for PATCH requests with specific middleware.
|
||||||
func (mr *MiddlewareRouter) Patch(path string, handler http.HandlerFunc) error {
|
func (mr *MiddlewareRouter) Patch(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mr.Handle("PATCH", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.patch, path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch registers a handler with parameters for PATCH requests with specific middleware.
|
|
||||||
func (mr *MiddlewareRouter) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.patch, path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete registers a handler for DELETE requests with specific middleware.
|
// Delete registers a handler for DELETE requests with specific middleware.
|
||||||
func (mr *MiddlewareRouter) Delete(path string, handler http.HandlerFunc) error {
|
func (mr *MiddlewareRouter) Delete(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mr.Handle("DELETE", path, handler)
|
||||||
handler(w, r)
|
}
|
||||||
|
|
||||||
|
// buildMiddleware returns combined middleware for the middleware group
|
||||||
|
func (mg *MiddlewareGroup) buildMiddleware() []Middleware {
|
||||||
|
middleware := slices.Clone(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)
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
fullPath := mg.group.prefix + path
|
||||||
middleware = append(middleware, mr.middleware...)
|
return mg.group.router.addRoute(root, fullPath, &httpHandler{h: handler}, mg.buildMiddleware())
|
||||||
return mr.router.addRoute(mr.router.delete, path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete registers a handler with parameters for DELETE requests with specific middleware.
|
|
||||||
func (mr *MiddlewareRouter) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mr.router.middleware...)
|
|
||||||
middleware = append(middleware, mr.middleware...)
|
|
||||||
return mr.router.addRoute(mr.router.delete, path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP method registration functions for MiddlewareGroup
|
|
||||||
|
|
||||||
// Get registers a handler for GET requests with specific middleware.
|
// Get registers a handler for GET requests with specific middleware.
|
||||||
func (mg *MiddlewareGroup) Get(path string, handler http.HandlerFunc) error {
|
func (mg *MiddlewareGroup) Get(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mg.Handle("GET", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.get, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get registers a handler with parameters for GET requests with specific middleware.
|
|
||||||
func (mg *MiddlewareGroup) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.get, mg.group.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post registers a handler for POST requests with specific middleware.
|
// Post registers a handler for POST requests with specific middleware.
|
||||||
func (mg *MiddlewareGroup) Post(path string, handler http.HandlerFunc) error {
|
func (mg *MiddlewareGroup) Post(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mg.Handle("POST", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.post, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post registers a handler with parameters for POST requests with specific middleware.
|
|
||||||
func (mg *MiddlewareGroup) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.post, mg.group.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put registers a handler for PUT requests with specific middleware.
|
// Put registers a handler for PUT requests with specific middleware.
|
||||||
func (mg *MiddlewareGroup) Put(path string, handler http.HandlerFunc) error {
|
func (mg *MiddlewareGroup) Put(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mg.Handle("PUT", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.put, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put registers a handler with parameters for PUT requests with specific middleware.
|
|
||||||
func (mg *MiddlewareGroup) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.put, mg.group.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch registers a handler for PATCH requests with specific middleware.
|
// Patch registers a handler for PATCH requests with specific middleware.
|
||||||
func (mg *MiddlewareGroup) Patch(path string, handler http.HandlerFunc) error {
|
func (mg *MiddlewareGroup) Patch(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mg.Handle("PATCH", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.patch, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch registers a handler with parameters for PATCH requests with specific middleware.
|
|
||||||
func (mg *MiddlewareGroup) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.patch, mg.group.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete registers a handler for DELETE requests with specific middleware.
|
// Delete registers a handler for DELETE requests with specific middleware.
|
||||||
func (mg *MiddlewareGroup) Delete(path string, handler http.HandlerFunc) error {
|
func (mg *MiddlewareGroup) Delete(path string, handler HandlerFunc) error {
|
||||||
httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) {
|
return mg.Handle("DELETE", path, handler)
|
||||||
handler(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
|
||||||
middleware = append(middleware, mg.middleware...)
|
|
||||||
return mg.group.router.addRoute(mg.group.router.delete, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete registers a handler with parameters for DELETE requests with specific middleware.
|
// Adapter for standard http.HandlerFunc
|
||||||
func (mg *MiddlewareGroup) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error {
|
func StandardHandler(handler http.HandlerFunc) HandlerFunc {
|
||||||
middleware := append([]Middleware{}, mg.group.router.middleware...)
|
return func(w http.ResponseWriter, r *http.Request, _ []string) {
|
||||||
middleware = append(middleware, mg.group.middleware...)
|
handler(w, r)
|
||||||
middleware = append(middleware, mg.middleware...)
|
}
|
||||||
return mg.group.router.addRoute(mg.group.router.delete, mg.group.prefix+path, &httpHandler{h: handler}, middleware)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// readSegment extracts the next path segment starting at the given position.
|
// readSegment extracts the next path segment starting at the given position.
|
||||||
@ -599,24 +432,13 @@ func (r *Router) addRoute(root *node, path string, handler Handler, middleware [
|
|||||||
// Lookup finds a handler matching the given method and path.
|
// Lookup finds a handler matching the given method and path.
|
||||||
// Returns the handler, any captured parameters, and whether a match was found.
|
// Returns the handler, any captured parameters, and whether a match was found.
|
||||||
func (r *Router) Lookup(method, path string) (Handler, []string, bool) {
|
func (r *Router) Lookup(method, path string) (Handler, []string, bool) {
|
||||||
var root *node
|
root := r.methodNode(method)
|
||||||
switch method {
|
if root == nil {
|
||||||
case "GET":
|
|
||||||
root = r.get
|
|
||||||
case "POST":
|
|
||||||
root = r.post
|
|
||||||
case "PUT":
|
|
||||||
root = r.put
|
|
||||||
case "PATCH":
|
|
||||||
root = r.patch
|
|
||||||
case "DELETE":
|
|
||||||
root = r.delete
|
|
||||||
default:
|
|
||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if path == "/" {
|
if path == "/" {
|
||||||
return root.handler, nil, root.handler != nil
|
return root.handler, []string{}, root.handler != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
params := make([]string, 0, root.maxParams)
|
params := make([]string, 0, root.maxParams)
|
||||||
|
151
router_test.go
151
router_test.go
@ -2,6 +2,7 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
assert "git.sharkk.net/Go/Assert"
|
assert "git.sharkk.net/Go/Assert"
|
||||||
@ -23,7 +24,7 @@ func newHandler(fn func(params []string)) Handler {
|
|||||||
|
|
||||||
func TestRootPath(t *testing.T) {
|
func TestRootPath(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.GetParam("/", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
// No-op for testing
|
// No-op for testing
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ func TestRootPath(t *testing.T) {
|
|||||||
|
|
||||||
func TestStaticPath(t *testing.T) {
|
func TestStaticPath(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.GetParam("/users/all", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/users/all", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
// No-op for testing
|
// No-op for testing
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ func TestSingleParameter(t *testing.T) {
|
|||||||
r := New()
|
r := New()
|
||||||
|
|
||||||
called := false
|
called := false
|
||||||
r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
called = true
|
called = true
|
||||||
assert.Equal(t, params[0], "123")
|
assert.Equal(t, params[0], "123")
|
||||||
})
|
})
|
||||||
@ -62,7 +63,7 @@ func TestMultipleParameters(t *testing.T) {
|
|||||||
r := New()
|
r := New()
|
||||||
|
|
||||||
called := false
|
called := false
|
||||||
r.GetParam("/users/[id]/posts/[postId]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/users/[id]/posts/[postId]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
called = true
|
called = true
|
||||||
assert.Equal(t, params[0], "123")
|
assert.Equal(t, params[0], "123")
|
||||||
assert.Equal(t, params[1], "456")
|
assert.Equal(t, params[1], "456")
|
||||||
@ -76,7 +77,7 @@ func TestMultipleParameters(t *testing.T) {
|
|||||||
|
|
||||||
func TestNonExistentPath(t *testing.T) {
|
func TestNonExistentPath(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
// No-op for testing
|
// No-op for testing
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ func TestNonExistentPath(t *testing.T) {
|
|||||||
|
|
||||||
func TestWrongMethod(t *testing.T) {
|
func TestWrongMethod(t *testing.T) {
|
||||||
r := New()
|
r := New()
|
||||||
r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
// No-op for testing
|
// No-op for testing
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -98,7 +99,7 @@ func TestTrailingSlash(t *testing.T) {
|
|||||||
r := New()
|
r := New()
|
||||||
|
|
||||||
called := false
|
called := false
|
||||||
r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
called = true
|
called = true
|
||||||
assert.Equal(t, params[0], "123")
|
assert.Equal(t, params[0], "123")
|
||||||
})
|
})
|
||||||
@ -114,11 +115,11 @@ func TestDifferentMethods(t *testing.T) {
|
|||||||
|
|
||||||
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
||||||
|
|
||||||
r.GetParam("/test", handler)
|
r.Get("/test", handler)
|
||||||
r.PostParam("/test", handler)
|
r.Post("/test", handler)
|
||||||
r.PutParam("/test", handler)
|
r.Put("/test", handler)
|
||||||
r.PatchParam("/test", handler)
|
r.Patch("/test", handler)
|
||||||
r.DeleteParam("/test", handler)
|
r.Delete("/test", handler)
|
||||||
|
|
||||||
methods := []string{"GET", "POST", "PUT", "PATCH", "DELETE"}
|
methods := []string{"GET", "POST", "PUT", "PATCH", "DELETE"}
|
||||||
for _, method := range methods {
|
for _, method := range methods {
|
||||||
@ -134,7 +135,7 @@ func TestWildcardPath(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("simple wildcard", func(t *testing.T) {
|
t.Run("simple wildcard", func(t *testing.T) {
|
||||||
called := false
|
called := false
|
||||||
err := r.GetParam("/files/*path", func(w http.ResponseWriter, r *http.Request, params []string) {
|
err := r.Get("/files/*path", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
called = true
|
called = true
|
||||||
assert.Equal(t, params[0], "docs/report.pdf")
|
assert.Equal(t, params[0], "docs/report.pdf")
|
||||||
})
|
})
|
||||||
@ -148,7 +149,7 @@ func TestWildcardPath(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("wildcard with empty path", func(t *testing.T) {
|
t.Run("wildcard with empty path", func(t *testing.T) {
|
||||||
called := false
|
called := false
|
||||||
err := r.GetParam("/download/*filepath", func(w http.ResponseWriter, r *http.Request, params []string) {
|
err := r.Get("/download/*filepath", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
called = true
|
called = true
|
||||||
assert.Equal(t, params[0], "")
|
assert.Equal(t, params[0], "")
|
||||||
})
|
})
|
||||||
@ -162,7 +163,7 @@ func TestWildcardPath(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("wildcard with parameter", func(t *testing.T) {
|
t.Run("wildcard with parameter", func(t *testing.T) {
|
||||||
called := false
|
called := false
|
||||||
err := r.GetParam("/users/[id]/*action", func(w http.ResponseWriter, r *http.Request, params []string) {
|
err := r.Get("/users/[id]/*action", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
called = true
|
called = true
|
||||||
assert.Equal(t, params[0], "123")
|
assert.Equal(t, params[0], "123")
|
||||||
assert.Equal(t, params[1], "settings/profile/avatar")
|
assert.Equal(t, params[1], "settings/profile/avatar")
|
||||||
@ -176,12 +177,12 @@ func TestWildcardPath(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("multiple wildcards not allowed", func(t *testing.T) {
|
t.Run("multiple wildcards not allowed", func(t *testing.T) {
|
||||||
err := r.GetParam("/api/*version/*path", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
err := r.Get("/api/*version/*path", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("non-last wildcard not allowed", func(t *testing.T) {
|
t.Run("non-last wildcard not allowed", func(t *testing.T) {
|
||||||
err := r.GetParam("/api/*version/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
err := r.Get("/api/*version/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -201,7 +202,7 @@ func TestMiddleware(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
r.Get("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
h, params, found := r.Lookup("GET", "/test")
|
h, params, found := r.Lookup("GET", "/test")
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
@ -231,7 +232,7 @@ func TestMiddleware(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {
|
r.Get("/test", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
order = append(order, 0)
|
order = append(order, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -260,7 +261,7 @@ func TestMiddleware(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
r.WithMiddleware(middleware).GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
r.WithMiddleware(middleware).Get("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
h, params, found := r.Lookup("GET", "/test")
|
h, params, found := r.Lookup("GET", "/test")
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
@ -276,7 +277,7 @@ func TestGroup(t *testing.T) {
|
|||||||
|
|
||||||
// Create API group
|
// Create API group
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
api.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
api.Get("/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
h, params, found := r.Lookup("GET", "/api/users")
|
h, params, found := r.Lookup("GET", "/api/users")
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
@ -289,7 +290,7 @@ func TestGroup(t *testing.T) {
|
|||||||
// Create nested groups
|
// Create nested groups
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
v1 := api.Group("/v1")
|
v1 := api.Group("/v1")
|
||||||
v1.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
v1.Get("/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
h, params, found := r.Lookup("GET", "/api/v1/users")
|
h, params, found := r.Lookup("GET", "/api/v1/users")
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
@ -309,7 +310,7 @@ func TestGroup(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
api.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
api.Get("/users", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
h, params, found := r.Lookup("GET", "/api/users")
|
h, params, found := r.Lookup("GET", "/api/users")
|
||||||
assert.True(t, found)
|
assert.True(t, found)
|
||||||
@ -339,7 +340,7 @@ func TestGroup(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
v1.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {
|
v1.Get("/users", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
order = append(order, 3)
|
order = append(order, 3)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -373,7 +374,7 @@ func TestGroup(t *testing.T) {
|
|||||||
order = append(order, 2)
|
order = append(order, 2)
|
||||||
next.Serve(params)
|
next.Serve(params)
|
||||||
})
|
})
|
||||||
}).GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {
|
}).Get("/users", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
order = append(order, 3)
|
order = append(order, 3)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -395,10 +396,10 @@ func BenchmarkRouterLookup(b *testing.B) {
|
|||||||
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
||||||
|
|
||||||
// Setup routes for benchmarking
|
// Setup routes for benchmarking
|
||||||
r.GetParam("/", handler)
|
r.Get("/", handler)
|
||||||
r.GetParam("/users/all", handler)
|
r.Get("/users/all", handler)
|
||||||
r.GetParam("/users/[id]", handler)
|
r.Get("/users/[id]", handler)
|
||||||
r.GetParam("/users/[id]/posts/[postId]", handler)
|
r.Get("/users/[id]/posts/[postId]", handler)
|
||||||
|
|
||||||
b.Run("root", func(b *testing.B) {
|
b.Run("root", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -435,9 +436,9 @@ func BenchmarkParallelLookup(b *testing.B) {
|
|||||||
r := New()
|
r := New()
|
||||||
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
||||||
|
|
||||||
r.GetParam("/users/[id]", handler)
|
r.Get("/users/[id]", handler)
|
||||||
r.GetParam("/posts/[id]/comments", handler)
|
r.Get("/posts/[id]/comments", handler)
|
||||||
r.GetParam("/products/[category]/[id]", handler)
|
r.Get("/products/[category]/[id]", handler)
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
i := 0
|
i := 0
|
||||||
@ -460,8 +461,8 @@ func BenchmarkWildcardLookup(b *testing.B) {
|
|||||||
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
||||||
|
|
||||||
// Setup routes for benchmarking
|
// Setup routes for benchmarking
|
||||||
r.GetParam("/files/*path", handler)
|
r.Get("/files/*path", handler)
|
||||||
r.GetParam("/users/[id]/*action", handler)
|
r.Get("/users/[id]/*action", handler)
|
||||||
|
|
||||||
b.Run("simple_wildcard", func(b *testing.B) {
|
b.Run("simple_wildcard", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -485,7 +486,7 @@ func BenchmarkMiddleware(b *testing.B) {
|
|||||||
|
|
||||||
b.Run("no_middleware", func(b *testing.B) {
|
b.Run("no_middleware", func(b *testing.B) {
|
||||||
r := New()
|
r := New()
|
||||||
r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
r.Get("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -497,7 +498,7 @@ func BenchmarkMiddleware(b *testing.B) {
|
|||||||
b.Run("one_middleware", func(b *testing.B) {
|
b.Run("one_middleware", func(b *testing.B) {
|
||||||
r := New()
|
r := New()
|
||||||
r.Use(passthrough)
|
r.Use(passthrough)
|
||||||
r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
r.Get("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -511,7 +512,7 @@ func BenchmarkMiddleware(b *testing.B) {
|
|||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
r.Use(passthrough)
|
r.Use(passthrough)
|
||||||
}
|
}
|
||||||
r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
r.Get("/test", func(w http.ResponseWriter, r *http.Request, params []string) {})
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -526,7 +527,7 @@ func BenchmarkGroups(b *testing.B) {
|
|||||||
|
|
||||||
b.Run("flat_route", func(b *testing.B) {
|
b.Run("flat_route", func(b *testing.B) {
|
||||||
r := New()
|
r := New()
|
||||||
r.GetParam("/api/v1/users", handler)
|
r.Get("/api/v1/users", handler)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -538,7 +539,7 @@ func BenchmarkGroups(b *testing.B) {
|
|||||||
r := New()
|
r := New()
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
v1 := api.Group("/v1")
|
v1 := api.Group("/v1")
|
||||||
v1.GetParam("/users", handler)
|
v1.Get("/users", handler)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -555,7 +556,7 @@ func BenchmarkGroups(b *testing.B) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
v1 := api.Group("/v1")
|
v1 := api.Group("/v1")
|
||||||
v1.GetParam("/users", handler)
|
v1.Get("/users", handler)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@ -565,15 +566,77 @@ func BenchmarkGroups(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkNetHttpServer(b *testing.B) {
|
||||||
|
r := New()
|
||||||
|
|
||||||
|
// Set up test routes
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
|
// No-op for benchmarking
|
||||||
|
})
|
||||||
|
r.Get("/users/all", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
|
// No-op for benchmarking
|
||||||
|
})
|
||||||
|
r.Get("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) {
|
||||||
|
// No-op for benchmarking
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create test server
|
||||||
|
server := httptest.NewServer(r)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
// Create HTTP client
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DisableKeepAlives: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Run("root", func(b *testing.B) {
|
||||||
|
url := server.URL + "/"
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
resp, err := client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("static_path", func(b *testing.B) {
|
||||||
|
url := server.URL + "/users/all"
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
resp, err := client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("param_path", func(b *testing.B) {
|
||||||
|
url := server.URL + "/users/123"
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
resp, err := client.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkComparison(b *testing.B) {
|
func BenchmarkComparison(b *testing.B) {
|
||||||
// Custom router setup
|
// Custom router setup
|
||||||
customRouter := New()
|
customRouter := New()
|
||||||
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
handler := func(w http.ResponseWriter, r *http.Request, params []string) {}
|
||||||
|
|
||||||
customRouter.GetParam("/", handler)
|
customRouter.Get("/", handler)
|
||||||
customRouter.GetParam("/users/all", handler)
|
customRouter.Get("/users/all", handler)
|
||||||
customRouter.GetParam("/users/[id]", handler)
|
customRouter.Get("/users/[id]", handler)
|
||||||
customRouter.GetParam("/users/[id]/posts/[postId]", handler)
|
customRouter.Get("/users/[id]/posts/[postId]", handler)
|
||||||
|
|
||||||
// Standard mux setup
|
// Standard mux setup
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user