# internal/route/rules Implements a rule engine for HTTP request/response processing, enabling conditional routing, header manipulation, authentication, and more. ## Overview The `internal/route/rules` package provides a powerful rule engine for GoDoxy. Rules allow conditional processing of HTTP requests and responses based on various matchers (headers, path, method, IP, etc.). Matching rules can modify requests, route to different backends, or terminate processing. ### Primary Consumers - **Route layer**: Applies rules during request processing - **Configuration system**: Parses rule YAML - **Middleware integration**: Extends rule capabilities ### Non-goals - Does not implement proxy transport (delegates to reverse proxy) - Does not handle TLS/SSL (handled at entrypoint) - Does not perform health checking ### Stability Internal package with stable YAML schema. Backward-compatible additions to rule types are allowed. ## Public API ### Exported Types ```go type Rules []Rule type Rule struct { Name string // Rule identifier for debugging On RuleOn // Condition matcher Do Command // Action to execute } type RuleOn struct { raw string checker Checker phase PhaseFlag } type Command struct { raw string pre Commands post Commands } ``` ### Exported Functions ```go // BuildHandler converts rules to an HTTP handler func (rules Rules) BuildHandler(up http.HandlerFunc) http.HandlerFunc // ParseRules parses rule configuration func ParseRules(config string) (Rules, error) // ValidateRules validates rule syntax func ValidateRules(config string) error // Validate validates rule semantics (e.g., prevents multiple default rules) func (rules Rules) Validate() gperr.Error ``` ## Architecture ### Core Components ```mermaid classDiagram class Rules { +BuildHandler(up) http.HandlerFunc } class Rule { +Name string +On RuleOn +Do Command +IsResponseRule() bool } class RuleOn { +raw string +checker Checker +isResponseChecker bool } class Command { +raw string +exec CommandHandler +isResponseHandler bool } class Checker { <> +Check(r *http.Request) bool +CheckResponse(w ResponseWriter, r *http.Request) bool } class CommandHandler { <> +Execute(w ResponseWriter, r *http.Request, rm *ResponseModifier) gperr.Error } Rules --> Rule Rule --> RuleOn Rule --> Command RuleOn --> Checker Command --> CommandHandler ``` ### Request Processing Flow ```mermaid sequenceDiagram participant Req as Request participant Pre as Pre Rules participant Proxy as Upstream participant Post as Post Rules Req->>Pre: Check pre-rules alt Rule matches Pre->>Pre: Execute handler alt Terminating action Pre-->>Req: Response Note right of Pre: Stop remaining pre commands end end opt No pre termination Req->>Proxy: Forward request Proxy-->>Req: Response end Req->>Post: Run scheduled post commands Req->>Post: Evaluate response matchers Post->>Post: Execute matched post handlers Post-->>Req: Final response ``` ### Execution Model (Authoritative) Rules run in two phases: 1. **Pre phase** - Evaluate only request-based matchers (`path`, `method`, `header`, `remote`, etc.) in declaration order. - Execute matched rule `do` pre-commands in order. - If a default rule exists (`name: default` or `on: default`), it is a fallback and runs only when no non-default pre rule matches. - If a terminating action runs, stop: - remaining commands in that rule - all later pre-phase commands. - Exception: rules that only contain post commands (no pre commands) are still scheduled for post phase. 2. **Upstream phase** - Upstream is called only if pre phase did not terminate. 3. **Post phase** - Run post-commands for rules whose pre phase executed, except rules that terminated in pre. - Then evaluate response-based matchers (`status`, `resp_header`) and execute their `do` commands. - Response-based rules run even when the response was produced in pre phase. **Important:** termination is explicit by command semantics, not inferred from status-code mutation. ### Phase Flags Rule and command parsing tracks phase requirements via `PhaseFlag`: - `PhasePre` - `PhasePost` - `PhasePre | PhasePost` (combined) Combined flags are expected for nested/compound commands and variable templates that may need both request and response context. ### Condition Matchers | Matcher | Type | Description | | ------------- | -------- | ---------------------------- | | `header` | Request | Match request header value | | `query` | Request | Match query parameter | | `cookie` | Request | Match cookie value | | `form` | Request | Match form field | | `method` | Request | Match HTTP method | | `host` | Request | Match virtual host | | `path` | Request | Match request path | | `proto` | Request | Match protocol (http/https) | | `remote` | Request | Match remote IP/CIDR | | `basic_auth` | Request | Match basic auth credentials | | `route` | Request | Match route name | | `resp_header` | Response | Match response header | | `status` | Response | Match status code range | ### Matcher Types ```sh # String: exact match (default) # Glob: shell-style wildcards (*, ?) # Regex: regular expressions path /api/users // exact match path glob("/api/*") // glob pattern path regex("/api/v[0-9]+/.*") // regex pattern ``` ### Actions **Terminating Actions** (stop processing): | Command | Description | | ------------------------------ | ------------------------------------- | | `upstream` / `bypass` / `pass` | Call upstream and terminate pre-phase | | `error ` | Return HTTP error | | `redirect ` | Redirect to URL | | `serve ` | Serve local files | | `route ` | Route to another route | | `proxy ` | Proxy to upstream | | `require_basic_auth ` | Return 401 challenge | **Non-Terminating Actions** (modify and continue): | Command | Description | | ------------------------------ | ---------------------- | | `rewrite ` | Rewrite request path | | `require_auth` | Require authentication | | `set ` | Set header/variable | | `add ` | Add header/variable | | `remove ` | Remove header/variable | **Response Actions**: | Command | Description | | ------------------------------------------ | ----------------- | | `log