diff --git a/core/lib/Drupal/Core/StackMiddleware/PageCache.php b/core/lib/Drupal/Core/StackMiddleware/PageCache.php index d4ec291..f8a6aa4 100644 --- a/core/lib/Drupal/Core/StackMiddleware/PageCache.php +++ b/core/lib/Drupal/Core/StackMiddleware/PageCache.php @@ -323,6 +323,12 @@ protected function getCacheId(Request $request) { $request->getUri(), $request->getRequestFormat(), ); + $header_contexts = explode(' ', $response->headers->get('X-Drupal-Cache-Contexts')); + if (!empty($header_contexts)) { + $contexts = $this->cacheContexts->convertTokensToKeys($header_contexts); + $cid_parts = array_merge($cid_parts, $contexts); + } + return implode(':', $cid_parts); } diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php index 9acd295..af36c75 100644 --- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php +++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php @@ -17,7 +17,6 @@ * @Plugin( * id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser::METHOD_ID, * weight = -2, - * cache = 0, * name = @Translation("Browser"), * description = @Translation("Language from the browser's language settings."), * config_route_name = "language.negotiation_browser" @@ -36,6 +35,11 @@ class LanguageNegotiationBrowser extends LanguageNegotiationMethodBase { public function getLangcode(Request $request = NULL) { $langcode = NULL; + // Whenever browser-based language negotiation is used, the page cannot be + // cached by reverse proxies. + // @todo Solve more elegantly in https://www.drupal.org/node/2430335. + \Drupal::service('page_cache_kill_switch')->trigger(); + if ($this->languageManager && $request && $request->server->get('HTTP_ACCEPT_LANGUAGE')) { $http_accept_language = $request->server->get('HTTP_ACCEPT_LANGUAGE'); $langcodes = array_keys($this->languageManager->getLanguages()); diff --git a/core/modules/system/config/install/system.performance.yml b/core/modules/system/config/install/system.performance.yml index 1e75b4b..98f34f0 100644 --- a/core/modules/system/config/install/system.performance.yml +++ b/core/modules/system/config/install/system.performance.yml @@ -1,6 +1,6 @@ cache: page: - use_internal: false + use_internal: true max_age: 0 css: preprocess: true diff --git a/core/modules/system/src/Tests/Session/SessionTest.php b/core/modules/system/src/Tests/Session/SessionTest.php index 0921f66..75b194a 100644 --- a/core/modules/system/src/Tests/Session/SessionTest.php +++ b/core/modules/system/src/Tests/Session/SessionTest.php @@ -141,7 +141,11 @@ function testDataPersistence() { * Test that empty anonymous sessions are destroyed. */ function testEmptyAnonymousSession() { - // Verify that no session is automatically created for anonymous user. + // Verify that no session is automatically created for anonymous user when + // page caching is disabled. + $config = $this->config('system.performance'); + $config->set('cache.page.use_internal', 0); + $config->save(); $this->drupalGet(''); $this->assertSessionCookie(FALSE); $this->assertSessionEmpty(TRUE); diff --git a/core/modules/system/src/Tests/System/CronRunTest.php b/core/modules/system/src/Tests/System/CronRunTest.php index de8cdcc..e17bbf3 100644 --- a/core/modules/system/src/Tests/System/CronRunTest.php +++ b/core/modules/system/src/Tests/System/CronRunTest.php @@ -49,6 +49,12 @@ function testCronRun() { * need the exact time when cron is triggered. */ function testAutomaticCron() { + // Test with a logged in user; anonymous users likely don't cause Drupal to + // fully bootstrap, because of the internal page cache or an external + // reverse proxy. Reuse this user for disabling cron later in the test. + $admin_user = $this->drupalCreateUser(array('administer site configuration')); + $this->drupalLogin($admin_user); + // Ensure cron does not run when the cron threshold is enabled and was // not passed. $cron_last = time(); @@ -68,8 +74,6 @@ function testAutomaticCron() { $this->assertTrue($cron_last < \Drupal::state()->get('system.cron_last'), 'Cron runs when the cron threshold is passed.'); // Disable the cron threshold through the interface. - $admin_user = $this->drupalCreateUser(array('administer site configuration')); - $this->drupalLogin($admin_user); $this->drupalPostForm('admin/config/system/cron', array('cron_safe_threshold' => 0), t('Save configuration')); $this->assertText(t('The configuration options have been saved.')); $this->drupalLogout(); diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml index 6eb709f..6dc4fdb 100644 --- a/core/modules/user/user.routing.yml +++ b/core/modules/user/user.routing.yml @@ -5,6 +5,9 @@ user.register: _title: 'Create new account' requirements: _access_user_register: 'TRUE' + # @todo Remove when https://www.drupal.org/node/2465053 lands. + options: + no_cache: TRUE user.logout: path: '/user/logout' diff --git a/core/modules/views/src/Tests/GlossaryTest.php b/core/modules/views/src/Tests/GlossaryTest.php index 2418887..7b1ecac 100644 --- a/core/modules/views/src/Tests/GlossaryTest.php +++ b/core/modules/views/src/Tests/GlossaryTest.php @@ -70,10 +70,20 @@ public function testGlossaryView() { // Enable the glossary to be displayed. $view->storage->enable()->save(); $this->container->get('router.builder')->rebuildIfNeeded(); + $url = Url::fromRoute('view.glossary.page_1'); + + // Verify cache tags. + $this->assertPageCacheContextsAndTags($url, ['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url', 'user.node_grants:view', 'user.permissions'], [ + 'config:views.view.glossary', + 'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(), + 'node_list', + 'user_list', + 'rendered', + ]); + // Check the actual page response. - $this->drupalGet('glossary'); + $this->drupalGet($url); $this->assertResponse(200); - foreach ($nodes_per_char as $char => $count) { $href = Url::fromRoute('view.glossary.page_1', ['arg_0' => $char])->toString(); $label = Unicode::strtoupper($char); @@ -85,16 +95,6 @@ public function testGlossaryView() { $result_count = trim(str_replace(array('|', '(', ')'), '', (string) $result[0])); $this->assertEqual($result_count, $count, 'The expected number got rendered.'); } - - // Verify cache tags. - $this->enablePageCaching(); - $this->assertPageCacheContextsAndTags(Url::fromRoute('view.glossary.page_1'), ['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url', 'user.node_grants:view', 'user.permissions'], [ - 'config:views.view.glossary', - 'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(), - 'node_list', - 'user_list', - 'rendered', - ]); } } diff --git a/core/modules/views/src/Tests/Plugin/AccessTest.php b/core/modules/views/src/Tests/Plugin/AccessTest.php index 4bc77db..715ebd5 100644 --- a/core/modules/views/src/Tests/Plugin/AccessTest.php +++ b/core/modules/views/src/Tests/Plugin/AccessTest.php @@ -7,6 +7,7 @@ namespace Drupal\views\Tests\Plugin; +use Drupal\Core\Cache\Cache; use Drupal\views\Tests\ViewTestData; use Drupal\views\Views; @@ -101,6 +102,14 @@ function testStaticAccessPlugin() { // termination event fires. Simulate that here. $this->container->get('router.builder')->rebuildIfNeeded(); + // Clear the page cache. + // @todo Remove this. The root cause is that the access plugins alters the + // route's access requirements. That means that the 403 from above does + // not have any cache tags, so modifying the View entity does not cause + // the cached 403 page to be invalidated. + // Remove as part of https://www.drupal.org/node/2464657. + Cache::invalidateTags(['rendered']); + $this->assertTrue($access_plugin->access($this->normalUser)); $this->drupalGet('test_access_static'); diff --git a/sites/example.settings.local.php b/sites/example.settings.local.php index 7859fe5..4cc2109 100644 --- a/sites/example.settings.local.php +++ b/sites/example.settings.local.php @@ -28,7 +28,7 @@ $config['system.performance']['js']['preprocess'] = FALSE; /** - * Disable the render cache. + * Disable the render cache (this includes the page cache). * * This setting disables the render cache by using the Null cache back-end * defined by the development.services.yml file above.