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 f5d6f6c

Browse filesBrowse files
committed
[FrameworkBundle][HttpFoundation] Add _stateless
1 parent 2b68d53 commit f5d6f6c
Copy full SHA for f5d6f6c

File tree

6 files changed

+93
-5
lines changed
Filter options

6 files changed

+93
-5
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@
6666
<argument type="service_locator">
6767
<argument key="session" type="service" id="session" on-invalid="ignore" />
6868
<argument key="initialized_session" type="service" id="session" on-invalid="ignore_uninitialized" />
69+
<argument key="logger" type="service" id="logger" on-invalid="ignore" />
6970
</argument>
71+
<argument>%kernel.debug%</argument>
7072
</service>
7173

7274
<!-- for BC -->

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ public function testNullSessionHandler()
504504
$this->assertNull($container->getDefinition('session.storage.native')->getArgument(1));
505505
$this->assertNull($container->getDefinition('session.storage.php_bridge')->getArgument(0));
506506

507-
$expected = ['session', 'initialized_session'];
507+
$expected = ['session', 'initialized_session', 'logger'];
508508
$this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues()));
509509
}
510510

@@ -1301,7 +1301,7 @@ public function testSessionCookieSecureAuto()
13011301
{
13021302
$container = $this->createContainerFromFile('session_cookie_secure_auto');
13031303

1304-
$expected = ['session', 'initialized_session', 'session_storage', 'request_stack'];
1304+
$expected = ['session', 'initialized_session', 'logger', 'session_storage', 'request_stack'];
13051305
$this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues()));
13061306
}
13071307

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
+25-1Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
1919
use Symfony\Component\HttpKernel\Event\RequestEvent;
2020
use Symfony\Component\HttpKernel\Event\ResponseEvent;
21+
use Symfony\Component\HttpKernel\Exception\UnexpectedSessionUsageException;
2122
use Symfony\Component\HttpKernel\KernelEvents;
2223

2324
/**
@@ -40,11 +41,13 @@ abstract class AbstractSessionListener implements EventSubscriberInterface
4041
const NO_AUTO_CACHE_CONTROL_HEADER = 'Symfony-Session-NoAutoCacheControl';
4142

4243
protected $container;
44+
private $debug;
4345
private $sessionUsageStack = [];
4446

45-
public function __construct(ContainerInterface $container = null)
47+
public function __construct(ContainerInterface $container = null, bool $debug = true)
4648
{
4749
$this->container = $container;
50+
$this->debug = $debug;
4851
}
4952

5053
public function onKernelRequest(RequestEvent $event)
@@ -83,6 +86,10 @@ public function onKernelResponse(ResponseEvent $event)
8386
}
8487

8588
if ($session instanceof Session ? $session->getUsageIndex() !== end($this->sessionUsageStack) : $session->isStarted()) {
89+
if ($event->getRequest()->attributes->get('_stateless', false)) {
90+
$this->reportUnexpectedSessionUse();
91+
}
92+
8693
if ($autoCacheControl) {
8794
$response
8895
->setExpires(new \DateTime())
@@ -145,4 +152,21 @@ public static function getSubscribedEvents(): array
145152
* @return SessionInterface|null A SessionInterface instance or null if no session is available
146153
*/
147154
abstract protected function getSession();
155+
156+
/**
157+
* Report that the session was unexpectedly used.
158+
*
159+
* @throws UnexpectedSessionUsageException
160+
*/
161+
private function reportUnexpectedSessionUse(): void
162+
{
163+
$message = 'Session was used while the request was declared stateless.';
164+
if ($this->debug) {
165+
throw new UnexpectedSessionUsageException($message);
166+
}
167+
168+
if ($this->container->has('logger')) {
169+
$this->container->get('logger')->warning($message);
170+
}
171+
}
148172
}

‎src/Symfony/Component/HttpKernel/EventListener/SessionListener.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/EventListener/SessionListener.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
*/
2929
class SessionListener extends AbstractSessionListener
3030
{
31-
public function __construct(ContainerInterface $container)
31+
public function __construct(ContainerInterface $container, bool $debug = true)
3232
{
33-
$this->container = $container;
33+
parent::__construct($container, $debug);
3434
}
3535

3636
protected function getSession(): ?SessionInterface
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Exception;
13+
14+
/**
15+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
16+
*/
17+
class UnexpectedSessionUsageException extends \LogicException
18+
{
19+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpKernel\Tests\EventListener;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Psr\Log\LoggerInterface;
1516
use Symfony\Component\DependencyInjection\Container;
1617
use Symfony\Component\DependencyInjection\ServiceLocator;
1718
use Symfony\Component\HttpFoundation\Request;
@@ -24,6 +25,7 @@
2425
use Symfony\Component\HttpKernel\Event\ResponseEvent;
2526
use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
2627
use Symfony\Component\HttpKernel\EventListener\SessionListener;
28+
use Symfony\Component\HttpKernel\Exception\UnexpectedSessionUsageException;
2729
use Symfony\Component\HttpKernel\HttpKernelInterface;
2830

2931
class SessionListenerTest extends TestCase
@@ -178,4 +180,45 @@ public function testSurrogateMasterRequestIsPublic()
178180
$this->assertTrue($response->headers->has('Expires'));
179181
$this->assertLessThanOrEqual((new \DateTime('now', new \DateTimeZone('UTC'))), (new \DateTime($response->headers->get('Expires'))));
180182
}
183+
184+
public function testSessionUsageExceptionIfStatelessAndSessionUsed()
185+
{
186+
$session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock();
187+
$session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1));
188+
189+
$container = new Container();
190+
$container->set('initialized_session', $session);
191+
192+
$listener = new SessionListener($container, true);
193+
$kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock();
194+
195+
$request = new Request();
196+
$request->attributes->set('_stateless', true);
197+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST));
198+
199+
$this->expectException(UnexpectedSessionUsageException::class);
200+
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, new Response()));
201+
}
202+
203+
public function testSessionUsageLogIfStatelessAndSessionUsed()
204+
{
205+
$session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock();
206+
$session->expects($this->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1));
207+
208+
$logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock();
209+
$logger->expects($this->exactly(1))->method('warning');
210+
211+
$container = new Container();
212+
$container->set('initialized_session', $session);
213+
$container->set('logger', $logger);
214+
215+
$listener = new SessionListener($container, false);
216+
$kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock();
217+
218+
$request = new Request();
219+
$request->attributes->set('_stateless', true);
220+
$listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST));
221+
222+
$listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, new Response()));
223+
}
181224
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.