Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f0b1dc2

Browse files
committed
bug #25583 [HttpKernel] Call Response->setPrivate() instead of sending raw header() when session is started (Toflar)
This PR was merged into the 3.4 branch. Discussion ---------- [HttpKernel] Call Response->setPrivate() instead of sending raw header() when session is started | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #24988 | License | MIT | Doc PR | - As described in #24988 I think the current handling of the `Cache-Control` header set by the `NativeSessionStorage` causes inconsistent behaviour. In #24988 @nicolas-grekas states that if you start a session a response should be considered to be private. I do agree with this but up until now, nobody takes care of this on `kernel.response`. I think we must always suppress the `NativeSessionStorage` from generating any headers by default. Otherwise the `Cache-Control` header never makes it to the `Response` instance and is thus missed by `kernel.response` listeners and for example the Symfony HttpCache. So depending on whether you use Symfony's HttpCache or Varnish as a reverse proxy, caching would be handled differently. Varnish would consider the response to be private if you set the php.ini setting `session.cache_limiter` to `nocache` (which is default) because it will receive the header. HttpCache would not because the `Cache-Control` header is not present on the `Response`. That's inconsistent and may cause confusion or problems when switching proxies. Commits ------- dbc1c1c [HttpKernel] Call Response->setPrivate() instead of sending raw header() when session is started
2 parents bbe23a7 + dbc1c1c commit f0b1dc2

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
825825

826826
// session storage
827827
$container->setAlias('session.storage', $config['storage_id'])->setPrivate(true);
828-
$options = array();
828+
$options = array('cache_limiter' => '0');
829829
foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'use_strict_mode') as $key) {
830830
if (isset($config[$key])) {
831831
$options[$key] = $config[$key];

src/Symfony/Component/HttpKernel/EventListener/SaveSessionListener.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public function onKernelResponse(FilterResponseEvent $event)
5353
$session = $event->getRequest()->getSession();
5454
if ($session && $session->isStarted()) {
5555
$session->save();
56+
$event->getResponse()
57+
->setPrivate()
58+
->setMaxAge(0)
59+
->headers->addCacheControlDirective('must-revalidate');
5660
}
5761
}
5862

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\Tests\EventListener;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
18+
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
19+
use Symfony\Component\HttpKernel\EventListener\SaveSessionListener;
20+
use Symfony\Component\HttpKernel\HttpKernelInterface;
21+
22+
class SaveSessionListenerTest extends TestCase
23+
{
24+
public function testOnlyTriggeredOnMasterRequest()
25+
{
26+
$listener = new SaveSessionListener();
27+
$event = $this->getMockBuilder(FilterResponseEvent::class)->disableOriginalConstructor()->getMock();
28+
$event->expects($this->once())->method('isMasterRequest')->willReturn(false);
29+
$event->expects($this->never())->method('getRequest');
30+
31+
// sub request
32+
$listener->onKernelResponse($event);
33+
}
34+
35+
public function testSessionSavedAndResponsePrivate()
36+
{
37+
$listener = new SaveSessionListener();
38+
$kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock();
39+
40+
$session = $this->getMockBuilder(SessionInterface::class)->disableOriginalConstructor()->getMock();
41+
$session->expects($this->once())->method('isStarted')->willReturn(true);
42+
$session->expects($this->once())->method('save');
43+
44+
$request = new Request();
45+
$request->setSession($session);
46+
$response = new Response();
47+
$listener->onKernelResponse(new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response));
48+
49+
$this->assertTrue($response->headers->hasCacheControlDirective('private'));
50+
$this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate'));
51+
$this->assertSame('0', $response->headers->getCacheControlDirective('max-age'));
52+
}
53+
}

0 commit comments

Comments
 (0)