mirror of
https://github.com/yusing/godoxy.git
synced 2026-02-22 08:47:48 +01:00
This is a large-scale refactoring across the codebase that replaces the custom `gperr.Error` type with Go's standard `error` interface. The changes include: - Replacing `gperr.Error` return types with `error` in function signatures - Using `errors.New()` and `fmt.Errorf()` instead of `gperr.New()` and `gperr.Errorf()` - Using `%w` format verb for error wrapping instead of `.With()` method - Replacing `gperr.Subject()` calls with `gperr.PrependSubject()` - Converting error logging from `gperr.Log*()` functions to zerolog's `.Err().Msg()` pattern - Update NewLogger to handle multiline error message - Updating `goutils` submodule to latest commit This refactoring aligns with Go idioms and removes the dependency on custom error handling abstractions in favor of standard library patterns.
8.3 KiB
8.3 KiB
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 // Target host
Port Port // Listen and target ports
Bind string // Bind address for listening (IP address, optional)
// 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() error
func (r *Route) Start(parent task.Parent) 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() error
+Start(parent) 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"`
Bind string `json:"bind,omitempty"` // Listen bind address
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
host: myapp.local
bind: 192.168.1.100 # Optional: bind to specific address
port:
proxy: 80
target: 3000
Dependency and Integration Map
| Dependency | Purpose |
|---|---|
internal/route/routes/context.go |
Route context helpers (only file) |
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,
},
}
Route with Custom Bind Address
route := &route.Route{
Alias: "myapp",
Scheme: route.SchemeHTTP,
Host: "myapp.local",
Bind: "192.168.1.100", // Bind to specific interface
Port: route.Port{Listening: 8443, Proxy: 80},
}
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