PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-includes/class-wp-http-curl.php

https://gitlab.com/ReneMC/Custom-wordpress-theme
PHP | 385 lines | 196 code | 53 blank | 136 comment | 50 complexity | cb526f55d0418f55a0b764e11e994c0d MD5 | raw file
  1. <?php
  2. /**
  3. * HTTP API: WP_Http_Curl class
  4. *
  5. * @package WordPress
  6. * @subpackage HTTP
  7. * @since 4.4.0
  8. */
  9. /**
  10. * Core class used to integrate Curl as an HTTP transport.
  11. *
  12. * HTTP request method uses Curl extension to retrieve the url.
  13. *
  14. * Requires the Curl extension to be installed.
  15. *
  16. * @since 2.7.0
  17. */
  18. class WP_Http_Curl {
  19. /**
  20. * Temporary header storage for during requests.
  21. *
  22. * @since 3.2.0
  23. * @access private
  24. * @var string
  25. */
  26. private $headers = '';
  27. /**
  28. * Temporary body storage for during requests.
  29. *
  30. * @since 3.6.0
  31. * @access private
  32. * @var string
  33. */
  34. private $body = '';
  35. /**
  36. * The maximum amount of data to receive from the remote server.
  37. *
  38. * @since 3.6.0
  39. * @access private
  40. * @var int
  41. */
  42. private $max_body_length = false;
  43. /**
  44. * The file resource used for streaming to file.
  45. *
  46. * @since 3.6.0
  47. * @access private
  48. * @var resource
  49. */
  50. private $stream_handle = false;
  51. /**
  52. * The total bytes written in the current request.
  53. *
  54. * @since 4.1.0
  55. * @access private
  56. * @var int
  57. */
  58. private $bytes_written_total = 0;
  59. /**
  60. * Send a HTTP request to a URI using cURL extension.
  61. *
  62. * @access public
  63. * @since 2.7.0
  64. *
  65. * @param string $url The request URL.
  66. * @param string|array $args Optional. Override the defaults.
  67. * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
  68. */
  69. public function request($url, $args = array()) {
  70. $defaults = array(
  71. 'method' => 'GET', 'timeout' => 5,
  72. 'redirection' => 5, 'httpversion' => '1.0',
  73. 'blocking' => true,
  74. 'headers' => array(), 'body' => null, 'cookies' => array()
  75. );
  76. $r = wp_parse_args( $args, $defaults );
  77. if ( isset( $r['headers']['User-Agent'] ) ) {
  78. $r['user-agent'] = $r['headers']['User-Agent'];
  79. unset( $r['headers']['User-Agent'] );
  80. } elseif ( isset( $r['headers']['user-agent'] ) ) {
  81. $r['user-agent'] = $r['headers']['user-agent'];
  82. unset( $r['headers']['user-agent'] );
  83. }
  84. // Construct Cookie: header if any cookies are set.
  85. WP_Http::buildCookieHeader( $r );
  86. $handle = curl_init();
  87. // cURL offers really easy proxy support.
  88. $proxy = new WP_HTTP_Proxy();
  89. if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
  90. curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
  91. curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() );
  92. curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() );
  93. if ( $proxy->use_authentication() ) {
  94. curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
  95. curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
  96. }
  97. }
  98. $is_local = isset($r['local']) && $r['local'];
  99. $ssl_verify = isset($r['sslverify']) && $r['sslverify'];
  100. if ( $is_local ) {
  101. /** This filter is documented in wp-includes/class-wp-http-streams.php */
  102. $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify );
  103. } elseif ( ! $is_local ) {
  104. /** This filter is documented in wp-includes/class-wp-http-streams.php */
  105. $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify );
  106. }
  107. /*
  108. * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since.
  109. * a value of 0 will allow an unlimited timeout.
  110. */
  111. $timeout = (int) ceil( $r['timeout'] );
  112. curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout );
  113. curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout );
  114. curl_setopt( $handle, CURLOPT_URL, $url);
  115. curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
  116. curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false );
  117. curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
  118. if ( $ssl_verify ) {
  119. curl_setopt( $handle, CURLOPT_CAINFO, $r['sslcertificates'] );
  120. }
  121. curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
  122. /*
  123. * The option doesn't work with safe mode or when open_basedir is set, and there's
  124. * a bug #17490 with redirected POST requests, so handle redirections outside Curl.
  125. */
  126. curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false );
  127. if ( defined( 'CURLOPT_PROTOCOLS' ) ) // PHP 5.2.10 / cURL 7.19.4
  128. curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
  129. switch ( $r['method'] ) {
  130. case 'HEAD':
  131. curl_setopt( $handle, CURLOPT_NOBODY, true );
  132. break;
  133. case 'POST':
  134. curl_setopt( $handle, CURLOPT_POST, true );
  135. curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
  136. break;
  137. case 'PUT':
  138. curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
  139. curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
  140. break;
  141. default:
  142. curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] );
  143. if ( ! is_null( $r['body'] ) )
  144. curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
  145. break;
  146. }
  147. if ( true === $r['blocking'] ) {
  148. curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) );
  149. curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) );
  150. }
  151. curl_setopt( $handle, CURLOPT_HEADER, false );
  152. if ( isset( $r['limit_response_size'] ) )
  153. $this->max_body_length = intval( $r['limit_response_size'] );
  154. else
  155. $this->max_body_length = false;
  156. // If streaming to a file open a file handle, and setup our curl streaming handler.
  157. if ( $r['stream'] ) {
  158. if ( ! WP_DEBUG )
  159. $this->stream_handle = @fopen( $r['filename'], 'w+' );
  160. else
  161. $this->stream_handle = fopen( $r['filename'], 'w+' );
  162. if ( ! $this->stream_handle )
  163. return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) );
  164. } else {
  165. $this->stream_handle = false;
  166. }
  167. if ( !empty( $r['headers'] ) ) {
  168. // cURL expects full header strings in each element.
  169. $headers = array();
  170. foreach ( $r['headers'] as $name => $value ) {
  171. $headers[] = "{$name}: $value";
  172. }
  173. curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
  174. }
  175. if ( $r['httpversion'] == '1.0' )
  176. curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
  177. else
  178. curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
  179. /**
  180. * Fires before the cURL request is executed.
  181. *
  182. * Cookies are not currently handled by the HTTP API. This action allows
  183. * plugins to handle cookies themselves.
  184. *
  185. * @since 2.8.0
  186. *
  187. * @param resource &$handle The cURL handle returned by curl_init().
  188. * @param array $r The HTTP request arguments.
  189. * @param string $url The request URL.
  190. */
  191. do_action_ref_array( 'http_api_curl', array( &$handle, $r, $url ) );
  192. // We don't need to return the body, so don't. Just execute request and return.
  193. if ( ! $r['blocking'] ) {
  194. curl_exec( $handle );
  195. if ( $curl_error = curl_error( $handle ) ) {
  196. curl_close( $handle );
  197. return new WP_Error( 'http_request_failed', $curl_error );
  198. }
  199. if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) {
  200. curl_close( $handle );
  201. return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
  202. }
  203. curl_close( $handle );
  204. return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
  205. }
  206. curl_exec( $handle );
  207. $theHeaders = WP_Http::processHeaders( $this->headers, $url );
  208. $theBody = $this->body;
  209. $bytes_written_total = $this->bytes_written_total;
  210. $this->headers = '';
  211. $this->body = '';
  212. $this->bytes_written_total = 0;
  213. $curl_error = curl_errno( $handle );
  214. // If an error occurred, or, no response.
  215. if ( $curl_error || ( 0 == strlen( $theBody ) && empty( $theHeaders['headers'] ) ) ) {
  216. if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error ) {
  217. if ( ! $this->max_body_length || $this->max_body_length != $bytes_written_total ) {
  218. if ( $r['stream'] ) {
  219. curl_close( $handle );
  220. fclose( $this->stream_handle );
  221. return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) );
  222. } else {
  223. curl_close( $handle );
  224. return new WP_Error( 'http_request_failed', curl_error( $handle ) );
  225. }
  226. }
  227. } else {
  228. if ( $curl_error = curl_error( $handle ) ) {
  229. curl_close( $handle );
  230. return new WP_Error( 'http_request_failed', $curl_error );
  231. }
  232. }
  233. if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) {
  234. curl_close( $handle );
  235. return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
  236. }
  237. }
  238. curl_close( $handle );
  239. if ( $r['stream'] )
  240. fclose( $this->stream_handle );
  241. $response = array(
  242. 'headers' => $theHeaders['headers'],
  243. 'body' => null,
  244. 'response' => $theHeaders['response'],
  245. 'cookies' => $theHeaders['cookies'],
  246. 'filename' => $r['filename']
  247. );
  248. // Handle redirects.
  249. if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) )
  250. return $redirect_response;
  251. if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )
  252. $theBody = WP_Http_Encoding::decompress( $theBody );
  253. $response['body'] = $theBody;
  254. return $response;
  255. }
  256. /**
  257. * Grabs the headers of the cURL request.
  258. *
  259. * Each header is sent individually to this callback, so we append to the `$header` property
  260. * for temporary storage
  261. *
  262. * @since 3.2.0
  263. * @access private
  264. *
  265. * @param resource $handle cURL handle.
  266. * @param string $headers cURL request headers.
  267. * @return int Length of the request headers.
  268. */
  269. private function stream_headers( $handle, $headers ) {
  270. $this->headers .= $headers;
  271. return strlen( $headers );
  272. }
  273. /**
  274. * Grabs the body of the cURL request.
  275. *
  276. * The contents of the document are passed in chunks, so we append to the `$body`
  277. * property for temporary storage. Returning a length shorter than the length of
  278. * `$data` passed in will cause cURL to abort the request with `CURLE_WRITE_ERROR`.
  279. *
  280. * @since 3.6.0
  281. * @access private
  282. *
  283. * @param resource $handle cURL handle.
  284. * @param string $data cURL request body.
  285. * @return int Total bytes of data written.
  286. */
  287. private function stream_body( $handle, $data ) {
  288. $data_length = strlen( $data );
  289. if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) {
  290. $data_length = ( $this->max_body_length - $this->bytes_written_total );
  291. $data = substr( $data, 0, $data_length );
  292. }
  293. if ( $this->stream_handle ) {
  294. $bytes_written = fwrite( $this->stream_handle, $data );
  295. } else {
  296. $this->body .= $data;
  297. $bytes_written = $data_length;
  298. }
  299. $this->bytes_written_total += $bytes_written;
  300. // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR.
  301. return $bytes_written;
  302. }
  303. /**
  304. * Determines whether this class can be used for retrieving a URL.
  305. *
  306. * @static
  307. * @since 2.7.0
  308. *
  309. * @param array $args Optional. Array of request arguments. Default empty array.
  310. * @return bool False means this class can not be used, true means it can.
  311. */
  312. public static function test( $args = array() ) {
  313. if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) )
  314. return false;
  315. $is_ssl = isset( $args['ssl'] ) && $args['ssl'];
  316. if ( $is_ssl ) {
  317. $curl_version = curl_version();
  318. // Check whether this cURL version support SSL requests.
  319. if ( ! (CURL_VERSION_SSL & $curl_version['features']) )
  320. return false;
  321. }
  322. /**
  323. * Filter whether cURL can be used as a transport for retrieving a URL.
  324. *
  325. * @since 2.7.0
  326. *
  327. * @param bool $use_class Whether the class can be used. Default true.
  328. * @param array $args An array of request arguments.
  329. */
  330. return apply_filters( 'use_curl_transport', true, $args );
  331. }
  332. }