rewrite

package
v1.0.219 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package rewrite owns per-host HTTP header rewrite rules: the rule DTO, the ordered active rule set, and the request/response header mutators. It is a self-contained leaf (stdlib + the uuid generator, no Culvert coupling) extracted from the flat package main per ADR-0002.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FingerprintRules added in v1.0.218

func FingerprintRules(rules []Rule) string

FingerprintRules is the canonical content fingerprint of an ORDERED rewrite rule set (§26): it covers stable identity, position, host, and every request/response header operation, with map keys sorted deterministically so map iteration order can never make the revision nondeterministic. The process-local integer ID is deliberately excluded — it changes across restarts without any semantic change.

func NewStableID added in v1.0.218

func NewStableID() string

NewStableID mints a durable rewrite-rule identity (server-owned; the interactive create path calls this so a client-supplied value is never trusted).

Types

type Rewriter

type Rewriter struct {
	// contains filtered or unexported fields
}

Rewriter holds the ordered list of active rewrite rules and applies them.

func NewRewriter

func NewRewriter() *Rewriter

NewRewriter returns a Rewriter with ID assignment starting at 1.

func (*Rewriter) Add

func (rw *Rewriter) Add(rule Rule) Rule

Add appends a rule and returns it with the assigned identities. The interactive trust boundary (§22): any CALLER-supplied StableID is ignored — the server owns identity generation on create.

func (*Rewriter) ApplyRequest

func (rw *Rewriter) ApplyRequest(host string, h http.Header)

ApplyRequest mutates h in-place for every matching rule.

func (*Rewriter) ApplyResponse

func (rw *Rewriter) ApplyResponse(host string, resp *http.Response)

ApplyResponse mutates resp.Header in-place for every matching rule.

func (*Rewriter) List

func (rw *Rewriter) List() []Rule

List returns a snapshot of the current rules.

func (*Rewriter) RemoveByID

func (rw *Rewriter) RemoveByID(id int) bool

RemoveByID deletes the rule with the given process-local integer ID (legacy-compat addressing). Returns false if not found.

func (*Rewriter) RemoveByStableID added in v1.0.218

func (rw *Rewriter) RemoveByStableID(stableID string) bool

RemoveByStableID deletes the rule with the given durable identity (the v2 management addressing). Returns false if not found.

func (*Rewriter) SetRules

func (rw *Rewriter) SetRules(rules []Rule) (backfilled int)

SetRules replaces the full rule set, PRESERVING LIST ORDER (order is evaluation semantics, §23) and every non-empty StableID verbatim (§21 — restart/rollback/snapshot must never re-identify a known rule). Legacy rules without a StableID are backfilled exactly once here; the returned count tells the caller whether a one-time migration happened so it can make the backfilled identities durable through the real persistence owner. Process-local integer IDs are reassigned as before (compatibility only).

DEFENSIVE dedupe: the validated doors (import / rollback / snapshot candidate checks) reject duplicate StableIDs before any apply reaches here; if a duplicate still arrives (hand-edited file that bypassed a door), the FIRST occurrence keeps the identity and later duplicates are regenerated — counted in the return so the caller persists and logs, never silently.

func (*Rewriter) Snapshot

func (rw *Rewriter) Snapshot() func()

Snapshot captures the current rules and ID counter and returns a closure that restores them, under the mutex on both ends. Production code never calls this; it exists so package main's startup-slice test isolation helper can save and restore the package-global rewriter without reaching across the package boundary into the unexported fields (ADR-0002 extraction).

func (*Rewriter) StateSnapshot added in v1.0.218

func (rw *Rewriter) StateSnapshot() (rules []Rule, revision string)

StateSnapshot returns the ordered rules AND the content-derived revision that describes exactly them, from ONE lock hold (v2 coherent-read contract): rows from one state must never pair with another state's fence.

type Rule

type Rule struct {
	// ID is assigned automatically when the rule is added at runtime.
	//
	// COMPATIBILITY/PROCESS-LOCAL ONLY (2D-C §19): SetRules reassigns
	// sequential IDs on every load, so this integer is NOT durable object
	// identity — it exists for legacy clients that address rules by it within
	// one process lifetime. Management identity is StableID.
	ID int `json:"id"`

	// StableID is the server-owned DURABLE rule identity (2D-C §20): a UUID
	// assigned once — at interactive Add, or backfilled exactly once when a
	// legacy persisted/imported rule without one first passes through
	// SetRules — and preserved verbatim across restart, export/import,
	// config-version rollback, and CP→DP snapshot sync (the persistence owner
	// is AdminSettings, which snapshots rules WITH their StableIDs). The v2
	// management surface addresses rules ONLY by this. Never derived from
	// array position or rule contents: two identical rules are two different
	// objects. yaml:"-" — YAML-authored config rules receive their identity at
	// first load and it becomes durable through the settings owner.
	StableID string `yaml:"-" json:"stableId,omitempty"`

	// Host is an exact hostname or wildcard pattern (*.example.com).
	// Empty string matches every request.
	Host string `yaml:"host" json:"host"`

	// Request header operations — applied before forwarding to upstream.
	ReqSet    map[string]string `yaml:"req_set"    json:"req_set,omitempty"`    // set / overwrite
	ReqAdd    map[string]string `yaml:"req_add"    json:"req_add,omitempty"`    // append
	ReqRemove []string          `yaml:"req_remove" json:"req_remove,omitempty"` // delete

	// Response header operations — applied before returning to client.
	RespSet    map[string]string `yaml:"resp_set"    json:"resp_set,omitempty"`
	RespAdd    map[string]string `yaml:"resp_add"    json:"resp_add,omitempty"`
	RespRemove []string          `yaml:"resp_remove" json:"resp_remove,omitempty"`
}

Rule defines header mutations applied to requests and/or responses whose destination host matches the given pattern.

Example (config.yaml):

rewrite:
  - host: "*.internal.corp"
    req_set:
      X-Forwarded-By: "Culvert"
    resp_remove:
      - Server
  - host: ""           # empty = match all hosts
    resp_set:
      Strict-Transport-Security: "max-age=31536000"

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL