[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Http/Transport/ -> CurlTransport.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2011 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Http\Transport;
  11  
  12  use Composer\CaBundle\CaBundle;
  13  use Joomla\CMS\Factory;
  14  use Joomla\CMS\Http\Response;
  15  use Joomla\CMS\Http\TransportInterface;
  16  use Joomla\CMS\Uri\Uri;
  17  use Joomla\Http\AbstractTransport;
  18  use Joomla\Http\Exception\InvalidResponseCodeException;
  19  use Joomla\Uri\UriInterface;
  20  use Laminas\Diactoros\Stream as StreamResponse;
  21  
  22  // phpcs:disable PSR1.Files.SideEffects
  23  \defined('JPATH_PLATFORM') or die;
  24  // phpcs:enable PSR1.Files.SideEffects
  25  
  26  /**
  27   * HTTP transport class for using cURL.
  28   *
  29   * @since  1.7.3
  30   */
  31  class CurlTransport extends AbstractTransport implements TransportInterface
  32  {
  33      /**
  34       * Send a request to the server and return a Response object with the response.
  35       *
  36       * @param   string        $method     The HTTP method for sending the request.
  37       * @param   UriInterface  $uri        The URI to the resource to request.
  38       * @param   mixed         $data       Either an associative array or a string to be sent with the request.
  39       * @param   array         $headers    An array of request headers to send with the request.
  40       * @param   integer       $timeout    Read timeout in seconds.
  41       * @param   string        $userAgent  The optional user agent string to send with the request.
  42       *
  43       * @return  Response
  44       *
  45       * @since   1.7.3
  46       * @throws  \RuntimeException
  47       */
  48      public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null)
  49      {
  50          // Setup the cURL handle.
  51          $ch = curl_init();
  52  
  53          $options = array();
  54  
  55          // Set the request method.
  56          switch (strtoupper($method)) {
  57              case 'GET':
  58                  $options[CURLOPT_HTTPGET] = true;
  59                  break;
  60  
  61              case 'POST':
  62                  $options[CURLOPT_POST] = true;
  63                  break;
  64  
  65              case 'PUT':
  66              default:
  67                  $options[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
  68                  break;
  69          }
  70  
  71          // Don't wait for body when $method is HEAD
  72          $options[CURLOPT_NOBODY] = ($method === 'HEAD');
  73  
  74          // Initialize the certificate store
  75          $options[CURLOPT_CAINFO] = $this->getOption('curl.certpath', CaBundle::getBundledCaBundlePath());
  76  
  77          // If data exists let's encode it and make sure our Content-type header is set.
  78          if (isset($data)) {
  79              // If the data is a scalar value simply add it to the cURL post fields.
  80              if (is_scalar($data) || (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') === 0)) {
  81                  $options[CURLOPT_POSTFIELDS] = $data;
  82              } else {
  83                  // Otherwise we need to encode the value first.
  84                  $options[CURLOPT_POSTFIELDS] = http_build_query($data);
  85              }
  86  
  87              if (!isset($headers['Content-Type'])) {
  88                  $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  89              }
  90  
  91              // Add the relevant headers.
  92              if (is_scalar($options[CURLOPT_POSTFIELDS])) {
  93                  $headers['Content-Length'] = \strlen($options[CURLOPT_POSTFIELDS]);
  94              }
  95          }
  96  
  97          // Build the headers string for the request.
  98          $headerArray = array();
  99  
 100          if (isset($headers)) {
 101              foreach ($headers as $key => $value) {
 102                  $headerArray[] = $key . ': ' . $value;
 103              }
 104  
 105              // Add the headers string into the stream context options array.
 106              $options[CURLOPT_HTTPHEADER] = $headerArray;
 107          }
 108  
 109          // Curl needs the accepted encoding header as option
 110          if (isset($headers['Accept-Encoding'])) {
 111              $options[CURLOPT_ENCODING] = $headers['Accept-Encoding'];
 112          }
 113  
 114          // If an explicit timeout is given user it.
 115          if (isset($timeout)) {
 116              $options[CURLOPT_TIMEOUT] = (int) $timeout;
 117              $options[CURLOPT_CONNECTTIMEOUT] = (int) $timeout;
 118          }
 119  
 120          // If an explicit user agent is given use it.
 121          if (isset($userAgent)) {
 122              $options[CURLOPT_USERAGENT] = $userAgent;
 123          }
 124  
 125          // Set the request URL.
 126          $options[CURLOPT_URL] = (string) $uri;
 127  
 128          // We want our headers. :-)
 129          $options[CURLOPT_HEADER] = true;
 130  
 131          // Return it... echoing it would be tacky.
 132          $options[CURLOPT_RETURNTRANSFER] = true;
 133  
 134          // Override the Expect header to prevent cURL from confusing itself in its own stupidity.
 135          // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/
 136          $options[CURLOPT_HTTPHEADER][] = 'Expect:';
 137  
 138          // Follow redirects if server config allows
 139          if ($this->redirectsAllowed()) {
 140              $options[CURLOPT_FOLLOWLOCATION] = (bool) $this->getOption('follow_location', true);
 141          }
 142  
 143          // Proxy configuration
 144          $app = Factory::getApplication();
 145  
 146          if ($app->get('proxy_enable')) {
 147              $options[CURLOPT_PROXY] = $app->get('proxy_host') . ':' . $app->get('proxy_port');
 148  
 149              if ($user = $app->get('proxy_user')) {
 150                  $options[CURLOPT_PROXYUSERPWD] = $user . ':' . $app->get('proxy_pass');
 151              }
 152          }
 153  
 154          // Set any custom transport options
 155          foreach ($this->getOption('transport.curl', array()) as $key => $value) {
 156              $options[$key] = $value;
 157          }
 158  
 159          // Authentication, if needed
 160          if ($this->getOption('userauth') && $this->getOption('passwordauth')) {
 161              $options[CURLOPT_USERPWD] = $this->getOption('userauth') . ':' . $this->getOption('passwordauth');
 162              $options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
 163          }
 164  
 165          // Set the cURL options.
 166          curl_setopt_array($ch, $options);
 167  
 168          // Execute the request and close the connection.
 169          $content = curl_exec($ch);
 170  
 171          // Check if the content is a string. If it is not, it must be an error.
 172          if (!\is_string($content)) {
 173              $message = curl_error($ch);
 174  
 175              if (empty($message)) {
 176                  // Error but nothing from cURL? Create our own
 177                  $message = 'No HTTP response received';
 178              }
 179  
 180              throw new \RuntimeException($message);
 181          }
 182  
 183          // Get the request information.
 184          $info = curl_getinfo($ch);
 185  
 186          // Close the connection.
 187          curl_close($ch);
 188  
 189          $response = $this->getResponse($content, $info);
 190  
 191          // Manually follow redirects if server doesn't allow to follow location using curl
 192          if ($response->code >= 301 && $response->code < 400 && isset($response->headers['Location']) && (bool) $this->getOption('follow_location', true)) {
 193              $redirect_uri = new Uri($response->headers['Location']);
 194  
 195              if (\in_array($redirect_uri->getScheme(), array('file', 'scp'))) {
 196                  throw new \RuntimeException('Curl redirect cannot be used in file or scp requests.');
 197              }
 198  
 199              $response = $this->request($method, $redirect_uri, $data, $headers, $timeout, $userAgent);
 200          }
 201  
 202          return $response;
 203      }
 204  
 205      /**
 206       * Method to get a response object from a server response.
 207       *
 208       * @param   string  $content  The complete server response, including headers
 209       *                            as a string if the response has no errors.
 210       * @param   array   $info     The cURL request information.
 211       *
 212       * @return  Response
 213       *
 214       * @since   1.7.3
 215       * @throws  InvalidResponseCodeException
 216       */
 217      protected function getResponse($content, $info)
 218      {
 219          // Try to get header size
 220          if (isset($info['header_size'])) {
 221              $headerString = trim(substr($content, 0, $info['header_size']));
 222              $headerArray  = explode("\r\n\r\n", $headerString);
 223  
 224              // Get the last set of response headers as an array.
 225              $headers = explode("\r\n", array_pop($headerArray));
 226  
 227              // Set the body for the response.
 228              $body = substr($content, $info['header_size']);
 229          } else {
 230              // Fallback and try to guess header count by redirect count
 231              // Get the number of redirects that occurred.
 232              $redirects = $info['redirect_count'] ?? 0;
 233  
 234              /*
 235               * Split the response into headers and body. If cURL encountered redirects, the headers for the redirected requests will
 236               * also be included. So we split the response into header + body + the number of redirects and only use the last two
 237               * sections which should be the last set of headers and the actual body.
 238               */
 239              $response = explode("\r\n\r\n", $content, 2 + $redirects);
 240  
 241              // Set the body for the response.
 242              $body = array_pop($response);
 243  
 244              // Get the last set of response headers as an array.
 245              $headers = explode("\r\n", array_pop($response));
 246          }
 247  
 248          // Get the response code from the first offset of the response headers.
 249          preg_match('/[0-9]{3}/', array_shift($headers), $matches);
 250  
 251          $code = \count($matches) ? $matches[0] : null;
 252  
 253          if (!is_numeric($code)) {
 254              // No valid response code was detected.
 255              throw new InvalidResponseCodeException('No HTTP response code found.');
 256          }
 257  
 258          $statusCode      = (int) $code;
 259          $verifiedHeaders = $this->processHeaders($headers);
 260  
 261          $streamInterface = new StreamResponse('php://memory', 'rw');
 262          $streamInterface->write($body);
 263  
 264          return new Response($streamInterface, $statusCode, $verifiedHeaders);
 265      }
 266  
 267      /**
 268       * Method to check if HTTP transport cURL is available for use
 269       *
 270       * @return boolean true if available, else false
 271       *
 272       * @since   3.0.0
 273       */
 274      public static function isSupported()
 275      {
 276          return \function_exists('curl_version') && curl_version();
 277      }
 278  
 279      /**
 280       * Check if redirects are allowed
 281       *
 282       * @return  boolean
 283       *
 284       * @since   3.0.0
 285       */
 286      private function redirectsAllowed()
 287      {
 288          $curlVersion = curl_version();
 289  
 290          // If open_basedir is enabled we also need to check if libcurl version is 7.19.4 or higher
 291          if (!ini_get('open_basedir') || version_compare($curlVersion['version'], '7.19.4', '>=')) {
 292              return true;
 293          }
 294  
 295          return false;
 296      }
 297  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer