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 :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

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 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

Middleware

  • 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.

Description
No description provided
Readme MIT 69 KiB
Languages
Go 100%