Route Registry
Provides centralized route registry with O(1) lookups and route context management for HTTP handlers.
Overview
The internal/route/routes package maintains the global route registry for GoDoxy. It provides thread-safe route lookups by alias, route iteration, and utilities for propagating route context through HTTP request handlers.
Primary Consumers
- HTTP handlers: Lookup routes and extract request context
- Route providers: Register and unregister routes
- Health system: Query route health status
- WebUI: Display route information
Non-goals
- Does not create or modify routes
- Does not handle route validation
- Does not implement routing logic (matching)
Stability
Internal package with stable public API.
Public API
Route Pools
var (
HTTP = pool.New[types.HTTPRoute]("http_routes")
Stream = pool.New[types.StreamRoute]("stream_routes")
Excluded = pool.New[types.Route]("excluded_routes")
)
Pool methods:
Get(alias string) (T, bool)- O(1) lookupAdd(r T)- Register routeDel(r T)- Unregister routeSize() int- Route countClear()- Remove all routesIter- Channel-based iteration
Exported Functions
// Iterate over active routes (HTTP + Stream)
func IterActive(yield func(r types.Route) bool)
// Iterate over all routes (HTTP + Stream + Excluded)
func IterAll(yield func(r types.Route) bool)
// Get route count
func NumActiveRoutes() int
func NumAllRoutes() int
// Clear all routes
func Clear()
// Lookup functions
func Get(alias string) (types.Route, bool)
func GetHTTPRouteOrExact(alias, host string) (types.HTTPRoute, bool)
Route Context
type RouteContext struct {
context.Context
Route types.HTTPRoute
}
// Attach route to request context (uses unsafe pointer for performance)
func WithRouteContext(r *http.Request, route types.HTTPRoute) *http.Request
// Extract route from request context
func TryGetRoute(r *http.Request) types.HTTPRoute
Upstream Information
func TryGetUpstreamName(r *http.Request) string
func TryGetUpstreamScheme(r *http.Request) string
func TryGetUpstreamHost(r *http.Request) string
func TryGetUpstreamPort(r *http.Request) string
func TryGetUpstreamHostPort(r *http.Request) string
func TryGetUpstreamAddr(r *http.Request) string
func TryGetUpstreamURL(r *http.Request) string
Health Information
type HealthInfo struct {
HealthInfoWithoutDetail
Detail string
}
type HealthInfoWithoutDetail struct {
Status types.HealthStatus
Uptime time.Duration
Latency time.Duration
}
func GetHealthInfo() map[string]HealthInfo
func GetHealthInfoWithoutDetail() map[string]HealthInfoWithoutDetail
func GetHealthInfoSimple() map[string]types.HealthStatus
Provider Grouping
func ByProvider() map[string][]types.Route
Proxmox Integration
Routes can be automatically linked to Proxmox nodes or LXC containers through reverse lookup during validation.
Node-Level Routes
Routes can be linked to a Proxmox node directly (VMID = 0) when the route's hostname, IP, or alias matches a node name or IP:
// Route linked to Proxmox node (no specific VM)
route.Proxmox = &proxmox.NodeConfig{
Node: "pve-node-01",
VMID: 0, // node-level, no container
VMName: "",
}
Container-Level Routes
Routes are linked to LXC containers when they match a VM resource by hostname, IP, or alias:
// Route linked to LXC container
route.Proxmox = &proxmox.NodeConfig{
Node: "pve-node-01",
VMID: 100,
VMName: "my-container",
}
Lookup Priority
- Node match - If hostname, IP, or alias matches a Proxmox node
- VM match - If hostname, IP, or alias matches a VM resource
Node-level routes skip container control logic (start/check IPs) and can be used to proxy node services directly.
Architecture
Core Components
classDiagram
class HTTP
class Stream
class Excluded
class RouteContext
HTTP : +Get(alias) T
HTTP : +Add(r)
HTTP : +Del(r)
HTTP : +Size() int
HTTP : +Iter chan
Stream : +Get(alias) T
Stream : +Add(r)
Stream : +Del(r)
Excluded : +Get(alias) T
Excluded : +Add(r)
Excluded : +Del(r)
Route Lookup Flow
flowchart TD
A[Lookup Request] --> B{HTTP Pool}
B -->|Found| C[Return Route]
B -->|Not Found| D{Stream Pool}
D -->|Found| C
D -->|Not Found| E[Return nil]
Context Propagation
sequenceDiagram
participant H as HTTP Handler
participant R as Registry
participant C as RouteContext
H->>R: WithRouteContext(req, route)
R->>C: Attach route via unsafe pointer
C-->>H: Modified request
H->>R: TryGetRoute(req)
R->>C: Extract route from context
C-->>R: Route
R-->>H: Route
Dependency and Integration Map
| Dependency | Purpose |
|---|---|
internal/types |
Route and health type definitions |
internal/proxmox |
Proxmox node/container integration |
github.com/yusing/goutils/pool |
Thread-safe pool implementation |
Observability
Logs
Registry operations logged at DEBUG level:
- Route add/remove
- Pool iteration
- Context operations
Performance
WithRouteContextusesunsafe.Pointerto avoid request cloning- Route lookups are O(1) using internal maps
- Iteration uses channels for memory efficiency
Security Considerations
- Route context propagation is internal to the process
- No sensitive data exposed in context keys
- Routes are validated before registration
Failure Modes and Recovery
| Failure | Behavior | Recovery |
|---|---|---|
| Route not found | Returns (nil, false) | Verify route alias |
| Context extraction on non-route request | Returns nil | Check request origin |
| Concurrent modification during iteration | Handled by pool implementation | N/A |
Usage Examples
Basic Route Lookup
route, ok := routes.Get("myapp")
if !ok {
return fmt.Errorf("route not found")
}
Iterating Over All Routes
for r := range routes.IterActive {
log.Printf("Route: %s", r.Name())
}
Getting Health Status
healthMap := routes.GetHealthInfo()
for name, health := range healthMap {
log.Printf("Route %s: %s (uptime: %v)", name, health.Status, health.Uptime)
}
Using Route Context in Handler
func MyHandler(w http.ResponseWriter, r *http.Request) {
route := routes.TryGetRoute(r)
if route == nil {
http.Error(w, "Route not found", http.StatusNotFound)
return
}
upstreamHost := routes.TryGetUpstreamHost(r)
log.Printf("Proxying to: %s", upstreamHost)
}
Grouping Routes by Provider
byProvider := routes.ByProvider()
for providerName, routeList := range byProvider {
log.Printf("Provider %s: %d routes", providerName, len(routeList))
}
Testing Notes
- Unit tests for pool thread safety
- Context propagation tests
- Health info aggregation tests
- Provider grouping tests