diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 6541350..42d61595 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Exception\RuntimeException as DependencyInjectionRuntimeException; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Drupal\Core\Language\Language; use Drupal\Core\Lock\DatabaseLockBackend; use Drupal\Core\Lock\LockBackendInterface; @@ -1067,6 +1068,8 @@ function drupal_load($type, $name) { * reason phrase, e.g. "404 Not Found". * @param $append * Whether to append the value to an existing header or to replace it. + * + * @deprecated */ function drupal_add_http_header($name, $value, $append = FALSE) { // The headers as name/value pairs. @@ -1086,7 +1089,6 @@ function drupal_add_http_header($name, $value, $append = FALSE) { else { $headers[$name_lower] = $value; } - drupal_send_headers(array($name => $headers[$name_lower]), TRUE); } /** @@ -1099,6 +1101,8 @@ function drupal_add_http_header($name, $value, $append = FALSE) { * @return * A string containing the header value, or FALSE if the header has been set, * or NULL if the header has not been set. + * + * @deprecated */ function drupal_get_http_header($name = NULL) { $headers = &drupal_static('drupal_http_headers', array()); @@ -1115,7 +1119,9 @@ function drupal_get_http_header($name = NULL) { * Sets the preferred name for the HTTP header. * * Header names are case-insensitive, but for maximum compatibility they should - * follow "common form" (see RFC 2617, section 4.2). + * follow "common form" (see RFC 2616, section 4.2). + * + * @deprecated */ function _drupal_set_preferred_header_name($name = NULL) { static $header_names = array(); @@ -1137,6 +1143,8 @@ function _drupal_set_preferred_header_name($name = NULL) { * @param bool $only_default * (optional) If TRUE and headers have already been sent, send only the * specified headers. + * + * @deprecated */ function drupal_send_headers($default_headers = array(), $only_default = FALSE) { $headers_sent = &drupal_static(__FUNCTION__, FALSE); @@ -1191,6 +1199,8 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE) * identical. * * @see drupal_page_set_cache() + * + * @deprecated */ function drupal_page_header() { $headers_sent = &drupal_static(__FUNCTION__, FALSE); @@ -1219,7 +1229,7 @@ function drupal_page_header() { * and the conditions match those currently in the cache, a 304 Not Modified * response is sent. */ -function drupal_serve_page_from_cache(stdClass $cache) { +function drupal_serve_page_from_cache(stdClass $cache, Response $response) { $config = config('system.performance'); // Negotiate whether to use compression. @@ -1238,7 +1248,7 @@ function drupal_serve_page_from_cache(stdClass $cache) { // 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])) { - drupal_add_http_header($name, $value); + $response->headers->set($name, $value); unset($cache->data['headers'][$name]); } } @@ -1248,11 +1258,11 @@ function drupal_serve_page_from_cache(stdClass $cache) { // 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 = !isset($_COOKIE[session_name()]) || isset($boot_headers['vary']) ? $config->get('cache.page.max_age') : 0; - $default_headers['Cache-Control'] = 'public, max-age=' . $max_age; + $response->headers->set('Cache-Control', 'public, max-age=' . $max_age); // Entity tag should change if the output changes. - $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"'; - header('Etag: ' . $etag); + $etag = $cache->created; + $response->setEtag($etag); // See if the client has provided the required HTTP headers. $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE; @@ -1261,25 +1271,23 @@ function drupal_serve_page_from_cache(stdClass $cache) { if ($if_modified_since && $if_none_match && $if_none_match == $etag // etag must match && $if_modified_since == $cache->created) { // if-modified-since must match - header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified'); - drupal_send_headers($default_headers); + $response->setStatusCode(304); return; } // Send the remaining headers. foreach ($cache->data['headers'] as $name => $value) { + $response->headers->set($name, $value); drupal_add_http_header($name, $value); } - $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created); + $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). - $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT'; - - drupal_send_headers($default_headers); + $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 @@ -1287,17 +1295,17 @@ function drupal_serve_page_from_cache(stdClass $cache) { // response to reply to a subsequent request for a given URL without // revalidation. if (!isset($boot_headers['vary']) && !settings()->get('omit_vary_cookie')) { - header('Vary: Cookie'); + $response->setVary('cookie'); } if ($page_compression) { - header('Vary: Accept-Encoding', FALSE); + $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'); - header('Content-Encoding: gzip'); + $response->headers->set('content-encoding', 'gzip'); } else { // The client does not support compression, so unzip the data in the @@ -1306,8 +1314,7 @@ function drupal_serve_page_from_cache(stdClass $cache) { } } - // Print the page. - print $cache->data['body']; + $response->setContent($cache->data['body']); } /** @@ -2072,24 +2079,28 @@ function _drupal_bootstrap_page_cache() { // If there is no session cookie and cache is enabled (or forced), try // to serve a cached page. if (!isset($_COOKIE[session_name()]) && $cache_enabled) { + $response = new Response(); // Make sure there is a user object because its timestamp will be checked. $user = drupal_anonymous_user(); // Get the page from the cache. $cache = drupal_page_get_cache(); // If there is a cached page, display it. if (is_object($cache)) { - header('X-Drupal-Cache: HIT'); + $response->headers->set('X-Drupal-Cache', 'HIT'); // Restore the metadata cached with the page. _current_path($cache->data['path']); drupal_set_title($cache->data['title'], PASS_THROUGH); date_default_timezone_set(drupal_get_user_timezone()); - drupal_serve_page_from_cache($cache); + drupal_serve_page_from_cache($cache, $response); + // We are done. + $response->send(); exit; } else { - header('X-Drupal-Cache: MISS'); + $response->headers->set('X-Drupal-Cache', 'MISS'); + $response->send(); } } } diff --git a/core/includes/common.inc b/core/includes/common.inc index 4f72021..c232d5d 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4650,15 +4650,15 @@ function _drupal_bootstrap_full($skip = FALSE) { * * @see drupal_page_header() */ -function drupal_page_set_cache($body) { +function drupal_page_set_cache(\Symfony\Component\HttpFoundation\Response $response, \Symfony\Component\HttpFoundation\Request $request) { global $base_root; if (drupal_page_is_cacheable()) { $cache = (object) array( - 'cid' => $base_root . request_uri(), + 'cid' => $base_root . $request->getRequestUri(), 'data' => array( - 'path' => current_path(), - 'body' => $body, + 'path' => $request->getQueryString(), + 'body' => $response->getContent(), 'title' => drupal_get_title(), 'headers' => array(), ), @@ -4667,17 +4667,12 @@ function drupal_page_set_cache($body) { 'created' => REQUEST_TIME, ); - // Restore preferred header names based on the lower-case names returned - // by drupal_get_http_header(). - $header_names = _drupal_set_preferred_header_name(); - foreach (drupal_get_http_header() as $name_lower => $value) { - $cache->data['headers'][$header_names[$name_lower]] = $value; - if ($name_lower == 'expires') { - // Use the actual timestamp from an Expires header if available. - $date = new DrupalDateTime($value); - $cache->expire = $date->getTimestamp(); - } - } + // @todo just setting this on the response SHOULD be adequate. + $cache->data['headers'] = $response->headers->all(); + + // Use the actual timestamp from an Expires header if available. + $date = new DrupalDateTime($response->getExpires()); + $cache->expire = $date->getTimestamp(); if ($cache->data['body']) { if (config('system.performance')->get('response.gzip') && extension_loaded('zlib')) { diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index 163d5c8..1853147 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -47,6 +47,7 @@ public function onRespond(FilterResponseEvent $event) { return; } + $request = $event->getRequest(); $response = $event->getResponse(); // Set the X-UA-Compatible HTTP header to force IE to use the most recent @@ -60,6 +61,7 @@ public function onRespond(FilterResponseEvent $event) { // since the page is in fact being regenerated right now. // @todo Remove this and use a more intelligent default so that HTTP // caching can function properly. + // @todo use $response->setLastModified() $response->headers->set('Last-Modified', gmdate(DATE_RFC1123, REQUEST_TIME)); // Also give each page a unique ETag. This will force clients to include @@ -82,22 +84,28 @@ public function onRespond(FilterResponseEvent $event) { // identical. // @todo Remove this line as no longer necessary per // http://drupal.org/node/1573064 - $response->headers->set('ETag', '"' . REQUEST_TIME . '"'); + $response->setEtag(REQUEST_TIME); // Authenticated users are always given a 'no-cache' header, and will fetch // a fresh page on every request. This prevents authenticated users from // seeing locally cached pages. // @todo Revisit whether or not this is still appropriate now that the - // Response object does its own cache control procesisng and we intend to + // Response object does its own cache control processing and we intend to // use partial page caching more extensively. // Commit the user session, if needed. drupal_session_commit(); + + // Attach globally-declared headers to the response object so that Symfony + // can send them for us correctly. + // @todo remove this once we have removed all drupal_add_http_header() calls + $headers = drupal_get_http_header(); + foreach ($headers as $name => $value) { + $response->headers->set($name, $value, FALSE); + } + $max_age = config('system.performance')->get('cache.page.max_age'); - if ($max_age > 0 && ($cache = drupal_page_set_cache($response->getContent()))) { - drupal_serve_page_from_cache($cache); - // drupal_serve_page_from_cache() already printed the response. - $response->setContent(''); - $response->headers->remove('cache-control'); + if ($max_age > 0 && ($cache = drupal_page_set_cache($response, $request))) { + drupal_serve_page_from_cache($cache, $response); } else { $response->headers->set('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT');