function page_get_cache() in bootstrap.inc only checks the page cache table on GET requests. This seems like a mistake; if a browser or proxy has a cached version of a page, it's not uncommon for it to make a HEAD request to see if it should fetch a new copy. Since HEAD requests never go to the page cache, Drupal will return headers indicating that the page has changed even if it hasn't.
In general, a HEAD request should return the same headers as a GET request. From RFC2616:
The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. ... The response to a HEAD request MAY be cacheable in the sense that the information contained in the response MAY be used to update a previously cached entity from that resource. If the new field values indicate that the cached entity differs from the current entity (as would be indicated by a change in Content-Length, Content-MD5, ETag or Last-Modified), then the cache MUST treat the cache entry as stale.
I suggest changing
if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0) {
to
if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_set_message()) == 0) {
| Comment | File | Size | Author |
|---|---|---|---|
| #26 | d6-head.patch | 2.21 KB | mfb |
| #23 | d6-head.patch | 2.61 KB | mfb |
| #18 | d6-head.patch | 2.71 KB | mfb |
| #7 | d7-head.patch | 7.02 KB | mfb |
| #2 | d7-head.patch | 2.73 KB | mfb |
Comments
Comment #1
mfbMost browsers will make a conditional GET request (If-Modified-Since), not a HEAD request. But tailing my logs, I do see HEAD requests, some from browsers and many from various bots and scripts.
So this seems like a good idea to me. It may also be a good idea to set the cache on a HEAD request (in common.inc); since we're going to all the effort to bootstrap and generate a page we may as well get something useful out of it.
Comment #2
mfbPatch (for head) to support HEAD requests.
Comment #3
moshe weitzman commentedYes, there is no reason why these HEAD requests should be excluded.
Comment #4
dries commentedIt would be great if we could write a test for this.
Comment #5
mfbShould the tests just check that page cache works with HEAD requests or anything more?
Comment #6
damien tournoud commented@mfb: we don't have a page cache at the moment, so this will have to be implemented. Moreover, there is no standardized way to call HEAD requests for now, so you will have to suggest an API extension to
drupal_web_test_case.Comment #7
mfbOK this adds drupalHead() and a very basic page cache test.
drupalHead() should be quite useful for future HTTP header tests.
Comment #8
dries commentedI've made some minor changes to this patch and committed it to CVS HEAD. Thanks.
Comment #9
mfbLooks like something got screwed up, drupalHead() function is repeated twice in drupal_web_test_case.php
Comment #10
mfbBy the way, I just did some testing and actually, it was pointless to add the if $_SERVER['REQUEST_METHOD'] == 'HEAD' logic to page_set_cache(), because on a HEAD request, PHP dies as soon as you call ob_end_flush(), just before cache_set() would be called.
Won't cause any trouble to leave it in but it doesn't do anything useful after all.
Comment #11
dropcube commentedMarking it critical for the fact that
drupalHead()function is repeated twice in drupal_web_test_case.phpComment #13
dries commentedFixed in CVS HEAD.
Comment #14
damien tournoud commentedAs a side note, Dries: #168909: Drupal messages (status/error) are cached along with nodes never made it to 7.x.
Comment #15
eli commentedmfb- Then something must have changed since Drupal 5. It definititely made a difference in 5.
Comment #16
mfb@eli: I was talking specifically about my patch to page_set_cache() in common.inc, not your patch to page_get_cache() in bootstrap.inc.
I think it's ok to leave the HEAD logic in common.inc so there is some uniformity to how HEAD requests are handled. If you skip this loop for HEAD requests then the HEAD request moves on to the exit hook, whereas HEAD requests normally (with caching disabled or a cache already set) die before reaching the exit hook.
Comment #17
Anonymous (not verified) commentedAutomatically closed -- issue fixed for two weeks with no activity.
Comment #18
mfbThis fix could get backported if desired.
Comment #19
webchickThis change broke SimpleTest all over the place in #308186: cURL 7.18.2 and SimpleTest module bitterly despise one another. After removing the CURLOPT_NOBODY => FALSE from curlExec() in drupal_web_test_case.php, things are working okay.
What larger consequence does that have, and can we get some tests in place to make ensure that whatever was changed here is doing what it needs to do?
Comment #20
chx commentedWe ran nto http://curl.haxx.se/mail/tracker-2008-07/0006.html . If you read the manpage though http://pwet.fr/man/linux/fonctions_bibliotheques/curl_easy_setopt When setting CURLOPT_POST to a non-zero value, it will automatically set CURLOPT_NOBODY to 0 (since 7.14.1). As 7.14.1 was released on 09-06-2005 I believe we can conclude this fixed. You can http://ca.php.net/manual/en/function.curl-version.php use this to complain hard when simpletest gets enabled if you so want.
Comment #21
chx commentedI compiled a libcurl 7.19 to confirm that's the culprit and indeed it is. Behold this:
where d.php just dump $_POST
Comment #22
webchickOk, sorry for the freakout. I couldn't quite parse that referenced changelog entry, but chx summed it up as:
"if you set FOO 1 then BAR gets set to 0 but if you set BAR to 0 then FOO gets set to 0 as well.
WTF?"
So this is basically a workaround we need to do in our curlExec function to accommodate cURL's wacky, wacky ways.
And since SimpleTest is not part of D6, this can go back where it belongs.
Comment #23
mfbReroll due to offset warnings.
Comment #24
dpearcefl commentedIs this still an issue using current Drupal 6?
Comment #26
mfbYes, re-rolled
Comment #27
dpearcefl commentedThanks!