mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-26 11:01:07 +01:00
344 lines
8.3 KiB
Markdown
344 lines
8.3 KiB
Markdown
# internal/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
|
|
|
|
```go
|
|
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
|
|
}
|
|
```
|
|
|
|
```go
|
|
type Scheme string
|
|
|
|
const (
|
|
SchemeHTTP Scheme = "http"
|
|
SchemeHTTPS Scheme = "https"
|
|
SchemeH2C Scheme = "h2c"
|
|
SchemeTCP Scheme = "tcp"
|
|
SchemeUDP Scheme = "udp"
|
|
SchemeFileServer Scheme = "fileserver"
|
|
)
|
|
```
|
|
|
|
```go
|
|
type ExcludedReason int
|
|
|
|
const (
|
|
ExcludedReasonNone ExcludedReason = iota
|
|
ExcludedReasonError
|
|
ExcludedReasonManual
|
|
ExcludedReasonNoPortContainer
|
|
ExcludedReasonNoPortSpecified
|
|
ExcludedReasonBlacklisted
|
|
ExcludedReasonBuildx
|
|
ExcludedReasonOld
|
|
)
|
|
```
|
|
|
|
### Exported Functions/Methods
|
|
|
|
```go
|
|
// 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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
graph TD
|
|
Route --> HTTPRoute
|
|
Route --> StreamRoute
|
|
|
|
HTTPRoute --> ReverseProxyRoute
|
|
HTTPRoute --> FileServer
|
|
|
|
StreamRoute --> TCPStream
|
|
StreamRoute --> UDPStream
|
|
```
|
|
|
|
### Request Processing Pipeline
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```go
|
|
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
|
|
|
|
```yaml
|
|
labels:
|
|
proxy.aliases: myapp
|
|
proxy.myapp.port: 3000
|
|
```
|
|
|
|
### YAML Configuration
|
|
|
|
```yaml
|
|
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_total`
|
|
- `health_check_failure_total`
|
|
- `health_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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
route := &route.Route{
|
|
Alias: "myservice",
|
|
HealthCheck: types.HealthCheckConfig{
|
|
Path: "/health",
|
|
Interval: 30 * time.Second,
|
|
Timeout: 5 * time.Second,
|
|
},
|
|
}
|
|
```
|
|
|
|
### Route with Custom Bind Address
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|