Skip to content

Commit 783f391

Browse files
committed
Merge pull request #273 from wickedOne/header-length
[cache tagging] http_resp_hdr_len check
2 parents eb24688 + 1ea0cf7 commit 783f391

File tree

4 files changed

+82
-11
lines changed

4 files changed

+82
-11
lines changed

CHANGELOG.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
Changelog
22
=========
3+
1.4.2
4+
-----
5+
6+
* The TagHandler constructor now accepts a ``headerLenght`` argument which will
7+
cause it's ``invalidateTags`` function to invalidate in batches if the header
8+
length exceeds this value.
39

410
1.3.3
511
-----
@@ -11,9 +17,9 @@ Changelog
1117

1218
* Added [TagHandler](http://foshttpcache.readthedocs.org/en/latest/invalidation-handlers.html#tag-handler).
1319
* It is no longer possible to change the event dispatcher of the
14-
CacheInvalidator once its instantiated. If you need a custom dispatcher, set
15-
it right after creating the invalidator instance.
16-
* Deprecated `CacheInvalidator::addSubscriber` in favor of either using the event
20+
CacheInvalidator once its instantiated. If you need a custom dispatcher, set
21+
it right after creating the invalidator instance.
22+
* Deprecated `CacheInvalidator::addSubscriber` in favor of either using the event
1723
dispatcher instance you inject or doing `getEventDispatcher()->addSubscriber($subscriber)`.
1824

1925
1.2.0

src/CacheInvalidator.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ class CacheInvalidator
7070
*/
7171
private $tagsHeader = 'X-Cache-Tags';
7272

73+
/**
74+
* @var int
75+
*/
76+
private $headerLength = 7500;
77+
7378
/**
7479
* Constructor
7580
*
@@ -79,7 +84,7 @@ public function __construct(ProxyClientInterface $cache)
7984
{
8085
$this->cache = $cache;
8186
if ($cache instanceof BanInterface) {
82-
$this->tagHandler = new TagHandler($this, $this->tagsHeader);
87+
$this->tagHandler = new TagHandler($this, $this->tagsHeader, $this->headerLength);
8388
}
8489
}
8590

@@ -172,7 +177,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber)
172177
public function setTagsHeader($tagsHeader)
173178
{
174179
if ($this->tagHandler && $this->tagHandler->getTagsHeaderName() !== $tagsHeader) {
175-
$this->tagHandler = new TagHandler($this, $tagsHeader);
180+
$this->tagHandler = new TagHandler($this, $tagsHeader, $this->headerLength);
176181
}
177182

178183
return $this;

src/Handler/TagHandler.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class TagHandler
3232
*/
3333
private $tagsHeader;
3434

35+
/**
36+
* @var int
37+
*/
38+
private $headerLength;
39+
3540
/**
3641
* @var array
3742
*/
@@ -40,18 +45,20 @@ class TagHandler
4045
/**
4146
* Constructor
4247
*
43-
* @param CacheInvalidator $invalidator The invalidator instance.
44-
* @param string $tagsHeader Header to use for tags, defaults to X-Cache-Tags.
48+
* @param CacheInvalidator $invalidator The invalidator instance.
49+
* @param string $tagsHeader Header to use for tags, defaults to X-Cache-Tags.
50+
* @param int $headerLength Maximum header size in bytes, defaults to 7500.
4551
*
4652
* @throws UnsupportedProxyOperationException If CacheInvalidator does not support invalidate requests
4753
*/
48-
public function __construct(CacheInvalidator $invalidator, $tagsHeader = 'X-Cache-Tags')
54+
public function __construct(CacheInvalidator $invalidator, $tagsHeader = 'X-Cache-Tags', $headerLength = 7500)
4955
{
5056
if (!$invalidator->supports(CacheInvalidator::INVALIDATE)) {
5157
throw UnsupportedProxyOperationException::cacheDoesNotImplement('BAN');
5258
}
5359
$this->invalidator = $invalidator;
5460
$this->tagsHeader = $tagsHeader;
61+
$this->headerLength = $headerLength;
5562
}
5663

5764
/**
@@ -64,6 +71,16 @@ public function getTagsHeaderName()
6471
return $this->tagsHeader;
6572
}
6673

74+
/**
75+
* Get the maximum HTTP header length.
76+
*
77+
* @return int
78+
*/
79+
public function getHeaderLength()
80+
{
81+
return $this->headerLength;
82+
}
83+
6784
/**
6885
* Get the value for the HTTP tag header.
6986
*
@@ -112,9 +129,24 @@ public function addTags(array $tags)
112129
*/
113130
public function invalidateTags(array $tags)
114131
{
115-
$tagExpression = sprintf('(%s)(,.+)?$', implode('|', array_map('preg_quote', $this->escapeTags($tags))));
116-
$headers = array($this->tagsHeader => $tagExpression);
117-
$this->invalidator->invalidate($headers);
132+
$escapedTags = array_map('preg_quote', $this->escapeTags($tags));
133+
134+
if (mb_strlen(implode('|', $escapedTags)) >= $this->headerLength) {
135+
/*
136+
* estimate the amount of tags to invalidate by dividing the max
137+
* header length by the largest tag (minus 1 for the implode character)
138+
*/
139+
$tagsize = max(array_map('mb_strlen', $escapedTags));
140+
$elems = floor($this->headerLength / ($tagsize - 1)) ? : 1;
141+
} else {
142+
$elems = count($escapedTags);
143+
}
144+
145+
foreach (array_chunk($escapedTags, $elems) as $tagchunk) {
146+
$tagExpression = sprintf('(%s)(,.+)?$', implode('|', $tagchunk));
147+
$headers = array($this->tagsHeader => $tagExpression);
148+
$this->invalidator->invalidate($headers);
149+
}
118150

119151
return $this;
120152
}

tests/Unit/Handler/TagHandlerTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,32 @@ public function testTagResponse()
9595
$this->assertTrue($tagHandler->hasTags());
9696
$this->assertEquals('post-1,test_post', $tagHandler->getTagsHeaderValue());
9797
}
98+
99+
public function testInvalidateTwice()
100+
{
101+
$cacheInvalidator = \Mockery::mock('FOS\HttpCache\CacheInvalidator')
102+
->shouldReceive('supports')
103+
->with(CacheInvalidator::INVALIDATE)
104+
->once()
105+
->andReturn(true)
106+
->shouldReceive('invalidate')
107+
->twice()
108+
->getMock();
109+
110+
$tagHandler = new TagHandler($cacheInvalidator, 'X-Cache-Tags', 7);
111+
$tagHandler->invalidateTags(array('post-1', 'post-2'));
112+
}
113+
114+
public function testHeaderLength()
115+
{
116+
$cacheInvalidator = \Mockery::mock('FOS\HttpCache\CacheInvalidator')
117+
->shouldReceive('supports')
118+
->with(CacheInvalidator::INVALIDATE)
119+
->once()
120+
->andReturn(true)
121+
->getMock();
122+
123+
$tagHandler = new TagHandler($cacheInvalidator, 'X-Cache-Tags', 8000);
124+
$this->assertEquals(8000, $tagHandler->getHeaderLength());
125+
}
98126
}

0 commit comments

Comments
 (0)