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

defn

(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?

defn

(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.