Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

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