Loading core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +8 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,14 @@ public function onRespond(ResponseEvent $event) { * TRUE when Cache-Control header was set explicitly on the given response. */ protected function isCacheControlCustomized(Response $response) { // Symfony >= 3.2 explicitly removes the Cache-Control header for 301 // redirects which do not have a custom Cache-Control header. Treat those // redirect responses as not customized. // @see https://github.com/symfony/symfony/issues/17139 if ($response->getStatusCode() === 301 && !$response->headers->has('Cache-Control')) { return FALSE; } $cache_control = $response->headers->get('Cache-Control'); return $cache_control != 'no-cache, private' && $cache_control != 'private, must-revalidate'; } Loading core/modules/page_cache/tests/src/Functional/PageCacheTest.php +33 −32 Original line number Diff line number Diff line Loading @@ -54,9 +54,7 @@ protected function setUp(): void { * persisted. */ public function testPageCacheTags() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $path = 'system-test/cache_tags_page'; $tags = ['system_test_cache_tags_page']; Loading Loading @@ -125,9 +123,7 @@ public function testPageCacheTagsIndependentFromCacheabilityHeaders() { * The request formats are specified via a query parameter. */ public function testQueryParameterFormatRequests() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $accept_header_cache_url = Url::fromRoute('system_test.page_cache_accept_header'); $accept_header_cache_url_with_json = Url::fromRoute('system_test.page_cache_accept_header', ['_format' => 'json']); Loading Loading @@ -165,9 +161,7 @@ public function testQueryParameterFormatRequests() { * Tests support of requests with If-Modified-Since and If-None-Match headers. */ public function testConditionalRequests() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); // Fill the cache. $this->drupalGet(''); Loading Loading @@ -228,9 +222,7 @@ public function testConditionalRequests() { * Tests cache headers. */ public function testPageCache() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); // Fill the cache. $this->drupalGet('system-test/set-header', ['query' => ['name' => 'Foo', 'value' => 'bar']]); Loading @@ -239,7 +231,7 @@ public function testPageCache() { // Symfony's Response logic determines a specific order for the subvalues // of the Cache-Control header, even if they are explicitly passed in to // the response header bag in a different order. $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); $this->assertSession()->responseHeaderEquals('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT'); $this->assertSession()->responseHeaderEquals('Foo', 'bar'); Loading @@ -247,7 +239,7 @@ public function testPageCache() { $this->drupalGet('system-test/set-header', ['query' => ['name' => 'Foo', 'value' => 'bar']]); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT'); $this->assertSession()->responseHeaderContains('Vary', 'cookie'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); $this->assertSession()->responseHeaderEquals('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT'); $this->assertSession()->responseHeaderEquals('Foo', 'bar'); Loading @@ -271,7 +263,7 @@ public function testPageCache() { // a custom #cache max-age set on an element does not affect page max-age. $this->drupalLogout(); $this->drupalGet('system-test/cache_max_age_page'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); } /** Loading @@ -288,9 +280,7 @@ public function testPageCache() { * roles. */ public function testPageCacheAnonymousRolePermissions() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $content_url = Url::fromRoute('system_test.permission_dependent_content'); $route_access_url = Url::fromRoute('system_test.permission_dependent_route_access'); Loading Loading @@ -416,9 +406,7 @@ public function testPageCacheAnonymous403404() { * Tests the omit_vary_cookie setting. */ public function testPageCacheWithoutVaryCookie() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $settings['settings']['omit_vary_cookie'] = (object) [ 'value' => TRUE, Loading @@ -430,13 +418,13 @@ public function testPageCacheWithoutVaryCookie() { $this->drupalGet(''); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'MISS'); $this->assertSession()->responseHeaderNotContains('Vary', 'cookie'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); // Check cache. $this->drupalGet(''); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT'); $this->assertSession()->responseHeaderNotContains('Vary', 'cookie'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); } /** Loading Loading @@ -474,9 +462,7 @@ public function testFormImmutability() { * CacheableResponseInterface. */ public function testCacheableResponseResponses() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); // GET a URL, which would be marked as a cache miss if it were cacheable. $this->drupalGet('/system-test/respond-response'); Loading @@ -501,12 +487,12 @@ public function testCacheableResponseResponses() { // GET a URL, which should be marked as a cache miss. $this->drupalGet('/system-test/respond-cacheable-response'); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'MISS'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); // GET it again, it should now be a cache hit. $this->drupalGet('/system-test/respond-cacheable-response'); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); // Uninstall page cache. This should flush all caches so the next call to a // previously cached page should be a miss now. Loading Loading @@ -552,20 +538,35 @@ public function testHead() { * Tests a cacheable response with custom cache control. */ public function testCacheableWithCustomCacheControl() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $this->drupalGet('/system-test/custom-cache-control'); $this->assertSession()->statusCodeEquals(200); $this->assertSession()->responseHeaderEquals('Cache-Control', 'bar, private'); } /** * Tests that the Cache-Control header is added by FinishResponseSubscriber. */ public function testCacheabilityOfRedirectResponses(): void { $this->enablePageCaching(); $this->getSession()->getDriver()->getClient()->followRedirects(FALSE); $this->maximumMetaRefreshCount = 0; foreach ([301, 302, 303, 307, 308] as $status_code) { foreach (['local', 'cacheable', 'trusted'] as $type) { $this->drupalGet("/system-test/redirect/{$type}/{$status_code}"); $this->assertSession()->statusCodeEquals($status_code); $this->assertCacheMaxAge(300); } } } /** * Tests that URLs are cached in a not normalized form. */ public function testNoUrlNormalization() { // Use absolute URLs to avoid any processing. $url = Url::fromRoute('<front>')->setAbsolute()->toString(); Loading core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php +27 −3 Original line number Diff line number Diff line Loading @@ -3,20 +3,23 @@ namespace Drupal\system_test\Controller; use Drupal\Core\Access\AccessResult; use Drupal\Core\Cache\CacheableRedirectResponse; use Drupal\Core\Cache\CacheableResponse; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\PageCache\ResponsePolicy\KillSwitch; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Render\Markup; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\LocalRedirectResponse; use Drupal\Core\Routing\TrustedRedirectResponse; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Drupal\Core\Lock\LockBackendInterface; /** * Controller routines for system_test routes. Loading Loading @@ -424,6 +427,27 @@ public function getCacheableResponseWithCustomCacheControl() { return new CacheableResponse('Foo', 200, ['Cache-Control' => 'bar']); } /** * Returns a CacheableRedirectResponse with the given status code. */ public function respondWithCacheableRedirectResponse(int $status_code): CacheableRedirectResponse { return new CacheableRedirectResponse('/llamas', $status_code); } /** * Returns a LocalRedirectResponse with the given status code. */ public function respondWithLocalRedirectResponse(int $status_code): LocalRedirectResponse { return new LocalRedirectResponse('/llamas', $status_code); } /** * Returns a TrustedRedirectResponse with the given status code. */ public function respondWithTrustedRedirectResponse(int $status_code): TrustedRedirectResponse { return new TrustedRedirectResponse('/llamas', $status_code); } /** * {@inheritdoc} */ Loading core/modules/system/tests/modules/system_test/system_test.routing.yml +24 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,30 @@ system_test.copy_field_value: requirements: _access: 'TRUE' router_test.cacheable_redirect: path: '/system-test/redirect/cacheable/{status_code}' defaults: _controller: '\Drupal\system_test\Controller\SystemTestController::respondWithCacheableRedirectResponse' requirements: _access: 'TRUE' status_code: 201|301|302|303|307|308 router_test.local_redirect: path: '/system-test/redirect/local/{status_code}' defaults: _controller: '\Drupal\system_test\Controller\SystemTestController::respondWithLocalRedirectResponse' requirements: _access: 'TRUE' status_code: 201|301|302|303|307|308 router_test.trusted_redirect: path: '/system-test/redirect/trusted/{status_code}' defaults: _controller: '\Drupal\system_test\Controller\SystemTestController::respondWithTrustedRedirectResponse' requirements: _access: 'TRUE' status_code: 201|301|302|303|307|308 system_test.install_profile: path: '/system-test/get-install-profile' defaults: Loading core/modules/system/tests/src/Functional/Cache/AssertPageCacheContextsAndTagsTrait.php +2 −2 Original line number Diff line number Diff line Loading @@ -162,8 +162,8 @@ protected function assertCacheContexts(array $expected_contexts, $message = NULL * @param int $max_age * The maximum age of the cache. */ protected function assertCacheMaxAge($max_age) { $this->assertSession()->responseHeaderContains('Cache-Control', 'max-age:' . $max_age); protected function assertCacheMaxAge(int $max_age) { $this->assertSession()->responseHeaderEquals('Cache-Control', "max-age=$max_age, public"); } } Loading
core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +8 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,14 @@ public function onRespond(ResponseEvent $event) { * TRUE when Cache-Control header was set explicitly on the given response. */ protected function isCacheControlCustomized(Response $response) { // Symfony >= 3.2 explicitly removes the Cache-Control header for 301 // redirects which do not have a custom Cache-Control header. Treat those // redirect responses as not customized. // @see https://github.com/symfony/symfony/issues/17139 if ($response->getStatusCode() === 301 && !$response->headers->has('Cache-Control')) { return FALSE; } $cache_control = $response->headers->get('Cache-Control'); return $cache_control != 'no-cache, private' && $cache_control != 'private, must-revalidate'; } Loading
core/modules/page_cache/tests/src/Functional/PageCacheTest.php +33 −32 Original line number Diff line number Diff line Loading @@ -54,9 +54,7 @@ protected function setUp(): void { * persisted. */ public function testPageCacheTags() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $path = 'system-test/cache_tags_page'; $tags = ['system_test_cache_tags_page']; Loading Loading @@ -125,9 +123,7 @@ public function testPageCacheTagsIndependentFromCacheabilityHeaders() { * The request formats are specified via a query parameter. */ public function testQueryParameterFormatRequests() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $accept_header_cache_url = Url::fromRoute('system_test.page_cache_accept_header'); $accept_header_cache_url_with_json = Url::fromRoute('system_test.page_cache_accept_header', ['_format' => 'json']); Loading Loading @@ -165,9 +161,7 @@ public function testQueryParameterFormatRequests() { * Tests support of requests with If-Modified-Since and If-None-Match headers. */ public function testConditionalRequests() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); // Fill the cache. $this->drupalGet(''); Loading Loading @@ -228,9 +222,7 @@ public function testConditionalRequests() { * Tests cache headers. */ public function testPageCache() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); // Fill the cache. $this->drupalGet('system-test/set-header', ['query' => ['name' => 'Foo', 'value' => 'bar']]); Loading @@ -239,7 +231,7 @@ public function testPageCache() { // Symfony's Response logic determines a specific order for the subvalues // of the Cache-Control header, even if they are explicitly passed in to // the response header bag in a different order. $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); $this->assertSession()->responseHeaderEquals('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT'); $this->assertSession()->responseHeaderEquals('Foo', 'bar'); Loading @@ -247,7 +239,7 @@ public function testPageCache() { $this->drupalGet('system-test/set-header', ['query' => ['name' => 'Foo', 'value' => 'bar']]); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT'); $this->assertSession()->responseHeaderContains('Vary', 'cookie'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); $this->assertSession()->responseHeaderEquals('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT'); $this->assertSession()->responseHeaderEquals('Foo', 'bar'); Loading @@ -271,7 +263,7 @@ public function testPageCache() { // a custom #cache max-age set on an element does not affect page max-age. $this->drupalLogout(); $this->drupalGet('system-test/cache_max_age_page'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); } /** Loading @@ -288,9 +280,7 @@ public function testPageCache() { * roles. */ public function testPageCacheAnonymousRolePermissions() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $content_url = Url::fromRoute('system_test.permission_dependent_content'); $route_access_url = Url::fromRoute('system_test.permission_dependent_route_access'); Loading Loading @@ -416,9 +406,7 @@ public function testPageCacheAnonymous403404() { * Tests the omit_vary_cookie setting. */ public function testPageCacheWithoutVaryCookie() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $settings['settings']['omit_vary_cookie'] = (object) [ 'value' => TRUE, Loading @@ -430,13 +418,13 @@ public function testPageCacheWithoutVaryCookie() { $this->drupalGet(''); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'MISS'); $this->assertSession()->responseHeaderNotContains('Vary', 'cookie'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); // Check cache. $this->drupalGet(''); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT'); $this->assertSession()->responseHeaderNotContains('Vary', 'cookie'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); } /** Loading Loading @@ -474,9 +462,7 @@ public function testFormImmutability() { * CacheableResponseInterface. */ public function testCacheableResponseResponses() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); // GET a URL, which would be marked as a cache miss if it were cacheable. $this->drupalGet('/system-test/respond-response'); Loading @@ -501,12 +487,12 @@ public function testCacheableResponseResponses() { // GET a URL, which should be marked as a cache miss. $this->drupalGet('/system-test/respond-cacheable-response'); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'MISS'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); // GET it again, it should now be a cache hit. $this->drupalGet('/system-test/respond-cacheable-response'); $this->assertSession()->responseHeaderEquals('X-Drupal-Cache', 'HIT'); $this->assertSession()->responseHeaderEquals('Cache-Control', 'max-age=300, public'); $this->assertCacheMaxAge(300); // Uninstall page cache. This should flush all caches so the next call to a // previously cached page should be a miss now. Loading Loading @@ -552,20 +538,35 @@ public function testHead() { * Tests a cacheable response with custom cache control. */ public function testCacheableWithCustomCacheControl() { $config = $this->config('system.performance'); $config->set('cache.page.max_age', 300); $config->save(); $this->enablePageCaching(); $this->drupalGet('/system-test/custom-cache-control'); $this->assertSession()->statusCodeEquals(200); $this->assertSession()->responseHeaderEquals('Cache-Control', 'bar, private'); } /** * Tests that the Cache-Control header is added by FinishResponseSubscriber. */ public function testCacheabilityOfRedirectResponses(): void { $this->enablePageCaching(); $this->getSession()->getDriver()->getClient()->followRedirects(FALSE); $this->maximumMetaRefreshCount = 0; foreach ([301, 302, 303, 307, 308] as $status_code) { foreach (['local', 'cacheable', 'trusted'] as $type) { $this->drupalGet("/system-test/redirect/{$type}/{$status_code}"); $this->assertSession()->statusCodeEquals($status_code); $this->assertCacheMaxAge(300); } } } /** * Tests that URLs are cached in a not normalized form. */ public function testNoUrlNormalization() { // Use absolute URLs to avoid any processing. $url = Url::fromRoute('<front>')->setAbsolute()->toString(); Loading
core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php +27 −3 Original line number Diff line number Diff line Loading @@ -3,20 +3,23 @@ namespace Drupal\system_test\Controller; use Drupal\Core\Access\AccessResult; use Drupal\Core\Cache\CacheableRedirectResponse; use Drupal\Core\Cache\CacheableResponse; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\PageCache\ResponsePolicy\KillSwitch; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Render\Markup; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\LocalRedirectResponse; use Drupal\Core\Routing\TrustedRedirectResponse; use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Drupal\Core\Lock\LockBackendInterface; /** * Controller routines for system_test routes. Loading Loading @@ -424,6 +427,27 @@ public function getCacheableResponseWithCustomCacheControl() { return new CacheableResponse('Foo', 200, ['Cache-Control' => 'bar']); } /** * Returns a CacheableRedirectResponse with the given status code. */ public function respondWithCacheableRedirectResponse(int $status_code): CacheableRedirectResponse { return new CacheableRedirectResponse('/llamas', $status_code); } /** * Returns a LocalRedirectResponse with the given status code. */ public function respondWithLocalRedirectResponse(int $status_code): LocalRedirectResponse { return new LocalRedirectResponse('/llamas', $status_code); } /** * Returns a TrustedRedirectResponse with the given status code. */ public function respondWithTrustedRedirectResponse(int $status_code): TrustedRedirectResponse { return new TrustedRedirectResponse('/llamas', $status_code); } /** * {@inheritdoc} */ Loading
core/modules/system/tests/modules/system_test/system_test.routing.yml +24 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,30 @@ system_test.copy_field_value: requirements: _access: 'TRUE' router_test.cacheable_redirect: path: '/system-test/redirect/cacheable/{status_code}' defaults: _controller: '\Drupal\system_test\Controller\SystemTestController::respondWithCacheableRedirectResponse' requirements: _access: 'TRUE' status_code: 201|301|302|303|307|308 router_test.local_redirect: path: '/system-test/redirect/local/{status_code}' defaults: _controller: '\Drupal\system_test\Controller\SystemTestController::respondWithLocalRedirectResponse' requirements: _access: 'TRUE' status_code: 201|301|302|303|307|308 router_test.trusted_redirect: path: '/system-test/redirect/trusted/{status_code}' defaults: _controller: '\Drupal\system_test\Controller\SystemTestController::respondWithTrustedRedirectResponse' requirements: _access: 'TRUE' status_code: 201|301|302|303|307|308 system_test.install_profile: path: '/system-test/get-install-profile' defaults: Loading
core/modules/system/tests/src/Functional/Cache/AssertPageCacheContextsAndTagsTrait.php +2 −2 Original line number Diff line number Diff line Loading @@ -162,8 +162,8 @@ protected function assertCacheContexts(array $expected_contexts, $message = NULL * @param int $max_age * The maximum age of the cache. */ protected function assertCacheMaxAge($max_age) { $this->assertSession()->responseHeaderContains('Cache-Control', 'max-age:' . $max_age); protected function assertCacheMaxAge(int $max_age) { $this->assertSession()->responseHeaderEquals('Cache-Control', "max-age=$max_age, public"); } }