I’ve been investigating why weak eTags aren’t
being handled properly, specifically why we always get a status 200 when we
include a weak eTag, and only get a status 304 when we delete the weak eTag and
use the last modified date instead, or make the eTag strong. I’ve
found the bug: it’s because the W tag is upper case, and Apache seems to
assume all headers are lower case. However, I don’t know where the
best place is to fix the bug, as discussed below. The bug is in the source code as follows:- The response is evaluated in http_protocol in a function
called ap_meets_conditions(), as follows http_protocol.c: v2.2.16, line 355 not_modified = ap_find_list_item(r->pool, if_nonematch,
etag); This line of code is executed for both strong and weak eTags
for full Get requests (ie Ap_find_list_item exits in the util.c file. util.c: v2.2.16, line 1372 good = good && (*pos++ == apr_tolower(*ptr)); The for-loop surrounding this line of code walks through the
characters in the two strings, comparing them character-by-character. It
maintains some state variables, such as in_qstr that tracks whether or not the
pointer is currently within a quote. If it is within a quote, it compares
the two characters directly; if not within quotes, it executes line 1372 that
lower-cases *ptr value. This assumes that the *pos value is already lower
case. However, the weak eTag is defined by protocol as beginning with the
uppercase letter W, and it appears that uppercase W is indeed what is stored in
the hash table pointed to by if_nonematch and pos. Testing this, my colleagues have created eTags with values such
as “W1234-123123” W/”1234-123123” w/”1234-123123” We have found that provided the eTag is fully quoted, or
preceded by a lowercase w, httpd works as we would expect and returns a 304 to
a conditional Get. However, if the eTag is preceded by an uppercase W, as
per the protocol, then httpd always returns 200. It appears as if the
cache is not working, but that is not the problem – it is the comparison of
the W that is failing and causing the behaviour. A targeted fix is to lowercase if_nonematch just prior to
calling ap_find_list_item(). Because if_nonematch is a pointer, this may
have an undesirable side-effect elsewhere in the code, so a “safer”
approach is first to copy the string into a local variable, then lowercase the
W, and then call ap_find_list_item(). However, I wonder if a more natural
home for the fix would be to make the W lowercase when the hash table is
loaded? Could someone who knows the code much better than me take up
this issue, decide the best location for a fix, and implement it? Many thanks, Ben Cooper Please consider the environment before printing this email. This message should be regarded as confidential. If you have received this email in error please notify the sender and destroy it immediately. Statements of intent shall only become binding when confirmed in hard copy by an authorised signatory. The contents of this email may relate to dealings with other companies within the Detica Limited group of companies. Detica Limited is registered in England under No: 1337451. Registered offices: Surrey Research Park, Guildford, Surrey, GU2 7YP, England. |