Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 53 additions & 34 deletions spec/CachePluginSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;

class CachePluginSpec extends ObjectBehavior
{
Expand All @@ -33,15 +34,16 @@ function it_is_a_plugin()
$this->shouldImplement('Http\Client\Common\Plugin');
}

function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream)
function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response, StreamInterface $stream)
{
$httpBody = 'body';
$stream->__toString()->willReturn($httpBody);
$stream->isSeekable()->willReturn(true);
$stream->rewind()->shouldBeCalled();

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$response->getStatusCode()->willReturn(200);
Expand All @@ -50,7 +52,7 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();

Expand All @@ -70,17 +72,18 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i
$this->handleRequest($request, $next, function () {});
}

function it_doesnt_store_failed_responses(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response)
function it_doesnt_store_failed_responses(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response)
{
$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$response->getStatusCode()->willReturn(400);
$response->getHeader('Cache-Control')->willReturn([]);
$response->getHeader('Expires')->willReturn([]);

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);

$next = function (RequestInterface $request) use ($response) {
Expand All @@ -90,10 +93,11 @@ function it_doesnt_store_failed_responses(CacheItemPoolInterface $pool, CacheIte
$this->handleRequest($request, $next, function () {});
}

function it_doesnt_store_post_requests_by_default(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response)
function it_doesnt_store_post_requests_by_default(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response)
{
$request->getMethod()->willReturn('POST');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');

$next = function (RequestInterface $request) use ($response) {
return new FulfilledPromise($response->getWrappedObject());
Expand All @@ -106,6 +110,7 @@ function it_stores_post_requests_when_allowed(
CacheItemPoolInterface $pool,
CacheItemInterface $item,
RequestInterface $request,
UriInterface $uri,
ResponseInterface $response,
StreamFactory $streamFactory,
StreamInterface $stream
Expand All @@ -122,7 +127,8 @@ function it_stores_post_requests_when_allowed(
$stream->rewind()->shouldBeCalled();

$request->getMethod()->willReturn('POST');
$request->getUri()->willReturn('/post');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->willReturn($stream);

$response->getStatusCode()->willReturn(200);
Expand All @@ -131,7 +137,7 @@ function it_stores_post_requests_when_allowed(
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();

$pool->getItem('e4311a9af932c603b400a54efab21b6d7dea7a90')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();

Expand Down Expand Up @@ -171,15 +177,16 @@ function it_does_not_allow_invalid_request_methods(
->during('__construct', [$pool, $streamFactory, ['methods' => ['GET', 'head', 'POST']]]);
}

function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream)
function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response, StreamInterface $stream)
{
$httpBody = 'body';
$stream->__toString()->willReturn($httpBody);
$stream->isSeekable()->willReturn(true);
$stream->rewind()->shouldBeCalled();

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$response->getStatusCode()->willReturn(200);
Expand All @@ -189,7 +196,7 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
$response->getHeader('Expires')->willReturn([]);
$response->getHeader('ETag')->willReturn([]);

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);

$item->set($this->getCacheItemMatcher([
Expand All @@ -210,7 +217,7 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI
$this->handleRequest($request, $next, function () {});
}

function it_saves_etag(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream)
function it_saves_etag(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response, StreamInterface $stream)
{
$httpBody = 'body';
$stream->__toString()->willReturn($httpBody);
Expand All @@ -219,14 +226,15 @@ function it_saves_etag(CacheItemPoolInterface $pool, CacheItemInterface $item, R
$request->getBody()->shouldBeCalled();

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$response->getStatusCode()->willReturn(200);
$response->getBody()->willReturn($stream);
$response->getHeader('Cache-Control')->willReturn([]);
$response->getHeader('Expires')->willReturn([]);
$response->getHeader('ETag')->willReturn(['foo_etag']);

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);
$item->expiresAfter(1060)->willReturn($item);

Expand All @@ -246,20 +254,21 @@ function it_saves_etag(CacheItemPoolInterface $pool, CacheItemInterface $item, R
$this->handleRequest($request, $next, function () {});
}

function it_adds_etag_and_modfied_since_to_request(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream)
function it_adds_etag_and_modfied_since_to_request(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response, StreamInterface $stream)
{
$httpBody = 'body';

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$request->withHeader('If-Modified-Since', 'Thursday, 01-Jan-70 01:18:31 GMT')->shouldBeCalled()->willReturn($request);
$request->withHeader('If-None-Match', 'foo_etag')->shouldBeCalled()->willReturn($request);

$response->getStatusCode()->willReturn(304);

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(true, false);
$item->get()->willReturn([
'response' => $response,
Expand All @@ -276,15 +285,16 @@ function it_adds_etag_and_modfied_since_to_request(CacheItemPoolInterface $pool,
$this->handleRequest($request, $next, function () {});
}

function it_servces_a_cached_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream, StreamFactory $streamFactory)
function it_servces_a_cached_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response, StreamInterface $stream, StreamFactory $streamFactory)
{
$httpBody = 'body';

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(true);
$item->get()->willReturn([
'response' => $response,
Expand All @@ -305,12 +315,13 @@ function it_servces_a_cached_response(CacheItemPoolInterface $pool, CacheItemInt
$this->handleRequest($request, $next, function () {});
}

function it_serves_and_resaved_expired_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, ResponseInterface $response, StreamInterface $stream, StreamFactory $streamFactory)
function it_serves_and_resaved_expired_response(CacheItemPoolInterface $pool, CacheItemInterface $item, RequestInterface $request, UriInterface $uri, ResponseInterface $response, StreamInterface $stream, StreamFactory $streamFactory)
{
$httpBody = 'body';

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$request->withHeader(Argument::any(), Argument::any())->willReturn($request);
Expand All @@ -323,7 +334,7 @@ function it_serves_and_resaved_expired_response(CacheItemPoolInterface $pool, Ca
// Make sure we add back the body
$response->withBody($stream)->willReturn($response)->shouldBeCalled();

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(true, true);
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();
$item->get()->willReturn([
Expand Down Expand Up @@ -356,6 +367,7 @@ function it_caches_private_responses_when_allowed(
CacheItemPoolInterface $pool,
CacheItemInterface $item,
RequestInterface $request,
UriInterface $uri,
ResponseInterface $response,
StreamFactory $streamFactory,
StreamInterface $stream
Expand All @@ -371,7 +383,8 @@ function it_caches_private_responses_when_allowed(
$stream->rewind()->shouldBeCalled();

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$response->getStatusCode()->willReturn(200);
Expand All @@ -380,7 +393,7 @@ function it_caches_private_responses_when_allowed(
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();

Expand All @@ -404,29 +417,31 @@ function it_does_not_store_responses_of_requests_to_blacklisted_paths(
CacheItemPoolInterface $pool,
CacheItemInterface $item,
RequestInterface $request,
UriInterface $uri,
ResponseInterface $response,
StreamFactory $streamFactory,
StreamInterface $stream
) {
$this->beConstructedThrough('clientCache', [$pool, $streamFactory, [
'default_ttl' => 60,
'cache_lifetime' => 1000,
'blacklisted_paths' => ['\/foo']
'blacklisted_paths' => ['@/foo@']
]]);

$httpBody = 'body';
$stream->__toString()->willReturn($httpBody);
$stream->isSeekable()->willReturn(true);

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/foo');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/foo');
$request->getBody()->shouldBeCalled();

$response->getStatusCode()->willReturn(200);
$response->getBody()->willReturn($stream);
$response->getHeader('Cache-Control')->willReturn([])->shouldBeCalled();

$pool->getItem('231392a16d98e1cf631845c79b7d45f40bab08f3')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);

$item->set($this->getCacheItemMatcher([
Expand All @@ -448,14 +463,15 @@ function it_stores_responses_of_requests_not_in_blacklisted_paths(
CacheItemPoolInterface $pool,
CacheItemInterface $item,
RequestInterface $request,
UriInterface $uri,
ResponseInterface $response,
StreamFactory $streamFactory,
StreamInterface $stream
) {
$this->beConstructedThrough('clientCache', [$pool, $streamFactory, [
'default_ttl' => 60,
'cache_lifetime' => 1000,
'blacklisted_paths' => ['\/foo']
'blacklisted_paths' => ['@/foo@']
]]);

$httpBody = 'body';
Expand All @@ -464,7 +480,8 @@ function it_stores_responses_of_requests_not_in_blacklisted_paths(
$stream->rewind()->shouldBeCalled();

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$request->getBody()->shouldBeCalled();

$response->getStatusCode()->willReturn(200);
Expand All @@ -473,7 +490,7 @@ function it_stores_responses_of_requests_not_in_blacklisted_paths(
$response->getHeader('Expires')->willReturn([])->shouldBeCalled();
$response->getHeader('ETag')->willReturn([])->shouldBeCalled();

$pool->getItem('d20f64acc6e70b6079845f2fe357732929550ae1')->shouldBeCalled()->willReturn($item);
$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
$item->isHit()->willReturn(false);
$item->expiresAfter(1060)->willReturn($item)->shouldBeCalled();

Expand All @@ -498,6 +515,7 @@ function it_can_be_initialized_with_custom_cache_key_generator(
CacheItemInterface $item,
StreamFactory $streamFactory,
RequestInterface $request,
UriInterface $uri,
ResponseInterface $response,
StreamInterface $stream,
SimpleGenerator $generator
Expand All @@ -513,7 +531,8 @@ function it_can_be_initialized_with_custom_cache_key_generator(
$streamFactory->createStream(Argument::any())->willReturn($stream);

$request->getMethod()->willReturn('GET');
$request->getUri()->willReturn('/');
$request->getUri()->willReturn($uri);
$uri->__toString()->willReturn('https://example.com/');
$response->withBody(Argument::any())->willReturn($response);

$pool->getItem(Argument::any())->shouldBeCalled()->willReturn($item);
Expand Down
7 changes: 4 additions & 3 deletions src/CachePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ final class CachePlugin implements Plugin
* we have to store the cache for a longer time than the server originally says it is valid for.
* We store a cache item for $cache_lifetime + max age of the response.
* @var array $methods list of request methods which can be cached
* @var array $blacklisted_paths list of regex patterns of paths explicitly not to be cached
* @var array $blacklisted_paths list of regex of to URLs explicitly not to be cached
* @var array $respect_response_cache_directives list of cache directives this plugin will respect while caching responses
* @var CacheKeyGenerator $cache_key_generator an object to generate the cache key. Defaults to a new instance of SimpleGenerator
* @var CacheListener[] $cache_listeners an array of objects to act on the response based on the results of the cache check.
Expand Down Expand Up @@ -280,8 +280,9 @@ protected function isCacheable(ResponseInterface $response)
*/
private function isCacheableRequest(RequestInterface $request)
{
foreach ($this->config['blacklisted_paths'] as $not_to_cache_path) {
if (1 === preg_match('/'.$not_to_cache_path.'/', $request->getUri())) {
$uri = $request->getUri()->__toString();
foreach ($this->config['blacklisted_paths'] as $regex) {
if (1 === preg_match($regex, $uri)) {
return false;
}
}
Expand Down