Documentation
¶
Overview ¶
Package rules implements pano's live traffic rules and breakpoints. An Engine satisfies proxy.Hooks: the proxy calls Request before contacting the origin and Response once response headers are known, and the engine matches the exchange against its rule set and applies the matching rules' actions.
Rules ¶
A rule (api.Rule) is a Match plus an ordered list of Actions. Rules are evaluated in priority order (higher first, then oldest first). Every field set on the Match must hold for the rule to fire:
- Host is a glob (internal/glob) matched against the flow host; a pattern containing ':' is matched against host:port instead.
- Path is a prefix ("/v1/"), a glob ("/v1/*/models"), or a regexp when wrapped in slashes and the inner text uses regexp syntax ("/^\/v[12]\//").
- Method is a case-insensitive list, Scheme is "http" or "https".
- Header maps a header name to a glob matched against the joined request header value; an empty glob only requires the header to be present.
- Status is a response-phase spec: "500", "4xx", "400-499", "!2xx", "200|204".
- Phase is request, response or both. When empty it is derived from the actions: a rule whose actions all run on the response side is a response rule, otherwise it is a request rule (or a response rule when Status is set).
Actions run in order. Each applied action appends a flow.RuleHit to the flow. mock, block and breakpoint (drop) end the evaluation. Action types:
delay sleep ms (+ random jitter_ms), cancelled with the request
set_header set name: value on the request or response
remove_header delete name
set_query set name=value on the request URL
rewrite_body json_patch (dotted paths -> values), regex + replace, or a
text/template with .Host .Path .Method .Status .Body and
.Header "Name"; gzip bodies are decoded and served plain
mock answer with status/headers/body without contacting the origin
mock_every_n like mock but only on every nth hit (value = n)
block mode reset (drop the connection), timeout (hang for ms),
or status (default: answer with status, default 502)
redirect send the request to upstream ("http://localhost:3000")
throttle limit the response body to kbps kilobytes per second
breakpoint park the exchange until Resume (alias: hold)
tag add tags to the flow
mock, mock_every_n, block, redirect and set_query default to the request side; throttle is response only; the rest apply in whichever phase the rule is evaluated unless On pins them. Probability gates the whole rule, MaxHits disables it once reached and TTLSeconds/Expires remove it lazily.
Breakpoints ¶
A breakpoint sets the flow to flow.StateHeld, publishes flow.EvHeld and blocks the exchange until Resume is called, the client goes away, or Options.HoldTimeout elapses. Resume may edit the parked request (URL, method, headers, body) or response (status, headers, body) before it continues, or drop it.
Concurrency and persistence ¶
The compiled rule set lives behind an atomic pointer and is replaced on every change, so the hot path is lock-free and allocates nothing for rules that do not match. Changes are written atomically to Options.PersistPath as a JSON array of api.Rule.
Index ¶
- Constants
- Variables
- type Engine
- func (e *Engine) Add(req api.RuleAddRequest) (api.Rule, error)
- func (e *Engine) Close() error
- func (e *Engine) Get(id string) (api.Rule, bool)
- func (e *Engine) Held() []api.Held
- func (e *Engine) List() []api.Rule
- func (e *Engine) Presets() []PresetInfo
- func (e *Engine) Remove(id string) error
- func (e *Engine) RemoveAll() int
- func (e *Engine) Request(ctx context.Context, f *flow.Flow, r *http.Request) proxy.Decision
- func (e *Engine) Response(ctx context.Context, f *flow.Flow, r *http.Request, resp *http.Response) proxy.Decision
- func (e *Engine) Resume(id flow.ID, req api.ResumeRequest) error
- func (e *Engine) Update(id string, p api.RulePatch) (api.Rule, error)
- func (e *Engine) Version() uint64
- type Options
- type PresetInfo
- type PresetParam
Constants ¶
const ( ActionDelay = "delay" ActionSetHeader = "set_header" ActionRemoveHeader = "remove_header" ActionSetQuery = "set_query" ActionRewriteBody = "rewrite_body" ActionMock = "mock" ActionMockEveryN = "mock_every_n" ActionBlock = "block" ActionRedirect = "redirect" ActionThrottle = "throttle" ActionBreakpoint = "breakpoint" ActionTag = "tag" )
Action types accepted in api.Action.Type.
const DefaultHoldTimeout = 120 * time.Second
DefaultHoldTimeout is how long a breakpoint parks an exchange before it auto-continues when Options.HoldTimeout is zero.
const MaxBodyBytes = 8 << 20
MaxBodyBytes bounds the body size the engine buffers to rewrite or hold an exchange. Larger bodies pass through untouched and the RuleHit says so.
Variables ¶
var ( // ErrNotFound is returned for unknown rule ids and held flow ids. ErrNotFound = errors.New("rules: not found") // ErrConflict is returned when adding a rule whose id already exists. ErrConflict = errors.New("rules: id already exists") // ErrInvalid is matched (errors.Is) by every validation failure. ErrInvalid = errors.New("rules: invalid") )
Functions ¶
This section is empty.
Types ¶
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine holds the rule set and the breakpoint registry. It implements proxy.Hooks. Mutations take a mutex; the hot path is lock-free.
func New ¶
New creates an engine, loading PersistPath when it exists. Rules whose expiry has passed are dropped at load; rules that no longer validate are skipped with a warning.
func (*Engine) Add ¶
Add creates a rule from req.Rule or from req.Preset and req.Params. Name and TTLS on the request override the rule's own. The returned rule is the normalised form with ID, CreatedAt, Enabled and Hits filled.
func (*Engine) Close ¶
Close releases every held exchange (they continue unmodified) and persists the final hit counters.
func (*Engine) Presets ¶
func (e *Engine) Presets() []PresetInfo
Presets lists the available presets with their parameters and defaults.
func (*Engine) Request ¶
Request implements proxy.Hooks. It runs the request-phase actions of every matching rule in order and stops at the first mock or block.
func (*Engine) Response ¶
func (e *Engine) Response(ctx context.Context, f *flow.Flow, r *http.Request, resp *http.Response) proxy.Decision
Response implements proxy.Hooks for the response phase.
func (*Engine) Resume ¶
Resume releases a held exchange. Action "resume" (or empty) continues it with the given edits applied; "drop" resets the connection. Request-phase edits: URL, Method, SetHeaders, RemoveHeaders, Body, BodyPatch. Response-phase edits: Status, SetHeaders, RemoveHeaders, Body, BodyPatch.
type Options ¶
type Options struct {
// PersistPath is the rules.json file written on every change and loaded
// by New. Empty disables persistence.
PersistPath string
// HoldTimeout bounds how long a breakpoint parks an exchange before it
// continues on its own. Zero means DefaultHoldTimeout.
HoldTimeout time.Duration
// Publish, if set, receives a flow.EvHeld event whenever a breakpoint
// parks a flow (typically bus.Publish).
Publish func(flow.Event)
// Logger receives warnings about persistence and skipped rules. Nil means
// slog.Default().
Logger *slog.Logger
// Now overrides the clock used for timestamps and expiry (tests).
Now func() time.Time
}
Options configure an Engine.
type PresetInfo ¶
type PresetInfo struct {
Name string `json:"name"`
Description string `json:"description"`
Params []PresetParam `json:"params"`
}
PresetInfo describes a rule preset.