mirror of
https://github.com/yusing/godoxy.git
synced 2026-01-11 14:20:32 +01:00
- Add OnDefault rule type that matches when no other rules match - Add validation to prevent multiple default rules - Fix typo: extension → extensions in route config JSON tag
Route
Provides HTTP routing, reverse proxy, file serving, and TCP/UDP stream proxying for GoDoxy.
Overview
The internal/route package implements the core routing system for GoDoxy. It handles reverse proxying HTTP requests, serving static files, and proxying TCP/UDP streams. Routes can be discovered from Docker containers, YAML files, or remote agents.
Primary Consumers
- Route providers: Create and manage route instances
- HTTP server: Dispatches requests to route handlers
- Configuration layer: Validates and loads route configs
Non-goals
- Does not implement container runtime operations (delegates to providers)
- Does not handle authentication (delegates to middleware/rules)
- Does not manage health checks (delegates to
internal/health/monitor)
Stability
Internal package with stable core types. Route configuration schema is versioned.
Public API
Exported Types
type Route struct {
Alias string // Unique route identifier
Scheme Scheme // http, https, h2c, tcp, udp, fileserver
Host string // Virtual host
Port Port // Listen and target ports
// File serving
Root string // Document root
SPA bool // Single-page app mode
Index string // Index file
// Route rules and middleware
HTTPConfig
PathPatterns []string
Rules rules.Rules
RuleFile string
// Health and load balancing
HealthCheck types.HealthCheckConfig
LoadBalance *types.LoadBalancerConfig
// Additional features
Middlewares map[string]types.LabelMap
Homepage *homepage.ItemConfig
AccessLog *accesslog.RequestLoggerConfig
Agent string
Idlewatcher *types.IdlewatcherConfig
Metadata
}
type Scheme string
const (
SchemeHTTP Scheme = "http"
SchemeHTTPS Scheme = "https"
SchemeH2C Scheme = "h2c"
SchemeTCP Scheme = "tcp"
SchemeUDP Scheme = "udp"
SchemeFileServer Scheme = "fileserver"
)
type ExcludedReason int
const (
ExcludedReasonNone ExcludedReason = iota
ExcludedReasonError
ExcludedReasonManual
ExcludedReasonNoPortContainer
ExcludedReasonNoPortSpecified
ExcludedReasonBlacklisted
ExcludedReasonBuildx
ExcludedReasonOld
)
Exported Functions/Methods
// Validation and lifecycle
func (r *Route) Validate() gperr.Error
func (r *Route) Start(parent task.Parent) gperr.Error
func (r *Route) Finish(reason any)
func (r *Route) Started() <-chan struct{}
// Route queries
func (r *Route) Impl() types.Route
func (r *Route) Task() *task.Task
func (r *Route) ProviderName() string
func (r *Route) TargetURL() *nettypes.URL
func (r *Route) References() []string
// Status queries
func (r *Route) ShouldExclude() bool
func (r *Route) UseLoadBalance() bool
func (r *Route) UseIdleWatcher() bool
func (r *Route) UseHealthCheck() bool
Architecture
Core Components
classDiagram
class Route {
+Validate() gperr.Error
+Start(parent) gperr.Error
+Finish(reason)
+Started() <-chan struct#123;#125;
}
class Metadata {
+impl types.Route
+task *task.Task
+started chan struct#123;#125;
}
class HealthMonitor {
+Start(parent) error
+Healthy() bool
+URL() string
}
Route --> Metadata : contains
Route --> HealthMonitor : optional
Route Types
graph TD
Route --> HTTPRoute
Route --> StreamRoute
HTTPRoute --> ReverseProxyRoute
HTTPRoute --> FileServer
StreamRoute --> TCPStream
StreamRoute --> UDPStream
Request Processing Pipeline
flowchart LR
A[Request] --> B[Route Matching]
B --> C{Route Type}
C -->|HTTP| D[Middleware]
C -->|FileServer| E[File System]
C -->|Stream| F[TCP/UDP Proxy]
D --> G[Rules Engine]
G --> H[Upstream]
H --> I[Response]
E --> I
F --> I
Reverse Proxy Flow
sequenceDiagram
participant C as Client
participant P as Proxy
participant L as Load Balancer
participant B as Backend
C->>P: GET /
P->>L: Select Backend
L-->>P: Backend1
P->>B: Forward Request
B-->>P: 200 OK
P-->>C: Response
Configuration Surface
Route Configuration
type Route struct {
Alias string `json:"alias"`
Scheme Scheme `json:"scheme"`
Host string `json:"host,omitempty"`
Port Port `json:"port"`
Root string `json:"root,omitempty"`
SPA bool `json:"spa,omitempty"`
Index string `json:"index,omitempty"`
// ... additional fields
}
Docker Labels
labels:
proxy.aliases: myapp
proxy.myapp.port: 3000
YAML Configuration
routes:
myapp:
scheme: http
root: /var/www/myapp
spa: true
Dependency and Integration Map
| Dependency | Purpose |
|---|---|
internal/route/routes |
Route registry and lookup |
internal/route/rules |
Request/response rule processing |
internal/route/stream |
TCP/UDP stream proxying |
internal/route/provider |
Route discovery and loading |
internal/health/monitor |
Health checking |
internal/idlewatcher |
Idle container management |
internal/logging/accesslog |
Request logging |
internal/homepage |
Dashboard integration |
github.com/yusing/goutils/errs |
Error handling |
Observability
Logs
- INFO: Route start/stop, validation results
- DEBUG: Request processing details
- ERROR: Proxy failures, health check failures
Log context includes: alias, host, method, path, status
Metrics
Health check metrics via internal/health/monitor:
health_check_totalhealth_check_failure_totalhealth_check_duration_seconds
Security Considerations
- Route matching validates host and path patterns
- Upstream URL validation prevents SSRF attacks
- Rules engine can enforce authentication/authorization
- ACL integration available for IP-based access control
Failure Modes and Recovery
| Failure | Behavior | Recovery |
|---|---|---|
| Backend unavailable | Returns 502 error | Fix backend service |
| Health check fails | Route marked unhealthy | Fix backend health endpoint |
| Route validation fails | Route excluded with reason | Fix configuration |
| TLS handshake fails | Connection error | Fix certificates |
| Load balancer no backends | Returns 503 error | Add healthy backends |
Usage Examples
Creating a Basic HTTP Route
route := &route.Route{
Alias: "myapp",
Scheme: route.SchemeHTTP,
Host: "myapp.local",
Port: route.Port{Proxy: 8080, Target: 3000},
}
if err := route.Validate(); err != nil {
return err
}
if err := route.Start(parent); err != nil {
return err
}
Route with Health Check
route := &route.Route{
Alias: "myservice",
HealthCheck: types.HealthCheckConfig{
Path: "/health",
Interval: 30 * time.Second,
Timeout: 5 * time.Second,
},
}
File Server Route
route := &route.Route{
Alias: "files",
Scheme: route.SchemeFileServer,
Root: "/var/www/files",
SPA: false,
Index: "index.html",
}
Testing Notes
- Unit tests for validation logic
- Integration tests with real backends
- Mock health monitors for testing
- Route exclusion tests cover all reason codes