requestcore.class.php 29 KB

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