- From: Alcides Viamontes E <alcidesv@zunzun.se>
- Date: Sun, 10 Jan 2016 18:11:44 +0100
- To: ietf-http-wg@w3.org
Hello, My interest in the draft "Cache Digests for HTTP/2" https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/ concerns the original, intended use case that Mr. Kakuho Oku and Mr. M. Nottingham cited. As the authors, I would like very much like to see this made a standard and implemented in browsers. However, I perceive a few issues. Beforehand, I apologize for this long email, for any gaps in my understanding of the subject, and for not being familiar with the language and procedures used in this list. Here are the issues that I see: 1.- In its current wording, no information about which version of a representation the browser already has is present in the cache digest. That information can be included in the URL itself (cache busting), but then it becomes a concern for web-developers, adds complexity to their work, and bypasses the mechanisms that HTTP has in place for maintaining cache state. It also increases space pressure in the the browser's cache as the server is left with no means to expire old cached contents in the browser. 2.- There is no way for the server to know that a CACHE_DIGEST frame is coming immediately after a HEADERS frame. A server may trigger some processing already after the end of headers has been received, while making further DATA frames available as a stream of data to the application. With CACHE_DIGEST frames, the cache aware server will have to delay processing until the end of the stream has been seen to be sure that no CACHE_DIGEST frame is coming, or would have to re-start processing on seeing the frame. Arguably this is not a big problem for GET requests with an empty body, but it would be nice if the spec didn't force the server to wait for the end of the stream. 3. - Traditionally, cache state information has been placed in HTTP header fields. A CACHE_DIGEST frame puts some of that information in a new place, which is sure to cause some pain to web developers and sys-admins trying to understand the behavior of their applications. 4.- The draft assumes a somewhat more restricted scope of Push than allowed by the HTTP/2 spec, RFC7540, and to some extent, goes against current practice. Section 8.2 of RFC7540, "Server Push", says "The server MUST include a value in the :authority" pseudo-header field for which the server is authoritative". Section 10.1 defines server authority by referring to [RFC7230], Section 9.1. For the HTTPS case, a server is authoritative for a domain if it can present a certificate that covers that domain. To the point, RFC7540 does not forbids a server to push resources for different domains, provided that it has the right credentials. Pushing assets for a domain different than the one where the request is received is useful when considering the way web applications are structured today: many serve their application logic using a www.example.com domain, while serving their static assets at static.example.com . Therefore, upon receiving a request to www.example.com, a server may want to push resources for static.example.com. However, section 2.1 of the draft works against that use case. 5.- A last issue has to do with what to include in the cache digest. Mr. Oku proposes to only push resources which are in the critical render path in his article at [1]. Correspondingly, the cache digest would only need to include those resources. Can we have a simple mechanism to control the cache digest contents? I can provide some data and some rough suggestions to address the issues above. How big would a cache digest be anyway? ------------------------------------------------------- To address issues 2 and 3 we need to determine how constrained we are regarding space. We have made a little study[2] across 1300 sites submitted by performance-conscious site operators, and from there we can establish that while 50% of sites fetch between 25 and 110 resources, it is not too rare to have sites doing more than 200 HTTP requests. If anything, that number is going to grow. Specially with HTTP/2. Let's then use 200 as a ballpark estimate of the number of items in a cache digest and start from there. The source that the draft includes for Golomb-coded-sets (GCS) hints that it is possible to use the number of bits in a Bloom filter as an upper bound for the size of the corresponding GCS. Therefore, with a digest of size 200, we would be using an upper bound of 200*1.44*512 bits, which is around 18 kB is expressed as binary, and around 24 kB if expressed in ascii form, base64-encoded, assuming a false positive probability of 1/512. Notice that by using PUSH the browser may skip many of those requests. In our site (https://www.shimmercat.com), we have measured HTTP/2 requests averaging at 60 bytes per request. Therefore, one may end up saving up to 200 * 60 = 12 kB in traffic, bringing down the previous numbers to 18 kB -12 kB =6 kB and 24 kB - 12 kB = 12 kB. I think that 12 kB is acceptable for a site with 200 requests, specially since HTTP/2 PUSH would greatly increase the data transfer density for those sites. Can we embed the cache digest in a header? ------------------------------------------------------------ Having 24 kB of cache digest in a header may delay processing the request more than acceptable, since most servers will wait to get the entire header block before starting to create an answer. There is an alternative however, and that would be to put a field with the cache digest in a request trailer, allowed with chunked transfer under HTTP/1.1 and in all streams with HTTP/2. The pros of having the cache digest in a header or trailer field are the following: we don't break with the tradition of exchanging cache state through headers, headers are visible to developers' tools, it would be possible to test things using polyfills and service workers while the browsers catch up with native implementations, no extensions to HTTP/2 are needed, and cache digests would become possible even over plain old HTTP/1.1. It can also be made a little more future-proof: In the headers: cache-digest: trailers (the indication above is not needed however if the cache-digest-scope is used, see below) In the trailers: cache-digest: data:application/golomb-coded-set;base64,..... The cons is that ascii is bigger than binary. Even if the CACHE_DIGEST frame is pursued, it would be nice to have cache-digest: frame as part of the request (and this time in the headers section, not the trailers) for the server to recognize that a cache digest frame is coming and for developers to have a hint that said information is being transmitted between client and server. Distinguishing representation versions in the cache digest (Addressing point 1) --------------------------------------------------------------------------------------------------------- The GCS filter requires the client and the server to be able to compute the same hash key for a given resource and version. As far as I understand, having semantics here similar to if-modified-since would not be possible. But strong etags could be used when computing the key, therefore enabling the equivalent to if-none-match. Step 4 in the algorithm of section 2.1 of the draft could be extended to have the etag used together with the URL when taking the hash. Which representations should be part of the digest? (Addressing point 4 and 5) -------------------------------------------------------------------------------------------------------- I suggest to introduce the concept of cache digest scope. Only representations which were given a cache digest scope would be made part of a cache digest. And the set of representations URLs to be included by the client in the digest would be the intersection of: 1. The set of representations that have the same cache digest scope in the browser's cache than the domain of the first request (the document), and 2. The set of representations in the browser's cache for which the server is considered authoritative. The cache digest scope would be unique per domain. In other words, it would look like the following: Client asks for https://www.example.com/ Server answers, and adds a header cache-digest-scope: example The server then answers or pushes https://static.example.com/styles.css , it uses the same header cache-digest-scope: example. The server also answers or pushes https://media.example.com/hero-1.png, but no cache-digest-scope is provided. .... some time after, when a new connection is established by the same client to fetch another page from the same domain: Client asks for (a different page) https://www.example.com/page1.html , now the client specifies a header cache-digest-scope: example client also provides a cache digest with all the resources that were assigned the same cache digest scope by the server. That digest would include the resource from https://static.example.com/styles.css but not the one at https://media.example.com/hero-1.png The server answers and pushes a 304 not modified for https://static.example.com/styles.css , or a 200 with new contents, using a cache contents aware PUSH_PROMISE frame. This mechanism addresses 4 by allowing digests to extend over multiple domains, and addresses 5 by allowing the server to control which assets are part of the digest: resources *without* the "cache-digest-scope" header are never made part of the digest. Also, the holder of a wildcard certificate can still use it to host separate multi-domain applications, for example (app1.example.com, static1.example.com with cache digest scope "1") and (app2.example.com, static2.example.com with cache digest scope "2"), without fearing the cache digest to grow too big. Furthermore, if a server doesn't implement PUSH or otherwise doesn't use the cache digest, it implicitly opts out of cache digests, saving bandwidth. The cache-digest-scope: xxxx header would be idem in most requests and responses, and HPACK in HTTP/2 could compress it to a few bytes by using the dynamic table. Best regards, ---- Alcides. [1] http://blog.kazuhooku.com/2015/12/optimizing-performance-of-multi-tiered.html [2] http://nbviewer.ipython.org/github/shimmercat/art_timings/blob/master/TimingsOfResourceLoads.ipynb
Received on Sunday, 10 January 2016 17:12:14 UTC