Issue #2217371 by Wim Leers: Expose cache tags to reverse proxies: X-Drupal-Cache-Tags header (by just slightly changing an existing header).

8.0.x
catch 2014-03-25 18:53:54 +01:00
parent 4a1ff2d485
commit 0b6e47e23d
6 changed files with 77 additions and 22 deletions

View File

@ -18,6 +18,7 @@ use Symfony\Component\Yaml\Exception\ParseException;
use Drupal\Component\PhpStorage\PhpStorageFactory;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\EventSubscriber\HtmlViewSubscriber;
use Drupal\Core\Routing\GeneratorNotInitializedException;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Render\Element;
@ -3169,7 +3170,7 @@ function drupal_page_set_cache(Response $response, Request $request) {
// because by the time it is read, the configuration might change.
'page_compressed' => $page_compressed,
),
'tags' => array('content' => TRUE) + drupal_cache_tags_page_get($response),
'tags' => HtmlViewSubscriber::convertHeaderToCacheTags($response->headers->get('X-Drupal-Cache-Tags')),
'expire' => Cache::PERMANENT,
'created' => REQUEST_TIME,
);
@ -4417,23 +4418,6 @@ function drupal_render_collect_cache_tags($element, $tags = array()) {
return $tags;
}
/**
* Return the cache tags that were stored during drupal_render_page().
*
* @param \Symfony\Component\HttpFoundation\Response $response
* The response object.
* @return array
* An array of cache tags.
*
* @see \Drupal\Core\EventSubscriber\HtmlViewSubscriber::onHtmlPage()
*/
function drupal_cache_tags_page_get(Response $response) {
if (($tags = $response->headers->get('cache_tags')) && $tags = unserialize($tags)) {
return $tags;
}
return array();
}
/**
* Creates the cache ID for a renderable element.
*

View File

@ -77,7 +77,7 @@ class HtmlViewSubscriber implements EventSubscriberInterface {
// recommended.
$response = new Response((string) $this->pageRenderer->render($page), $page->getStatusCode());
if ($tags = $page->getCacheTags()) {
$response->headers->set('cache_tags', serialize($tags));
$response->headers->set('X-Drupal-Cache-Tags', static::convertCacheTagsToHeader($tags));
}
if ($keys = $page->getCacheKeys()) {
$response->headers->set('cache_keys', serialize($keys));
@ -105,4 +105,60 @@ class HtmlViewSubscriber implements EventSubscriberInterface {
return $events;
}
/**
* Converts a cache tags array into a X-Drupal-Cache-Tags header value.
*
* @param array $tags
* Associative array of cache tags to flatten.
*
* @return string
* A space-separated list of flattened cache tag identifiers.
*/
public static function convertCacheTagsToHeader(array $tags) {
$flat_tags = array();
foreach ($tags as $namespace => $values) {
if (is_array($values)) {
foreach ($values as $value) {
$flat_tags[] = "$namespace:$value";
}
}
else {
$flat_tags[] = "$namespace:$values";
}
}
return implode(' ', $flat_tags);
}
/**
* Converts a X-Drupal-Cache-Tags header value into a cache tags array.
*
* @param string $tags_header
* A space-separated list of flattened cache tag identifiers.
*
* @return array
* Associative array of cache tags to flatten.
*/
public static function convertHeaderToCacheTags($tags_header) {
if (!is_string($tags_header) || strlen(trim($tags_header)) == 0) {
return array();
}
$flat_tags = explode(' ', trim($tags_header));
$tags = array();
foreach ($flat_tags as $flat_tag) {
list($namespace, $value) = explode(':', $flat_tag);
if (!isset($tags[$namespace])) {
$tags[$namespace] = $value;
}
// Multiple values in this namespace.
else {
if (!is_array($tags[$namespace])) {
$tags[$namespace] = array($tags[$namespace]);
}
$tags[$namespace][] = $value;
}
}
return $tags;
}
}

View File

@ -55,7 +55,11 @@ class DefaultHtmlFragmentRenderer implements HtmlFragmentRendererInterface {
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
$page->setContent(drupal_render($page_array));
// Collect cache tags for all the content in all the regions on the page.
$page->setCacheTags($page_array['#cache']['tags']);
$tags = $page_array['#cache']['tags'];
// Enforce the generic "content" cache tag on all pages.
// @todo Remove the "content" cache tag. @see https://drupal.org/node/2124957
$tags['content'] = TRUE;
$page->setCacheTags($tags);
$page->setStatusCode($status_code);
return $page;

View File

@ -65,7 +65,8 @@ class PageCacheTest extends WebTestBase {
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
$cid = sha1(implode(':', $cid_parts));
$cache_entry = \Drupal::cache('page')->get($cid);
$this->assertIdentical($cache_entry->tags, array('content:1', 'system_test_cache_tags_page:1', 'pre_render:1'));
sort($cache_entry->tags);
$this->assertIdentical($cache_entry->tags, array('content:1', 'pre_render:1', 'system_test_cache_tags_page:1'));
Cache::invalidateTags($tags);
$this->drupalGet($path);

View File

@ -132,14 +132,22 @@ class PageCacheTagsIntegrationTest extends WebTestBase {
* The expected cache tags for the page cache entry of the given $path.
*/
protected function verifyPageCacheTags($path, $expected_tags) {
sort($expected_tags);
$this->drupalGet($path);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
$actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
sort($actual_tags);
$this->assertIdentical($actual_tags, $expected_tags);
$this->drupalGet($path);
$actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
sort($actual_tags);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
$this->assertIdentical($actual_tags, $expected_tags);
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
$cid = sha1(implode(':', $cid_parts));
$cache_entry = \Drupal::cache('page')->get($cid);
$this->assertIdentical($cache_entry->tags, $expected_tags);
sort($cache_entry->tags);
$this->assertEqual($cache_entry->tags, $expected_tags);
}
}

View File

@ -49,6 +49,8 @@ abstract class PageCacheTagsTestBase extends WebTestBase {
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
$cid = sha1(implode(':', $cid_parts));
$cache_entry = \Drupal::cache('page')->get($cid);
sort($cache_entry->tags);
sort($tags);
$this->assertIdentical($cache_entry->tags, $tags);
}
}