Requests

Requests for PHP.

Defined (1)

The class is defined in the following location(s).

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