Last active
July 16, 2018 22:41
-
-
Save malcolmp/2bc070211c793041e040702ea10cc9c3 to your computer and use it in GitHub Desktop.
A Response subscriber that bubbles up the response cache metadata to the page cache.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| use Drupal\Core\Cache\CacheableResponseInterface; | |
| use Drupal\Core\Config\ConfigFactoryInterface; | |
| use Drupal\Core\PageCache\RequestPolicyInterface; | |
| use Drupal\Core\PageCache\ResponsePolicyInterface; | |
| use Symfony\Component\HttpKernel\Event\FilterResponseEvent; | |
| use Symfony\Component\HttpKernel\KernelEvents; | |
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
| /** | |
| * Response subscriber to handle bubbling of response cacheable metadata to | |
| * the max-age Cache-Control header. | |
| * | |
| * @see Drupal\Core\EventSubscriber\FinishResponseSubscriber | |
| * @see https://www.drupal.org/node/2352009 | |
| * @see https://www.drupal.org/node/2732129 | |
| */ | |
| class MaxAgeBubblingResponseSubscriber implements EventSubscriberInterface { | |
| /** | |
| * A config object for the system performance configuration. | |
| * | |
| * @var \Drupal\Core\Config\Config | |
| */ | |
| protected $performanceConfig; | |
| protected $customConfig; | |
| /** | |
| * A policy rule determining the cacheability of a request. | |
| * | |
| * @var \Drupal\Core\PageCache\RequestPolicyInterface | |
| */ | |
| protected $requestPolicy; | |
| /** | |
| * A policy rule determining the cacheability of the response. | |
| * | |
| * @var \Drupal\Core\PageCache\ResponsePolicyInterface | |
| */ | |
| protected $responsePolicy; | |
| /** | |
| * Constructs an EventSubscriber object. | |
| * | |
| * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory | |
| * A config factory for retrieving required config objects. | |
| * @param RequestPolicyInterface $request_policy | |
| * @param ResponsePolicyInterface $response_policy | |
| */ | |
| public function __construct(ConfigFactoryInterface $config_factory, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy) { | |
| $this->performanceConfig = $config_factory->get('system.performance'); | |
| $this->customConfig = $config_factory->get('my_custom_performance.settings'); | |
| $this->requestPolicy = $request_policy; | |
| $this->responsePolicy = $response_policy; | |
| } | |
| /** | |
| * Sets extra headers on successful responses. | |
| * | |
| * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event | |
| * The event to process. | |
| */ | |
| public function onRespond(FilterResponseEvent $event) { | |
| if (!$event->isMasterRequest() || !$this->isCacheable($event)) { | |
| return; | |
| } | |
| $bubble_max_age = $this->customConfig->get('cache.page.bubble_max_age', TRUE); | |
| if ($bubble_max_age == FALSE) { return; } | |
| $response = $event->getResponse(); | |
| $max_age = $this->performanceConfig->get('cache.page.max_age'); | |
| if ($max_age > 0 && $response instanceof CacheableResponseInterface) { | |
| $cacheMetadata = $response->getCacheableMetadata(); | |
| $cacheMaxAge = $cacheMetadata->getCacheMaxAge(); | |
| if ($cacheMaxAge > 0) { | |
| $max_age = min($max_age, $cacheMaxAge); | |
| } else if ($cacheMaxAge === 0 && $this->customConfig->get('cache.page.allow_uncacheable')) { | |
| // Only allow uncacheable requests if setting is TRUE. | |
| $max_age = 0; | |
| } | |
| $response->headers->set('Cache-Control', 'public, max-age=' . $max_age); | |
| } | |
| } | |
| protected function isCacheable(FilterResponseEvent $event) { | |
| $request = $event->getRequest(); | |
| $response = $event->getResponse(); | |
| // TODO technically, we may be ok in some cases only checking responsePolicy | |
| // but the NoSessionOpen check is request only so for now authenticated responses | |
| // can not be cached for anonymous, even if it was ok to do so. | |
| return ($this->requestPolicy->check($request) === RequestPolicyInterface::ALLOW) && ($this->responsePolicy->check($response, $request) !== ResponsePolicyInterface::DENY); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function getSubscribedEvents() { | |
| // Our EventSubscriber must be after Drupal\Core\EventSubscriber\FinishResponseSubscriber | |
| $events[KernelEvents::RESPONSE][] = ['onRespond', -1]; | |
| return $events; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| services: | |
| my_custom.max_age_response_subscriber: | |
| class: Drupal\my_custom\EventSubscriber\MaxAgeBubblingResponseSubscriber | |
| arguments: ['@config.factory', '@page_cache_request_policy', '@page_cache_response_policy'] | |
| tags: | |
| - { name: event_subscriber } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment