wp/wp-includes/class-requests.php
changeset 21 48c4eec2b7e6
parent 18 be944660c56a
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     5  * Inspired by Requests for Python.
     5  * Inspired by Requests for Python.
     6  *
     6  *
     7  * Based on concepts from SimplePie_File, RequestCore and WP_Http.
     7  * Based on concepts from SimplePie_File, RequestCore and WP_Http.
     8  *
     8  *
     9  * @package Requests
     9  * @package Requests
       
    10  *
       
    11  * @deprecated 6.2.0
    10  */
    12  */
       
    13 
       
    14 /*
       
    15  * Integrators who cannot yet upgrade to the PSR-4 class names can silence deprecations
       
    16  * by defining a `REQUESTS_SILENCE_PSR0_DEPRECATIONS` constant and setting it to `true`.
       
    17  * The constant needs to be defined before this class is required.
       
    18  */
       
    19 if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS') || REQUESTS_SILENCE_PSR0_DEPRECATIONS !== true) {
       
    20 	// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
       
    21 	trigger_error(
       
    22 		'The PSR-0 `Requests_...` class names in the Requests library are deprecated.'
       
    23 		. ' Switch to the PSR-4 `WpOrg\Requests\...` class names at your earliest convenience.',
       
    24 		E_USER_DEPRECATED
       
    25 	);
       
    26 
       
    27 	// Prevent the deprecation notice from being thrown twice.
       
    28 	if (!defined('REQUESTS_SILENCE_PSR0_DEPRECATIONS')) {
       
    29 		define('REQUESTS_SILENCE_PSR0_DEPRECATIONS', true);
       
    30 	}
       
    31 }
       
    32 
       
    33 require_once __DIR__ . '/Requests/src/Requests.php';
    11 
    34 
    12 /**
    35 /**
    13  * Requests for PHP
    36  * Requests for PHP
    14  *
    37  *
    15  * Inspired by Requests for Python.
    38  * Inspired by Requests for Python.
    16  *
    39  *
    17  * Based on concepts from SimplePie_File, RequestCore and WP_Http.
    40  * Based on concepts from SimplePie_File, RequestCore and WP_Http.
    18  *
    41  *
    19  * @package Requests
    42  * @package Requests
       
    43  *
       
    44  * @deprecated 6.2.0 Use `WpOrg\Requests\Requests` instead for the actual functionality and
       
    45  *                   use `WpOrg\Requests\Autoload` for the autoloading.
    20  */
    46  */
    21 class Requests {
    47 class Requests extends WpOrg\Requests\Requests {
    22 	/**
       
    23 	 * POST method
       
    24 	 *
       
    25 	 * @var string
       
    26 	 */
       
    27 	const POST = 'POST';
       
    28 
    48 
    29 	/**
    49 	/**
    30 	 * PUT method
    50 	 * Deprecated autoloader for Requests.
    31 	 *
    51 	 *
    32 	 * @var string
    52 	 * @deprecated 6.2.0 Use the `WpOrg\Requests\Autoload::load()` method instead.
    33 	 */
       
    34 	const PUT = 'PUT';
       
    35 
       
    36 	/**
       
    37 	 * GET method
       
    38 	 *
       
    39 	 * @var string
       
    40 	 */
       
    41 	const GET = 'GET';
       
    42 
       
    43 	/**
       
    44 	 * HEAD method
       
    45 	 *
       
    46 	 * @var string
       
    47 	 */
       
    48 	const HEAD = 'HEAD';
       
    49 
       
    50 	/**
       
    51 	 * DELETE method
       
    52 	 *
       
    53 	 * @var string
       
    54 	 */
       
    55 	const DELETE = 'DELETE';
       
    56 
       
    57 	/**
       
    58 	 * OPTIONS method
       
    59 	 *
       
    60 	 * @var string
       
    61 	 */
       
    62 	const OPTIONS = 'OPTIONS';
       
    63 
       
    64 	/**
       
    65 	 * TRACE method
       
    66 	 *
       
    67 	 * @var string
       
    68 	 */
       
    69 	const TRACE = 'TRACE';
       
    70 
       
    71 	/**
       
    72 	 * PATCH method
       
    73 	 *
       
    74 	 * @link https://tools.ietf.org/html/rfc5789
       
    75 	 * @var string
       
    76 	 */
       
    77 	const PATCH = 'PATCH';
       
    78 
       
    79 	/**
       
    80 	 * Default size of buffer size to read streams
       
    81 	 *
       
    82 	 * @var integer
       
    83 	 */
       
    84 	const BUFFER_SIZE = 1160;
       
    85 
       
    86 	/**
       
    87 	 * Current version of Requests
       
    88 	 *
       
    89 	 * @var string
       
    90 	 */
       
    91 	const VERSION = '1.8.1';
       
    92 
       
    93 	/**
       
    94 	 * Registered transport classes
       
    95 	 *
       
    96 	 * @var array
       
    97 	 */
       
    98 	protected static $transports = array();
       
    99 
       
   100 	/**
       
   101 	 * Selected transport name
       
   102 	 *
       
   103 	 * Use {@see get_transport()} instead
       
   104 	 *
       
   105 	 * @var array
       
   106 	 */
       
   107 	public static $transport = array();
       
   108 
       
   109 	/**
       
   110 	 * Default certificate path.
       
   111 	 *
       
   112 	 * @see Requests::get_certificate_path()
       
   113 	 * @see Requests::set_certificate_path()
       
   114 	 *
       
   115 	 * @var string
       
   116 	 */
       
   117 	protected static $certificate_path;
       
   118 
       
   119 	/**
       
   120 	 * This is a static class, do not instantiate it
       
   121 	 *
       
   122 	 * @codeCoverageIgnore
       
   123 	 */
       
   124 	private function __construct() {}
       
   125 
       
   126 	/**
       
   127 	 * Autoloader for Requests
       
   128 	 *
       
   129 	 * Register this with {@see register_autoloader()} if you'd like to avoid
       
   130 	 * having to create your own.
       
   131 	 *
       
   132 	 * (You can also use `spl_autoload_register` directly if you'd prefer.)
       
   133 	 *
    53 	 *
   134 	 * @codeCoverageIgnore
    54 	 * @codeCoverageIgnore
   135 	 *
    55 	 *
   136 	 * @param string $class Class name to load
    56 	 * @param string $class Class name to load
   137 	 */
    57 	 */
   138 	public static function autoloader($class) {
    58 	public static function autoloader($class) {
   139 		// Check that the class starts with "Requests"
    59 		if (class_exists('WpOrg\Requests\Autoload') === false) {
   140 		if (strpos($class, 'Requests') !== 0) {
    60 			require_once __DIR__ . '/Requests/src/Autoload.php';
   141 			return;
       
   142 		}
    61 		}
   143 
    62 
   144 		$file = str_replace('_', '/', $class);
    63 		return WpOrg\Requests\Autoload::load($class);
   145 		if (file_exists(dirname(__FILE__) . '/' . $file . '.php')) {
       
   146 			require_once dirname(__FILE__) . '/' . $file . '.php';
       
   147 		}
       
   148 	}
    64 	}
   149 
    65 
   150 	/**
    66 	/**
   151 	 * Register the built-in autoloader
    67 	 * Register the built-in autoloader
   152 	 *
    68 	 *
       
    69 	 * @deprecated 6.2.0 Include the `WpOrg\Requests\Autoload` class and
       
    70 	 *                   call `WpOrg\Requests\Autoload::register()` instead.
       
    71 	 *
   153 	 * @codeCoverageIgnore
    72 	 * @codeCoverageIgnore
   154 	 */
    73 	 */
   155 	public static function register_autoloader() {
    74 	public static function register_autoloader() {
   156 		spl_autoload_register(array('Requests', 'autoloader'));
    75 		require_once __DIR__ . '/Requests/src/Autoload.php';
   157 	}
    76 		WpOrg\Requests\Autoload::register();
   158 
       
   159 	/**
       
   160 	 * Register a transport
       
   161 	 *
       
   162 	 * @param string $transport Transport class to add, must support the Requests_Transport interface
       
   163 	 */
       
   164 	public static function add_transport($transport) {
       
   165 		if (empty(self::$transports)) {
       
   166 			self::$transports = array(
       
   167 				'Requests_Transport_cURL',
       
   168 				'Requests_Transport_fsockopen',
       
   169 			);
       
   170 		}
       
   171 
       
   172 		self::$transports = array_merge(self::$transports, array($transport));
       
   173 	}
       
   174 
       
   175 	/**
       
   176 	 * Get a working transport
       
   177 	 *
       
   178 	 * @throws Requests_Exception If no valid transport is found (`notransport`)
       
   179 	 * @return Requests_Transport
       
   180 	 */
       
   181 	protected static function get_transport($capabilities = array()) {
       
   182 		// Caching code, don't bother testing coverage
       
   183 		// @codeCoverageIgnoreStart
       
   184 		// array of capabilities as a string to be used as an array key
       
   185 		ksort($capabilities);
       
   186 		$cap_string = serialize($capabilities);
       
   187 
       
   188 		// Don't search for a transport if it's already been done for these $capabilities
       
   189 		if (isset(self::$transport[$cap_string]) && self::$transport[$cap_string] !== null) {
       
   190 			$class = self::$transport[$cap_string];
       
   191 			return new $class();
       
   192 		}
       
   193 		// @codeCoverageIgnoreEnd
       
   194 
       
   195 		if (empty(self::$transports)) {
       
   196 			self::$transports = array(
       
   197 				'Requests_Transport_cURL',
       
   198 				'Requests_Transport_fsockopen',
       
   199 			);
       
   200 		}
       
   201 
       
   202 		// Find us a working transport
       
   203 		foreach (self::$transports as $class) {
       
   204 			if (!class_exists($class)) {
       
   205 				continue;
       
   206 			}
       
   207 
       
   208 			$result = call_user_func(array($class, 'test'), $capabilities);
       
   209 			if ($result) {
       
   210 				self::$transport[$cap_string] = $class;
       
   211 				break;
       
   212 			}
       
   213 		}
       
   214 		if (self::$transport[$cap_string] === null) {
       
   215 			throw new Requests_Exception('No working transports found', 'notransport', self::$transports);
       
   216 		}
       
   217 
       
   218 		$class = self::$transport[$cap_string];
       
   219 		return new $class();
       
   220 	}
       
   221 
       
   222 	/**#@+
       
   223 	 * @see request()
       
   224 	 * @param string $url
       
   225 	 * @param array $headers
       
   226 	 * @param array $options
       
   227 	 * @return Requests_Response
       
   228 	 */
       
   229 	/**
       
   230 	 * Send a GET request
       
   231 	 */
       
   232 	public static function get($url, $headers = array(), $options = array()) {
       
   233 		return self::request($url, $headers, null, self::GET, $options);
       
   234 	}
       
   235 
       
   236 	/**
       
   237 	 * Send a HEAD request
       
   238 	 */
       
   239 	public static function head($url, $headers = array(), $options = array()) {
       
   240 		return self::request($url, $headers, null, self::HEAD, $options);
       
   241 	}
       
   242 
       
   243 	/**
       
   244 	 * Send a DELETE request
       
   245 	 */
       
   246 	public static function delete($url, $headers = array(), $options = array()) {
       
   247 		return self::request($url, $headers, null, self::DELETE, $options);
       
   248 	}
       
   249 
       
   250 	/**
       
   251 	 * Send a TRACE request
       
   252 	 */
       
   253 	public static function trace($url, $headers = array(), $options = array()) {
       
   254 		return self::request($url, $headers, null, self::TRACE, $options);
       
   255 	}
       
   256 	/**#@-*/
       
   257 
       
   258 	/**#@+
       
   259 	 * @see request()
       
   260 	 * @param string $url
       
   261 	 * @param array $headers
       
   262 	 * @param array $data
       
   263 	 * @param array $options
       
   264 	 * @return Requests_Response
       
   265 	 */
       
   266 	/**
       
   267 	 * Send a POST request
       
   268 	 */
       
   269 	public static function post($url, $headers = array(), $data = array(), $options = array()) {
       
   270 		return self::request($url, $headers, $data, self::POST, $options);
       
   271 	}
       
   272 	/**
       
   273 	 * Send a PUT request
       
   274 	 */
       
   275 	public static function put($url, $headers = array(), $data = array(), $options = array()) {
       
   276 		return self::request($url, $headers, $data, self::PUT, $options);
       
   277 	}
       
   278 
       
   279 	/**
       
   280 	 * Send an OPTIONS request
       
   281 	 */
       
   282 	public static function options($url, $headers = array(), $data = array(), $options = array()) {
       
   283 		return self::request($url, $headers, $data, self::OPTIONS, $options);
       
   284 	}
       
   285 
       
   286 	/**
       
   287 	 * Send a PATCH request
       
   288 	 *
       
   289 	 * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the
       
   290 	 * specification recommends that should send an ETag
       
   291 	 *
       
   292 	 * @link https://tools.ietf.org/html/rfc5789
       
   293 	 */
       
   294 	public static function patch($url, $headers, $data = array(), $options = array()) {
       
   295 		return self::request($url, $headers, $data, self::PATCH, $options);
       
   296 	}
       
   297 	/**#@-*/
       
   298 
       
   299 	/**
       
   300 	 * Main interface for HTTP requests
       
   301 	 *
       
   302 	 * This method initiates a request and sends it via a transport before
       
   303 	 * parsing.
       
   304 	 *
       
   305 	 * The `$options` parameter takes an associative array with the following
       
   306 	 * options:
       
   307 	 *
       
   308 	 * - `timeout`: How long should we wait for a response?
       
   309 	 *    Note: for cURL, a minimum of 1 second applies, as DNS resolution
       
   310 	 *    operates at second-resolution only.
       
   311 	 *    (float, seconds with a millisecond precision, default: 10, example: 0.01)
       
   312 	 * - `connect_timeout`: How long should we wait while trying to connect?
       
   313 	 *    (float, seconds with a millisecond precision, default: 10, example: 0.01)
       
   314 	 * - `useragent`: Useragent to send to the server
       
   315 	 *    (string, default: php-requests/$version)
       
   316 	 * - `follow_redirects`: Should we follow 3xx redirects?
       
   317 	 *    (boolean, default: true)
       
   318 	 * - `redirects`: How many times should we redirect before erroring?
       
   319 	 *    (integer, default: 10)
       
   320 	 * - `blocking`: Should we block processing on this request?
       
   321 	 *    (boolean, default: true)
       
   322 	 * - `filename`: File to stream the body to instead.
       
   323 	 *    (string|boolean, default: false)
       
   324 	 * - `auth`: Authentication handler or array of user/password details to use
       
   325 	 *    for Basic authentication
       
   326 	 *    (Requests_Auth|array|boolean, default: false)
       
   327 	 * - `proxy`: Proxy details to use for proxy by-passing and authentication
       
   328 	 *    (Requests_Proxy|array|string|boolean, default: false)
       
   329 	 * - `max_bytes`: Limit for the response body size.
       
   330 	 *    (integer|boolean, default: false)
       
   331 	 * - `idn`: Enable IDN parsing
       
   332 	 *    (boolean, default: true)
       
   333 	 * - `transport`: Custom transport. Either a class name, or a
       
   334 	 *    transport object. Defaults to the first working transport from
       
   335 	 *    {@see getTransport()}
       
   336 	 *    (string|Requests_Transport, default: {@see getTransport()})
       
   337 	 * - `hooks`: Hooks handler.
       
   338 	 *    (Requests_Hooker, default: new Requests_Hooks())
       
   339 	 * - `verify`: Should we verify SSL certificates? Allows passing in a custom
       
   340 	 *    certificate file as a string. (Using true uses the system-wide root
       
   341 	 *    certificate store instead, but this may have different behaviour
       
   342 	 *    across transports.)
       
   343 	 *    (string|boolean, default: library/Requests/Transport/cacert.pem)
       
   344 	 * - `verifyname`: Should we verify the common name in the SSL certificate?
       
   345 	 *    (boolean, default: true)
       
   346 	 * - `data_format`: How should we send the `$data` parameter?
       
   347 	 *    (string, one of 'query' or 'body', default: 'query' for
       
   348 	 *    HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
       
   349 	 *
       
   350 	 * @throws Requests_Exception On invalid URLs (`nonhttp`)
       
   351 	 *
       
   352 	 * @param string $url URL to request
       
   353 	 * @param array $headers Extra headers to send with the request
       
   354 	 * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
       
   355 	 * @param string $type HTTP request type (use Requests constants)
       
   356 	 * @param array $options Options for the request (see description for more information)
       
   357 	 * @return Requests_Response
       
   358 	 */
       
   359 	public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array()) {
       
   360 		if (empty($options['type'])) {
       
   361 			$options['type'] = $type;
       
   362 		}
       
   363 		$options = array_merge(self::get_default_options(), $options);
       
   364 
       
   365 		self::set_defaults($url, $headers, $data, $type, $options);
       
   366 
       
   367 		$options['hooks']->dispatch('requests.before_request', array(&$url, &$headers, &$data, &$type, &$options));
       
   368 
       
   369 		if (!empty($options['transport'])) {
       
   370 			$transport = $options['transport'];
       
   371 
       
   372 			if (is_string($options['transport'])) {
       
   373 				$transport = new $transport();
       
   374 			}
       
   375 		}
       
   376 		else {
       
   377 			$need_ssl     = (stripos($url, 'https://') === 0);
       
   378 			$capabilities = array('ssl' => $need_ssl);
       
   379 			$transport    = self::get_transport($capabilities);
       
   380 		}
       
   381 		$response = $transport->request($url, $headers, $data, $options);
       
   382 
       
   383 		$options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options));
       
   384 
       
   385 		return self::parse_response($response, $url, $headers, $data, $options);
       
   386 	}
       
   387 
       
   388 	/**
       
   389 	 * Send multiple HTTP requests simultaneously
       
   390 	 *
       
   391 	 * The `$requests` parameter takes an associative or indexed array of
       
   392 	 * request fields. The key of each request can be used to match up the
       
   393 	 * request with the returned data, or with the request passed into your
       
   394 	 * `multiple.request.complete` callback.
       
   395 	 *
       
   396 	 * The request fields value is an associative array with the following keys:
       
   397 	 *
       
   398 	 * - `url`: Request URL Same as the `$url` parameter to
       
   399 	 *    {@see Requests::request}
       
   400 	 *    (string, required)
       
   401 	 * - `headers`: Associative array of header fields. Same as the `$headers`
       
   402 	 *    parameter to {@see Requests::request}
       
   403 	 *    (array, default: `array()`)
       
   404 	 * - `data`: Associative array of data fields or a string. Same as the
       
   405 	 *    `$data` parameter to {@see Requests::request}
       
   406 	 *    (array|string, default: `array()`)
       
   407 	 * - `type`: HTTP request type (use Requests constants). Same as the `$type`
       
   408 	 *    parameter to {@see Requests::request}
       
   409 	 *    (string, default: `Requests::GET`)
       
   410 	 * - `cookies`: Associative array of cookie name to value, or cookie jar.
       
   411 	 *    (array|Requests_Cookie_Jar)
       
   412 	 *
       
   413 	 * If the `$options` parameter is specified, individual requests will
       
   414 	 * inherit options from it. This can be used to use a single hooking system,
       
   415 	 * or set all the types to `Requests::POST`, for example.
       
   416 	 *
       
   417 	 * In addition, the `$options` parameter takes the following global options:
       
   418 	 *
       
   419 	 * - `complete`: A callback for when a request is complete. Takes two
       
   420 	 *    parameters, a Requests_Response/Requests_Exception reference, and the
       
   421 	 *    ID from the request array (Note: this can also be overridden on a
       
   422 	 *    per-request basis, although that's a little silly)
       
   423 	 *    (callback)
       
   424 	 *
       
   425 	 * @param array $requests Requests data (see description for more information)
       
   426 	 * @param array $options Global and default options (see {@see Requests::request})
       
   427 	 * @return array Responses (either Requests_Response or a Requests_Exception object)
       
   428 	 */
       
   429 	public static function request_multiple($requests, $options = array()) {
       
   430 		$options = array_merge(self::get_default_options(true), $options);
       
   431 
       
   432 		if (!empty($options['hooks'])) {
       
   433 			$options['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
       
   434 			if (!empty($options['complete'])) {
       
   435 				$options['hooks']->register('multiple.request.complete', $options['complete']);
       
   436 			}
       
   437 		}
       
   438 
       
   439 		foreach ($requests as $id => &$request) {
       
   440 			if (!isset($request['headers'])) {
       
   441 				$request['headers'] = array();
       
   442 			}
       
   443 			if (!isset($request['data'])) {
       
   444 				$request['data'] = array();
       
   445 			}
       
   446 			if (!isset($request['type'])) {
       
   447 				$request['type'] = self::GET;
       
   448 			}
       
   449 			if (!isset($request['options'])) {
       
   450 				$request['options']         = $options;
       
   451 				$request['options']['type'] = $request['type'];
       
   452 			}
       
   453 			else {
       
   454 				if (empty($request['options']['type'])) {
       
   455 					$request['options']['type'] = $request['type'];
       
   456 				}
       
   457 				$request['options'] = array_merge($options, $request['options']);
       
   458 			}
       
   459 
       
   460 			self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']);
       
   461 
       
   462 			// Ensure we only hook in once
       
   463 			if ($request['options']['hooks'] !== $options['hooks']) {
       
   464 				$request['options']['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
       
   465 				if (!empty($request['options']['complete'])) {
       
   466 					$request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']);
       
   467 				}
       
   468 			}
       
   469 		}
       
   470 		unset($request);
       
   471 
       
   472 		if (!empty($options['transport'])) {
       
   473 			$transport = $options['transport'];
       
   474 
       
   475 			if (is_string($options['transport'])) {
       
   476 				$transport = new $transport();
       
   477 			}
       
   478 		}
       
   479 		else {
       
   480 			$transport = self::get_transport();
       
   481 		}
       
   482 		$responses = $transport->request_multiple($requests, $options);
       
   483 
       
   484 		foreach ($responses as $id => &$response) {
       
   485 			// If our hook got messed with somehow, ensure we end up with the
       
   486 			// correct response
       
   487 			if (is_string($response)) {
       
   488 				$request = $requests[$id];
       
   489 				self::parse_multiple($response, $request);
       
   490 				$request['options']['hooks']->dispatch('multiple.request.complete', array(&$response, $id));
       
   491 			}
       
   492 		}
       
   493 
       
   494 		return $responses;
       
   495 	}
       
   496 
       
   497 	/**
       
   498 	 * Get the default options
       
   499 	 *
       
   500 	 * @see Requests::request() for values returned by this method
       
   501 	 * @param boolean $multirequest Is this a multirequest?
       
   502 	 * @return array Default option values
       
   503 	 */
       
   504 	protected static function get_default_options($multirequest = false) {
       
   505 		$defaults = array(
       
   506 			'timeout'          => 10,
       
   507 			'connect_timeout'  => 10,
       
   508 			'useragent'        => 'php-requests/' . self::VERSION,
       
   509 			'protocol_version' => 1.1,
       
   510 			'redirected'       => 0,
       
   511 			'redirects'        => 10,
       
   512 			'follow_redirects' => true,
       
   513 			'blocking'         => true,
       
   514 			'type'             => self::GET,
       
   515 			'filename'         => false,
       
   516 			'auth'             => false,
       
   517 			'proxy'            => false,
       
   518 			'cookies'          => false,
       
   519 			'max_bytes'        => false,
       
   520 			'idn'              => true,
       
   521 			'hooks'            => null,
       
   522 			'transport'        => null,
       
   523 			'verify'           => self::get_certificate_path(),
       
   524 			'verifyname'       => true,
       
   525 		);
       
   526 		if ($multirequest !== false) {
       
   527 			$defaults['complete'] = null;
       
   528 		}
       
   529 		return $defaults;
       
   530 	}
       
   531 
       
   532 	/**
       
   533 	 * Get default certificate path.
       
   534 	 *
       
   535 	 * @return string Default certificate path.
       
   536 	 */
       
   537 	public static function get_certificate_path() {
       
   538 		if (!empty(self::$certificate_path)) {
       
   539 			return self::$certificate_path;
       
   540 		}
       
   541 
       
   542 		return dirname(__FILE__) . '/Requests/Transport/cacert.pem';
       
   543 	}
       
   544 
       
   545 	/**
       
   546 	 * Set default certificate path.
       
   547 	 *
       
   548 	 * @param string $path Certificate path, pointing to a PEM file.
       
   549 	 */
       
   550 	public static function set_certificate_path($path) {
       
   551 		self::$certificate_path = $path;
       
   552 	}
       
   553 
       
   554 	/**
       
   555 	 * Set the default values
       
   556 	 *
       
   557 	 * @param string $url URL to request
       
   558 	 * @param array $headers Extra headers to send with the request
       
   559 	 * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
       
   560 	 * @param string $type HTTP request type
       
   561 	 * @param array $options Options for the request
       
   562 	 * @return array $options
       
   563 	 */
       
   564 	protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) {
       
   565 		if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) {
       
   566 			throw new Requests_Exception('Only HTTP(S) requests are handled.', 'nonhttp', $url);
       
   567 		}
       
   568 
       
   569 		if (empty($options['hooks'])) {
       
   570 			$options['hooks'] = new Requests_Hooks();
       
   571 		}
       
   572 
       
   573 		if (is_array($options['auth'])) {
       
   574 			$options['auth'] = new Requests_Auth_Basic($options['auth']);
       
   575 		}
       
   576 		if ($options['auth'] !== false) {
       
   577 			$options['auth']->register($options['hooks']);
       
   578 		}
       
   579 
       
   580 		if (is_string($options['proxy']) || is_array($options['proxy'])) {
       
   581 			$options['proxy'] = new Requests_Proxy_HTTP($options['proxy']);
       
   582 		}
       
   583 		if ($options['proxy'] !== false) {
       
   584 			$options['proxy']->register($options['hooks']);
       
   585 		}
       
   586 
       
   587 		if (is_array($options['cookies'])) {
       
   588 			$options['cookies'] = new Requests_Cookie_Jar($options['cookies']);
       
   589 		}
       
   590 		elseif (empty($options['cookies'])) {
       
   591 			$options['cookies'] = new Requests_Cookie_Jar();
       
   592 		}
       
   593 		if ($options['cookies'] !== false) {
       
   594 			$options['cookies']->register($options['hooks']);
       
   595 		}
       
   596 
       
   597 		if ($options['idn'] !== false) {
       
   598 			$iri       = new Requests_IRI($url);
       
   599 			$iri->host = Requests_IDNAEncoder::encode($iri->ihost);
       
   600 			$url       = $iri->uri;
       
   601 		}
       
   602 
       
   603 		// Massage the type to ensure we support it.
       
   604 		$type = strtoupper($type);
       
   605 
       
   606 		if (!isset($options['data_format'])) {
       
   607 			if (in_array($type, array(self::HEAD, self::GET, self::DELETE), true)) {
       
   608 				$options['data_format'] = 'query';
       
   609 			}
       
   610 			else {
       
   611 				$options['data_format'] = 'body';
       
   612 			}
       
   613 		}
       
   614 	}
       
   615 
       
   616 	/**
       
   617 	 * HTTP response parser
       
   618 	 *
       
   619 	 * @throws Requests_Exception On missing head/body separator (`requests.no_crlf_separator`)
       
   620 	 * @throws Requests_Exception On missing head/body separator (`noversion`)
       
   621 	 * @throws Requests_Exception On missing head/body separator (`toomanyredirects`)
       
   622 	 *
       
   623 	 * @param string $headers Full response text including headers and body
       
   624 	 * @param string $url Original request URL
       
   625 	 * @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects
       
   626 	 * @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects
       
   627 	 * @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects
       
   628 	 * @return Requests_Response
       
   629 	 */
       
   630 	protected static function parse_response($headers, $url, $req_headers, $req_data, $options) {
       
   631 		$return = new Requests_Response();
       
   632 		if (!$options['blocking']) {
       
   633 			return $return;
       
   634 		}
       
   635 
       
   636 		$return->raw  = $headers;
       
   637 		$return->url  = (string) $url;
       
   638 		$return->body = '';
       
   639 
       
   640 		if (!$options['filename']) {
       
   641 			$pos = strpos($headers, "\r\n\r\n");
       
   642 			if ($pos === false) {
       
   643 				// Crap!
       
   644 				throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator');
       
   645 			}
       
   646 
       
   647 			$headers = substr($return->raw, 0, $pos);
       
   648 			// Headers will always be separated from the body by two new lines - `\n\r\n\r`.
       
   649 			$body = substr($return->raw, $pos + 4);
       
   650 			if (!empty($body)) {
       
   651 				$return->body = $body;
       
   652 			}
       
   653 		}
       
   654 		// Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
       
   655 		$headers = str_replace("\r\n", "\n", $headers);
       
   656 		// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
       
   657 		$headers = preg_replace('/\n[ \t]/', ' ', $headers);
       
   658 		$headers = explode("\n", $headers);
       
   659 		preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
       
   660 		if (empty($matches)) {
       
   661 			throw new Requests_Exception('Response could not be parsed', 'noversion', $headers);
       
   662 		}
       
   663 		$return->protocol_version = (float) $matches[1];
       
   664 		$return->status_code      = (int) $matches[2];
       
   665 		if ($return->status_code >= 200 && $return->status_code < 300) {
       
   666 			$return->success = true;
       
   667 		}
       
   668 
       
   669 		foreach ($headers as $header) {
       
   670 			list($key, $value) = explode(':', $header, 2);
       
   671 			$value             = trim($value);
       
   672 			preg_replace('#(\s+)#i', ' ', $value);
       
   673 			$return->headers[$key] = $value;
       
   674 		}
       
   675 		if (isset($return->headers['transfer-encoding'])) {
       
   676 			$return->body = self::decode_chunked($return->body);
       
   677 			unset($return->headers['transfer-encoding']);
       
   678 		}
       
   679 		if (isset($return->headers['content-encoding'])) {
       
   680 			$return->body = self::decompress($return->body);
       
   681 		}
       
   682 
       
   683 		//fsockopen and cURL compatibility
       
   684 		if (isset($return->headers['connection'])) {
       
   685 			unset($return->headers['connection']);
       
   686 		}
       
   687 
       
   688 		$options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options));
       
   689 
       
   690 		if ($return->is_redirect() && $options['follow_redirects'] === true) {
       
   691 			if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
       
   692 				if ($return->status_code === 303) {
       
   693 					$options['type'] = self::GET;
       
   694 				}
       
   695 				$options['redirected']++;
       
   696 				$location = $return->headers['location'];
       
   697 				if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) {
       
   698 					// relative redirect, for compatibility make it absolute
       
   699 					$location = Requests_IRI::absolutize($url, $location);
       
   700 					$location = $location->uri;
       
   701 				}
       
   702 
       
   703 				$hook_args = array(
       
   704 					&$location,
       
   705 					&$req_headers,
       
   706 					&$req_data,
       
   707 					&$options,
       
   708 					$return,
       
   709 				);
       
   710 				$options['hooks']->dispatch('requests.before_redirect', $hook_args);
       
   711 				$redirected            = self::request($location, $req_headers, $req_data, $options['type'], $options);
       
   712 				$redirected->history[] = $return;
       
   713 				return $redirected;
       
   714 			}
       
   715 			elseif ($options['redirected'] >= $options['redirects']) {
       
   716 				throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return);
       
   717 			}
       
   718 		}
       
   719 
       
   720 		$return->redirects = $options['redirected'];
       
   721 
       
   722 		$options['hooks']->dispatch('requests.after_request', array(&$return, $req_headers, $req_data, $options));
       
   723 		return $return;
       
   724 	}
       
   725 
       
   726 	/**
       
   727 	 * Callback for `transport.internal.parse_response`
       
   728 	 *
       
   729 	 * Internal use only. Converts a raw HTTP response to a Requests_Response
       
   730 	 * while still executing a multiple request.
       
   731 	 *
       
   732 	 * @param string $response Full response text including headers and body (will be overwritten with Response instance)
       
   733 	 * @param array $request Request data as passed into {@see Requests::request_multiple()}
       
   734 	 * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object
       
   735 	 */
       
   736 	public static function parse_multiple(&$response, $request) {
       
   737 		try {
       
   738 			$url      = $request['url'];
       
   739 			$headers  = $request['headers'];
       
   740 			$data     = $request['data'];
       
   741 			$options  = $request['options'];
       
   742 			$response = self::parse_response($response, $url, $headers, $data, $options);
       
   743 		}
       
   744 		catch (Requests_Exception $e) {
       
   745 			$response = $e;
       
   746 		}
       
   747 	}
       
   748 
       
   749 	/**
       
   750 	 * Decoded a chunked body as per RFC 2616
       
   751 	 *
       
   752 	 * @see https://tools.ietf.org/html/rfc2616#section-3.6.1
       
   753 	 * @param string $data Chunked body
       
   754 	 * @return string Decoded body
       
   755 	 */
       
   756 	protected static function decode_chunked($data) {
       
   757 		if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
       
   758 			return $data;
       
   759 		}
       
   760 
       
   761 		$decoded = '';
       
   762 		$encoded = $data;
       
   763 
       
   764 		while (true) {
       
   765 			$is_chunked = (bool) preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', $encoded, $matches);
       
   766 			if (!$is_chunked) {
       
   767 				// Looks like it's not chunked after all
       
   768 				return $data;
       
   769 			}
       
   770 
       
   771 			$length = hexdec(trim($matches[1]));
       
   772 			if ($length === 0) {
       
   773 				// Ignore trailer headers
       
   774 				return $decoded;
       
   775 			}
       
   776 
       
   777 			$chunk_length = strlen($matches[0]);
       
   778 			$decoded     .= substr($encoded, $chunk_length, $length);
       
   779 			$encoded      = substr($encoded, $chunk_length + $length + 2);
       
   780 
       
   781 			if (trim($encoded) === '0' || empty($encoded)) {
       
   782 				return $decoded;
       
   783 			}
       
   784 		}
       
   785 
       
   786 		// We'll never actually get down here
       
   787 		// @codeCoverageIgnoreStart
       
   788 	}
       
   789 	// @codeCoverageIgnoreEnd
       
   790 
       
   791 	/**
       
   792 	 * Convert a key => value array to a 'key: value' array for headers
       
   793 	 *
       
   794 	 * @param array $array Dictionary of header values
       
   795 	 * @return array List of headers
       
   796 	 */
       
   797 	public static function flatten($array) {
       
   798 		$return = array();
       
   799 		foreach ($array as $key => $value) {
       
   800 			$return[] = sprintf('%s: %s', $key, $value);
       
   801 		}
       
   802 		return $return;
       
   803 	}
       
   804 
       
   805 	/**
       
   806 	 * Convert a key => value array to a 'key: value' array for headers
       
   807 	 *
       
   808 	 * @codeCoverageIgnore
       
   809 	 * @deprecated Misspelling of {@see Requests::flatten}
       
   810 	 * @param array $array Dictionary of header values
       
   811 	 * @return array List of headers
       
   812 	 */
       
   813 	public static function flattern($array) {
       
   814 		return self::flatten($array);
       
   815 	}
       
   816 
       
   817 	/**
       
   818 	 * Decompress an encoded body
       
   819 	 *
       
   820 	 * Implements gzip, compress and deflate. Guesses which it is by attempting
       
   821 	 * to decode.
       
   822 	 *
       
   823 	 * @param string $data Compressed data in one of the above formats
       
   824 	 * @return string Decompressed string
       
   825 	 */
       
   826 	public static function decompress($data) {
       
   827 		if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") {
       
   828 			// Not actually compressed. Probably cURL ruining this for us.
       
   829 			return $data;
       
   830 		}
       
   831 
       
   832 		if (function_exists('gzdecode')) {
       
   833 			// phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.gzdecodeFound -- Wrapped in function_exists() for PHP 5.2.
       
   834 			$decoded = @gzdecode($data);
       
   835 			if ($decoded !== false) {
       
   836 				return $decoded;
       
   837 			}
       
   838 		}
       
   839 
       
   840 		if (function_exists('gzinflate')) {
       
   841 			$decoded = @gzinflate($data);
       
   842 			if ($decoded !== false) {
       
   843 				return $decoded;
       
   844 			}
       
   845 		}
       
   846 
       
   847 		$decoded = self::compatible_gzinflate($data);
       
   848 		if ($decoded !== false) {
       
   849 			return $decoded;
       
   850 		}
       
   851 
       
   852 		if (function_exists('gzuncompress')) {
       
   853 			$decoded = @gzuncompress($data);
       
   854 			if ($decoded !== false) {
       
   855 				return $decoded;
       
   856 			}
       
   857 		}
       
   858 
       
   859 		return $data;
       
   860 	}
       
   861 
       
   862 	/**
       
   863 	 * Decompression of deflated string while staying compatible with the majority of servers.
       
   864 	 *
       
   865 	 * Certain Servers will return deflated data with headers which PHP's gzinflate()
       
   866 	 * function cannot handle out of the box. The following function has been created from
       
   867 	 * various snippets on the gzinflate() PHP documentation.
       
   868 	 *
       
   869 	 * Warning: Magic numbers within. Due to the potential different formats that the compressed
       
   870 	 * data may be returned in, some "magic offsets" are needed to ensure proper decompression
       
   871 	 * takes place. For a simple progmatic way to determine the magic offset in use, see:
       
   872 	 * https://core.trac.wordpress.org/ticket/18273
       
   873 	 *
       
   874 	 * @since 2.8.1
       
   875 	 * @link https://core.trac.wordpress.org/ticket/18273
       
   876 	 * @link https://secure.php.net/manual/en/function.gzinflate.php#70875
       
   877 	 * @link https://secure.php.net/manual/en/function.gzinflate.php#77336
       
   878 	 *
       
   879 	 * @param string $gz_data String to decompress.
       
   880 	 * @return string|bool False on failure.
       
   881 	 */
       
   882 	public static function compatible_gzinflate($gz_data) {
       
   883 		// Compressed data might contain a full zlib header, if so strip it for
       
   884 		// gzinflate()
       
   885 		if (substr($gz_data, 0, 3) === "\x1f\x8b\x08") {
       
   886 			$i   = 10;
       
   887 			$flg = ord(substr($gz_data, 3, 1));
       
   888 			if ($flg > 0) {
       
   889 				if ($flg & 4) {
       
   890 					list($xlen) = unpack('v', substr($gz_data, $i, 2));
       
   891 					$i         += 2 + $xlen;
       
   892 				}
       
   893 				if ($flg & 8) {
       
   894 					$i = strpos($gz_data, "\0", $i) + 1;
       
   895 				}
       
   896 				if ($flg & 16) {
       
   897 					$i = strpos($gz_data, "\0", $i) + 1;
       
   898 				}
       
   899 				if ($flg & 2) {
       
   900 					$i += 2;
       
   901 				}
       
   902 			}
       
   903 			$decompressed = self::compatible_gzinflate(substr($gz_data, $i));
       
   904 			if ($decompressed !== false) {
       
   905 				return $decompressed;
       
   906 			}
       
   907 		}
       
   908 
       
   909 		// If the data is Huffman Encoded, we must first strip the leading 2
       
   910 		// byte Huffman marker for gzinflate()
       
   911 		// The response is Huffman coded by many compressors such as
       
   912 		// java.util.zip.Deflater, Ruby’s Zlib::Deflate, and .NET's
       
   913 		// System.IO.Compression.DeflateStream.
       
   914 		//
       
   915 		// See https://decompres.blogspot.com/ for a quick explanation of this
       
   916 		// data type
       
   917 		$huffman_encoded = false;
       
   918 
       
   919 		// low nibble of first byte should be 0x08
       
   920 		list(, $first_nibble) = unpack('h', $gz_data);
       
   921 
       
   922 		// First 2 bytes should be divisible by 0x1F
       
   923 		list(, $first_two_bytes) = unpack('n', $gz_data);
       
   924 
       
   925 		if ($first_nibble === 0x08 && ($first_two_bytes % 0x1F) === 0) {
       
   926 			$huffman_encoded = true;
       
   927 		}
       
   928 
       
   929 		if ($huffman_encoded) {
       
   930 			$decompressed = @gzinflate(substr($gz_data, 2));
       
   931 			if ($decompressed !== false) {
       
   932 				return $decompressed;
       
   933 			}
       
   934 		}
       
   935 
       
   936 		if (substr($gz_data, 0, 4) === "\x50\x4b\x03\x04") {
       
   937 			// ZIP file format header
       
   938 			// Offset 6: 2 bytes, General-purpose field
       
   939 			// Offset 26: 2 bytes, filename length
       
   940 			// Offset 28: 2 bytes, optional field length
       
   941 			// Offset 30: Filename field, followed by optional field, followed
       
   942 			// immediately by data
       
   943 			list(, $general_purpose_flag) = unpack('v', substr($gz_data, 6, 2));
       
   944 
       
   945 			// If the file has been compressed on the fly, 0x08 bit is set of
       
   946 			// the general purpose field. We can use this to differentiate
       
   947 			// between a compressed document, and a ZIP file
       
   948 			$zip_compressed_on_the_fly = ((0x08 & $general_purpose_flag) === 0x08);
       
   949 
       
   950 			if (!$zip_compressed_on_the_fly) {
       
   951 				// Don't attempt to decode a compressed zip file
       
   952 				return $gz_data;
       
   953 			}
       
   954 
       
   955 			// Determine the first byte of data, based on the above ZIP header
       
   956 			// offsets:
       
   957 			$first_file_start = array_sum(unpack('v2', substr($gz_data, 26, 4)));
       
   958 			$decompressed     = @gzinflate(substr($gz_data, 30 + $first_file_start));
       
   959 			if ($decompressed !== false) {
       
   960 				return $decompressed;
       
   961 			}
       
   962 			return false;
       
   963 		}
       
   964 
       
   965 		// Finally fall back to straight gzinflate
       
   966 		$decompressed = @gzinflate($gz_data);
       
   967 		if ($decompressed !== false) {
       
   968 			return $decompressed;
       
   969 		}
       
   970 
       
   971 		// Fallback for all above failing, not expected, but included for
       
   972 		// debugging and preventing regressions and to track stats
       
   973 		$decompressed = @gzinflate(substr($gz_data, 2));
       
   974 		if ($decompressed !== false) {
       
   975 			return $decompressed;
       
   976 		}
       
   977 
       
   978 		return false;
       
   979 	}
       
   980 
       
   981 	public static function match_domain($host, $reference) {
       
   982 		// Check for a direct match
       
   983 		if ($host === $reference) {
       
   984 			return true;
       
   985 		}
       
   986 
       
   987 		// Calculate the valid wildcard match if the host is not an IP address
       
   988 		// Also validates that the host has 3 parts or more, as per Firefox's
       
   989 		// ruleset.
       
   990 		$parts = explode('.', $host);
       
   991 		if (ip2long($host) === false && count($parts) >= 3) {
       
   992 			$parts[0] = '*';
       
   993 			$wildcard = implode('.', $parts);
       
   994 			if ($wildcard === $reference) {
       
   995 				return true;
       
   996 			}
       
   997 		}
       
   998 
       
   999 		return false;
       
  1000 	}
    77 	}
  1001 }
    78 }