Precondition
evaluates the conditional request headers of RFC 9110 §13 —
If-Match, If-Unmodified-Since, If-None-Match and If-Modified-Since —
against the validators of the representation a server selected, and answers with
the status they demand.
(match (Request.preconditions &req &etag &modified)
(Maybe.Just code) (Response.respond code {} @"")
(Maybe.Nothing) (Response.ok {} @"the body"))
If-Range demands no status of its own, so it is evaluated apart from the four,
by if-range-matches?: a range request whose condition fails
is answered with the whole representation rather than an error.
evaluate
(Fn [(Ref (Map String (Array String)) a), (Ref String b), (Ref (Maybe ETag) c), (Ref (Maybe Datetime) d)] (Maybe Int))
(evaluate hdrs method etag modified)
evaluates the conditional request headers in hdrs against
etag and modified, the entity-tag and last modification date of the
representation the server selected — either may be Nothing — for a request of
method method. Returns the status to send as (Maybe Int): Nothing when
every precondition held and the request should be answered normally, otherwise
a 304 or a 412.
The four headers are evaluated in the precedence RFC 9110 §13.2.2 lays down.
If-Match is evaluated first, and only when it is absent is
If-Unmodified-Since; then If-None-Match, and only when it is absent, and
the method is GET or HEAD, is If-Modified-Since. A failed If-None-Match
is a 304 for a GET or a HEAD and a 412 for anything else; every other
failure is a 412.
Both date headers are ignored when the representation has no modification date
or the field value is not a valid HTTP-date, as §13.1.3 and §13.1.4 require. An
If-None-Match that is not a valid entity-tag list is ignored too, but it is
still present, and step 4 is gated on presence: it suppresses If-Modified-Since
just as a readable one would. An If-Match that is not readable fails closed
with a 412 instead: the header is there to prevent a lost update, so a value
the server cannot read is not one it may act on.
A * is read as matching whenever evaluate is called, so a resource with no
current representation must not reach here: answer that with a 404, or with a
412 when the request carries an If-Match. Its existence is the one thing a
* cannot be told apart from a representation carrying no entity-tag, so the
conditional creation an If-None-Match of * guards is the caller's too;
ETagList.parse,
ETagList.strong-match? and
ETagList.weak-match? are public for it.
if-range-matches?
(Fn [(Ref (Map String (Array String)) a), (Ref (Maybe ETag) b), (Ref (Maybe Datetime) c)] Bool)
(if-range-matches? hdrs etag modified)
whether the Range header in hdrs is to be honored,
given etag and modified, the entity-tag and last modification date of the
representation the server selected — either may be Nothing.
true when the request carries no If-Range, when it carries no Range for one
to gate, or when the If-Range validator matches (RFC 9110 §13.1.5). false
means the Range must be ignored and the whole representation sent with a 200,
the client holding a version other than the one it asks to continue.
The two validator forms are told apart by the first characters of the field
value: a quote or a W/ names an entity-tag, anything else an HTTP-date. An
entity-tag is compared strongly (§8.8.3.2), so a weak one — which §13.1.5 forbids
a client to send and obliges a recipient to ignore — never matches. A date
matches the modification date only when it names the very same instant, as
§8.8.2.2's strong-validator rule demands. A field value that reads as neither
form, and a validator the selected representation has no counterpart to, are
non-matches.
§13.2.1 orders the three steps of a conditional range request:
evaluate first, then this, then the Range itself.