GoogleGAL_IO_Abstract

The Google Apps Login GoogleGAL IO Abstract class.

Defined (1)

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

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