support for parameters, middleware, and route groups.
This commit is contained in:
256
README.md
256
README.md
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user