Verified Commit ec45fd56 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3054821 by mr.baileys, wiifm, sime, angrytoast, pobster, smustgrave,...

Issue #3054821 by mr.baileys, wiifm, sime, angrytoast, pobster, smustgrave, RichardGaunt, alexpott, Wim Leers, quietone, Berdir, neclimdul, acbramley: Include Cache-Control header on 301 redirects

(cherry picked from commit 2fbe22aa)
parent 6e733866
Loading
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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';
  }
+33 −32
Original line number Diff line number Diff line
@@ -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'];
@@ -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']);
@@ -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('');
@@ -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']]);
@@ -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');

@@ -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');

@@ -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);
  }

  /**
@@ -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');
@@ -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,
@@ -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);
  }

  /**
@@ -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');
@@ -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.
@@ -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();

+27 −3
Original line number Diff line number Diff line
@@ -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.
@@ -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}
   */
+24 −0
Original line number Diff line number Diff line
@@ -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:
+2 −2
Original line number Diff line number Diff line
@@ -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