summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2013-06-06 09:21:58 +0100
committerAlex Pott2013-06-06 09:21:58 +0100
commit6941c5b78b345762049e147cc1d71a3635a76fa9 (patch)
treee37e554c015ba4f30143bd06ec76590044dd5bdf
parentb21943922d94261dfa8de34995d206d5e8e9c3d7 (diff)
Issue #1984766 by sdboyer, Niklas Fiekas, msonnabaum: Fixed Start relying on Request/Response objects for cache handling.
-rw-r--r--core/includes/bootstrap.inc77
-rw-r--r--core/includes/common.inc32
-rw-r--r--core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php25
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php17
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php2
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php2
-rw-r--r--core/modules/toolbar/toolbar.module11
-rw-r--r--core/update.php1
8 files changed, 104 insertions, 63 deletions
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 74d0cb2..c8c6e25 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\RuntimeException as DependencyInjectionRuntimeException;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Language\Language;
use Drupal\Core\Lock\DatabaseLockBackend;
use Drupal\Core\Lock\LockBackendInterface;
@@ -1068,6 +1069,8 @@ function drupal_load($type, $name) {
* reason phrase, e.g. "404 Not Found".
* @param $append
* Whether to append the value to an existing header or to replace it.
+ *
+ * @deprecated Header handling is being shifted to a Symfony response object.
*/
function drupal_add_http_header($name, $value, $append = FALSE) {
// The headers as name/value pairs.
@@ -1087,7 +1090,6 @@ function drupal_add_http_header($name, $value, $append = FALSE) {
else {
$headers[$name_lower] = $value;
}
- drupal_send_headers(array($name => $headers[$name_lower]), TRUE);
}
/**
@@ -1100,6 +1102,8 @@ function drupal_add_http_header($name, $value, $append = FALSE) {
* @return
* A string containing the header value, or FALSE if the header has been set,
* or NULL if the header has not been set.
+ *
+ * @deprecated Header handling is being shifted to a Symfony response object.
*/
function drupal_get_http_header($name = NULL) {
$headers = &drupal_static('drupal_http_headers', array());
@@ -1116,7 +1120,9 @@ function drupal_get_http_header($name = NULL) {
* Sets the preferred name for the HTTP header.
*
* Header names are case-insensitive, but for maximum compatibility they should
- * follow "common form" (see RFC 2617, section 4.2).
+ * follow "common form" (see RFC 2616, section 4.2).
+ *
+ * @deprecated Header handling is being shifted to a Symfony response object.
*/
function _drupal_set_preferred_header_name($name = NULL) {
static $header_names = array();
@@ -1138,6 +1144,8 @@ function _drupal_set_preferred_header_name($name = NULL) {
* @param bool $only_default
* (optional) If TRUE and headers have already been sent, send only the
* specified headers.
+ *
+ * @deprecated Header handling is being shifted to a Symfony response object.
*/
function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
$headers_sent = &drupal_static(__FUNCTION__, FALSE);
@@ -1192,6 +1200,8 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
* identical.
*
* @see drupal_page_set_cache()
+ *
+ * @deprecated Header handling is being shifted to a Symfony response object.
*/
function drupal_page_header() {
$headers_sent = &drupal_static(__FUNCTION__, FALSE);
@@ -1220,26 +1230,24 @@ function drupal_page_header() {
* and the conditions match those currently in the cache, a 304 Not Modified
* response is sent.
*/
-function drupal_serve_page_from_cache(stdClass $cache) {
+function drupal_serve_page_from_cache(stdClass $cache, Response $response, Request $request) {
$config = config('system.performance');
+ // First half: we must determine if we should be returning a 304.
+
// Negotiate whether to use compression.
$page_compression = $config->get('response.gzip') && extension_loaded('zlib');
- $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
+ $return_compressed = $page_compression && $request->server->has('HTTP_ACCEPT_ENCODING') && strpos($request->server->get('HTTP_ACCEPT_ENCODING'), 'gzip') !== FALSE;
// Get headers. Keys are lower-case.
$boot_headers = drupal_get_http_header();
- // Headers generated in this function, that may be replaced or unset using
- // drupal_add_http_headers(). Keys are mixed-case.
- $default_headers = array();
-
foreach ($cache->data['headers'] as $name => $value) {
// In the case of a 304 response, certain headers must be sent, and the
// remaining may not (see RFC 2616, section 10.3.5).
$name_lower = strtolower($name);
if (in_array($name_lower, array('content-location', 'expires', 'cache-control', 'vary')) && !isset($boot_headers[$name_lower])) {
- drupal_add_http_header($name, $value);
+ $response->headers->set($name, $value);
unset($cache->data['headers'][$name]);
}
}
@@ -1248,39 +1256,40 @@ function drupal_serve_page_from_cache(stdClass $cache) {
// to that one particular client due to Vary: Cookie. Thus, do not set
// max-age > 0, allowing the page to be cached by external proxies, when a
// session cookie is present unless the Vary header has been replaced.
- $max_age = !isset($_COOKIE[session_name()]) || isset($boot_headers['vary']) ? $config->get('cache.page.max_age') : 0;
- $default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
+ $max_age = !$request->cookies->has(session_name()) || isset($boot_headers['vary']) ? $config->get('cache.page.max_age') : 0;
+ $response->headers->set('Cache-Control', 'public, max-age=' . $max_age);
// Entity tag should change if the output changes.
- $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"';
- header('Etag: ' . $etag);
+ $response->setEtag($cache->created);
// See if the client has provided the required HTTP headers.
- $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
- $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
+ $if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE;
+ $if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE;
if ($if_modified_since && $if_none_match
- && $if_none_match == $etag // etag must match
+ && $if_none_match == $response->headers->get('etag') // etag must match
&& $if_modified_since == $cache->created) { // if-modified-since must match
- header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
- drupal_send_headers($default_headers);
+ $response->setStatusCode(304);
return;
}
+ // Second half: we're not returning a 304, so put in other headers.
+
// Send the remaining headers.
foreach ($cache->data['headers'] as $name => $value) {
+ $response->headers->set($name, $value);
drupal_add_http_header($name, $value);
}
- $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
+ $response->setLastModified(\DateTime::createFromFormat('U', $cache->created));
// HTTP/1.0 proxies does not support the Vary header, so prevent any caching
// 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';
-
- drupal_send_headers($default_headers);
+ if (!$response->getExpires()) {
+ $response->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 GMT'));
+ }
// Allow HTTP proxies to cache pages for anonymous users without a session
// cookie. The Vary header is used to indicates the set of request-header
@@ -1288,17 +1297,17 @@ function drupal_serve_page_from_cache(stdClass $cache) {
// response to reply to a subsequent request for a given URL without
// revalidation.
if (!isset($boot_headers['vary']) && !settings()->get('omit_vary_cookie')) {
- header('Vary: Cookie');
+ $response->setVary('Cookie', FALSE);
}
if ($page_compression) {
- header('Vary: Accept-Encoding', FALSE);
+ $response->setVary('accept-encoding', FALSE);
// If page_compression is enabled, the cache contains gzipped data.
if ($return_compressed) {
// $cache->data['body'] is already gzip'ed, so make sure
// zlib.output_compression does not compress it once more.
ini_set('zlib.output_compression', '0');
- header('Content-Encoding: gzip');
+ $response->headers->set('content-encoding', 'gzip');
}
else {
// The client does not support compression, so unzip the data in the
@@ -1307,8 +1316,7 @@ function drupal_serve_page_from_cache(stdClass $cache) {
}
}
- // Print the page.
- print $cache->data['body'];
+ $response->setContent($cache->data['body']);
}
/**
@@ -2066,27 +2074,34 @@ function _drupal_bootstrap_page_cache() {
$config = config('system.performance');
$cache_enabled = $config->get('cache.page.use_internal');
}
+
+ // @todo this is *criminal*. but, necessary, until we fix bootstrap ordering.
+ $request = Request::createFromGlobals();
// 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 (!$request->cookies->has(session_name()) && $cache_enabled) {
// Make sure there is a user object because its timestamp will be checked.
$user = drupal_anonymous_user();
// Get the page from the cache.
$cache = drupal_page_get_cache();
// If there is a cached page, display it.
if (is_object($cache)) {
- header('X-Drupal-Cache: HIT');
+ $response = new Response();
+ $response->headers->set('X-Drupal-Cache', 'HIT');
// Restore the metadata cached with the page.
_current_path($cache->data['path']);
drupal_set_title($cache->data['title'], PASS_THROUGH);
date_default_timezone_set(drupal_get_user_timezone());
- drupal_serve_page_from_cache($cache);
+ drupal_serve_page_from_cache($cache, $response, $request);
+
// We are done.
+ $response->prepare($request);
+ $response->send();
exit;
}
else {
- header('X-Drupal-Cache: MISS');
+ drupal_add_http_header('X-Drupal-Cache', 'MISS');
}
}
}
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 0f8a90e..1ec3eaa 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -8,6 +8,8 @@ use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\Language;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Yaml\Parser;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\PhpStorage\PhpStorageFactory;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\CacheBackendInterface;
@@ -4232,15 +4234,15 @@ function _drupal_bootstrap_full($skip = FALSE) {
*
* @see drupal_page_header()
*/
-function drupal_page_set_cache($body) {
+function drupal_page_set_cache(Response $response, Request $request) {
global $base_root;
if (drupal_page_is_cacheable()) {
$cache = (object) array(
- 'cid' => $base_root . request_uri(),
+ 'cid' => $base_root . $request->getRequestUri(),
'data' => array(
- 'path' => current_path(),
- 'body' => $body,
+ 'path' => $request->attributes->get('system_path'),
+ 'body' => $response->getContent(),
'title' => drupal_get_title(),
'headers' => array(),
),
@@ -4249,16 +4251,18 @@ function drupal_page_set_cache($body) {
'created' => REQUEST_TIME,
);
- // Restore preferred header names based on the lower-case names returned
- // by drupal_get_http_header().
- $header_names = _drupal_set_preferred_header_name();
- foreach (drupal_get_http_header() as $name_lower => $value) {
- $cache->data['headers'][$header_names[$name_lower]] = $value;
- if ($name_lower == 'expires') {
- // Use the actual timestamp from an Expires header if available.
- $date = new DrupalDateTime($value);
- $cache->expire = $date->getTimestamp();
- }
+ $cache->data['headers'] = $response->headers->all();
+
+ // Hack: exclude the x-drupal-cache header; it may make it in here because
+ // of awkwardness in how we defer sending it over in _drupal_page_get_cache.
+ if (isset($cache->data['headers']['x-drupal-cache'])) {
+ unset($cache->data['headers']['x-drupal-cache']);
+ }
+
+ // Use the actual timestamp from an Expires header, if available.
+ if ($date = $response->getExpires()) {
+ $date = new DrupalDateTime($date);
+ $cache->expire = $date->getTimestamp();
}
if ($cache->data['body']) {
diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
index 163d5c8..0b9bbad 100644
--- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
@@ -47,6 +47,7 @@ class FinishResponseSubscriber implements EventSubscriberInterface {
return;
}
+ $request = $event->getRequest();
$response = $event->getResponse();
// Set the X-UA-Compatible HTTP header to force IE to use the most recent
@@ -60,7 +61,7 @@ class FinishResponseSubscriber implements EventSubscriberInterface {
// since the page is in fact being regenerated right now.
// @todo Remove this and use a more intelligent default so that HTTP
// caching can function properly.
- $response->headers->set('Last-Modified', gmdate(DATE_RFC1123, REQUEST_TIME));
+ $response->setLastModified(new \DateTime(gmdate(DATE_RFC1123, REQUEST_TIME)));
// Also give each page a unique ETag. This will force clients to include
// both an If-Modified-Since header and an If-None-Match header when doing
@@ -82,25 +83,31 @@ class FinishResponseSubscriber implements EventSubscriberInterface {
// identical.
// @todo Remove this line as no longer necessary per
// http://drupal.org/node/1573064
- $response->headers->set('ETag', '"' . REQUEST_TIME . '"');
+ $response->setEtag(REQUEST_TIME);
// Authenticated users are always given a 'no-cache' header, and will fetch
// a fresh page on every request. This prevents authenticated users from
// seeing locally cached pages.
// @todo Revisit whether or not this is still appropriate now that the
- // Response object does its own cache control procesisng and we intend to
+ // Response object does its own cache control processing and we intend to
// use partial page caching more extensively.
// Commit the user session, if needed.
drupal_session_commit();
+
+ // Attach globally-declared headers to the response object so that Symfony
+ // can send them for us correctly.
+ // @todo remove this once we have removed all drupal_add_http_header() calls
+ $headers = drupal_get_http_header();
+ foreach ($headers as $name => $value) {
+ $response->headers->set($name, $value, FALSE);
+ }
+
$max_age = config('system.performance')->get('cache.page.max_age');
- if ($max_age > 0 && ($cache = drupal_page_set_cache($response->getContent()))) {
- drupal_serve_page_from_cache($cache);
- // drupal_serve_page_from_cache() already printed the response.
- $response->setContent('');
- $response->headers->remove('cache-control');
+ if ($max_age > 0 && ($cache = drupal_page_set_cache($response, $request))) {
+ drupal_serve_page_from_cache($cache, $response, $request);
}
else {
- $response->headers->set('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT');
+ $response->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 GMT'));
$response->headers->set('Cache-Control', 'no-cache, must-revalidate, post-check=0, pre-check=0');
}
}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php
index 253c463..ca2d7b3 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php
@@ -92,21 +92,25 @@ class PageCacheTest extends WebTestBase {
$config = config('system.performance');
$config->set('cache.page.use_internal', 1);
$config->set('cache.page.max_age', 300);
+ $config->set('response.gzip', 1);
$config->save();
// Fill the cache.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
- $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary header was sent.');
- $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=300', 'Cache-Control header was sent.');
+ $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'cookie,accept-encoding', 'Vary header was sent.');
+ // Symfony's Response logic determines a specific order for the subvalues
+ // of the Cache-Control header, even if they are explicitly passed in to
+ // the response header bag in a different order.
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'max-age=300, public', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
// Check cache.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
- $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary: Cookie header was sent.');
- $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=300', 'Cache-Control header was sent.');
+ $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'cookie,accept-encoding', 'Vary: Cookie header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'max-age=300, public', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
@@ -114,14 +118,14 @@ class PageCacheTest extends WebTestBase {
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
$this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', 'Default header was replaced.');
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
- $this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', 'Default header was replaced.');
+ $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'user-agent,accept-encoding', 'Default header was replaced.');
// Check that authenticated users bypass the cache.
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.');
- $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
+ $this->assertTrue(strpos(strtolower($this->drupalGetHeader('Vary')), 'cookie') === FALSE, 'Vary: Cookie header was not sent.');
$this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent.');
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
$this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
@@ -148,6 +152,7 @@ class PageCacheTest extends WebTestBase {
$config = config('system.performance');
$config->set('cache.page.use_internal', 1);
$config->set('cache.page.max_age', 300);
+ $config->set('response.gzip', 1);
$config->save();
// Fill the cache and verify that output is compressed.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php b/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php
index d1a601a..f96b13e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php
@@ -18,6 +18,8 @@ class SessionTest extends WebTestBase {
*/
public static $modules = array('session_test');
+ protected $dumpHeaders = TRUE;
+
public static function getInfo() {
return array(
'name' => 'Session tests',
diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
index 2bb96b9..b0e255e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php
@@ -21,6 +21,8 @@ class UpdateScriptTest extends WebTestBase {
*/
public static $modules = array('update_script_test', 'dblog');
+ protected $dumpHeaders = TRUE;
+
private $update_url;
private $update_user;
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index c9f0c22..e1d55f3 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -9,6 +9,7 @@ use Drupal\Core\Language\Language;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\Core\Template\Attribute;
use Drupal\Component\Utility\Crypt;
+use Symfony\Component\HttpFoundation\Response;
/**
* Implements hook_help().
@@ -129,19 +130,23 @@ function _toolbar_initialize_page_cache() {
// @see _drupal_bootstrap_page_cache()
$cache = drupal_page_get_cache();
if (is_object($cache)) {
- header('X-Drupal-Cache: HIT');
+ $response = new Response();
+ $request = \Drupal::request();
+ $response->headers->set('X-Drupal-Cache', 'HIT');
// Restore the metadata cached with the page.
$_GET['q'] = $cache->data['path'];
date_default_timezone_set(drupal_get_user_timezone());
- drupal_serve_page_from_cache($cache);
+ drupal_serve_page_from_cache($cache, $response, $request);
+ $response->prepare($request);
+ $response->send();
// We are done.
exit;
}
// Otherwise, create a new page response (that will be cached).
- header('X-Drupal-Cache: MISS');
+ drupal_add_http_header('X-Drupal-Cache', 'MISS');
// The Expires HTTP header is the heart of the client-side HTTP caching. The
// additional server-side page cache only takes effect when the client
diff --git a/core/update.php b/core/update.php
index fedd2b0..a3dd4bb 100644
--- a/core/update.php
+++ b/core/update.php
@@ -317,6 +317,7 @@ function update_info_page() {
*/
function update_access_denied_page() {
drupal_add_http_header('Status', '403 Forbidden');
+ header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
watchdog('access denied', 'update.php', NULL, WATCHDOG_WARNING);
drupal_set_title('Access denied');
return '<p>Access denied. You are not authorized to access this page. Log in using either an account with the <em>administer software updates</em> permission or the site maintenance account (the account you created during installation). If you cannot log in, you will have to edit <code>settings.php</code> to bypass this access check. To do this:</p>