PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wordpress-3.4.2/wp-content/plugins/w3-total-cache/lib/SNS/lib/requestcore/requestcore.class.php

https://bitbucket.org/emallove/bedforddavis.com
PHP | 1029 lines | 484 code | 154 blank | 391 comment | 67 complexity | 79ced8dd63c528888e50cd3342255775 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0, BSD-3-Clause, GPL-3.0, AGPL-1.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Handles all HTTP requests using cURL and manages the responses.
  4. *
  5. * @version 2011.06.07
  6. * @copyright 2006-2011 Ryan Parman
  7. * @copyright 2006-2010 Foleeo Inc.
  8. * @copyright 2010-2011 Amazon.com, Inc. or its affiliates.
  9. * @copyright 2008-2011 Contributors
  10. * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License
  11. */
  12. class RequestCore
  13. {
  14. /**
  15. * The URL being requested.
  16. */
  17. public $request_url;
  18. /**
  19. * The headers being sent in the request.
  20. */
  21. public $request_headers;
  22. /**
  23. * The body being sent in the request.
  24. */
  25. public $request_body;
  26. /**
  27. * The response returned by the request.
  28. */
  29. public $response;
  30. /**
  31. * The headers returned by the request.
  32. */
  33. public $response_headers;
  34. /**
  35. * The body returned by the request.
  36. */
  37. public $response_body;
  38. /**
  39. * The HTTP status code returned by the request.
  40. */
  41. public $response_code;
  42. /**
  43. * Additional response data.
  44. */
  45. public $response_info;
  46. /**
  47. * The handle for the cURL object.
  48. */
  49. public $curl_handle;
  50. /**
  51. * The method by which the request is being made.
  52. */
  53. public $method;
  54. /**
  55. * Stores the proxy settings to use for the request.
  56. */
  57. public $proxy = null;
  58. /**
  59. * The username to use for the request.
  60. */
  61. public $username = null;
  62. /**
  63. * The password to use for the request.
  64. */
  65. public $password = null;
  66. /**
  67. * Custom CURLOPT settings.
  68. */
  69. public $curlopts = null;
  70. /**
  71. * The state of debug mode.
  72. */
  73. public $debug_mode = false;
  74. /**
  75. * The default class to use for HTTP Requests (defaults to <RequestCore>).
  76. */
  77. public $request_class = 'RequestCore';
  78. /**
  79. * The default class to use for HTTP Responses (defaults to <ResponseCore>).
  80. */
  81. public $response_class = 'ResponseCore';
  82. /**
  83. * Default useragent string to use.
  84. */
  85. public $useragent = 'RequestCore/1.4.3';
  86. /**
  87. * File to read from while streaming up.
  88. */
  89. public $read_file = null;
  90. /**
  91. * The resource to read from while streaming up.
  92. */
  93. public $read_stream = null;
  94. /**
  95. * The size of the stream to read from.
  96. */
  97. public $read_stream_size = null;
  98. /**
  99. * The length already read from the stream.
  100. */
  101. public $read_stream_read = 0;
  102. /**
  103. * File to write to while streaming down.
  104. */
  105. public $write_file = null;
  106. /**
  107. * The resource to write to while streaming down.
  108. */
  109. public $write_stream = null;
  110. /**
  111. * Stores the intended starting seek position.
  112. */
  113. public $seek_position = null;
  114. /**
  115. * The location of the cacert.pem file to use.
  116. */
  117. public $cacert_location = false;
  118. /**
  119. * The state of SSL certificate verification.
  120. */
  121. public $ssl_verification = true;
  122. /**
  123. * The user-defined callback function to call when a stream is read from.
  124. */
  125. public $registered_streaming_read_callback = null;
  126. /**
  127. * The user-defined callback function to call when a stream is written to.
  128. */
  129. public $registered_streaming_write_callback = null;
  130. /*%******************************************************************************************%*/
  131. // CONSTANTS
  132. /**
  133. * GET HTTP Method
  134. */
  135. const HTTP_GET = 'GET';
  136. /**
  137. * POST HTTP Method
  138. */
  139. const HTTP_POST = 'POST';
  140. /**
  141. * PUT HTTP Method
  142. */
  143. const HTTP_PUT = 'PUT';
  144. /**
  145. * DELETE HTTP Method
  146. */
  147. const HTTP_DELETE = 'DELETE';
  148. /**
  149. * HEAD HTTP Method
  150. */
  151. const HTTP_HEAD = 'HEAD';
  152. /*%******************************************************************************************%*/
  153. // CONSTRUCTOR/DESTRUCTOR
  154. /**
  155. * Constructs a new instance of this class.
  156. *
  157. * @param string $url (Optional) The URL to request or service endpoint to query.
  158. * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
  159. * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.
  160. * @return $this A reference to the current instance.
  161. */
  162. public function __construct($url = null, $proxy = null, $helpers = null)
  163. {
  164. // Set some default values.
  165. $this->request_url = $url;
  166. $this->method = self::HTTP_GET;
  167. $this->request_headers = array();
  168. $this->request_body = '';
  169. // Set a new Request class if one was set.
  170. if (isset($helpers['request']) && !empty($helpers['request']))
  171. {
  172. $this->request_class = $helpers['request'];
  173. }
  174. // Set a new Request class if one was set.
  175. if (isset($helpers['response']) && !empty($helpers['response']))
  176. {
  177. $this->response_class = $helpers['response'];
  178. }
  179. if ($proxy)
  180. {
  181. $this->set_proxy($proxy);
  182. }
  183. return $this;
  184. }
  185. /**
  186. * Destructs the instance. Closes opened file handles.
  187. *
  188. * @return $this A reference to the current instance.
  189. */
  190. public function __destruct()
  191. {
  192. if (isset($this->read_file) && isset($this->read_stream))
  193. {
  194. fclose($this->read_stream);
  195. }
  196. if (isset($this->write_file) && isset($this->write_stream))
  197. {
  198. fclose($this->write_stream);
  199. }
  200. return $this;
  201. }
  202. /*%******************************************************************************************%*/
  203. // REQUEST METHODS
  204. /**
  205. * Sets the credentials to use for authentication.
  206. *
  207. * @param string $user (Required) The username to authenticate with.
  208. * @param string $pass (Required) The password to authenticate with.
  209. * @return $this A reference to the current instance.
  210. */
  211. public function set_credentials($user, $pass)
  212. {
  213. $this->username = $user;
  214. $this->password = $pass;
  215. return $this;
  216. }
  217. /**
  218. * Adds a custom HTTP header to the cURL request.
  219. *
  220. * @param string $key (Required) The custom HTTP header to set.
  221. * @param mixed $value (Required) The value to assign to the custom HTTP header.
  222. * @return $this A reference to the current instance.
  223. */
  224. public function add_header($key, $value)
  225. {
  226. $this->request_headers[$key] = $value;
  227. return $this;
  228. }
  229. /**
  230. * Removes an HTTP header from the cURL request.
  231. *
  232. * @param string $key (Required) The custom HTTP header to set.
  233. * @return $this A reference to the current instance.
  234. */
  235. public function remove_header($key)
  236. {
  237. if (isset($this->request_headers[$key]))
  238. {
  239. unset($this->request_headers[$key]);
  240. }
  241. return $this;
  242. }
  243. /**
  244. * Set the method type for the request.
  245. *
  246. * @param string $method (Required) One of the following constants: <HTTP_GET>, <HTTP_POST>, <HTTP_PUT>, <HTTP_HEAD>, <HTTP_DELETE>.
  247. * @return $this A reference to the current instance.
  248. */
  249. public function set_method($method)
  250. {
  251. $this->method = strtoupper($method);
  252. return $this;
  253. }
  254. /**
  255. * Sets a custom useragent string for the class.
  256. *
  257. * @param string $ua (Required) The useragent string to use.
  258. * @return $this A reference to the current instance.
  259. */
  260. public function set_useragent($ua)
  261. {
  262. $this->useragent = $ua;
  263. return $this;
  264. }
  265. /**
  266. * Set the body to send in the request.
  267. *
  268. * @param string $body (Required) The textual content to send along in the body of the request.
  269. * @return $this A reference to the current instance.
  270. */
  271. public function set_body($body)
  272. {
  273. $this->request_body = $body;
  274. return $this;
  275. }
  276. /**
  277. * Set the URL to make the request to.
  278. *
  279. * @param string $url (Required) The URL to make the request to.
  280. * @return $this A reference to the current instance.
  281. */
  282. public function set_request_url($url)
  283. {
  284. $this->request_url = $url;
  285. return $this;
  286. }
  287. /**
  288. * Set additional CURLOPT settings. These will merge with the default settings, and override if
  289. * there is a duplicate.
  290. *
  291. * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings.
  292. * @return $this A reference to the current instance.
  293. */
  294. public function set_curlopts($curlopts)
  295. {
  296. $this->curlopts = $curlopts;
  297. return $this;
  298. }
  299. /**
  300. * Sets the length in bytes to read from the stream while streaming up.
  301. *
  302. * @param integer $size (Required) The length in bytes to read from the stream.
  303. * @return $this A reference to the current instance.
  304. */
  305. public function set_read_stream_size($size)
  306. {
  307. $this->read_stream_size = $size;
  308. return $this;
  309. }
  310. /**
  311. * Sets the resource to read from while streaming up. Reads the stream from its current position until
  312. * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by <php:fstat()> and
  313. * <php:ftell()>.
  314. *
  315. * @param resource $resource (Required) The readable resource to read from.
  316. * @param integer $size (Optional) The size of the stream to read.
  317. * @return $this A reference to the current instance.
  318. */
  319. public function set_read_stream($resource, $size = null)
  320. {
  321. if (!isset($size) || $size < 0)
  322. {
  323. $stats = fstat($resource);
  324. if ($stats && $stats['size'] >= 0)
  325. {
  326. $position = ftell($resource);
  327. if ($position !== false && $position >= 0)
  328. {
  329. $size = $stats['size'] - $position;
  330. }
  331. }
  332. }
  333. $this->read_stream = $resource;
  334. return $this->set_read_stream_size($size);
  335. }
  336. /**
  337. * Sets the file to read from while streaming up.
  338. *
  339. * @param string $location (Required) The readable location to read from.
  340. * @return $this A reference to the current instance.
  341. */
  342. public function set_read_file($location)
  343. {
  344. $this->read_file = $location;
  345. $read_file_handle = fopen($location, 'r');
  346. return $this->set_read_stream($read_file_handle);
  347. }
  348. /**
  349. * Sets the resource to write to while streaming down.
  350. *
  351. * @param resource $resource (Required) The writeable resource to write to.
  352. * @return $this A reference to the current instance.
  353. */
  354. public function set_write_stream($resource)
  355. {
  356. $this->write_stream = $resource;
  357. return $this;
  358. }
  359. /**
  360. * Sets the file to write to while streaming down.
  361. *
  362. * @param string $location (Required) The writeable location to write to.
  363. * @return $this A reference to the current instance.
  364. */
  365. public function set_write_file($location)
  366. {
  367. $this->write_file = $location;
  368. $write_file_handle = fopen($location, 'w');
  369. return $this->set_write_stream($write_file_handle);
  370. }
  371. /**
  372. * Set the proxy to use for making requests.
  373. *
  374. * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
  375. * @return $this A reference to the current instance.
  376. */
  377. public function set_proxy($proxy)
  378. {
  379. $proxy = parse_url($proxy);
  380. $proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null;
  381. $proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null;
  382. $proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null;
  383. $this->proxy = $proxy;
  384. return $this;
  385. }
  386. /**
  387. * Set the intended starting seek position.
  388. *
  389. * @param integer $position (Required) The byte-position of the stream to begin reading from.
  390. * @return $this A reference to the current instance.
  391. */
  392. public function set_seek_position($position)
  393. {
  394. $this->seek_position = isset($position) ? (integer) $position : null;
  395. return $this;
  396. }
  397. /**
  398. * Register a callback function to execute whenever a data stream is read from using
  399. * <CFRequest::streaming_read_callback()>.
  400. *
  401. * The user-defined callback function should accept three arguments:
  402. *
  403. * <ul>
  404. * <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
  405. * <li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
  406. * <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
  407. * </ul>
  408. *
  409. * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
  410. * <li>The name of a global function to execute, passed as a string.</li>
  411. * <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
  412. * <li>An anonymous function (PHP 5.3+).</li></ul>
  413. * @return $this A reference to the current instance.
  414. */
  415. public function register_streaming_read_callback($callback)
  416. {
  417. $this->registered_streaming_read_callback = $callback;
  418. return $this;
  419. }
  420. /**
  421. * Register a callback function to execute whenever a data stream is written to using
  422. * <CFRequest::streaming_write_callback()>.
  423. *
  424. * The user-defined callback function should accept two arguments:
  425. *
  426. * <ul>
  427. * <li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
  428. * <li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
  429. * </ul>
  430. *
  431. * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
  432. * <li>The name of a global function to execute, passed as a string.</li>
  433. * <li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
  434. * <li>An anonymous function (PHP 5.3+).</li></ul>
  435. * @return $this A reference to the current instance.
  436. */
  437. public function register_streaming_write_callback($callback)
  438. {
  439. $this->registered_streaming_write_callback = $callback;
  440. return $this;
  441. }
  442. /*%******************************************************************************************%*/
  443. // PREPARE, SEND, AND PROCESS REQUEST
  444. /**
  445. * A callback function that is invoked by cURL for streaming up.
  446. *
  447. * @param resource $curl_handle (Required) The cURL handle for the request.
  448. * @param resource $file_handle (Required) The open file handle resource.
  449. * @param integer $length (Required) The maximum number of bytes to read.
  450. * @return binary Binary data from a stream.
  451. */
  452. public function streaming_read_callback($curl_handle, $file_handle, $length)
  453. {
  454. // Once we've sent as much as we're supposed to send...
  455. if ($this->read_stream_read >= $this->read_stream_size)
  456. {
  457. // Send EOF
  458. return '';
  459. }
  460. // If we're at the beginning of an upload and need to seek...
  461. if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream))
  462. {
  463. if (fseek($this->read_stream, $this->seek_position) !== 0)
  464. {
  465. throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.');
  466. }
  467. }
  468. $read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size
  469. $this->read_stream_read += strlen($read);
  470. $out = $read === false ? '' : $read;
  471. // Execute callback function
  472. if ($this->registered_streaming_read_callback)
  473. {
  474. call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out);
  475. }
  476. return $out;
  477. }
  478. /**
  479. * A callback function that is invoked by cURL for streaming down.
  480. *
  481. * @param resource $curl_handle (Required) The cURL handle for the request.
  482. * @param binary $data (Required) The data to write.
  483. * @return integer The number of bytes written.
  484. */
  485. public function streaming_write_callback($curl_handle, $data)
  486. {
  487. $length = strlen($data);
  488. $written_total = 0;
  489. $written_last = 0;
  490. while ($written_total < $length)
  491. {
  492. $written_last = fwrite($this->write_stream, substr($data, $written_total));
  493. if ($written_last === false)
  494. {
  495. return $written_total;
  496. }
  497. $written_total += $written_last;
  498. }
  499. // Execute callback function
  500. if ($this->registered_streaming_write_callback)
  501. {
  502. call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total);
  503. }
  504. return $written_total;
  505. }
  506. /**
  507. * Prepares and adds the details of the cURL request. This can be passed along to a <php:curl_multi_exec()>
  508. * function.
  509. *
  510. * @return resource The handle for the cURL object.
  511. */
  512. public function prep_request()
  513. {
  514. $curl_handle = curl_init();
  515. // Set default options.
  516. curl_setopt($curl_handle, CURLOPT_URL, $this->request_url);
  517. curl_setopt($curl_handle, CURLOPT_FILETIME, true);
  518. curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false);
  519. curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
  520. curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5);
  521. curl_setopt($curl_handle, CURLOPT_HEADER, true);
  522. curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
  523. curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000);
  524. curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120);
  525. curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true);
  526. curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url);
  527. curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent);
  528. curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback'));
  529. // Verification of the SSL cert
  530. if ($this->ssl_verification)
  531. {
  532. curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true);
  533. curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, true);
  534. }
  535. else
  536. {
  537. curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
  538. curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);
  539. }
  540. // chmod the file as 0755
  541. if ($this->cacert_location === true)
  542. {
  543. curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
  544. }
  545. elseif (is_string($this->cacert_location))
  546. {
  547. curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location);
  548. }
  549. // Debug mode
  550. if ($this->debug_mode)
  551. {
  552. curl_setopt($curl_handle, CURLOPT_VERBOSE, true);
  553. }
  554. // Handle open_basedir & safe mode
  555. if (!ini_get('safe_mode') && !ini_get('open_basedir'))
  556. {
  557. curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
  558. }
  559. // Enable a proxy connection if requested.
  560. if ($this->proxy)
  561. {
  562. curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true);
  563. $host = $this->proxy['host'];
  564. $host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : '';
  565. curl_setopt($curl_handle, CURLOPT_PROXY, $host);
  566. if (isset($this->proxy['user']) && isset($this->proxy['pass']))
  567. {
  568. curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
  569. }
  570. }
  571. // Set credentials for HTTP Basic/Digest Authentication.
  572. if ($this->username && $this->password)
  573. {
  574. curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  575. curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
  576. }
  577. // Handle the encoding if we can.
  578. if (extension_loaded('zlib'))
  579. {
  580. curl_setopt($curl_handle, CURLOPT_ENCODING, '');
  581. }
  582. // Process custom headers
  583. if (isset($this->request_headers) && count($this->request_headers))
  584. {
  585. $temp_headers = array();
  586. foreach ($this->request_headers as $k => $v)
  587. {
  588. $temp_headers[] = $k . ': ' . $v;
  589. }
  590. curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers);
  591. }
  592. switch ($this->method)
  593. {
  594. case self::HTTP_PUT:
  595. curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT');
  596. if (isset($this->read_stream))
  597. {
  598. if (!isset($this->read_stream_size) || $this->read_stream_size < 0)
  599. {
  600. throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.');
  601. }
  602. curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size);
  603. curl_setopt($curl_handle, CURLOPT_UPLOAD, true);
  604. }
  605. else
  606. {
  607. curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
  608. }
  609. break;
  610. case self::HTTP_POST:
  611. curl_setopt($curl_handle, CURLOPT_POST, true);
  612. curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
  613. break;
  614. case self::HTTP_HEAD:
  615. curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD);
  616. curl_setopt($curl_handle, CURLOPT_NOBODY, 1);
  617. break;
  618. default: // Assumed GET
  619. curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method);
  620. if (isset($this->write_stream))
  621. {
  622. curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback'));
  623. curl_setopt($curl_handle, CURLOPT_HEADER, false);
  624. }
  625. else
  626. {
  627. curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
  628. }
  629. break;
  630. }
  631. // Merge in the CURLOPTs
  632. if (isset($this->curlopts) && sizeof($this->curlopts) > 0)
  633. {
  634. foreach ($this->curlopts as $k => $v)
  635. {
  636. curl_setopt($curl_handle, $k, $v);
  637. }
  638. }
  639. return $curl_handle;
  640. }
  641. /**
  642. * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the
  643. * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via
  644. * parameters.
  645. *
  646. * @param resource $curl_handle (Optional) The reference to the already executed cURL request.
  647. * @param string $response (Optional) The actual response content itself that needs to be parsed.
  648. * @return ResponseCore A <ResponseCore> object containing a parsed HTTP response.
  649. */
  650. public function process_response($curl_handle = null, $response = null)
  651. {
  652. // Accept a custom one if it's passed.
  653. if ($curl_handle && $response)
  654. {
  655. $this->curl_handle = $curl_handle;
  656. $this->response = $response;
  657. }
  658. // As long as this came back as a valid resource...
  659. if (is_resource($this->curl_handle))
  660. {
  661. // Determine what's what.
  662. $header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE);
  663. $this->response_headers = substr($this->response, 0, $header_size);
  664. $this->response_body = substr($this->response, $header_size);
  665. $this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE);
  666. $this->response_info = curl_getinfo($this->curl_handle);
  667. // Parse out the headers
  668. $this->response_headers = explode("\r\n\r\n", trim($this->response_headers));
  669. $this->response_headers = array_pop($this->response_headers);
  670. $this->response_headers = explode("\r\n", $this->response_headers);
  671. array_shift($this->response_headers);
  672. // Loop through and split up the headers.
  673. $header_assoc = array();
  674. foreach ($this->response_headers as $header)
  675. {
  676. $kv = explode(': ', $header);
  677. $header_assoc[strtolower($kv[0])] = $kv[1];
  678. }
  679. // Reset the headers to the appropriate property.
  680. $this->response_headers = $header_assoc;
  681. $this->response_headers['_info'] = $this->response_info;
  682. $this->response_headers['_info']['method'] = $this->method;
  683. if ($curl_handle && $response)
  684. {
  685. return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle);
  686. }
  687. }
  688. // Return false
  689. return false;
  690. }
  691. /**
  692. * Sends the request, calling necessary utility functions to update built-in properties.
  693. *
  694. * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not.
  695. * @return string The resulting unparsed data from the request.
  696. */
  697. public function send_request($parse = false)
  698. {
  699. set_time_limit(0);
  700. $curl_handle = $this->prep_request();
  701. $this->response = curl_exec($curl_handle);
  702. if ($this->response === false)
  703. {
  704. throw new RequestCore_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (' . curl_errno($curl_handle) . ')');
  705. }
  706. $parsed_response = $this->process_response($curl_handle, $this->response);
  707. curl_close($curl_handle);
  708. if ($parse)
  709. {
  710. return $parsed_response;
  711. }
  712. return $this->response;
  713. }
  714. /**
  715. * Sends the request using <php:curl_multi_exec()>, enabling parallel requests. Uses the "rolling" method.
  716. *
  717. * @param array $handles (Required) An indexed array of cURL handles to process simultaneously.
  718. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  719. * <li><code>callback</code> - <code>string|array</code> - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the <code>[0]</code> index is the class and the <code>[1]</code> index is the method name.</li>
  720. * <li><code>limit</code> - <code>integer</code> - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.</li></ul>
  721. * @return array Post-processed cURL responses.
  722. */
  723. public function send_multi_request($handles, $opt = null)
  724. {
  725. set_time_limit(0);
  726. // Skip everything if there are no handles to process.
  727. if (count($handles) === 0) return array();
  728. if (!$opt) $opt = array();
  729. // Initialize any missing options
  730. $limit = isset($opt['limit']) ? $opt['limit'] : -1;
  731. // Initialize
  732. $handle_list = $handles;
  733. $http = new $this->request_class();
  734. $multi_handle = curl_multi_init();
  735. $handles_post = array();
  736. $added = count($handles);
  737. $last_handle = null;
  738. $count = 0;
  739. $i = 0;
  740. // Loop through the cURL handles and add as many as it set by the limit parameter.
  741. while ($i < $added)
  742. {
  743. if ($limit > 0 && $i >= $limit) break;
  744. curl_multi_add_handle($multi_handle, array_shift($handles));
  745. $i++;
  746. }
  747. do
  748. {
  749. $active = false;
  750. // Start executing and wait for a response.
  751. while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM)
  752. {
  753. // Start looking for possible responses immediately when we have to add more handles
  754. if (count($handles) > 0) break;
  755. }
  756. // Figure out which requests finished.
  757. $to_process = array();
  758. while ($done = curl_multi_info_read($multi_handle))
  759. {
  760. // Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html )
  761. if ($done['result'] > 0)
  762. {
  763. throw new RequestCore_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (' . $done['result'] . ')');
  764. }
  765. // Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests
  766. elseif (!isset($to_process[(int) $done['handle']]))
  767. {
  768. $to_process[(int) $done['handle']] = $done;
  769. }
  770. }
  771. // Actually deal with the request
  772. foreach ($to_process as $pkey => $done)
  773. {
  774. $response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle']));
  775. $key = array_search($done['handle'], $handle_list, true);
  776. $handles_post[$key] = $response;
  777. if (count($handles) > 0)
  778. {
  779. curl_multi_add_handle($multi_handle, array_shift($handles));
  780. }
  781. curl_multi_remove_handle($multi_handle, $done['handle']);
  782. curl_close($done['handle']);
  783. }
  784. }
  785. while ($active || count($handles_post) < $added);
  786. curl_multi_close($multi_handle);
  787. ksort($handles_post, SORT_NUMERIC);
  788. return $handles_post;
  789. }
  790. /*%******************************************************************************************%*/
  791. // RESPONSE METHODS
  792. /**
  793. * Get the HTTP response headers from the request.
  794. *
  795. * @param string $header (Optional) A specific header value to return. Defaults to all headers.
  796. * @return string|array All or selected header values.
  797. */
  798. public function get_response_header($header = null)
  799. {
  800. if ($header)
  801. {
  802. return $this->response_headers[strtolower($header)];
  803. }
  804. return $this->response_headers;
  805. }
  806. /**
  807. * Get the HTTP response body from the request.
  808. *
  809. * @return string The response body.
  810. */
  811. public function get_response_body()
  812. {
  813. return $this->response_body;
  814. }
  815. /**
  816. * Get the HTTP response code from the request.
  817. *
  818. * @return string The HTTP response code.
  819. */
  820. public function get_response_code()
  821. {
  822. return $this->response_code;
  823. }
  824. }
  825. /**
  826. * Container for all response-related methods.
  827. */
  828. class ResponseCore
  829. {
  830. /**
  831. * Stores the HTTP header information.
  832. */
  833. public $header;
  834. /**
  835. * Stores the SimpleXML response.
  836. */
  837. public $body;
  838. /**
  839. * Stores the HTTP response code.
  840. */
  841. public $status;
  842. /**
  843. * Constructs a new instance of this class.
  844. *
  845. * @param array $header (Required) Associative array of HTTP headers (typically returned by <RequestCore::get_response_header()>).
  846. * @param string $body (Required) XML-formatted response from AWS.
  847. * @param integer $status (Optional) HTTP response status code from the request.
  848. * @return object Contains an <php:array> `header` property (HTTP headers as an associative array), a <php:SimpleXMLElement> or <php:string> `body` property, and an <php:integer> `status` code.
  849. */
  850. public function __construct($header, $body, $status = null)
  851. {
  852. $this->header = $header;
  853. $this->body = $body;
  854. $this->status = $status;
  855. return $this;
  856. }
  857. /**
  858. * Did we receive the status code we expected?
  859. *
  860. * @param integer|array $codes (Optional) The status code(s) to expect. Pass an <php:integer> for a single acceptable value, or an <php:array> of integers for multiple acceptable values.
  861. * @return boolean Whether we received the expected status code or not.
  862. */
  863. public function isOK($codes = array(200, 201, 204, 206))
  864. {
  865. if (is_array($codes))
  866. {
  867. return in_array($this->status, $codes);
  868. }
  869. return $this->status === $codes;
  870. }
  871. }
  872. /**
  873. * Default RequestCore Exception.
  874. */
  875. class RequestCore_Exception extends Exception {}