[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |