diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 2d97c4bcb26..0345c336bfa 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -37,6 +37,23 @@ define('CACHE_PERMANENT', 0); */ define('CACHE_TEMPORARY', -1); +/** + * Indicates that page caching is disabled. + */ +define('CACHE_MODE_DISABLED', 0); + +/** + * Indicates that page caching is enabled. + */ +define('CACHE_MODE_NORMAL', 1); + +/** + * Indicates that page caching is using "external" mode. This disables the + * internal page cache but returns headers allowing downstream caches (such + * as Squid, Varnish, and other reverse proxies) to cache full pages. + */ +define('CACHE_MODE_EXTERNAL', 3); + /** * @defgroup logging_severity_levels Logging severity levels * @{ @@ -1295,10 +1312,17 @@ function drupal_page_header() { } $headers_sent = TRUE; + // If an external cache is being used and there isn't a session cookie allow + // the page to be cached. + $cache_control = 'no-cache, must-revalidate, post-check=0, pre-check=0'; + if (!isset($_COOKIE[session_name()]) && variable_get('cache') == CACHE_MODE_EXTERNAL) { + $cache_control = 'public, max-age=' . variable_get('page_cache_maximum_age', 0); + } + $default_headers = array( - 'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT', + 'Expires' => 'Sun, 11 Mar 1984 12:00:00 GMT', 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME), - 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0', + 'Cache-Control' => $cache_control, 'ETag' => '"' . REQUEST_TIME . '"', ); drupal_send_headers($default_headers); @@ -1373,7 +1397,7 @@ function drupal_serve_page_from_cache(stdClass $cache) { // by sending an Expires date in the past. HTTP/1.1 clients ignores the // Expires header if a Cache-Control: max-age= directive is specified (see RFC // 2616, section 14.9.3). - $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT'; + $default_headers['Expires'] = 'Sun, 11 Mar 1984 12:00:00 GMT'; drupal_send_headers($default_headers); @@ -2318,12 +2342,12 @@ function _drupal_bootstrap_page_cache() { } else { drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE); - $cache_enabled = variable_get('cache'); + $cache_enabled = variable_get('cache', CACHE_MODE_DISABLED); } drupal_block_denied(ip_address()); // If there is no session cookie and cache is enabled (or forced), try // to serve a cached page. - if (!isset($_COOKIE[session_name()]) && $cache_enabled) { + if (!isset($_COOKIE[session_name()]) && $cache_enabled && $cache_enabled != CACHE_MODE_EXTERNAL) { // Make sure there is a user object because its timestamp will be // checked, hook_boot might check for anonymous user etc. $user = drupal_anonymous_user(); @@ -2350,7 +2374,7 @@ function _drupal_bootstrap_page_cache() { // We are done. exit; } - else { + elseif ($cache_enabled != CACHE_MODE_EXTERNAL) { header('X-Drupal-Cache: MISS'); } } diff --git a/includes/common.inc b/includes/common.inc index e6ed202d35a..7f2f5d31484 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2606,7 +2606,9 @@ function drupal_page_footer() { // Commit the user session, if needed. drupal_session_commit(); - if (variable_get('cache', 0) && ($cache = drupal_page_set_cache())) { + // Do not cache if cache is disabled or external. + $cache_mode = variable_get('cache', CACHE_MODE_DISABLED); + if (($cache_mode && $cache_mode != CACHE_MODE_EXTERNAL) && ($cache = drupal_page_set_cache())) { drupal_serve_page_from_cache($cache); } else { diff --git a/includes/form.inc b/includes/form.inc index d7350b3e2b5..df667228977 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -861,7 +861,7 @@ function drupal_process_form($form_id, &$form, &$form_state) { // We'll clear out the cached copies of the form and its stored data // here, as we've finished with them. The in-memory copies are still // here, though. - if (!variable_get('cache', 0) && !empty($form_state['values']['form_build_id'])) { + if (!variable_get('cache', CACHE_MODE_DISABLED) && !empty($form_state['values']['form_build_id'])) { cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form'); cache_clear_all('form_state_' . $form_state['values']['form_build_id'], 'cache_form'); } diff --git a/includes/language.inc b/includes/language.inc index 20909f5a6f4..9b652e5508a 100644 --- a/includes/language.inc +++ b/includes/language.inc @@ -343,7 +343,7 @@ function language_provider_invoke($provider_id, $provider = NULL) { // If the language provider has no cache preference or this is satisfied // we can execute the callback. - $cache = !isset($provider['cache']) || $user->uid || $provider['cache'] == variable_get('cache', 0); + $cache = !isset($provider['cache']) || $user->uid || $provider['cache'] == variable_get('cache', CACHE_MODE_DISABLED); $callback = isset($provider['callbacks']['language']) ? $provider['callbacks']['language'] : FALSE; $langcode = $cache && function_exists($callback) ? $callback($languages) : FALSE; $results[$provider_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE; diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test index 014fc94883c..09c10610702 100644 --- a/modules/simpletest/tests/bootstrap.test +++ b/modules/simpletest/tests/bootstrap.test @@ -161,7 +161,7 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase { $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', t('Page was not cached.')); $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary header was sent.')); $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.')); - $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); + $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 11 Mar 1984 12:00:00 GMT', t('Expires header was sent.')); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.')); // Check cache. @@ -169,7 +169,7 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase { $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', t('Page was cached.')); $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', t('Vary: Cookie header was sent.')); $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', t('Cache-Control header was sent.')); - $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); + $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 11 Mar 1984 12:00:00 GMT', t('Expires header was sent.')); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.')); // Check replacing default headers. @@ -185,7 +185,7 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase { $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), t('Caching was bypassed.')); $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, t('Vary: Cookie header was not sent.')); $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was sent.')); - $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); + $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 11 Mar 1984 12:00:00 GMT', t('Expires header was sent.')); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', t('Custom header was sent.')); } diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 23a975b0cf5..8edf37ad9ae 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -1678,12 +1678,35 @@ function system_performance_settings() { '#title' => t('Caching'), ); - $cache = variable_get('cache', 0); + $cache = variable_get('cache', CACHE_MODE_DISABLED); + $description = '

' . t('The normal cache mode is suitable for most sites and does not cause any side effects. The external cache mode allows Pressflow to set appropriate cache headers for use with an external caching system. Pages served from an external cache will skip the loading (boot) and unloading (exit) of enabled modules and may cause unwanted side effects.') . '

'; + + $problem_modules = array_unique(array_merge(module_implements('boot'), module_implements('exit'))); + sort($problem_modules); + + if (count($problem_modules) > 0) { + $description .= '

' . t('The following enabled modules are potentially incompatible with and external-mode caching and may not function properly: %modules', array('%modules' => implode(', ', $problem_modules))) . '.

'; + } + else { + $description .= '

' . t('Currently, all enabled modules are compatible with an external caching policy. Please note, if you use external caching and enable new modules, you will need to check this page again to ensure compatibility.') . '

'; + } + + $form['page_cache'] = array( + '#type' => 'fieldset', + '#title' => t('Page cache'), + '#description' => t('Enabling the page cache will offer a significant performance boost. Pressflow can store and send compressed cached pages requested by anonymous users. By caching a web page, Pressflow does not have to construct the page each time it is viewed.'), + ); $form['caching']['cache'] = array( - '#type' => 'checkbox', - '#title' => t('Cache pages for anonymous users'), + '#type' => 'radios', + '#title' => t('Caching mode'), '#default_value' => $cache, + '#options' => array( + CACHE_MODE_DISABLED => t('Disabled'), + CACHE_MODE_NORMAL => t('Normal (reccomended for production sites, no side effects)'), + CACHE_MODE_EXTERNAL => t('External (experts only, possible side effects)'), + ), '#weight' => -2, + '#description' => $description, ); $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval'); $period[0] = '<' . t('none') . '>'; diff --git a/modules/system/system.install b/modules/system/system.install index 1161d6cddf1..1f7e7ac6180 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -2128,8 +2128,8 @@ function system_update_7032() { * Move CACHE_AGGRESSIVE to CACHE_NORMAL. */ function system_update_7033() { - if (variable_get('cache') == 2) { - variable_set('cache', 1); + if (variable_get('cache', CACHE_MODE_DISABLED) == 2) { + variable_set('cache', CACHE_MODE_NORMAL); return t('Aggressive caching was disabled and replaced with normal caching. Read the page caching section in default.settings.php for more information on how to enable similar functionality.'); } }