[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/http/src/Transport/ -> Socket.php (source)

   1  <?php
   2  /**
   3   * Part of the Joomla Framework Http Package
   4   *
   5   * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
   6   * @license    GNU General Public License version 2 or later; see LICENSE
   7   */
   8  
   9  namespace Joomla\Http\Transport;
  10  
  11  use Joomla\Http\AbstractTransport;
  12  use Joomla\Http\Exception\InvalidResponseCodeException;
  13  use Joomla\Http\Response;
  14  use Joomla\Uri\Uri;
  15  use Joomla\Uri\UriInterface;
  16  use Laminas\Diactoros\Stream as StreamResponse;
  17  
  18  /**
  19   * HTTP transport class for using sockets directly.
  20   *
  21   * @since  1.0
  22   */
  23  class Socket extends AbstractTransport
  24  {
  25      /**
  26       * Reusable socket connections.
  27       *
  28       * @var    array
  29       * @since  1.0
  30       */
  31      protected $connections;
  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.0
  46       * @throws  \RuntimeException
  47       */
  48  	public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null)
  49      {
  50          $connection = $this->connect($uri, $timeout);
  51  
  52          // Make sure the connection is alive and valid.
  53          if (!\is_resource($connection))
  54          {
  55              throw new \RuntimeException('Not connected to server.');
  56          }
  57  
  58          // Make sure the connection has not timed out.
  59          $meta = stream_get_meta_data($connection);
  60  
  61          if ($meta['timed_out'])
  62          {
  63              throw new \RuntimeException('Server connection timed out.');
  64          }
  65  
  66          // Get the request path from the URI object.
  67          $path = $uri->toString(['path', 'query']);
  68  
  69          // If we have data to send make sure our request is setup for it.
  70          if (!empty($data))
  71          {
  72              // If the data is not a scalar value encode it to be sent with the request.
  73              if (!is_scalar($data))
  74              {
  75                  $data = http_build_query($data);
  76              }
  77  
  78              if (!isset($headers['Content-Type']))
  79              {
  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          // Configure protocol version, use transport's default if not set otherwise.
  88          $protocolVersion = $this->getOption('protocolVersion', '1.0');
  89  
  90          // Build the request payload.
  91          $request   = [];
  92          $request[] = strtoupper($method) . ' ' . ((empty($path)) ? '/' : $path) . ' HTTP/' . $protocolVersion;
  93  
  94          if (!isset($headers['Host']))
  95          {
  96              $request[] = 'Host: ' . $uri->getHost();
  97          }
  98  
  99          // If an explicit user agent is given use it.
 100          if (isset($userAgent))
 101          {
 102              $headers['User-Agent'] = $userAgent;
 103          }
 104  
 105          // If we have a username then we include basic authentication credentials.
 106          if ($uri->getUser())
 107          {
 108              $authString               = $uri->getUser() . ':' . $uri->getPass();
 109              $headers['Authorization'] = 'Basic ' . base64_encode($authString);
 110          }
 111  
 112          // If there are custom headers to send add them to the request payload.
 113          if (!empty($headers))
 114          {
 115              foreach ($headers as $key => $value)
 116              {
 117                  if (\is_array($value))
 118                  {
 119                      foreach ($value as $header)
 120                      {
 121                          $request[] = "$key: $header";
 122                      }
 123                  }
 124                  else
 125                  {
 126                      $request[] = "$key: $value";
 127                  }
 128              }
 129          }
 130  
 131          // Authentication, if needed
 132          if ($this->getOption('userauth') && $this->getOption('passwordauth'))
 133          {
 134              $request[] = 'Authorization: Basic ' . base64_encode($this->getOption('userauth') . ':' . $this->getOption('passwordauth'));
 135          }
 136  
 137          // Set any custom transport options
 138          foreach ($this->getOption('transport.socket', []) as $value)
 139          {
 140              $request[] = $value;
 141          }
 142  
 143          // If we have data to send add it to the request payload.
 144          if (!empty($data))
 145          {
 146              $request[] = null;
 147              $request[] = $data;
 148          }
 149  
 150          // Send the request to the server.
 151          fwrite($connection, implode("\r\n", $request) . "\r\n\r\n");
 152  
 153          // Get the response data from the server.
 154          $content = '';
 155  
 156          while (!feof($connection))
 157          {
 158              $content .= fgets($connection, 4096);
 159          }
 160  
 161          $content = $this->getResponse($content);
 162  
 163          // Follow Http redirects
 164          if ($content->getStatusCode() >= 301 && $content->getStatusCode() < 400 && $content->hasHeader('Location'))
 165          {
 166              return $this->request($method, new Uri($content->getHeaderLine('Location')), $data, $headers, $timeout, $userAgent);
 167          }
 168  
 169          return $content;
 170      }
 171  
 172      /**
 173       * Method to get a response object from a server response.
 174       *
 175       * @param   string  $content  The complete server response, including headers.
 176       *
 177       * @return  Response
 178       *
 179       * @since   1.0
 180       * @throws  \UnexpectedValueException
 181       * @throws  InvalidResponseCodeException
 182       */
 183  	protected function getResponse($content)
 184      {
 185          if (empty($content))
 186          {
 187              throw new \UnexpectedValueException('No content in response.');
 188          }
 189  
 190          // Split the response into headers and body.
 191          $response = explode("\r\n\r\n", $content, 2);
 192  
 193          // Get the response headers as an array.
 194          $headers = explode("\r\n", $response[0]);
 195  
 196          // Set the body for the response.
 197          $body = empty($response[1]) ? '' : $response[1];
 198  
 199          // Get the response code from the first offset of the response headers.
 200          preg_match('/[0-9]{3}/', array_shift($headers), $matches);
 201          $code = $matches[0];
 202  
 203          if (!is_numeric($code))
 204          {
 205              // No valid response code was detected.
 206              throw new InvalidResponseCodeException('No HTTP response code found.');
 207          }
 208  
 209          $statusCode      = (int) $code;
 210          $verifiedHeaders = $this->processHeaders($headers);
 211  
 212          $streamInterface = new StreamResponse('php://memory', 'rw');
 213          $streamInterface->write($body);
 214  
 215          return new Response($streamInterface, $statusCode, $verifiedHeaders);
 216      }
 217  
 218      /**
 219       * Method to connect to a server and get the resource.
 220       *
 221       * @param   UriInterface  $uri      The URI to connect with.
 222       * @param   integer       $timeout  Read timeout in seconds.
 223       *
 224       * @return  resource  Socket connection resource.
 225       *
 226       * @since   1.0
 227       * @throws  \RuntimeException
 228       */
 229  	protected function connect(UriInterface $uri, $timeout = null)
 230      {
 231          $errno = null;
 232          $err   = null;
 233  
 234          // Get the host from the uri.
 235          $host = ($uri->isSsl()) ? 'ssl://' . $uri->getHost() : $uri->getHost();
 236  
 237          // If the port is not explicitly set in the URI detect it.
 238          if (!$uri->getPort())
 239          {
 240              $port = ($uri->getScheme() == 'https') ? 443 : 80;
 241          }
 242  
 243          // Use the set port.
 244          else
 245          {
 246              $port = $uri->getPort();
 247          }
 248  
 249          // Build the connection key for resource memory caching.
 250          $key = md5($host . $port);
 251  
 252          // If the connection already exists, use it.
 253          if (!empty($this->connections[$key]) && \is_resource($this->connections[$key]))
 254          {
 255              // Connection reached EOF, cannot be used anymore
 256              $meta = stream_get_meta_data($this->connections[$key]);
 257  
 258              if ($meta['eof'])
 259              {
 260                  if (!fclose($this->connections[$key]))
 261                  {
 262                      throw new \RuntimeException('Cannot close connection');
 263                  }
 264              }
 265  
 266              // Make sure the connection has not timed out.
 267              elseif (!$meta['timed_out'])
 268              {
 269                  return $this->connections[$key];
 270              }
 271          }
 272  
 273          if (!is_numeric($timeout))
 274          {
 275              $timeout = ini_get('default_socket_timeout');
 276          }
 277  
 278          // Capture PHP errors
 279          $php_errormsg = '';
 280          $trackErrors  = ini_get('track_errors');
 281          ini_set('track_errors', true);
 282  
 283          // PHP sends a warning if the uri does not exists; we silence it and throw an exception instead.
 284          // Attempt to connect to the server
 285          $connection = @fsockopen($host, $port, $errno, $err, $timeout);
 286  
 287          if (!$connection)
 288          {
 289              if (!$php_errormsg)
 290              {
 291                  // Error but nothing from php? Create our own
 292                  $php_errormsg = sprintf('Could not connect to resource: %s', $uri);
 293              }
 294  
 295              // Restore error tracking to give control to the exception handler
 296              ini_set('track_errors', $trackErrors);
 297  
 298              throw new \RuntimeException($php_errormsg);
 299          }
 300  
 301          // Restore error tracking to what it was before.
 302          ini_set('track_errors', $trackErrors);
 303  
 304          // Since the connection was successful let's store it in case we need to use it later.
 305          $this->connections[$key] = $connection;
 306  
 307          stream_set_timeout($this->connections[$key], (int) $timeout);
 308  
 309          return $this->connections[$key];
 310      }
 311  
 312      /**
 313       * Method to check if http transport socket available for use
 314       *
 315       * @return  boolean   True if available else false
 316       *
 317       * @since   1.0
 318       */
 319  	public static function isSupported()
 320      {
 321          return \function_exists('fsockopen') && \is_callable('fsockopen');
 322      }
 323  }


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