Changeset 52244 for trunk/src/wp-includes/Requests/Transport/Fsockopen.php
- Timestamp:
- 11/25/2021 01:10:30 AM (5 years ago)
- File:
-
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/Requests/Transport/Fsockopen.php
r52243 r52244 3 3 * fsockopen HTTP transport 4 4 * 5 * @package Requests 6 * @subpackage Transport 5 * @package Requests\Transport 7 6 */ 7 8 namespace WpOrg\Requests\Transport; 9 10 use WpOrg\Requests\Capability; 11 use WpOrg\Requests\Exception; 12 use WpOrg\Requests\Exception\InvalidArgument; 13 use WpOrg\Requests\Port; 14 use WpOrg\Requests\Requests; 15 use WpOrg\Requests\Ssl; 16 use WpOrg\Requests\Transport; 17 use WpOrg\Requests\Utility\CaseInsensitiveDictionary; 18 use WpOrg\Requests\Utility\InputValidator; 8 19 9 20 /** 10 21 * fsockopen HTTP transport 11 22 * 12 * @package Requests 13 * @subpackage Transport 23 * @package Requests\Transport 14 24 */ 15 class Requests_Transport_fsockopen implements Requests_Transport {25 final class Fsockopen implements Transport { 16 26 /** 17 27 * Second to microsecond conversion … … 31 41 * Stream metadata 32 42 * 33 * @var array Associative array of properties, see {@ see https://secure.php.net/stream_get_meta_data}43 * @var array Associative array of properties, see {@link https://www.php.net/stream_get_meta_data} 34 44 */ 35 45 public $info; … … 40 50 * @var int|bool Byte count, or false if no limit. 41 51 */ 42 pr otected$max_bytes = false;43 44 pr otected$connect_error = '';52 private $max_bytes = false; 53 54 private $connect_error = ''; 45 55 46 56 /** 47 57 * Perform a request 48 58 * 49 * @throws Requests_Exception On failure to connect to socket (`fsockopenerror`) 50 * @throws Requests_Exception On socket timeout (`timeout`) 51 * 52 * @param string $url URL to request 59 * @param string|Stringable $url URL to request 53 60 * @param array $headers Associative array of request headers 54 61 * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD 55 * @param array $options Request options, see {@see Requests::response()} for documentation62 * @param array $options Request options, see {@see \WpOrg\Requests\Requests::response()} for documentation 56 63 * @return string Raw HTTP result 57 */ 58 public function request($url, $headers = array(), $data = array(), $options = array()) { 64 * 65 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable. 66 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $headers argument is not an array. 67 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $data parameter is not an array or string. 68 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array. 69 * @throws \WpOrg\Requests\Exception On failure to connect to socket (`fsockopenerror`) 70 * @throws \WpOrg\Requests\Exception On socket timeout (`timeout`) 71 */ 72 public function request($url, $headers = [], $data = [], $options = []) { 73 if (InputValidator::is_string_or_stringable($url) === false) { 74 throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url)); 75 } 76 77 if (is_array($headers) === false) { 78 throw InvalidArgument::create(2, '$headers', 'array', gettype($headers)); 79 } 80 81 if (!is_array($data) && !is_string($data)) { 82 if ($data === null) { 83 $data = ''; 84 } else { 85 throw InvalidArgument::create(3, '$data', 'array|string', gettype($data)); 86 } 87 } 88 89 if (is_array($options) === false) { 90 throw InvalidArgument::create(4, '$options', 'array', gettype($options)); 91 } 92 59 93 $options['hooks']->dispatch('fsockopen.before_request'); 60 94 61 95 $url_parts = parse_url($url); 62 96 if (empty($url_parts)) { 63 throw new Requests_Exception('Invalid URL.', 'invalidurl', $url);97 throw new Exception('Invalid URL.', 'invalidurl', $url); 64 98 } 65 99 $host = $url_parts['host']; 66 100 $context = stream_context_create(); 67 101 $verifyname = false; 68 $case_insensitive_headers = new Requests_Utility_CaseInsensitiveDictionary($headers);102 $case_insensitive_headers = new CaseInsensitiveDictionary($headers); 69 103 70 104 // HTTPS support … … 72 106 $remote_socket = 'ssl://' . $host; 73 107 if (!isset($url_parts['port'])) { 74 $url_parts['port'] = 443;75 } 76 77 $context_options = array(108 $url_parts['port'] = Port::HTTPS; 109 } 110 111 $context_options = [ 78 112 'verify_peer' => true, 79 113 'capture_peer_cert' => true, 80 );114 ]; 81 115 $verifyname = true; 82 116 … … 106 140 } 107 141 108 stream_context_set_option($context, array('ssl' => $context_options));142 stream_context_set_option($context, ['ssl' => $context_options]); 109 143 } 110 144 else { … … 115 149 116 150 if (!isset($url_parts['port'])) { 117 $url_parts['port'] = 80;151 $url_parts['port'] = Port::HTTP; 118 152 } 119 153 $remote_socket .= ':' . $url_parts['port']; 120 154 121 155 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler 122 set_error_handler( array($this, 'connect_error_handler'), E_WARNING | E_NOTICE);123 124 $options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket));156 set_error_handler([$this, 'connect_error_handler'], E_WARNING | E_NOTICE); 157 158 $options['hooks']->dispatch('fsockopen.remote_socket', [&$remote_socket]); 125 159 126 160 $socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context); … … 129 163 130 164 if ($verifyname && !$this->verify_certificate_from_context($host, $context)) { 131 throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');165 throw new Exception('SSL certificate did not match the requested domain name', 'ssl.no_match'); 132 166 } 133 167 … … 135 169 if ($errno === 0) { 136 170 // Connection issue 137 throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error');138 } 139 140 throw new Requests_Exception($errstr, 'fsockopenerror', null, $errno);171 throw new Exception(rtrim($this->connect_error), 'fsockopen.connect_error'); 172 } 173 174 throw new Exception($errstr, 'fsockopenerror', null, $errno); 141 175 } 142 176 … … 148 182 } 149 183 else { 150 $path = self::format_get($url_parts, array());151 } 152 153 $options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url));184 $path = self::format_get($url_parts, []); 185 } 186 187 $options['hooks']->dispatch('fsockopen.remote_host_path', [&$path, $url]); 154 188 155 189 $request_body = ''; … … 178 212 179 213 if (!isset($case_insensitive_headers['Host'])) { 180 $out .= sprintf('Host: %s', $url_parts['host']); 181 182 if ((strtolower($url_parts['scheme']) === 'http' && $url_parts['port'] !== 80) || (strtolower($url_parts['scheme']) === 'https' && $url_parts['port'] !== 443)) { 214 $out .= sprintf('Host: %s', $url_parts['host']); 215 $scheme_lower = strtolower($url_parts['scheme']); 216 217 if (($scheme_lower === 'http' && $url_parts['port'] !== Port::HTTP) || ($scheme_lower === 'https' && $url_parts['port'] !== Port::HTTPS)) { 183 218 $out .= ':' . $url_parts['port']; 184 219 } … … 201 236 } 202 237 203 $options['hooks']->dispatch('fsockopen.after_headers', array(&$out));238 $options['hooks']->dispatch('fsockopen.after_headers', [&$out]); 204 239 205 240 if (substr($out, -2) !== "\r\n") { … … 213 248 $out .= "\r\n" . $request_body; 214 249 215 $options['hooks']->dispatch('fsockopen.before_send', array(&$out));250 $options['hooks']->dispatch('fsockopen.before_send', [&$out]); 216 251 217 252 fwrite($socket, $out); 218 $options['hooks']->dispatch('fsockopen.after_send', array($out));253 $options['hooks']->dispatch('fsockopen.after_send', [$out]); 219 254 220 255 if (!$options['blocking']) { 221 256 fclose($socket); 222 257 $fake_headers = ''; 223 $options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers));258 $options['hooks']->dispatch('fsockopen.after_request', [&$fake_headers]); 224 259 return ''; 225 260 } … … 242 277 $download = false; 243 278 if ($options['filename']) { 244 $download = fopen($options['filename'], 'wb'); 279 // phpcs:ignore WordPress.PHP.NoSilencedErrors -- Silenced the PHP native warning in favour of throwing an exception. 280 $download = @fopen($options['filename'], 'wb'); 281 if ($download === false) { 282 $error = error_get_last(); 283 throw new Exception($error['message'], 'fopen'); 284 } 245 285 } 246 286 … … 248 288 $this->info = stream_get_meta_data($socket); 249 289 if ($this->info['timed_out']) { 250 throw new Requests_Exception('fsocket timed out', 'timeout');290 throw new Exception('fsocket timed out', 'timeout'); 251 291 } 252 292 … … 262 302 // Are we in body mode now? 263 303 if ($doingbody) { 264 $options['hooks']->dispatch('request.progress', array($block, $size, $this->max_bytes));304 $options['hooks']->dispatch('request.progress', [$block, $size, $this->max_bytes]); 265 305 $data_length = strlen($block); 266 306 if ($this->max_bytes) { … … 295 335 fclose($socket); 296 336 297 $options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers, &$this->info));337 $options['hooks']->dispatch('fsockopen.after_request', [&$this->headers, &$this->info]); 298 338 return $this->headers; 299 339 } … … 302 342 * Send multiple requests simultaneously 303 343 * 304 * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request} 305 * @param array $options Global options, see {@see Requests::response()} for documentation 306 * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) 344 * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see \WpOrg\Requests\Transport::request()} 345 * @param array $options Global options, see {@see \WpOrg\Requests\Requests::response()} for documentation 346 * @return array Array of \WpOrg\Requests\Response objects (may contain \WpOrg\Requests\Exception or string responses as well) 347 * 348 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access. 349 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array. 307 350 */ 308 351 public function request_multiple($requests, $options) { 309 $responses = array(); 352 // If you're not requesting, we can't get any responses ¯\_(ツ)_/¯ 353 if (empty($requests)) { 354 return []; 355 } 356 357 if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) { 358 throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests)); 359 } 360 361 if (is_array($options) === false) { 362 throw InvalidArgument::create(2, '$options', 'array', gettype($options)); 363 } 364 365 $responses = []; 310 366 $class = get_class($this); 311 367 foreach ($requests as $id => $request) { … … 314 370 $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); 315 371 316 $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request));317 } 318 catch ( Requests_Exception $e) {372 $request['options']['hooks']->dispatch('transport.internal.parse_response', [&$responses[$id], $request]); 373 } 374 catch (Exception $e) { 319 375 $responses[$id] = $e; 320 376 } 321 377 322 378 if (!is_string($responses[$id])) { 323 $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id));379 $request['options']['hooks']->dispatch('multiple.request.complete', [&$responses[$id], $id]); 324 380 } 325 381 } … … 333 389 * @return string Accept-Encoding header value 334 390 */ 335 pr otectedstatic function accept_encoding() {336 $type = array();391 private static function accept_encoding() { 392 $type = []; 337 393 if (function_exists('gzinflate')) { 338 394 $type[] = 'deflate;q=1.0'; … … 352 408 * 353 409 * @param array $url_parts 354 * @param array|object $data Data to build query using, see {@ see https://secure.php.net/http_build_query}410 * @param array|object $data Data to build query using, see {@link https://www.php.net/http_build_query} 355 411 * @return string URL with data 356 412 */ 357 pr otectedstatic function format_get($url_parts, $data) {413 private static function format_get($url_parts, $data) { 358 414 if (!empty($data)) { 359 415 if (empty($url_parts['query'])) { … … 402 458 * Instead 403 459 * 404 * @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 405 * 406 * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`) 407 * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) 460 * @link https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 461 * 408 462 * @param string $host Host name to verify against 409 463 * @param resource $context Stream context 410 464 * @return bool 465 * 466 * @throws \WpOrg\Requests\Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`) 467 * @throws \WpOrg\Requests\Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) 411 468 */ 412 469 public function verify_certificate_from_context($host, $context) { … … 416 473 // all 417 474 if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { 418 throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error');475 throw new Exception(rtrim($this->connect_error), 'ssl.connect_error'); 419 476 } 420 477 421 478 $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); 422 479 423 return Requests_SSL::verify_certificate($host, $cert); 424 } 425 426 /** 427 * Whether this transport is valid 480 return Ssl::verify_certificate($host, $cert); 481 } 482 483 /** 484 * Self-test whether the transport can be used. 485 * 486 * The available capabilities to test for can be found in {@see \WpOrg\Requests\Capability}. 428 487 * 429 488 * @codeCoverageIgnore 430 * @return boolean True if the transport is valid, false otherwise. 431 */ 432 public static function test($capabilities = array()) { 489 * @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`. 490 * @return bool Whether the transport can be used. 491 */ 492 public static function test($capabilities = []) { 433 493 if (!function_exists('fsockopen')) { 434 494 return false; … … 436 496 437 497 // If needed, check that streams support SSL 438 if (isset($capabilities[ 'ssl']) && $capabilities['ssl']) {498 if (isset($capabilities[Capability::SSL]) && $capabilities[Capability::SSL]) { 439 499 if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) { 440 500 return false; 441 501 } 442 443 // Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156444 if (defined('HHVM_VERSION')) {445 return false;446 }447 502 } 448 503
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)