-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[1.3] Backport Symfony session listener for Symfony 3.4+ to restore u…
…ser context functionality (#441) * Decorate default Symfony session listener for Symfony 3.4+ to restore user context functionality
- Loading branch information
Showing
9 changed files
with
249 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the FOSHttpCacheBundle package. | ||
* | ||
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace FOS\HttpCacheBundle\EventListener; | ||
|
||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; | ||
use Symfony\Component\HttpKernel\Event\GetResponseEvent; | ||
use Symfony\Component\HttpKernel\EventListener\SessionListener as BaseSessionListener; | ||
|
||
/** | ||
* Decorates the default Symfony session listener. | ||
* | ||
* The default Symfony session listener automatically makes responses private | ||
* in case the session was started. This kills the user context feature of | ||
* FOSHttpCache. We disable the default behaviour only if the user context header | ||
* is part of the Vary headers to reduce the possible impacts on other parts | ||
* of your application. | ||
* | ||
* @author Yanick Witschi <[email protected]> | ||
*/ | ||
final class SessionListener implements EventSubscriberInterface | ||
{ | ||
/** | ||
* @var BaseSessionListener | ||
*/ | ||
private $inner; | ||
|
||
/** | ||
* @var string | ||
*/ | ||
private $userHashHeader; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private $userIdentifierHeaders; | ||
|
||
/** | ||
* @param BaseSessionListener $inner | ||
* @param string $userHashHeader Must be lower-cased | ||
* @param array $userIdentifierHeaders Must be lower-cased | ||
*/ | ||
public function __construct(BaseSessionListener $inner, $userHashHeader, array $userIdentifierHeaders) | ||
{ | ||
$this->inner = $inner; | ||
$this->userHashHeader = $userHashHeader; | ||
$this->userIdentifierHeaders = $userIdentifierHeaders; | ||
} | ||
|
||
public function onKernelRequest(GetResponseEvent $event) | ||
{ | ||
return $this->inner->onKernelRequest($event); | ||
} | ||
|
||
public function onKernelResponse(FilterResponseEvent $event) | ||
{ | ||
if (!$event->isMasterRequest()) { | ||
return; | ||
} | ||
|
||
$varyHeaders = array_map('strtolower', $event->getResponse()->getVary()); | ||
$relevantHeaders = array_merge($this->userIdentifierHeaders, array($this->userHashHeader)); | ||
|
||
// Call default behaviour if it's an irrelevant request for the user context | ||
if (0 === count(array_intersect($varyHeaders, $relevantHeaders))) { | ||
$this->inner->onKernelResponse($event); | ||
} | ||
|
||
// noop, see class description | ||
} | ||
|
||
public static function getSubscribedEvents() | ||
{ | ||
return BaseSessionListener::getSubscribedEvents(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
fos_http_cache: | ||
fos_http_cache: [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the FOSHttpCacheBundle package. | ||
* | ||
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace FOS\HttpCacheBundle\Tests\Unit\EventListener; | ||
|
||
use FOS\HttpCacheBundle\EventListener\SessionListener; | ||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; | ||
use Symfony\Component\HttpKernel\EventListener\SessionListener as BaseSessionListener; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
use Symfony\Component\HttpKernel\Kernel; | ||
|
||
/** | ||
* @group sf34 | ||
*/ | ||
class SessionListenerTest extends TestCase | ||
{ | ||
public function testOnKernelRequestRemainsUntouched() | ||
{ | ||
$event = $this | ||
->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$inner = $this | ||
->getMockBuilder('Symfony\Component\HttpKernel\EventListener\SessionListener') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$inner | ||
->expects($this->once()) | ||
->method('onKernelRequest') | ||
->with($event) | ||
; | ||
|
||
$listener = $this->getListener($inner); | ||
$listener->onKernelRequest($event); | ||
} | ||
|
||
/** | ||
* @dataProvider onKernelResponseProvider | ||
*/ | ||
public function testOnKernelResponse(Response $response, $shouldCallDecoratedListener) | ||
{ | ||
if (version_compare(Kernel::VERSION, '3.4', '<')) { | ||
$this->markTestSkipped('Irrelevant for Symfony < 3.4'); | ||
} | ||
|
||
$httpKernel = $this | ||
->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$event = new FilterResponseEvent( | ||
$httpKernel, | ||
new Request(), | ||
HttpKernelInterface::MASTER_REQUEST, | ||
$response | ||
); | ||
|
||
$inner = $this | ||
->getMockBuilder('Symfony\Component\HttpKernel\EventListener\SessionListener') | ||
->disableOriginalConstructor() | ||
->getMock(); | ||
|
||
$inner | ||
->expects($shouldCallDecoratedListener ? $this->once() : $this->never()) | ||
->method('onKernelResponse') | ||
->with($event) | ||
; | ||
|
||
$listener = $this->getListener($inner); | ||
$listener->onKernelResponse($event); | ||
} | ||
|
||
public function onKernelResponseProvider() | ||
{ | ||
// Response, decorated listener should be called or not | ||
return array( | ||
'Irrelevant response' => array(new Response(), true), | ||
'Irrelevant response header' => array(new Response('', 200, array('Content-Type' => 'Foobar')), true), | ||
'Context hash header is present in Vary' => array(new Response('', 200, array('Vary' => 'X-User-Context-Hash')), false), | ||
'User identifier header is present in Vary' => array(new Response('', 200, array('Vary' => 'cookie')), false), | ||
'Both, context hash and identifier headers are present in Vary' => array(new Response('', 200, array('Vary' => 'Cookie, X-User-Context-Hash')), false), | ||
); | ||
} | ||
|
||
private function getListener(BaseSessionListener $inner, $userHashHeader = 'x-user-context-hash', $userIdentifierHeaders = array('cookie', 'authorization')) | ||
{ | ||
return new SessionListener($inner, $userHashHeader, $userIdentifierHeaders); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters