[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 * Copyright 2013 Google Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * Abstract IO base class 20 */ 21 22 require_once 'Google/Client.php'; 23 require_once 'Google/IO/Exception.php'; 24 require_once 'Google/Http/CacheParser.php'; 25 require_once 'Google/Http/Request.php'; 26 27 abstract class Google_IO_Abstract 28 { 29 const UNKNOWN_CODE = 0; 30 const FORM_URLENCODED = 'application/x-www-form-urlencoded'; 31 private static $CONNECTION_ESTABLISHED_HEADERS = array( 32 "HTTP/1.0 200 Connection established\r\n\r\n", 33 "HTTP/1.1 200 Connection established\r\n\r\n", 34 ); 35 private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null); 36 37 /** @var Google_Client */ 38 protected $client; 39 40 public function __construct(Google_Client $client) 41 { 42 $this->client = $client; 43 $timeout = $client->getClassConfig('Google_IO_Abstract', 'request_timeout_seconds'); 44 if ($timeout > 0) { 45 $this->setTimeout($timeout); 46 } 47 } 48 49 /** 50 * Executes a Google_Http_Request and returns the resulting populated Google_Http_Request 51 * @param Google_Http_Request $request 52 * @return Google_Http_Request $request 53 */ 54 abstract public function executeRequest(Google_Http_Request $request); 55 56 /** 57 * Set options that update the transport implementation's behavior. 58 * @param $options 59 */ 60 abstract public function setOptions($options); 61 62 /** 63 * Set the maximum request time in seconds. 64 * @param $timeout in seconds 65 */ 66 abstract public function setTimeout($timeout); 67 68 /** 69 * Get the maximum request time in seconds. 70 * @return timeout in seconds 71 */ 72 abstract public function getTimeout(); 73 74 /** 75 * Test for the presence of a cURL header processing bug 76 * 77 * The cURL bug was present in versions prior to 7.30.0 and caused the header 78 * length to be miscalculated when a "Connection established" header added by 79 * some proxies was present. 80 * 81 * @return boolean 82 */ 83 abstract protected function needsQuirk(); 84 85 /** 86 * @visible for testing. 87 * Cache the response to an HTTP request if it is cacheable. 88 * @param Google_Http_Request $request 89 * @return bool Returns true if the insertion was successful. 90 * Otherwise, return false. 91 */ 92 public function setCachedRequest(Google_Http_Request $request) 93 { 94 // Determine if the request is cacheable. 95 if (Google_Http_CacheParser::isResponseCacheable($request)) { 96 $this->client->getCache()->set($request->getCacheKey(), $request); 97 return true; 98 } 99 100 return false; 101 } 102 103 /** 104 * Execute an HTTP Request 105 * 106 * @param Google_HttpRequest $request the http request to be executed 107 * @return Google_HttpRequest http request with the response http code, 108 * response headers and response body filled in 109 * @throws Google_IO_Exception on curl or IO error 110 */ 111 public function makeRequest(Google_Http_Request $request) 112 { 113 // First, check to see if we have a valid cached version. 114 $cached = $this->getCachedRequest($request); 115 if ($cached !== false && $cached instanceof Google_Http_Request) { 116 if (!$this->checkMustRevalidateCachedRequest($cached, $request)) { 117 return $cached; 118 } 119 } 120 121 if (array_key_exists($request->getRequestMethod(), self::$ENTITY_HTTP_METHODS)) { 122 $request = $this->processEntityRequest($request); 123 } 124 125 list($responseData, $responseHeaders, $respHttpCode) = $this->executeRequest($request); 126 127 if ($respHttpCode == 304 && $cached) { 128 // If the server responded NOT_MODIFIED, return the cached request. 129 $this->updateCachedRequest($cached, $responseHeaders); 130 return $cached; 131 } 132 133 if (!isset($responseHeaders['Date']) && !isset($responseHeaders['date'])) { 134 $responseHeaders['Date'] = date("r"); 135 } 136 137 $request->setResponseHttpCode($respHttpCode); 138 $request->setResponseHeaders($responseHeaders); 139 $request->setResponseBody($responseData); 140 // Store the request in cache (the function checks to see if the request 141 // can actually be cached) 142 $this->setCachedRequest($request); 143 return $request; 144 } 145 146 /** 147 * @visible for testing. 148 * @param Google_Http_Request $request 149 * @return Google_Http_Request|bool Returns the cached object or 150 * false if the operation was unsuccessful. 151 */ 152 public function getCachedRequest(Google_Http_Request $request) 153 { 154 if (false === Google_Http_CacheParser::isRequestCacheable($request)) { 155 return false; 156 } 157 158 return $this->client->getCache()->get($request->getCacheKey()); 159 } 160 161 /** 162 * @visible for testing 163 * Process an http request that contains an enclosed entity. 164 * @param Google_Http_Request $request 165 * @return Google_Http_Request Processed request with the enclosed entity. 166 */ 167 public function processEntityRequest(Google_Http_Request $request) 168 { 169 $postBody = $request->getPostBody(); 170 $contentType = $request->getRequestHeader("content-type"); 171 172 // Set the default content-type as application/x-www-form-urlencoded. 173 if (false == $contentType) { 174 $contentType = self::FORM_URLENCODED; 175 $request->setRequestHeaders(array('content-type' => $contentType)); 176 } 177 178 // Force the payload to match the content-type asserted in the header. 179 if ($contentType == self::FORM_URLENCODED && is_array($postBody)) { 180 $postBody = http_build_query($postBody, '', '&'); 181 $request->setPostBody($postBody); 182 } 183 184 // Make sure the content-length header is set. 185 if (!$postBody || is_string($postBody)) { 186 $postsLength = strlen($postBody); 187 $request->setRequestHeaders(array('content-length' => $postsLength)); 188 } 189 190 return $request; 191 } 192 193 /** 194 * Check if an already cached request must be revalidated, and if so update 195 * the request with the correct ETag headers. 196 * @param Google_Http_Request $cached A previously cached response. 197 * @param Google_Http_Request $request The outbound request. 198 * return bool If the cached object needs to be revalidated, false if it is 199 * still current and can be re-used. 200 */ 201 protected function checkMustRevalidateCachedRequest($cached, $request) 202 { 203 if (Google_Http_CacheParser::mustRevalidate($cached)) { 204 $addHeaders = array(); 205 if ($cached->getResponseHeader('etag')) { 206 // [13.3.4] If an entity tag has been provided by the origin server, 207 // we must use that entity tag in any cache-conditional request. 208 $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag'); 209 } elseif ($cached->getResponseHeader('date')) { 210 $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date'); 211 } 212 213 $request->setRequestHeaders($addHeaders); 214 return true; 215 } else { 216 return false; 217 } 218 } 219 220 /** 221 * Update a cached request, using the headers from the last response. 222 * @param Google_HttpRequest $cached A previously cached response. 223 * @param mixed Associative array of response headers from the last request. 224 */ 225 protected function updateCachedRequest($cached, $responseHeaders) 226 { 227 if (isset($responseHeaders['connection'])) { 228 $hopByHop = array_merge( 229 self::$HOP_BY_HOP, 230 explode( 231 ',', 232 $responseHeaders['connection'] 233 ) 234 ); 235 236 $endToEnd = array(); 237 foreach ($hopByHop as $key) { 238 if (isset($responseHeaders[$key])) { 239 $endToEnd[$key] = $responseHeaders[$key]; 240 } 241 } 242 $cached->setResponseHeaders($endToEnd); 243 } 244 } 245 246 /** 247 * Used by the IO lib and also the batch processing. 248 * 249 * @param $respData 250 * @param $headerSize 251 * @return array 252 */ 253 public function parseHttpResponse($respData, $headerSize) 254 { 255 // check proxy header 256 foreach (self::$CONNECTION_ESTABLISHED_HEADERS as $established_header) { 257 if (stripos($respData, $established_header) !== false) { 258 // existed, remove it 259 $respData = str_ireplace($established_header, '', $respData); 260 // Subtract the proxy header size unless the cURL bug prior to 7.30.0 261 // is present which prevented the proxy header size from being taken into 262 // account. 263 if (!$this->needsQuirk()) { 264 $headerSize -= strlen($established_header); 265 } 266 break; 267 } 268 } 269 270 if ($headerSize) { 271 $responseBody = substr($respData, $headerSize); 272 $responseHeaders = substr($respData, 0, $headerSize); 273 } else { 274 list($responseHeaders, $responseBody) = explode("\r\n\r\n", $respData, 2); 275 } 276 277 $responseHeaders = $this->getHttpResponseHeaders($responseHeaders); 278 return array($responseHeaders, $responseBody); 279 } 280 281 /** 282 * Parse out headers from raw headers 283 * @param rawHeaders array or string 284 * @return array 285 */ 286 public function getHttpResponseHeaders($rawHeaders) 287 { 288 if (is_array($rawHeaders)) { 289 return $this->parseArrayHeaders($rawHeaders); 290 } else { 291 return $this->parseStringHeaders($rawHeaders); 292 } 293 } 294 295 private function parseStringHeaders($rawHeaders) 296 { 297 $headers = array(); 298 $responseHeaderLines = explode("\r\n", $rawHeaders); 299 foreach ($responseHeaderLines as $headerLine) { 300 if ($headerLine && strpos($headerLine, ':') !== false) { 301 list($header, $value) = explode(': ', $headerLine, 2); 302 $header = strtolower($header); 303 if (isset($headers[$header])) { 304 $headers[$header] .= "\n" . $value; 305 } else { 306 $headers[$header] = $value; 307 } 308 } 309 } 310 return $headers; 311 } 312 313 private function parseArrayHeaders($rawHeaders) 314 { 315 $header_count = count($rawHeaders); 316 $headers = array(); 317 318 for ($i = 0; $i < $header_count; $i++) { 319 $header = $rawHeaders[$i]; 320 // Times will have colons in - so we just want the first match. 321 $header_parts = explode(': ', $header, 2); 322 if (count($header_parts) == 2) { 323 $headers[$header_parts[0]] = $header_parts[1]; 324 } 325 } 326 327 return $headers; 328 } 329 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |