Google_IO_Abstract

The Google Photos & Picasa Viewer Google IO Abstract class.

Defined (1)

The class is defined in the following location(s).

/includes/api-libs/Google/IO/Abstract.php  
  1. abstract class Google_IO_Abstract 
  2. const UNKNOWN_CODE = 0; 
  3. const FORM_URLENCODED = 'application/x-www-form-urlencoded'; 
  4. private static $CONNECTION_ESTABLISHED_HEADERS = array( 
  5. "HTTP/1.0 200 Connection established\r\n\r\n",  
  6. "HTTP/1.1 200 Connection established\r\n\r\n",  
  7. ); 
  8. private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null); 
  9. private static $HOP_BY_HOP = array( 
  10. 'connection' => true,  
  11. 'keep-alive' => true,  
  12. 'proxy-authenticate' => true,  
  13. 'proxy-authorization' => true,  
  14. 'te' => true,  
  15. 'trailers' => true,  
  16. 'transfer-encoding' => true,  
  17. 'upgrade' => true 
  18. ); 
  19.  
  20.  
  21. /** @var Google_Client */ 
  22. protected $client; 
  23.  
  24. public function __construct(Google_Client $client) 
  25. $this->client = $client; 
  26. $timeout = $client->getClassConfig('Google_IO_Abstract', 'request_timeout_seconds'); 
  27. if ($timeout > 0) { 
  28. $this->setTimeout($timeout); 
  29.  
  30. /** 
  31. * Executes a Google_Http_Request and returns the resulting populated Google_Http_Request 
  32. * @param Google_Http_Request $request 
  33. * @return Google_Http_Request $request 
  34. */ 
  35. abstract public function executeRequest(Google_Http_Request $request); 
  36.  
  37. /** 
  38. * Set options that update the transport implementation's behavior. 
  39. * @param $options 
  40. */ 
  41. abstract public function setOptions($options); 
  42.  
  43. /** 
  44. * Set the maximum request time in seconds. 
  45. * @param $timeout in seconds 
  46. */ 
  47. abstract public function setTimeout($timeout); 
  48.  
  49. /** 
  50. * Get the maximum request time in seconds. 
  51. * @return timeout in seconds 
  52. */ 
  53. abstract public function getTimeout(); 
  54.  
  55. /** 
  56. * Test for the presence of a cURL header processing bug 
  57. * The cURL bug was present in versions prior to 7.30.0 and caused the header 
  58. * length to be miscalculated when a "Connection established" header added by 
  59. * some proxies was present. 
  60. * @return boolean 
  61. */ 
  62. abstract protected function needsQuirk(); 
  63.  
  64. /** 
  65. * @visible for testing. 
  66. * Cache the response to an HTTP request if it is cacheable. 
  67. * @param Google_Http_Request $request 
  68. * @return bool Returns true if the insertion was successful. 
  69. * Otherwise, return false. 
  70. */ 
  71. public function setCachedRequest(Google_Http_Request $request) 
  72. // Determine if the request is cacheable. 
  73. if (Google_Http_CacheParser::isResponseCacheable($request)) { 
  74. $this->client->getCache()->set($request->getCacheKey(), $request); 
  75. return true; 
  76.  
  77. return false; 
  78.  
  79. /** 
  80. * Execute an HTTP Request 
  81. * @param Google_HttpRequest $request the http request to be executed 
  82. * @return Google_HttpRequest http request with the response http code,  
  83. * response headers and response body filled in 
  84. * @throws Google_IO_Exception on curl or IO error 
  85. */ 
  86. public function makeRequest(Google_Http_Request $request) 
  87. // First, check to see if we have a valid cached version. 
  88. $cached = $this->getCachedRequest($request); 
  89. if ($cached !== false && $cached instanceof Google_Http_Request) { 
  90. if (!$this->checkMustRevalidateCachedRequest($cached, $request)) { 
  91. return $cached; 
  92.  
  93. if (array_key_exists($request->getRequestMethod(), self::$ENTITY_HTTP_METHODS)) { 
  94. $request = $this->processEntityRequest($request); 
  95.  
  96. list($responseData, $responseHeaders, $respHttpCode) = $this->executeRequest($request); 
  97.  
  98. if ($respHttpCode == 304 && $cached) { 
  99. // If the server responded NOT_MODIFIED, return the cached request. 
  100. $this->updateCachedRequest($cached, $responseHeaders); 
  101. return $cached; 
  102.  
  103. if (!isset($responseHeaders['Date']) && !isset($responseHeaders['date'])) { 
  104. $responseHeaders['date'] = date("r"); 
  105.  
  106. $request->setResponseHttpCode($respHttpCode); 
  107. $request->setResponseHeaders($responseHeaders); 
  108. $request->setResponseBody($responseData); 
  109. // Store the request in cache (the function checks to see if the request 
  110. // can actually be cached) 
  111. $this->setCachedRequest($request); 
  112. return $request; 
  113.  
  114. /** 
  115. * @visible for testing. 
  116. * @param Google_Http_Request $request 
  117. * @return Google_Http_Request|bool Returns the cached object or 
  118. * false if the operation was unsuccessful. 
  119. */ 
  120. public function getCachedRequest(Google_Http_Request $request) 
  121. if (false === Google_Http_CacheParser::isRequestCacheable($request)) { 
  122. return false; 
  123.  
  124. return $this->client->getCache()->get($request->getCacheKey()); 
  125.  
  126. /** 
  127. * @visible for testing 
  128. * Process an http request that contains an enclosed entity. 
  129. * @param Google_Http_Request $request 
  130. * @return Google_Http_Request Processed request with the enclosed entity. 
  131. */ 
  132. public function processEntityRequest(Google_Http_Request $request) 
  133. $postBody = $request->getPostBody(); 
  134. $contentType = $request->getRequestHeader("content-type"); 
  135.  
  136. // Set the default content-type as application/x-www-form-urlencoded. 
  137. if (false == $contentType) { 
  138. $contentType = self::FORM_URLENCODED; 
  139. $request->setRequestHeaders(array('content-type' => $contentType)); 
  140.  
  141. // Force the payload to match the content-type asserted in the header. 
  142. if ($contentType == self::FORM_URLENCODED && is_array($postBody)) { 
  143. $postBody = http_build_query($postBody, '', '&'); 
  144. $request->setPostBody($postBody); 
  145.  
  146. // Make sure the content-length header is set. 
  147. if (!$postBody || is_string($postBody)) { 
  148. $postsLength = strlen($postBody); 
  149. $request->setRequestHeaders(array('content-length' => $postsLength)); 
  150.  
  151. return $request; 
  152.  
  153. /** 
  154. * Check if an already cached request must be revalidated, and if so update 
  155. * the request with the correct ETag headers. 
  156. * @param Google_Http_Request $cached A previously cached response. 
  157. * @param Google_Http_Request $request The outbound request. 
  158. * return bool If the cached object needs to be revalidated, false if it is 
  159. * still current and can be re-used. 
  160. */ 
  161. protected function checkMustRevalidateCachedRequest($cached, $request) 
  162. if (Google_Http_CacheParser::mustRevalidate($cached)) { 
  163. $addHeaders = array(); 
  164. if ($cached->getResponseHeader('etag')) { 
  165. // [13.3.4] If an entity tag has been provided by the origin server,  
  166. // we must use that entity tag in any cache-conditional request. 
  167. $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag'); 
  168. } elseif ($cached->getResponseHeader('date')) { 
  169. $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date'); 
  170.  
  171. $request->setRequestHeaders($addHeaders); 
  172. return true; 
  173. } else { 
  174. return false; 
  175.  
  176. /** 
  177. * Update a cached request, using the headers from the last response. 
  178. * @param Google_HttpRequest $cached A previously cached response. 
  179. * @param mixed Associative array of response headers from the last request. 
  180. */ 
  181. protected function updateCachedRequest($cached, $responseHeaders) 
  182. $hopByHop = self::$HOP_BY_HOP; 
  183. if (!empty($responseHeaders['connection'])) { 
  184. $connectionHeaders = array_map( 
  185. 'strtolower',  
  186. array_filter( 
  187. array_map('trim', explode(', ', $responseHeaders['connection'])) 
  188. ); 
  189. $hopByHop += array_fill_keys($connectionHeaders, true); 
  190.  
  191. $endToEnd = array_diff_key($responseHeaders, $hopByHop); 
  192. $cached->setResponseHeaders($endToEnd); 
  193.  
  194. /** 
  195. * Used by the IO lib and also the batch processing. 
  196. * @param $respData 
  197. * @param $headerSize 
  198. * @return array 
  199. */ 
  200. public function parseHttpResponse($respData, $headerSize) 
  201. // check proxy header 
  202. foreach (self::$CONNECTION_ESTABLISHED_HEADERS as $established_header) { 
  203. if (stripos($respData, $established_header) !== false) { 
  204. // existed, remove it 
  205. $respData = str_ireplace($established_header, '', $respData); 
  206. // Subtract the proxy header size unless the cURL bug prior to 7.30.0 
  207. // is present which prevented the proxy header size from being taken into 
  208. // account. 
  209. if (!$this->needsQuirk()) { 
  210. $headerSize -= strlen($established_header); 
  211. break; 
  212.  
  213. if ($headerSize) { 
  214. $responseBody = substr($respData, $headerSize); 
  215. $responseHeaders = substr($respData, 0, $headerSize); 
  216. } else { 
  217. $responseSegments = explode("\r\n\r\n", $respData, 2); 
  218. $responseHeaders = $responseSegments[0]; 
  219. $responseBody = isset($responseSegments[1]) ? $responseSegments[1] : 
  220. null; 
  221.  
  222. $responseHeaders = $this->getHttpResponseHeaders($responseHeaders); 
  223. return array($responseHeaders, $responseBody); 
  224.  
  225. /** 
  226. * Parse out headers from raw headers 
  227. * @param rawHeaders array or string 
  228. * @return array 
  229. */ 
  230. public function getHttpResponseHeaders($rawHeaders) 
  231. if (is_array($rawHeaders)) { 
  232. return $this->parseArrayHeaders($rawHeaders); 
  233. } else { 
  234. return $this->parseStringHeaders($rawHeaders); 
  235.  
  236. private function parseStringHeaders($rawHeaders) 
  237. $headers = array(); 
  238. $responseHeaderLines = explode("\r\n", $rawHeaders); 
  239. foreach ($responseHeaderLines as $headerLine) { 
  240. if ($headerLine && strpos($headerLine, ':') !== false) { 
  241. list($header, $value) = explode(': ', $headerLine, 2); 
  242. $header = strtolower($header); 
  243. if (isset($headers[$header])) { 
  244. $headers[$header] .= "\n" . $value; 
  245. } else { 
  246. $headers[$header] = $value; 
  247. return $headers; 
  248.  
  249. private function parseArrayHeaders($rawHeaders) 
  250. $header_count = count($rawHeaders); 
  251. $headers = array(); 
  252.  
  253. for ($i = 0; $i < $header_count; $i++) { 
  254. $header = $rawHeaders[$i]; 
  255. // Times will have colons in - so we just want the first match. 
  256. $header_parts = explode(': ', $header, 2); 
  257. if (count($header_parts) == 2) { 
  258. $headers[strtolower($header_parts[0])] = $header_parts[1]; 
  259.  
  260. return $headers;