diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 7dc75f5..4924ef3 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1151,6 +1151,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. @@ -1170,7 +1172,6 @@ function drupal_add_http_header($name, $value, $append = FALSE) { else { $headers[$name_lower] = $value; } - drupal_send_headers(array($name => $headers[$name_lower]), TRUE); } /** @@ -1183,6 +1184,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()); @@ -1199,7 +1202,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(); @@ -1221,6 +1226,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); @@ -1275,6 +1282,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); @@ -1303,7 +1312,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, \Symfony\Component\HttpFoundation\Response $response) { $config = config('system.performance'); // Negotiate whether to use compression. @@ -1322,7 +1331,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]); } } @@ -1336,7 +1345,7 @@ function drupal_serve_page_from_cache(stdClass $cache) { // Entity tag should change if the output changes. $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"'; - header('Etag: ' . $etag); + $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; @@ -1345,25 +1354,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 @@ -1371,17 +1378,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 @@ -1390,8 +1397,7 @@ function drupal_serve_page_from_cache(stdClass $cache) { } } - // Print the page. - print $cache->data['body']; + $response->setContent($cache->data['body']); } /** @@ -2156,24 +2162,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 \Symfony\Component\HttpFoundation\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 eabc080..6d7d5b6 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -4649,15 +4649,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(), ), @@ -4666,17 +4666,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 c5c491c..a01a0f4 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -46,20 +46,22 @@ 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 // rendering engine or use Chrome's frame rendering engine if available. - $response->headers->set('X-UA-Compatible', 'IE=edge,chrome=1', false); + $response->headers->set('x-ua-compatible', 'IE=edge,chrome=1', FALSE); // Set the Content-language header. - $response->headers->set('Content-language', $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE)->langcode); + $response->headers->set('content-language', $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE)->langcode); // Because pages are highly dynamic, set the last-modified time to now // 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. - $response->headers->set('Last-Modified', gmdate(DATE_RFC1123, REQUEST_TIME)); + // @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 // both an If-Modified-Since header and an If-None-Match header when doing @@ -81,26 +83,32 @@ 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'); - $response->headers->set('Cache-Control', 'no-cache, must-revalidate, post-check=0, pre-check=0'); + $response->headers->set('cache-control', 'no-cache, must-revalidate, post-check=0, pre-check=0'); } }