[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/google/Google/IO/ -> Abstract.php (source)

   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  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1