diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index 9865d6a79aba3..9d4051338716d 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -20,13 +20,22 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** - * Sets the session in the request. + * Sets the session onto the request on the "kernel.request" event and saves + * it on the "kernel.response" event. + * + * In addition, if the session has been started it overrides the Cache-Control + * header in such a way that all caching is disabled in that case. + * If you have a scenario where caching responses with session information in + * them makes sense, you can disable this behaviour by setting the header + * AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER on the response. * * @author Johannes M. Schmitt * @author Tobias Schultze */ abstract class AbstractSessionListener implements EventSubscriberInterface { + const NO_AUTO_CACHE_CONTROL_HEADER = 'Symfony-Session-NoAutoCacheControl'; + protected $container; public function __construct(ContainerInterface $container = null) @@ -60,13 +69,20 @@ public function onKernelResponse(FilterResponseEvent $event) return; } + $response = $event->getResponse(); + if ($session->isStarted() || ($session instanceof Session && $session->hasBeenStarted())) { - $event->getResponse() - ->setPrivate() - ->setMaxAge(0) - ->headers->addCacheControlDirective('must-revalidate'); + if (!$response->headers->has(self::NO_AUTO_CACHE_CONTROL_HEADER)) { + $response + ->setPrivate() + ->setMaxAge(0) + ->headers->addCacheControlDirective('must-revalidate'); + } } + // Always remove the internal header if present + $response->headers->remove(self::NO_AUTO_CACHE_CONTROL_HEADER); + if ($session->isStarted()) { /* * Saves the session, in case it is still open, before sending the response/headers. diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php index 79fd88e4ab1cd..c196e07b2745e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php @@ -56,7 +56,7 @@ public function testSessionIsSet() $this->assertSame($session, $request->getSession()); } - public function testResponseIsPrivate() + public function testResponseIsPrivateIfSessionStarted() { $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); $session->expects($this->exactly(2))->method('isStarted')->willReturn(false); @@ -74,6 +74,31 @@ public function testResponseIsPrivate() $this->assertTrue($response->headers->hasCacheControlDirective('private')); $this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate')); $this->assertSame('0', $response->headers->getCacheControlDirective('max-age')); + $this->assertFalse($response->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER)); + } + + public function testResponseIsStillPublicIfSessionStartedAndHeaderPresent() + { + $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); + $session->expects($this->exactly(2))->method('isStarted')->willReturn(false); + $session->expects($this->once())->method('hasBeenStarted')->willReturn(true); + + $container = new Container(); + $container->set('initialized_session', $session); + + $listener = new SessionListener($container); + $kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock(); + + $response = new Response(); + $response->setSharedMaxAge(60); + $response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, 'true'); + $listener->onKernelResponse(new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response)); + + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + $this->assertFalse($response->headers->hasCacheControlDirective('must-revalidate')); + $this->assertSame('60', $response->headers->getCacheControlDirective('s-maxage')); + $this->assertFalse($response->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER)); } public function testUninitilizedSession()