? 147310-94.patch ? 147310-95.patch ? 147310-96.patch ? 147310-97.patch ? sites/all/modules ? sites/default/files ? sites/default/settings.php Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.263 diff -u -p -r1.263 bootstrap.inc --- includes/bootstrap.inc 4 Jan 2009 16:15:54 -0000 1.263 +++ includes/bootstrap.inc 7 Jan 2009 05:31:33 -0000 @@ -720,10 +720,24 @@ function drupal_load($type, $name) { * @see page_set_cache() */ function drupal_page_header() { - header("Expires: Sun, 19 Nov 1978 05:00:00 GMT"); - header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); - header("Cache-Control: store, no-cache, must-revalidate"); - header("Cache-Control: post-check=0, pre-check=0", FALSE); + // Check if module_implements exists to avoid problems during installation. + $send_uncacheable_headers = TRUE; + if(function_exists('module_implements') && module_implements('expires')) { + // Pass FALSE to hook_expires because the page isn't coming from cache. + $lifetime = min(module_invoke_all('expires', FALSE)); + if($lifetime > 0) { + $send_uncacheable_headers = FALSE; + header("Expires: " . gmdate(DATE_RFC1123, REQUEST_TIME + $lifetime)); + header('Cache-Control: public, max-age=' . $lifetime); + } + } + + if($send_uncacheable_headers) { + header("Expires: Sun, 19 Nov 1978 05:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: store, no-cache, must-revalidate"); + header("Cache-Control: post-check=0, pre-check=0", FALSE); + } } /** @@ -756,9 +770,23 @@ function drupal_page_cache_header($cache header("Last-Modified: " . gmdate(DATE_RFC1123, $cache->created)); header("ETag: $etag"); - // The following headers force validation of cache: - header("Expires: Sun, 19 Nov 1978 05:00:00 GMT"); - header("Cache-Control: must-revalidate"); + // The following allows modules to specify their own cache expiration time. + $send_uncacheable_headers = TRUE; + if(count(module_implements('expires')) > 0) { + $lifetime = min(module_invoke_all('expires', TRUE)); + $age = abs(REQUEST_TIME - $cache->created); + if($lifetime - $age > 0) { + $send_uncacheable_headers = FALSE; + header("Expires: " . gmdate(DATE_RFC1123, $cache->created + $lifetime)); + header('Cache-Control: public, max-age=' . max(0, $lifetime - $age)); + } + } + + if($send_uncacheable_headers) { + // Use the old header which does not get cached. + header("Expires: Sun, 19 Nov 1978 05:00:00 GMT"); + header("Cache-Control: must-revalidate"); + } if (variable_get('page_compression', TRUE)) { // Determine if the browser accepts gzipped data. Index: modules/system/system.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v retrieving revision 1.9 diff -u -p -r1.9 system.api.php --- modules/system/system.api.php 4 Jan 2009 19:56:51 -0000 1.9 +++ modules/system/system.api.php 7 Jan 2009 05:31:33 -0000 @@ -144,6 +144,24 @@ function hook_exit($destination = NULL) db_query('UPDATE {counter} SET hits = hits + 1 WHERE type = 1'); } +/** + * Suggest an expiration time to send to the client as the expires HTTP header. + * + * Only use this hook if you need your pages to be cacheable by reverse proxies + * or CDNs. If multiple modules return a value for this function, the earliest + * timestamp is honored. The simplest implementation of this function would + * return the number of seconds to cache all pages. More advanced implementations + * could return a different value depending on the current page. + * + * @param $page_is_cached + * Whether the response is from cache + * @return + * Number of seconds before the current page expires. + */ +function hook_expires($page_is_cached) { + return 0; +} + /** * Insert closing HTML. *