/** * Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling. * * @param HTTPResponse The HTTPResponse object to augment. Omitted the argument or passing a string is deprecated; in these * cases, the headers are output directly. */ static function add_cache_headers($body = null) { // Validate argument if ($body && !$body instanceof HTTPResponse) { user_error("HTTP::add_cache_headers() must be passed an HTTPResponse object", E_USER_WARNING); $body = null; } // Development sites have frequently changing templates; this can get stuffed up by the code // below. if (Director::isDev()) { return; } // The headers have been sent and we don't have an HTTPResponse object to attach things to; no point in us trying. if (headers_sent() && !$body) { return; } // Popuplate $responseHeaders with all the headers that we want to build $responseHeaders = array(); if (function_exists('apache_request_headers')) { $requestHeaders = apache_request_headers(); if (isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With'] == 'XMLHttpRequest') { self::$cache_age = 0; } // bdc: now we must check for DUMB IE6: if (isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with'] == 'XMLHttpRequest') { self::$cache_age = 0; } } if (self::$cache_age > 0) { $responseHeaders["Cache-Control"] = "max-age=" . self::$cache_age . ", must-revalidate"; $responseHeaders["Pragma"] = ""; } else { $responseHeaders["Cache-Control"] = "no-cache, max-age=0, must-revalidate"; } if (self::$modification_date && self::$cache_age > 0) { $responseHeaders["Last-Modified"] = self::gmt_date(self::$modification_date); // 304 response detection if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $ifModifiedSince = strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE'])); if ($ifModifiedSince >= self::$modification_date) { if ($body) { $body->setStatusCode(304); $body->setBody(''); } else { header('HTTP/1.0 304 Not Modified'); die; } } } $expires = time() + self::$cache_age; $responseHeaders["Expires"] = self::gmt_date($expires); } if (self::$etag) { $responseHeaders['ETag'] = self::$etag; } // Now that we've generated them, either output them or attach them to the HTTPResponse as appropriate foreach ($responseHeaders as $k => $v) { if ($body) { $body->addHeader($k, $v); } else { if (!headers_sent()) { header("{$k}: {$v}"); } } } }
/** * Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling. * * @param SS_HTTPResponse The SS_HTTPResponse object to augment. Omitted the argument or passing a string is * deprecated; in these cases, the headers are output directly. */ public static function add_cache_headers($body = null) { // Validate argument if ($body && !$body instanceof SS_HTTPResponse) { user_error("HTTP::add_cache_headers() must be passed an SS_HTTPResponse object", E_USER_WARNING); $body = null; } // Development sites have frequently changing templates; this can get stuffed up by the code // below. if (Director::isDev()) { return; } // The headers have been sent and we don't have an SS_HTTPResponse object to attach things to; no point in // us trying. if (headers_sent() && !$body) { return; } // Popuplate $responseHeaders with all the headers that we want to build $responseHeaders = array(); if (function_exists('apache_request_headers')) { $requestHeaders = apache_request_headers(); if (isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With'] == 'XMLHttpRequest') { self::$cache_age = 0; } // bdc: now we must check for DUMB IE6: if (isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with'] == 'XMLHttpRequest') { self::$cache_age = 0; } } if (self::$cache_age > 0) { $responseHeaders["Cache-Control"] = "max-age=" . self::$cache_age . ", must-revalidate, no-transform"; $responseHeaders["Pragma"] = ""; // To do: User-Agent should only be added in situations where you *are* actually // varying according to user-agent. $responseHeaders['Vary'] = 'Cookie, X-Forwarded-Protocol, User-Agent, Accept'; } else { if ($body) { // Grab header for checking. Unfortunately HTTPRequest uses a mistyped variant. $contentDisposition = $body->getHeader('Content-disposition'); if (!$contentDisposition) { $contentDisposition = $body->getHeader('Content-Disposition'); } } if ($body && Director::is_https() && strstr($_SERVER["HTTP_USER_AGENT"], 'MSIE') == true && strstr($contentDisposition, 'attachment;') == true) { // IE6-IE8 have problems saving files when https and no-cache are used // (http://support.microsoft.com/kb/323308) // Note: this is also fixable by ticking "Do not save encrypted pages to disk" in advanced options. $responseHeaders["Cache-Control"] = "max-age=3, must-revalidate, no-transform"; $responseHeaders["Pragma"] = ""; } else { $responseHeaders["Cache-Control"] = "no-cache, max-age=0, must-revalidate, no-transform"; } } if (self::$modification_date && self::$cache_age > 0) { $responseHeaders["Last-Modified"] = self::gmt_date(self::$modification_date); // Chrome ignores Varies when redirecting back (http://code.google.com/p/chromium/issues/detail?id=79758) // which means that if you log out, you get redirected back to a page which Chrome then checks against // last-modified (which passes, getting a 304) // when it shouldn't be trying to use that page at all because it's the "logged in" version. // By also using and etag that includes both the modification date and all the varies // values which we also check against we can catch this and not return a 304 $etagParts = array(self::$modification_date, serialize($_COOKIE)); if (isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) { $etagParts[] = $_SERVER['HTTP_X_FORWARDED_PROTOCOL']; } if (isset($_SERVER['HTTP_USER_AGENT'])) { $etagParts[] = $_SERVER['HTTP_USER_AGENT']; } if (isset($_SERVER['HTTP_ACCEPT'])) { $etagParts[] = $_SERVER['HTTP_ACCEPT']; } $etag = sha1(implode(':', $etagParts)); $responseHeaders["ETag"] = $etag; // 304 response detection if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $ifModifiedSince = strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE'])); // As above, only 304 if the last request had all the same varies values // (or the etag isn't passed as part of the request - but with chrome it always is) $matchesEtag = !isset($_SERVER['HTTP_IF_NONE_MATCH']) || $_SERVER['HTTP_IF_NONE_MATCH'] == $etag; if ($ifModifiedSince >= self::$modification_date && $matchesEtag) { if ($body) { $body->setStatusCode(304); $body->setBody(''); } else { header('HTTP/1.0 304 Not Modified'); die; } } } $expires = time() + self::$cache_age; $responseHeaders["Expires"] = self::gmt_date($expires); } if (self::$etag) { $responseHeaders['ETag'] = self::$etag; } // Now that we've generated them, either output them or attach them to the SS_HTTPResponse as appropriate foreach ($responseHeaders as $k => $v) { if ($body) { $body->addHeader($k, $v); } else { if (!headers_sent()) { header("{$k}: {$v}"); } } } }
/** * Set the maximum age of this page in web caches, in seconds */ public static function set_cache_age($age) { self::$cache_age = $age; }
/** * Add the appropriate caching headers to the response * * @param string The reponse body */ static function add_cache_headers($body = null) { // Development sites have frequently changing templates; this can get stuffed up by the code // below. if (Director::isDev()) { return; } if (!headers_sent()) { if (function_exists('apache_request_headers')) { $headers = apache_request_headers(); if (isset($headers['X-Requested-With']) && $headers['X-Requested-With'] == 'XMLHttpRequest') { self::$cache_age = 0; } // bdc: now we must check for DUMB IE6: if (isset($headers['x-requested-with']) && $headers['x-requested-with'] == 'XMLHttpRequest') { self::$cache_age = 0; } } if (self::$cache_age > 0) { header("Cache-Control: max-age=" . self::$cache_age . ", must-revalidate"); header("Pragma:"); } else { header("Cache-Control: no-cache, max-age=0, must-revalidate"); } if (self::$modification_date && self::$cache_age > 0) { header("Last-Modified: " . self::gmt_date(self::$modification_date)); $expires = 2 * time() - self::$modification_date; header("Expires: " . self::gmt_date($expires)); } if (self::$etag) { header('ETag: ' . self::$etag); } } }