--
Ticket URL: <https://code.djangoproject.com/ticket/17834>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* needs_docs: => 0
* has_patch: 0 => 1
* needs_better_patch: => 0
* needs_tests: => 1
* stage: Unreviewed => Accepted
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:1>
* status: new => closed
* resolution: => wontfix
Comment:
I digged a little more in the code. In Django code, patch_response_headers
is never called for responses which code is not in the 200 range. If you
want to call this function from your own code, I think it's your
responsability to not call it on redirect responses.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:2>
Comment (by paulegan):
True, internally it's currently used only in UpdateCacheMiddleware, but
even there a response with status 200 can still have an empty body.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:3>
Comment (by claudep):
Yes, but being empty or not, two identical responses get the same ETag. Is
it a problem for you?
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:4>
Comment (by paulegan):
The problem is that the responses are not necessarily identical. The
response headers are often as important as the content itself. The http
spec is clear about this -
http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html - and goes on to say
"in order to be legal, a strong entity tag MUST change whenever the
associated entity value changes in any way".
Ideally Django ought to compute the ETag from the entity body and entity
headers but in practice the body alone is usually sufficient for uniquely
identifying the entity. Of course that's not the case when the body is
empty. If explicitly coding an ETag for such a response, the values from
the appropriate entity headers would be included when generating the tag.
For a function like patch_response_headers, where the addition of the ETag
header is implicit, I think it is simpler & safer to not compute a tag
when the body is empty (and the headers dominate).
I should note that this ticket isn't simple nit-picking but based on a
real-world issue found in a production environment.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:5>
Comment (by claudep):
I guess that generating the ETag based on the content will match 98% of
use cases. Now for the 2% remaining, you should be able to take advantage
of view decorators to customize the generation of ETags
(https://docs.djangoproject.com/en/dev/topics/conditional-view-
processing/). Did you try to use them?
Replying to [comment:5 paulegan]:
> I should note that this ticket isn't simple nit-picking but based on a
real-world issue found in a production environment.
Sure, I don't blame you for discussing it :-) But as the use case of the
!HttpResponseRedirect in the description is not entirely valid IMHO, maybe
you could provide us with more details about your real use case.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:6>
Comment (by paulegan):
The specific issue I came across was with a redirect response and wasn't
using the cache middleware. I don't entirely agree that this immediately
invalidates the bug however since many projects use patch_response_headers
directly (a google search throws up quite a few examples and there are
sure to be many more private projects, as in my case).
Stepping aside that argument, my main point is that a user of
patch_response_headers may unexpectedly create responses that don't meet
the http requirements and run into caching issues. It's certainly an
unusual case that probably won't affect too many projects and can
definitely be worked-around with other methods (an explicit ETag or the
conditional decorators). However when the entity body is empty it seems
to me to be safer to avoid the potential for ETag collision.
I guess it comes down to a balance between those users of
patch_response_headers who expect an ETag even on empty responses and
those who expect the function to do the right thing (not break caching).
:-)
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:7>
* status: closed => reopened
* resolution: wontfix =>
* stage: Accepted => Design decision needed
Comment:
At this point, I see 3 ways to solve this:
1. Consider it is a corner case, and let the programmers handle
patch_response_headers carefully for responses without content.
2. Change ETag computing to always include headers (generates digest on
str(response) instead of str(response.content)).
3. Special case ETag computing to use headers only if response.content is
empty (OP proposal).
Note also that I have refactored ETag computing in a patch on #14722.
Marking as DDN to get a core committer opinion.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:8>
* cc: rene.puls@… (added)
Comment:
I just stumbled over the same problem and would like to add another
possible solution.
The RFC says this about redirect responses (e.g. section 10.3.2): "Unless
the request method was HEAD, the entity of the response SHOULD contain a
short hypertext note with a hyperlink to the new URI(s)." While this is
not a requirement, it would help in this scenario, because the response
body (and thus the ETag) would then depend on the redirect URL.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:9>
* stage: Design decision needed => Accepted
Comment:
It seems to me that the headers could be included in the Etag calculation
without any harm.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:11>
Comment (by aaugustin):
#12789 was a duplicate.
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:12>
* owner: nobody => dwightgunning
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:13>
* cc: k@… (added)
Comment:
For reference, here's what the updated
[https://tools.ietf.org/html/rfc7232#section-2.1 RFC 7232] specification
has to say on the matter:
> A "strong validator" [like the ETags we compute] is representation
metadata that changes value whenever a change occurs to the representation
data that would be observable in the payload body of a 200 (OK) response
to GET. A strong validator might change for reasons other than a change to
the representation data, such as when a semantically significant part of
the representation metadata is changed (e.g., Content-Type), but it is in
the best interests of the origin server to only change the value when it
is necessary to invalidate the stored responses held by remote caches and
authoring tools.
So, the specification does not require the `ETag` to change unless the
response body itself changes. It //could// change, if we know that a
change to the headers invalidates the response. Since the framework can't,
in general, know that, and since the specification cautions against
invalidating the response when it's unnecessary, my opinion is that the
status quo of basing the `ETag` only on the response body is fine.
(If we were to try and take the headers into account, though, it's worth
noting which headers they should be.
[https://tools.ietf.org/html/rfc7231#section-3.1 RFC 7231] defines the
representation metadata as the `Content-Type`, `Content-Encoding`,
`Content-Language`, and `Content-Location` headers.)
--
Ticket URL: <https://code.djangoproject.com/ticket/17834#comment:14>