/** * Check if an already cached request must be revalidated, and if so update * the request with the correct ETag headers. * @param HttpRequest $cached A previously cached response. * @param HttpRequest $request The outbound request. * return bool If the cached object needs to be revalidated, false if it is * still current and can be re-used. */ protected function checkMustRevaliadateCachedRequest($cached, $request) { if (Google_CacheParser::mustRevalidate($cached)) { $addHeaders = array(); if ($cached->getResponseHeader('etag')) { // [13.3.4] If an entity tag has been provided by the origin server, // we must use that entity tag in any cache-conditional request. $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag'); } elseif ($cached->getResponseHeader('date')) { $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date'); } $request->setRequestHeaders($addHeaders); return true; } else { return false; } }
/** * Execute a apiHttpRequest * * @param Google_HttpRequest $request the http request to be executed * @return Google_HttpRequest http request with the response http code, response * headers and response body filled in * @throws Google_IOException on curl or IO error */ public function makeRequest(Google_HttpRequest $request) { // First, check to see if we have a valid cached version. $cached = $this->getCachedRequest($request); if ($cached !== false) { if (Google_CacheParser::mustRevalidate($cached)) { $addHeaders = array(); if ($cached->getResponseHeader('etag')) { // [13.3.4] If an entity tag has been provided by the origin server, // we must use that entity tag in any cache-conditional request. $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag'); } elseif ($cached->getResponseHeader('date')) { $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date'); } $request->setRequestHeaders($addHeaders); } else { // No need to revalidate the request, return it directly return $cached; } } if (array_key_exists($request->getRequestMethod(), self::$ENTITY_HTTP_METHODS)) { $request = $this->processEntityRequest($request); } $ch = curl_init(); curl_setopt_array($ch, $this->curlParams); curl_setopt($ch, CURLOPT_URL, $request->getUrl()); if ($request->getPostBody()) { curl_setopt($ch, CURLOPT_POSTFIELDS, $request->getPostBody()); } $requestHeaders = $request->getRequestHeaders(); if ($requestHeaders && is_array($requestHeaders)) { $parsed = array(); foreach ($requestHeaders as $k => $v) { $parsed[] = "{$k}: {$v}"; } curl_setopt($ch, CURLOPT_HTTPHEADER, $parsed); } curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request->getRequestMethod()); curl_setopt($ch, CURLOPT_USERAGENT, $request->getUserAgent()); $respData = curl_exec($ch); // Retry if certificates are missing. if (curl_errno($ch) == CURLE_SSL_CACERT) { error_log('SSL certificate problem, verify that the CA cert is OK.' . ' Retrying with the CA cert bundle from google-api-php-client.'); curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacerts.pem'); $respData = curl_exec($ch); } $respHeaderSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $respHttpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlErrorNum = curl_errno($ch); $curlError = curl_error($ch); curl_close($ch); if ($curlErrorNum != CURLE_OK) { throw new Google_IOException("HTTP Error: ({$respHttpCode}) {$curlError}"); } // Parse out the raw response into usable bits list($responseHeaders, $responseBody) = self::parseHttpResponse($respData, $respHeaderSize); if ($respHttpCode == 304 && $cached) { // If the server responded NOT_MODIFIED, return the cached request. if (isset($responseHeaders['connection'])) { $hopByHop = array_merge(self::$HOP_BY_HOP, explode(',', $responseHeaders['connection'])); $endToEnd = array(); foreach ($hopByHop as $key) { if (isset($responseHeaders[$key])) { $endToEnd[$key] = $responseHeaders[$key]; } } $cached->setResponseHeaders($endToEnd); } return $cached; } // Fill in the apiHttpRequest with the response values $request->setResponseHttpCode($respHttpCode); $request->setResponseHeaders($responseHeaders); $request->setResponseBody($responseBody); // Store the request in cache (the function checks to see if the request // can actually be cached) $this->setCachedRequest($request); // And finally return it return $request; }
public function testMustRevalidate() { $now = time(); // Expires 1 year in the future, and contains the must-revalidate directive. // Don't revalidate. must-revalidate only applies to expired entries. $future = $now + 365 * 24 * 60 * 60; $resp = new Google_HttpRequest('http://localhost', 'GET'); $resp->setResponseHttpCode('200'); $resp->setResponseHeaders(array('Cache-Control' => 'max-age=3600, must-revalidate', 'Expires' => gmdate('D, d M Y H:i:s', $future) . ' GMT', 'Date' => gmdate('D, d M Y H:i:s', $now) . ' GMT')); $this->assertFalse(Google_CacheParser::mustRevalidate($resp)); // Contains the max-age=3600 directive, but was created 2 hours ago. // Must revalidate. $past = $now - 2 * 60 * 60; $resp = new Google_HttpRequest('http://localhost', 'GET'); $resp->setResponseHttpCode('200'); $resp->setResponseHeaders(array('Cache-Control' => 'max-age=3600', 'Expires' => gmdate('D, d M Y H:i:s', $future) . ' GMT', 'Date' => gmdate('D, d M Y H:i:s', $past) . ' GMT')); $this->assertTrue(Google_CacheParser::mustRevalidate($resp)); // Contains the max-age=3600 directive, and was created 600 seconds ago. // No need to revalidate, regardless of the expires header. $past = $now - 600; $resp = new Google_HttpRequest('http://localhost', 'GET'); $resp->setResponseHttpCode('200'); $resp->setResponseHeaders(array('Cache-Control' => 'max-age=3600', 'Expires' => gmdate('D, d M Y H:i:s', $past) . ' GMT', 'Date' => gmdate('D, d M Y H:i:s', $past) . ' GMT')); $this->assertFalse(Google_CacheParser::mustRevalidate($resp)); }