| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Core.Server.Conditional
Description
Conditional-GET / ETag handling, split by how the served body relates to upstream's.
The proxy serves two kinds of body, and they validate differently (see
docs/architecture/web-layer.md → "Middleware and helper libraries"):
- Pass-through bodies -- artifacts, and unfiltered private-upstream metadata --
are byte-identical to upstream's, so upstream's own validator is authoritative.
The client's validators are relayed upstream (
forwardValidators) and an upstream304is passed straight back (isNotModified). Relaying is correct precisely because we do not change the bytes. - Transformed bodies -- every packument, which is merged across upstreams and
filtered by the rules -- differ from any single upstream's body, so an upstream
validator would validate the wrong bytes. We instead serve our own strong
ETag(mkStrongETag) and answer the client's conditional request against it (evaluateETag).
The own-ETag is derived from the serve's inputs, not hashed over its output: a
SHA-256 over the origin bodies' digests, the per-source surviving version sets, and
the assembly's identity (see packumentETag).
The served document is a deterministic function of exactly those inputs, so the tag
can never validate a stale body as fresh -- the direction correctness needs -- while
it may occasionally change when the re-assembled bytes would not have (a spurious
200, never a wrong 304). Deriving it from inputs is what lets the serve path
answer a 304 without assembling, encoding, or hashing the document at all, and
stream a 200 body without materialising it for a hash pass first. The functions
here are pure; turning a Conditional or relayed status into a WAI response is the
serving layer's job.
Synopsis
- data ETag
- mkStrongETag :: Digest SHA256 -> ETag
- renderETag :: ETag -> Text
- etagHeader :: ETag -> Header
- data Conditional
- evaluateETag :: RequestHeaders -> ETag -> Conditional
- forwardValidators :: RequestHeaders -> RequestHeaders
- isNotModified :: Status -> Bool
Our own ETag (transformed bodies)
A strong entity tag for a body we serve: the quoted opaque-tag form
("…"), as it appears in the ETag header. A 'newtype' so the quoted wire form
is not confused with the bare digest or any other Text.
mkStrongETag :: Digest SHA256 -> ETag Source #
Quote a SHA-256 digest as a strong ETag -- hex-encoded, in the quoted
opaque-tag wire form. The digest is whatever fingerprint the serving layer stands
behind; for packuments that is the input fingerprint of
packumentETag.
renderETag :: ETag -> Text Source #
The ETags wire form, the quoted opaque tag as it goes into the header.
etagHeader :: ETag -> Header Source #
The ETag response header carrying this validator.
data Conditional Source #
The conditional outcome for a transformed body: whether the client's validator already matches what we would serve.
Constructors
| NotModified ETag | The served body is unchanged from the client's validator -- answer |
| Modified ETag | The served body differs (or no validator was sent) -- serve |
Instances
| Show Conditional Source # | |
Defined in Ecluse.Core.Server.Conditional Methods showsPrec :: Int -> Conditional -> ShowS # show :: Conditional -> String # showList :: [Conditional] -> ShowS # | |
| Eq Conditional Source # | |
Defined in Ecluse.Core.Server.Conditional | |
evaluateETag :: RequestHeaders -> ETag -> Conditional Source #
Evaluate a conditional request against our own ETag for a transformed body.
The given tag is matched against the request's If-None-Match: a * wildcard, or
any tag in the (comma-separated) list whose opaque value equals ours, is a match →
NotModified. The match is weak (RFC 7232): a W/ prefix on either side is
ignored, so a client echoing our tag with a weakness marker still matches. Anything
else -- a stale tag, or no validator -- is Modified.
If-Modified-Since is deliberately not consulted for transformed bodies: a merged
packument has no single upstream Last-Modified to compare to, and the strong
ETag is the precise validator.
Relaying validators (pass-through bodies)
forwardValidators :: RequestHeaders -> RequestHeaders Source #
The client's conditional validators to relay upstream for a pass-through
body. Only the request-side conditional headers (If-None-Match,
If-Modified-Since) are forwarded; everything else is dropped, since this is the
exact set that lets upstream answer 304 for a body we serve unchanged.
isNotModified :: Status -> Bool Source #
Whether an upstream response is a 304 Not Modified to pass straight back to
the client unchanged. Used on the pass-through path, where upstream's own
validator decided the conditional request.