wp/wp-includes/class-wp-http-curl.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    36 
    36 
    37 	/**
    37 	/**
    38 	 * The maximum amount of data to receive from the remote server.
    38 	 * The maximum amount of data to receive from the remote server.
    39 	 *
    39 	 *
    40 	 * @since 3.6.0
    40 	 * @since 3.6.0
    41 	 * @var int
    41 	 * @var int|false
    42 	 */
    42 	 */
    43 	private $max_body_length = false;
    43 	private $max_body_length = false;
    44 
    44 
    45 	/**
    45 	/**
    46 	 * The file resource used for streaming to file.
    46 	 * The file resource used for streaming to file.
    47 	 *
    47 	 *
    48 	 * @since 3.6.0
    48 	 * @since 3.6.0
    49 	 * @var resource
    49 	 * @var resource|false
    50 	 */
    50 	 */
    51 	private $stream_handle = false;
    51 	private $stream_handle = false;
    52 
    52 
    53 	/**
    53 	/**
    54 	 * The total bytes written in the current request.
    54 	 * The total bytes written in the current request.
    61 	/**
    61 	/**
    62 	 * Send a HTTP request to a URI using cURL extension.
    62 	 * Send a HTTP request to a URI using cURL extension.
    63 	 *
    63 	 *
    64 	 * @since 2.7.0
    64 	 * @since 2.7.0
    65 	 *
    65 	 *
    66 	 * @param string $url The request URL.
    66 	 * @param string       $url  The request URL.
    67 	 * @param string|array $args Optional. Override the defaults.
    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
    68 	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
    69 	 */
    69 	 */
    70 	public function request( $url, $args = array() ) {
    70 	public function request( $url, $args = array() ) {
    71 		$defaults = array(
    71 		$defaults = array(
    77 			'headers'     => array(),
    77 			'headers'     => array(),
    78 			'body'        => null,
    78 			'body'        => null,
    79 			'cookies'     => array(),
    79 			'cookies'     => array(),
    80 		);
    80 		);
    81 
    81 
    82 		$r = wp_parse_args( $args, $defaults );
    82 		$parsed_args = wp_parse_args( $args, $defaults );
    83 
    83 
    84 		if ( isset( $r['headers']['User-Agent'] ) ) {
    84 		if ( isset( $parsed_args['headers']['User-Agent'] ) ) {
    85 			$r['user-agent'] = $r['headers']['User-Agent'];
    85 			$parsed_args['user-agent'] = $parsed_args['headers']['User-Agent'];
    86 			unset( $r['headers']['User-Agent'] );
    86 			unset( $parsed_args['headers']['User-Agent'] );
    87 		} elseif ( isset( $r['headers']['user-agent'] ) ) {
    87 		} elseif ( isset( $parsed_args['headers']['user-agent'] ) ) {
    88 			$r['user-agent'] = $r['headers']['user-agent'];
    88 			$parsed_args['user-agent'] = $parsed_args['headers']['user-agent'];
    89 			unset( $r['headers']['user-agent'] );
    89 			unset( $parsed_args['headers']['user-agent'] );
    90 		}
    90 		}
    91 
    91 
    92 		// Construct Cookie: header if any cookies are set.
    92 		// Construct Cookie: header if any cookies are set.
    93 		WP_Http::buildCookieHeader( $r );
    93 		WP_Http::buildCookieHeader( $parsed_args );
    94 
    94 
    95 		$handle = curl_init();
    95 		$handle = curl_init();
    96 
    96 
    97 		// cURL offers really easy proxy support.
    97 		// cURL offers really easy proxy support.
    98 		$proxy = new WP_HTTP_Proxy();
    98 		$proxy = new WP_HTTP_Proxy();
   107 				curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
   107 				curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
   108 				curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
   108 				curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
   109 			}
   109 			}
   110 		}
   110 		}
   111 
   111 
   112 		$is_local   = isset( $r['local'] ) && $r['local'];
   112 		$is_local   = isset( $parsed_args['local'] ) && $parsed_args['local'];
   113 		$ssl_verify = isset( $r['sslverify'] ) && $r['sslverify'];
   113 		$ssl_verify = isset( $parsed_args['sslverify'] ) && $parsed_args['sslverify'];
   114 		if ( $is_local ) {
   114 		if ( $is_local ) {
   115 			/** This filter is documented in wp-includes/class-wp-http-streams.php */
   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 );
   116 			$ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url );
   117 		} elseif ( ! $is_local ) {
   117 		} elseif ( ! $is_local ) {
   118 			/** This filter is documented in wp-includes/class-http.php */
   118 			/** This filter is documented in wp-includes/class-http.php */
   121 
   121 
   122 		/*
   122 		/*
   123 		 * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since.
   123 		 * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since.
   124 		 * a value of 0 will allow an unlimited timeout.
   124 		 * a value of 0 will allow an unlimited timeout.
   125 		 */
   125 		 */
   126 		$timeout = (int) ceil( $r['timeout'] );
   126 		$timeout = (int) ceil( $parsed_args['timeout'] );
   127 		curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout );
   127 		curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout );
   128 		curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout );
   128 		curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout );
   129 
   129 
   130 		curl_setopt( $handle, CURLOPT_URL, $url );
   130 		curl_setopt( $handle, CURLOPT_URL, $url );
   131 		curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
   131 		curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
   132 		curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false );
   132 		curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( true === $ssl_verify ) ? 2 : false );
   133 		curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
   133 		curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify );
   134 
   134 
   135 		if ( $ssl_verify ) {
   135 		if ( $ssl_verify ) {
   136 			curl_setopt( $handle, CURLOPT_CAINFO, $r['sslcertificates'] );
   136 			curl_setopt( $handle, CURLOPT_CAINFO, $parsed_args['sslcertificates'] );
   137 		}
   137 		}
   138 
   138 
   139 		curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
   139 		curl_setopt( $handle, CURLOPT_USERAGENT, $parsed_args['user-agent'] );
   140 
   140 
   141 		/*
   141 		/*
   142 		 * The option doesn't work with safe mode or when open_basedir is set, and there's
   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.
   143 		 * a bug #17490 with redirected POST requests, so handle redirections outside Curl.
   144 		 */
   144 		 */
   145 		curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false );
   145 		curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false );
   146 		if ( defined( 'CURLOPT_PROTOCOLS' ) ) { // PHP 5.2.10 / cURL 7.19.4
   146 		curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
   147 			curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
   147 
   148 		}
   148 		switch ( $parsed_args['method'] ) {
   149 
       
   150 		switch ( $r['method'] ) {
       
   151 			case 'HEAD':
   149 			case 'HEAD':
   152 				curl_setopt( $handle, CURLOPT_NOBODY, true );
   150 				curl_setopt( $handle, CURLOPT_NOBODY, true );
   153 				break;
   151 				break;
   154 			case 'POST':
   152 			case 'POST':
   155 				curl_setopt( $handle, CURLOPT_POST, true );
   153 				curl_setopt( $handle, CURLOPT_POST, true );
   156 				curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
   154 				curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] );
   157 				break;
   155 				break;
   158 			case 'PUT':
   156 			case 'PUT':
   159 				curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
   157 				curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' );
   160 				curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
   158 				curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] );
   161 				break;
   159 				break;
   162 			default:
   160 			default:
   163 				curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] );
   161 				curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $parsed_args['method'] );
   164 				if ( ! is_null( $r['body'] ) ) {
   162 				if ( ! is_null( $parsed_args['body'] ) ) {
   165 					curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] );
   163 					curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] );
   166 				}
   164 				}
   167 				break;
   165 				break;
   168 		}
   166 		}
   169 
   167 
   170 		if ( true === $r['blocking'] ) {
   168 		if ( true === $parsed_args['blocking'] ) {
   171 			curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) );
   169 			curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) );
   172 			curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) );
   170 			curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) );
   173 		}
   171 		}
   174 
   172 
   175 		curl_setopt( $handle, CURLOPT_HEADER, false );
   173 		curl_setopt( $handle, CURLOPT_HEADER, false );
   176 
   174 
   177 		if ( isset( $r['limit_response_size'] ) ) {
   175 		if ( isset( $parsed_args['limit_response_size'] ) ) {
   178 			$this->max_body_length = intval( $r['limit_response_size'] );
   176 			$this->max_body_length = intval( $parsed_args['limit_response_size'] );
   179 		} else {
   177 		} else {
   180 			$this->max_body_length = false;
   178 			$this->max_body_length = false;
   181 		}
   179 		}
   182 
   180 
   183 		// If streaming to a file open a file handle, and setup our curl streaming handler.
   181 		// If streaming to a file open a file handle, and setup our curl streaming handler.
   184 		if ( $r['stream'] ) {
   182 		if ( $parsed_args['stream'] ) {
   185 			if ( ! WP_DEBUG ) {
   183 			if ( ! WP_DEBUG ) {
   186 				$this->stream_handle = @fopen( $r['filename'], 'w+' );
   184 				$this->stream_handle = @fopen( $parsed_args['filename'], 'w+' );
   187 			} else {
   185 			} else {
   188 				$this->stream_handle = fopen( $r['filename'], 'w+' );
   186 				$this->stream_handle = fopen( $parsed_args['filename'], 'w+' );
   189 			}
   187 			}
   190 			if ( ! $this->stream_handle ) {
   188 			if ( ! $this->stream_handle ) {
   191 				return new WP_Error(
   189 				return new WP_Error(
   192 					'http_request_failed',
   190 					'http_request_failed',
   193 					sprintf(
   191 					sprintf(
   194 						/* translators: 1: fopen(), 2: file name */
   192 						/* translators: 1: fopen(), 2: File name. */
   195 						__( 'Could not open handle for %1$s to %2$s.' ),
   193 						__( 'Could not open handle for %1$s to %2$s.' ),
   196 						'fopen()',
   194 						'fopen()',
   197 						$r['filename']
   195 						$parsed_args['filename']
   198 					)
   196 					)
   199 				);
   197 				);
   200 			}
   198 			}
   201 		} else {
   199 		} else {
   202 			$this->stream_handle = false;
   200 			$this->stream_handle = false;
   203 		}
   201 		}
   204 
   202 
   205 		if ( ! empty( $r['headers'] ) ) {
   203 		if ( ! empty( $parsed_args['headers'] ) ) {
   206 			// cURL expects full header strings in each element.
   204 			// cURL expects full header strings in each element.
   207 			$headers = array();
   205 			$headers = array();
   208 			foreach ( $r['headers'] as $name => $value ) {
   206 			foreach ( $parsed_args['headers'] as $name => $value ) {
   209 				$headers[] = "{$name}: $value";
   207 				$headers[] = "{$name}: $value";
   210 			}
   208 			}
   211 			curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
   209 			curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
   212 		}
   210 		}
   213 
   211 
   214 		if ( $r['httpversion'] == '1.0' ) {
   212 		if ( '1.0' === $parsed_args['httpversion'] ) {
   215 			curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
   213 			curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
   216 		} else {
   214 		} else {
   217 			curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
   215 			curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
   218 		}
   216 		}
   219 
   217 
   223 		 * Cookies are not currently handled by the HTTP API. This action allows
   221 		 * Cookies are not currently handled by the HTTP API. This action allows
   224 		 * plugins to handle cookies themselves.
   222 		 * plugins to handle cookies themselves.
   225 		 *
   223 		 *
   226 		 * @since 2.8.0
   224 		 * @since 2.8.0
   227 		 *
   225 		 *
   228 		 * @param resource $handle  The cURL handle returned by curl_init() (passed by reference).
   226 		 * @param resource $handle      The cURL handle returned by curl_init() (passed by reference).
   229 		 * @param array    $r       The HTTP request arguments.
   227 		 * @param array    $parsed_args The HTTP request arguments.
   230 		 * @param string   $url     The request URL.
   228 		 * @param string   $url         The request URL.
   231 		 */
   229 		 */
   232 		do_action_ref_array( 'http_api_curl', array( &$handle, $r, $url ) );
   230 		do_action_ref_array( 'http_api_curl', array( &$handle, $parsed_args, $url ) );
   233 
   231 
   234 		// We don't need to return the body, so don't. Just execute request and return.
   232 		// We don't need to return the body, so don't. Just execute request and return.
   235 		if ( ! $r['blocking'] ) {
   233 		if ( ! $parsed_args['blocking'] ) {
   236 			curl_exec( $handle );
   234 			curl_exec( $handle );
   237 
   235 
   238 			if ( $curl_error = curl_error( $handle ) ) {
   236 			$curl_error = curl_error( $handle );
       
   237 			if ( $curl_error ) {
   239 				curl_close( $handle );
   238 				curl_close( $handle );
   240 				return new WP_Error( 'http_request_failed', $curl_error );
   239 				return new WP_Error( 'http_request_failed', $curl_error );
   241 			}
   240 			}
   242 			if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) {
   241 			if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) {
   243 				curl_close( $handle );
   242 				curl_close( $handle );
   244 				return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
   243 				return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
   245 			}
   244 			}
   246 
   245 
   247 			curl_close( $handle );
   246 			curl_close( $handle );
   269 
   268 
   270 		// If an error occurred, or, no response.
   269 		// If an error occurred, or, no response.
   271 		if ( $curl_error || ( 0 == strlen( $theBody ) && empty( $theHeaders['headers'] ) ) ) {
   270 		if ( $curl_error || ( 0 == strlen( $theBody ) && empty( $theHeaders['headers'] ) ) ) {
   272 			if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error ) {
   271 			if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error ) {
   273 				if ( ! $this->max_body_length || $this->max_body_length != $bytes_written_total ) {
   272 				if ( ! $this->max_body_length || $this->max_body_length != $bytes_written_total ) {
   274 					if ( $r['stream'] ) {
   273 					if ( $parsed_args['stream'] ) {
   275 						curl_close( $handle );
   274 						curl_close( $handle );
   276 						fclose( $this->stream_handle );
   275 						fclose( $this->stream_handle );
   277 						return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) );
   276 						return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) );
   278 					} else {
   277 					} else {
   279 						curl_close( $handle );
   278 						curl_close( $handle );
   280 						return new WP_Error( 'http_request_failed', curl_error( $handle ) );
   279 						return new WP_Error( 'http_request_failed', curl_error( $handle ) );
   281 					}
   280 					}
   282 				}
   281 				}
   283 			} else {
   282 			} else {
   284 				if ( $curl_error = curl_error( $handle ) ) {
   283 				$curl_error = curl_error( $handle );
       
   284 				if ( $curl_error ) {
   285 					curl_close( $handle );
   285 					curl_close( $handle );
   286 					return new WP_Error( 'http_request_failed', $curl_error );
   286 					return new WP_Error( 'http_request_failed', $curl_error );
   287 				}
   287 				}
   288 			}
   288 			}
   289 			if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) {
   289 			if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) {
   290 				curl_close( $handle );
   290 				curl_close( $handle );
   291 				return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
   291 				return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) );
   292 			}
   292 			}
   293 		}
   293 		}
   294 
   294 
   295 		curl_close( $handle );
   295 		curl_close( $handle );
   296 
   296 
   297 		if ( $r['stream'] ) {
   297 		if ( $parsed_args['stream'] ) {
   298 			fclose( $this->stream_handle );
   298 			fclose( $this->stream_handle );
   299 		}
   299 		}
   300 
   300 
   301 		$response = array(
   301 		$response = array(
   302 			'headers'  => $theHeaders['headers'],
   302 			'headers'  => $theHeaders['headers'],
   303 			'body'     => null,
   303 			'body'     => null,
   304 			'response' => $theHeaders['response'],
   304 			'response' => $theHeaders['response'],
   305 			'cookies'  => $theHeaders['cookies'],
   305 			'cookies'  => $theHeaders['cookies'],
   306 			'filename' => $r['filename'],
   306 			'filename' => $parsed_args['filename'],
   307 		);
   307 		);
   308 
   308 
   309 		// Handle redirects.
   309 		// Handle redirects.
   310 		if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) ) {
   310 		$redirect_response = WP_HTTP::handle_redirects( $url, $parsed_args, $response );
       
   311 		if ( false !== $redirect_response ) {
   311 			return $redirect_response;
   312 			return $redirect_response;
   312 		}
   313 		}
   313 
   314 
   314 		if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode( $theHeaders['headers'] ) ) {
   315 		if ( true === $parsed_args['decompress'] && true === WP_Http_Encoding::should_decode( $theHeaders['headers'] ) ) {
   315 			$theBody = WP_Http_Encoding::decompress( $theBody );
   316 			$theBody = WP_Http_Encoding::decompress( $theBody );
   316 		}
   317 		}
   317 
   318 
   318 		$response['body'] = $theBody;
   319 		$response['body'] = $theBody;
   319 
   320