Skip to content

Commit

Permalink
Merge pull request #157 from rhysemmerson/invalidate-items-for-all-me…
Browse files Browse the repository at this point in the history
…thods

Invalidate items for all methods on unsafe method
  • Loading branch information
Kevinrob authored Feb 7, 2022
2 parents fda11c5 + 1830d78 commit 22d79bb
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 18 deletions.
19 changes: 16 additions & 3 deletions src/CacheMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ class CacheMiddleware
*/
protected $httpMethods = ['GET' => true];

/**
* List of safe methods
*
* https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1
*
* @var array
*/
protected $safeMethods = ['GET' => true, 'HEAD' => true, 'OPTIONS' => true, 'TRACE' => true];

/**
* @param CacheStrategyInterface|null $cacheStrategy
*/
Expand Down Expand Up @@ -117,8 +126,10 @@ public function __invoke(callable $handler)

return $handler($request, $options)->then(
function (ResponseInterface $response) use ($request) {
// Invalidate cache after a call of non-safe method on the same URI
$response = $this->invalidateCache($request, $response);
if (!isset($this->safeMethods[$request->getMethod()])) {
// Invalidate cache after a call of non-safe method on the same URI
$response = $this->invalidateCache($request, $response);
}

return $response->withHeader(self::HEADER_CACHE_INFO, self::HEADER_CACHE_MISS);
}
Expand Down Expand Up @@ -375,7 +386,9 @@ public static function getMiddleware(CacheStrategyInterface $cacheStorage = null
*/
private function invalidateCache(RequestInterface $request, ResponseInterface $response)
{
$this->cacheStorage->delete($request);
foreach (array_keys($this->httpMethods) as $method) {
$this->cacheStorage->delete($request->withMethod($method));
}

return $response->withHeader(self::HEADER_INVALIDATION, true);
}
Expand Down
79 changes: 64 additions & 15 deletions tests/InvalidateCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

namespace Kevinrob\GuzzleCache\Tests;

use Cache\Adapter\PHPArray\ArrayCachePool;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise\FulfilledPromise;
use GuzzleHttp\Psr7\Response;
use Kevinrob\GuzzleCache\CacheMiddleware;
use Kevinrob\GuzzleCache\Storage\Psr6CacheStorage;
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
use PHPUnit\Framework\TestCase;

class InvalidateCacheTest extends TestCase
Expand All @@ -16,35 +19,81 @@ class InvalidateCacheTest extends TestCase
*/
protected $client;

/**
* @var CacheMiddleware
*/
protected $middleware;

protected function setUp(): void
{
// Create default HandlerStack
$stack = HandlerStack::create(function () {
return new FulfilledPromise(new Response());
return new FulfilledPromise(new Response(200, [
'Cache-Control' => 'private, max-age=300'
]));
});

// Add this middleware to the top with `push`
$stack->push(new CacheMiddleware(), 'cache');
$this->middleware = new CacheMiddleware(new PrivateCacheStrategy(
new Psr6CacheStorage(new ArrayCachePool())
));

$stack->push($this->middleware, 'cache');

// Initialize the client with the handler option
$this->client = new Client(['handler' => $stack]);
}

public function testInvalidationCacheIfNotValidHttpMethod()
/**
* @dataProvider unsafeMethods
*/
public function testItInvalidatesForUnsafeHttpMethods($unsafeMethod)
{
$response = $this->client->get('anything');
$this->assertSame('', $response->getHeaderLine(CacheMiddleware::HEADER_INVALIDATION));
$this->middleware->setHttpMethods([
'GET' => true,
'HEAD' => true,
]);

$response = $this->client->post('anything');
$this->assertSame('1', $response->getHeaderLine(CacheMiddleware::HEADER_INVALIDATION));
$this->client->get('resource');
$this->client->head('resource');

$response = $this->client->put('anything');
$response = $this->client->{$unsafeMethod}('resource');
$this->assertSame('1', $response->getHeaderLine(CacheMiddleware::HEADER_INVALIDATION));

$response = $this->client->delete('anything');
$this->assertSame('1', $response->getHeaderLine(CacheMiddleware::HEADER_INVALIDATION));
$response = $this->client->get('resource');
$this->assertEquals(CacheMiddleware::HEADER_CACHE_MISS, $response->getHeaderLine('X-Kevinrob-Cache'));

$response = $this->client->patch('anything');
$this->assertSame('1', $response->getHeaderLine(CacheMiddleware::HEADER_INVALIDATION));
$response = $this->client->head('resource');
$this->assertEquals(CacheMiddleware::HEADER_CACHE_MISS, $response->getHeaderLine('X-Kevinrob-Cache'));
}

/**
* @dataProvider safeMethods
*/
public function testItDoesNotInvalidateForSafeHttpMethods($safeMethod)
{
$this->client->get('resource');

$response = $this->client->{$safeMethod}('resource');
$this->assertSame('', $response->getHeaderLine(CacheMiddleware::HEADER_INVALIDATION));

$response = $this->client->get('resource');
$this->assertEquals(CacheMiddleware::HEADER_CACHE_HIT, $response->getHeaderLine('X-Kevinrob-Cache'));
}

public function unsafeMethods()
{
return [
'delete' => ['delete'],
'put' => ['put'],
'post' => ['post'],
];
}

public function safemethods()
{
return [
'get' => ['get'],
'options' => ['options'],
'trace' => ['trace'],
'head' => ['head'],
];
}
}

0 comments on commit 22d79bb

Please sign in to comment.