[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Http/Transport/ -> SocketTransport.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 Joomla\CMS\Factory;
  13  use Joomla\CMS\Http\Response;
  14  use Joomla\CMS\Http\TransportInterface;
  15  use Joomla\CMS\Uri\Uri;
  16  use Joomla\Http\AbstractTransport;
  17  use Joomla\Http\Exception\InvalidResponseCodeException;
  18  use Joomla\Uri\UriInterface;
  19  use Laminas\Diactoros\Stream as StreamResponse;
  20  
  21  // phpcs:disable PSR1.Files.SideEffects
  22  \defined('JPATH_PLATFORM') or die;
  23  // phpcs:enable PSR1.Files.SideEffects
  24  
  25  /**
  26   * HTTP transport class for using sockets directly.
  27   *
  28   * @since  1.7.3
  29   */
  30  class SocketTransport extends AbstractTransport implements TransportInterface
  31  {
  32      /**
  33       * @var    array  Reusable socket connections.
  34       * @since  1.7.3
  35       */
  36      protected $connections;
  37  
  38      /**
  39       * Send a request to the server and return a Response object with the response.
  40       *
  41       * @param   string        $method     The HTTP method for sending the request.
  42       * @param   UriInterface  $uri        The URI to the resource to request.
  43       * @param   mixed         $data       Either an associative array or a string to be sent with the request.
  44       * @param   array         $headers    An array of request headers to send with the request.
  45       * @param   integer       $timeout    Read timeout in seconds.
  46       * @param   string        $userAgent  The optional user agent string to send with the request.
  47       *
  48       * @return  Response
  49       *
  50       * @since   1.7.3
  51       * @throws  \RuntimeException
  52       */
  53      public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null)
  54      {
  55          $connection = $this->connect($uri, $timeout);
  56  
  57          // Make sure the connection is alive and valid.
  58          if (\is_resource($connection)) {
  59              // Make sure the connection has not timed out.
  60              $meta = stream_get_meta_data($connection);
  61  
  62              if ($meta['timed_out']) {
  63                  throw new \RuntimeException('Server connection timed out.');
  64              }
  65          } else {
  66              throw new \RuntimeException('Not connected to server.');
  67          }
  68  
  69          // Get the request path from the URI object.
  70          $path = $uri->toString(array('path', 'query'));
  71  
  72          // If we have data to send make sure our request is setup for it.
  73          if (!empty($data)) {
  74              // If the data is not a scalar value encode it to be sent with the request.
  75              if (!is_scalar($data)) {
  76                  $data = http_build_query($data);
  77              }
  78  
  79              if (!isset($headers['Content-Type'])) {
  80                  $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  81              }
  82  
  83              // Add the relevant headers.
  84              $headers['Content-Length'] = \strlen($data);
  85          }
  86  
  87          // Build the request payload.
  88          $request = array();
  89          $request[] = strtoupper($method) . ' ' . ((empty($path)) ? '/' : $path) . ' HTTP/1.1';
  90          $request[] = 'Host: ' . $uri->getHost();
  91  
  92          // If an explicit user agent is given use it.
  93          if (isset($userAgent)) {
  94              $headers['User-Agent'] = $userAgent;
  95          }
  96  
  97          // If there are custom headers to send add them to the request payload.
  98          if (\is_array($headers)) {
  99              foreach ($headers as $k => $v) {
 100                  $request[] = $k . ': ' . $v;
 101              }
 102          }
 103  
 104          // Set any custom transport options
 105          foreach ($this->getOption('transport.socket', array()) as $value) {
 106              $request[] = $value;
 107          }
 108  
 109          // If we have data to send add it to the request payload.
 110          if (!empty($data)) {
 111              $request[] = null;
 112              $request[] = $data;
 113          }
 114  
 115          // Authentication, if needed
 116          if ($this->getOption('userauth') && $this->getOption('passwordauth')) {
 117              $request[] = 'Authorization: Basic ' . base64_encode($this->getOption('userauth') . ':' . $this->getOption('passwordauth'));
 118          }
 119  
 120          // Send the request to the server.
 121          fwrite($connection, implode("\r\n", $request) . "\r\n\r\n");
 122  
 123          // Get the response data from the server.
 124          $content = '';
 125  
 126          while (!feof($connection)) {
 127              $content .= fgets($connection, 4096);
 128          }
 129  
 130          $content = $this->getResponse($content);
 131  
 132          // Follow Http redirects
 133          if ($content->code >= 301 && $content->code < 400 && isset($content->headers['Location'])) {
 134              return $this->request($method, new Uri($content->headers['Location']), $data, $headers, $timeout, $userAgent);
 135          }
 136  
 137          return $content;
 138      }
 139  
 140      /**
 141       * Method to get a response object from a server response.
 142       *
 143       * @param   string  $content  The complete server response, including headers.
 144       *
 145       * @return  Response
 146       *
 147       * @since   1.7.3
 148       * @throws  InvalidResponseCodeException
 149       */
 150      protected function getResponse($content)
 151      {
 152          if (empty($content)) {
 153              throw new \UnexpectedValueException('No content in response.');
 154          }
 155  
 156          // Split the response into headers and body.
 157          $response = explode("\r\n\r\n", $content, 2);
 158  
 159          // Get the response headers as an array.
 160          $headers = explode("\r\n", $response[0]);
 161  
 162          // Set the body for the response.
 163          $body = empty($response[1]) ? '' : $response[1];
 164  
 165          // Get the response code from the first offset of the response headers.
 166          preg_match('/[0-9]{3}/', array_shift($headers), $matches);
 167          $code = $matches[0];
 168  
 169          if (!is_numeric($code)) {
 170              // No valid response code was detected.
 171              throw new InvalidResponseCodeException('No HTTP response code found.');
 172          }
 173  
 174          $statusCode      = (int) $code;
 175          $verifiedHeaders = $this->processHeaders($headers);
 176  
 177          $streamInterface = new StreamResponse('php://memory', 'rw');
 178          $streamInterface->write($body);
 179  
 180          return new Response($streamInterface, $statusCode, $verifiedHeaders);
 181      }
 182  
 183      /**
 184       * Method to connect to a server and get the resource.
 185       *
 186       * @param   UriInterface  $uri      The URI to connect with.
 187       * @param   integer       $timeout  Read timeout in seconds.
 188       *
 189       * @return  resource  Socket connection resource.
 190       *
 191       * @since   1.7.3
 192       * @throws  \RuntimeException
 193       */
 194      protected function connect(UriInterface $uri, $timeout = null)
 195      {
 196          $errno = null;
 197          $err = null;
 198  
 199          // Get the host from the uri.
 200          $host = ($uri->isSsl()) ? 'ssl://' . $uri->getHost() : $uri->getHost();
 201  
 202          // If the port is not explicitly set in the URI detect it.
 203          if (!$uri->getPort()) {
 204              $port = ($uri->getScheme() === 'https') ? 443 : 80;
 205          } else {
 206              // Use the set port.
 207              $port = $uri->getPort();
 208          }
 209  
 210          // Build the connection key for resource memory caching.
 211          $key = md5($host . $port);
 212  
 213          // If the connection already exists, use it.
 214          if (!empty($this->connections[$key]) && \is_resource($this->connections[$key])) {
 215              // Connection reached EOF, cannot be used anymore
 216              $meta = stream_get_meta_data($this->connections[$key]);
 217  
 218              if ($meta['eof']) {
 219                  if (!fclose($this->connections[$key])) {
 220                      throw new \RuntimeException('Cannot close connection');
 221                  }
 222              } elseif (!$meta['timed_out']) {
 223                  // Make sure the connection has not timed out.
 224                  return $this->connections[$key];
 225              }
 226          }
 227  
 228          if (!is_numeric($timeout)) {
 229              $timeout = ini_get('default_socket_timeout');
 230          }
 231  
 232          // Capture PHP errors
 233          $php_errormsg = '';
 234          $track_errors = ini_get('track_errors');
 235          ini_set('track_errors', true);
 236  
 237          // PHP sends a warning if the uri does not exists; we silence it and throw an exception instead.
 238          // Attempt to connect to the server
 239          $connection = @fsockopen($host, $port, $errno, $err, $timeout);
 240  
 241          if (!$connection) {
 242              if (!$php_errormsg) {
 243                  // Error but nothing from php? Create our own
 244                  $php_errormsg = sprintf('Could not connect to resource %s: %s (error code %d)', $uri, $err, $errno);
 245              }
 246  
 247              // Restore error tracking to give control to the exception handler
 248              ini_set('track_errors', $track_errors);
 249  
 250              throw new \RuntimeException($php_errormsg);
 251          }
 252  
 253          // Restore error tracking to what it was before.
 254          ini_set('track_errors', $track_errors);
 255  
 256          // Since the connection was successful let's store it in case we need to use it later.
 257          $this->connections[$key] = $connection;
 258  
 259          // If an explicit timeout is set, set it.
 260          if (isset($timeout)) {
 261              stream_set_timeout($this->connections[$key], (int) $timeout);
 262          }
 263  
 264          return $this->connections[$key];
 265      }
 266  
 267      /**
 268       * Method to check if http transport socket 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('fsockopen') && \is_callable('fsockopen') && !Factory::getApplication()->get('proxy_enable');
 277      }
 278  }


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