[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> class-wp-http-curl.php (source)

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


Generated: Tue Sep 17 01:00:03 2019 Cross-referenced by PHPXref 0.7.1