Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 146e01c

Browse filesBrowse files
[HttpKernel] fix session tracking in surrogate master requests
1 parent 143bdfc commit 146e01c
Copy full SHA for 146e01c

File tree

5 files changed

+77
-13
lines changed
Filter options

5 files changed

+77
-13
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Session/Session.php
+14-5Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/Session/SessionBagProxy.php
+10-4Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
+16-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
+36-2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/composer.json
+1-1Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.