db42316b490394284b219cfc0b3241de7d8b884c
Sux
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
:paramand*wildcardparameters - 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
package main
import (
"code.icod.de/dalu/sux"
"io"
"net/http"
)
func main() {
r := sux.New()
r.GET("/", Hello)
r.GET("/simple", Simple)
http.ListenAndServe(":8080", r)
}
func Hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hello")
}
func Simple(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
io.WriteString(w, "hello "+name)
}
URL Parameters
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
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
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
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
// 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 hash map-based O(1) child lookups:
BenchmarkStaticRoute-8 1821735 798.5 ns/op 644 B/op 8 allocs/op
BenchmarkParameterRoute-8 1000000 1154 ns/op 576 B/op 6 allocs/op
BenchmarkWildcardRoute-8 757272 1676 ns/op 656 B/op 8 allocs/op
BenchmarkMultipleParameters-8 682251 1753 ns/op 768 B/op 8 allocs/op
BenchmarkMiddleware-8 753614 3782 ns/op 1472 B/op 17 allocs/op
BenchmarkRouteGroups-8 694045 2855 ns/op 1352 B/op 12 allocs/op
BenchmarkLargeRouter-8 1000000 1103 ns/op 576 B/op 6 allocs/op
Performance Improvements:
- Static routes: 9.3% faster with hash map optimization
- Multiple parameters: 12.2% faster
- Large router scenarios: 60.7% faster
Performance Comparison
Compared to other popular Go routers:
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 7.08ms 3.02ms 64.38ms 89.40%
Req/Sec 18.24k 2.11k 24.06k 73.88%
4260394 requests in 30.00s, 491.63MB read
Requests/sec: 142025.60
Transfer/sec: 16.39MB
Compared to https://github.com/julienschmidt/httprouter
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 7.14ms 3.13ms 74.06ms 89.99%
Req/Sec 18.18k 2.19k 23.31k 75.88%
4224358 requests in 30.00s, 487.47MB read
Requests/sec: 140826.15
Transfer/sec: 16.25MB
API Reference
Router Methods
New() *Router- Creates a new router instanceGET(path string, handler http.HandlerFunc) *RouterPOST(path string, handler http.HandlerFunc) *RouterPUT(path string, handler http.HandlerFunc) *RouterPATCH(path string, handler http.HandlerFunc) *RouterDELETE(path string, handler http.HandlerFunc) *RouterOPTIONS(path string, handler http.HandlerFunc) *RouterHEAD(path string, handler http.HandlerFunc) *Router
Middleware
Use(middleware ...MiddlewareFunc)- Add global middlewareGroup(prefix string, middleware ...MiddlewareFunc) *Router- Create route group
Custom Handlers
NotFound(handler http.HandlerFunc)- Set custom 404 handlerMethodNotAllowed(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/:idmatches/users/123
Wildcard Parameters (*param)
- Matches one or more path segments
- Captures the rest of the path
- Example:
/files/*pathmatches/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.
Languages
Go
100%