diff --git a/core/core.services.yml b/core/core.services.yml index 165b0bae8f3c..4006ceb829e5 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -581,7 +581,7 @@ services: class: Drupal\Core\EventSubscriber\FinishResponseSubscriber tags: - { name: event_subscriber } - arguments: ['@language_manager'] + arguments: ['@language_manager', '@config.factory'] scope: request redirect_response_subscriber: class: Drupal\Core\EventSubscriber\RedirectResponseSubscriber diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 9ca9dcdc86c0..d33d1ee55964 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -650,26 +650,27 @@ function drupal_page_cache_get_cid(Request $request) { /** * Retrieves the current page from the cache. * - * Note: we do not serve cached pages to authenticated users, or to anonymous - * users when $_SESSION is non-empty. $_SESSION may contain status messages - * from a form submission, the contents of a shopping cart, or other user- - * specific content that should not be cached and displayed to other users. - * * @param \Symfony\Component\HttpFoundation\Request $request * The request for this page. * - * @return - * The cache object, if the page was found in the cache, NULL otherwise. + * @return \Symfony\Component\HttpFoundation\Response + * The response, if the page was found in the cache, NULL otherwise. */ function drupal_page_get_cache(Request $request) { - if (drupal_page_is_cacheable()) { - return \Drupal::cache('render')->get(drupal_page_cache_get_cid($request)); + $cache = \Drupal::cache('render')->get(drupal_page_cache_get_cid($request)); + if ($cache) { + return $cache->data; } } /** * Determines the cacheability of the current page. * + * Note: we do not serve cached pages to authenticated users, or to anonymous + * users when $_SESSION is non-empty. $_SESSION may contain status messages + * from a form submission, the contents of a shopping cart, or other user- + * specific content that should not be cached and displayed to other users. + * * @param $allow_caching * Set to FALSE if you want to prevent this page to get cached. * @@ -818,7 +819,7 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE) * fresh page on every request. This prevents authenticated users from seeing * locally cached pages. * - * Also give each page a unique ETag. This will force clients to include both + * Also give each page a unique ETag. This should force clients to include both * an If-Modified-Since header and an If-None-Match header when doing * conditional requests for the page (required by RFC 2616, section 13.3.4), * making the validation more robust. This is a workaround for a bug in Mozilla @@ -868,93 +869,59 @@ 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, Response $response, Request $request) { - $config = \Drupal::config('system.performance'); +function drupal_serve_page_from_cache(Response $response, Request $request) { + // Only allow caching in the browser and prevent that the response is stored + // by an external proxy server when the following conditions apply: + // 1. There is a session cookie on the request. + // 2. The Vary: Cookie header is on the response. + // 3. The Cache-Control header does not contain the no-cache directive. + if ($request->cookies->has(session_name()) && + in_array('Cookie', $response->getVary()) && + !$response->headers->hasCacheControlDirective('no-cache')) { - // First half: we must determine if we should be returning a 304. + $response->setPrivate(); + } // Negotiate whether to use compression. - $page_compression = !empty($cache->data['page_compressed']) && extension_loaded('zlib'); - $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(); - - 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])) { - $response->headers->set($name, $value); - unset($cache->data['headers'][$name]); - } - } - - // If the client sent a session cookie, a cached copy will only be served - // 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 = !$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. - $response->setEtag($cache->created); - - // See if the client has provided the required HTTP headers. - $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 == $response->headers->get('etag') // etag must match - && $if_modified_since == $cache->created) { // if-modified-since must match - $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); - } - - $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). - 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 - // fields that fully determines whether a cache is permitted to use the - // response to reply to a subsequent request for a given URL without - // revalidation. - if (!isset($boot_headers['vary']) && !Settings::get('omit_vary_cookie')) { - $response->setVary('Cookie', FALSE); - } - - if ($page_compression) { - $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 + if ($response->headers->get('Content-Encoding') == 'gzip' && extension_loaded('zlib')) { + if (strpos($request->headers->get('Accept-Encoding'), 'gzip') !== FALSE) { + // The response content is already gzip'ed, so make sure // zlib.output_compression does not compress it once more. ini_set('zlib.output_compression', '0'); - $response->headers->set('content-encoding', 'gzip'); } else { - // The client does not support compression, so unzip the data in the - // cache. Strip the gzip header and run uncompress. - $cache->data['body'] = gzinflate(substr(substr($cache->data['body'], 10), 0, -8)); + // The client does not support compression. Decompress the content and + // remove the Content-Encoding header. + $content = $response->getContent(); + $content = gzinflate(substr(substr($content, 10), 0, -8)); + $response->setContent($content); + $response->headers->remove('Content-Encoding'); } } - $response->setContent($cache->data['body']); + // Perform HTTP revalidation. + // @todo Use Response::isNotModified() as per https://drupal.org/node/2259489 + $last_modified = $response->getLastModified(); + if ($last_modified) { + // See if the client has provided the required HTTP headers. + $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 == $response->getEtag() // etag must match + && $if_modified_since == $last_modified->getTimestamp()) { // if-modified-since must match + $response->setStatusCode(304); + $response->setContent(NULL); + + // In the case of a 304 response, certain headers must be sent, and the + // remaining may not (see RFC 2616, section 10.3.5). + foreach (array_keys($response->headers->all()) as $name) { + if (!in_array($name, array('content-location', 'expires', 'cache-control', 'vary'))) { + $response->headers->remove($name); + } + } + } + } } /** @@ -1617,8 +1584,6 @@ function _drupal_bootstrap_kernel() { * Attempts to serve a page from the cache. */ function _drupal_bootstrap_page_cache() { - global $user; - require_once __DIR__ . '/database.inc'; // Check for a cache mode force from settings.php. if (Settings::get('page_cache_without_database')) { @@ -1629,30 +1594,23 @@ function _drupal_bootstrap_page_cache() { $cache_enabled = $config->get('cache.page.use_internal'); } - $request = Request::createFromGlobals(); + $request = \Drupal::request(); // If there is no session cookie and cache is enabled (or forced), try // to serve a cached page. - if (!$request->cookies->has(session_name()) && $cache_enabled) { - // Make sure there is a user object because its timestamp will be checked. - $user = new AnonymousUserSession(); + if (!$request->cookies->has(session_name()) && $cache_enabled && drupal_page_is_cacheable()) { // Get the page from the cache. - $cache = drupal_page_get_cache($request); + $response = drupal_page_get_cache($request); // If there is a cached page, display it. - if (is_object($cache)) { - $response = new Response(); + if ($response) { $response->headers->set('X-Drupal-Cache', 'HIT'); - date_default_timezone_set(drupal_get_user_timezone()); - drupal_serve_page_from_cache($cache, $response, $request); + drupal_serve_page_from_cache($response, $request); // We are done. $response->prepare($request); $response->send(); exit; } - else { - drupal_add_http_header('X-Drupal-Cache', 'MISS'); - } } } diff --git a/core/includes/common.inc b/core/includes/common.inc index 6f866b2593ec..8e0d53ef5021 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -2750,55 +2750,37 @@ function _drupal_bootstrap_full($skip = FALSE) { * Page compression requires the PHP zlib extension * (http://php.net/manual/ref.zlib.php). * - * @param $body - * The response body. - * @return - * The cached object or NULL if the page cache was not set. + * @param \Symfony\Component\HttpFoundation\Response $response + * The fully populated response. + * @param \Symfony\Component\HttpFoundation\Request $request + * The request for this page. * * @see drupal_page_header() */ function drupal_page_set_cache(Response $response, Request $request) { - if (drupal_page_is_cacheable()) { + // Check if the current page may be compressed. + if (\Drupal::config('system.performance')->get('response.gzip') && + !$response->headers->get('Content-Encoding') && extension_loaded('zlib')) { - // Check if the current page may be compressed. - $page_compressed = \Drupal::config('system.performance')->get('response.gzip') && extension_loaded('zlib'); - - $cache = (object) array( - 'cid' => drupal_page_cache_get_cid($request), - 'data' => array( - 'body' => $response->getContent(), - 'headers' => array(), - // We need to store whether page was compressed or not, - // because by the time it is read, the configuration might change. - 'page_compressed' => $page_compressed, - ), - 'tags' => HtmlViewSubscriber::convertHeaderToCacheTags($response->headers->get('X-Drupal-Cache-Tags')), - 'expire' => Cache::PERMANENT, - 'created' => REQUEST_TIME, - ); - - $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']); + $content = $response->getContent(); + if ($content) { + $response->setContent(gzencode($content, 9, FORCE_GZIP)); + $response->headers->set('Content-Encoding', 'gzip'); } - // Use the actual timestamp from an Expires header, if available. - if ($date = $response->getExpires()) { - $date = DrupalDateTime::createFromDateTime($date); - $cache->expire = $date->getTimestamp(); - } - - if ($cache->data['body']) { - if ($page_compressed) { - $cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP); - } - \Drupal::cache('render')->set($cache->cid, $cache->data, $cache->expire, $cache->tags); - } - return $cache; + // When page compression is enabled, ensure that proxy caches will record + // and deliver different versions of a page depending on whether the + // client supports gzip or not. + $response->setVary('Accept-Encoding', FALSE); } + + // Use the actual timestamp from an Expires header, if available. + $date = $response->getExpires(); + $expire = ($date > (new DateTime())) ? $date->getTimestamp() : Cache::PERMANENT; + + $cid = drupal_page_cache_get_cid($request); + $tags = HtmlViewSubscriber::convertHeaderToCacheTags($response->headers->get('X-Drupal-Cache-Tags')); + \Drupal::cache('render')->set($cid, $response, $expire, $tags); } /** diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index 8e4596f12795..9c7f56cc37ce 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -7,8 +7,13 @@ namespace Drupal\Core\EventSubscriber; +use Drupal\Core\Config\Config; +use Drupal\Core\Config\ConfigFactory; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManager; +use Drupal\Core\Site\Settings; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -26,14 +31,24 @@ class FinishResponseSubscriber implements EventSubscriberInterface { */ protected $languageManager; + /** + * A config object for the system performance configuration. + * + * @var \Drupal\Core\Config\Config + */ + protected $config; + /** * Constructs a FinishResponseSubscriber object. * * @param LanguageManager $language_manager * The LanguageManager object for retrieving the correct language code. + * @param \Drupal\Core\Config\ConfigFactory $config_factory + * A config factory for retrieving required config objects. */ - public function __construct(LanguageManager $language_manager) { + public function __construct(LanguageManager $language_manager, ConfigFactory $config_factory) { $this->languageManager = $language_manager; + $this->config = $config_factory->get('system.performance'); } /** @@ -57,57 +72,148 @@ class FinishResponseSubscriber implements EventSubscriberInterface { // Set the Content-language header. $response->headers->set('Content-language', $this->languageManager->getCurrentLanguage()->id); - // Because pages are highly dynamic, set the last-modified time to now - // 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->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 - // conditional requests for the page (required by RFC 2616, section 13.3.4), - // making the validation more robust. This is a workaround for a bug in - // Mozilla Firefox that is triggered when Drupal's caching is enabled and - // the user accesses Drupal via an HTTP proxy (see - // https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an - // authenticated user requests a page, and then logs out and requests the - // same page again, Firefox may send a conditional request based on the - // page that was cached locally when the user was logged in. If this page - // did not have an ETag header, the request only contains an - // If-Modified-Since header. The date will be recent, because with - // authenticated users the Last-Modified header always refers to the time - // of the request. If the user accesses Drupal via a proxy server, and the - // proxy already has a cached copy of the anonymous page with an older - // Last-Modified date, the proxy may respond with 304 Not Modified, making - // the client think that the anonymous and authenticated pageviews are - // identical. - // @todo Remove this line as no longer necessary per - // http://drupal.org/node/1573064 - $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 processing and we intend to - // use partial page caching more extensively. - // 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 + // @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 = \Drupal::config('system.performance')->get('cache.page.max_age'); - if ($max_age > 0 && ($cache = drupal_page_set_cache($response, $request))) { - drupal_serve_page_from_cache($cache, $response, $request); + $is_cacheable = drupal_page_is_cacheable(); + + // Add headers necessary to specify whether the response should be cached by + // proxies and/or the browser. + if ($is_cacheable && $this->config->get('cache.page.max_age') > 0) { + if (!$this->isCacheControlCustomized($response)) { + $this->setResponseCacheable($response, $request); + } } else { - $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'); + $this->setResponseNotCacheable($response, $request); } + + // Store the response in the internal page cache. + if ($is_cacheable && $this->config->get('cache.page.use_internal')) { + drupal_page_set_cache($response, $request); + $response->headers->set('X-Drupal-Cache', 'MISS'); + drupal_serve_page_from_cache($response, $request); + } + } + + /** + * Determine whether the given response has a custom Cache-Control header. + * + * Upon construction, the ResponseHeaderBag is initialized with an empty + * Cache-Control header. Consequently it is not possible to check whether the + * header was set explicitly by simply checking its presence. Instead, it is + * necessary to examine the computed Cache-Control header and compare with + * values known to be present only when Cache-Control was never set + * explicitly. + * + * When neither Cache-Control nor any of the ETag, Last-Modified, Expires + * headers are set on the response, ::get('Cache-Control') returns the value + * 'no-cache'. If any of ETag, Last-Modified or Expires are set but not + * Cache-Control, then 'private, must-revalidate' (in exactly this order) is + * returned. + * + * @see \Symfony\Component\HttpFoundation\ResponseHeaderBag::computeCacheControlValue() + * + * @param \Symfony\Component\HttpFoundation\Response $response + * + * @return bool + * TRUE when Cache-Control header was set explicitely on the given response. + */ + protected function isCacheControlCustomized(Response $response) { + $cache_control = $response->headers->get('Cache-Control'); + return $cache_control != 'no-cache' && $cache_control != 'private, must-revalidate'; + } + + /** + * Add Cache-Control and Expires headers to a response which is not cacheable. + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + * @param \Symfony\Component\HttpFoundation\Request $request + * A request object. + */ + protected function setResponseNotCacheable(Response $response, Request $request) { + $this->setCacheControlNoCache($response); + $this->setExpiresNoCache($response); + + // There is no point in sending along headers necessary for cache + // revalidation, if caching by proxies and browsers is denied in the first + // place. Therefore remove ETag, Last-Modified and Vary in that case. + $response->setEtag(NULL); + $response->setLastModified(NULL); + $response->setVary(NULL); + } + + /** + * Add Cache-Control and Expires headers to a cacheable response. + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + * @param \Symfony\Component\HttpFoundation\Request $request + * A request object. + */ + protected function setResponseCacheable(Response $response, Request $request) { + // HTTP/1.0 proxies do not support the Vary header, so prevent any caching + // by sending an Expires date in the past. HTTP/1.1 clients ignore the + // Expires header if a Cache-Control: max-age directive is specified (see + // RFC 2616, section 14.9.3). + if (!$response->headers->has('Expires')) { + $this->setExpiresNoCache($response); + } + + $max_age = $this->config->get('cache.page.max_age'); + $response->headers->set('Cache-Control', 'public, max-age=' . $max_age); + + // In order to support HTTP cache-revalidation, ensure that there is a + // Last-Modified and an ETag header on the response. + if (!$response->headers->has('Last-Modified')) { + $timestamp = REQUEST_TIME; + $response->setLastModified(new \DateTime(gmdate(DATE_RFC1123, REQUEST_TIME))); + } + else { + $timestamp = $response->getLastModified()->getTimestamp(); + } + $response->setEtag($timestamp); + + // 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 + // fields that fully determines whether a cache is permitted to use the + // response to reply to a subsequent request for a given URL without + // revalidation. + if (!$response->hasVary() && !Settings::get('omit_vary_cookie')) { + $response->setVary('Cookie', FALSE); + } + } + + /** + * Disable caching in the browser and for HTTP/1.1 proxies and clients. + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + */ + protected function setCacheControlNoCache(Response $response) { + $response->headers->set('Cache-Control', 'no-cache, must-revalidate, post-check=0, pre-check=0'); + } + + /** + * Disable caching in ancient browsers and for HTTP/1.0 proxies and clients. + * + * HTTP/1.0 proxies do not support the Vary header, so prevent any caching by + * sending an Expires date in the past. HTTP/1.1 clients ignore the Expires + * header if a Cache-Control: max-age= directive is specified (see RFC 2616, + * section 14.9.3). + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + */ + protected function setExpiresNoCache(Response $response) { + $response->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 GMT')); } /** @@ -116,7 +222,7 @@ class FinishResponseSubscriber implements EventSubscriberInterface { * @return array * An array of event listener definitions. */ - static function getSubscribedEvents() { + public static function getSubscribedEvents() { $events[KernelEvents::RESPONSE][] = array('onRespond'); return $events; } 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 7f6b6a0fe084..fdf16b90b6e0 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php @@ -196,15 +196,31 @@ class PageCacheTest extends WebTestBase { $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.'); + } + + /** + * Tests the omit_vary_cookie setting. + */ + public function testPageCacheWithoutVaryCookie() { + $config = \Drupal::config('system.performance'); + $config->set('cache.page.use_internal', 1); + $config->set('cache.page.max_age', 300); + $config->save(); - // Check the omit_vary_cookie setting. - $this->drupalLogout(); $settings['settings']['omit_vary_cookie'] = (object) array( 'value' => TRUE, 'required' => TRUE, ); $this->writeSettings($settings); - $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); + + // Fill the cache. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.'); + $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.'); + + // Check cache. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.'); $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.'); } diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index 2a76441b93eb..4d810d6b126b 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -121,13 +121,11 @@ function _toolbar_initialize_page_cache() { // If we have a cache, serve it. // @see _drupal_bootstrap_page_cache() $request = \Drupal::request(); - $cache = drupal_page_get_cache($request); - if (is_object($cache)) { - $response = new Response(); + $response = drupal_page_get_cache($request); + if ($response) { $response->headers->set('X-Drupal-Cache', 'HIT'); - date_default_timezone_set(drupal_get_user_timezone()); - drupal_serve_page_from_cache($cache, $response, $request); + drupal_serve_page_from_cache($response, $request); $response->prepare($request); $response->send(); @@ -135,9 +133,6 @@ function _toolbar_initialize_page_cache() { exit; } - // Otherwise, create a new page response (that will be cached). - 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 // accesses the callback URL again (e.g., after clearing the browser cache or