[ 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 Composer\CaBundle\CaBundle; 12 use Joomla\Http\AbstractTransport; 13 use Joomla\Http\Exception\InvalidResponseCodeException; 14 use Joomla\Http\Response; 15 use Joomla\Uri\UriInterface; 16 use Laminas\Diactoros\Stream as StreamResponse; 17 18 /** 19 * HTTP transport class for using cURL. 20 * 21 * @since 1.0 22 */ 23 class Curl extends AbstractTransport 24 { 25 /** 26 * Send a request to the server and return a Response object with the response. 27 * 28 * @param string $method The HTTP method for sending the request. 29 * @param UriInterface $uri The URI to the resource to request. 30 * @param mixed $data Either an associative array or a string to be sent with the request. 31 * @param array $headers An array of request headers to send with the request. 32 * @param integer $timeout Read timeout in seconds. 33 * @param string $userAgent The optional user agent string to send with the request. 34 * 35 * @return Response 36 * 37 * @since 1.0 38 * @throws \RuntimeException 39 */ 40 public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) 41 { 42 // Setup the cURL handle. 43 $ch = curl_init(); 44 45 // Initialize the certificate store 46 $this->setCAOptionAndValue($ch); 47 48 $options = []; 49 50 // Set the request method. 51 switch (strtoupper($method)) 52 { 53 case 'GET': 54 $options[\CURLOPT_HTTPGET] = true; 55 56 break; 57 58 case 'POST': 59 $options[\CURLOPT_POST] = true; 60 61 break; 62 63 default: 64 $options[\CURLOPT_CUSTOMREQUEST] = strtoupper($method); 65 66 break; 67 } 68 69 // Don't wait for body when $method is HEAD 70 $options[\CURLOPT_NOBODY] = ($method === 'HEAD'); 71 72 // Initialize the certificate store 73 $options[CURLOPT_CAINFO] = $this->getOption('curl.certpath', CaBundle::getSystemCaRootBundlePath()); 74 75 // If data exists let's encode it and make sure our Content-type header is set. 76 if (isset($data)) 77 { 78 // If the data is a scalar value simply add it to the cURL post fields. 79 if (is_scalar($data) || (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') === 0)) 80 { 81 $options[\CURLOPT_POSTFIELDS] = $data; 82 } 83 else 84 { 85 // Otherwise we need to encode the value first. 86 $options[\CURLOPT_POSTFIELDS] = http_build_query($data); 87 } 88 89 if (!isset($headers['Content-Type'])) 90 { 91 $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; 92 } 93 94 // Add the relevant headers. 95 if (is_scalar($options[\CURLOPT_POSTFIELDS])) 96 { 97 $headers['Content-Length'] = \strlen($options[\CURLOPT_POSTFIELDS]); 98 } 99 } 100 101 // Build the headers string for the request. 102 $headerArray = []; 103 104 if (!empty($headers)) 105 { 106 foreach ($headers as $key => $value) 107 { 108 if (\is_array($value)) 109 { 110 foreach ($value as $header) 111 { 112 $headerArray[] = "$key: $header"; 113 } 114 } 115 else 116 { 117 $headerArray[] = "$key: $value"; 118 } 119 } 120 121 // Add the headers string into the stream context options array. 122 $options[\CURLOPT_HTTPHEADER] = $headerArray; 123 } 124 125 // Curl needs the accepted encoding header as option 126 if (isset($headers['Accept-Encoding'])) 127 { 128 $options[\CURLOPT_ENCODING] = $headers['Accept-Encoding']; 129 } 130 131 // If an explicit timeout is given user it. 132 if (isset($timeout)) 133 { 134 $options[\CURLOPT_TIMEOUT] = (int) $timeout; 135 $options[\CURLOPT_CONNECTTIMEOUT] = (int) $timeout; 136 } 137 138 // If an explicit user agent is given use it. 139 if (isset($userAgent)) 140 { 141 $options[\CURLOPT_USERAGENT] = $userAgent; 142 } 143 144 // Set the request URL. 145 $options[\CURLOPT_URL] = (string) $uri; 146 147 // We want our headers. :-) 148 $options[\CURLOPT_HEADER] = true; 149 150 // Return it... echoing it would be tacky. 151 $options[\CURLOPT_RETURNTRANSFER] = true; 152 153 // Override the Expect header to prevent cURL from confusing itself in its own stupidity. 154 // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/ 155 $options[\CURLOPT_HTTPHEADER][] = 'Expect:'; 156 157 // Follow redirects if server config allows 158 if ($this->redirectsAllowed()) 159 { 160 $options[\CURLOPT_FOLLOWLOCATION] = (bool) $this->getOption('follow_location', true); 161 } 162 163 // Authentication, if needed 164 if ($this->getOption('userauth') && $this->getOption('passwordauth')) 165 { 166 $options[\CURLOPT_USERPWD] = $this->getOption('userauth') . ':' . $this->getOption('passwordauth'); 167 $options[\CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; 168 } 169 170 // Configure protocol version 171 if ($protocolVersion = $this->getOption('protocolVersion')) 172 { 173 $options[\CURLOPT_HTTP_VERSION] = $this->mapProtocolVersion($protocolVersion); 174 } 175 176 // Set any custom transport options 177 foreach ($this->getOption('transport.curl', []) as $key => $value) 178 { 179 $options[$key] = $value; 180 } 181 182 // Set the cURL options. 183 curl_setopt_array($ch, $options); 184 185 // Execute the request and close the connection. 186 $content = curl_exec($ch); 187 188 // Check if the content is a string. If it is not, it must be an error. 189 if (!\is_string($content)) 190 { 191 $message = curl_error($ch); 192 193 if (empty($message)) 194 { 195 // Error but nothing from cURL? Create our own 196 $message = 'No HTTP response received'; 197 } 198 199 throw new \RuntimeException($message); 200 } 201 202 // Get the request information. 203 $info = curl_getinfo($ch); 204 205 // Close the connection. 206 curl_close($ch); 207 208 return $this->getResponse($content, $info); 209 } 210 211 /** 212 * Configure the cURL resources with the appropriate root certificates. 213 * 214 * @param resource $ch The cURL resource you want to configure the certificates on. 215 * 216 * @return void 217 * 218 * @since 1.3.2 219 */ 220 protected function setCAOptionAndValue($ch) 221 { 222 if ($certpath = $this->getOption('curl.certpath')) 223 { 224 // Option is passed to a .PEM file. 225 curl_setopt($ch, \CURLOPT_CAINFO, $certpath); 226 227 return; 228 } 229 230 $caPathOrFile = CaBundle::getSystemCaRootBundlePath(); 231 232 if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) 233 { 234 curl_setopt($ch, \CURLOPT_CAPATH, $caPathOrFile); 235 236 return; 237 } 238 239 curl_setopt($ch, \CURLOPT_CAINFO, $caPathOrFile); 240 } 241 242 /** 243 * Method to get a response object from a server response. 244 * 245 * @param string $content The complete server response, including headers 246 * as a string if the response has no errors. 247 * @param array $info The cURL request information. 248 * 249 * @return Response 250 * 251 * @since 1.0 252 * @throws InvalidResponseCodeException 253 */ 254 protected function getResponse($content, $info) 255 { 256 // Try to get header size 257 if (isset($info['header_size'])) 258 { 259 $headerString = trim(substr($content, 0, $info['header_size'])); 260 $headerArray = explode("\r\n\r\n", $headerString); 261 262 // Get the last set of response headers as an array. 263 $headers = explode("\r\n", array_pop($headerArray)); 264 265 // Set the body for the response. 266 $body = substr($content, $info['header_size']); 267 } 268 // Fallback and try to guess header count by redirect count 269 else 270 { 271 // Get the number of redirects that occurred. 272 $redirects = $info['redirect_count'] ?? 0; 273 274 /* 275 * Split the response into headers and body. If cURL encountered redirects, the headers for the redirected requests will 276 * also be included. So we split the response into header + body + the number of redirects and only use the last two 277 * sections which should be the last set of headers and the actual body. 278 */ 279 $response = explode("\r\n\r\n", $content, 2 + $redirects); 280 281 // Set the body for the response. 282 $body = array_pop($response); 283 284 // Get the last set of response headers as an array. 285 $headers = explode("\r\n", array_pop($response)); 286 } 287 288 // Get the response code from the first offset of the response headers. 289 preg_match('/[0-9]{3}/', array_shift($headers), $matches); 290 291 $code = \count($matches) ? $matches[0] : null; 292 293 if (!is_numeric($code)) 294 { 295 // No valid response code was detected. 296 throw new InvalidResponseCodeException('No HTTP response code found.'); 297 } 298 299 $statusCode = (int) $code; 300 $verifiedHeaders = $this->processHeaders($headers); 301 302 $streamInterface = new StreamResponse('php://memory', 'rw'); 303 $streamInterface->write($body); 304 305 return new Response($streamInterface, $statusCode, $verifiedHeaders); 306 } 307 308 /** 309 * Method to check if HTTP transport cURL is available for use 310 * 311 * @return boolean True if available, else false 312 * 313 * @since 1.0 314 */ 315 public static function isSupported() 316 { 317 return \function_exists('curl_version') && curl_version(); 318 } 319 320 /** 321 * Get the cURL constant for a HTTP protocol version 322 * 323 * @param string $version The HTTP protocol version to use 324 * 325 * @return integer 326 * 327 * @since 1.3.1 328 */ 329 private function mapProtocolVersion(string $version): int 330 { 331 switch ($version) 332 { 333 case '1.0': 334 return \CURL_HTTP_VERSION_1_0; 335 336 case '1.1': 337 return \CURL_HTTP_VERSION_1_1; 338 339 case '2.0': 340 case '2': 341 if (\defined('CURL_HTTP_VERSION_2')) 342 { 343 return \CURL_HTTP_VERSION_2; 344 } 345 } 346 347 return \CURL_HTTP_VERSION_NONE; 348 } 349 350 /** 351 * Check if redirects are allowed 352 * 353 * @return boolean 354 * 355 * @since 1.2.1 356 */ 357 private function redirectsAllowed(): bool 358 { 359 return true; 360 } 361 }
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 |