RequestCore.php 29 KB

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