support for parameters, middleware, and route groups.

This commit is contained in:
2025-10-26 12:25:18 +01:00
parent 6c318a988c
commit 6f87960f3a
4 changed files with 1117 additions and 149 deletions

256
README.md
View File

@@ -1,11 +1,23 @@
# Sux
Static route http router that considers the request method
Static route http router that considers the request method with support for parameters, middleware, and route groups.
Useful for serving server-side rendered content.
## Features
- **High Performance**: Optimized trie-based routing with minimal allocations
- **URL Parameters**: Support for `:param` and `*wildcard` parameters
- **Middleware**: Global and route-specific middleware support
- **Route Groups**: Group routes with shared prefixes and middleware
- **Thread-Safe**: No global state - multiple router instances supported
- **Method Not Allowed**: Proper 405 responses when path exists but method doesn't
- **Custom Handlers**: Custom 404 and 405 handlers
## How
### Basic Usage
```go
package main
@@ -32,7 +44,134 @@ func Simple(w http.ResponseWriter, r *http.Request) {
}
```
### Performance
### URL Parameters
```go
r := sux.New()
// Named parameters
r.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
params := sux.ParamsFromContext(r)
id := params["id"]
io.WriteString(w, "User ID: "+id)
})
// Wildcard parameters (captures rest of path)
r.GET("/files/*path", func(w http.ResponseWriter, r *http.Request) {
params := sux.ParamsFromContext(r)
path := params["path"]
io.WriteString(w, "File path: "+path)
})
// Multiple parameters
r.GET("/users/:userId/posts/:postId", func(w http.ResponseWriter, r *http.Request) {
params := sux.ParamsFromContext(r)
userId := params["userId"]
postId := params["postId"]
io.WriteString(w, "User "+userId+", Post "+postId)
})
```
### Middleware
```go
r := sux.New()
// Global middleware
r.Use(loggingMiddleware, authMiddleware)
// Route-specific middleware
r.GET("/admin", adminOnlyMiddleware(adminHandler))
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
})
}
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check authentication
if !isAuthenticated(r) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
```
### Route Groups
```go
r := sux.New()
// API group with middleware
api := r.Group("/api", apiVersionMiddleware, corsMiddleware)
// Group routes automatically get the prefix and middleware
api.GET("/users", listUsers)
api.POST("/users", createUser)
api.GET("/users/:id", getUser)
// Nested groups
v1 := api.Group("/v1")
v1.GET("/posts", listPostsV1)
v2 := api.Group("/v2")
v2.GET("/posts", listPostsV2)
```
### Custom Handlers
```go
r := sux.New()
// Custom 404 handler
r.NotFound(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Custom 404 - Page not found"))
})
// Custom 405 handler
r.MethodNotAllowed(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("Custom 405 - Method not allowed"))
})
```
### Multiple Router Instances
```go
// Each router is independent and thread-safe
mainRouter := sux.New()
mainRouter.GET("/", homeHandler)
apiRouter := sux.New()
apiRouter.GET("/users", usersHandler)
// Use different routers for different purposes
go http.ListenAndServe(":8080", mainRouter)
go http.ListenAndServe(":8081", apiRouter)
```
## Performance
The router is optimized for high performance with minimal allocations:
```
BenchmarkStaticRoute-8 2011203 877.6 ns/op 644 B/op 8 allocs/op
BenchmarkParameterRoute-8 1388089 943.1 ns/op 576 B/op 6 allocs/op
BenchmarkWildcardRoute-8 986684 1100 ns/op 656 B/op 8 allocs/op
BenchmarkMultipleParameters-8 811143 1520 ns/op 768 B/op 8 allocs/op
BenchmarkMiddleware-8 575060 2479 ns/op 1472 B/op 17 allocs/op
BenchmarkRouteGroups-8 569205 1889 ns/op 1352 B/op 12 allocs/op
```
### Performance Comparison
Compared to other popular Go routers:
```
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/
@@ -44,16 +183,6 @@ Running 30s test @ http://inuc:8080/
4260394 requests in 30.00s, 491.63MB read
Requests/sec: 142025.60
Transfer/sec: 16.39MB
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple?name=Darko
Running 30s test @ http://inuc:8080/simple?name=Darko
8 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 8.11ms 3.84ms 243.61ms 89.96%
Req/Sec 16.01k 2.20k 23.88k 72.87%
3723315 requests in 30.00s, 454.51MB read
Requests/sec: 124116.94
Transfer/sec: 15.15MB
```
Compared to https://github.com/julienschmidt/httprouter
@@ -67,64 +196,57 @@ Running 30s test @ http://inuc:8080/
4224358 requests in 30.00s, 487.47MB read
Requests/sec: 140826.15
Transfer/sec: 16.25MB
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple/Darko
Running 30s test @ http://inuc:8080/simple/Darko
8 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 7.78ms 3.40ms 60.03ms 88.79%
Req/Sec 16.55k 1.86k 19.95k 66.95%
3873629 requests in 30.00s, 472.86MB read
Requests/sec: 129131.98
Transfer/sec: 15.76MB
```
Compared to https://github.com/gocraft/web
```
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/
Running 30s test @ http://inuc:8080/
8 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 9.07ms 4.58ms 420.89ms 91.40%
Req/Sec 14.42k 2.28k 25.00k 80.03%
3309152 requests in 30.00s, 378.70MB read
Requests/sec: 110315.36
Transfer/sec: 12.62MB
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple
Running 30s test @ http://inuc:8080/simple
8 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 9.86ms 4.00ms 66.98ms 87.79%
Req/Sec 13.05k 1.65k 17.52k 69.71%
3055899 requests in 30.00s, 375.95MB read
Requests/sec: 101874.46
Transfer/sec: 12.53MB
```
## API Reference
Compared to https://github.com/gomango/mux
```
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/
Running 30s test @ http://inuc:8080/
8 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 9.22ms 12.73ms 235.88ms 84.36%
Req/Sec 13.78k 1.77k 25.52k 74.17%
3242004 requests in 30.00s, 377.20MB read
Socket errors: connect 0, read 0, write 0, timeout 10
Requests/sec: 108078.84
Transfer/sec: 12.57MB
darko@arch ~ $ wrk -c1000 -t8 -d30s http://inuc:8080/simple
Running 30s test @ http://inuc:8080/simple
8 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 16.24ms 14.39ms 150.41ms 56.30%
Req/Sec 7.77k 593.31 9.78k 68.20%
1841839 requests in 30.00s, 226.59MB read
Requests/sec: 61402.97
Transfer/sec: 7.55MB
```
### Router Methods
Apples and Oranges but not quite
- `New() *Router` - Creates a new router instance
- `GET(path string, handler http.HandlerFunc) *Router`
- `POST(path string, handler http.HandlerFunc) *Router`
- `PUT(path string, handler http.HandlerFunc) *Router`
- `PATCH(path string, handler http.HandlerFunc) *Router`
- `DELETE(path string, handler http.HandlerFunc) *Router`
- `OPTIONS(path string, handler http.HandlerFunc) *Router`
- `HEAD(path string, handler http.HandlerFunc) *Router`
Hardware
### Middleware
http://www.intel.com/content/www/us/en/nuc/nuc-kit-d54250wykh.html
- `Use(middleware ...MiddlewareFunc)` - Add global middleware
- `Group(prefix string, middleware ...MiddlewareFunc) *Router` - Create route group
### Custom Handlers
- `NotFound(handler http.HandlerFunc)` - Set custom 404 handler
- `MethodNotAllowed(handler http.HandlerFunc)` - Set custom 405 handler
### Parameter Extraction
- `ParamsFromContext(r *http.Request) Params` - Extract route parameters from request
## Parameter Types
### Named Parameters (`:param`)
- Matches a single path segment
- Extracted by name from the context
- Example: `/users/:id` matches `/users/123`
### Wildcard Parameters (`*param`)
- Matches one or more path segments
- Captures the rest of the path
- Example: `/files/*path` matches `/files/docs/readme.txt`
## Thread Safety
The router is completely thread-safe:
- No global state
- Multiple router instances can be used concurrently
- Safe for concurrent use from multiple goroutines
## License
MIT License - see LICENSE file for details.