SSI support
This commit is contained in:
parent
5febbb27f5
commit
06cea083b4
@ -81,6 +81,7 @@ class Configuration implements ConfigurationInterface
|
||||
$this->addCsrfSection($rootNode);
|
||||
$this->addFormSection($rootNode);
|
||||
$this->addEsiSection($rootNode);
|
||||
$this->addSsiSection($rootNode);
|
||||
$this->addFragmentsSection($rootNode);
|
||||
$this->addProfilerSection($rootNode);
|
||||
$this->addRouterSection($rootNode);
|
||||
@ -148,6 +149,17 @@ class Configuration implements ConfigurationInterface
|
||||
;
|
||||
}
|
||||
|
||||
private function addSsiSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
$rootNode
|
||||
->children()
|
||||
->arrayNode('ssi')
|
||||
->info('ssi configuration')
|
||||
->canBeEnabled()
|
||||
->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
private function addFragmentsSection(ArrayNodeDefinition $rootNode)
|
||||
{
|
||||
$rootNode
|
||||
|
@ -122,6 +122,7 @@ class FrameworkExtension extends Extension
|
||||
|
||||
$this->registerValidationConfiguration($config['validation'], $container, $loader);
|
||||
$this->registerEsiConfiguration($config['esi'], $container, $loader);
|
||||
$this->registerSsiConfiguration($config['ssi'], $container, $loader);
|
||||
$this->registerFragmentsConfiguration($config['fragments'], $container, $loader);
|
||||
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
|
||||
$this->registerTranslatorConfiguration($config['translator'], $container);
|
||||
@ -208,6 +209,22 @@ class FrameworkExtension extends Extension
|
||||
$loader->load('esi.xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the SSI configuration.
|
||||
*
|
||||
* @param array $config An SSI configuration array
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
* @param XmlFileLoader $loader An XmlFileLoader instance
|
||||
*/
|
||||
private function registerSsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
|
||||
{
|
||||
if (!$this->isConfigEnabled($container, $config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$loader->load('ssi.xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the fragments configuration.
|
||||
*
|
||||
|
@ -39,7 +39,7 @@ abstract class HttpCache extends BaseHttpCache
|
||||
$this->kernel = $kernel;
|
||||
$this->cacheDir = $cacheDir;
|
||||
|
||||
parent::__construct($kernel, $this->createStore(), $this->createEsi(), array_merge(array('debug' => $kernel->isDebug()), $this->getOptions()));
|
||||
parent::__construct($kernel, $this->createStore(), $this->createSurrogate(), array_merge(array('debug' => $kernel->isDebug()), $this->getOptions()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +55,7 @@ abstract class HttpCache extends BaseHttpCache
|
||||
{
|
||||
$this->getKernel()->boot();
|
||||
$this->getKernel()->getContainer()->set('cache', $this);
|
||||
$this->getKernel()->getContainer()->set('esi', $this->getEsi());
|
||||
$this->getKernel()->getContainer()->set($this->getSurrogate()->getName(), $this->getSurrogate());
|
||||
|
||||
return parent::forward($request, $raw, $entry);
|
||||
}
|
||||
@ -70,6 +70,18 @@ abstract class HttpCache extends BaseHttpCache
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function createSurrogate()
|
||||
{
|
||||
return $this->createEsi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new ESI instance
|
||||
*
|
||||
* @return Esi
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use createSurrogate() instead
|
||||
*/
|
||||
protected function createEsi()
|
||||
{
|
||||
return new Esi();
|
||||
|
@ -10,6 +10,7 @@
|
||||
<parameter key="fragment.renderer.hinclude.class">Symfony\Bundle\FrameworkBundle\Fragment\ContainerAwareHIncludeFragmentRenderer</parameter>
|
||||
<parameter key="fragment.renderer.hinclude.global_template"></parameter>
|
||||
<parameter key="fragment.renderer.esi.class">Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer</parameter>
|
||||
<parameter key="fragment.renderer.ssi.class">Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer</parameter>
|
||||
<parameter key="fragment.path">/_fragment</parameter>
|
||||
</parameters>
|
||||
|
||||
@ -41,5 +42,17 @@
|
||||
<argument type="service" id="fragment.renderer.inline" />
|
||||
<call method="setFragmentPath"><argument>%fragment.path%</argument></call>
|
||||
</service>
|
||||
|
||||
<service id="fragment.renderer.ssi" class="%fragment.renderer.ssi.class%">
|
||||
<tag name="kernel.fragment_renderer" />
|
||||
<argument type="service" id="ssi" on-invalid="null" />
|
||||
<argument type="service" id="fragment.renderer.inline" />
|
||||
<call method="setFragmentPath">
|
||||
<argument>%fragment.path%</argument>
|
||||
</call>
|
||||
<call method="setUriSigner">
|
||||
<argument type="service" id="uri_signer" />
|
||||
</call>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
20
src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml
Normal file
20
src/Symfony/Bundle/FrameworkBundle/Resources/config/ssi.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="ssi.class">Symfony\Component\HttpKernel\HttpCache\Ssi</parameter>
|
||||
<parameter key="ssi_listener.class">Symfony\Component\HttpKernel\EventListener\SurrogateListener</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="ssi" class="%ssi.class%" />
|
||||
|
||||
<service id="ssi_listener" class="%ssi_listener.class%">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="service" id="ssi" on-invalid="ignore" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
@ -105,6 +105,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
|
||||
'field_name' => '_token',
|
||||
),
|
||||
'esi' => array('enabled' => false),
|
||||
'ssi' => array('enabled' => false),
|
||||
'fragments' => array(
|
||||
'enabled' => false,
|
||||
'path' => '/_fragment',
|
||||
|
@ -11,48 +11,13 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\HttpCache\Esi;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* EsiListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for ESI.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use SurrogateListener instead
|
||||
*/
|
||||
class EsiListener implements EventSubscriberInterface
|
||||
class EsiListener extends SurrogateListener
|
||||
{
|
||||
private $esi;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Esi $esi An ESI instance
|
||||
*/
|
||||
public function __construct(Esi $esi = null)
|
||||
{
|
||||
$this->esi = $esi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Response.
|
||||
*
|
||||
* @param FilterResponseEvent $event A FilterResponseEvent instance
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
if (!$event->isMasterRequest() || null === $this->esi) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->esi->addSurrogateControl($event->getResponse());
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::RESPONSE => 'onKernelResponse',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* SurrogateListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for Surrogates
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SurrogateListener implements EventSubscriberInterface
|
||||
{
|
||||
private $surrogate;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param SurrogateInterface $surrogate An SurrogateInterface instance
|
||||
*/
|
||||
public function __construct(SurrogateInterface $surrogate = null)
|
||||
{
|
||||
$this->surrogate = $surrogate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Response.
|
||||
*
|
||||
* @param FilterResponseEvent $event A FilterResponseEvent instance
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
if (!$event->isMasterRequest() || null === $this->surrogate) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->surrogate->addSurrogateControl($event->getResponse());
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::RESPONSE => 'onKernelResponse',
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Fragment;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
|
||||
|
||||
/**
|
||||
* Implements Surrogate rendering strategy.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractSurrogateFragmentRenderer extends RoutableFragmentRenderer
|
||||
{
|
||||
private $surrogate;
|
||||
private $inlineStrategy;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* The "fallback" strategy when surrogate is not available should always be an
|
||||
* instance of InlineFragmentRenderer.
|
||||
*
|
||||
* @param SurrogateInterface $surrogate An Surrogate instance
|
||||
* @param FragmentRendererInterface $inlineStrategy The inline strategy to use when the surrogate is not supported
|
||||
*/
|
||||
public function __construct(SurrogateInterface $surrogate = null, FragmentRendererInterface $inlineStrategy)
|
||||
{
|
||||
$this->surrogate = $surrogate;
|
||||
$this->inlineStrategy = $inlineStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Note that if the current Request has no surrogate capability, this method
|
||||
* falls back to use the inline rendering strategy.
|
||||
*
|
||||
* Additional available options:
|
||||
*
|
||||
* * alt: an alternative URI to render in case of an error
|
||||
* * comment: a comment to add when returning the surrogate tag
|
||||
*
|
||||
* Note, that not all surrogate strategies support all options. For now
|
||||
* 'alt' and 'comment' are only supported by ESI.
|
||||
*
|
||||
* @see Symfony\Component\HttpKernel\HttpCache\SurrogateInterface
|
||||
*/
|
||||
public function render($uri, Request $request, array $options = array())
|
||||
{
|
||||
if (!$this->surrogate || !$this->surrogate->hasSurrogateCapability($request)) {
|
||||
return $this->inlineStrategy->render($uri, $request, $options);
|
||||
}
|
||||
|
||||
if ($uri instanceof ControllerReference) {
|
||||
$uri = $this->generateFragmentUri($uri, $request);
|
||||
}
|
||||
|
||||
$alt = isset($options['alt']) ? $options['alt'] : null;
|
||||
if ($alt instanceof ControllerReference) {
|
||||
$alt = $this->generateFragmentUri($alt, $request);
|
||||
}
|
||||
|
||||
$tag = $this->surrogate->renderIncludeTag($uri, $alt, isset($options['ignore_errors']) ? $options['ignore_errors'] : false, isset($options['comment']) ? $options['comment'] : '');
|
||||
|
||||
return new Response($tag);
|
||||
}
|
||||
}
|
@ -11,69 +11,13 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Fragment;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
use Symfony\Component\HttpKernel\HttpCache\Esi;
|
||||
|
||||
/**
|
||||
* Implements the ESI rendering strategy.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EsiFragmentRenderer extends RoutableFragmentRenderer
|
||||
class EsiFragmentRenderer extends AbstractSurrogateFragmentRenderer
|
||||
{
|
||||
private $esi;
|
||||
private $inlineStrategy;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* The "fallback" strategy when ESI is not available should always be an
|
||||
* instance of InlineFragmentRenderer.
|
||||
*
|
||||
* @param Esi $esi An Esi instance
|
||||
* @param FragmentRendererInterface $inlineStrategy The inline strategy to use when ESI is not supported
|
||||
*/
|
||||
public function __construct(Esi $esi = null, InlineFragmentRenderer $inlineStrategy)
|
||||
{
|
||||
$this->esi = $esi;
|
||||
$this->inlineStrategy = $inlineStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Note that if the current Request has no ESI capability, this method
|
||||
* falls back to use the inline rendering strategy.
|
||||
*
|
||||
* Additional available options:
|
||||
*
|
||||
* * alt: an alternative URI to render in case of an error
|
||||
* * comment: a comment to add when returning an esi:include tag
|
||||
*
|
||||
* @see Symfony\Component\HttpKernel\HttpCache\ESI
|
||||
*/
|
||||
public function render($uri, Request $request, array $options = array())
|
||||
{
|
||||
if (!$this->esi || !$this->esi->hasSurrogateEsiCapability($request)) {
|
||||
return $this->inlineStrategy->render($uri, $request, $options);
|
||||
}
|
||||
|
||||
if ($uri instanceof ControllerReference) {
|
||||
$uri = $this->generateFragmentUri($uri, $request);
|
||||
}
|
||||
|
||||
$alt = isset($options['alt']) ? $options['alt'] : null;
|
||||
if ($alt instanceof ControllerReference) {
|
||||
$alt = $this->generateFragmentUri($alt, $request);
|
||||
}
|
||||
|
||||
$tag = $this->esi->renderIncludeTag($uri, $alt, isset($options['ignore_errors']) ? $options['ignore_errors'] : false, isset($options['comment']) ? $options['comment'] : '');
|
||||
|
||||
return new Response($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Fragment;
|
||||
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\UriSigner;
|
||||
|
||||
/**
|
||||
* Implements the ESI rendering strategy.
|
||||
*
|
||||
* @author Sebastian Krebs <krebs.seb@gmail.com>
|
||||
*/
|
||||
class SsiFragmentRenderer extends AbstractSurrogateFragmentRenderer
|
||||
{
|
||||
/** @var UriSigner */
|
||||
private $signer;
|
||||
|
||||
/**
|
||||
* Set uri signer
|
||||
*
|
||||
* @param UriSigner $signer
|
||||
*/
|
||||
public function setUriSigner(UriSigner $signer)
|
||||
{
|
||||
$this->signer = $signer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'ssi';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function generateFragmentUri(ControllerReference $reference, Request $request, $absolute = false, $strict = true)
|
||||
{
|
||||
$uri = parent::generateFragmentUri($reference, $request, $absolute, $strict);
|
||||
|
||||
if ($this->signer) {
|
||||
$uri = $this->signer->sign($uri);
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Esi
|
||||
class Esi implements SurrogateInterface
|
||||
{
|
||||
private $contentTypes;
|
||||
|
||||
@ -41,10 +41,15 @@ class Esi
|
||||
$this->contentTypes = $contentTypes;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'esi';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new cache strategy instance.
|
||||
*
|
||||
* @return EsiResponseCacheStrategyInterface A EsiResponseCacheStrategyInterface instance
|
||||
* @return ResponseCacheStrategyInterface A ResponseCacheStrategyInterface instance
|
||||
*/
|
||||
public function createCacheStrategy()
|
||||
{
|
||||
@ -58,6 +63,20 @@ class Esi
|
||||
*
|
||||
* @return bool true if one surrogate has ESI/1.0 capability, false otherwise
|
||||
*/
|
||||
public function hasSurrogateCapability(Request $request)
|
||||
{
|
||||
return $this->hasSurrogateEsiCapability($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that at least one surrogate has ESI/1.0 capability.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return bool true if one surrogate has ESI/1.0 capability, false otherwise
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use hasSurrogateCapability() instead
|
||||
*/
|
||||
public function hasSurrogateEsiCapability(Request $request)
|
||||
{
|
||||
if (null === $value = $request->headers->get('Surrogate-Capability')) {
|
||||
@ -72,6 +91,18 @@ class Esi
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*/
|
||||
public function addSurrogateCapability(Request $request)
|
||||
{
|
||||
$this->addSurrogateEsiCapability($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds ESI/1.0 capability to the given Request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use addSurrogateCapability() instead
|
||||
*/
|
||||
public function addSurrogateEsiCapability(Request $request)
|
||||
{
|
||||
$current = $request->headers->get('Surrogate-Capability');
|
||||
@ -101,6 +132,20 @@ class Esi
|
||||
*
|
||||
* @return bool true if the Response needs to be parsed, false otherwise
|
||||
*/
|
||||
public function needsParsing(Response $response)
|
||||
{
|
||||
return $this->needsEsiParsing($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the Response needs to be parsed for ESI tags.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @return bool true if the Response needs to be parsed, false otherwise
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use needsParsing() instead
|
||||
*/
|
||||
public function needsEsiParsing(Response $response)
|
||||
{
|
||||
if (!$control = $response->headers->get('Surrogate-Control')) {
|
||||
@ -236,7 +281,7 @@ class Esi
|
||||
throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
|
||||
}
|
||||
|
||||
return sprintf('<?php echo $this->esi->handle($this, \'%s\', \'%s\', %s) ?>'."\n",
|
||||
return sprintf('<?php echo $this->surrogate->handle($this, \'%s\', \'%s\', %s) ?>'."\n",
|
||||
$options['src'],
|
||||
isset($options['alt']) ? $options['alt'] : null,
|
||||
isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false'
|
||||
|
@ -15,8 +15,6 @@
|
||||
|
||||
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.
|
||||
@ -25,61 +23,9 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
* or force validation if one of the ESI has validation cache strategy.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use ResponseCacheStrategy instead
|
||||
*/
|
||||
class EsiResponseCacheStrategy implements EsiResponseCacheStrategyInterface
|
||||
class EsiResponseCacheStrategy extends ResponseCacheStrategy implements EsiResponseCacheStrategyInterface
|
||||
{
|
||||
private $cacheable = true;
|
||||
private $embeddedResponses = 0;
|
||||
private $ttls = array();
|
||||
private $maxAges = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(Response $response)
|
||||
{
|
||||
if ($response->isValidateable()) {
|
||||
$this->cacheable = false;
|
||||
} else {
|
||||
$this->ttls[] = $response->getTtl();
|
||||
$this->maxAges[] = $response->getMaxAge();
|
||||
}
|
||||
|
||||
$this->embeddedResponses++;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function update(Response $response)
|
||||
{
|
||||
// if we have no embedded Response, do nothing
|
||||
if (0 === $this->embeddedResponses) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove validation related headers in order to avoid browsers using
|
||||
// their own cache, because some of the response content comes from
|
||||
// at least one embedded response (which likely has a different caching strategy).
|
||||
if ($response->isValidateable()) {
|
||||
$response->setEtag(null);
|
||||
$response->setLastModified(null);
|
||||
$this->cacheable = false;
|
||||
}
|
||||
|
||||
if (!$this->cacheable) {
|
||||
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ttls[] = $response->getTtl();
|
||||
$this->maxAges[] = $response->getMaxAge();
|
||||
|
||||
if (null !== $maxAge = min($this->maxAges)) {
|
||||
$response->setSharedMaxAge($maxAge);
|
||||
$response->headers->set('Age', $maxAge - min($this->ttls));
|
||||
}
|
||||
$response->setMaxAge(0);
|
||||
}
|
||||
}
|
||||
|
@ -15,27 +15,14 @@
|
||||
|
||||
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.
|
||||
* ResponseCacheStrategyInterface implementations know how to compute the
|
||||
* Response cache HTTP header based on the different response cache headers.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use ResponseCacheStrategyInterface instead
|
||||
*/
|
||||
interface EsiResponseCacheStrategyInterface
|
||||
interface EsiResponseCacheStrategyInterface extends ResponseCacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Adds a Response.
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function add(Response $response);
|
||||
|
||||
/**
|
||||
* Updates the Response HTTP headers based on the embedded Responses.
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function update(Response $response);
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
private $kernel;
|
||||
private $store;
|
||||
private $request;
|
||||
private $esi;
|
||||
private $esiCacheStrategy;
|
||||
private $surrogate;
|
||||
private $surrogateCacheStrategy;
|
||||
private $options = array();
|
||||
private $traces = array();
|
||||
|
||||
@ -72,16 +72,16 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
* This setting is overridden by the stale-if-error HTTP Cache-Control extension
|
||||
* (see RFC 5861).
|
||||
*
|
||||
* @param HttpKernelInterface $kernel An HttpKernelInterface instance
|
||||
* @param StoreInterface $store A Store instance
|
||||
* @param Esi $esi An Esi instance
|
||||
* @param array $options An array of options
|
||||
* @param HttpKernelInterface $kernel An HttpKernelInterface instance
|
||||
* @param StoreInterface $store A Store instance
|
||||
* @param SurrogateInterface $surrogate A SurrogateInterface instance
|
||||
* @param array $options An array of options
|
||||
*/
|
||||
public function __construct(HttpKernelInterface $kernel, StoreInterface $store, Esi $esi = null, array $options = array())
|
||||
public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = array())
|
||||
{
|
||||
$this->store = $store;
|
||||
$this->kernel = $kernel;
|
||||
$this->esi = $esi;
|
||||
$this->surrogate = $surrogate;
|
||||
|
||||
// needed in case there is a fatal error because the backend is too slow to respond
|
||||
register_shutdown_function(array($this->store, 'cleanup'));
|
||||
@ -153,14 +153,33 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Surrogate instance
|
||||
*
|
||||
* @throws \LogicException
|
||||
* @return SurrogateInterface A Surrogate instance
|
||||
*/
|
||||
public function getSurrogate()
|
||||
{
|
||||
return $this->getEsi();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Esi instance
|
||||
*
|
||||
* @throws \LogicException
|
||||
* @return Esi An Esi instance
|
||||
*
|
||||
* @deprecated Deprecated since version 2.6, to be removed in 3.0. Use getSurrogate() instead
|
||||
*/
|
||||
public function getEsi()
|
||||
{
|
||||
return $this->esi;
|
||||
if (!$this->surrogate instanceof Esi) {
|
||||
throw new \LogicException('This instance of HttpCache was not set up to use ESI as surrogate handler. You must overwrite and use createSurrogate');
|
||||
}
|
||||
|
||||
return $this->surrogate;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,8 +193,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
if (HttpKernelInterface::MASTER_REQUEST === $type) {
|
||||
$this->traces = array();
|
||||
$this->request = $request;
|
||||
if (null !== $this->esi) {
|
||||
$this->esiCacheStrategy = $this->esi->createCacheStrategy();
|
||||
if (null !== $this->surrogate) {
|
||||
$this->surrogateCacheStrategy = $this->surrogate->createCacheStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,11 +220,11 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
$response->headers->set('X-Symfony-Cache', $this->getLog());
|
||||
}
|
||||
|
||||
if (null !== $this->esi) {
|
||||
if (null !== $this->surrogate) {
|
||||
if (HttpKernelInterface::MASTER_REQUEST === $type) {
|
||||
$this->esiCacheStrategy->update($response);
|
||||
$this->surrogateCacheStrategy->update($response);
|
||||
} else {
|
||||
$this->esiCacheStrategy->add($response);
|
||||
$this->surrogateCacheStrategy->add($response);
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,8 +465,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
*/
|
||||
protected function forward(Request $request, $catch = false, Response $entry = null)
|
||||
{
|
||||
if ($this->esi) {
|
||||
$this->esi->addSurrogateEsiCapability($request);
|
||||
if ($this->surrogate) {
|
||||
$this->surrogate->addSurrogateCapability($request);
|
||||
}
|
||||
|
||||
// modify the X-Forwarded-For header if needed
|
||||
@ -640,8 +659,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
|
||||
protected function processResponseBody(Request $request, Response $response)
|
||||
{
|
||||
if (null !== $this->esi && $this->esi->needsEsiParsing($response)) {
|
||||
$this->esi->process($request, $response);
|
||||
if (null !== $this->surrogate && $this->surrogate->needsParsing($response)) {
|
||||
$this->surrogate->process($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* ResponseCacheStrategy knows how to compute the Response cache HTTP header
|
||||
* based on the different response cache headers.
|
||||
*
|
||||
* This implementation changes the master response TTL to the smallest TTL received
|
||||
* or force validation if one of the surrogates has validation cache strategy.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ResponseCacheStrategy implements ResponseCacheStrategyInterface
|
||||
{
|
||||
private $cacheable = true;
|
||||
private $embeddedResponses = 0;
|
||||
private $ttls = array();
|
||||
private $maxAges = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(Response $response)
|
||||
{
|
||||
if ($response->isValidateable()) {
|
||||
$this->cacheable = false;
|
||||
} else {
|
||||
$this->ttls[] = $response->getTtl();
|
||||
$this->maxAges[] = $response->getMaxAge();
|
||||
}
|
||||
|
||||
$this->embeddedResponses++;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function update(Response $response)
|
||||
{
|
||||
// if we have no embedded Response, do nothing
|
||||
if (0 === $this->embeddedResponses) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove validation related headers in order to avoid browsers using
|
||||
// their own cache, because some of the response content comes from
|
||||
// at least one embedded response (which likely has a different caching strategy).
|
||||
if ($response->isValidateable()) {
|
||||
$response->setEtag(null);
|
||||
$response->setLastModified(null);
|
||||
$this->cacheable = false;
|
||||
}
|
||||
|
||||
if (!$this->cacheable) {
|
||||
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ttls[] = $response->getTtl();
|
||||
$this->maxAges[] = $response->getMaxAge();
|
||||
|
||||
if (null !== $maxAge = min($this->maxAges)) {
|
||||
$response->setSharedMaxAge($maxAge);
|
||||
$response->headers->set('Age', $maxAge - min($this->ttls));
|
||||
}
|
||||
$response->setMaxAge(0);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* ResponseCacheStrategyInterface implementations know how to compute the
|
||||
* Response cache HTTP header based on the different response cache headers.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ResponseCacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Adds a Response.
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function add(Response $response);
|
||||
|
||||
/**
|
||||
* Updates the Response HTTP headers based on the embedded Responses.
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function update(Response $response);
|
||||
}
|
197
src/Symfony/Component/HttpKernel/HttpCache/Ssi.php
Normal file
197
src/Symfony/Component/HttpKernel/HttpCache/Ssi.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Ssi implements the SSI capabilities to Request and Response instances.
|
||||
*
|
||||
* @author Sebastian Krebs <krebs.seb@gmail.com>
|
||||
*/
|
||||
class Ssi implements SurrogateInterface
|
||||
{
|
||||
private $contentTypes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $contentTypes An array of content-type that should be parsed for SSI information.
|
||||
* (default: text/html, text/xml, application/xhtml+xml, and application/xml)
|
||||
*/
|
||||
public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xhtml+xml', 'application/xml'))
|
||||
{
|
||||
$this->contentTypes = $contentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'ssi';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createCacheStrategy()
|
||||
{
|
||||
return new ResponseCacheStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasSurrogateCapability(Request $request)
|
||||
{
|
||||
if (null === $value = $request->headers->get('Surrogate-Capability')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false !== strpos($value, 'SSI/1.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSurrogateCapability(Request $request)
|
||||
{
|
||||
$current = $request->headers->get('Surrogate-Capability');
|
||||
$new = 'symfony2="SSI/1.0"';
|
||||
|
||||
$request->headers->set('Surrogate-Capability', $current ? $current . ', ' . $new : $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSurrogateControl(Response $response)
|
||||
{
|
||||
if (false !== strpos($response->getContent(), '<!--#include')) {
|
||||
$response->headers->set('Surrogate-Control', 'content="SSI/1.0"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function needsParsing(Response $response)
|
||||
{
|
||||
if (!$control = $response->headers->get('Surrogate-Control')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) preg_match('#content="[^"]*SSI/1.0[^"]*"#', $control);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function renderIncludeTag($uri, $alt = null, $ignoreErrors = true, $comment = '')
|
||||
{
|
||||
return sprintf('<!--#include virtual="%s" -->', $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(Request $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$type = $response->headers->get('Content-Type');
|
||||
if (empty($type)) {
|
||||
$type = 'text/html';
|
||||
}
|
||||
|
||||
$parts = explode(';', $type);
|
||||
if (!in_array($parts[0], $this->contentTypes)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// we don't use a proper XML parser here as we can have SSI tags in a plain text response
|
||||
$content = $response->getContent();
|
||||
$content = str_replace(array('<?', '<%'), array('<?php echo "<?"; ?>', '<?php echo "<%"; ?>'), $content);
|
||||
$content = preg_replace_callback('#<!--\#include\s+(.*?)\s*-->#', array($this, 'handleIncludeTag'), $content);
|
||||
|
||||
$response->setContent($content);
|
||||
$response->headers->set('X-Body-Eval', 'SSI');
|
||||
|
||||
// remove SSI/1.0 from the Surrogate-Control header
|
||||
if ($response->headers->has('Surrogate-Control')) {
|
||||
$value = $response->headers->get('Surrogate-Control');
|
||||
if ('content="SSI/1.0"' == $value) {
|
||||
$response->headers->remove('Surrogate-Control');
|
||||
} elseif (preg_match('#,\s*content="SSI/1.0"#', $value)) {
|
||||
$response->headers->set('Surrogate-Control', preg_replace('#,\s*content="SSI/1.0"#', '', $value));
|
||||
} elseif (preg_match('#content="SSI/1.0",\s*#', $value)) {
|
||||
$response->headers->set('Surrogate-Control', preg_replace('#content="SSI/1.0",\s*#', '', $value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors)
|
||||
{
|
||||
$subRequest = Request::create($uri, 'get', array(), $cache->getRequest()->cookies->all(), array(), $cache->getRequest()->server->all());
|
||||
|
||||
try {
|
||||
$response = $cache->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);
|
||||
|
||||
if (!$response->isSuccessful()) {
|
||||
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $subRequest->getUri(), $response->getStatusCode()));
|
||||
}
|
||||
|
||||
return $response->getContent();
|
||||
} catch (\Exception $e) {
|
||||
if ($alt) {
|
||||
return $this->handle($cache, $alt, '', $ignoreErrors);
|
||||
}
|
||||
|
||||
if (!$ignoreErrors) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an SSI include tag (called internally).
|
||||
*
|
||||
* @param array $attributes An array containing the attributes.
|
||||
*
|
||||
* @return string The response content for the include.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function handleIncludeTag($attributes)
|
||||
{
|
||||
$options = array();
|
||||
preg_match_all('/(virtual)="([^"]*?)"/', $attributes[1], $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $set) {
|
||||
$options[$set[1]] = $set[2];
|
||||
}
|
||||
|
||||
if (!isset($options['virtual'])) {
|
||||
throw new \RuntimeException('Unable to process an SSI tag without a "virtual" attribute.');
|
||||
}
|
||||
|
||||
return sprintf('<?php echo $this->surrogate->handle($this, \'%s\', \'%s\', %s) ?>' . "\n",
|
||||
$options['virtual'],
|
||||
'',
|
||||
'false'
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* 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\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
interface SurrogateInterface
|
||||
{
|
||||
/**
|
||||
* Returns surrogate name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Returns a new cache strategy instance.
|
||||
*
|
||||
* @return ResponseCacheStrategyInterface A ResponseCacheStrategyInterface instance
|
||||
*/
|
||||
public function createCacheStrategy();
|
||||
|
||||
/**
|
||||
* Checks that at least one surrogate has Surrogate capability.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return bool true if one surrogate has Surrogate capability, false otherwise
|
||||
*/
|
||||
public function hasSurrogateCapability(Request $request);
|
||||
|
||||
/**
|
||||
* Adds Surrogate-capability to the given Request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*/
|
||||
public function addSurrogateCapability(Request $request);
|
||||
|
||||
/**
|
||||
* Adds HTTP headers to specify that the Response needs to be parsed for Surrogate.
|
||||
*
|
||||
* This method only adds an Surrogate HTTP header if the Response has some Surrogate tags.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
*/
|
||||
public function addSurrogateControl(Response $response);
|
||||
|
||||
/**
|
||||
* Checks that the Response needs to be parsed for Surrogate tags.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @return bool true if the Response needs to be parsed, false otherwise
|
||||
*/
|
||||
public function needsParsing(Response $response);
|
||||
|
||||
/**
|
||||
* Renders a Surrogate tag.
|
||||
*
|
||||
* @param string $uri A URI
|
||||
* @param string $alt An alternate URI
|
||||
* @param bool $ignoreErrors Whether to ignore errors or not
|
||||
* @param string $comment A comment to add as an esi:include tag
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function renderIncludeTag($uri, $alt = null, $ignoreErrors = true, $comment = '');
|
||||
|
||||
/**
|
||||
* Replaces a Response Surrogate tags with the included resource content.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function process(Request $request, Response $response);
|
||||
|
||||
/**
|
||||
* Handles a Surrogate from the cache.
|
||||
*
|
||||
* @param HttpCache $cache An HttpCache instance
|
||||
* @param string $uri The main URI
|
||||
* @param string $alt An alternative URI
|
||||
* @param bool $ignoreErrors Whether to ignore errors or not
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors);
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
namespace Symfony\Component\HttpKernel\Tests\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpCache\Esi;
|
||||
use Symfony\Component\HttpKernel\EventListener\EsiListener;
|
||||
use Symfony\Component\HttpKernel\EventListener\SurrogateListener;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
@ -27,7 +27,7 @@ class EsiListenerTest extends \PHPUnit_Framework_TestCase
|
||||
$dispatcher = new EventDispatcher();
|
||||
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
|
||||
$response = new Response('foo <esi:include src="" />');
|
||||
$listener = new EsiListener(new Esi());
|
||||
$listener = new SurrogateListener(new Esi());
|
||||
|
||||
$dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'));
|
||||
$event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::SUB_REQUEST, $response);
|
||||
@ -41,7 +41,7 @@ class EsiListenerTest extends \PHPUnit_Framework_TestCase
|
||||
$dispatcher = new EventDispatcher();
|
||||
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
|
||||
$response = new Response('foo <esi:include src="" />');
|
||||
$listener = new EsiListener(new Esi());
|
||||
$listener = new SurrogateListener(new Esi());
|
||||
|
||||
$dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'));
|
||||
$event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response);
|
||||
@ -55,7 +55,7 @@ class EsiListenerTest extends \PHPUnit_Framework_TestCase
|
||||
$dispatcher = new EventDispatcher();
|
||||
$kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
|
||||
$response = new Response('foo');
|
||||
$listener = new EsiListener(new Esi());
|
||||
$listener = new SurrogateListener(new Esi());
|
||||
|
||||
$dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'));
|
||||
$event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response);
|
||||
|
@ -23,14 +23,14 @@ class EsiTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
|
||||
$this->assertTrue($esi->hasSurrogateEsiCapability($request));
|
||||
$this->assertTrue($esi->hasSurrogateCapability($request));
|
||||
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Surrogate-Capability', 'foobar');
|
||||
$this->assertFalse($esi->hasSurrogateEsiCapability($request));
|
||||
$this->assertFalse($esi->hasSurrogateCapability($request));
|
||||
|
||||
$request = Request::create('/');
|
||||
$this->assertFalse($esi->hasSurrogateEsiCapability($request));
|
||||
$this->assertFalse($esi->hasSurrogateCapability($request));
|
||||
}
|
||||
|
||||
public function testAddSurrogateEsiCapability()
|
||||
@ -38,10 +38,10 @@ class EsiTest extends \PHPUnit_Framework_TestCase
|
||||
$esi = new Esi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$esi->addSurrogateEsiCapability($request);
|
||||
$esi->addSurrogateCapability($request);
|
||||
$this->assertEquals('symfony2="ESI/1.0"', $request->headers->get('Surrogate-Capability'));
|
||||
|
||||
$esi->addSurrogateEsiCapability($request);
|
||||
$esi->addSurrogateCapability($request);
|
||||
$this->assertEquals('symfony2="ESI/1.0", symfony2="ESI/1.0"', $request->headers->get('Surrogate-Capability'));
|
||||
}
|
||||
|
||||
@ -64,10 +64,10 @@ class EsiTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$response = new Response();
|
||||
$response->headers->set('Surrogate-Control', 'content="ESI/1.0"');
|
||||
$this->assertTrue($esi->needsEsiParsing($response));
|
||||
$this->assertTrue($esi->needsParsing($response));
|
||||
|
||||
$response = new Response();
|
||||
$this->assertFalse($esi->needsEsiParsing($response));
|
||||
$this->assertFalse($esi->needsParsing($response));
|
||||
}
|
||||
|
||||
public function testRenderIncludeTag()
|
||||
@ -100,18 +100,18 @@ class EsiTest extends \PHPUnit_Framework_TestCase
|
||||
$response = new Response('foo <esi:comment text="some comment" /><esi:include src="..." alt="alt" onerror="continue" />');
|
||||
$esi->process($request, $response);
|
||||
|
||||
$this->assertEquals('foo <?php echo $this->esi->handle($this, \'...\', \'alt\', true) ?>'."\n", $response->getContent());
|
||||
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'alt\', true) ?>'."\n", $response->getContent());
|
||||
$this->assertEquals('ESI', $response->headers->get('x-body-eval'));
|
||||
|
||||
$response = new Response('foo <esi:include src="..." />');
|
||||
$esi->process($request, $response);
|
||||
|
||||
$this->assertEquals('foo <?php echo $this->esi->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
|
||||
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
|
||||
|
||||
$response = new Response('foo <esi:include src="..."></esi:include>');
|
||||
$esi->process($request, $response);
|
||||
|
||||
$this->assertEquals('foo <?php echo $this->esi->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
|
||||
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
|
||||
}
|
||||
|
||||
public function testProcessEscapesPhpTags()
|
||||
|
209
src/Symfony/Component/HttpKernel/Tests/HttpCache/SsiTest.php
Normal file
209
src/Symfony/Component/HttpKernel/Tests/HttpCache/SsiTest.php
Normal file
@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Tests\HttpCache;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpCache\Ssi;
|
||||
|
||||
class SsiTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testHasSurrogateEsiCapability()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Surrogate-Capability', 'abc="SSI/1.0"');
|
||||
$this->assertTrue($ssi->hasSurrogateCapability($request));
|
||||
|
||||
$request = Request::create('/');
|
||||
$request->headers->set('Surrogate-Capability', 'foobar');
|
||||
$this->assertFalse($ssi->hasSurrogateCapability($request));
|
||||
|
||||
$request = Request::create('/');
|
||||
$this->assertFalse($ssi->hasSurrogateCapability($request));
|
||||
}
|
||||
|
||||
public function testAddSurrogateEsiCapability()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$ssi->addSurrogateCapability($request);
|
||||
$this->assertEquals('symfony2="SSI/1.0"', $request->headers->get('Surrogate-Capability'));
|
||||
|
||||
$ssi->addSurrogateCapability($request);
|
||||
$this->assertEquals('symfony2="SSI/1.0", symfony2="SSI/1.0"', $request->headers->get('Surrogate-Capability'));
|
||||
}
|
||||
|
||||
public function testAddSurrogateControl()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$response = new Response('foo <!--#include virtual="" -->');
|
||||
$ssi->addSurrogateControl($response);
|
||||
$this->assertEquals('content="SSI/1.0"', $response->headers->get('Surrogate-Control'));
|
||||
|
||||
$response = new Response('foo');
|
||||
$ssi->addSurrogateControl($response);
|
||||
$this->assertEquals('', $response->headers->get('Surrogate-Control'));
|
||||
}
|
||||
|
||||
public function testNeedsEsiParsing()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$response = new Response();
|
||||
$response->headers->set('Surrogate-Control', 'content="SSI/1.0"');
|
||||
$this->assertTrue($ssi->needsParsing($response));
|
||||
|
||||
$response = new Response();
|
||||
$this->assertFalse($ssi->needsParsing($response));
|
||||
}
|
||||
|
||||
public function testRenderIncludeTag()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$this->assertEquals('<!--#include virtual="/" -->', $ssi->renderIncludeTag('/', '/alt', true));
|
||||
$this->assertEquals('<!--#include virtual="/" -->', $ssi->renderIncludeTag('/', '/alt', false));
|
||||
$this->assertEquals('<!--#include virtual="/" -->', $ssi->renderIncludeTag('/'));
|
||||
}
|
||||
|
||||
public function testProcessDoesNothingIfContentTypeIsNotHtml()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$response = new Response();
|
||||
$response->headers->set('Content-Type', 'text/plain');
|
||||
$ssi->process($request, $response);
|
||||
|
||||
$this->assertFalse($response->headers->has('x-body-eval'));
|
||||
}
|
||||
|
||||
public function testProcess()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$response = new Response('foo <!--#include virtual="..." -->');
|
||||
$ssi->process($request, $response);
|
||||
|
||||
$this->assertEquals('foo <?php echo $this->surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent());
|
||||
$this->assertEquals('SSI', $response->headers->get('x-body-eval'));
|
||||
}
|
||||
|
||||
public function testProcessEscapesPhpTags()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$response = new Response('foo <?php die("foo"); ?><%= "lala" %>');
|
||||
$ssi->process($request, $response);
|
||||
|
||||
$this->assertEquals('foo <?php echo "<?"; ?>php die("foo"); ?><?php echo "<%"; ?>= "lala" %>', $response->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testProcessWhenNoSrcInAnEsi()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$response = new Response('foo <!--#include -->');
|
||||
$ssi->process($request, $response);
|
||||
}
|
||||
|
||||
public function testProcessRemoveSurrogateControlHeader()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
|
||||
$request = Request::create('/');
|
||||
$response = new Response('foo <!--#include virtual="..." -->');
|
||||
$response->headers->set('Surrogate-Control', 'content="SSI/1.0"');
|
||||
$ssi->process($request, $response);
|
||||
$this->assertEquals('SSI', $response->headers->get('x-body-eval'));
|
||||
|
||||
$response->headers->set('Surrogate-Control', 'no-store, content="SSI/1.0"');
|
||||
$ssi->process($request, $response);
|
||||
$this->assertEquals('SSI', $response->headers->get('x-body-eval'));
|
||||
$this->assertEquals('no-store', $response->headers->get('surrogate-control'));
|
||||
|
||||
$response->headers->set('Surrogate-Control', 'content="SSI/1.0", no-store');
|
||||
$ssi->process($request, $response);
|
||||
$this->assertEquals('SSI', $response->headers->get('x-body-eval'));
|
||||
$this->assertEquals('no-store', $response->headers->get('surrogate-control'));
|
||||
}
|
||||
|
||||
public function testHandle()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
$cache = $this->getCache(Request::create('/'), new Response('foo'));
|
||||
$this->assertEquals('foo', $ssi->handle($cache, '/', '/alt', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testHandleWhenResponseIsNot200()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
$response = new Response('foo');
|
||||
$response->setStatusCode(404);
|
||||
$cache = $this->getCache(Request::create('/'), $response);
|
||||
$ssi->handle($cache, '/', '/alt', false);
|
||||
}
|
||||
|
||||
public function testHandleWhenResponseIsNot200AndErrorsAreIgnored()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
$response = new Response('foo');
|
||||
$response->setStatusCode(404);
|
||||
$cache = $this->getCache(Request::create('/'), $response);
|
||||
$this->assertEquals('', $ssi->handle($cache, '/', '/alt', true));
|
||||
}
|
||||
|
||||
public function testHandleWhenResponseIsNot200AndAltIsPresent()
|
||||
{
|
||||
$ssi = new Ssi();
|
||||
$response1 = new Response('foo');
|
||||
$response1->setStatusCode(404);
|
||||
$response2 = new Response('bar');
|
||||
$cache = $this->getCache(Request::create('/'), array($response1, $response2));
|
||||
$this->assertEquals('bar', $ssi->handle($cache, '/', '/alt', false));
|
||||
}
|
||||
|
||||
protected function getCache($request, $response)
|
||||
{
|
||||
$cache = $this->getMock('Symfony\Component\HttpKernel\HttpCache\HttpCache', array('getRequest', 'handle'), array(), '', false);
|
||||
$cache->expects($this->any())
|
||||
->method('getRequest')
|
||||
->will($this->returnValue($request))
|
||||
;
|
||||
if (is_array($response)) {
|
||||
$cache->expects($this->any())
|
||||
->method('handle')
|
||||
->will(call_user_func_array(array($this, 'onConsecutiveCalls'), $response))
|
||||
;
|
||||
} else {
|
||||
$cache->expects($this->any())
|
||||
->method('handle')
|
||||
->will($this->returnValue($response))
|
||||
;
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user