/** * Override of the set_cache method, works in exactly the same way except * the check for the max-age header in the response has been removed so that * Codebase API responses will always be cached, this breaks HTTP Cache * rules but is the cleanest way of enabling caching for all responses * within Kohana. * * @param Response $response * @return boolean */ public function set_cache(Response $response) { $headers = $response->headers()->getArrayCopy(); if ($cache_control = Arr::get($headers, 'cache-control')) { // Parse the cache control $cache_control = HTTP_Header::parse_cache_control($cache_control); // If the no-cache or no-store directive is set, return if (array_intersect($cache_control, array('no-cache', 'no-store'))) { return FALSE; } // Check for private cache and get out of here if invalid if (!$this->_allow_private_cache and in_array('private', $cache_control)) { if (!isset($cache_control['s-maxage'])) { return FALSE; } // If there is a s-maxage directive we can use that $cache_control['max-age'] = $cache_control['s-maxage']; } } /** * if the max-age cache control header is set to 0 in the response, set * it to 1 hour so the reponse will be cacheable */ $cache_control_header = $response->headers('Cache-Control'); $response->headers('Cache-Control', str_replace('max-age=0', 'max-age=3600', $cache_control_header)); if ($expires = Arr::get($headers, 'expires') and !isset($cache_control['max-age'])) { // Can't cache things that have expired already if (strtotime($expires) <= time()) { return FALSE; } } return TRUE; }
/** * Calculates the total Time To Live based on the specification * RFC 2616 cache lifetime rules. * * @param Response $response Response to evaluate * @return mixed TTL value or false if the response should not be cached */ public function cache_lifetime(Response $response) { // Get out of here if this cannot be cached if (!$this->set_cache($response)) { return FALSE; } // Calculate apparent age if ($date = $response->headers('date')) { $apparent_age = max(0, $this->_response_time - strtotime($date)); } else { $apparent_age = max(0, $this->_response_time); } // Calculate corrected received age if ($age = $response->headers('age')) { $corrected_received_age = max($apparent_age, intval($age)); } else { $corrected_received_age = $apparent_age; } // Corrected initial age $corrected_initial_age = $corrected_received_age + $this->request_execution_time(); // Resident time $resident_time = time() - $this->_response_time; // Current age $current_age = $corrected_initial_age + $resident_time; // Prepare the cache freshness lifetime $ttl = NULL; // Cache control overrides if ($cache_control = $response->headers('cache-control')) { // Parse the cache control header $cache_control = HTTP_Header::parse_cache_control($cache_control); if (isset($cache_control['max-age'])) { $ttl = $cache_control['max-age']; } if (isset($cache_control['s-maxage']) and isset($cache_control['private']) and $this->_allow_private_cache) { $ttl = $cache_control['s-maxage']; } if (isset($cache_control['max-stale']) and !isset($cache_control['must-revalidate'])) { $ttl = $current_age + $cache_control['max-stale']; } } // If we have a TTL at this point, return if ($ttl !== NULL) { return $ttl; } if ($expires = $response->headers('expires')) { return strtotime($expires) - $current_age; } return FALSE; }
/** * Tests that `parse_cache_control()` outputs the correct cache control * parsed data from the input string * * @dataProvider provider_parse_cache_control * * @param string $input input * @param array $expected expected * @return void */ public function test_parse_cache_control($input, array $expected) { $parsed = HTTP_Header::parse_cache_control($input); $this->assertInternalType('array', $parsed); foreach ($expected as $key => $value) { if (is_int($key)) { $this->assertTrue(in_array($value, $parsed)); } else { $this->assertTrue(array_key_exists($key, $parsed)); $this->assertSame($value, $parsed[$key]); } } }