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( |
72 'method' => 'GET', 'timeout' => 5, |
72 'method' => 'GET', |
73 'redirection' => 5, 'httpversion' => '1.0', |
73 'timeout' => 5, |
74 'blocking' => true, |
74 'redirection' => 5, |
75 'headers' => array(), 'body' => null, 'cookies' => array() |
75 'httpversion' => '1.0', |
|
76 'blocking' => true, |
|
77 'headers' => array(), |
|
78 'body' => null, |
|
79 'cookies' => array(), |
76 ); |
80 ); |
77 |
81 |
78 $r = wp_parse_args( $args, $defaults ); |
82 $r = wp_parse_args( $args, $defaults ); |
79 |
83 |
80 if ( isset( $r['headers']['User-Agent'] ) ) { |
84 if ( isset( $r['headers']['User-Agent'] ) ) { |
103 curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); |
107 curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); |
104 curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() ); |
108 curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() ); |
105 } |
109 } |
106 } |
110 } |
107 |
111 |
108 $is_local = isset($r['local']) && $r['local']; |
112 $is_local = isset( $r['local'] ) && $r['local']; |
109 $ssl_verify = isset($r['sslverify']) && $r['sslverify']; |
113 $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify']; |
110 if ( $is_local ) { |
114 if ( $is_local ) { |
111 /** 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 */ |
112 $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify ); |
116 $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url ); |
113 } elseif ( ! $is_local ) { |
117 } elseif ( ! $is_local ) { |
114 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
118 /** This filter is documented in wp-includes/class-http.php */ |
115 $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify ); |
119 $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify, $url ); |
116 } |
120 } |
117 |
121 |
118 /* |
122 /* |
119 * 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. |
120 * a value of 0 will allow an unlimited timeout. |
124 * a value of 0 will allow an unlimited timeout. |
121 */ |
125 */ |
122 $timeout = (int) ceil( $r['timeout'] ); |
126 $timeout = (int) ceil( $r['timeout'] ); |
123 curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout ); |
127 curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout ); |
124 curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout ); |
128 curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout ); |
125 |
129 |
126 curl_setopt( $handle, CURLOPT_URL, $url); |
130 curl_setopt( $handle, CURLOPT_URL, $url ); |
127 curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); |
131 curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); |
128 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false ); |
132 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false ); |
129 curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); |
133 curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); |
130 |
134 |
131 if ( $ssl_verify ) { |
135 if ( $ssl_verify ) { |
137 /* |
141 /* |
138 * 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 |
139 * 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. |
140 */ |
144 */ |
141 curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); |
145 curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); |
142 if ( defined( 'CURLOPT_PROTOCOLS' ) ) // PHP 5.2.10 / cURL 7.19.4 |
146 if ( defined( 'CURLOPT_PROTOCOLS' ) ) { // PHP 5.2.10 / cURL 7.19.4 |
143 curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); |
147 curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); |
|
148 } |
144 |
149 |
145 switch ( $r['method'] ) { |
150 switch ( $r['method'] ) { |
146 case 'HEAD': |
151 case 'HEAD': |
147 curl_setopt( $handle, CURLOPT_NOBODY, true ); |
152 curl_setopt( $handle, CURLOPT_NOBODY, true ); |
148 break; |
153 break; |
154 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); |
159 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); |
155 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); |
160 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); |
156 break; |
161 break; |
157 default: |
162 default: |
158 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] ); |
163 curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] ); |
159 if ( ! is_null( $r['body'] ) ) |
164 if ( ! is_null( $r['body'] ) ) { |
160 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); |
165 curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); |
|
166 } |
161 break; |
167 break; |
162 } |
168 } |
163 |
169 |
164 if ( true === $r['blocking'] ) { |
170 if ( true === $r['blocking'] ) { |
165 curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) ); |
171 curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) ); |
166 curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) ); |
172 curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) ); |
167 } |
173 } |
168 |
174 |
169 curl_setopt( $handle, CURLOPT_HEADER, false ); |
175 curl_setopt( $handle, CURLOPT_HEADER, false ); |
170 |
176 |
171 if ( isset( $r['limit_response_size'] ) ) |
177 if ( isset( $r['limit_response_size'] ) ) { |
172 $this->max_body_length = intval( $r['limit_response_size'] ); |
178 $this->max_body_length = intval( $r['limit_response_size'] ); |
173 else |
179 } else { |
174 $this->max_body_length = false; |
180 $this->max_body_length = false; |
|
181 } |
175 |
182 |
176 // If streaming to a file open a file handle, and setup our curl streaming handler. |
183 // If streaming to a file open a file handle, and setup our curl streaming handler. |
177 if ( $r['stream'] ) { |
184 if ( $r['stream'] ) { |
178 if ( ! WP_DEBUG ) |
185 if ( ! WP_DEBUG ) { |
179 $this->stream_handle = @fopen( $r['filename'], 'w+' ); |
186 $this->stream_handle = @fopen( $r['filename'], 'w+' ); |
180 else |
187 } else { |
181 $this->stream_handle = fopen( $r['filename'], 'w+' ); |
188 $this->stream_handle = fopen( $r['filename'], 'w+' ); |
|
189 } |
182 if ( ! $this->stream_handle ) { |
190 if ( ! $this->stream_handle ) { |
183 return new WP_Error( 'http_request_failed', sprintf( |
191 return new WP_Error( |
184 /* translators: 1: fopen() 2: file name */ |
192 'http_request_failed', |
185 __( 'Could not open handle for %1$s to %2$s.' ), |
193 sprintf( |
186 'fopen()', |
194 /* translators: 1: fopen(), 2: file name */ |
187 $r['filename'] |
195 __( 'Could not open handle for %1$s to %2$s.' ), |
188 ) ); |
196 'fopen()', |
|
197 $r['filename'] |
|
198 ) |
|
199 ); |
189 } |
200 } |
190 } else { |
201 } else { |
191 $this->stream_handle = false; |
202 $this->stream_handle = false; |
192 } |
203 } |
193 |
204 |
194 if ( !empty( $r['headers'] ) ) { |
205 if ( ! empty( $r['headers'] ) ) { |
195 // cURL expects full header strings in each element. |
206 // cURL expects full header strings in each element. |
196 $headers = array(); |
207 $headers = array(); |
197 foreach ( $r['headers'] as $name => $value ) { |
208 foreach ( $r['headers'] as $name => $value ) { |
198 $headers[] = "{$name}: $value"; |
209 $headers[] = "{$name}: $value"; |
199 } |
210 } |
200 curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers ); |
211 curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers ); |
201 } |
212 } |
202 |
213 |
203 if ( $r['httpversion'] == '1.0' ) |
214 if ( $r['httpversion'] == '1.0' ) { |
204 curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); |
215 curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); |
205 else |
216 } else { |
206 curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 ); |
217 curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 ); |
|
218 } |
207 |
219 |
208 /** |
220 /** |
209 * Fires before the cURL request is executed. |
221 * Fires before the cURL request is executed. |
210 * |
222 * |
211 * Cookies are not currently handled by the HTTP API. This action allows |
223 * Cookies are not currently handled by the HTTP API. This action allows |
231 curl_close( $handle ); |
243 curl_close( $handle ); |
232 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); |
244 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); |
233 } |
245 } |
234 |
246 |
235 curl_close( $handle ); |
247 curl_close( $handle ); |
236 return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); |
248 return array( |
|
249 'headers' => array(), |
|
250 'body' => '', |
|
251 'response' => array( |
|
252 'code' => false, |
|
253 'message' => false, |
|
254 ), |
|
255 'cookies' => array(), |
|
256 ); |
237 } |
257 } |
238 |
258 |
239 curl_exec( $handle ); |
259 curl_exec( $handle ); |
240 $theHeaders = WP_Http::processHeaders( $this->headers, $url ); |
260 $theHeaders = WP_Http::processHeaders( $this->headers, $url ); |
241 $theBody = $this->body; |
261 $theBody = $this->body; |
242 $bytes_written_total = $this->bytes_written_total; |
262 $bytes_written_total = $this->bytes_written_total; |
243 |
263 |
244 $this->headers = ''; |
264 $this->headers = ''; |
245 $this->body = ''; |
265 $this->body = ''; |
246 $this->bytes_written_total = 0; |
266 $this->bytes_written_total = 0; |
247 |
267 |
248 $curl_error = curl_errno( $handle ); |
268 $curl_error = curl_errno( $handle ); |
249 |
269 |
250 // If an error occurred, or, no response. |
270 // If an error occurred, or, no response. |
272 } |
292 } |
273 } |
293 } |
274 |
294 |
275 curl_close( $handle ); |
295 curl_close( $handle ); |
276 |
296 |
277 if ( $r['stream'] ) |
297 if ( $r['stream'] ) { |
278 fclose( $this->stream_handle ); |
298 fclose( $this->stream_handle ); |
|
299 } |
279 |
300 |
280 $response = array( |
301 $response = array( |
281 'headers' => $theHeaders['headers'], |
302 'headers' => $theHeaders['headers'], |
282 'body' => null, |
303 'body' => null, |
283 'response' => $theHeaders['response'], |
304 'response' => $theHeaders['response'], |
284 'cookies' => $theHeaders['cookies'], |
305 'cookies' => $theHeaders['cookies'], |
285 'filename' => $r['filename'] |
306 'filename' => $r['filename'], |
286 ); |
307 ); |
287 |
308 |
288 // Handle redirects. |
309 // Handle redirects. |
289 if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) ) |
310 if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) ) { |
290 return $redirect_response; |
311 return $redirect_response; |
291 |
312 } |
292 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) ) |
313 |
|
314 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode( $theHeaders['headers'] ) ) { |
293 $theBody = WP_Http_Encoding::decompress( $theBody ); |
315 $theBody = WP_Http_Encoding::decompress( $theBody ); |
|
316 } |
294 |
317 |
295 $response['body'] = $theBody; |
318 $response['body'] = $theBody; |
296 |
319 |
297 return $response; |
320 return $response; |
298 } |
321 } |
330 private function stream_body( $handle, $data ) { |
353 private function stream_body( $handle, $data ) { |
331 $data_length = strlen( $data ); |
354 $data_length = strlen( $data ); |
332 |
355 |
333 if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) { |
356 if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) { |
334 $data_length = ( $this->max_body_length - $this->bytes_written_total ); |
357 $data_length = ( $this->max_body_length - $this->bytes_written_total ); |
335 $data = substr( $data, 0, $data_length ); |
358 $data = substr( $data, 0, $data_length ); |
336 } |
359 } |
337 |
360 |
338 if ( $this->stream_handle ) { |
361 if ( $this->stream_handle ) { |
339 $bytes_written = fwrite( $this->stream_handle, $data ); |
362 $bytes_written = fwrite( $this->stream_handle, $data ); |
340 } else { |
363 } else { |
341 $this->body .= $data; |
364 $this->body .= $data; |
342 $bytes_written = $data_length; |
365 $bytes_written = $data_length; |
343 } |
366 } |
344 |
367 |
345 $this->bytes_written_total += $bytes_written; |
368 $this->bytes_written_total += $bytes_written; |
346 |
369 |
349 } |
372 } |
350 |
373 |
351 /** |
374 /** |
352 * Determines whether this class can be used for retrieving a URL. |
375 * Determines whether this class can be used for retrieving a URL. |
353 * |
376 * |
354 * @static |
|
355 * @since 2.7.0 |
377 * @since 2.7.0 |
356 * |
378 * |
357 * @param array $args Optional. Array of request arguments. Default empty array. |
379 * @param array $args Optional. Array of request arguments. Default empty array. |
358 * @return bool False means this class can not be used, true means it can. |
380 * @return bool False means this class can not be used, true means it can. |
359 */ |
381 */ |
360 public static function test( $args = array() ) { |
382 public static function test( $args = array() ) { |
361 if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) |
383 if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) { |
362 return false; |
384 return false; |
|
385 } |
363 |
386 |
364 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; |
387 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; |
365 |
388 |
366 if ( $is_ssl ) { |
389 if ( $is_ssl ) { |
367 $curl_version = curl_version(); |
390 $curl_version = curl_version(); |
368 // Check whether this cURL version support SSL requests. |
391 // Check whether this cURL version support SSL requests. |
369 if ( ! (CURL_VERSION_SSL & $curl_version['features']) ) |
392 if ( ! ( CURL_VERSION_SSL & $curl_version['features'] ) ) { |
370 return false; |
393 return false; |
|
394 } |
371 } |
395 } |
372 |
396 |
373 /** |
397 /** |
374 * Filters whether cURL can be used as a transport for retrieving a URL. |
398 * Filters whether cURL can be used as a transport for retrieving a URL. |
375 * |
399 * |