Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  /*
   3   * Copyright 2014 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   * Curl based implementation of Google_IO.
  20   *
  21   * @author Stuart Langley <slangley@google.com>
  22   */
  23  
  24  if (!class_exists('Google_Client')) {
  25    require_once dirname(__FILE__) . '/../autoload.php';
  26  }
  27  
  28  class Google_IO_Curl extends Google_IO_Abstract
  29  {
  30    // cURL hex representation of version 7.30.0
  31    const NO_QUIRK_VERSION = 0x071E00;
  32  
  33    private $options = array();
  34  
  35    /** @var bool $disableProxyWorkaround */
  36    private $disableProxyWorkaround;
  37  
  38    public function __construct(Google_Client $client)
  39    {
  40      if (!extension_loaded('curl')) {
  41        $error = 'The cURL IO handler requires the cURL extension to be enabled';
  42        $client->getLogger()->critical($error);
  43        throw new Google_IO_Exception($error);
  44      }
  45  
  46      parent::__construct($client);
  47  
  48      $this->disableProxyWorkaround = $this->client->getClassConfig(
  49          'Google_IO_Curl',
  50          'disable_proxy_workaround'
  51      );
  52    }
  53  
  54    /**
  55     * Execute an HTTP Request
  56     *
  57     * @param Google_Http_Request $request the http request to be executed
  58     * @return array containing response headers, body, and http code
  59     * @throws Google_IO_Exception on curl or IO error
  60     */
  61    public function executeRequest(Google_Http_Request $request)
  62    {
  63      $curl = curl_init();
  64  
  65      if ($request->getPostBody()) {
  66        curl_setopt($curl, CURLOPT_POSTFIELDS, $request->getPostBody());
  67      }
  68  
  69      $requestHeaders = $request->getRequestHeaders();
  70      if ($requestHeaders && is_array($requestHeaders)) {
  71        $curlHeaders = array();
  72        foreach ($requestHeaders as $k => $v) {
  73          $curlHeaders[] = "$k: $v";
  74        }
  75        curl_setopt($curl, CURLOPT_HTTPHEADER, $curlHeaders);
  76      }
  77      curl_setopt($curl, CURLOPT_URL, $request->getUrl());
  78  
  79      curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $request->getRequestMethod());
  80      curl_setopt($curl, CURLOPT_USERAGENT, $request->getUserAgent());
  81  
  82      curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
  83      curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
  84  
  85      // The SSL version will be determined by the underlying library
  86      // @see https://github.com/google/google-api-php-client/pull/644
  87      //curl_setopt($curl, CURLOPT_SSLVERSION, 1);
  88  
  89      curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  90      curl_setopt($curl, CURLOPT_HEADER, true);
  91  
  92      if ($request->canGzip()) {
  93        curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
  94      }
  95      
  96      $options = $this->client->getClassConfig('Google_IO_Curl', 'options');
  97      if (is_array($options)) {
  98        $this->setOptions($options);
  99      }
 100      
 101      foreach ($this->options as $key => $var) {
 102        curl_setopt($curl, $key, $var);
 103      }
 104  
 105      if (!isset($this->options[CURLOPT_CAINFO])) {
 106        curl_setopt($curl, CURLOPT_CAINFO, dirname(__FILE__) . '/cacerts.pem');
 107      }
 108  
 109      $this->client->getLogger()->debug(
 110          'cURL request',
 111          array(
 112              'url' => $request->getUrl(),
 113              'method' => $request->getRequestMethod(),
 114              'headers' => $requestHeaders,
 115              'body' => $request->getPostBody()
 116          )
 117      );
 118  
 119      $response = curl_exec($curl);
 120      if ($response === false) {
 121        $error = curl_error($curl);
 122        $code = curl_errno($curl);
 123        $map = $this->client->getClassConfig('Google_IO_Exception', 'retry_map');
 124  
 125        $this->client->getLogger()->error('cURL ' . $error);
 126        throw new Google_IO_Exception($error, $code, null, $map);
 127      }
 128      $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
 129  
 130      list($responseHeaders, $responseBody) = $this->parseHttpResponse($response, $headerSize);
 131      $responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
 132  
 133      $this->client->getLogger()->debug(
 134          'cURL response',
 135          array(
 136              'code' => $responseCode,
 137              'headers' => $responseHeaders,
 138              'body' => $responseBody,
 139          )
 140      );
 141  
 142      return array($responseBody, $responseHeaders, $responseCode);
 143    }
 144  
 145    /**
 146     * Set options that update the transport implementation's behavior.
 147     * @param $options
 148     */
 149    public function setOptions($options)
 150    {
 151      $this->options = $options + $this->options;
 152    }
 153  
 154    /**
 155     * Set the maximum request time in seconds.
 156     * @param $timeout in seconds
 157     */
 158    public function setTimeout($timeout)
 159    {
 160      // Since this timeout is really for putting a bound on the time
 161      // we'll set them both to the same. If you need to specify a longer
 162      // CURLOPT_TIMEOUT, or a higher CONNECTTIMEOUT, the best thing to
 163      // do is use the setOptions method for the values individually.
 164      $this->options[CURLOPT_CONNECTTIMEOUT] = $timeout;
 165      $this->options[CURLOPT_TIMEOUT] = $timeout;
 166    }
 167  
 168    /**
 169     * Get the maximum request time in seconds.
 170     * @return timeout in seconds
 171     */
 172    public function getTimeout()
 173    {
 174      return $this->options[CURLOPT_TIMEOUT];
 175    }
 176  
 177    /**
 178     * Test for the presence of a cURL header processing bug
 179     *
 180     * {@inheritDoc}
 181     *
 182     * @return boolean
 183     */
 184    protected function needsQuirk()
 185    {
 186      if ($this->disableProxyWorkaround) {
 187        return false;
 188      }
 189  
 190      $ver = curl_version();
 191      $versionNum = $ver['version_number'];
 192      return $versionNum < Google_IO_Curl::NO_QUIRK_VERSION;
 193    }
 194  }