Skip to content

Commit 5c1599c

Browse files
authored
Merge pull request #61 from nobYsDarling/feature/blacklisted-paths-25112019
Added blacklisted_paths option
2 parents 0f75407 + 61aab95 commit 5c1599c

File tree

3 files changed

+121
-5
lines changed

3 files changed

+121
-5
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Change Log
22

3+
## 1.7.0 - unreleased
4+
5+
### Added
6+
7+
* Added `blacklisted_paths` option, which takes an array of `strings` (regular expressions) and allows to define paths, that shall not be cached in any case.
8+
39
## 1.6.0 - 2019-01-23
410

511
### Added

spec/CachePluginSpec.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,98 @@ function it_caches_private_responses_when_allowed(
400400
$this->handleRequest($request, $next, function () {});
401401
}
402402

403+
function it_does_not_store_responses_of_requests_to_blacklisted_paths(
404+
CacheItemPoolInterface $pool,
405+
CacheItemInterface $item,
406+
RequestInterface $request,
407+
ResponseInterface $response,
408+
StreamFactory $streamFactory,
409+
StreamInterface $stream
410+
) {
411+
$this->beConstructedThrough('clientCache', [$pool, $streamFactory, [
412+
'default_ttl' => 60,
413+
'cache_lifetime' => 1000,
414+
'blacklisted_paths' => ['\/foo']
415+
]]);
416+
417+
$httpBody = 'body';
418+
$stream->__toString()->willReturn($httpBody);
419+
$stream->isSeekable()->willReturn(true);
420+
421+
$request->getMethod()->willReturn('GET');
422+
$request->getUri()->willReturn('/foo');
423+
$request->getBody()->shouldBeCalled();
424+
425+
$response->getStatusCode()->willReturn(200);
426+
$response->getBody()->willReturn($stream);
427+
$response->getHeader('Cache-Control')->willReturn([])->shouldBeCalled();
428+
429+
$pool->getItem('231392a16d98e1cf631845c79b7d45f40bab08f3')->shouldBeCalled()->willReturn($item);
430+
$item->isHit()->willReturn(false);
431+
432+
$item->set($this->getCacheItemMatcher([
433+
'response' => $response->getWrappedObject(),
434+
'body' => $httpBody,
435+
'expiresAt' => 0,
436+
'createdAt' => 0
437+
]))->willReturn($item)->shouldNotBeCalled();
438+
$pool->save(Argument::any())->shouldNotBeCalled();
439+
440+
$next = function (RequestInterface $request) use ($response) {
441+
return new FulfilledPromise($response->getWrappedObject());
442+
};
443+
444+
$this->handleRequest($request, $next, function () {});
445+
}
446+
447+
function it_stores_responses_of_requests_not_in_blacklisted_paths(
448+
CacheItemPoolInterface $pool,
449+
CacheItemInterface $item,
450+
RequestInterface $request,
451+
ResponseInterface $response,
452+
StreamFactory $streamFactory,
453+
StreamInterface $stream
454+
) {
455+
$this->beConstructedThrough('clientCache', [$pool, $streamFactory, [
456+
'default_ttl' => 60,
457+
'cache_lifetime' => 1000,
458+
'blacklisted_paths' => ['\/foo']
459+
]]);
460+
461+
$httpBody = 'body';
462+
$stream->__toString()->willReturn($httpBody);
463+
$stream->isSeekable()->willReturn(true);
464+
$stream->rewind()->shouldBeCalled();
465+
466+
$request->getMethod()->willReturn('GET');
467+
$request->getUri()->willReturn('/');
468+
$request->getBody()->shouldBeCalled();
469+
470+
$response->getStatusCode()->willReturn(200);
471+
$response->getBody()->willReturn($stream);
472+
$response->getHeader('Cache-Control')->willReturn([])->shouldBeCalled();
473+
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
474+
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();
475+
476+
$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
477+
$item->isHit()->willReturn(false);
478+
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();
479+
480+
$item->set($this->getCacheItemMatcher([
481+
'response' => $response->getWrappedObject(),
482+
'body' => $httpBody,
483+
'expiresAt' => 0,
484+
'createdAt' => 0,
485+
'etag' => []
486+
]))->willReturn($item)->shouldBeCalled();
487+
$pool->save(Argument::any())->shouldBeCalled();
488+
489+
$next = function (RequestInterface $request) use ($response) {
490+
return new FulfilledPromise($response->getWrappedObject());
491+
};
492+
493+
$this->handleRequest($request, $next, function () {});
494+
}
403495

404496
function it_can_be_initialized_with_custom_cache_key_generator(
405497
CacheItemPoolInterface $pool,

src/CachePlugin.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ final class CachePlugin implements Plugin
6060
* we have to store the cache for a longer time than the server originally says it is valid for.
6161
* We store a cache item for $cache_lifetime + max age of the response.
6262
* @var array $methods list of request methods which can be cached
63+
* @var array $blacklisted_paths list of regex patterns of paths explicitly not to be cached
6364
* @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses
6465
* @var CacheKeyGenerator $cache_key_generator an object to generate the cache key. Defaults to a new instance of SimpleGenerator
6566
* @var CacheListener[] $cache_listeners an array of objects to act on the response based on the results of the cache check.
@@ -72,10 +73,7 @@ public function __construct(CacheItemPoolInterface $pool, StreamFactory $streamF
7273
$this->streamFactory = $streamFactory;
7374

7475
if (isset($config['respect_cache_headers']) && isset($config['respect_response_cache_directives'])) {
75-
throw new \InvalidArgumentException(
76-
'You can\'t provide config option "respect_cache_headers" and "respect_response_cache_directives". '.
77-
'Use "respect_response_cache_directives" instead.'
78-
);
76+
throw new \InvalidArgumentException('You can\'t provide config option "respect_cache_headers" and "respect_response_cache_directives". Use "respect_response_cache_directives" instead.');
7977
}
8078

8179
$optionsResolver = new OptionsResolver();
@@ -183,7 +181,7 @@ protected function doHandleRequest(RequestInterface $request, callable $next, ca
183181
return $this->handleCacheListeners($request, $this->createResponseFromCacheItem($cacheItem), true, $cacheItem);
184182
}
185183

186-
if ($this->isCacheable($response)) {
184+
if ($this->isCacheable($response) && $this->isCacheableRequest($request)) {
187185
$bodyStream = $response->getBody();
188186
$body = $bodyStream->__toString();
189187
if ($bodyStream->isSeekable()) {
@@ -266,6 +264,24 @@ protected function isCacheable(ResponseInterface $response)
266264
return true;
267265
}
268266

267+
/**
268+
* Verify that we can cache this request.
269+
*
270+
* @param RequestInterface $request
271+
*
272+
* @return bool
273+
*/
274+
private function isCacheableRequest(RequestInterface $request)
275+
{
276+
foreach ($this->config['blacklisted_paths'] as $not_to_cache_path) {
277+
if (1 === preg_match('/'.$not_to_cache_path.'/', $request->getUri())) {
278+
return false;
279+
}
280+
}
281+
282+
return true;
283+
}
284+
269285
/**
270286
* Get the value of a parameter in the cache control header.
271287
*
@@ -353,13 +369,15 @@ private function configureOptions(OptionsResolver $resolver)
353369
'respect_response_cache_directives' => ['no-cache', 'private', 'max-age', 'no-store'],
354370
'cache_key_generator' => null,
355371
'cache_listeners' => [],
372+
'blacklisted_paths' => [],
356373
]);
357374

358375
$resolver->setAllowedTypes('cache_lifetime', ['int', 'null']);
359376
$resolver->setAllowedTypes('default_ttl', ['int', 'null']);
360377
$resolver->setAllowedTypes('respect_cache_headers', ['bool', 'null']);
361378
$resolver->setAllowedTypes('methods', 'array');
362379
$resolver->setAllowedTypes('cache_key_generator', ['null', 'Http\Client\Common\Plugin\Cache\Generator\CacheKeyGenerator']);
380+
$resolver->setAllowedTypes('blacklisted_paths', 'array');
363381
$resolver->setAllowedValues('hash_algo', hash_algos());
364382
$resolver->setAllowedValues('methods', function ($value) {
365383
/* RFC7230 sections 3.1.1 and 3.2.6 except limited to uppercase characters. */

0 commit comments

Comments
 (0)