diff --git a/core/core.services.yml b/core/core.services.yml index ca95c1b..2ae5e26 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -64,61 +64,31 @@ services: factory_service: cache_factory arguments: [path] page_cache: - class: Drupal\Core\PageCache\PageCacheInterface - factory_method: getPageCache - factory_service: page_cache_factory - page_cache_factory: - class: Drupal\Core\PageCache\DelegatePageCacheFactory - arguments: ['@page_cache_policy'] - tags: - - { name: delegate_page_cache.factory } + class: Drupal\Core\PageCache\PageCacheBase + arguments: ['@page_cache_policy', '@page_cache_storage'] page_cache_policy: - class: Drupal\Core\PageCache\CompoundPolicy - calls: - - [add, ['@page_cache_policy.deny_cli_or_unsafe_method']] - page_cache_policy.deny_cli_or_unsafe_method: - class: Drupal\Core\PageCache\PolicyRule\DenyCommandLineOrUnsafeMethod - page_cache_policy.deny_open_session: - class: Drupal\Core\PageCache\PolicyRule\DenyOpenSession - page_cache_policy.use_internal_config: - class: Drupal\Core\PageCache\PolicyRule\RequireInternalCacheConfig - arguments: ['@config.factory'] - page_cache_policy.cache_max_age_config: - class: Drupal\Core\PageCache\PolicyRule\RequireMaxAgeConfig - arguments: ['@config.factory'] - page_cache_selector.internal_storage: - class: Drupal\Core\PageCache\DelegatePageCache\PolicyBoundStorageSelector - arguments: ['@page_cache.internal_storage'] - calls: - - [add, ['@page_cache_policy', '@page_cache_policy.use_internal_config']] - - [add, ['@page_cache_policy', '@page_cache_policy.deny_open_session']] + class: Drupal\Core\PageCache\Policy + factory_class: Drupal\Core\PageCache\Policy + factory_method: getSingleton tags: - - { name: delegate_page_cache.storage_selector } - page_cache_selector.public_external_cache: - class: Drupal\Core\PageCache\DelegatePageCache\PolicyBoundCacheControlSelector - arguments: ['@page_cache.public_external_cache'] - calls: - - [add, ['@page_cache_policy', '@page_cache_policy.cache_max_age_config']] - - [add, ['@page_cache_policy', '@page_cache_policy.deny_open_session']] + - { name: page_cache_policy} + page_cache_veto: + class: Drupal\Core\PageCache\PolicyRule\Veto tags: - - { name: delegate_page_cache.cache_control_selector } - page_cache.cid_generator: + - { name: page_cache_policy.deny} + page_cache_response_subscriber: + class: Drupal\Core\PageCache\CacheControlResponseSubscriber + arguments: ['@page_cache_policy', '@config.factory', '@settings'] + tags: + - { name: event_subscriber } + page_cache_cid_generator: class: Drupal\Core\PageCache\Storage\CidGenerator arguments: ['@content_negotiation'] - page_cache_factory.internal_storage: - class: Drupal\Core\PageCache\Storage\InternalCacheFactory - arguments: ['@cache.page', '@page_cache.cid_generator', '@config.factory'] - page_cache.internal_storage: - class: Drupal\Core\PageCache\StorageInterface - factory_method: getInternalCache - factory_service: page_cache_factory.internal_storage - page_cache_factory.cache_control: - class: Drupal\Core\PageCache\CacheControlDefaultFactory - arguments: ['@config.factory', '@settings'] - page_cache.public_external_cache: - class: Drupal\Core\PageCache\CacheControlInterface - factory_method: getPublicExternal - factory_service: page_cache_factory.cache_control + page_cache_storage: + class: Drupal\Core\PageCache\Storage\InternalCache + factory_class: Drupal\Core\PageCache\Storage\InternalCache + factory_method: constructWithConfig + arguments: ['@cache.page', '@page_cache_cid_generator', '@config.factory'] config.cachedstorage.storage: class: Drupal\Core\Config\FileStorage factory_class: Drupal\Core\Config\FileStorageFactory diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 4dcf190..f5acae3 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1111,7 +1111,7 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) } // Mark this page as being uncacheable. - \Drupal::service('page_cache')->setNotCacheable(); + \Drupal::service('page_cache_veto')->useVeto(); } // Messages not set when DB connection fails. @@ -1316,7 +1316,7 @@ function drupal_handle_request($test_only = FALSE) { \Drupal::getContainer()->set('request', $request); // Try to retrieve the page from the cache. - if ($page_cache_helper->deliverFromCacheAfterContainer(\Drupal::service('page_cache'), $request)) { + if ($page_cache_helper->deliverFromCacheAfterContainer($request)) { return; } @@ -1326,7 +1326,7 @@ function drupal_handle_request($test_only = FALSE) { // Save the rendered markup to the page cache and add relevant headers for // external caches like browsers and proxies if necessary. - $page_cache_helper->recordAndDeliver(\Drupal::service('page_cache'), $response, $request); + $page_cache_helper->recordAndDeliver($response, $request); $kernel->terminate($request, $response); } @@ -1450,8 +1450,8 @@ function _drupal_bootstrap_configuration() { Unicode::check(); // Set the Drupal custom error handler. (requires \Drupal::config()) - set_error_handler('_drupal_error_handler'); - set_exception_handler('_drupal_exception_handler'); + # set_error_handler('_drupal_error_handler'); + # set_exception_handler('_drupal_exception_handler'); // Redirect the user to the installation script if Drupal has not been // installed yet (i.e., if no $databases array has been defined in the diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 43d64fd..c379ca3 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -9,7 +9,7 @@ use Drupal\Core\Cache\ListCacheBinsPass; use Drupal\Core\Config\ConfigFactoryOverridePass; -use Drupal\Core\PageCache\DelegatePageCache\RegisterSelectorsPass; +use Drupal\Core\PageCache\BuildPolicyPass; use Drupal\Core\DependencyInjection\ServiceProviderInterface; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; @@ -72,7 +72,8 @@ public function register(ContainerBuilder $container) { $container->addCompilerPass(new RegisterPathProcessorsPass()); $container->addCompilerPass(new RegisterRouteProcessorsPass()); $container->addCompilerPass(new ListCacheBinsPass()); - $container->addCompilerPass(new RegisterSelectorsPass()); + // Add the compiler pass for collecting page cache policy rules. + $container->addCompilerPass(new BuildPolicyPass()); // Add the compiler pass for appending string translators. $container->addCompilerPass(new RegisterStringTranslatorsPass()); // Add the compiler pass that will process the tagged breadcrumb builder diff --git a/core/lib/Drupal/Core/PageCache/BuildPolicyPass.php b/core/lib/Drupal/Core/PageCache/BuildPolicyPass.php new file mode 100644 index 0000000..860900d --- /dev/null +++ b/core/lib/Drupal/Core/PageCache/BuildPolicyPass.php @@ -0,0 +1,53 @@ +findTaggedServiceIds('page_cache_policy') as $id => $attributes) { + $policydef = $container->getDefinition($id); + + // Register all policy rules tagged with page_cache_policy.deny. + foreach ($container->findTaggedServiceIds('page_cache_policy.deny') as $id => $attributes) { + $def = $container->getDefinition($id); + $class = $def->getClass(); + $reflection_class = new \ReflectionClass($class); + if (!$reflection_class->implementsInterface('Drupal\Core\PageCache\PolicyRuleInterface')) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $policydef->addMethodCall('deny', array(new Reference($id))); + } + + // Register all policy rules tagged with page_cache_policy.allow. + foreach ($container->findTaggedServiceIds('page_cache_policy.allow') as $id => $attributes) { + $def = $container->getDefinition($id); + $class = $def->getClass(); + $reflection_class = new \ReflectionClass($class); + if (!$reflection_class->implementsInterface('Drupal\Core\PageCache\PolicyRuleInterface')) { + throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + + $policydef->addMethodCall('allow', array(new Reference($id))); + } + } + } + +} diff --git a/core/lib/Drupal/Core/PageCache/CacheControl/NoCache.php b/core/lib/Drupal/Core/PageCache/CacheControl/NoCache.php deleted file mode 100644 index bc34e55..0000000 --- a/core/lib/Drupal/Core/PageCache/CacheControl/NoCache.php +++ /dev/null @@ -1,26 +0,0 @@ -setNotCacheable($response, $request); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/CacheControl/PrivateBrowser.php b/core/lib/Drupal/Core/PageCache/CacheControl/PrivateBrowser.php deleted file mode 100644 index f45cc47..0000000 --- a/core/lib/Drupal/Core/PageCache/CacheControl/PrivateBrowser.php +++ /dev/null @@ -1,55 +0,0 @@ -ttl = $ttl; - } - - /** - * Enforce caching in browser. - * - * The Expires HTTP header is the heart of the client-side HTTP caching. The - * additional server-side page cache only takes effect when the client - * accesses the callback URL again (e.g., after clearing the browser cache or - * when force-reloading a Drupal page). - * - * @param \Symfony\Component\HttpFoundation\Response $response - * A response object. - * @param \Symfony\Component\HttpFoundation\Request $request - * A request object. - */ - public function setCacheable(Response $response, Request $request) { - $response->setExpires(\DateTime::createFromFormat('U', REQUEST_TIME + $this->ttl)); - $response->headers->set('Cache-Control', 'private'); - $response->setMaxAge($this->ttl); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/CacheControl/PublicExternal.php b/core/lib/Drupal/Core/PageCache/CacheControl/PublicExternal.php deleted file mode 100644 index b3ca376..0000000 --- a/core/lib/Drupal/Core/PageCache/CacheControl/PublicExternal.php +++ /dev/null @@ -1,138 +0,0 @@ -maxAge = (int) $max_age; - $this->omitVaryCookie = (bool) $omit_vary_cookie; - } - - /** - * Sets HTTP headers in preparation for a cached page response. - * - * The headers allow caching as much as possible in proxies and browsers - * without any particular knowledge about the pages. Controllers and event - * listeners may supply additional headers by adding them directly to the - * response object. - * - * If the request is conditional (using If-Modified-Since and If-None-Match), - * and the conditions match those currently in the cache, a 304 Not Modified - * response is sent. - * - * @param \Symfony\Component\HttpFoundation\Response $response - * A response object. - * @param \Symfony\Component\HttpFoundation\Request $request - * A request object. - */ - public function setCacheable(Response $response, Request $request) { - // HTTP/1.0 proxies do not support the Vary header, so prevent any caching - // by sending an Expires date in the past. HTTP/1.1 clients ignore the - // Expires header if a Cache-Control: max-age= directive is specified (see - // RFC 2616, section 14.9.3). - if (!$response->headers->has('Expires')) { - $this->setExpiresNoCache($response); - } - - // Check whether Vary was set to a custom value. Ignore Accept-Encoding, - // this has been set by the gzip filter. - // - // @todo: Let's get rid of that mechanism and instead instruct site builders - // and module authors to swap out page_cache.storage.external when custom - // Vary headers are required. - $vary_customized = (0 < count(array_diff($response->getVary(), array('Accept-Encoding')))); - - // Replace the default restrictive cache-control header with a permissive - // one. - $response->headers->set('Cache-Control', 'public'); - $max_age = !$request->cookies->has(session_name()) || $vary_customized ? $this->maxAge : 0; - $response->setMaxAge($max_age); - - // ETag should change if the output changes. - $last_modified = $response->getLastModified(); - $created = $last_modified ? $last_modified->getTimestamp() : REQUEST_TIME; - $response->setEtag($created); - - // Allow HTTP proxies to cache pages for anonymous users without a session - // cookie. The Vary header is used to indicates the set of request-header - // fields that fully determines whether a cache is permitted to use the - // response to reply to a subsequent request for a given URL without - // revalidation. - if (!$vary_customized && !$this->omitVaryCookie) { - $response->setVary('Cookie', FALSE); - } - - // @todo: Replace this with Response::isNotModified() - $this->revalidateResponse($response, $request, $created); - } - - /** - * Send an 304 Not Modified response if appropriate. - * - * If the request is conditional (using If-Modified-Since and If-None-Match), - * and the conditions match those currently in the cache, a 304 Not Modified - * response is sent. - * - * @param \Symfony\Component\HttpFoundation\Response $response - * A response object complete with headers and content. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - */ - protected function revalidateResponse(Response $response, Request $request, $created) { - // See if the client has provided the required HTTP headers. - $if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE; - $if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE; - - if ($if_modified_since && $if_none_match - && $if_none_match == $response->headers->get('etag') // etag must match - && $if_modified_since == $created) { // if-modified-since must match - $response->setStatusCode(304); - $response->setContent(null); - - // In the case of a 304 response, certain headers must be sent, and the - // remaining may not (see RFC 2616, section 10.3.5). - foreach (array_keys($response->headers->all()) as $name) { - if (!in_array($name, array('content-location', 'expires', 'cache-control', 'vary'))) { - $response->headers->remove($name); - } - } - } - } - -} diff --git a/core/lib/Drupal/Core/PageCache/CacheControlBase.php b/core/lib/Drupal/Core/PageCache/CacheControlBase.php deleted file mode 100644 index a616490..0000000 --- a/core/lib/Drupal/Core/PageCache/CacheControlBase.php +++ /dev/null @@ -1,52 +0,0 @@ -setExpiresNoCache($response); - $this->setCacheControlNoCache($response); - - // There is no point in sending along headers necessary for cache - // revalidation, if caching by proxies and browsers is denied in the first - // place. Therefore remove Etag, Last-Modified and Vary in that case. - $response->setEtag(NULL); - $response->setLastModified(NULL); - $response->setVary(NULL); - } - - /** - * Disable caching in the browser and for HTTP/1.1 proxies and clients. - */ - protected function setCacheControlNoCache(Response $response) { - $response->headers->set('Cache-Control', 'no-cache, must-revalidate, post-check=0, pre-check=0'); - } - - /** - * Disable caching in ancient browsers and for HTTP/1.0 proxies and clients. - * - * HTTP/1.0 proxies do not support the Vary header, so prevent any caching by - * sending an Expires date in the past. HTTP/1.1 clients ignore the Expires - * header if a Cache-Control: max-age= directive is specified (see RFC 2616, - * section 14.9.3). - */ - protected function setExpiresNoCache(Response $response) { - $response->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 GMT')); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/CacheControlDefaultFactory.php b/core/lib/Drupal/Core/PageCache/CacheControlDefaultFactory.php deleted file mode 100644 index c623ce9..0000000 --- a/core/lib/Drupal/Core/PageCache/CacheControlDefaultFactory.php +++ /dev/null @@ -1,60 +0,0 @@ -config = $config_factory->get('system.performance'); - $this->settings = $settings; - } - - /** - * Return a PublicExternalCacheControl instance. - */ - public function getPublicExternal() { - $max_age = $this->config->get('cache.page.max_age'); - $omit_vary_cookie = $this->settings->get('omit_vary_cookie'); - return new CacheControl\PublicExternal($max_age, $omit_vary_cookie); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/CacheControlInterface.php b/core/lib/Drupal/Core/PageCache/CacheControlInterface.php deleted file mode 100644 index d520466..0000000 --- a/core/lib/Drupal/Core/PageCache/CacheControlInterface.php +++ /dev/null @@ -1,38 +0,0 @@ -get('system.performance')->get('cache.page.max_age')); + $this->omitVaryCookie = (bool) $settings->get('omit_vary_cookie'); + } + + /** + * {@inheritdoc} + */ + protected function setCacheable(Response $response, Request $request) { + // HTTP/1.0 proxies do not support the Vary header, so prevent any caching + // by sending an Expires date in the past. HTTP/1.1 clients ignore the + // Expires header if a Cache-Control: max-age directive is specified (see + // RFC 2616, section 14.9.3). + if (!$response->headers->has('Expires')) { + $this->setExpiresNoCache($response); + } + + // Check whether Vary was set to a custom value. + // + // @todo: Let's get rid of that mechanism and instead instruct site + // builders and module authors to swap out + // page_cache.cache_control_response_subscriber when custom Vary headers + // are required. + $vary_customized = (bool) $response->getVary(); + + // Replace the default restrictive cache-control header with a permissive + // one. + $response->headers->set('Cache-Control', 'public'); + $max_age = !$request->cookies->has(session_name()) || $vary_customized ? $this->getMaxAge() : 0; + $response->setMaxAge($max_age); + + // In order to support HTTP cache-revalidation, ensure that there is a + // Last-Modified and an ETag header on the response. + $created = REQUEST_TIME; + if (!$response->headers->has('Last-Modified')) { + $response->setLastModified(new \DateTime(gmdate(DATE_RFC1123, REQUEST_TIME))); + } + else { + $created = $response->getLastModified()->getTimestamp(); + } + $response->setEtag($created); + + // Allow HTTP proxies to cache pages for anonymous users without a session + // cookie. The Vary header is used to indicates the set of request-header + // fields that fully determines whether a cache is permitted to use the + // response to reply to a subsequent request for a given URL without + // revalidation. + if (!$vary_customized && !$this->omitVaryCookie) { + $response->setVary('Cookie', FALSE); + } + } + +} diff --git a/core/lib/Drupal/Core/PageCache/CacheControlResponseSubscriberBase.php b/core/lib/Drupal/Core/PageCache/CacheControlResponseSubscriberBase.php new file mode 100644 index 0000000..d96510e --- /dev/null +++ b/core/lib/Drupal/Core/PageCache/CacheControlResponseSubscriberBase.php @@ -0,0 +1,180 @@ +policy = $policy; + $this->maxAge = $max_age; + } + + /** + * Set the max-age directive to be used when cache-control header is added. + */ + public function setMaxAge($max_age) { + $this->maxAge; + } + + /** + * Return the max-age directive to be used when cache-control header is added. + */ + public function getMaxAge() { + return $this->maxAge; + } + + /** + * Determine whether the given request has a custom cache-control header. + * + * Upon construction the ResponseHeaderBag is initialized with an empty + * Cache-Control header. Consequently it is not possible to check whether the + * header was set explicitely simply by checking for the presence of it. + * Rather it is necessary to examine the computed Cache-Control header and + * match for results known to be present only when Cache-Control was never set + * explicitely. + * + * When neither Cache-Control nor any of the ETag, Last-Modified, Expires + * headers were set on the response, Cache-Control returns the value + * 'no-cache'. If any of ETag, Last-Modified or Expires was set but not + * Cache-Control, then 'private, must-revalidate' (in exactly this order) is + * returned. + * + * @see \Symfony\Component\HttpFoundation\ResponseHeaderBag::computeCacheControlValue() + * + * @return bool + * TRUE when Cache-Control header was set explicitely on the given response. + */ + protected function isCacheControlCustomized(Response $response) { + $cache_control = $response->headers->get('cache-control'); + return $cache_control != 'no-cache' && $cache_control != 'private, must-revalidate'; + } + + /** + * Add Cache-Control and Expires header to a cacheable response. + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + * @param \Symfony\Component\HttpFoundation\Request $request + * A request object. + */ + protected abstract function setCacheable(Response $response, Request $request); + + /** + * Add Cache-Control and Expires header for a response which is not cacheable. + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + * @param \Symfony\Component\HttpFoundation\Request $request + * A request object. + */ + protected function setNotCacheable(Response $response, Request $request) { + $this->setCacheControlNoCache($response); + $this->setExpiresNoCache($response); + + // There is no point in sending along headers necessary for cache + // revalidation, if caching by proxies and browsers is denied in the first + // place. Therefore remove Etag, Last-Modified and Vary in that case. + $response->setEtag(NULL); + $response->setLastModified(NULL); + $response->setVary(NULL); + } + + /** + * Disable caching in the browser and for HTTP/1.1 proxies and clients. + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + */ + protected function setCacheControlNoCache(Response $response) { + $response->headers->set('Cache-Control', 'no-cache, must-revalidate, post-check=0, pre-check=0'); + } + + /** + * Disable caching in ancient browsers and for HTTP/1.0 proxies and clients. + * + * HTTP/1.0 proxies do not support the Vary header, so prevent any caching by + * sending an Expires date in the past. HTTP/1.1 clients ignore the Expires + * header if a Cache-Control: max-age= directive is specified (see RFC 2616, + * section 14.9.3). + * + * @param \Symfony\Component\HttpFoundation\Response $response + * A response object. + */ + protected function setExpiresNoCache(Response $response) { + $response->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 GMT')); + } + + /** + * Sets extra headers on successful responses. + * + * @param Symfony\Component\HttpKernel\Event\FilterResponseEvent $event + * The event to process. + */ + public function onResponse(FilterResponseEvent $event) { + if ($event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) { + $request = $event->getRequest(); + $response = $event->getResponse(); + + $cacheable = $this->policy->apply($request); + if ($cacheable && $this->maxAge > 0 && !$this->isCacheControlCustomized($response)) { + $this->setCacheable($response, $request); + } + + // Response is not cacheable, if Cache-Control remained unset until now or + // if the page cache was disabled for this response. + if (!$cacheable || !$this->isCacheControlCustomized($response)) { + $this->setNotCacheable($response, $request); + } + } + } + + /** + * Registers the methods in this class that should be listeners. + * + * @return array + * An array of event listener definitions. + */ + public static function getSubscribedEvents() { + $events[KernelEvents::RESPONSE][] = array('onResponse', -50); + return $events; + } + +} diff --git a/core/lib/Drupal/Core/PageCache/CompoundPolicy.php b/core/lib/Drupal/Core/PageCache/CompoundPolicy.php deleted file mode 100644 index c4780f6..0000000 --- a/core/lib/Drupal/Core/PageCache/CompoundPolicy.php +++ /dev/null @@ -1,95 +0,0 @@ -rules)) { - return FALSE; - } - - // Apply all policy rules and collect the results. - $result = array(); - foreach ($this->rules as $rule_hash => $rule) { - $result[$rule_hash] = $rule->apply($request); - } - - // Notify observers. - foreach ($this->observers as $observer_hash => $observer) { - $partial_result = $this->checkResult($result, $this->observedRules[$observer_hash]); - $observer->onCompoundPolicyResult($this->observedRules[$observer_hash], $partial_result); - } - - return $this->checkResult($result, $this->mandatoryRules); - } - - /** - * Add a mapping between a rule and a observer. - */ - public function add(PolicyRuleInterface $rule, $mandatory = TRUE, CompoundPolicyObserverInterface $observer = NULL) { - $rule_hash = spl_object_hash($rule); - $this->rules[$rule_hash] = $rule; - - if ($mandatory) { - $this->mandatoryRules[$rule_hash] = $rule_hash; - } - - if (isset($observer)) { - $observer_hash = spl_object_hash($observer); - $this->observers[$observer_hash] = $observer; - $this->observedRules[$observer_hash][$rule_hash] = $rule_hash; - } - } - - /** - * Verify that a result array only consists of success-results. - */ - protected function checkResult(array $result, array $rules) { - $rules_result = array_intersect_key($result, $rules); - return $rules_result == array_filter($rules_result); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/CompoundPolicyObserverInterface.php b/core/lib/Drupal/Core/PageCache/CompoundPolicyObserverInterface.php deleted file mode 100644 index 237c63f..0000000 --- a/core/lib/Drupal/Core/PageCache/CompoundPolicyObserverInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -storageSelectors = $storage_selectors; - $this->cacheControlSelectors = $cache_control_selectors; - } - - /** - * {@inheritdoc} - */ - protected function prepareCacheGet(Request $request) { - parent::prepareCacheGet($request); - - if ($this->isCacheable()) { - $this->selectStorage($request); - $this->selectCacheControl($request); - } - } - - /** - * Evaluate all storage selectors and set storage if appropriate. - */ - protected function selectStorage(Request $request) { - $this->storage = NULL; - - foreach ($this->storageSelectors as $storage_selector) { - $storage = $storage_selector->selectStorage($request); - if ($storage) { - $this->setStorage($storage); - return; - } - } - } - - /** - * Evaluate all cache control selectors and set cache control. - */ - protected function selectCacheControl(Request $request) { - $this->cacheControl = NULL; - - foreach ($this->cacheControlSelectors as $cache_control_selector) { - $cache_control = $cache_control_selector->selectCacheControl($request); - if ($cache_control) { - $this->setCacheControl($cache_control); - return; - } - } - } - -} diff --git a/core/lib/Drupal/Core/PageCache/DelegatePageCache/CacheControlSelectorInterface.php b/core/lib/Drupal/Core/PageCache/DelegatePageCache/CacheControlSelectorInterface.php deleted file mode 100644 index 60bf424..0000000 --- a/core/lib/Drupal/Core/PageCache/DelegatePageCache/CacheControlSelectorInterface.php +++ /dev/null @@ -1,28 +0,0 @@ -cacheControl = $cache_control; - } - - /** - * {@inheritdoc} - */ - public function selectCacheControl(Request $request) { - if ($this->policyResult) { - return $this->cacheControl; - } - } - -} diff --git a/core/lib/Drupal/Core/PageCache/DelegatePageCache/PolicyBoundSelectorBase.php b/core/lib/Drupal/Core/PageCache/DelegatePageCache/PolicyBoundSelectorBase.php deleted file mode 100644 index 1669d4a..0000000 --- a/core/lib/Drupal/Core/PageCache/DelegatePageCache/PolicyBoundSelectorBase.php +++ /dev/null @@ -1,40 +0,0 @@ -add($rule, FALSE, $this); - } - - /** - * {@inheritdoc} - */ - public function onCompoundPolicyResult(array $rules, $result) { - $this->policyResult = $result; - } - -} diff --git a/core/lib/Drupal/Core/PageCache/DelegatePageCache/PolicyBoundStorageSelector.php b/core/lib/Drupal/Core/PageCache/DelegatePageCache/PolicyBoundStorageSelector.php deleted file mode 100644 index c68e82c..0000000 --- a/core/lib/Drupal/Core/PageCache/DelegatePageCache/PolicyBoundStorageSelector.php +++ /dev/null @@ -1,44 +0,0 @@ -storage = $storage; - } - - /** - * {@inheritdoc} - */ - public function selectStorage(Request $request) { - if ($this->policyResult) { - return $this->storage; - } - } - -} diff --git a/core/lib/Drupal/Core/PageCache/DelegatePageCache/RegisterSelectorsPass.php b/core/lib/Drupal/Core/PageCache/DelegatePageCache/RegisterSelectorsPass.php deleted file mode 100644 index c4e5acd..0000000 --- a/core/lib/Drupal/Core/PageCache/DelegatePageCache/RegisterSelectorsPass.php +++ /dev/null @@ -1,55 +0,0 @@ -findTaggedServiceIds('delegate_page_cache.factory') as $id => $attributes) { - $factorydef = $container->getDefinition($id); - - // Register all services tagged with delegate_page_cache.storage_selector - // with the page cache service. - foreach ($container->findTaggedServiceIds('delegate_page_cache.storage_selector') as $id => $attributes) { - $def = $container->getDefinition($id); - $class = $def->getClass(); - $reflection_class = new \ReflectionClass($class); - if (!$reflection_class->implementsInterface('Drupal\Core\PageCache\DelegatePageCache\StorageSelectorInterface')) { - throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); - } - - $factorydef->addMethodCall('addStorageSelector', array(new Reference($id))); - } - - // Register all services tagged with delegate_page_cache.cache_control_selector - // with the page cache service. - foreach ($container->findTaggedServiceIds('delegate_page_cache.cache_control_selector') as $id => $attributes) { - $def = $container->getDefinition($id); - $class = $def->getClass(); - $reflection_class = new \ReflectionClass($class); - if (!$reflection_class->implementsInterface('Drupal\Core\PageCache\DelegatePageCache\CacheControlSelectorInterface')) { - throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); - } - - $factorydef->addMethodCall('addCacheControlSelector', array(new Reference($id))); - } - } - } - -} diff --git a/core/lib/Drupal/Core/PageCache/DelegatePageCache/StorageSelectorInterface.php b/core/lib/Drupal/Core/PageCache/DelegatePageCache/StorageSelectorInterface.php deleted file mode 100644 index 000d9ab..0000000 --- a/core/lib/Drupal/Core/PageCache/DelegatePageCache/StorageSelectorInterface.php +++ /dev/null @@ -1,28 +0,0 @@ -policy = $policy; - $this->storageSelectors = array(); - $this->cacheControlSelectors = array(); - } - - /** - * {@inheritdoc} - */ - public function getModestPageCache() { - return new DelegatePageCache($this->policy, $this->storageSelectors, $this->cacheControlSelectors); - } - - /** - * Add one storage selector to the list of selectors. - * - * @param \Drupal\Core\PageCache\DelegatePageCache\StorageSelectorInterface $storage_selector - * A storage selector. - */ - public function addStorageSelector(DelegatePageCache\StorageSelectorInterface $storage_selector) { - $this->storageSelectors[] = $storage_selector; - } - - /** - * Add one cache control selector to the list of selectors. - * - * @param \Drupal\Core\PageCache\DelegatePageCache\CacheControlSelectorInterface $cache_control_selector - * A cache control selector. - */ - public function addCacheControlSelector(DelegatePageCache\CacheControlSelectorInterface $cache_control_selector) { - $this->cacheControlSelectors[] = $cache_control_selector; - } - -} diff --git a/core/lib/Drupal/Core/PageCache/FastPageCacheFactoryBase.php b/core/lib/Drupal/Core/PageCache/FastPageCacheFactoryBase.php index 5171338..2cf46ad 100644 --- a/core/lib/Drupal/Core/PageCache/FastPageCacheFactoryBase.php +++ b/core/lib/Drupal/Core/PageCache/FastPageCacheFactoryBase.php @@ -21,13 +21,9 @@ public function getFastPageCache(Settings $settings, Request $request) { $policy = $this->getPolicy($settings, $request); $storage = $this->getStorage($settings, $request); - $cache_control = $this->getCacheControl($settings, $request); - - if ($policy && $storage && $cache_control) { - $page_cache = new PageCacheBase($policy); - $page_cache->setStorage($storage); - $page_cache->setCacheControl($cache_control); + if ($policy && $storage) { + $page_cache = new PageCacheBase($policy, $storage); return $page_cache; } } @@ -39,11 +35,7 @@ public function getFastPageCache(Settings $settings, Request $request) { * The global policy to be used in order to test whether caching is allowed. */ protected function getPolicy(Settings $settings, Request $request) { - $policy = new CompoundPolicy(); - $policy->add(new PolicyRule\DenyCommandLineOrUnsafeMethod()); - $policy->add(new PolicyRule\DenyOpenSession()); - - return $policy; + return Policy::getSingleton(); } /** @@ -54,22 +46,4 @@ protected function getPolicy(Settings $settings, Request $request) { */ protected abstract function getStorage(Settings $settings, Request $request); - /** - * Construct a cache control instance. - * - * @return \Drupal\Core\PageCache\CacheControlInterface|NULL - * The cache control to be used when delivering cacheable pages. - */ - protected function getCacheControl(Settings $settings, Request $request) { - $max_age = $settings->get('page_cache_max_age'); - $omit_vary_cookie = $settings->get('omit_vary_cookie'); - - if ($max_age > 0) { - return new CacheControl\PublicExternal($max_age, $omit_vary_cookie); - } - else { - return new CacheControl\NoCache(); - } - } - } diff --git a/core/lib/Drupal/Core/PageCache/PageCacheBase.php b/core/lib/Drupal/Core/PageCache/PageCacheBase.php index 64946af..ae4d04e 100644 --- a/core/lib/Drupal/Core/PageCache/PageCacheBase.php +++ b/core/lib/Drupal/Core/PageCache/PageCacheBase.php @@ -16,13 +16,6 @@ class PageCacheBase implements PageCacheInterface { /** - * TRUE when current request can be saved to the cache, FALSE otherwise. - * - * @var bool - */ - protected $cacheable = FALSE; - - /** * A policy rule used to decide whether a page request is cacheable. * * @var \Drupal\Core\PageCache\PolicyRuleInterface @@ -37,63 +30,34 @@ class PageCacheBase implements PageCacheInterface { protected $storage; /** - * Cache control implementation governing external caches. - * - * @var \Drupal\Core\PageCache\CacheControlInterface - */ - protected $cacheControl; - - /** * Construct new basic page cache instance. * * @param \Drupal\Core\PageCache\PolicyRuleInterface $policy * A policy rule used to decide whether a page request is cacheable. */ - public function __construct(PolicyRuleInterface $policy) { + public function __construct(PolicyRuleInterface $policy, StorageInterface $storage) { $this->policy = $policy; + $this->storage = $storage; } /** * {@inheritdoc} */ public function handle(Request $request) { - $this->prepareCacheGet($request); - - if (!$this->isCacheable()) { - return; - } - - $storage = $this->getStorage(); - if (!$storage) { - return; - } - - $response = $storage->cacheGet($request); - if (!$response) { - return; + if ($this->policy->apply($request)) { + $response = $this->storage->cacheGet($request); + if ($response) { + return $response; + } } - - $this->prepareCacheHit($response, $request); - - return $response; } /** * {@inheritdoc} */ public function record(Response $response, Request $request) { - $this->prepareCacheSet($response, $request); - - if ($this->isCacheable($response, $request)) { - $storage = $this->getStorage(); - if ($storage) { - $storage->cacheSet($response, $request); - } - - $this->prepareCacheMiss($response, $request); - } - else { - $this->prepareCachePass($response, $request); + if ($this->policy->apply($request)) { + $this->storage->cacheSet($response, $request); } return $response; @@ -101,85 +65,34 @@ public function record(Response $response, Request $request) { /** * {@inheritdoc} + * + * @todo: Use \Symfony\Component\HttpFoundation\Response::isNotModified() */ - public function setNotCacheable() { - $this->cacheable = FALSE; - } - - /** - * {@inheritdoc} - */ - public function isCacheable() { - return $this->cacheable; - } - - /** - * Set a storage backend. - */ - public function setStorage(StorageInterface $storage) { - $this->storage = $storage; - } - - /** - * Return the current storage backend. - */ - public function getStorage() { - return $this->storage; - } - - /** - * Set a cache-control backend. - */ - public function setCacheControl(CacheControlInterface $cache_control) { - $this->cacheControl = $cache_control; - } - - /** - * Return the current cache-control backend. - */ - public function getCacheControl() { - return $this->cacheControl; - } - - /** - * Apply the cache policy before attempting to retrieve a page from the cache. - */ - protected function prepareCacheGet(Request $request) { - $this->cacheable = TRUE; - - if (!$this->policy->apply($request)) { - $this->setNotCacheable(); + public function revalidate(Response $response, Request $request) { + $last_modified = $response->getLastModified(); + if (!$last_modified) { + return; + } + $created = $last_modified->getTimestamp(); + + // See if the client has provided the required HTTP headers. + $if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE; + $if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE; + + if ($if_modified_since && $if_none_match + && $if_none_match == $response->getEtag() // etag must match + && $if_modified_since == $created) { // if-modified-since must match + $response->setStatusCode(304); + $response->setContent(null); + + // In the case of a 304 response, certain headers must be sent, and the + // remaining may not (see RFC 2616, section 10.3.5). + foreach (array_keys($response->headers->all()) as $name) { + if (!in_array($name, array('content-location', 'expires', 'cache-control', 'vary'))) { + $response->headers->remove($name); + } + } } - } - - /** - * Apply the policy rules before saving a page to the cache. - */ - protected function prepareCacheSet(Response $response, Request $request) { - } - - /** - * Prepare response headers before delivering a cacheable page to the client. - */ - protected function prepareCacheHit(Response $response, Request $request) { - $cache_control = $this->getCacheControl() ?: new CacheControl\NoCache(); - $cache_control->setCacheable($response, $request); - } - - /** - * Prepare response headers before delivering a cacheable page to the client. - */ - protected function prepareCacheMiss(Response $response, Request $request) { - $cache_control = $this->getCacheControl() ?: new CacheControl\NoCache(); - $cache_control->setCacheable($response, $request); - } - - /** - * Prepare headers on a non-cacheable response. - */ - protected function prepareCachePass(Response $response, Request $request) { - $cache_control = $this->getCacheControl() ?: new CacheControl\NoCache(); - $cache_control->setNotCacheable($response, $request); } } diff --git a/core/lib/Drupal/Core/PageCache/PageCacheFactoryBase.php b/core/lib/Drupal/Core/PageCache/PageCacheFactoryBase.php deleted file mode 100644 index da53c73..0000000 --- a/core/lib/Drupal/Core/PageCache/PageCacheFactoryBase.php +++ /dev/null @@ -1,49 +0,0 @@ -getPageCacheHelper(); - if ($helper->existsFastPageCacheInstance()) { - return $helper->getFastPageCacheInstance(); - } - else { - return $this->getModestPageCache(); - } - } - - /** - * Return a page cache helper instance. - * - * @return \Drupal\Core\PageCache\PageCacheHelper - * A page cache heler class to use. - */ - protected function getPageCacheHelper() { - return new PageCacheHelper(); - } - - /** - * Construct and return a new page cache instance. - * - * @return \Drupal\Core\PageCache\PageCacheInterface - * A page cache implementation. - */ - protected abstract function getModestPageCache(); - -} diff --git a/core/lib/Drupal/Core/PageCache/PageCacheFactoryInterface.php b/core/lib/Drupal/Core/PageCache/PageCacheFactoryInterface.php deleted file mode 100644 index 40de240..0000000 --- a/core/lib/Drupal/Core/PageCache/PageCacheFactoryInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -existsFastPageCacheInstance()) { + public function deliverFromCacheAfterContainer(Request $request) { + if (!$this->existsFastPageCacheInstance() && \Drupal::config('system.performance')->get('cache.page.use_internal')) { + $page_cache = \Drupal::service('page_cache'); $response = $page_cache->handle($request); if ($response) { $this->deliver($response, $request); @@ -97,11 +98,16 @@ public function deliverFromCacheAfterContainer(PageCacheInterface $page_cache, R * Save the rendered markup to the page cache and add appropriate headers * instructing external caches like browsers and proxies if necessary. */ - public function recordAndDeliver(PageCacheInterface $page_cache, Response $response, Request $request) { - if ($this->existsFastPageCacheInstance()) { - $page_cache = $this->getFastPageCacheInstance(); + public function recordAndDeliver(Response $response, Request $request) { + if (\Drupal::config('system.performance')->get('cache.page.use_internal')) { + if ($this->existsFastPageCacheInstance()) { + $page_cache = $this->getFastPageCacheInstance(); + } + else { + $page_cache = \Drupal::service('page_cache'); + } + $page_cache->record($response, $request); } - $page_cache->record($response, $request); $this->deliver($response, $request); } @@ -109,7 +115,17 @@ public function recordAndDeliver(PageCacheInterface $page_cache, Response $respo * Prepare and send a response. */ protected function deliver(Response $response, Request $request) { - $response->prepare($request)->send(); + $response->prepare($request); + + if ($this->existsFastPageCacheInstance()) { + $page_cache = $this->getFastPageCacheInstance(); + } + else { + $page_cache = \Drupal::service('page_cache'); + } + $page_cache->revalidate($response, $request); + + $response->send(); } } diff --git a/core/lib/Drupal/Core/PageCache/PageCacheInterface.php b/core/lib/Drupal/Core/PageCache/PageCacheInterface.php index adf75ff..c2937cd 100644 --- a/core/lib/Drupal/Core/PageCache/PageCacheInterface.php +++ b/core/lib/Drupal/Core/PageCache/PageCacheInterface.php @@ -38,16 +38,12 @@ public function handle(Request $request); public function record(Response $response, Request $request); /** - * Disable any page caching for the current request. - */ - public function setNotCacheable(); - - /** - * Test whether the current page request is cacheable. + * Send an 304 Not Modified response if appropriate. * - * @return bool - * Return TRUE when the response to the current request is cacheable. + * If the request is conditional (using If-Modified-Since and If-None-Match), + * and the conditions match those currently in the cache, a 304 Not Modified + * response is sent. */ - public function isCacheable(); + public function revalidate(Response $response, Request $request); } diff --git a/core/lib/Drupal/Core/PageCache/Policy.php b/core/lib/Drupal/Core/PageCache/Policy.php new file mode 100644 index 0000000..80e5879 --- /dev/null +++ b/core/lib/Drupal/Core/PageCache/Policy.php @@ -0,0 +1,106 @@ +deny as $rule) { + if ($rule->apply($request)) { + return FALSE; + } + } + + foreach ($this->allow as $rule) { + if ($rule->apply($request)) { + return TRUE; + } + } + + return FALSE; + } + + /** + * Add one policy rule to the deny-chain. + * + * @param \Drupal\Core\PageCache\PolicyRuleInterface $rule + * The rules to add to the deny-chain. + * + * @return \Drupal\Core\PageCache\Policy + * This policy instance. + */ + public function deny(PolicyRuleInterface $rule) { + $this->deny[spl_object_hash($rule)] = $rule; + return $this; + } + + /** + * Add one policy rule to the allow-chain. + * + * @param \Drupal\Core\PageCache\PolicyRuleInterface $rule + * The rules to add to the deny-chain. + * + * @return \Drupal\Core\PageCache\Policy + * This policy instance. + */ + public function allow(PolicyRuleInterface $rule) { + $this->allow[spl_object_hash($rule)] = $rule; + return $this; + } + + /** + * Return the singleton policy instance. + * + * If it does not exist, construct a new policy instance and populate it with + * default rules. + * + * @return \Drupal\Core\PageCache\Policy + * The singleton policy instance. + */ + public static function getSingleton() { + if (!isset(static::$instance)) { + $policy = new static(); + $policy->deny(new PolicyRule\CommandLineOrUnsafeMethod()); + $policy->allow(new PolicyRule\NoSessionOpen()); + static::$instance = $policy; + } + + return static::$instance; + } +} diff --git a/core/lib/Drupal/Core/PageCache/PolicyRule/CommandLineOrUnsafeMethod.php b/core/lib/Drupal/Core/PageCache/PolicyRule/CommandLineOrUnsafeMethod.php new file mode 100644 index 0000000..f7fb3ad --- /dev/null +++ b/core/lib/Drupal/Core/PageCache/PolicyRule/CommandLineOrUnsafeMethod.php @@ -0,0 +1,37 @@ +isCLIRequest($request) || !$request->isMethodSafe(); + } + + /** + * Exclude a page request when run from a command line script. + */ + protected function isCLIRequest(Request $request) { + return drupal_is_cli(); + } + +} diff --git a/core/lib/Drupal/Core/PageCache/PolicyRule/DenyCommandLineOrUnsafeMethod.php b/core/lib/Drupal/Core/PageCache/PolicyRule/DenyCommandLineOrUnsafeMethod.php deleted file mode 100644 index 4f1b466..0000000 --- a/core/lib/Drupal/Core/PageCache/PolicyRule/DenyCommandLineOrUnsafeMethod.php +++ /dev/null @@ -1,42 +0,0 @@ -isNotCLIRequest($request) && $request->isMethodSafe(); - } - - /** - * Exclude a page request when run from a command line script. - */ - protected function isNotCLIRequest(Request $request) { - return !drupal_is_cli(); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/PolicyRule/DenyOpenSession.php b/core/lib/Drupal/Core/PageCache/PolicyRule/DenyOpenSession.php deleted file mode 100644 index 2a8f350..0000000 --- a/core/lib/Drupal/Core/PageCache/PolicyRule/DenyOpenSession.php +++ /dev/null @@ -1,30 +0,0 @@ -cookies->has(session_name()); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/PolicyRule/NoSessionOpen.php b/core/lib/Drupal/Core/PageCache/PolicyRule/NoSessionOpen.php new file mode 100644 index 0000000..52f1b93 --- /dev/null +++ b/core/lib/Drupal/Core/PageCache/PolicyRule/NoSessionOpen.php @@ -0,0 +1,30 @@ +cookies->has(session_name()); + } + +} diff --git a/core/lib/Drupal/Core/PageCache/PolicyRule/RequireInternalCacheConfig.php b/core/lib/Drupal/Core/PageCache/PolicyRule/RequireInternalCacheConfig.php deleted file mode 100644 index 7de86c1..0000000 --- a/core/lib/Drupal/Core/PageCache/PolicyRule/RequireInternalCacheConfig.php +++ /dev/null @@ -1,44 +0,0 @@ -config = $config_factory->get('system.performance'); - } - - /** - * {@inheritdoc} - */ - public function apply(Request $request) { - return $this->config->get('cache.page.use_internal'); - } - -} diff --git a/core/lib/Drupal/Core/PageCache/PolicyRule/RequireMaxAgeConfig.php b/core/lib/Drupal/Core/PageCache/PolicyRule/RequireMaxAgeConfig.php deleted file mode 100644 index 635021f..0000000 --- a/core/lib/Drupal/Core/PageCache/PolicyRule/RequireMaxAgeConfig.php +++ /dev/null @@ -1,43 +0,0 @@ -config = $config_factory->get('system.performance'); - } - - /** - * {@inheritdoc} - */ - public function apply(Request $request) { - return $this->config->get('cache.page.max_age') > 0; - } - -} diff --git a/core/lib/Drupal/Core/PageCache/PolicyRule/Veto.php b/core/lib/Drupal/Core/PageCache/PolicyRule/Veto.php new file mode 100644 index 0000000..a23646b --- /dev/null +++ b/core/lib/Drupal/Core/PageCache/PolicyRule/Veto.php @@ -0,0 +1,38 @@ +veto; + } + + /** + * Deny any page caching on the current request. + */ + public function useVeto() { + $this->veto = TRUE; + } +} diff --git a/core/lib/Drupal/Core/PageCache/Storage/InternalCache.php b/core/lib/Drupal/Core/PageCache/Storage/InternalCache.php index 134b57f..ecd850b 100644 --- a/core/lib/Drupal/Core/PageCache/Storage/InternalCache.php +++ b/core/lib/Drupal/Core/PageCache/Storage/InternalCache.php @@ -66,6 +66,24 @@ public function __construct(CacheBackendInterface $cache, CidGeneratorInterface } /** + * A static factory method used to instantiate the class as a service. + * + * @param \Drupal\Core\Cache\CacheBackendInterface $cache + * The cache backend to use. + * @param \Drupal\Core\PageCache\Storage\CidGeneratorInterface $cid_generator + * Generator for the cache id. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory instance. + * + * @return \Drupal\Core\PageCache\Storage\InternalCache + * A fully configured page cache storage instance. + */ + public static function constructWithConfig(CacheBackendInterface $cache, CidGeneratorInterface $cid_generator, ConfigFactoryInterface $config_factory) { + $use_compression = $config_factory->get('system.performance')->get('response.gzip'); + return new static($cache, $cid_generator, $use_compression); + } + + /** * {@inheritdoc} */ public function cacheGet(Request $request) { @@ -75,8 +93,8 @@ public function cacheGet(Request $request) { return; } - // Prepare a new response and set the content. - $response = new Response($cache->data['body'], $cache->data['status'], $cache->data['headers']); + // Retrieve the response from the cache object. + $response = $cache->data; // If there is no Last-Modified header on the cached response, compute it // from the creation date of the cache-object. @@ -102,7 +120,7 @@ public function cacheGet(Request $request) { public function cacheSet(Response $response, Request $request) { // Use the actual timestamp from an Expires header, if available. $date = $response->getExpires(); - $expire = $date ? $date->getTimestamp() : Cache::PERMANENT; + $expire = $date && $date->getTimestamp() > REQUEST_TIME ? $date->getTimestamp() : Cache::PERMANENT; if ($this->useCompression) { $compression_helper = $this->getCompressionHelper(); @@ -110,13 +128,8 @@ public function cacheSet(Response $response, Request $request) { } $cid = $this->cidGenerator->getCid($request); - $data = array( - 'status' => $response->getStatusCode(), - 'headers' => $response->headers->all(), - 'body' => $response->getContent(), - ); $tags = array('content' => TRUE) + drupal_cache_tags_page_get($response); - $this->cache->set($cid, $data, $expire, $tags); + $this->cache->set($cid, $response, $expire, $tags); // Uncompress the response when serving clients which cannot handle gzip. if (isset($compression_helper)) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php index 8cd1923..6fe9753 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php @@ -157,7 +157,7 @@ function testPageCache() { // Fill the cache. $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.'); - $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'accept-encoding,cookie', 'Vary header was sent.'); + $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'cookie,accept-encoding', 'Vary header was sent.'); // Symfony's Response logic determines a specific order for the subvalues // of the Cache-Control header, even if they are explicitly passed in to // the response header bag in a different order. @@ -168,7 +168,7 @@ function testPageCache() { // Check cache. $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.'); - $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'accept-encoding,cookie', 'Vary: Cookie header was sent.'); + $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'cookie,accept-encoding', 'Vary: Cookie header was sent.'); $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'max-age=300, public', 'Cache-Control header was sent.'); $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.'); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.'); @@ -188,9 +188,13 @@ function testPageCache() { $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent.'); $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.'); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.'); + } + /** + * Tests cache headers. + */ + public function testOmitVaryCookie() { // Check the omit_vary_cookie setting. - $this->drupalLogout(); $settings['settings']['omit_vary_cookie'] = (object) array( 'value' => TRUE, 'required' => TRUE, diff --git a/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php b/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php index d099ed6..baca823 100644 --- a/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php +++ b/core/modules/toolbar/lib/Drupal/toolbar/Controller/ToolbarController.php @@ -26,6 +26,19 @@ public function subtreesJsonp() { $subtrees = toolbar_get_rendered_subtrees(); $response = new JsonResponse($subtrees); $response->setCallback('Drupal.toolbar.setSubtrees.resolve'); + + // The Expires HTTP header is the heart of the client-side HTTP caching. The + // additional server-side page cache only takes effect when the client + // accesses the callback URL again (e.g., after clearing the browser cache + // or when force-reloading a Drupal page). + $max_age = 365 * 24 * 60; + $response->setPrivate(); + $response->setMaxAge($max_age); + + $expires = new \DateTime(); + $expires->setTimestamp(REQUEST_TIME + $max_age); + $response->setExpires($expires); + return $response; } diff --git a/core/modules/toolbar/lib/Drupal/toolbar/PageCache/IsToolbarPath.php b/core/modules/toolbar/lib/Drupal/toolbar/PageCache/IsToolbarPath.php new file mode 100644 index 0000000..e3f063f --- /dev/null +++ b/core/modules/toolbar/lib/Drupal/toolbar/PageCache/IsToolbarPath.php @@ -0,0 +1,28 @@ +getPathInfo(), '/toolbar/subtrees/') === 0; + } + +} diff --git a/core/modules/toolbar/lib/Drupal/toolbar/PageCache/RequireToolbarPath.php b/core/modules/toolbar/lib/Drupal/toolbar/PageCache/RequireToolbarPath.php deleted file mode 100644 index 0c41610..0000000 --- a/core/modules/toolbar/lib/Drupal/toolbar/PageCache/RequireToolbarPath.php +++ /dev/null @@ -1,28 +0,0 @@ -getPathInfo(), '/toolbar/subtrees/') === 0; - } - -} diff --git a/core/modules/toolbar/toolbar.services.yml b/core/modules/toolbar/toolbar.services.yml index 5db7287..b7ccf49 100644 --- a/core/modules/toolbar/toolbar.services.yml +++ b/core/modules/toolbar/toolbar.services.yml @@ -7,22 +7,6 @@ services: factory_service: cache_factory arguments: [toolbar] toolbar.page_cache_policy.toolbar_path: - class: Drupal\toolbar\PageCache\RequireToolbarPath - toolbar.page_cache_selector.internal_storage: - class: Drupal\Core\PageCache\DelegatePageCache\PolicyBoundStorageSelector - arguments: ['@page_cache.internal_storage'] - calls: - - [add, ['@page_cache_policy', '@page_cache_policy.use_internal_config']] - - [add, ['@page_cache_policy', '@toolbar.page_cache_policy.toolbar_path']] + class: Drupal\toolbar\PageCache\IsToolbarPath tags: - - { name: delegate_page_cache.storage_selector } - toolbar.page_cache_selector.private_browser_cache: - class: Drupal\Core\PageCache\DelegatePageCache\PolicyBoundCacheControlSelector - arguments: ['@toolbar.page_cache.private_browser_cache'] - calls: - - [add, ['@page_cache_policy', '@toolbar.page_cache_policy.toolbar_path']] - tags: - - { name: delegate_page_cache.cache_control_selector } - toolbar.page_cache.private_browser_cache: - class: Drupal\Core\PageCache\CacheControl\PrivateBrowser - arguments: [31536000] + - { name: page_cache_policy.allow}