Commit c29daaaa authored by Dries's avatar Dries
Browse files

- Patch #80887 by Matt et al: added an aggressive caching strategy.

parent 8118bd0e
......@@ -45,7 +45,9 @@ Drupal x.x.x, xxxx-xx-xx (development version)
* changed node rendering to work with structured arrays.
- performance:
* improved session handling: reduces database overhead.
* improved access checking: reduces database overhead.
* omit sidebars when serving a '404 - Page not found': saves CPU cycles and bandwidth.
* added an 'aggressive' caching policy.
- removed the archive module.
- upgrade system:
* created space for update branches.
......
......@@ -10,7 +10,8 @@
define('CACHE_TEMPORARY', -1);
define('CACHE_DISABLED', 0);
define('CACHE_ENABLED', 1);
define('CACHE_NORMAL', 1);
define('CACHE_AGGRESSIVE', 2);
define('WATCHDOG_NOTICE', 0);
define('WATCHDOG_WARNING', 1);
......@@ -139,7 +140,7 @@ function conf_path() {
function drupal_unset_globals() {
if (ini_get('register_globals')) {
$allowed = array('_ENV' => 1, '_GET' => 1, '_POST' => 1, '_COOKIE' => 1, '_FILES' => 1, '_SERVER' => 1, '_REQUEST' => 1, 'access_check' => 1, 'GLOBALS' => 1);
foreach ($GLOBALS as $key => $value) {
foreach ($GLOBALS as $key) {
if (!isset($allowed[$key])) {
unset($GLOBALS[$key]);
}
......@@ -382,11 +383,6 @@ function drupal_load($type, $name) {
/**
* Set HTTP headers in preparation for a page response.
*
* The general approach here is that anonymous users can keep a local
* cache of the page, but must revalidate it on every request. Then,
* they are given a '304 Not Modified' response as long as they stay
* logged out and the page has not been modified.
*
* Authenticated users are always given a 'no-cache' header, and will
* fetch a fresh page on every request. This prevents authenticated
* users seeing locally cached pages that show them as logged out.
......@@ -394,62 +390,66 @@ function drupal_load($type, $name) {
* @see page_set_cache
*/
function drupal_page_header() {
if (variable_get('cache', 0) && $cache = page_get_cache()) {
bootstrap_invoke_all('init');
// Set default values:
$last_modified = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
$etag = '"'.md5($last_modified).'"';
// See if the client has provided the required HTTP headers:
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
if ($if_modified_since && $if_none_match
&& $if_none_match == $etag // etag must match
&& $if_modified_since == $last_modified) { // if-modified-since must match
header('HTTP/1.1 304 Not Modified');
// All 304 responses must send an etag if the 200 response for the same object contained an etag
header("Etag: $etag");
exit();
}
// Send appropriate response:
header("Last-Modified: $last_modified");
header("ETag: $etag");
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: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", FALSE);
header("Pragma: no-cache");
}
// The following headers force validation of cache:
header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
header("Cache-Control: must-revalidate");
/**
* Set HTTP headers in preparation for a cached page response.
*
* The general approach here is that anonymous users can keep a local
* cache of the page, but must revalidate it on every request. Then,
* they are given a '304 Not Modified' response as long as they stay
* logged out and the page has not been modified.
*
*/
function drupal_page_cache_header($cache) {
// Set default values:
$last_modified = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
$etag = '"'.md5($last_modified).'"';
// See if the client has provided the required HTTP headers:
$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
if ($if_modified_since && $if_none_match
&& $if_none_match == $etag // etag must match
&& $if_modified_since == $last_modified) { // if-modified-since must match
header('HTTP/1.1 304 Not Modified');
// All 304 responses must send an etag if the 200 response for the same object contained an etag
header("Etag: $etag");
exit();
}
// Determine if the browser accepts gzipped data.
if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE && function_exists('gzencode')) {
// Strip the gzip header and run uncompress.
$cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
}
elseif (function_exists('gzencode')) {
header('Content-Encoding: gzip');
}
// Send appropriate response:
header("Last-Modified: $last_modified");
header("ETag: $etag");
// Send the original request's headers. We send them one after
// another so PHP's header() function can deal with duplicate
// headers.
$headers = explode("\n", $cache->headers);
foreach ($headers as $header) {
header($header);
}
// The following headers force validation of cache:
header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
header("Cache-Control: must-revalidate");
print $cache->data;
bootstrap_invoke_all('exit');
exit();
// Determine if the browser accepts gzipped data.
if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE && function_exists('gzencode')) {
// Strip the gzip header and run uncompress.
$cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
}
else {
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: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", FALSE);
header("Pragma: no-cache");
elseif (function_exists('gzencode')) {
header('Content-Encoding: gzip');
}
// Send the original request's headers. We send them one after
// another so PHP's header() function can deal with duplicate
// headers.
$headers = explode("\n", $cache->headers);
foreach ($headers as $header) {
header($header);
}
print $cache->data;
}
/**
......@@ -677,18 +677,17 @@ function _drupal_bootstrap($phase) {
global $conf;
switch ($phase) {
case DRUPAL_BOOTSTRAP_CONFIGURATION:
drupal_unset_globals();
// Initialize the configuration
conf_init();
break;
case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
if (variable_get('page_cache_fastpath', 0)) {
require_once variable_get('cache_inc', './includes/cache.inc');
page_cache_fastpath();
exit;
}
_drupal_cache_init($phase);
break;
case DRUPAL_BOOTSTRAP_DATABASE:
// Initialize the default database.
require_once './includes/database.inc';
......@@ -702,10 +701,10 @@ function _drupal_bootstrap($phase) {
break;
case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
require_once variable_get('cache_inc', './includes/cache.inc');
require_once './includes/module.inc';
// Start a page timer:
timer_start('page');
// Initialize configuration variables, using values from settings.php if available.
$conf = variable_init(isset($conf) ? $conf : array());
_drupal_cache_init($phase);
// deny access to hosts which were banned. t() is not yet available.
if (drupal_is_denied('host', $_SERVER['REMOTE_ADDR'])) {
......@@ -714,8 +713,9 @@ function _drupal_bootstrap($phase) {
exit();
}
// Initialize configuration variables, using values from conf.php if available.
$conf = variable_init(isset($conf) ? $conf : array());
// Start a page timer:
timer_start('page');
drupal_page_header();
break;
......@@ -732,6 +732,36 @@ function _drupal_bootstrap($phase) {
}
}
/**
* Initialize the caching strategy, which loads at different stages within
* Drupal's bootstrap process.
*/
function _drupal_cache_init($phase) {
require_once variable_get('cache_inc', './includes/cache.inc');
if ($phase == DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE && variable_get('page_cache_fastpath', 0)) {
if (page_cache_fastpath()) {
exit();
}
}
elseif ($phase == DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE) {
if ($cache = page_get_cache()) {
if (variable_get('cache', CACHE_DISABLED) == CACHE_AGGRESSIVE) {
drupal_page_cache_header($cache);
exit();
}
elseif (variable_get('cache', CACHE_DISABLED) == CACHE_NORMAL) {
require_once './includes/module.inc';
bootstrap_invoke_all('init');
drupal_page_cache_header($cache);
bootstrap_invoke_all('exit');
exit();
}
}
require_once './includes/module.inc';
}
}
/**
* Enables use of the theme system without requiring database access. Since
* there is not database access no theme will be enabled and the default
......
......@@ -38,6 +38,9 @@ thead th {
.error {
color: red;
}
.ok {
color: green;
}
.item-list .icon {
color: #555;
float: right;
......
......@@ -34,6 +34,8 @@ function system_help($section) {
return t('Handles general site configuration for administrators.');
case 'admin':
return t('<p>Welcome to the administration section. Here you may control how your site functions.</p>');
case 'admin/settings/page-caching':
return t('Enabling the cache will offer a sufficient performance boost. Drupal can store and send compressed cached pages requested by "anonymous" users. By caching a web page, Drupal does not have to create the page each time someone wants to view it.');
case 'admin/build/themes':
return t('<p>Select which themes are available to your users and specify the default theme. To configure site-wide display settings, click the "configure" task above. Alternately, to override these settings in a specific theme, click the "configure" link for the corresponding theme. Note that different themes may have different regions available for rendering content like blocks. If you want consistency in what your users see, you may wish to enable only one theme.</p>');
case 'admin/build/themes/settings':
......@@ -586,12 +588,24 @@ function system_error_reporting_settings() {
function system_page_caching_settings() {
$description = '<p>'. t("The normal cache mode is suitable for most sites and does not cause any side effects. The aggressive cache mode causes Drupal to skip the loading (init) and unloading (exit) of enabled modules when serving a cached page. This results in an additional performance boost but can cause unwanted side effects.") .'</p>';
$problem_modules = array_unique(array_merge(module_implements('init'), module_implements('exit')));
sort($problem_modules);
if (count($problem_modules) > 0) {
$description .= '<p>'. t("<strong class=\"error\">The following enabled modules are incompatible with aggressive mode caching and will not function properly: %modules</strong>", array('%modules' => implode(', ', $problem_modules))) .'.</p>';
}
else {
$description .= '<p>'. t("<strong class=\"ok\">Currently, all enabled modules are compatible with the aggressive caching policy.</strong> Please note, if you use aggressive caching and enable new modules, you'll need to check this page again to ensure compatibility.") .'</p>';
}
$form['cache'] = array(
'#type' => 'radios',
'#title' => t('Page cache'),
'#title' => t('Caching mode'),
'#default_value' => variable_get('cache', CACHE_DISABLED),
'#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_ENABLED => t('Enabled')),
'#description' => t("Drupal has a caching mechanism which stores dynamically generated web pages in a database. By caching a web page, Drupal does not have to create the page each time someone wants to view it, instead it takes only one SQL query to display it, reducing response time and the server's load. Only pages requested by \"anonymous\" users are cached. In order to reduce server load and save bandwidth, Drupal stores and sends compressed cached pages.")
'#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL => t('Normal (recommended, no side effects)'), CACHE_AGGRESSIVE => t('Aggressive (experts only, possible side effects)')),
'#description' => $description
);
$period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
......@@ -601,7 +615,7 @@ function system_page_caching_settings() {
'#title' => t('Minimum cache lifetime'),
'#default_value' => variable_get('cache_lifetime', 0),
'#options' => $period,
'#description' => t('Enabling the cache will offer a sufficient performance boost for most low-traffic and medium-traffic sites. On high-traffic sites it can become necessary to enforce a minimum cache lifetime. The minimum cache lifetime is the minimum amount of time that will go by before the cache is emptied and recreated. A larger minimum cache lifetime offers better performance, but users will not see new content for a longer period of time.')
'#description' => t('On high-traffic sites it can become necessary to enforce a minimum cache lifetime. The minimum cache lifetime is the minimum amount of time that will go by before the cache is emptied and recreated. A larger minimum cache lifetime offers better performance, but users will not see new content for a longer period of time.')
);
return system_settings_form($form);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment