173 lines
4.3 KiB
Markdown
173 lines
4.3 KiB
Markdown
# Router
|
|
|
|
A high-performance router for Go with support for path parameters, wildcards, middleware, and route grouping. Sports an incredibly simple API over the top of a prefix tree data structure. Minimal allocations and, dare we say, 🔥*blazingly* fast🔥.
|
|
|
|
## Features
|
|
- Zero dependencies
|
|
- Fast path matching with radix tree structure
|
|
- Support for path parameters (`[id]`) and wildcards (`*path`)
|
|
- Middleware support for request processing pipelines
|
|
- Route grouping with shared prefixes and middleware
|
|
- Adapters for net/http and fasthttp (optional)
|
|
- Up to 15x faster than `http.ServeMux` for dynamic routes
|
|
- Zero allocations for static routes
|
|
|
|
## Installation
|
|
|
|
```shell
|
|
go get git.sharkk.net/Go/Router
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Basic Routing
|
|
|
|
```go
|
|
// Create a new router
|
|
r := router.New()
|
|
|
|
// Static routes
|
|
r.Get("/", router.FuncHandler(func(params []string) {
|
|
fmt.Println("Root handler")
|
|
}))
|
|
|
|
// Parameter routes
|
|
r.Get("/users/[id]", router.FuncHandler(func(params []string) {
|
|
userID := params[0]
|
|
fmt.Printf("User ID: %s\n", userID)
|
|
}))
|
|
|
|
// Wildcard routes
|
|
r.Get("/files/*path", router.FuncHandler(func(params []string) {
|
|
filePath := params[0]
|
|
fmt.Printf("File path: %s\n", filePath)
|
|
}))
|
|
|
|
// Lookup routes
|
|
if handler, params, ok := r.Lookup("GET", "/users/123"); ok {
|
|
handler.Serve(params)
|
|
}
|
|
```
|
|
|
|
### Middleware
|
|
|
|
```go
|
|
// Create logging middleware
|
|
func LoggingMiddleware(next router.Handler) router.Handler {
|
|
return router.FuncHandler(func(params []string) {
|
|
fmt.Println("Request started")
|
|
next.Serve(params)
|
|
fmt.Println("Request completed")
|
|
})
|
|
}
|
|
|
|
// Apply middleware globally
|
|
r := router.New()
|
|
r.Use(LoggingMiddleware)
|
|
|
|
// Apply middleware to specific routes
|
|
r.WithMiddleware(AuthMiddleware).Get("/admin", adminHandler)
|
|
```
|
|
|
|
### Route Groups
|
|
|
|
```go
|
|
// Create a router
|
|
r := router.New()
|
|
|
|
// Create an API group
|
|
api := r.Group("/api")
|
|
api.Get("/users", listUsersHandler) // matches /api/users
|
|
|
|
// Nested groups
|
|
v1 := api.Group("/v1")
|
|
v1.Get("/products", listProductsHandler) // matches /api/v1/products
|
|
|
|
// Group with middleware
|
|
admin := api.Group("/admin")
|
|
admin.Use(AuthMiddleware)
|
|
admin.Get("/stats", statsHandler) // matches /api/admin/stats
|
|
```
|
|
|
|
### HTTP Integration
|
|
|
|
```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
|
|
|
|
Benchmark results comparing our router to the standard `http.ServeMux` on AMD Ryzen 9 7950X:
|
|
|
|
```
|
|
cpu: AMD Ryzen 9 7950X 16-Core Processor
|
|
|
|
BenchmarkComparison/root_path
|
|
Router: 2.098 ns/op 0 B/op 0 allocs/op
|
|
ServeMux: 32.010 ns/op 0 B/op 0 allocs/op
|
|
|
|
BenchmarkComparison/static_path
|
|
Router: 16.050 ns/op 0 B/op 0 allocs/op
|
|
ServeMux: 67.980 ns/op 0 B/op 0 allocs/op
|
|
|
|
BenchmarkComparison/dynamic_path
|
|
Router: 39.170 ns/op 16 B/op 1 allocs/op
|
|
ServeMux: 174.000 ns/op 48 B/op 3 allocs/op
|
|
|
|
BenchmarkComparison/not_found
|
|
Router: 10.580 ns/op 0 B/op 0 allocs/op
|
|
ServeMux: 178.100 ns/op 56 B/op 3 allocs/op
|
|
```
|
|
|
|
Key Performance Points:
|
|
- Root path lookups are 15x faster
|
|
- Static paths are 4x faster with zero allocations
|
|
- Dynamic paths are 4.4x faster with fewer allocations
|
|
- Not found paths are 16.8x faster with zero allocations
|
|
|
|
## License
|
|
|
|
[Sharkk Minimal License](LICENSE); do what you like!
|