mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-17 22:19:42 +02:00
308 lines
7.3 KiB
Markdown
308 lines
7.3 KiB
Markdown
# 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
|
|
|
|
```go
|
|
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) lookup
|
|
- `Add(r T)` - Register route
|
|
- `Del(r T)` - Unregister route
|
|
- `Size() int` - Route count
|
|
- `Clear()` - Remove all routes
|
|
- `Iter` - Channel-based iteration
|
|
|
|
### Exported Functions
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
// 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:
|
|
|
|
```go
|
|
// Route linked to LXC container
|
|
route.Proxmox = &proxmox.NodeConfig{
|
|
Node: "pve-node-01",
|
|
VMID: 100,
|
|
VMName: "my-container",
|
|
}
|
|
```
|
|
|
|
### Lookup Priority
|
|
|
|
1. **Node match** - If hostname, IP, or alias matches a Proxmox node
|
|
2. **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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
- `WithRouteContext` uses `unsafe.Pointer` to 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
|
|
|
|
```go
|
|
route, ok := routes.Get("myapp")
|
|
if !ok {
|
|
return fmt.Errorf("route not found")
|
|
}
|
|
```
|
|
|
|
### Iterating Over All Routes
|
|
|
|
```go
|
|
for r := range routes.IterActive {
|
|
log.Printf("Route: %s", r.Name())
|
|
}
|
|
```
|
|
|
|
### Getting Health Status
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|