#19705: CommonMiddleware handles If-None-Match incorrectly
-------------------------------+------------------------------------
     Reporter:  aaugustin      |                    Owner:  syphar
         Type:  Bug            |                   Status:  assigned
    Component:  HTTP handling  |                  Version:  master
     Severity:  Normal         |               Resolution:
     Keywords:                 |             Triage Stage:  Accepted
    Has patch:  1              |      Needs documentation:  0
  Needs tests:  0              |  Patch needs improvement:  1
Easy pickings:  0              |                    UI/UX:  0
-------------------------------+------------------------------------
Changes (by marfire):

 * cc: k@… (added)


Comment:

 At the moment there are several interrelated problems with the way Django
 handles ETags. Briefly:

 * As stated in the original report above, we're not returning the correct
 headers with our `304s`.
 * Gzipped responses are not getting the benefits of `304s` since their
 ETags are not compared properly. See tickets #16035 and #26771.
 * Our processing of conditional requests does not follow the
 specification's rewritten
 [[https://tools.ietf.org/html/rfc7232#section-6|precedence rules]] from
 2014.
 * The `condition()`, `vary*()`, `cache_control()`, and `gzip_page()`
 decorators work properly in some undocumented orders but not in others.
 Here are the changes I would suggest to address these problems:
 * Move ETag handling out of `CommonMiddleware` and into
 `ConditionalGetMiddleware`. This change has already been accepted (#26447)
 and partially implemented ([[https://github.com/django/django/pull/6393|PR
 6393]]). (Though I'm less confident about the deprecation of `USE_ETAGS`
 in that ticket.)
 * Change `GZipMiddleware` to remove the `;gzip` token from the incoming
 ETags in `process_request()`. That will allow comparisons to work
 properly. Document this.
 * Change the `304` headers to match the specification.
 * Change our conditional view logic to match the (simpler) specified
 precedence rules.
 * Change our use of `GzipFile` to specify a modification time of 0. That
 will make our gzip output dependent only on the response body, which means
 that we can usefully compare ETags on gzipped content, which means that we
 don't have to care about the order of the `gzip_page()` decorator.
 * Document that the `condition()` decorator should be below `vary()` and
 `cache_control()` so that those headers can be set properly.
 In more detail:

 [[https://code.djangoproject.com/ticket/19705#comment:12|mrmachine's
 comment]] raises a fair point. The fundamental issue here is that Django's
 architecture strives to be layered and decoupled, but ETags are by
 definition highly coupled to the response, so it's difficult to isolate
 them to one layer (whether that be middleware or a decorator).

 That said, [[https://tools.ietf.org/html/rfc7232#section-2.1|the
 specification]] only requires that the ETag change when the response body
 changes, and I don't think that's a very common middleware behavior. Only
 `GZipMiddleware` meets that definition among the middleware in core, for
 example. And if the body ''is'' changed, the ETag can be changed in a
 manner similar to what I'm suggesting for `GZipMiddleware`. I do think
 this needs to be documented, though.

 Regarding which headers to send back with `304s`,
 [[https://tools.ietf.org/html/rfc7232#section-4.1|the specification]] says
 this:
 > The server generating a 304 response MUST generate any of the following
 header fields that would have been sent in a 200 (OK) response to the same
 request: `Cache-Control`, `Content-Location`, `Date`, `ETag`, `Expires`,
 and `Vary`. Since the goal of a 304 response is to minimize information
 transfer when the recipient already has one or more cached
 representations, a sender SHOULD NOT generate representation metadata
 other than the above listed fields unless said metadata exists for the
 purpose of guiding cache updates (e.g., `Last-Modified` might be useful if
 the response does not have an `ETag` field).
 So I think we should return those headers from the response, along with
 the cookies (since that is what Django already does, and since it's
 [[https://bz.apache.org/bugzilla/show_bug.cgi?id=18388|apparently very
 common]] despite not being mandated by the standard).

 I'm able to work on this, but the next step depends on the fate of
 [[https://github.com/django/django/pull/6393|PR 6393]] , since that
 accomplishes some of the reorganization mentioned above.

--
Ticket URL: <https://code.djangoproject.com/ticket/19705#comment:14>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/067.224b38f84dac91d967d222c9a58b124e%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to