[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 Composer\CaBundle\CaBundle; 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Http\Response; 15 use Joomla\CMS\Http\TransportInterface; 16 use Joomla\CMS\Uri\Uri; 17 use Joomla\Http\AbstractTransport; 18 use Joomla\Http\Exception\InvalidResponseCodeException; 19 use Joomla\Uri\UriInterface; 20 use Laminas\Diactoros\Stream as StreamResponse; 21 22 // phpcs:disable PSR1.Files.SideEffects 23 \defined('JPATH_PLATFORM') or die; 24 // phpcs:enable PSR1.Files.SideEffects 25 26 /** 27 * HTTP transport class for using cURL. 28 * 29 * @since 1.7.3 30 */ 31 class CurlTransport extends AbstractTransport implements TransportInterface 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.7.3 46 * @throws \RuntimeException 47 */ 48 public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) 49 { 50 // Setup the cURL handle. 51 $ch = curl_init(); 52 53 $options = array(); 54 55 // Set the request method. 56 switch (strtoupper($method)) { 57 case 'GET': 58 $options[CURLOPT_HTTPGET] = true; 59 break; 60 61 case 'POST': 62 $options[CURLOPT_POST] = true; 63 break; 64 65 case 'PUT': 66 default: 67 $options[CURLOPT_CUSTOMREQUEST] = strtoupper($method); 68 break; 69 } 70 71 // Don't wait for body when $method is HEAD 72 $options[CURLOPT_NOBODY] = ($method === 'HEAD'); 73 74 // Initialize the certificate store 75 $options[CURLOPT_CAINFO] = $this->getOption('curl.certpath', CaBundle::getBundledCaBundlePath()); 76 77 // If data exists let's encode it and make sure our Content-type header is set. 78 if (isset($data)) { 79 // If the data is a scalar value simply add it to the cURL post fields. 80 if (is_scalar($data) || (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') === 0)) { 81 $options[CURLOPT_POSTFIELDS] = $data; 82 } else { 83 // Otherwise we need to encode the value first. 84 $options[CURLOPT_POSTFIELDS] = http_build_query($data); 85 } 86 87 if (!isset($headers['Content-Type'])) { 88 $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; 89 } 90 91 // Add the relevant headers. 92 if (is_scalar($options[CURLOPT_POSTFIELDS])) { 93 $headers['Content-Length'] = \strlen($options[CURLOPT_POSTFIELDS]); 94 } 95 } 96 97 // Build the headers string for the request. 98 $headerArray = array(); 99 100 if (isset($headers)) { 101 foreach ($headers as $key => $value) { 102 $headerArray[] = $key . ': ' . $value; 103 } 104 105 // Add the headers string into the stream context options array. 106 $options[CURLOPT_HTTPHEADER] = $headerArray; 107 } 108 109 // Curl needs the accepted encoding header as option 110 if (isset($headers['Accept-Encoding'])) { 111 $options[CURLOPT_ENCODING] = $headers['Accept-Encoding']; 112 } 113 114 // If an explicit timeout is given user it. 115 if (isset($timeout)) { 116 $options[CURLOPT_TIMEOUT] = (int) $timeout; 117 $options[CURLOPT_CONNECTTIMEOUT] = (int) $timeout; 118 } 119 120 // If an explicit user agent is given use it. 121 if (isset($userAgent)) { 122 $options[CURLOPT_USERAGENT] = $userAgent; 123 } 124 125 // Set the request URL. 126 $options[CURLOPT_URL] = (string) $uri; 127 128 // We want our headers. :-) 129 $options[CURLOPT_HEADER] = true; 130 131 // Return it... echoing it would be tacky. 132 $options[CURLOPT_RETURNTRANSFER] = true; 133 134 // Override the Expect header to prevent cURL from confusing itself in its own stupidity. 135 // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/ 136 $options[CURLOPT_HTTPHEADER][] = 'Expect:'; 137 138 // Follow redirects if server config allows 139 if ($this->redirectsAllowed()) { 140 $options[CURLOPT_FOLLOWLOCATION] = (bool) $this->getOption('follow_location', true); 141 } 142 143 // Proxy configuration 144 $app = Factory::getApplication(); 145 146 if ($app->get('proxy_enable')) { 147 $options[CURLOPT_PROXY] = $app->get('proxy_host') . ':' . $app->get('proxy_port'); 148 149 if ($user = $app->get('proxy_user')) { 150 $options[CURLOPT_PROXYUSERPWD] = $user . ':' . $app->get('proxy_pass'); 151 } 152 } 153 154 // Set any custom transport options 155 foreach ($this->getOption('transport.curl', array()) as $key => $value) { 156 $options[$key] = $value; 157 } 158 159 // Authentication, if needed 160 if ($this->getOption('userauth') && $this->getOption('passwordauth')) { 161 $options[CURLOPT_USERPWD] = $this->getOption('userauth') . ':' . $this->getOption('passwordauth'); 162 $options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; 163 } 164 165 // Set the cURL options. 166 curl_setopt_array($ch, $options); 167 168 // Execute the request and close the connection. 169 $content = curl_exec($ch); 170 171 // Check if the content is a string. If it is not, it must be an error. 172 if (!\is_string($content)) { 173 $message = curl_error($ch); 174 175 if (empty($message)) { 176 // Error but nothing from cURL? Create our own 177 $message = 'No HTTP response received'; 178 } 179 180 throw new \RuntimeException($message); 181 } 182 183 // Get the request information. 184 $info = curl_getinfo($ch); 185 186 // Close the connection. 187 curl_close($ch); 188 189 $response = $this->getResponse($content, $info); 190 191 // Manually follow redirects if server doesn't allow to follow location using curl 192 if ($response->code >= 301 && $response->code < 400 && isset($response->headers['Location']) && (bool) $this->getOption('follow_location', true)) { 193 $redirect_uri = new Uri($response->headers['Location']); 194 195 if (\in_array($redirect_uri->getScheme(), array('file', 'scp'))) { 196 throw new \RuntimeException('Curl redirect cannot be used in file or scp requests.'); 197 } 198 199 $response = $this->request($method, $redirect_uri, $data, $headers, $timeout, $userAgent); 200 } 201 202 return $response; 203 } 204 205 /** 206 * Method to get a response object from a server response. 207 * 208 * @param string $content The complete server response, including headers 209 * as a string if the response has no errors. 210 * @param array $info The cURL request information. 211 * 212 * @return Response 213 * 214 * @since 1.7.3 215 * @throws InvalidResponseCodeException 216 */ 217 protected function getResponse($content, $info) 218 { 219 // Try to get header size 220 if (isset($info['header_size'])) { 221 $headerString = trim(substr($content, 0, $info['header_size'])); 222 $headerArray = explode("\r\n\r\n", $headerString); 223 224 // Get the last set of response headers as an array. 225 $headers = explode("\r\n", array_pop($headerArray)); 226 227 // Set the body for the response. 228 $body = substr($content, $info['header_size']); 229 } else { 230 // Fallback and try to guess header count by redirect count 231 // Get the number of redirects that occurred. 232 $redirects = $info['redirect_count'] ?? 0; 233 234 /* 235 * Split the response into headers and body. If cURL encountered redirects, the headers for the redirected requests will 236 * also be included. So we split the response into header + body + the number of redirects and only use the last two 237 * sections which should be the last set of headers and the actual body. 238 */ 239 $response = explode("\r\n\r\n", $content, 2 + $redirects); 240 241 // Set the body for the response. 242 $body = array_pop($response); 243 244 // Get the last set of response headers as an array. 245 $headers = explode("\r\n", array_pop($response)); 246 } 247 248 // Get the response code from the first offset of the response headers. 249 preg_match('/[0-9]{3}/', array_shift($headers), $matches); 250 251 $code = \count($matches) ? $matches[0] : null; 252 253 if (!is_numeric($code)) { 254 // No valid response code was detected. 255 throw new InvalidResponseCodeException('No HTTP response code found.'); 256 } 257 258 $statusCode = (int) $code; 259 $verifiedHeaders = $this->processHeaders($headers); 260 261 $streamInterface = new StreamResponse('php://memory', 'rw'); 262 $streamInterface->write($body); 263 264 return new Response($streamInterface, $statusCode, $verifiedHeaders); 265 } 266 267 /** 268 * Method to check if HTTP transport cURL is 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('curl_version') && curl_version(); 277 } 278 279 /** 280 * Check if redirects are allowed 281 * 282 * @return boolean 283 * 284 * @since 3.0.0 285 */ 286 private function redirectsAllowed() 287 { 288 $curlVersion = curl_version(); 289 290 // If open_basedir is enabled we also need to check if libcurl version is 7.19.4 or higher 291 if (!ini_get('open_basedir') || version_compare($curlVersion['version'], '7.19.4', '>=')) { 292 return true; 293 } 294 295 return false; 296 } 297 }
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 |