vendor/contao/core-bundle/src/EventListener/MakeResponsePrivateListener.php line 50

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\EventListener;
  11. use Contao\CoreBundle\Routing\ScopeMatcher;
  12. use Symfony\Component\HttpFoundation\Cookie;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  15. use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
  16. /**
  17.  * @internal
  18.  */
  19. class MakeResponsePrivateListener
  20. {
  21.     public const DEBUG_HEADER 'Contao-Private-Response-Reason';
  22.     /**
  23.      * @var ScopeMatcher
  24.      */
  25.     private $scopeMatcher;
  26.     public function __construct(ScopeMatcher $scopeMatcher)
  27.     {
  28.         $this->scopeMatcher $scopeMatcher;
  29.     }
  30.     /**
  31.      * Make sure that the current response becomes a private response if any
  32.      * of the following conditions are true.
  33.      *
  34.      *   1. An Authorization header is present and not empty
  35.      *   2. The session was started
  36.      *   3. The response sets a cookie (same reason as 2 but for other cookies than the session cookie)
  37.      *   4. The response has a "Vary: Cookie" header and the request provides at least one cookie
  38.      *
  39.      * Some of this logic is also already implemented in the HttpCache (1, 2 and 3), but we
  40.      * want to make sure it works for any reverse proxy without having to configure too much.
  41.      */
  42.     public function __invoke(ResponseEvent $event): void
  43.     {
  44.         if (!$this->scopeMatcher->isContaoMasterRequest($event)) {
  45.             return;
  46.         }
  47.         $request $event->getRequest();
  48.         $response $event->getResponse();
  49.         // Disable the default Symfony auto cache control
  50.         $response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER'1');
  51.         // If the response is not cacheable for a reverse proxy, we don't have to do anything anyway
  52.         if (!$response->isCacheable()) {
  53.             return;
  54.         }
  55.         // 1) An Authorization header is present and not empty
  56.         if ('' !== (string) $request->headers->get('Authorization')) {
  57.             $this->makePrivate($response'authorization');
  58.             return;
  59.         }
  60.         // 2) The session was started
  61.         if ($request->hasSession() && $request->getSession()->isStarted()) {
  62.             $this->makePrivate($response'session-cookie');
  63.             return;
  64.         }
  65.         // 3) The response sets a cookie (same reason as 2 but for other cookies than the session cookie)
  66.         $cookies $response->headers->getCookies();
  67.         if (!== \count($cookies)) {
  68.             $this->makePrivate(
  69.                 $response,
  70.                 sprintf('response-cookies (%s)'implode(', 'array_map(
  71.                     static function (Cookie $cookie) {
  72.                         return $cookie->getName();
  73.                     },
  74.                     $cookies
  75.                 )))
  76.             );
  77.             return;
  78.         }
  79.         // 4) The response has a "Vary: Cookie" header and the request provides at least one cookie
  80.         if ($request->cookies->count() && \in_array('cookie'array_map('strtolower'$response->getVary()), true)) {
  81.             $this->makePrivate(
  82.                 $response,
  83.                 sprintf('request-cookies (%s)'implode(', 'array_keys($request->cookies->all())))
  84.             );
  85.         }
  86.     }
  87.     private function makePrivate(Response $responsestring $reason): void
  88.     {
  89.         $response->setPrivate();
  90.         $response->headers->set(self::DEBUG_HEADER$reason);
  91.     }
  92. }