49 } |
49 } |
50 |
50 |
51 // Construct Cookie: header if any cookies are set. |
51 // Construct Cookie: header if any cookies are set. |
52 WP_Http::buildCookieHeader( $parsed_args ); |
52 WP_Http::buildCookieHeader( $parsed_args ); |
53 |
53 |
54 $arrURL = parse_url( $url ); |
54 $parsed_url = parse_url( $url ); |
55 |
55 |
56 $connect_host = $arrURL['host']; |
56 $connect_host = $parsed_url['host']; |
57 |
57 |
58 $secure_transport = ( 'ssl' === $arrURL['scheme'] || 'https' === $arrURL['scheme'] ); |
58 $secure_transport = ( 'ssl' === $parsed_url['scheme'] || 'https' === $parsed_url['scheme'] ); |
59 if ( ! isset( $arrURL['port'] ) ) { |
59 if ( ! isset( $parsed_url['port'] ) ) { |
60 if ( 'ssl' === $arrURL['scheme'] || 'https' === $arrURL['scheme'] ) { |
60 if ( 'ssl' === $parsed_url['scheme'] || 'https' === $parsed_url['scheme'] ) { |
61 $arrURL['port'] = 443; |
61 $parsed_url['port'] = 443; |
62 $secure_transport = true; |
62 $secure_transport = true; |
63 } else { |
63 } else { |
64 $arrURL['port'] = 80; |
64 $parsed_url['port'] = 80; |
65 } |
65 } |
66 } |
66 } |
67 |
67 |
68 // Always pass a path, defaulting to the root in cases such as http://example.com. |
68 // Always pass a path, defaulting to the root in cases such as http://example.com. |
69 if ( ! isset( $arrURL['path'] ) ) { |
69 if ( ! isset( $parsed_url['path'] ) ) { |
70 $arrURL['path'] = '/'; |
70 $parsed_url['path'] = '/'; |
71 } |
71 } |
72 |
72 |
73 if ( isset( $parsed_args['headers']['Host'] ) || isset( $parsed_args['headers']['host'] ) ) { |
73 if ( isset( $parsed_args['headers']['Host'] ) || isset( $parsed_args['headers']['host'] ) ) { |
74 if ( isset( $parsed_args['headers']['Host'] ) ) { |
74 if ( isset( $parsed_args['headers']['Host'] ) ) { |
75 $arrURL['host'] = $parsed_args['headers']['Host']; |
75 $parsed_url['host'] = $parsed_args['headers']['Host']; |
76 } else { |
76 } else { |
77 $arrURL['host'] = $parsed_args['headers']['host']; |
77 $parsed_url['host'] = $parsed_args['headers']['host']; |
78 } |
78 } |
79 unset( $parsed_args['headers']['Host'], $parsed_args['headers']['host'] ); |
79 unset( $parsed_args['headers']['Host'], $parsed_args['headers']['host'] ); |
80 } |
80 } |
81 |
81 |
82 /* |
82 /* |
102 * @param bool $ssl_verify Whether to verify the SSL connection. Default true. |
103 * @param bool $ssl_verify Whether to verify the SSL connection. Default true. |
103 * @param string $url The request URL. |
104 * @param string $url The request URL. |
104 */ |
105 */ |
105 $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url ); |
106 $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url ); |
106 } elseif ( ! $is_local ) { |
107 } elseif ( ! $is_local ) { |
107 /** This filter is documented in wp-includes/class-http.php */ |
108 /** This filter is documented in wp-includes/class-wp-http.php */ |
108 $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify, $url ); |
109 $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify, $url ); |
109 } |
110 } |
110 |
111 |
111 $proxy = new WP_HTTP_Proxy(); |
112 $proxy = new WP_HTTP_Proxy(); |
112 |
113 |
113 $context = stream_context_create( |
114 $context = stream_context_create( |
114 array( |
115 array( |
115 'ssl' => array( |
116 'ssl' => array( |
116 'verify_peer' => $ssl_verify, |
117 'verify_peer' => $ssl_verify, |
117 // 'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate(). |
118 // 'CN_match' => $parsed_url['host'], // This is handled by self::verify_ssl_certificate(). |
118 'capture_peer_cert' => $ssl_verify, |
119 'capture_peer_cert' => $ssl_verify, |
119 'SNI_enabled' => true, |
120 'SNI_enabled' => true, |
120 'cafile' => $parsed_args['sslcertificates'], |
121 'cafile' => $parsed_args['sslcertificates'], |
121 'allow_self_signed' => ! $ssl_verify, |
122 'allow_self_signed' => ! $ssl_verify, |
122 ), |
123 ), |
139 $error_reporting = error_reporting( 0 ); |
140 $error_reporting = error_reporting( 0 ); |
140 } |
141 } |
141 |
142 |
142 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
143 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
143 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
144 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
144 $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
145 $handle = @stream_socket_client( |
|
146 'tcp://' . $proxy->host() . ':' . $proxy->port(), |
|
147 $connection_error, |
|
148 $connection_error_str, |
|
149 $connect_timeout, |
|
150 STREAM_CLIENT_CONNECT, |
|
151 $context |
|
152 ); |
145 } else { |
153 } else { |
146 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
154 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
147 $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
155 $handle = @stream_socket_client( |
|
156 $connect_host . ':' . $parsed_url['port'], |
|
157 $connection_error, |
|
158 $connection_error_str, |
|
159 $connect_timeout, |
|
160 STREAM_CLIENT_CONNECT, |
|
161 $context |
|
162 ); |
148 } |
163 } |
149 |
164 |
150 if ( $secure_transport ) { |
165 if ( $secure_transport ) { |
151 error_reporting( $error_reporting ); |
166 error_reporting( $error_reporting ); |
152 } |
167 } |
153 } else { |
168 } else { |
154 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
169 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
155 $handle = stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
170 $handle = stream_socket_client( |
|
171 'tcp://' . $proxy->host() . ':' . $proxy->port(), |
|
172 $connection_error, |
|
173 $connection_error_str, |
|
174 $connect_timeout, |
|
175 STREAM_CLIENT_CONNECT, |
|
176 $context |
|
177 ); |
156 } else { |
178 } else { |
157 $handle = stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
179 $handle = stream_socket_client( |
|
180 $connect_host . ':' . $parsed_url['port'], |
|
181 $connection_error, |
|
182 $connection_error_str, |
|
183 $connect_timeout, |
|
184 STREAM_CLIENT_CONNECT, |
|
185 $context |
|
186 ); |
158 } |
187 } |
159 } |
188 } |
160 |
189 |
161 if ( false === $handle ) { |
190 if ( false === $handle ) { |
162 // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken. |
191 // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken. |
167 return new WP_Error( 'http_request_failed', $connection_error . ': ' . $connection_error_str ); |
196 return new WP_Error( 'http_request_failed', $connection_error . ': ' . $connection_error_str ); |
168 } |
197 } |
169 |
198 |
170 // Verify that the SSL certificate is valid for this request. |
199 // Verify that the SSL certificate is valid for this request. |
171 if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) { |
200 if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) { |
172 if ( ! self::verify_ssl_certificate( $handle, $arrURL['host'] ) ) { |
201 if ( ! self::verify_ssl_certificate( $handle, $parsed_url['host'] ) ) { |
173 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); |
202 return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); |
174 } |
203 } |
175 } |
204 } |
176 |
205 |
177 stream_set_timeout( $handle, $timeout, $utimeout ); |
206 stream_set_timeout( $handle, $timeout, $utimeout ); |
178 |
207 |
179 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { // Some proxies require full URL in this field. |
208 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { // Some proxies require full URL in this field. |
180 $requestPath = $url; |
209 $request_path = $url; |
181 } else { |
210 } else { |
182 $requestPath = $arrURL['path'] . ( isset( $arrURL['query'] ) ? '?' . $arrURL['query'] : '' ); |
211 $request_path = $parsed_url['path'] . ( isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '' ); |
183 } |
212 } |
184 |
213 |
185 $strHeaders = strtoupper( $parsed_args['method'] ) . ' ' . $requestPath . ' HTTP/' . $parsed_args['httpversion'] . "\r\n"; |
214 $headers = strtoupper( $parsed_args['method'] ) . ' ' . $request_path . ' HTTP/' . $parsed_args['httpversion'] . "\r\n"; |
186 |
215 |
187 $include_port_in_host_header = ( |
216 $include_port_in_host_header = ( |
188 ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || |
217 ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) |
189 ( 'http' === $arrURL['scheme'] && 80 != $arrURL['port'] ) || |
218 || ( 'http' === $parsed_url['scheme'] && 80 != $parsed_url['port'] ) |
190 ( 'https' === $arrURL['scheme'] && 443 != $arrURL['port'] ) |
219 || ( 'https' === $parsed_url['scheme'] && 443 != $parsed_url['port'] ) |
191 ); |
220 ); |
192 |
221 |
193 if ( $include_port_in_host_header ) { |
222 if ( $include_port_in_host_header ) { |
194 $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; |
223 $headers .= 'Host: ' . $parsed_url['host'] . ':' . $parsed_url['port'] . "\r\n"; |
195 } else { |
224 } else { |
196 $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; |
225 $headers .= 'Host: ' . $parsed_url['host'] . "\r\n"; |
197 } |
226 } |
198 |
227 |
199 if ( isset( $parsed_args['user-agent'] ) ) { |
228 if ( isset( $parsed_args['user-agent'] ) ) { |
200 $strHeaders .= 'User-agent: ' . $parsed_args['user-agent'] . "\r\n"; |
229 $headers .= 'User-agent: ' . $parsed_args['user-agent'] . "\r\n"; |
201 } |
230 } |
202 |
231 |
203 if ( is_array( $parsed_args['headers'] ) ) { |
232 if ( is_array( $parsed_args['headers'] ) ) { |
204 foreach ( (array) $parsed_args['headers'] as $header => $headerValue ) { |
233 foreach ( (array) $parsed_args['headers'] as $header => $header_value ) { |
205 $strHeaders .= $header . ': ' . $headerValue . "\r\n"; |
234 $headers .= $header . ': ' . $header_value . "\r\n"; |
206 } |
235 } |
207 } else { |
236 } else { |
208 $strHeaders .= $parsed_args['headers']; |
237 $headers .= $parsed_args['headers']; |
209 } |
238 } |
210 |
239 |
211 if ( $proxy->use_authentication() ) { |
240 if ( $proxy->use_authentication() ) { |
212 $strHeaders .= $proxy->authentication_header() . "\r\n"; |
241 $headers .= $proxy->authentication_header() . "\r\n"; |
213 } |
242 } |
214 |
243 |
215 $strHeaders .= "\r\n"; |
244 $headers .= "\r\n"; |
216 |
245 |
217 if ( ! is_null( $parsed_args['body'] ) ) { |
246 if ( ! is_null( $parsed_args['body'] ) ) { |
218 $strHeaders .= $parsed_args['body']; |
247 $headers .= $parsed_args['body']; |
219 } |
248 } |
220 |
249 |
221 fwrite( $handle, $strHeaders ); |
250 fwrite( $handle, $headers ); |
222 |
251 |
223 if ( ! $parsed_args['blocking'] ) { |
252 if ( ! $parsed_args['blocking'] ) { |
224 stream_set_blocking( $handle, 0 ); |
253 stream_set_blocking( $handle, 0 ); |
225 fclose( $handle ); |
254 fclose( $handle ); |
226 return array( |
255 return array( |
260 ) |
291 ) |
261 ); |
292 ); |
262 } |
293 } |
263 |
294 |
264 $bytes_written = 0; |
295 $bytes_written = 0; |
|
296 |
265 while ( ! feof( $handle ) && $keep_reading ) { |
297 while ( ! feof( $handle ) && $keep_reading ) { |
266 $block = fread( $handle, $block_size ); |
298 $block = fread( $handle, $block_size ); |
267 if ( ! $bodyStarted ) { |
299 if ( ! $body_started ) { |
268 $strResponse .= $block; |
300 $response .= $block; |
269 if ( strpos( $strResponse, "\r\n\r\n" ) ) { |
301 if ( strpos( $response, "\r\n\r\n" ) ) { |
270 $process = WP_Http::processResponse( $strResponse ); |
302 $processed_response = WP_Http::processResponse( $response ); |
271 $bodyStarted = true; |
303 $body_started = true; |
272 $block = $process['body']; |
304 $block = $processed_response['body']; |
273 unset( $strResponse ); |
305 unset( $response ); |
274 $process['body'] = ''; |
306 $processed_response['body'] = ''; |
275 } |
307 } |
276 } |
308 } |
277 |
309 |
278 $this_block_size = strlen( $block ); |
310 $this_block_size = strlen( $block ); |
279 |
311 |
280 if ( isset( $parsed_args['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $parsed_args['limit_response_size'] ) { |
312 if ( isset( $parsed_args['limit_response_size'] ) |
|
313 && ( $bytes_written + $this_block_size ) > $parsed_args['limit_response_size'] |
|
314 ) { |
281 $this_block_size = ( $parsed_args['limit_response_size'] - $bytes_written ); |
315 $this_block_size = ( $parsed_args['limit_response_size'] - $bytes_written ); |
282 $block = substr( $block, 0, $this_block_size ); |
316 $block = substr( $block, 0, $this_block_size ); |
283 } |
317 } |
284 |
318 |
285 $bytes_written_to_file = fwrite( $stream_handle, $block ); |
319 $bytes_written_to_file = fwrite( $stream_handle, $block ); |
290 return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); |
324 return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); |
291 } |
325 } |
292 |
326 |
293 $bytes_written += $bytes_written_to_file; |
327 $bytes_written += $bytes_written_to_file; |
294 |
328 |
295 $keep_reading = ! isset( $parsed_args['limit_response_size'] ) || $bytes_written < $parsed_args['limit_response_size']; |
329 $keep_reading = ( |
|
330 ! isset( $parsed_args['limit_response_size'] ) |
|
331 || $bytes_written < $parsed_args['limit_response_size'] |
|
332 ); |
296 } |
333 } |
297 |
334 |
298 fclose( $stream_handle ); |
335 fclose( $stream_handle ); |
299 |
336 |
300 } else { |
337 } else { |
301 $header_length = 0; |
338 $header_length = 0; |
|
339 |
302 while ( ! feof( $handle ) && $keep_reading ) { |
340 while ( ! feof( $handle ) && $keep_reading ) { |
303 $block = fread( $handle, $block_size ); |
341 $block = fread( $handle, $block_size ); |
304 $strResponse .= $block; |
342 $response .= $block; |
305 if ( ! $bodyStarted && strpos( $strResponse, "\r\n\r\n" ) ) { |
343 |
306 $header_length = strpos( $strResponse, "\r\n\r\n" ) + 4; |
344 if ( ! $body_started && strpos( $response, "\r\n\r\n" ) ) { |
307 $bodyStarted = true; |
345 $header_length = strpos( $response, "\r\n\r\n" ) + 4; |
|
346 $body_started = true; |
308 } |
347 } |
309 $keep_reading = ( ! $bodyStarted || ! isset( $parsed_args['limit_response_size'] ) || strlen( $strResponse ) < ( $header_length + $parsed_args['limit_response_size'] ) ); |
348 |
310 } |
349 $keep_reading = ( |
311 |
350 ! $body_started |
312 $process = WP_Http::processResponse( $strResponse ); |
351 || ! isset( $parsed_args['limit_response_size'] ) |
313 unset( $strResponse ); |
352 || strlen( $response ) < ( $header_length + $parsed_args['limit_response_size'] ) |
|
353 ); |
|
354 } |
|
355 |
|
356 $processed_response = WP_Http::processResponse( $response ); |
|
357 unset( $response ); |
314 |
358 |
315 } |
359 } |
316 |
360 |
317 fclose( $handle ); |
361 fclose( $handle ); |
318 |
362 |
319 $arrHeaders = WP_Http::processHeaders( $process['headers'], $url ); |
363 $processed_headers = WP_Http::processHeaders( $processed_response['headers'], $url ); |
320 |
364 |
321 $response = array( |
365 $response = array( |
322 'headers' => $arrHeaders['headers'], |
366 'headers' => $processed_headers['headers'], |
323 // Not yet processed. |
367 // Not yet processed. |
324 'body' => null, |
368 'body' => null, |
325 'response' => $arrHeaders['response'], |
369 'response' => $processed_headers['response'], |
326 'cookies' => $arrHeaders['cookies'], |
370 'cookies' => $processed_headers['cookies'], |
327 'filename' => $parsed_args['filename'], |
371 'filename' => $parsed_args['filename'], |
328 ); |
372 ); |
329 |
373 |
330 // Handle redirects. |
374 // Handle redirects. |
331 $redirect_response = WP_Http::handle_redirects( $url, $parsed_args, $response ); |
375 $redirect_response = WP_Http::handle_redirects( $url, $parsed_args, $response ); |
332 if ( false !== $redirect_response ) { |
376 if ( false !== $redirect_response ) { |
333 return $redirect_response; |
377 return $redirect_response; |
334 } |
378 } |
335 |
379 |
336 // If the body was chunk encoded, then decode it. |
380 // If the body was chunk encoded, then decode it. |
337 if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) |
381 if ( ! empty( $processed_response['body'] ) |
338 && 'chunked' === $arrHeaders['headers']['transfer-encoding'] |
382 && isset( $processed_headers['headers']['transfer-encoding'] ) |
|
383 && 'chunked' === $processed_headers['headers']['transfer-encoding'] |
339 ) { |
384 ) { |
340 $process['body'] = WP_Http::chunkTransferDecode( $process['body'] ); |
385 $processed_response['body'] = WP_Http::chunkTransferDecode( $processed_response['body'] ); |
341 } |
386 } |
342 |
387 |
343 if ( true === $parsed_args['decompress'] && true === WP_Http_Encoding::should_decode( $arrHeaders['headers'] ) ) { |
388 if ( true === $parsed_args['decompress'] |
344 $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); |
389 && true === WP_Http_Encoding::should_decode( $processed_headers['headers'] ) |
345 } |
390 ) { |
346 |
391 $processed_response['body'] = WP_Http_Encoding::decompress( $processed_response['body'] ); |
347 if ( isset( $parsed_args['limit_response_size'] ) && strlen( $process['body'] ) > $parsed_args['limit_response_size'] ) { |
392 } |
348 $process['body'] = substr( $process['body'], 0, $parsed_args['limit_response_size'] ); |
393 |
349 } |
394 if ( isset( $parsed_args['limit_response_size'] ) |
350 |
395 && strlen( $processed_response['body'] ) > $parsed_args['limit_response_size'] |
351 $response['body'] = $process['body']; |
396 ) { |
|
397 $processed_response['body'] = substr( $processed_response['body'], 0, $parsed_args['limit_response_size'] ); |
|
398 } |
|
399 |
|
400 $response['body'] = $processed_response['body']; |
352 |
401 |
353 return $response; |
402 return $response; |
354 } |
403 } |
355 |
404 |
356 /** |
405 /** |
363 * |
412 * |
364 * IP Address support is included if the request is being made to an IP address. |
413 * IP Address support is included if the request is being made to an IP address. |
365 * |
414 * |
366 * @since 3.7.0 |
415 * @since 3.7.0 |
367 * |
416 * |
368 * @param stream $stream The PHP Stream which the SSL request is being made over |
417 * @param resource $stream The PHP Stream which the SSL request is being made over |
369 * @param string $host The hostname being requested |
418 * @param string $host The hostname being requested |
370 * @return bool If the cerficiate presented in $stream is valid for $host |
419 * @return bool If the certificate presented in $stream is valid for $host |
371 */ |
420 */ |
372 public static function verify_ssl_certificate( $stream, $host ) { |
421 public static function verify_ssl_certificate( $stream, $host ) { |
373 $context_options = stream_context_get_options( $stream ); |
422 $context_options = stream_context_get_options( $stream ); |
374 |
423 |
375 if ( empty( $context_options['ssl']['peer_certificate'] ) ) { |
424 if ( empty( $context_options['ssl']['peer_certificate'] ) ) { |