Commit 1e985b0d authored by alexpott's avatar alexpott

Issue #2348679 by znerol, Wim Leers: Move the remaining procedural page cache...

Issue #2348679 by znerol, Wim Leers: Move the remaining procedural page cache code to the page cache stack middleware
parent 5d20c57b
......@@ -124,6 +124,7 @@ services:
class: Drupal\Core\PageCache\ChainResponsePolicy
tags:
- { name: service_collector, tag: page_cache_response_policy, call: addPolicy}
lazy: true
page_cache_kill_switch:
class: Drupal\Core\PageCache\ResponsePolicy\KillSwitch
tags:
......@@ -455,7 +456,7 @@ services:
- { name: http_middleware, priority: 300 }
http_middleware.page_cache:
class: Drupal\Core\StackMiddleware\PageCache
arguments: ['@kernel']
arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy', '@content_negotiation']
tags:
- { name: http_middleware, priority: 200 }
http_middleware.kernel_pre_handle:
......
......@@ -68,25 +68,18 @@
const DRUPAL_BOOTSTRAP_KERNEL = 1;
/**
* Third bootstrap phase: try to serve a cached page.
* Third bootstrap phase: load code for subsystems and modules.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
*/
const DRUPAL_BOOTSTRAP_PAGE_CACHE = 2;
/**
* Fourth bootstrap phase: load code for subsystems and modules.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
*/
const DRUPAL_BOOTSTRAP_CODE = 3;
const DRUPAL_BOOTSTRAP_CODE = 2;
/**
* Final bootstrap phase: initialize language, path, theme, and modules.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
*/
const DRUPAL_BOOTSTRAP_FULL = 4;
const DRUPAL_BOOTSTRAP_FULL = 3;
/**
* Role ID for anonymous users; should match what's in the "role" table.
......@@ -310,39 +303,6 @@ function drupal_get_path($type, $name) {
return dirname(drupal_get_filename($type, $name));
}
/**
* Gets the page cache cid for this request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request for this page.
*
* @return string
* The cid for this request.
*/
function drupal_page_cache_get_cid(Request $request) {
$cid_parts = array(
$request->getUri(),
\Drupal::service('content_negotiation')->getContentType($request),
);
return implode(':', $cid_parts);
}
/**
* Retrieves the current page from the cache.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request for this page.
*
* @return \Symfony\Component\HttpFoundation\Response
* The response, if the page was found in the cache, NULL otherwise.
*/
function drupal_page_get_cache(Request $request) {
$cache = \Drupal::cache('render')->get(drupal_page_cache_get_cid($request));
if ($cache) {
return $cache->data;
}
}
/**
* Sets an HTTP response header for the current page.
*
......@@ -426,72 +386,6 @@ function _drupal_set_preferred_header_name($name = NULL) {
$header_names[strtolower($name)] = $name;
}
/**
* Sets HTTP headers in preparation for a cached page response.
*
* The headers allow as much as possible in proxies and browsers without any
* particular knowledge about the pages. Modules can override these headers
* using _drupal_add_http_header().
*
* 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.
*/
function drupal_serve_page_from_cache(Response $response, Request $request) {
// Only allow caching in the browser and prevent that the response is stored
// by an external proxy server when the following conditions apply:
// 1. There is a session cookie on the request.
// 2. The Vary: Cookie header is on the response.
// 3. The Cache-Control header does not contain the no-cache directive.
if ($request->cookies->has(session_name()) &&
in_array('Cookie', $response->getVary()) &&
!$response->headers->hasCacheControlDirective('no-cache')) {
$response->setPrivate();
}
// Negotiate whether to use compression.
if ($response->headers->get('Content-Encoding') == 'gzip' && extension_loaded('zlib')) {
if (strpos($request->headers->get('Accept-Encoding'), 'gzip') !== FALSE) {
// The response content is already gzip'ed, so make sure
// zlib.output_compression does not compress it once more.
ini_set('zlib.output_compression', '0');
}
else {
// The client does not support compression. Decompress the content and
// remove the Content-Encoding header.
$content = $response->getContent();
$content = gzinflate(substr(substr($content, 10), 0, -8));
$response->setContent($content);
$response->headers->remove('Content-Encoding');
}
}
// Perform HTTP revalidation.
// @todo Use Response::isNotModified() as per https://drupal.org/node/2259489
$last_modified = $response->getLastModified();
if ($last_modified) {
// 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 == $last_modified->getTimestamp()) { // 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);
}
}
}
}
}
/**
* Translates a string to the current language or to a given language.
*
......@@ -830,10 +724,6 @@ function drupal_bootstrap($phase = NULL) {
$kernel->boot();
break;
case DRUPAL_BOOTSTRAP_PAGE_CACHE:
$kernel->handlePageCache($request);
break;
case DRUPAL_BOOTSTRAP_CODE:
case DRUPAL_BOOTSTRAP_FULL:
$kernel->prepareLegacyRequest($request);
......
......@@ -1267,48 +1267,6 @@ function drupal_clear_js_cache() {
\Drupal::service('asset.js.collection_optimizer')->deleteAll();
}
/**
* Stores the current page in the cache.
*
* If page_compression is enabled, a gzipped version of the page is stored in
* the cache to avoid compressing the output on each request. The cache entry
* is unzipped in the relatively rare event that the page is requested by a
* client without gzip support.
*
* Page compression requires the PHP zlib extension
* (http://php.net/manual/ref.zlib.php).
*
* @param \Symfony\Component\HttpFoundation\Response $response
* The fully populated response.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request for this page.
*/
function drupal_page_set_cache(Response $response, Request $request) {
// Check if the current page may be compressed.
if (extension_loaded('zlib') && !$response->headers->get('Content-Encoding') &&
\Drupal::config('system.performance')->get('response.gzip')) {
$content = $response->getContent();
if ($content) {
$response->setContent(gzencode($content, 9, FORCE_GZIP));
$response->headers->set('Content-Encoding', 'gzip');
}
// When page compression is enabled, ensure that proxy caches will record
// and deliver different versions of a page depending on whether the
// client supports gzip or not.
$response->setVary('Accept-Encoding', FALSE);
}
// Use the actual timestamp from an Expires header, if available.
$date = $response->getExpires();
$expire = ($date > (new DateTime())) ? $date->getTimestamp() : Cache::PERMANENT;
$cid = drupal_page_cache_get_cid($request);
$tags = explode(' ', $response->headers->get('X-Drupal-Cache-Tags'));
\Drupal::cache('render')->set($cid, $response, $expire, $tags);
}
/**
* Pre-render callback: Renders a link into #markup.
*
......
......@@ -76,6 +76,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
*/
protected $booted = FALSE;
/**
* Whether essential services have been set up properly by preHandle().
*
* @var bool
*/
protected $prepared = FALSE;
/**
* Holds the list of enabled modules.
*
......@@ -474,42 +481,8 @@ public function preHandle(Request $request) {
// Override of Symfony's mime type guesser singleton.
MimeTypeGuesser::registerWithSymfonyGuesser($this->container);
}
/**
* {@inheritdoc}
*
* @todo Invoke proper request/response/terminate events.
*/
public function handlePageCache(Request $request) {
$this->boot();
// Check for a cache mode force from settings.php.
if (Settings::get('page_cache_without_database')) {
$cache_enabled = TRUE;
}
else {
$config = $this->getContainer()->get('config.factory')->get('system.performance');
$cache_enabled = $config->get('cache.page.use_internal');
}
$request_policy = \Drupal::service('page_cache_request_policy');
if ($cache_enabled && $request_policy->check($request) === RequestPolicyInterface::ALLOW) {
// Get the page from the cache.
$response = drupal_page_get_cache($request);
// If there is a cached page, display it.
if ($response) {
$response->headers->set('X-Drupal-Cache', 'HIT');
drupal_serve_page_from_cache($response, $request);
// We are done.
$response->prepare($request);
$response->send();
exit;
}
}
return $this;
$this->prepared = TRUE;
}
/**
......@@ -577,7 +550,9 @@ public function getServiceProviders($origin) {
* {@inheritdoc}
*/
public function terminate(Request $request, Response $response) {
if (FALSE === $this->booted) {
// Only run terminate() when essential services have been set up properly
// by preHandle() before.
if (FALSE === $this->prepared) {
return;
}
......
......@@ -93,16 +93,6 @@ public function getAppRoot();
*/
public function updateModules(array $module_list, array $module_filenames = array());
/**
* Attempts to serve a page from the cache.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*
* @return $this
*/
public function handlePageCache(Request $request);
/**
* Prepare the kernel for handling a request without handling the request.
*
......
......@@ -136,17 +136,6 @@ public function onRespond(FilterResponseEvent $event) {
// header declaring the response as not cacheable.
$this->setResponseNotCacheable($response, $request);
}
// Currently it is not possible to cache some types of responses. Therefore
// exclude binary file responses (generated files, e.g. images with image
// styles) and streamed responses (files directly read from the disk).
// see: https://github.com/symfony/symfony/issues/9128#issuecomment-25088678
if ($is_cacheable && $this->config->get('cache.page.use_internal') && !($response instanceof BinaryFileResponse) && !($response instanceof StreamedResponse)) {
// Store the response in the internal page cache.
drupal_page_set_cache($response, $request);
$response->headers->set('X-Drupal-Cache', 'MISS');
drupal_serve_page_from_cache($response, $request);
}
}
/**
......
......@@ -25,13 +25,18 @@ public function testDestructionUsed() {
// Enable the test module to add it to the container.
$this->enableModules(array('service_provider_test'));
$request = $this->container->get('request_stack')->getCurrentRequest();
$kernel = $this->container->get('kernel');
$kernel->preHandle($request);
// The service has not been destructed yet.
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
// Call the class and then terminate the kernel
$this->container->get('service_provider_test_class');
$response = new Response();
$this->container->get('kernel')->terminate($this->container->get('request_stack')->getCurrentRequest(), $response);
$kernel->terminate($request, $response);
$this->assertTrue(\Drupal::state()->get('service_provider_test.destructed'));
}
......@@ -42,13 +47,17 @@ public function testDestructionUnused() {
// Enable the test module to add it to the container.
$this->enableModules(array('service_provider_test'));
$request = $this->container->get('request_stack')->getCurrentRequest();
$kernel = $this->container->get('kernel');
$kernel->preHandle($request);
// The service has not been destructed yet.
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
// Terminate the kernel. The test class has not been called, so it should not
// be destructed.
$response = new Response();
$this->container->get('kernel')->terminate($this->container->get('request_stack')->getCurrentRequest(), $response);
$kernel->terminate($request, $response);
$this->assertNull(\Drupal::state()->get('service_provider_test.destructed'));
}
}
......@@ -26,7 +26,6 @@
$request = Request::createFromGlobals();
$kernel = TestKernel::createFromRequest($request, $autoloader, 'testing', TRUE);
$response = $kernel
->handlePageCache($request)
->handle($request)
// Handle the response object.
->prepare($request)->send();
......
......@@ -28,7 +28,6 @@
$request = Request::createFromGlobals();
$kernel = TestKernel::createFromRequest($request, $autoloader, 'testing', TRUE);
$response = $kernel
->handlePageCache($request)
->handle($request)
// Handle the response object.
->prepare($request)->send();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment