Issue #2217371 by Wim Leers: Expose cache tags to reverse proxies: X-Drupal-Cache-Tags header (by just slightly changing an existing header).
parent
4a1ff2d485
commit
0b6e47e23d
|
@ -18,6 +18,7 @@ use Symfony\Component\Yaml\Exception\ParseException;
|
||||||
use Drupal\Component\PhpStorage\PhpStorageFactory;
|
use Drupal\Component\PhpStorage\PhpStorageFactory;
|
||||||
use Drupal\Component\Utility\NestedArray;
|
use Drupal\Component\Utility\NestedArray;
|
||||||
use Drupal\Core\Datetime\DrupalDateTime;
|
use Drupal\Core\Datetime\DrupalDateTime;
|
||||||
|
use Drupal\Core\EventSubscriber\HtmlViewSubscriber;
|
||||||
use Drupal\Core\Routing\GeneratorNotInitializedException;
|
use Drupal\Core\Routing\GeneratorNotInitializedException;
|
||||||
use Drupal\Core\Template\Attribute;
|
use Drupal\Core\Template\Attribute;
|
||||||
use Drupal\Core\Render\Element;
|
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.
|
// because by the time it is read, the configuration might change.
|
||||||
'page_compressed' => $page_compressed,
|
'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,
|
'expire' => Cache::PERMANENT,
|
||||||
'created' => REQUEST_TIME,
|
'created' => REQUEST_TIME,
|
||||||
);
|
);
|
||||||
|
@ -4417,23 +4418,6 @@ function drupal_render_collect_cache_tags($element, $tags = array()) {
|
||||||
return $tags;
|
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.
|
* Creates the cache ID for a renderable element.
|
||||||
*
|
*
|
||||||
|
|
|
@ -77,7 +77,7 @@ class HtmlViewSubscriber implements EventSubscriberInterface {
|
||||||
// recommended.
|
// recommended.
|
||||||
$response = new Response((string) $this->pageRenderer->render($page), $page->getStatusCode());
|
$response = new Response((string) $this->pageRenderer->render($page), $page->getStatusCode());
|
||||||
if ($tags = $page->getCacheTags()) {
|
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()) {
|
if ($keys = $page->getCacheKeys()) {
|
||||||
$response->headers->set('cache_keys', serialize($keys));
|
$response->headers->set('cache_keys', serialize($keys));
|
||||||
|
@ -105,4 +105,60 @@ class HtmlViewSubscriber implements EventSubscriberInterface {
|
||||||
return $events;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,11 @@ class DefaultHtmlFragmentRenderer implements HtmlFragmentRendererInterface {
|
||||||
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
|
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
|
||||||
$page->setContent(drupal_render($page_array));
|
$page->setContent(drupal_render($page_array));
|
||||||
// Collect cache tags for all the content in all the regions on the page.
|
// 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);
|
$page->setStatusCode($status_code);
|
||||||
|
|
||||||
return $page;
|
return $page;
|
||||||
|
|
|
@ -65,7 +65,8 @@ class PageCacheTest extends WebTestBase {
|
||||||
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
|
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
|
||||||
$cid = sha1(implode(':', $cid_parts));
|
$cid = sha1(implode(':', $cid_parts));
|
||||||
$cache_entry = \Drupal::cache('page')->get($cid);
|
$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);
|
Cache::invalidateTags($tags);
|
||||||
$this->drupalGet($path);
|
$this->drupalGet($path);
|
||||||
|
|
|
@ -132,14 +132,22 @@ class PageCacheTagsIntegrationTest extends WebTestBase {
|
||||||
* The expected cache tags for the page cache entry of the given $path.
|
* The expected cache tags for the page cache entry of the given $path.
|
||||||
*/
|
*/
|
||||||
protected function verifyPageCacheTags($path, $expected_tags) {
|
protected function verifyPageCacheTags($path, $expected_tags) {
|
||||||
|
sort($expected_tags);
|
||||||
$this->drupalGet($path);
|
$this->drupalGet($path);
|
||||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
$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);
|
$this->drupalGet($path);
|
||||||
|
$actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
|
||||||
|
sort($actual_tags);
|
||||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||||
|
$this->assertIdentical($actual_tags, $expected_tags);
|
||||||
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
|
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
|
||||||
$cid = sha1(implode(':', $cid_parts));
|
$cid = sha1(implode(':', $cid_parts));
|
||||||
$cache_entry = \Drupal::cache('page')->get($cid);
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,8 @@ abstract class PageCacheTagsTestBase extends WebTestBase {
|
||||||
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
|
$cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
|
||||||
$cid = sha1(implode(':', $cid_parts));
|
$cid = sha1(implode(':', $cid_parts));
|
||||||
$cache_entry = \Drupal::cache('page')->get($cid);
|
$cache_entry = \Drupal::cache('page')->get($cid);
|
||||||
|
sort($cache_entry->tags);
|
||||||
|
sort($tags);
|
||||||
$this->assertIdentical($cache_entry->tags, $tags);
|
$this->assertIdentical($cache_entry->tags, $tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue