Subject: SHOULD-level requirements in p6-caching

From: Mark Nottingham <[email-masked]>
Date: Thu, 7 Apr 2011 18:19:51 +1000

I'm starting to survey the SHOULD-level requirements, as per <http://trac.tools.ietf.org/wg/httpbis/trac/ticket/271>, with an initial focus on p6.

I've put some specific comments below, but underlying them are a few premises which I'd like to hear people's opinions on.

* SHOULD-level requirements are to be avoided where possible; MUST is better for interoperability, and

* Where we have SHOULD-level requirements, the exceptional circumstances (as per the 2119 definition of SHOULD) should usually be explicitly enumerated, unless there is an obvious interpretation, and

* SHOULD is sometimes used as a "get-out clause" for cases where there is an expected behaviour, but where other circumstances may require a different behaviour. In principle I'd like to avoid this, as it sends a mixed message. Not sure if it's possible in practice, however. And,

* The added meaning of SHOULD as a metric for "conditional conformance" isn't that useful, isn't defined in 2119, and we should consider dropping it.

If we can get agreement to these, a few proposals for p6 follow.

In 2.2:

>    A cache, especially a shared cache, SHOULD use a mechanism, such as NTP
>    [RFC1305], to synchronize its clock with a reliable external
>    standard.

Similarly, we later see:

>    A cache SHOULD use NTP ([RFC1305]) or some similar protocol to synchronize its clocks to a globally accurate time standard.

I think these are fine uses of RFC2119 SHOULD language; no need for a change. I.e., an administrator who carefully considers the interoperability issues
can still run a cache without NTP.

In 2.3.1.1,

>    Also, if the response has a Last-Modified header field (Section 6.6
>    of [Part4]), a cache SHOULD NOT use a heuristic expiration value that
>    is more than some fraction of the interval since that time.  A
>    typical setting of this fraction might be 10%.

This is a pretty meaningless requirement, as heuristic freshness is already a huge get-out clause in HTTP caching; you can just say that you don't base your heuristic on Last-Modified, and choose any value. 

I propose reducing this from a requirement to a prose advisory, e.g., "caches are expected to..."

There may be a separate issue on how wide-open heuristic expiry is here.

In 2.3.3,

>    A cache SHOULD NOT return stale responses unless it is disconnected
>    (i.e., it cannot contact the origin server or otherwise find a
>    forward path) or otherwise explicitly allowed (e.g., the max-stale
>    request directive; see Section 3.2.1).

I think this should be a MUST NOT for clarity. Thoughts?

Also in 2.3.3,

>    If a cache receives a first-hand response (either an entire response,
>    or a 304 (Not Modified) response) that it would normally forward to
>    the requesting client, and the received response is no longer fresh,
>    the cache SHOULD forward it to the requesting client without adding a
>    new Warning (but without removing any existing Warning header
>    fields).

This seems like it should be non-normative prose, not a requirement.

Right below that,

> A cache SHOULD NOT attempt to validate a response simply
>    because that response became stale in transit.

Prose.

In 2.4,

>    When sending such a conditional request, a cache SHOULD add an If-
>    Modified-Since header field whose value is that of the Last-Modified
>    header field from the selected (see Section 2.7) stored response, if
>    available.

Prose.

Below that,

>    Additionally, a cache SHOULD add an If-None-Match header field whose
>    value is that of the ETag header field(s) from all responses stored
>    for the requested URI, if present.  However, if any of the stored
>    responses contains only partial content, the cache SHOULD NOT include
>    its entity-tag in the If-None-Match header field unless the request
>    is for a range that would be fully satisfied by that stored response.

The first SHOULD would work better as prose, IMO. The second one is more debatable, but if it's a requirement, I'd think it'd be more clear as a MUST.

Further down,

>    A full response (i.e., one with a response body) indicates that none
>    of the stored responses nominated in the conditional request is
>    suitable.  Instead, a cache SHOULD use the full response to satisfy
>    the request and MAY replace the stored response.

I think this SHOULD is more appropriate as non-normative prose. 

In 2.5, 

>    A cache that passes through requests with methods it does not
>    understand SHOULD invalidate the effective request URI (Section 4.3
>    of [Part1]).

I'm not sure why this is a SHOULD when all of the other invalidation side effects are MUST-level requirements. Can we raise this to a MUST as well?

In 3.1, 

>    Recipients parsing the Age header field-value SHOULD use an
>    arithmetic type of at least 31 bits of range.

It seems to me that interop requires a MUST here.

In 3.2.1 (only-if-cached),

>       If it receives this
>       directive, a cache SHOULD either respond using a stored response
>       that is consistent with the other constraints of the request, or
>       respond with a 504 (Gateway Timeout) status code.

MUST?

In 3.2.2 (must-revalidate),

>       A server SHOULD send the must-revalidate directive if and only if
>       failure to validate a request on the representation could result
>       in incorrect operation, such as a silently unexecuted financial
>       transaction.

Prose.

In 3.3,

>    A server SHOULD NOT send Expires dates more than one year in the
>    future.

Prose.

In 3.4,

>    When the no-cache directive is present in a request message, a cache
>    SHOULD forward the request toward the origin server even if it has a
>    stored copy of what is being requested.

Prose.

> A client SHOULD include both header fields when a no-cache
>    request is sent to a server not known to be HTTP/1.1 compliant.

Prose.

In 3.5,

>    A server SHOULD include a Vary header field with any cacheable
>    response that is subject to server-driven negotiation.

I can't decide if this needs to be a requirement; if it does, I think it should be a MUST; if not, it should be prose. Thoughts?

I've omitted a number of Warning-related SHOULDs, as I think they need to be examined separately. 

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

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Thu, 07 Apr 2011 09:35:33 +0000

In message <[email-masked]>, Mark Nottingham writes:

>I'm not sure why this is a SHOULD when all of the other invalidation 
>side effects are MUST-level requirements. Can we raise this to a MUST as 
>well?

First off, what does "not understand" mean here ?

Does that cover a cache which goes "Ohh, POST, I don't do those:
pass it through" ?

Or does it only cover "XYZZY / HTTP/1.1" style requests ?

Second: Are we sure this complies with Principle Of Least Astonishment ?

Third: do we really want to give script kiddies their own private
standards-mandated cache-invalidation button ?

If anything, I would think it should be turned into a MUST NOT
requirement, reasoning that if you use privately defined requests,
you'd better think carefully about how that should work with client
side caches in the first place.

>>       respond with a 504 (Gateway Timeout) status code.

I must confess I have never understood this directive, nor been able to
come up with a non-hostile intent for using it.  Can anybody enlighten me ?

Nit:
	"The only-if-cached request directive indicates that the client
	only wishes to return a stored response."

	s/return/receive/ ?

>>    A server SHOULD NOT send Expires dates more than one year in the

Why this policy restriction ?

Remove entirely ?

>>    stored copy of what is being requested.

Have you discussed the future of "Pragma:" before ?

I would like to see the text say that if there is a "Cache-Control:"
header "Pragma:" MUST be ignored, to resolve the possible conflicts
between them.

>I can't decide if this needs to be a requirement; if it does, I think it 
>should be a MUST; if not, it should be prose. Thoughts?

This one is a nasty one.

Vary is a very crude mechanism in todays web, where objects are
customized to specific (browser, platform, extension) triplets.

For instance Varnish experience is that "Vary: User-Agent" reduces
cache hit rate by about two orders of magnitude.

Some sites "solve" this problem using a cookie instead.  However,
you cannot say "Vary: Cookie/my_idea_of_your_browser" to vary only
on a single cookie, so that doesn't really solve the cache problem.

This may be a good case for the geniuine SHOULD:  You really should,
unless you shouldn't because it breaks caching.

Related to this, we should think a moment about Google Analytics
here, since it tends to pollute all objects with Cookies, and "Vary:
Cookie" is implied (but not specified as I recall)

It would be incredibly beneficial for caching, if it were possible
to say "Vary: !Cookie" to indicate that the Cookies are not really
important anyway.

Poul-Henning

-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
[email-masked]         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe    
Never attribute to malice what can adequately be explained by incompetence.

---

From: "Moore, Jonathan (CIM)" <[email-masked]>
Date: Thu, 7 Apr 2011 09:57:41 +0000

>come up with a non-hostile intent for using it.  Can anybody enlighten me ?

This is primarily useful in the presence of client-side caches attached to applications. Let's say I have an in-memory object that is a parsed representation of some resource http://foo.example.com/obj. In the course of serving a synchronous client request, I'd like to refresh/validate my parsed representation, but not if it involves making a long WAN request to validate back to the origin. In this case, I might want to issue a conditional GET with only-if-cached; this way, I get a fast response with either (a) 504, (b) 200 with updated info loaded from client cache, (c) 304 validation from client cache. If I get a 504, I might then follow up with an asynchronous validation request without only-if-cached, but maybe not.

This is important because my parsed object with its validator is a very tiny client-side cache, and while I want to revalidate it, it's possible that the previous cache entry has disappeared from my local client HTTP cache.

In short, it allows a client with a client cache to control the latency of the request/response cycle, including doing client-driven stale-while-revalidate.

Jon

---

From: Mark Nottingham <[email-masked]>
Date: Fri, 8 Apr 2011 15:10:17 +1000

Thanks for the feedback, Poul-Henning. Responses below.

> Does that cover a cache which goes "Ohh, POST, I don't do those:

POST is explicitly covered elsewhere in the section, so there's an overlap here; all caches are expected to do this (and more) for POST.

> Or does it only cover "XYZZY / HTTP/1.1" style requests ?

It does that as well.

> Second: Are we sure this complies with Principle Of Least Astonishment ?

Can you say a bit more here? 

> standards-mandated cache-invalidation button ?

Please read the entire section; this is not new text, and has been present in HTTP for over a decade. In short, there is a mechanism to prevent this kind of attack.

> come up with a non-hostile intent for using it.  Can anybody enlighten me ?

I actually know of lots of people using it; it's useful to get something from cache in a low-cost way, without incurring the latency/back-end overhead of a request if it's not in cache.

> 	only wishes to return a stored response."

ack.

> Why this policy restriction ?

Anyone have history on this one before I dig through the cache list archives?

> header "Pragma:" MUST be ignored, to resolve the possible conflicts

We can't say it MUST be ignored, because I suspect that's break a lot of existing implementations. However, I agree it'd be nice to disambiguate the relative precedence of Pragma and Cache-Control (just as has been done for Cache-Control and Expires).

I also think we can drop "A client SHOULD include both header fields when a no-cache request is sent to a server not known to be HTTP/1.1 compliant. "

> unless you shouldn't because it breaks caching.

I think we need a lot more advice about Vary. This is one of those cases where the spec currently talks about implementation details, when really it should be talking about expected behaviour on the wire. I.e., if you want to enable a client (including one that has a cache) to consider this response specific to the values of certain request headers, you need to include a Vary. That really isn't a SHOULD, it's advisory text, IMO.

This trips people up just as much because they forget to include Vary when they really need to.

> to say "Vary: !Cookie" to indicate that the Cookies are not really

There's been a bit of discussion about defining a new mechanism for refining the cache key; there might be a draft soon. That's not a WG item, however (but it can still be talked about on-list).

Cheers,

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Fri, 08 Apr 2011 08:47:05 +0000

In message <[email-masked]>, Mark Nottingham wri

>POST is explicitly covered elsewhere in the section, so there's an 
>overlap here; all caches are expected to do this (and more) for POST.

But that is not what the text says.

It is perfectly possible to write a cache that only "understands"
GET and HEAD, and just pass everything else upstream.

If you want it to mean "methods not defined in this document which
the cache does not understand", then the text should say that.

>Can you say a bit more here? 

It is not at all obvious to me why a random method always MUST
automatically invalidate a cached copy.   If we want to make this
mandatory, I would prefer to have more and better reasons than
"we'd like to get rid of 'SHOULD'" in the text.

>Please read the entire section; this is not new text, and has been 
>present in HTTP for over a decade. In short, there is a mechanism to 
>prevent this kind of attack.

There is ?  What would that be ?

All I have to do is send a "XYZZY / HTTP/1.1" request and the
cache is forced to dump its copy.

As many sites use CMS systems with very systematic URLs, washing
a cache that way, and bothering the backend server a lot is a
Simple Matter Of Programming.

dDoS attack are a fact of life, and from a standards point of view
we SHOULD consider clients hostile to cache/server-operation
when judging such aspects of the standard.

>when they really need to.

Indeed.

>There's been a bit of discussion about defining a new mechanism for 
>refining the cache key; there might be a draft soon. That's not a WG 
>item, however (but it can still be talked about on-list).

Please keep me posted.

---

From: Julian Reschke <[email-masked]>
Date: Fri, 08 Apr 2011 10:49:11 +0200

On 07.04.2011 10:19, Mark Nottingham wrote:
>
> I'm starting to survey the SHOULD-level requirements, as per<http://trac.tools.ietf.org/wg/httpbis/trac/ticket/271>, with an initial focus on p6.
>

...like when we know that it should be MUST NOT, but implementations are 
known not do this?

> can still run a cache without NTP.

Yes.

> There may be a separate issue on how wide-open heuristic expiry is here.

Generally, +1 on avoiding RFC2119 keywords unless we really need them 
("In particular, they MUST only be used where it is actually required 
for interoperation or to limit behavior which has potential for causing 
harm (e.g., limiting retransmisssions)").

>>     because that response became stale in transit.

As in "rewrite without RFC2119? Yes.

> The first SHOULD would work better as prose, IMO. The second one is more debatable, but if it's a requirement, I'd think it'd be more clear as a MUST.

I think this just explains in how a cache can implement something. It 
would be totally sufficient to have prose explaining what's the best way 
to do here.

> I think this SHOULD is more appropriate as non-normative prose.

We should think about the MAYs as well :-)

> I'm not sure why this is a SHOULD when all of the other invalidation side effects are MUST-level requirements. Can we raise this to a MUST as well?

If this doesn't result in anything a client can reliably observe then 
I'm not even sure it should be a requirement. Requests can go through 
different paths, so just because one cache invalidates something, it 
doesn't mean all of them do, no...?

> It seems to me that interop requires a MUST here.

+1.

>>        respond with a 504 (Gateway Timeout) status code.

Borderline. It just affects performance, right?

>>        in incorrect operation, such as a silently unexecuted financial

Yes.

>>     A server SHOULD NOT send Expires dates more than one year in the

Yes. BTW: it would be good to explain why this is a problem.

>>     stored copy of what is being requested.

Isn't that a case for MUST?

>>     request is sent to a server not known to be HTTP/1.1 compliant.

Yes.

> ...

This is a case where it's the right thing to do but it's not done 
because of the negative effects on some UAs (like some versions of IE 
not even being able to forward to a helper application). Maybe this is a 
case where we need to add a lot more text to explain the problem.

BR, Julian

---

From: Julian Reschke <[email-masked]>
Date: Fri, 08 Apr 2011 10:52:09 +0200

> Cookie" is implied (but not specified as I recall)

What exactly do you mean when you say "is implied"?

Best regards, Julian

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Fri, 08 Apr 2011 09:04:48 +0000

>What exactly do you mean when you say "is implied"?

That web-apps expect caches to either use the cookies as part of their
hash-key or not cache at all when cookies are present, or they break
seven ways before breakfast.

---

From: Julian Reschke <[email-masked]>
Date: Fri, 08 Apr 2011 11:54:37 +0200

On 08.04.2011 11:04, Poul-Henning Kamp wrote:
> In message<[email-masked]>, Julian Reschke writes:

Ack.

This is similar to the special case we have in P1 with respect to 
handling multiple Set-Cookie header fields 
(<http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p1-messaging-13.html#rfc.section.3.2.p.7>).

So we probably should mention it, but adding a requirement might break 
existing conforming implementations, so I'd prefer not to.

Best regards, Julian

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Fri, 08 Apr 2011 10:11:51 +0000

>existing conforming implementations, so I'd prefer not to.

Each cookies should have an attribute that tells if they should
affect the Vary/hash-key or not.

---

From: Julian Reschke <[email-masked]>
Date: Fri, 08 Apr 2011 12:16:16 +0200

On 08.04.2011 12:11, Poul-Henning Kamp wrote:
> In message<[email-masked]>, Julian Reschke writes:

Well, if we were to change cookies-as-used-in-the-wild, then we just 
should tell producers to send the Vary header if they need to.

Best regards, Julian

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Fri, 08 Apr 2011 10:21:26 +0000

>should tell producers to send the Vary header if they need to.

Only if we teach Vary about cookies...

The problem is that some cookies matter (user-id) and some don't
(statistics-tracking) for caching purposes.

The easiest way would be to have the cookies themselves tell us.

---

From: David Morris <[email-masked]>
Date: Fri, 8 Apr 2011 08:12:45 -0700 (PDT)

> The easiest way would be to have the cookies themselves tell us.

Probably the hardest way ... but in any case, the IETF HTTP State WG 
<[email-masked]> is probably the right place to make the
proposal.

My expectation is that current sites which use cookies to control
content already use other caching controls to prevent reuse of
unsharabale content.

---

From: "Poul-Henning Kamp" <[email-masked]>
Date: Fri, 08 Apr 2011 17:33:42 +0000

In message <[email-masked]>, David Morris wr

>content already use other caching controls to prevent reuse of

IMO an unwarranted assumption.

As author of a major server-side cache-software, I see a lot of
server side people with absolutely no clue to caching and its
interaction with cookies, vary or for that matter cache-control
headers.

---

From: Willy Tarreau <[email-masked]>
Date: Sat, 9 Apr 2011 07:54:29 +0200

> interaction with cookies, vary or for that matter cache-control

I agree with you, I've had the same experience. People develop
applications and install them in a hosting infrastructure they
don't necessarily understand. The hosting infrastructure uses
caches and the application people complain that the caches are
"abusive" and distribute session cookies to end users...

I even had to develop specific options in haproxy to test for
response cachability combined with set-cookie headers and be
able to block them instead of letting them leak into caches.

For me it's a proof that there are many application people who
have no clue about caching and who sometimes don't want to know
how it works.

Regards,
Willy

---

From: Willy Tarreau <[email-masked]>
Date: Sat, 9 Apr 2011 08:05:57 +0200

> >       respond with a 504 (Gateway Timeout) status code.

Is there any reason for a 504 here ? I mean, 5xx are server-side errors
which should theorically not be decided by the client. In my opinion here
the client asks for an object and sets a condition to retrieve it, if the
object cannot be fetched from the cache, the client's condition cannot be
satisfied, so it should be a 4xx (possibly a 412 Precondition Failed).

It is important to ensure that clients cannot force servers to emit 5xx
errors, because it is common to include status code checks when monitoring
production systems. If a cache emits a 504, usually it's because it cannot
reach the next hop so there is a connectivity issue that must be addressed.

Regards,
Willy

---

From: David Morris <[email-masked]>
Date: Fri, 8 Apr 2011 23:14:07 -0700 (PDT)

> have no clue about caching and who sometimes don't want to know

And changing the spec would help that how? If feature that already
exist aren't being used, why does another feature help, in particular
in the face of the normal propigation delay between specifiction
and sufficient deployment.

---

From: Willy Tarreau <[email-masked]>
Date: Sat, 9 Apr 2011 08:28:23 +0200

> and sufficient deployment.

I was not discussing a change of spec, rather the point that was made
that people who use cookies know how to use a cache. That assumption
cannot be made, so if it's used to justify a spec change, it's hasardous.

Regards,
Willy