requestcore.class.php 33 KB

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