Subject: Why should caches and intermediaries ignore If-Match?

From: Tom Bergan <[email-masked]>
Date: Wed, 15 Feb 2017 15:43:13 -0800

RFC 7232 Section 3.1 says:

> The If-Match header field can be ignored by caches and intermediaries
> because it is not applicable to a stored response.

RFC 7234 Section 4.3.2 says the same thing:

> The If-Match ... conditional header fields are not applicable to a cache.

Why?

Suppose the client sends a GET request with If-Match: "A". This request is
first sent to an HTTP cache. Suppose the requested resource is cached, and
suppose the cache ignores the If-Match header as suggested above. If the
cache has version "B" cached, not version "A", the cache will return a 2xx
with ETag: "B". This behavior is surprising and seems to contradict the
following statement from RFC 7232 Section 3.1:

> [If-Match] can also be used with safe
> methods to abort a request if the selected representation does not
> match one already stored (or partially stored) from a prior request.

I am trying to use If-Match for exactly this reason.

I understand why a cache would not generate 412s. The cache is not
authoritative for the resource and 412 implies authority. But, it seems
like the cache should never respond with an ETag that is disallowed by
If-Match, and if the cache does not have an allowed ETag, then the request
should be forwarded to the origin. Does that sound right?

---

From: Alex Rousskov <[email-masked]>
Date: Thu, 16 Feb 2017 18:21:18 -0700

>> The If-Match ... conditional header fields are not applicable to a cache.

When rewriting RFC 2616, HTTPbis authors concluded that "caches don't
have enough information to process methods like If-Match" and decided to
limit If-Match and If-Unmodified-Since effects to origin servers:

    https://trac.ietf.org/trac/httpbis/ticket/498
    https://trac.ietf.org/trac/httpbis/ticket/479
    https://trac.ietf.org/trac/httpbis/changeset/2408

I bet the authors were thinking about classic use cases that deal with
uncachable resources created and/or updated on the origin server via
POST/PUT/DELETE methods and missed some more esoteric use cases.

N.B. "can be ignored" is not exactly "MUST ignore" so there is some
wiggle room for cache optimization here (e.g., mismatching cached
resources do not have to be DELETEd from a proxy cache, I guess).

> Suppose the client sends a GET request with If-Match: "A". This request
> is first sent to an HTTP cache. Suppose the requested resource is
> cached, and suppose the cache ignores the If-Match header as suggested
> above. If the cache has version "B" cached, not version "A", the cache
> will return a 2xx with ETag: "B". 

Yes, If-Match in HTTPbis is not designed to work [well] with GET methods
that result in cachable responses.

> This behavior is surprising and seems
> to contradict the following statement from RFC 7232 Section 3.1:

>> match one already stored (or partially stored) from a prior request.

I suspect this text now refers to representations stored on the origin
server [as the result of prior conditional requests], not
representations cached by a proxy. Again, the former is probably the
only intentionally supported use case as far as HTTPbis is concerned.

> I am trying to use If-Match for exactly this reason.

Then you have two options:

A) Claim that s/server/origin/g change was wrong and fix HTTPbis RFCs.
B) Make sure your responses are not cachable.

Given the widespread changes and serious incompatibility with RFC 2616
that an HTTPbis fix to satisfy your use case would require, I cannot
recommend (A) unless HTTPbis authors explicitly encourage you to propose
a fix.

> If-Match, and if the cache does not have an allowed ETag, then the
> request should be forwarded to the origin. Does that sound right?

It sounds reasonable to me but it would violate the original RFC 2616
text and go against the letter (but perhaps not the intent) of the new
HTTPbis rules.

HTH,

Alex.

---

From: Tom Bergan <[email-masked]>
Date: Fri, 17 Feb 2017 14:06:43 -0800

> resources do not have to be DELETEd from a proxy cache, I guess).

Thanks for digging up the history!

> text and go against the letter (but perhaps not the intent) of the new

That would violate the letter of RFC 7232, as you say, but I don't think it
would violate the original RFC 2616 text? RFC 2616 Section 14.24 only
mentions caches once, in relation to If-Match: *, and that mention implies
that caches must handle If-Match:

> RFC 2616 Section 14.24
> The meaning of "If-Match: *" is that the method SHOULD be performed
> if the representation selected by the origin server (or by a cache,
> possibly using the Vary mechanism, see section 14.44) exists, and
> MUST NOT be performed if the representation does not exist.

Mark, Roy, any thoughts?

Given the potential for incompatibility, we're going to look into a
solution that doesn't use If-Match, but it still seems like RFC 7232 should
be updated to explicitly state that either (a) caches should not process
requests with If-Match, or (b) caches may process requests with If-Match,
as suggested above.

---

From: Alex Rousskov <[email-masked]>
Date: Fri, 17 Feb 2017 15:15:36 -0700

On 02/17/2017 03:06 PM, Tom Bergan wrote:
> On Thu, Feb 16, 2017 at 5:21 PM, Alex Rousskov wrote:
>     > if the cache does not have an allowed ETag, then the

> I don't think it would violate the original RFC 2616 text?

AFAICT, it would violate the following RFC 2616 Section 14.24 MUST:

>    If none of the entity tags match [...]
>    the server [...]
>    MUST return a 412 (Precondition Failed) response.

A [caching] proxy is a server.

Alex.

---

From: Tom Bergan <[email-masked]>
Date: Fri, 17 Feb 2017 14:42:48 -0800

> A [caching] proxy is a server.

Ah yes, you're right.

---

From: Mark Nottingham <[email-masked]>
Date: Mon, 27 Feb 2017 10:49:41 +1100

I think the best way to characterise the situation currently is that HTTP doesn't define any requirements for If-Match on non-origin servers; the only requirements in 7232 Section 3.1 apply to origin servers.

AFAIK current intermediaries ignore If-Match, so if you wanted to define some guidelines here, they'd need to be completely optional. E.g., "An intermediary MAY process If-Match based upon the contents of its cache, replying with 4xx when..." (note that that's just rapid hand-waving, not suggested spec text).

If we did that, we'd have a header whose handling by origin servers was mandatory for some methods, and handling by intermediary servers was optional for other methods. Not sure how much that would confuse people, but properly spec'd, it'd probably be OK.

We'd also have to have a discussion about whether 412 was the right status code.

Roy, any thoughts?

Tom, can you say any more about your use case?

Cheers,

> On 18 Feb 2017, at 9:06 am, Tom Bergan <[email-masked]> wrote:
> 
> On Thu, Feb 16, 2017 at 5:21 PM, Alex Rousskov <[email-masked]> wrote:
> That would violate the letter of RFC 7232, as you say, but I don't think it would violate the original RFC 2616 text? RFC 2616 Section 14.24 only mentions caches once, in relation to If-Match: *, and that mention implies that caches must handle If-Match:
> 
> > RFC 2616 Section 14.24
> Given the potential for incompatibility, we're going to look into a solution that doesn't use If-Match, but it still seems like RFC 7232 should be updated to explicitly state that either (a) caches should not process requests with If-Match, or (b) caches may process requests with If-Match, as suggested above.

--
Mark Nottingham   https://www.mnot.net/

---

From: "Roy T. Fielding" <[email-masked]>
Date: Mon, 27 Feb 2017 17:03:34 -0800

> On Feb 26, 2017, at 3:49 PM, Mark Nottingham <[email-masked]> wrote:
> 

My thoughts would probably depend on the use case.
Note that the HTTP spec is only defining rules for communication
between independent components.  Although the internal architecture of
a user agent might include something like an HTTP cache, HTTP's rules
do not limit communication between the UA and its own internal cache.
As far as HTTP is concerned, they are both part of the user agent.

Thus, the sentence in 3.1:

   It can also be used with safe
   methods to abort a request if the selected representation does not
   match one already stored (or partially stored) from a prior request.

is referring to one already stored on the user agent from a prior
request by that user agent.

Originally, If-Match was defined to be answerable by intermediaries
for GET/HEAD requests.  However, 412 was considered by the WG to be an
undesirable response in those cases, so If-Range was created to
replace that function. My guess is that's the use case here. OTOH,
a 412 might be preferred for safe methods other than GET and HEAD.

AFAICR, limiting If-Match requirements to origin servers in RFC7232
was due to lack of implementation by clients (aside from the unsafe
methods) and a desire for semantic consistency for the field.

For unsafe methods, the client's field value is referring to the
current selected representation on the origin server, which is something
that can only be tested by the origin server. Having a special-case for
safe methods meant that both the meaning of the field changed per
method and the need to implement it changed per method, which is quite
a bit of complexity for a feature that nobody ever used.

BTW, Apache httpd implements If-Match in the default resource handler
and anywhere that calls ap_meets_conditions().  That will result in a
412 response to an otherwise successful request if the etag given
doesn't match the selected representation, regardless of the method.
[I haven't tested it to see if that gets called by default when the
server is installed as an intermediary.]

Cheers,

....Roy

---

From: Alex Rousskov <[email-masked]>
Date: Mon, 27 Feb 2017 21:23:36 -0700

On 02/26/2017 04:49 PM, Mark Nottingham wrote:

> AFAIK current intermediaries ignore If-Match

FWIW, Squid does not ignore If-Match. I did not study this in-depth, but
parts of the code appear to implement the old RFC 2616 logic (412
responses), and the more general "Is this a response to a conditional
request?" code paths are also affected. I am not claiming that Squid is
compliant in this area; just that the If-Match header will affect Squid
behavior is some cases.

Alex.