From cef86a3771d28a6588492664d6c4c370a4cb676f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 22 Feb 2011 13:50:26 +0100 Subject: [PATCH] [HttpKernel] added a way to change the ESI cache strategy --- .../FrameworkBundle/HttpCache/HttpCache.php | 1 + .../Component/HttpKernel/HttpCache/Esi.php | 10 +++ .../HttpCache/EsiResponseCacheStrategy.php | 68 +++++++++++++++++++ .../EsiResponseCacheStrategyInterface.php | 41 +++++++++++ .../HttpKernel/HttpCache/HttpCache.php | 39 ++--------- 5 files changed, 127 insertions(+), 32 deletions(-) create mode 100644 src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php create mode 100644 src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategyInterface.php diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php index 75ac9edaa707..2923903658fc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php +++ b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php @@ -5,6 +5,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpCache\HttpCache as BaseHttpCache; use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\HttpCache\EsiResponseCacheStrategy; use Symfony\Component\HttpKernel\HttpCache\Store; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php index b2cadb710cb4..b7c6553c3a47 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Esi.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Esi.php @@ -41,6 +41,16 @@ public function __construct(array $contentTypes = array('text/html', 'text/xml', $this->contentTypes = $contentTypes; } + /** + * Returns a new cache strategy instance. + * + * @return EsiResponseCacheStrategyInterface A EsiResponseCacheStrategyInterface instance + */ + public function createCacheStrategy() + { + return new EsiResponseCacheStrategy(); + } + /** * Checks that at least one surrogate has ESI/1.0 capability. * diff --git a/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php b/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php new file mode 100644 index 000000000000..02f0ce8f3b38 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategy.php @@ -0,0 +1,68 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Response; + +/** + * EsiResponseCacheStrategy knows how to compute the Response cache HTTP header + * based on the different ESI response cache headers. + * + * This implementation changes the master response TTL to the smallest TTL received + * or force validation if one of the ESI has validation cache strategy. + * + * @author Fabien Potencier + */ +class EsiResponseCacheStrategy implements EsiResponseCacheStrategyInterface +{ + protected $cacheable = true; + protected $ttls = array(); + protected $maxAges = array(); + + /** + * Adds a Response. + * + * @param Response $response + */ + public function add(Response $response) + { + if ($response->isValidateable()) { + $this->cacheable = false; + } else { + $this->ttls[] = $response->getTtl(); + $this->maxAges[] = $response->getMaxAge(); + } + } + + /** + * Updates the Response HTTP headers based on the embedded Responses. + * + * @param Response $response + */ + public function update(Response $response) + { + if (!$this->cacheable) { + $response->headers->set('Cache-Control', 'no-cache, must-revalidate'); + + return; + } + + $maxAge = min($this->maxAges); + $response->setSharedMaxAge($maxAge); + $response->setMaxAge(0); + $response->headers->set('Age', $maxAge - min($this->ttls)); + } +} diff --git a/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategyInterface.php b/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategyInterface.php new file mode 100644 index 000000000000..68cda38611d9 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/HttpCache/EsiResponseCacheStrategyInterface.php @@ -0,0 +1,41 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Response; + +/** + * EsiResponseCacheStrategyInterface implementations know how to compute the + * Response cache HTTP header based on the different ESI response cache headers. + * + * @author Fabien Potencier + */ +interface EsiResponseCacheStrategyInterface +{ + /** + * Adds a Response. + * + * @param Response $response + */ + function add(Response $response); + + /** + * Updates the Response HTTP headers based on the embedded Responses. + * + * @param Response $response + */ + function update(Response $response); +} diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index cadbfa9f8326..a4c1e2d7bd70 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -31,7 +31,7 @@ class HttpCache implements HttpKernelInterface protected $store; protected $request; protected $esi; - protected $esiTtls; + protected $esiCacheStrategy; /** * Constructor. @@ -137,7 +137,9 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ if (HttpKernelInterface::MASTER_REQUEST === $type) { $this->traces = array(); $this->request = $request; - $this->esiTtls = array(); + if (null !== $this->esi) { + $this->esiCacheStrategy = $this->esi->createCacheStrategy(); + } } $path = $request->getPathInfo(); @@ -163,43 +165,16 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ } if (null !== $this->esi) { - $this->addEsiTtl($response); + $this->esiCacheStrategy->add($response); - if ($request === $this->request) { - $this->updateResponseCacheControl($response); + if (HttpKernelInterface::MASTER_REQUEST === $type) { + $this->esiCacheStrategy->update($response); } } return $response; } - /** - * Stores the response's TTL locally. - * - * @param Response $response - */ - protected function addEsiTtl(Response $response) - { - $this->esiTtls[] = $response->isValidateable() ? -1 : $response->getTtl(); - } - - /** - * Changes the master response TTL to the smallest TTL received or force validation if - * one of the ESI has validation cache strategy. - * - * @param Response $response - */ - protected function updateResponseCacheControl(Response $response) - { - $ttl = min($this->esiTtls); - if (-1 === $ttl) { - $response->headers->set('Cache-Control', 'no-cache, must-revalidate'); - } else { - $response->setSharedMaxAge($ttl); - $response->setMaxAge(0); - } - } - /** * Forwards the Request to the backend without storing the Response in the cache. *