8000 Implemented header value splitting · FriendsOfSymfony/FOSHttpCache@b452710 · GitHub
[go: up one dir, main page]

Skip to content

Commit b452710

Browse files
committed
Implemented header value splitting
1 parent fdd605d commit b452710

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed

src/ProxyClient/HttpProxyClient.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,50 @@ protected function escapeTags(array $tags)
114114

115115
return $tags;
116116
}
117+
118+
/**
119+
* Splits a header value into an array of values. E.g. useful for tag
120+
* invalidation requests where you might need multiple requests if you
121+
* want to invalidate too many cache tags (so the header would get too long).
122+
*
123+
* @param string $value
124+
* @param int $length
125+
* @param string $delimiter
126+
*
127+
* @return array
128+
*/
129+
protected function splitLongHeaderValue($value, $length = 7500, $delimiter = ',')
130+
{
131+
if (mb_strlen($value) <= $length) {
132+
133+
return [$value];
134+
}
135+
136+
$tmp = [];
137+
$chunks = explode($delimiter, $value);
138+
$currentLength = 0;
139+
$index = 0;
140+
141+
foreach ($chunks as $chunk) {
142+
143+
$chunkLength = mb_strlen($chunk) + 1;
144+
145+
if (($currentLength + $chunkLength) <= $length) {
146+
147+
$tmp[$index][] = $chunk;
148+
$currentLength += $chunkLength;
149+
} else {
150+
$index++;
151+
$currentLength = $chunkLength;
152+
$tmp[$index][] = $chunk;
153+
}
154+
}
155+
156+
$result = [];
157+
foreach ($tmp as $values) {
158+
$result[] = implode($delimiter, $values);
159+
}
160+
161+
return $result;
162+
}
117163
}

src/ProxyClient/Symfony.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@ protected function configureOptions()
5656
$resolver = parent::configureOptions();
5757
$resolver->setDefault('purge_method', PurgeListener::DEFAULT_PURGE_METHOD);
5858
$resolver->setAllowedTypes('purge_method', 'string');
59+
$resolver->setDefault('purge_tags_method', PurgeTagsListener::DEFAULT_PURGE_TAGS_METHOD);
60+
$resolver->setAllowedTypes('purge_tags_method', 'string');
5961
$resolver->setDefault('purge_tags_header', PurgeTagsListener::DEFAULT_PURGE_TAGS_HEADER);
6062
$resolver->setAllowedTypes('purge_tags_header', 'string');
63+
$resolver->setDefault('purge_tags_header_length', 7500);
64+
$resolver->setAllowedTypes('purge_tags_header_length', 'int');
6165

6266
return $resolver;
6367
}
@@ -73,6 +77,14 @@ public function invalidateTags(array $tags)
7377
{
7478
$escapedTags = $this->escapeTags($tags);
7579

76-
$this->purge('/', [$this->options['purge_tags_header'] => implode(',', $escapedTags)]);
80+
$chunks = $this->splitLongHeaderValue(implode(',', $escapedTags), $this->options['purge_tags_header_length']);
81+
82+
foreach ($chunks as $chunk) {
83+
$this->queueRequest(
84+
$this->options['purge_tags_method'],
85+
'/',
86+
[$this->options['purge_tags_header'] => $chunk]
87+
);
88+
}
7789
}
7890
}

tests/Unit/ProxyClient/SymfonyTest.php

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function testInvalidateTags()
5959
$this->httpDispatcher->shouldReceive('invalidate')->once()->with(
6060
\Mockery::on(
6161
function (RequestInterface $request) {
62-
$this->assertEquals('PURGE', $request->getMethod());
62+
$this->assertEquals('PURGETAGS', $request->getMethod());
6363

6464
$this->assertEquals('/', $request->getUri());
6565
$this->assertContains('foobar,other tag', $request->getHeaderLine('X-Cache-Tags'));
@@ -73,6 +73,73 @@ function (RequestInterface $request) {
7373
$symfony->invalidateTags(['foobar', 'other tag']);
7474
}
7575

76+
public function testInvalidateTagsWithALotOfTags()
77+
{
78+
$dispatcher = $this->createMock(HttpDispatcher::class);
79+
$dispatcher
80+
->expects($this->exactly(3))
81+
->method('invalidate')
82+
->withConsecutive(
83+
[
84+
$this->callback(function (RequestInterface $request) {
85+
$this->assertEquals('PURGETAGS', $request->getMethod());
86+
$this->assertContains('foobar,foobar1,foobar2,foobar3,foobar4,foobar5,foobar6,foobar7,foobar8,foobar9,foobar10,foobar11', $request->getHeaderLine('X-Cache-Tags'));
87+
88+
return true;
89+
}),
90+
true
91+
],
92+
[
93+
$this->callback(function (RequestInterface $request) {
94+
$this->assertEquals('PURGETAGS', $request->getMethod());
95+
$this->assertContains('foobar12,foobar13,foobar14,foobar15,foobar16,foobar17,foobar18,foobar19,foobar20,foobar22,foobar23', $request->getHeaderLine('X-Cache-Tags'));
96+
97+
return true;
98+
}),
99+
true
100+
],
101+
[
102+
$this->callback(function (RequestInterface $request) {
103+
$this->assertEquals('PURGETAGS', $request->getMethod());
104+
$this->assertContains('foobar24,foobar25', $request->getHeaderLine('X-Cache-Tags'));
105+
106+
return true;
107+
}),
108+
true
109+
]
110+
);
111+
112+
$symfony = new Symfony($dispatcher, ['purge_tags_header_length' => 100]);
113+
114+
$symfony->invalidateTags([
115+
'foobar',
116+
'foobar1',
117+
'foobar2',
118+
'foobar3',
119+
'foobar4',
120+
'foobar5',
121+
'foobar6',
122+
'foobar7',
123+
'foobar8',
124+
'foobar9',
125+
'foobar10',
126+
'foobar11',
127+
'foobar12',
128+
'foobar13',
129+
'foobar14',
130+
'foobar15',
131+
'foobar16',
132+
'foobar17',
133+
'foobar18',
134+
'foobar19',
135+
'foobar20',
136+
'foobar22',
137+
'foobar23',
138+
'foobar24',
139+
'foobar25',
140+
]);
141+
}
142+
76143
public function testRefresh()
77144
{
78145
$symfony = new Symfony($this->httpDispatcher);

0 commit comments

Comments
 (0)
0