From 1ea0cf7d23820f2dad5b8c204f61280ac5528d76 Mon Sep 17 00:00:00 2001 From: wickedOne Date: Thu, 28 Jan 2016 14:35:23 +0100 Subject: [PATCH] [cache tagging] http_resp_hdr_len check fixes #269 --- CHANGELOG.md | 12 ++++++-- src/CacheInvalidator.php | 9 ++++-- src/Handler/TagHandler.php | 44 +++++++++++++++++++++++---- tests/Unit/Handler/TagHandlerTest.php | 28 +++++++++++++++++ 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0edd966e..8d27efba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ Changelog ========= +1.4.2 +----- + +* The TagHandler constructor now accepts a ``headerLenght`` argument which will + cause it's ``invalidateTags`` function to invalidate in batches if the header + length exceeds this value. 1.3.3 ----- @@ -11,9 +17,9 @@ Changelog * Added [TagHandler](http://foshttpcache.readthedocs.org/en/latest/invalidation-handlers.html#tag-handler). * It is no longer possible to change the event dispatcher of the - CacheInvalidator once its instantiated. If you need a custom dispatcher, set - it right after creating the invalidator instance. -* Deprecated `CacheInvalidator::addSubscriber` in favor of either using the event + CacheInvalidator once its instantiated. If you need a custom dispatcher, set + it right after creating the invalidator instance. +* Deprecated `CacheInvalidator::addSubscriber` in favor of either using the event dispatcher instance you inject or doing `getEventDispatcher()->addSubscriber($subscriber)`. 1.2.0 diff --git a/src/CacheInvalidator.php b/src/CacheInvalidator.php index 11f517f8..4027c9e1 100644 --- a/src/CacheInvalidator.php +++ b/src/CacheInvalidator.php @@ -70,6 +70,11 @@ class CacheInvalidator */ private $tagsHeader = 'X-Cache-Tags'; + /** + * @var int + */ + private $headerLength = 7500; + /** * Constructor * @@ -79,7 +84,7 @@ public function __construct(ProxyClientInterface $cache) { $this->cache = $cache; if ($cache instanceof BanInterface) { - $this->tagHandler = new TagHandler($this, $this->tagsHeader); + $this->tagHandler = new TagHandler($this, $this->tagsHeader, $this->headerLength); } } @@ -172,7 +177,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber) public function setTagsHeader($tagsHeader) { if ($this->tagHandler && $this->tagHandler->getTagsHeaderName() !== $tagsHeader) { - $this->tagHandler = new TagHandler($this, $tagsHeader); + $this->tagHandler = new TagHandler($this, $tagsHeader, $this->headerLength); } return $this; diff --git a/src/Handler/TagHandler.php b/src/Handler/TagHandler.php index cf03cd63..a8c43a7d 100644 --- a/src/Handler/TagHandler.php +++ b/src/Handler/TagHandler.php @@ -32,6 +32,11 @@ class TagHandler */ private $tagsHeader; + /** + * @var int + */ + private $headerLength; + /** * @var array */ @@ -40,18 +45,20 @@ class TagHandler /** * Constructor * - * @param CacheInvalidator $invalidator The invalidator instance. - * @param string $tagsHeader Header to use for tags, defaults to X-Cache-Tags. + * @param CacheInvalidator $invalidator The invalidator instance. + * @param string $tagsHeader Header to use for tags, defaults to X-Cache-Tags. + * @param int $headerLength Maximum header size in bytes, defaults to 7500. * * @throws UnsupportedProxyOperationException If CacheInvalidator does not support invalidate requests */ - public function __construct(CacheInvalidator $invalidator, $tagsHeader = 'X-Cache-Tags') + public function __construct(CacheInvalidator $invalidator, $tagsHeader = 'X-Cache-Tags', $headerLength = 7500) { if (!$invalidator->supports(CacheInvalidator::INVALIDATE)) { throw UnsupportedProxyOperationException::cacheDoesNotImplement('BAN'); } $this->invalidator = $invalidator; $this->tagsHeader = $tagsHeader; + $this->headerLength = $headerLength; } /** @@ -64,6 +71,16 @@ public function getTagsHeaderName() return $this->tagsHeader; } + /** + * Get the maximum HTTP header length. + * + * @return int + */ + public function getHeaderLength() + { + return $this->headerLength; + } + /** * Get the value for the HTTP tag header. * @@ -112,9 +129,24 @@ public function addTags(array $tags) */ public function invalidateTags(array $tags) { - $tagExpression = sprintf('(%s)(,.+)?$', implode('|', array_map('preg_quote', $this->escapeTags($tags)))); - $headers = array($this->tagsHeader => $tagExpression); - $this->invalidator->invalidate($headers); + $escapedTags = array_map('preg_quote', $this->escapeTags($tags)); + + if (mb_strlen(implode('|', $escapedTags)) >= $this->headerLength) { + /* + * estimate the amount of tags to invalidate by dividing the max + * header length by the largest tag (minus 1 for the implode character) + */ + $tagsize = max(array_map('mb_strlen', $escapedTags)); + $elems = floor($this->headerLength / ($tagsize - 1)) ? : 1; + } else { + $elems = count($escapedTags); + } + + foreach (array_chunk($escapedTags, $elems) as $tagchunk) { + $tagExpression = sprintf('(%s)(,.+)?$', implode('|', $tagchunk)); + $headers = array($this->tagsHeader => $tagExpression); + $this->invalidator->invalidate($headers); + } return $this; } diff --git a/tests/Unit/Handler/TagHandlerTest.php b/tests/Unit/Handler/TagHandlerTest.php index ac56be2e..1f7fd57a 100644 --- a/tests/Unit/Handler/TagHandlerTest.php +++ b/tests/Unit/Handler/TagHandlerTest.php @@ -95,4 +95,32 @@ public function testTagResponse() $this->assertTrue($tagHandler->hasTags()); $this->assertEquals('post-1,test_post', $tagHandler->getTagsHeaderValue()); } + + public function testInvalidateTwice() + { + $cacheInvalidator = \Mockery::mock('FOS\HttpCache\CacheInvalidator') + ->shouldReceive('supports') + ->with(CacheInvalidator::INVALIDATE) + ->once() + ->andReturn(true) + ->shouldReceive('invalidate') + ->twice() + ->getMock(); + + $tagHandler = new TagHandler($cacheInvalidator, 'X-Cache-Tags', 7); + $tagHandler->invalidateTags(array('post-1', 'post-2')); + } + + public function testHeaderLength() + { + $cacheInvalidator = \Mockery::mock('FOS\HttpCache\CacheInvalidator') + ->shouldReceive('supports') + ->with(CacheInvalidator::INVALIDATE) + ->once() + ->andReturn(true) + ->getMock(); + + $tagHandler = new TagHandler($cacheInvalidator, 'X-Cache-Tags', 8000); + $this->assertEquals(8000, $tagHandler->getHeaderLength()); + } }