Commit 7eaa1b76 authored by alexpott's avatar alexpott

Issue #2445761 by Wim Leers: Add a X-Drupal-Cache-Contexts header to aid in debugging and testing

parent 28e35f65
...@@ -129,21 +129,25 @@ public function renderResponse(array $main_content, Request $request, RouteMatch ...@@ -129,21 +129,25 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
} }
$content = $this->renderer->render($html); $content = $this->renderer->render($html);
// Store the cache tags associated with this page in a X-Drupal-Cache-Tags // Expose the cache contexts and cache tags associated with this page in a
// header. Also associate the "rendered" cache tag. This allows us to // X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags header respectively. Also
// invalidate the entire render cache, regardless of the cache bin. // associate the "rendered" cache tag. This allows us to invalidate the
$cache_tags = Cache::mergeTags( // entire render cache, regardless of the cache bin.
isset($html['page_top']) ? $html['page_top']['#cache']['tags'] : [], $cache_contexts = [];
$html['page']['#cache']['tags'], $cache_tags = ['rendered'];
isset($html['page_bottom']) ? $html['page_bottom']['#cache']['tags'] : [], foreach (['page_top', 'page', 'page_bottom'] as $region) {
['rendered'] if (isset($html[$region])) {
); $cache_contexts = Cache::mergeContexts($cache_contexts, $html[$region]['#cache']['contexts']);
$cache_tags = Cache::mergeTags($cache_tags, $html[$region]['#cache']['tags']);
}
}
// Set the generator in the HTTP header. // Set the generator in the HTTP header.
list($version) = explode('.', \Drupal::VERSION, 2); list($version) = explode('.', \Drupal::VERSION, 2);
return new Response($content, 200,[ return new Response($content, 200,[
'X-Drupal-Cache-Tags' => implode(' ', $cache_tags), 'X-Drupal-Cache-Tags' => implode(' ', $cache_tags),
'X-Drupal-Cache-Contexts' => implode(' ', $cache_contexts),
'X-Generator' => 'Drupal ' . $version . ' (http://drupal.org)' 'X-Generator' => 'Drupal ' . $version . ' (http://drupal.org)'
]); ]);
} }
......
...@@ -70,8 +70,19 @@ function testPageCacheTags() { ...@@ -70,8 +70,19 @@ function testPageCacheTags() {
), ),
)); ));
$cache_contexts = [
'language',
'menu.active_trail:account',
'menu.active_trail:footer',
'menu.active_trail:main',
'menu.active_trail:tools',
'theme',
'timezone',
'user.roles',
];
// Full node page 1. // Full node page 1.
$this->verifyPageCacheTags($node_1->urlInfo(), array( $this->assertPageCacheContextsAndTags($node_1->urlInfo(), $cache_contexts, array(
'rendered', 'rendered',
'block_view', 'block_view',
'config:block_list', 'config:block_list',
...@@ -103,7 +114,7 @@ function testPageCacheTags() { ...@@ -103,7 +114,7 @@ function testPageCacheTags() {
)); ));
// Full node page 2. // Full node page 2.
$this->verifyPageCacheTags($node_2->urlInfo(), array( $this->assertPageCacheContextsAndTags($node_2->urlInfo(), $cache_contexts, array(
'rendered', 'rendered',
'block_view', 'block_view',
'config:block_list', 'config:block_list',
...@@ -138,27 +149,37 @@ function testPageCacheTags() { ...@@ -138,27 +149,37 @@ function testPageCacheTags() {
} }
/** /**
* Fills page cache for the given path, verify cache tags on page cache hit. * Asserts page cache miss, then hit for the given URL; checks cache headers.
* *
* @param \Drupal\Core\Url $url * @param \Drupal\Core\Url $url
* The url * The URL to test.
* @param $expected_tags * @param string[] $expected_contexts
* The expected cache tags for the page cache entry of the given $path. * The expected cache contexts for the given URL.
* @param string[] $expected_tags
* The expected cache tags for the given URL.
*/ */
protected function verifyPageCacheTags(Url $url, $expected_tags) { protected function assertPageCacheContextsAndTags(Url $url, array $expected_contexts, array $expected_tags) {
// @todo Change ->drupalGet() calls to just pass $url when $absolute_url = $url->setAbsolute()->toString();
// https://www.drupal.org/node/2350837 gets committed sort($expected_contexts);
sort($expected_tags); sort($expected_tags);
$this->drupalGet($url->setAbsolute()->toString());
// Assert cache miss + expected cache contexts + tags.
$this->drupalGet($absolute_url);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
$actual_contexts = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Contexts'));
$actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); $actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
sort($actual_tags); $this->assertIdentical($actual_contexts, $expected_contexts);
$this->assertIdentical($actual_tags, $expected_tags); $this->assertIdentical($actual_tags, $expected_tags);
$this->drupalGet($url->setAbsolute()->toString());
// Assert cache hit + expected cache contexts + tags.
$this->drupalGet($absolute_url);
$actual_contexts = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Contexts'));
$actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); $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_contexts, $expected_contexts);
$this->assertIdentical($actual_tags, $expected_tags); $this->assertIdentical($actual_tags, $expected_tags);
// Assert page cache item + expected cache tags.
$cid_parts = array($url->setAbsolute()->toString(), 'html'); $cid_parts = array($url->setAbsolute()->toString(), 'html');
$cid = implode(':', $cid_parts); $cid = implode(':', $cid_parts);
$cache_entry = \Drupal::cache('render')->get($cid); $cache_entry = \Drupal::cache('render')->get($cid);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment