Current File : /home/pacjaorg/public_html/dnpsom/libraries/vendor/joomla/http/src/Transport/Socket.php |
<?php
/**
* Part of the Joomla Framework Http Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Http\Transport;
use Joomla\Http\AbstractTransport;
use Joomla\Http\Exception\InvalidResponseCodeException;
use Joomla\Http\Response;
use Joomla\Uri\Uri;
use Joomla\Uri\UriInterface;
use Laminas\Diactoros\Stream as StreamResponse;
/**
* HTTP transport class for using sockets directly.
*
* @since 1.0
*/
class Socket extends AbstractTransport
{
/**
* Reusable socket connections.
*
* @var array
* @since 1.0
*/
protected $connections;
/**
* Send a request to the server and return a Response object with the response.
*
* @param string $method The HTTP method for sending the request.
* @param UriInterface $uri The URI to the resource to request.
* @param mixed $data Either an associative array or a string to be sent with the request.
* @param array $headers An array of request headers to send with the request.
* @param integer $timeout Read timeout in seconds.
* @param string $userAgent The optional user agent string to send with the request.
*
* @return Response
*
* @since 1.0
* @throws \RuntimeException
*/
public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null)
{
$connection = $this->connect($uri, $timeout);
// Make sure the connection is alive and valid.
if (!\is_resource($connection))
{
throw new \RuntimeException('Not connected to server.');
}
// Make sure the connection has not timed out.
$meta = stream_get_meta_data($connection);
if ($meta['timed_out'])
{
throw new \RuntimeException('Server connection timed out.');
}
// Get the request path from the URI object.
$path = $uri->toString(['path', 'query']);
// If we have data to send make sure our request is setup for it.
if (!empty($data))
{
// If the data is not a scalar value encode it to be sent with the request.
if (!is_scalar($data))
{
$data = http_build_query($data);
}
if (!isset($headers['Content-Type']))
{
$headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
}
// Add the relevant headers.
$headers['Content-Length'] = \strlen($data);
}
// Configure protocol version, use transport's default if not set otherwise.
$protocolVersion = $this->getOption('protocolVersion', '1.0');
// Build the request payload.
$request = [];
$request[] = strtoupper($method) . ' ' . ((empty($path)) ? '/' : $path) . ' HTTP/' . $protocolVersion;
if (!isset($headers['Host']))
{
$request[] = 'Host: ' . $uri->getHost();
}
// If an explicit user agent is given use it.
if (isset($userAgent))
{
$headers['User-Agent'] = $userAgent;
}
// If we have a username then we include basic authentication credentials.
if ($uri->getUser())
{
$authString = $uri->getUser() . ':' . $uri->getPass();
$headers['Authorization'] = 'Basic ' . base64_encode($authString);
}
// If there are custom headers to send add them to the request payload.
if (!empty($headers))
{
foreach ($headers as $key => $value)
{
if (\is_array($value))
{
foreach ($value as $header)
{
$request[] = "$key: $header";
}
}
else
{
$request[] = "$key: $value";
}
}
}
// Authentication, if needed
if ($this->getOption('userauth') && $this->getOption('passwordauth'))
{
$request[] = 'Authorization: Basic ' . base64_encode($this->getOption('userauth') . ':' . $this->getOption('passwordauth'));
}
// Set any custom transport options
foreach ($this->getOption('transport.socket', []) as $value)
{
$request[] = $value;
}
// If we have data to send add it to the request payload.
if (!empty($data))
{
$request[] = null;
$request[] = $data;
}
// Send the request to the server.
fwrite($connection, implode("\r\n", $request) . "\r\n\r\n");
// Get the response data from the server.
$content = '';
while (!feof($connection))
{
$content .= fgets($connection, 4096);
}
$content = $this->getResponse($content);
// Follow Http redirects
if ($content->getStatusCode() >= 301 && $content->getStatusCode() < 400 && $content->hasHeader('Location'))
{
return $this->request($method, new Uri($content->getHeaderLine('Location')), $data, $headers, $timeout, $userAgent);
}
return $content;
}
/**
* Method to get a response object from a server response.
*
* @param string $content The complete server response, including headers.
*
* @return Response
*
* @since 1.0
* @throws \UnexpectedValueException
* @throws InvalidResponseCodeException
*/
protected function getResponse($content)
{
if (empty($content))
{
throw new \UnexpectedValueException('No content in response.');
}
// Split the response into headers and body.
$response = explode("\r\n\r\n", $content, 2);
// Get the response headers as an array.
$headers = explode("\r\n", $response[0]);
// Set the body for the response.
$body = empty($response[1]) ? '' : $response[1];
// Get the response code from the first offset of the response headers.
preg_match('/[0-9]{3}/', array_shift($headers), $matches);
$code = $matches[0];
if (!is_numeric($code))
{
// No valid response code was detected.
throw new InvalidResponseCodeException('No HTTP response code found.');
}
$statusCode = (int) $code;
$verifiedHeaders = $this->processHeaders($headers);
$streamInterface = new StreamResponse('php://memory', 'rw');
$streamInterface->write($body);
return new Response($streamInterface, $statusCode, $verifiedHeaders);
}
/**
* Method to connect to a server and get the resource.
*
* @param UriInterface $uri The URI to connect with.
* @param integer $timeout Read timeout in seconds.
*
* @return resource Socket connection resource.
*
* @since 1.0
* @throws \RuntimeException
*/
protected function connect(UriInterface $uri, $timeout = null)
{
$errno = null;
$err = null;
// Get the host from the uri.
$host = ($uri->isSsl()) ? 'ssl://' . $uri->getHost() : $uri->getHost();
// If the port is not explicitly set in the URI detect it.
if (!$uri->getPort())
{
$port = ($uri->getScheme() == 'https') ? 443 : 80;
}
// Use the set port.
else
{
$port = $uri->getPort();
}
// Build the connection key for resource memory caching.
$key = md5($host . $port);
// If the connection already exists, use it.
if (!empty($this->connections[$key]) && \is_resource($this->connections[$key]))
{
// Connection reached EOF, cannot be used anymore
$meta = stream_get_meta_data($this->connections[$key]);
if ($meta['eof'])
{
if (!fclose($this->connections[$key]))
{
throw new \RuntimeException('Cannot close connection');
}
}
// Make sure the connection has not timed out.
elseif (!$meta['timed_out'])
{
return $this->connections[$key];
}
}
if (!is_numeric($timeout))
{
$timeout = ini_get('default_socket_timeout');
}
// Capture PHP errors
$php_errormsg = '';
$trackErrors = ini_get('track_errors');
ini_set('track_errors', true);
// PHP sends a warning if the uri does not exists; we silence it and throw an exception instead.
// Attempt to connect to the server
$connection = @fsockopen($host, $port, $errno, $err, $timeout);
if (!$connection)
{
if (!$php_errormsg)
{
// Error but nothing from php? Create our own
$php_errormsg = sprintf('Could not connect to resource: %s', $uri);
}
// Restore error tracking to give control to the exception handler
ini_set('track_errors', $trackErrors);
throw new \RuntimeException($php_errormsg);
}
// Restore error tracking to what it was before.
ini_set('track_errors', $trackErrors);
// Since the connection was successful let's store it in case we need to use it later.
$this->connections[$key] = $connection;
stream_set_timeout($this->connections[$key], (int) $timeout);
return $this->connections[$key];
}
/**
* Method to check if http transport socket available for use
*
* @return boolean True if available else false
*
* @since 1.0
*/
public static function isSupported()
{
return \function_exists('fsockopen') && \is_callable('fsockopen');
}
}