Skip to content

Commit

Permalink
Merge pull request #273 from wickedOne/header-length
Browse files Browse the repository at this point in the history
[cache tagging] http_resp_hdr_len check
  • Loading branch information
dbu committed Feb 2, 2016
2 parents eb24688 + 1ea0cf7 commit 783f391
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 11 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
-----
Expand All @@ -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
Expand Down
9 changes: 7 additions & 2 deletions src/CacheInvalidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ class CacheInvalidator
*/
private $tagsHeader = 'X-Cache-Tags';

/**
* @var int
*/
private $headerLength = 7500;

/**
* Constructor
*
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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;
Expand Down
44 changes: 38 additions & 6 deletions src/Handler/TagHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class TagHandler
*/
private $tagsHeader;

/**
* @var int
*/
private $headerLength;

/**
* @var array
*/
Expand All @@ -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;
}

/**
Expand All @@ -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.
*
Expand Down Expand Up @@ -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;
}
Expand Down
28 changes: 28 additions & 0 deletions tests/Unit/Handler/TagHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

0 comments on commit 783f391

Please sign in to comment.