Skip to content

Commit e6227e8

Browse files
Toflardbu
authored andcommitted
[cache kernel] Adding a listener to remove cache tags before response is sent to the client
1 parent 88e4d42 commit e6227e8

File tree

6 files changed

+148
-2
lines changed

6 files changed

+148
-2
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ Changelog
33

44
See also the [GitHub releases page](https://github.com/FriendsOfSymfony/FOSHttpCache/releases).
55

6+
2.4.0 (unreleased)
7+
------------------
8+
9+
### Symfony
10+
11+
* Added: `CleanupCacheTagsListener` to remove the cache tags header from the final
12+
response that is sent to the client. Add this listener to your cache kernel.
13+
614
2.3.1
715
-----
816

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
},
5555
"extra": {
5656
"branch-alias": {
57-
"dev-master": "2.3.x-dev"
57+
"dev-master": "2.4.x-dev"
5858
}
5959
}
6060
}

doc/symfony-cache-configuration.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ store does not have tagging support.
199199
To install the store, run
200200
``composer require toflar/psr6-symfony-http-cache-store``.
201201

202+
You should also add the ``CleanupCacheTagsListener`` to make sure the final
203+
response when sent to the client does not contain any cache tags in the
204+
headers anymore.
205+
202206
Purging tags is only allowed from the same machine by default. To change this,
203207
you have the same configuration options as with the ``PurgeListener``. *Only
204208
set one of ``client_ips`` or ``client_matcher``*. Additionally, you can
@@ -229,6 +233,9 @@ To get cache tagging support, register the ``PurgeTagsListener`` and use the
229233

230234
use Toflar\Psr6HttpCacheStore\Psr6Store;
231235
use FOS\HttpCache\SymfonyCache\PurgeTagsListener;
236+
use FOS\HttpCache\SymfonyCache\CleanupCacheTagsListener;
237+
238+
const TAGS_HEADER = 'Custom-Cache-Tags-Header';
232239

233240
// ...
234241

@@ -242,12 +249,13 @@ To get cache tagging support, register the ``PurgeTagsListener`` and use the
242249
) {
243250
$store = new Psr6Store([
244251
'cache_directory' => $kernel->getCacheDir(),
245-
'cache_tags_header' => 'X-Cache-Tags',
252+
'cache_tags_header' => self::TAGS_HEADER,
246253
]);
247254

248255
parent::__construct($kernel, $store, $surrogate, $options);
249256

250257
$this->addSubscriber(new PurgeTagsListener());
258+
$this->addSubscriber(new CleanupCacheTagsListener(self::TAGS_HEADER));
251259
}
252260

253261
.. _symfony-cache user context:
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.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 FOS\HttpCache\SymfonyCache;
13+
14+
use FOS\HttpCache\TagHeaderFormatter\TagHeaderFormatter;
15+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16+
17+
/**
18+
* Listener to remove the cache tags header before the response
19+
* is delivered to the client so it's not exposed to the world.
20+
*
21+
* @author Yanick Witschi <[email protected]>
22+
*/
23+
class CleanupCacheTagsListener implements EventSubscriberInterface
24+
{
25+
/**
26+
* @var string
27+
*/
28+
private $tagsHeader;
29+
30+
/**
31+
* @param string $tagsHeader The header that is used for cache tags
32+
*/
33+
public function __construct($tagsHeader = TagHeaderFormatter::DEFAULT_HEADER_NAME)
34+
{
35+
$this->tagsHeader = $tagsHeader;
36+
}
37+
38+
public function removeTagsHeader(CacheEvent $e)
39+
{
40+
if (null === $response = $e->getResponse()) {
41+
return;
42+
}
43+
44+
$response->headers->remove($this->tagsHeader);
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public static function getSubscribedEvents()
51+
{
52+
return [
53+
Events::POST_HANDLE => 'removeTagsHeader',
54+
];
55+
}
56+
}

tests/Functional/Symfony/EventDispatchingHttpCacheTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace FOS\HttpCache\Tests\Functional\Symfony;
1313

1414
use FOS\HttpCache\SymfonyCache\CacheInvalidation;
15+
use FOS\HttpCache\SymfonyCache\CleanupCacheTagsListener;
1516
use FOS\HttpCache\SymfonyCache\CustomTtlListener;
1617
use FOS\HttpCache\SymfonyCache\DebugListener;
1718
use FOS\HttpCache\SymfonyCache\EventDispatchingHttpCache;
@@ -38,6 +39,7 @@ public function testEventListeners()
3839
$request = new Request();
3940
$expectedResponse = new Response();
4041
$expectedResponse->headers->set('X-Reverse-Proxy-TTL', 60);
42+
$expectedResponse->headers->set('X-Cache-Tags', 'foo, bar');
4143

4244
$httpKernel = \Mockery::mock(HttpKernelInterface::class)
4345
->shouldReceive('handle')
@@ -59,10 +61,12 @@ public function testEventListeners()
5961
// we already test anonymous hash lookup in the UserContextListener unit test
6062
'anonymous_hash' => 'abcdef',
6163
]));
64+
$kernel->addSubscriber(new CleanupCacheTagsListener());
6265

6366
$response = $kernel->handle($request);
6467
$this->assertSame($expectedResponse, $response);
6568
$this->assertFalse($response->headers->has('X-Reverse-Proxy-TTL'));
69+
$this->assertFalse($response->headers->has('X-Cache-Tags'));
6670
}
6771
}
6872

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.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 FOS\HttpCache\Tests\Unit\SymfonyCache;
13+
14+
use FOS\HttpCache\SymfonyCache\CacheEvent;
15+
use FOS\HttpCache\SymfonyCache\CacheInvalidation;
16+
use FOS\HttpCache\SymfonyCache\CleanupCacheTagsListener;
17+
use FOS\HttpCache\SymfonyCache\Events;
18+
use PHPUnit\Framework\TestCase;
19+
use Symfony\Component\HttpFoundation\Request;
20+
use Symfony\Component\HttpFoundation\Response;
21+
22+
/**
23+
* @author Yanick Witschi <[email protected]>
24+
*/
25+
class CleanupCacheTagsListenerTest extends TestCase
26+
{
27+
public function testSubscribedEvents()
28+
{
29+
$this->assertEquals([
30+
Events::POST_HANDLE => 'removeTagsHeader',
31+
], CleanupCacheTagsListener::getSubscribedEvents());
32+
}
33+
34+
public function testNoResponse()
35+
{
36+
$listener = new CleanupCacheTagsListener();
37+
$listener->removeTagsHeader($this->createEvent());
38+
$this->addToAssertionCount(1); // Nothing should happen, just asserting the "response is null" case
39+
}
40+
41+
public function testResponseHeaderIsCleanedUp()
42+
{
43+
// Default cache tags header
44+
$response = new Response();
45+
$response->headers->set('X-Cache-Tags', 'foo, bar');
46+
47+
$listener = new CleanupCacheTagsListener();
48+
$listener->removeTagsHeader($this->createEvent($response));
49+
50+
$this->assertFalse($response->headers->has('X-Cache-Tags'));
51+
52+
// Custom cache tags header
53+
$response = new Response();
54+
$response->headers->set('Foobar', 'foo, bar');
55+
56+
$listener = new CleanupCacheTagsListener('Foobar');
57+
$listener->removeTagsHeader($this->createEvent($response));
58+
59+
$this->assertFalse($response->headers->has('Foobar'));
60+
}
61+
62+
private function createEvent(Response $response = null)
63+
{
64+
return new CacheEvent(
65+
$this->createMock(CacheInvalidation::class),
66+
new Request(),
67+
$response
68+
);
69+
}
70+
}

0 commit comments

Comments
 (0)