/** * Check if a cache response satisfies a request's caching constraints * * @param RequestInterface $request Request to validate * @param Response $response Response to validate * * @return bool */ public function canResponseSatisfyRequest(RequestInterface $request, Response $response) { $responseAge = $response->getAge(); // Check the request's max-age header against the age of the response if ($request->hasCacheControlDirective('max-age') && $responseAge > $request->getCacheControlDirective('max-age')) { return false; } // Check the response's max-age header if ($response->isFresh() === false) { $maxStale = $request->getCacheControlDirective('max-stale'); if (null !== $maxStale) { if ($maxStale !== true && $response->getFreshness() < -1 * $maxStale) { return false; } } elseif ($response->hasCacheControlDirective('max-age') && $responseAge > $response->getCacheControlDirective('max-age')) { return false; } } // Only revalidate GET requests if ($request->getMethod() == RequestInterface::GET) { // Check if the response must be validated against the origin server if ($request->getHeader('Pragma') == 'no-cache' || $request->hasCacheControlDirective('no-cache') || $request->hasCacheControlDirective('must-revalidate') || $response->hasCacheControlDirective('must-revalidate') || $response->hasCacheControlDirective('no-cache')) { // no-cache: When no parameters are present, always revalidate // When parameters are present in no-cache and the request includes those same parameters, then the // response must re-validate. I'll need an example of what fields look like in order to implement a // smarter version of no-cache // Requests can decline to revalidate against the origin server by setting the cache.revalidate param: // - never - To never revalidate and always contact the origin server // - skip - To skip revalidation and just use what is in cache switch ($request->getParams()->get('cache.revalidate')) { case 'never': return false; case 'skip': return true; default: return $this->revalidation->revalidate($request, $response); } } } return true; }
/** * @covers Guzzle\Http\Message\Response::getFreshness * @covers Guzzle\Http\Message\Response::isFresh */ public function testCalculatesFreshness() { $response = new Response(200); $this->assertNull($response->isFresh()); $this->assertNull($response->getFreshness()); $response->addCacheControlDirective('max-age', 120); $response->setHeader('Age', 100); $this->assertEquals(20, $response->getFreshness()); $this->assertTrue($response->isFresh()); $response->setHeader('Age', 150); $this->assertEquals(-30, $response->getFreshness()); $this->assertFalse($response->isFresh()); }
/** * Add the plugin's headers to a response * * @param RequestInterface $request Request * @param Response $response Response to add headers to */ protected function addResponseHeaders(RequestInterface $request, Response $response) { $params = $request->getParams(); $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache'; if ($header = $response->getHeader('X-Cache-Lookup')) { // Don't add duplicates $values = $header->toArray(); $values[] = $lookup; $response->setHeader('X-Cache-Lookup', array_unique($values)); } else { $response->setHeader('X-Cache-Lookup', $lookup); } if ($params['cache.hit'] === true) { $xcache = 'HIT from GuzzleCache'; } elseif ($params['cache.hit'] == 'error') { $xcache = 'HIT_ERROR from GuzzleCache'; } else { $xcache = 'MISS from GuzzleCache'; } if ($header = $response->getHeader('X-Cache')) { // Don't add duplicates $values = $header->toArray(); $values[] = $xcache; $response->setHeader('X-Cache', array_unique($values)); } else { $response->setHeader('X-Cache', $xcache); } if ($response->isFresh() === false) { $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); if ($params['cache.hit'] === 'error') { $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); } } }
/** * Add the plugin's headers to a response * * @param string $cacheKey Cache key * @param RequestInterface $request Request * @param Response $response Response to add headers to */ protected function addResponseHeaders($cacheKey, RequestInterface $request, Response $response) { if (!$response->hasHeader('X-Guzzle-Cache')) { $response->setHeader('X-Guzzle-Cache', "key={$cacheKey}"); } $response->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); if ($this->debugHeaders) { if ($request->getParams()->get('cache.lookup') === true) { $response->addHeader('X-Cache-Lookup', 'HIT from GuzzleCache'); } else { $response->addHeader('X-Cache-Lookup', 'MISS from GuzzleCache'); } if ($request->getParams()->get('cache.hit') === true) { $response->addHeader('X-Cache', 'HIT from GuzzleCache'); } elseif ($request->getParams()->get('cache.hit') === 'error') { $response->addHeader('X-Cache', 'HIT_ERROR from GuzzleCache'); } else { $response->addHeader('X-Cache', 'MISS from GuzzleCache'); } } if ($response->isFresh() === false) { $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); if ($request->getParams()->get('cache.hit') === 'error') { $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); } } }