function drupal_serve_page_from_cache

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.

3 calls to drupal_serve_page_from_cache()
FinishResponseSubscriber::onRespond in drupal/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
Sets extra headers on successful responses.
_drupal_bootstrap_page_cache in drupal/core/includes/bootstrap.inc
Attempts to serve a page from the cache.
_toolbar_initialize_page_cache in drupal/core/modules/toolbar/toolbar.module
Use Drupal's page cache for toolbar/subtrees/*, even for authenticated users.

File

drupal/core/includes/bootstrap.inc, line 1233
Functions that need to be loaded on every Drupal request.

Code

function drupal_serve_page_from_cache(stdClass $cache, Response $response, Request $request) {
  $config = config('system.performance');

  // First half: we must determine if we should be returning a 304.
  // Negotiate whether to use compression.
  $page_compression = $config
    ->get('response.gzip') && extension_loaded('zlib');
  $return_compressed = $page_compression && $request->server
    ->has('HTTP_ACCEPT_ENCODING') && strpos($request->server
    ->get('HTTP_ACCEPT_ENCODING'), 'gzip') !== FALSE;

  // Get headers. Keys are lower-case.
  $boot_headers = drupal_get_http_header();
  foreach ($cache->data['headers'] as $name => $value) {

    // In the case of a 304 response, certain headers must be sent, and the
    // remaining may not (see RFC 2616, section 10.3.5).
    $name_lower = strtolower($name);
    if (in_array($name_lower, array(
      'content-location',
      'expires',
      'cache-control',
      'vary',
    )) && !isset($boot_headers[$name_lower])) {
      $response->headers
        ->set($name, $value);
      unset($cache->data['headers'][$name]);
    }
  }

  // If the client sent a session cookie, a cached copy will only be served
  // to that one particular client due to Vary: Cookie. Thus, do not set
  // max-age > 0, allowing the page to be cached by external proxies, when a
  // session cookie is present unless the Vary header has been replaced.
  $max_age = !$request->cookies
    ->has(session_name()) || isset($boot_headers['vary']) ? $config
    ->get('cache.page.max_age') : 0;
  $response->headers
    ->set('Cache-Control', 'public, max-age=' . $max_age);

  // Entity tag should change if the output changes.
  $response
    ->setEtag($cache->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') && $if_modified_since == $cache->created) {

    // if-modified-since must match
    $response
      ->setStatusCode(304);
    return;
  }

  // Second half: we're not returning a 304, so put in other headers.
  // Send the remaining headers.
  foreach ($cache->data['headers'] as $name => $value) {
    $response->headers
      ->set($name, $value);
    drupal_add_http_header($name, $value);
  }
  $response
    ->setLastModified(\DateTime::createFromFormat('U', $cache->created));

  // HTTP/1.0 proxies does not support the Vary header, so prevent any caching
  // by sending an Expires date in the past. HTTP/1.1 clients ignores the
  // Expires header if a Cache-Control: max-age= directive is specified (see RFC
  // 2616, section 14.9.3).
  if (!$response
    ->getExpires()) {
    $response
      ->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 GMT'));
  }

  // 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 (!isset($boot_headers['vary']) && !settings()
    ->get('omit_vary_cookie')) {
    $response
      ->setVary('Cookie', FALSE);
  }
  if ($page_compression) {
    $response
      ->setVary('accept-encoding', FALSE);

    // If page_compression is enabled, the cache contains gzipped data.
    if ($return_compressed) {

      // $cache->data['body'] is already gzip'ed, so make sure
      // zlib.output_compression does not compress it once more.
      ini_set('zlib.output_compression', '0');
      $response->headers
        ->set('content-encoding', 'gzip');
    }
    else {

      // The client does not support compression, so unzip the data in the
      // cache. Strip the gzip header and run uncompress.
      $cache->data['body'] = gzinflate(substr(substr($cache->data['body'], 10), 0, -8));
    }
  }
  $response
    ->setContent($cache->data['body']);
}