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

Skip to content

Commit 7b18ee6

Browse files
[HttpKernel] fix session tracking in surrogate master requests
1 parent 143bdfc commit 7b18ee6

File tree

5 files changed

+77
-13
lines changed

5 files changed

+77
-13
lines changed

src/Symfony/Component/HttpFoundation/Session/Session.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Session implements SessionInterface, \IteratorAggregate, \Countable
2929
private $flashName;
3030
private $attributeName;
3131
private $data = array();
32-
private $hasBeenStarted;
32+
private $usageIndex = 0;
3333

3434
/**
3535
* @param SessionStorageInterface $storage A SessionStorageInterface instance
@@ -54,6 +54,8 @@ public function __construct(SessionStorageInterface $storage = null, AttributeBa
5454
*/
5555
public function start()
5656
{
57+
++$this->usageIndex;
58+
5759
return $this->storage->start();
5860
}
5961

@@ -142,13 +144,13 @@ public function count()
142144
}
143145

144146
/**
145-
* @return bool
147+
* @return int
146148
*
147149
* @internal
148150
*/
149-
public function hasBeenStarted()
151+
public function getUsageIndex()
150152
{
151-
return $this->hasBeenStarted;
153+
return $this->usageIndex;
152154
}
153155

154156
/**
@@ -158,6 +160,7 @@ public function hasBeenStarted()
158160
*/
159161
public function isEmpty()
160162
{
163+
++$this->usageIndex;
161164
foreach ($this->data as &$data) {
162165
if (!empty($data)) {
163166
return false;
@@ -182,6 +185,8 @@ public function invalidate($lifetime = null)
182185
*/
183186
public function migrate($destroy = false, $lifetime = null)
184187
{
188+
++$this->usageIndex;
189+
185190
return $this->storage->regenerate($destroy, $lifetime);
186191
}
187192

@@ -190,6 +195,8 @@ public function migrate($destroy = false, $lifetime = null)
190195
*/
191196
public function save()
192197
{
198+
++$this->usageIndex;
199+
193200
$this->storage->save();
194201
}
195202

@@ -230,6 +237,8 @@ public function setName($name)
230237
*/
231238
public function getMetadataBag()
232239
{
240+
++$this->usageIndex;
241+
233242
return $this->storage->getMetadataBag();
234243
}
235244

@@ -238,7 +247,7 @@ public function getMetadataBag()
238247
*/
239248
public function registerBag(SessionBagInterface $bag)
240249
{
241-
$this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->hasBeenStarted));
250+
$this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex));
242251
}
243252

244253
/**

src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,22 @@ final class SessionBagProxy implements SessionBagInterface
2020
{
2121
private $bag;
2222
private $data;
23-
private $hasBeenStarted;
23+
private $usageIndex;
2424

25-
public function __construct(SessionBagInterface $bag, array &$data, &$hasBeenStarted)
25+
public function __construct(SessionBagInterface $bag, array &$data, &$usageIndex)
2626
{
2727
$this->bag = $bag;
2828
$this->data = &$data;
29-
$this->hasBeenStarted = &$hasBeenStarted;
29+
$this->usageIndex = &$usageIndex;
3030
}
3131

3232
/**
3333
* @return SessionBagInterface
3434
*/
3535
public function getBag()
3636
{
37+
++$this->usageIndex;
38+
3739
return $this->bag;
3840
}
3941

@@ -42,6 +44,8 @@ public function getBag()
4244
*/
4345
public function isEmpty()
4446
{
47+
++$this->usageIndex;
48+
4549
return empty($this->data[$this->bag->getStorageKey()]);
4650
}
4751

@@ -58,7 +62,7 @@ public function getName()
5862
*/
5963
public function initialize(array &$array)
6064
{
61-
$this->hasBeenStarted = true;
65+
++$this->usageIndex;
6266
$this->data[$this->bag->getStorageKey()] = &$array;
6367

6468
$this->bag->initialize($array);
@@ -77,6 +81,8 @@ public function getStorageKey()
7781
*/
7882
public function clear()
7983
{
84+
++$this->usageIndex;
85+
8086
return $this->bag->clear();
8187
}
8288
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\HttpFoundation\Session\Session;
1515
use Symfony\Component\HttpFoundation\Session\SessionInterface;
1616
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
17+
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
1718
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1819
use Symfony\Component\HttpKernel\KernelEvents;
1920
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -25,6 +26,8 @@
2526
*/
2627
abstract class AbstractSessionListener implements EventSubscriberInterface
2728
{
29+
private $sessionUsageStack = array();
30+
2831
public function onKernelRequest(GetResponseEvent $event)
2932
{
3033
if (!$event->isMasterRequest()) {
@@ -33,6 +36,7 @@ public function onKernelRequest(GetResponseEvent $event)
3336

3437
$request = $event->getRequest();
3538
$session = $this->getSession();
39+
$this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : null;
3640
if (null === $session || $request->hasSession()) {
3741
return;
3842
}
@@ -50,20 +54,31 @@ public function onKernelResponse(FilterResponseEvent $event)
5054
return;
5155
}
5256

53-
if ($session->isStarted() || ($session instanceof Session && $session->hasBeenStarted())) {
57+
if ($session instanceof Session ? $session->getUsageIndex() !== end($this->sessionUsageStack) : $session->isStarted()) {
5458
$event->getResponse()
5559
->setPrivate()
5660
->setMaxAge(0)
5761
->headers->addCacheControlDirective('must-revalidate');
5862
}
5963
}
6064

65+
/**
66+
* @internal
67+
*/
68+
public function onFinishRequest(FinishRequestEvent $event)
69+
{
70+
if ($event->isMasterRequest()) {
71+
array_pop($this->sessionUsageStack);
72+
}
73+
}
74+
6175
public static function getSubscribedEvents()
6276
{
6377
return array(
6478
KernelEvents::REQUEST => array('onKernelRequest', 128),
6579
// low priority to come after regular response listeners, same as SaveSessionListener
6680
KernelEvents::RESPONSE => array('onKernelResponse', -1000),
81+
KernelEvents::FINISH_REQUEST => array('onFinishRequest'),
6782
);
6883
}
6984

src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\HttpFoundation\Session\Session;
1919
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
2020
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
21+
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
2122
use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
2223
use Symfony\Component\HttpKernel\EventListener\SessionListener;
2324
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -58,8 +59,7 @@ public function testSessionIsSet()
5859
public function testResponseIsPrivate()
5960
{
6061
$session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock();
61-
$session->expects($this->once())->method('isStarted')->willReturn(false);
62-
$session->expects($this->once())->method('hasBeenStarted')->willReturn(true);
62+
$session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1));
6363

6464
$container = new Container();
6565
$container->set('session', $session);
@@ -76,4 +76,38 @@ public function testResponseIsPrivate()
7676
$this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate'));
7777
$this->assertSame('0', $response->headers->getCacheControlDirective('max-age'));
7878
}
79+
80+
public function testSurrogateMasterRequestIsPublic()
81+
{
82+
$session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock();
83+
$session->expects($this->exactly(4))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1, 1, 1));
84+
85+
$container = new Container();
86+
$container->set('session', $session);
87+
88+
$listener = new SessionListener($container);
89+
$kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock();
90+
91+
$request = new Request();
92+
$response = new Response();
93+
$response->setCache(array('public' => true, 'max_age' => '30'));
94+
$listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST));
95+
$this->assertTrue($request->hasSession());
96+
97+
$subRequest = clone $request;
98+
$this->assertSame($request->getSession(), $subRequest->getSession());
99+
$listener->onKernelRequest(new GetResponseEvent($kernel, $subRequest, HttpKernelInterface::MASTER_REQUEST));
100+
$listener->onKernelResponse(new FilterResponseEvent($kernel, $subRequest, HttpKernelInterface::MASTER_REQUEST, $response));
101+
$listener->onFinishRequest(new FinishRequestEvent($kernel, $subRequest, HttpKernelInterface::MASTER_REQUEST));
102+
103+
$this->assertFalse($response->headers->hasCacheControlDirective('private'));
104+
$this->assertFalse($response->headers->hasCacheControlDirective('must-revalidate'));
105+
$this->assertSame('30', $response->headers->getCacheControlDirective('max-age'));
106+
107+
$listener->onKernelResponse(new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response));
108+
109+
$this->assertTrue($response->headers->hasCacheControlDirective('private'));
110+
$this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate'));
111+
$this->assertSame('0', $response->headers->getCacheControlDirective('max-age'));
112+
}
79113
}

src/Symfony/Component/HttpKernel/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"require": {
1919
"php": "^5.5.9|>=7.0.8",
2020
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
21-
"symfony/http-foundation": "^3.4.4|^4.0.4",
21+
"symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1",
2222
"symfony/debug": "~2.8|~3.0|~4.0",
2323
"symfony/polyfill-ctype": "~1.8",
2424
"psr/log": "~1.0"

0 commit comments

Comments
 (0)