Request flow

The nine stages every request passes through, and where each one can end early.

Every call to Engine.Serve runs the same stages in the same order. A stage can finish the request, and the first four often do.

The nine stages of ServeNine stages run in order. Stages one to four can end the request early. Stages two and five are the security boundary.1closed?ErrClosed2validate and classify4003unsafe or unknown methodpass through4bypass rulepass through5build forwarded request and keysecurity boundary ends here6store lookup: primary, then variant7epoch check (purges)8decide: serve, serve stale, coalesced fetch9record: event, miss rate, Cache-Status

The stages

StageWhat happens
1Closed?After Close starts, Serve returns ErrClosed (a 503) and touches nothing.
2Validate and classifyThe scheme, host, path and query are checked against fixed limits (FR-VAL-1). A request that fails is answered 400 with no store or origin cost, which is the point: malformed input must not become a cheap way to miss (T-13). The method decides the class: GET and HEAD are cacheable, everything else is not.
3Unsafe or unknown methodPOST, PUT, DELETE and the rest pass through to the origin with their body. They still hold a limiter slot, and a successful unsafe response invalidates the stored entry for that URL.
4Bypass ruleA request matching Bypass.Cookies or Bypass.Headers (a session cookie, say) passes through with all its headers. Cache-Status shows fwd=bypass.
5Build the forwarded request and the keyWeir rewrites the request (drops unkeyed headers and query parameters, normalizes what you asked it to) and computes the key from the rewritten form. From here on nothing reads the client's raw headers again.
6Store lookupFirst the primary key. If the stored response had a Vary header, the lookup finds a vary spec and does a second lookup under the variant key.
7Epoch checkPurges are recorded as epochs, timestamps per URL, group, origin or everything. An entry older than an epoch that applies to it is stale (soft purge) or gone (hard purge). See Purging.
8DecideA fresh entry is served, and may trigger an early refresh. A stale entry inside its stale-while-revalidate window is served while a background fetch renews it. A live negative entry answers with a synthesized error. Anything else becomes a coalesced fetch through the limiter and the breaker.
9RecordThe outcome goes to the miss-rate tracker and the observer, and into the Cache-Status header.

Stages 2 and 5 are the security boundary. Everything downstream, including the store, the flight table and the origin, sees only the forwarded request (INV-1).

One place calls the origin

Foreground misses, background refreshes, Warm, pass-through requests and validations all reach the origin through a single function. That is how one limiter and one breaker cover every path, and it is enforced by a test (originrule_test.go) that fails if a second call site appears.

What the adapter does

Weir never opens a socket. An adapter turns the wire request into a weir.Request, calls Serve, and writes the weir.Response or the error back. weirhttp does this for net/http; the Caddy module does it inside Caddy's handler chain. Error-to-status mapping is in the errors reference.

Limits

Range requests are answered from a complete stored object when one is fresh; a range request that misses passes through and stores nothing. Multi-range requests get the full 200. CONNECT and protocol upgrades are not served by the engine: weirhttp.Handler answers 501 and weirhttp.Middleware hands them to the next handler.