20 * @see WP_Http::request For default options descriptions. |
20 * @see WP_Http::request For default options descriptions. |
21 * |
21 * |
22 * @since 2.7.0 |
22 * @since 2.7.0 |
23 * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). |
23 * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). |
24 * |
24 * |
25 * @param string $url The request URL. |
25 * @param string $url The request URL. |
26 * @param string|array $args Optional. Override the defaults. |
26 * @param string|array $args Optional. Override the defaults. |
27 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error |
27 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error |
28 */ |
28 */ |
29 public function request( $url, $args = array() ) { |
29 public function request( $url, $args = array() ) { |
30 $defaults = array( |
30 $defaults = array( |
36 'headers' => array(), |
36 'headers' => array(), |
37 'body' => null, |
37 'body' => null, |
38 'cookies' => array(), |
38 'cookies' => array(), |
39 ); |
39 ); |
40 |
40 |
41 $r = wp_parse_args( $args, $defaults ); |
41 $parsed_args = wp_parse_args( $args, $defaults ); |
42 |
42 |
43 if ( isset( $r['headers']['User-Agent'] ) ) { |
43 if ( isset( $parsed_args['headers']['User-Agent'] ) ) { |
44 $r['user-agent'] = $r['headers']['User-Agent']; |
44 $parsed_args['user-agent'] = $parsed_args['headers']['User-Agent']; |
45 unset( $r['headers']['User-Agent'] ); |
45 unset( $parsed_args['headers']['User-Agent'] ); |
46 } elseif ( isset( $r['headers']['user-agent'] ) ) { |
46 } elseif ( isset( $parsed_args['headers']['user-agent'] ) ) { |
47 $r['user-agent'] = $r['headers']['user-agent']; |
47 $parsed_args['user-agent'] = $parsed_args['headers']['user-agent']; |
48 unset( $r['headers']['user-agent'] ); |
48 unset( $parsed_args['headers']['user-agent'] ); |
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( $r ); |
52 WP_Http::buildCookieHeader( $parsed_args ); |
53 |
53 |
54 $arrURL = parse_url( $url ); |
54 $arrURL = parse_url( $url ); |
55 |
55 |
56 $connect_host = $arrURL['host']; |
56 $connect_host = $arrURL['host']; |
57 |
57 |
58 $secure_transport = ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ); |
58 $secure_transport = ( 'ssl' === $arrURL['scheme'] || 'https' === $arrURL['scheme'] ); |
59 if ( ! isset( $arrURL['port'] ) ) { |
59 if ( ! isset( $arrURL['port'] ) ) { |
60 if ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) { |
60 if ( 'ssl' === $arrURL['scheme'] || 'https' === $arrURL['scheme'] ) { |
61 $arrURL['port'] = 443; |
61 $arrURL['port'] = 443; |
62 $secure_transport = true; |
62 $secure_transport = true; |
63 } else { |
63 } else { |
64 $arrURL['port'] = 80; |
64 $arrURL['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( $arrURL['path'] ) ) { |
70 $arrURL['path'] = '/'; |
70 $arrURL['path'] = '/'; |
71 } |
71 } |
72 |
72 |
73 if ( isset( $r['headers']['Host'] ) || isset( $r['headers']['host'] ) ) { |
73 if ( isset( $parsed_args['headers']['Host'] ) || isset( $parsed_args['headers']['host'] ) ) { |
74 if ( isset( $r['headers']['Host'] ) ) { |
74 if ( isset( $parsed_args['headers']['Host'] ) ) { |
75 $arrURL['host'] = $r['headers']['Host']; |
75 $arrURL['host'] = $parsed_args['headers']['Host']; |
76 } else { |
76 } else { |
77 $arrURL['host'] = $r['headers']['host']; |
77 $arrURL['host'] = $parsed_args['headers']['host']; |
78 } |
78 } |
79 unset( $r['headers']['Host'], $r['headers']['host'] ); |
79 unset( $parsed_args['headers']['Host'], $parsed_args['headers']['host'] ); |
80 } |
80 } |
81 |
81 |
82 /* |
82 /* |
83 * Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect |
83 * Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect |
84 * to ::1, which fails when the server is not set up for it. For compatibility, always |
84 * to ::1, which fails when the server is not set up for it. For compatibility, always |
85 * connect to the IPv4 address. |
85 * connect to the IPv4 address. |
86 */ |
86 */ |
87 if ( 'localhost' == strtolower( $connect_host ) ) { |
87 if ( 'localhost' === strtolower( $connect_host ) ) { |
88 $connect_host = '127.0.0.1'; |
88 $connect_host = '127.0.0.1'; |
89 } |
89 } |
90 |
90 |
91 $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host; |
91 $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host; |
92 |
92 |
93 $is_local = isset( $r['local'] ) && $r['local']; |
93 $is_local = isset( $parsed_args['local'] ) && $parsed_args['local']; |
94 $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify']; |
94 $ssl_verify = isset( $parsed_args['sslverify'] ) && $parsed_args['sslverify']; |
95 if ( $is_local ) { |
95 if ( $is_local ) { |
96 /** |
96 /** |
97 * Filters whether SSL should be verified for local requests. |
97 * Filters whether SSL should be verified for local requests. |
98 * |
98 * |
99 * @since 2.8.0 |
99 * @since 2.8.0 |
112 |
112 |
113 $context = stream_context_create( |
113 $context = stream_context_create( |
114 array( |
114 array( |
115 'ssl' => array( |
115 'ssl' => array( |
116 'verify_peer' => $ssl_verify, |
116 'verify_peer' => $ssl_verify, |
117 //'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate() |
117 // 'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate(). |
118 'capture_peer_cert' => $ssl_verify, |
118 'capture_peer_cert' => $ssl_verify, |
119 'SNI_enabled' => true, |
119 'SNI_enabled' => true, |
120 'cafile' => $r['sslcertificates'], |
120 'cafile' => $parsed_args['sslcertificates'], |
121 'allow_self_signed' => ! $ssl_verify, |
121 'allow_self_signed' => ! $ssl_verify, |
122 ), |
122 ), |
123 ) |
123 ) |
124 ); |
124 ); |
125 |
125 |
126 $timeout = (int) floor( $r['timeout'] ); |
126 $timeout = (int) floor( $parsed_args['timeout'] ); |
127 $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000; |
127 $utimeout = $timeout == $parsed_args['timeout'] ? 0 : 1000000 * $parsed_args['timeout'] % 1000000; |
128 $connect_timeout = max( $timeout, 1 ); |
128 $connect_timeout = max( $timeout, 1 ); |
129 |
129 |
130 // Store error number. |
130 // Store error number. |
131 $connection_error = null; |
131 $connection_error = null; |
132 |
132 |
133 // Store error string. |
133 // Store error string. |
134 $connection_error_str = null; |
134 $connection_error_str = null; |
135 |
135 |
136 if ( ! WP_DEBUG ) { |
136 if ( ! WP_DEBUG ) { |
137 // In the event that the SSL connection fails, silence the many PHP Warnings. |
137 // In the event that the SSL connection fails, silence the many PHP warnings. |
138 if ( $secure_transport ) { |
138 if ( $secure_transport ) { |
139 $error_reporting = error_reporting( 0 ); |
139 $error_reporting = error_reporting( 0 ); |
140 } |
140 } |
141 |
141 |
142 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
142 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { |
|
143 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
143 $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
144 $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
144 } else { |
145 } else { |
|
146 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
145 $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
147 $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); |
146 } |
148 } |
147 |
149 |
148 if ( $secure_transport ) { |
150 if ( $secure_transport ) { |
149 error_reporting( $error_reporting ); |
151 error_reporting( $error_reporting ); |
172 } |
174 } |
173 } |
175 } |
174 |
176 |
175 stream_set_timeout( $handle, $timeout, $utimeout ); |
177 stream_set_timeout( $handle, $timeout, $utimeout ); |
176 |
178 |
177 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { //Some proxies require full URL in this field. |
179 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { // Some proxies require full URL in this field. |
178 $requestPath = $url; |
180 $requestPath = $url; |
179 } else { |
181 } else { |
180 $requestPath = $arrURL['path'] . ( isset( $arrURL['query'] ) ? '?' . $arrURL['query'] : '' ); |
182 $requestPath = $arrURL['path'] . ( isset( $arrURL['query'] ) ? '?' . $arrURL['query'] : '' ); |
181 } |
183 } |
182 |
184 |
183 $strHeaders = strtoupper( $r['method'] ) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n"; |
185 $strHeaders = strtoupper( $parsed_args['method'] ) . ' ' . $requestPath . ' HTTP/' . $parsed_args['httpversion'] . "\r\n"; |
184 |
186 |
185 $include_port_in_host_header = ( |
187 $include_port_in_host_header = ( |
186 ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || |
188 ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || |
187 ( 'http' == $arrURL['scheme'] && 80 != $arrURL['port'] ) || |
189 ( 'http' === $arrURL['scheme'] && 80 != $arrURL['port'] ) || |
188 ( 'https' == $arrURL['scheme'] && 443 != $arrURL['port'] ) |
190 ( 'https' === $arrURL['scheme'] && 443 != $arrURL['port'] ) |
189 ); |
191 ); |
190 |
192 |
191 if ( $include_port_in_host_header ) { |
193 if ( $include_port_in_host_header ) { |
192 $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; |
194 $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; |
193 } else { |
195 } else { |
194 $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; |
196 $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; |
195 } |
197 } |
196 |
198 |
197 if ( isset( $r['user-agent'] ) ) { |
199 if ( isset( $parsed_args['user-agent'] ) ) { |
198 $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n"; |
200 $strHeaders .= 'User-agent: ' . $parsed_args['user-agent'] . "\r\n"; |
199 } |
201 } |
200 |
202 |
201 if ( is_array( $r['headers'] ) ) { |
203 if ( is_array( $parsed_args['headers'] ) ) { |
202 foreach ( (array) $r['headers'] as $header => $headerValue ) { |
204 foreach ( (array) $parsed_args['headers'] as $header => $headerValue ) { |
203 $strHeaders .= $header . ': ' . $headerValue . "\r\n"; |
205 $strHeaders .= $header . ': ' . $headerValue . "\r\n"; |
204 } |
206 } |
205 } else { |
207 } else { |
206 $strHeaders .= $r['headers']; |
208 $strHeaders .= $parsed_args['headers']; |
207 } |
209 } |
208 |
210 |
209 if ( $proxy->use_authentication() ) { |
211 if ( $proxy->use_authentication() ) { |
210 $strHeaders .= $proxy->authentication_header() . "\r\n"; |
212 $strHeaders .= $proxy->authentication_header() . "\r\n"; |
211 } |
213 } |
212 |
214 |
213 $strHeaders .= "\r\n"; |
215 $strHeaders .= "\r\n"; |
214 |
216 |
215 if ( ! is_null( $r['body'] ) ) { |
217 if ( ! is_null( $parsed_args['body'] ) ) { |
216 $strHeaders .= $r['body']; |
218 $strHeaders .= $parsed_args['body']; |
217 } |
219 } |
218 |
220 |
219 fwrite( $handle, $strHeaders ); |
221 fwrite( $handle, $strHeaders ); |
220 |
222 |
221 if ( ! $r['blocking'] ) { |
223 if ( ! $parsed_args['blocking'] ) { |
222 stream_set_blocking( $handle, 0 ); |
224 stream_set_blocking( $handle, 0 ); |
223 fclose( $handle ); |
225 fclose( $handle ); |
224 return array( |
226 return array( |
225 'headers' => array(), |
227 'headers' => array(), |
226 'body' => '', |
228 'body' => '', |
234 |
236 |
235 $strResponse = ''; |
237 $strResponse = ''; |
236 $bodyStarted = false; |
238 $bodyStarted = false; |
237 $keep_reading = true; |
239 $keep_reading = true; |
238 $block_size = 4096; |
240 $block_size = 4096; |
239 if ( isset( $r['limit_response_size'] ) ) { |
241 if ( isset( $parsed_args['limit_response_size'] ) ) { |
240 $block_size = min( $block_size, $r['limit_response_size'] ); |
242 $block_size = min( $block_size, $parsed_args['limit_response_size'] ); |
241 } |
243 } |
242 |
244 |
243 // If streaming to a file setup the file handle. |
245 // If streaming to a file setup the file handle. |
244 if ( $r['stream'] ) { |
246 if ( $parsed_args['stream'] ) { |
245 if ( ! WP_DEBUG ) { |
247 if ( ! WP_DEBUG ) { |
246 $stream_handle = @fopen( $r['filename'], 'w+' ); |
248 $stream_handle = @fopen( $parsed_args['filename'], 'w+' ); |
247 } else { |
249 } else { |
248 $stream_handle = fopen( $r['filename'], 'w+' ); |
250 $stream_handle = fopen( $parsed_args['filename'], 'w+' ); |
249 } |
251 } |
250 if ( ! $stream_handle ) { |
252 if ( ! $stream_handle ) { |
251 return new WP_Error( |
253 return new WP_Error( |
252 'http_request_failed', |
254 'http_request_failed', |
253 sprintf( |
255 sprintf( |
254 /* translators: 1: fopen(), 2: file name */ |
256 /* translators: 1: fopen(), 2: File name. */ |
255 __( 'Could not open handle for %1$s to %2$s.' ), |
257 __( 'Could not open handle for %1$s to %2$s.' ), |
256 'fopen()', |
258 'fopen()', |
257 $r['filename'] |
259 $parsed_args['filename'] |
258 ) |
260 ) |
259 ); |
261 ); |
260 } |
262 } |
261 |
263 |
262 $bytes_written = 0; |
264 $bytes_written = 0; |
320 'headers' => $arrHeaders['headers'], |
322 'headers' => $arrHeaders['headers'], |
321 // Not yet processed. |
323 // Not yet processed. |
322 'body' => null, |
324 'body' => null, |
323 'response' => $arrHeaders['response'], |
325 'response' => $arrHeaders['response'], |
324 'cookies' => $arrHeaders['cookies'], |
326 'cookies' => $arrHeaders['cookies'], |
325 'filename' => $r['filename'], |
327 'filename' => $parsed_args['filename'], |
326 ); |
328 ); |
327 |
329 |
328 // Handle redirects. |
330 // Handle redirects. |
329 if ( false !== ( $redirect_response = WP_Http::handle_redirects( $url, $r, $response ) ) ) { |
331 $redirect_response = WP_Http::handle_redirects( $url, $parsed_args, $response ); |
|
332 if ( false !== $redirect_response ) { |
330 return $redirect_response; |
333 return $redirect_response; |
331 } |
334 } |
332 |
335 |
333 // If the body was chunk encoded, then decode it. |
336 // If the body was chunk encoded, then decode it. |
334 if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) && 'chunked' == $arrHeaders['headers']['transfer-encoding'] ) { |
337 if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) |
|
338 && 'chunked' === $arrHeaders['headers']['transfer-encoding'] |
|
339 ) { |
335 $process['body'] = WP_Http::chunkTransferDecode( $process['body'] ); |
340 $process['body'] = WP_Http::chunkTransferDecode( $process['body'] ); |
336 } |
341 } |
337 |
342 |
338 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode( $arrHeaders['headers'] ) ) { |
343 if ( true === $parsed_args['decompress'] && true === WP_Http_Encoding::should_decode( $arrHeaders['headers'] ) ) { |
339 $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); |
344 $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); |
340 } |
345 } |
341 |
346 |
342 if ( isset( $r['limit_response_size'] ) && strlen( $process['body'] ) > $r['limit_response_size'] ) { |
347 if ( isset( $parsed_args['limit_response_size'] ) && strlen( $process['body'] ) > $parsed_args['limit_response_size'] ) { |
343 $process['body'] = substr( $process['body'], 0, $r['limit_response_size'] ); |
348 $process['body'] = substr( $process['body'], 0, $parsed_args['limit_response_size'] ); |
344 } |
349 } |
345 |
350 |
346 $response['body'] = $process['body']; |
351 $response['body'] = $process['body']; |
347 |
352 |
348 return $response; |
353 return $response; |
385 $certificate_hostnames = array(); |
390 $certificate_hostnames = array(); |
386 if ( ! empty( $cert['extensions']['subjectAltName'] ) ) { |
391 if ( ! empty( $cert['extensions']['subjectAltName'] ) ) { |
387 $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] ); |
392 $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] ); |
388 foreach ( $match_against as $match ) { |
393 foreach ( $match_against as $match ) { |
389 list( $match_type, $match_host ) = explode( ':', $match ); |
394 list( $match_type, $match_host ) = explode( ':', $match ); |
390 if ( $host_type == strtolower( trim( $match_type ) ) ) { // IP: or DNS: |
395 if ( strtolower( trim( $match_type ) ) === $host_type ) { // IP: or DNS: |
391 $certificate_hostnames[] = strtolower( trim( $match_host ) ); |
396 $certificate_hostnames[] = strtolower( trim( $match_host ) ); |
392 } |
397 } |
393 } |
398 } |
394 } elseif ( ! empty( $cert['subject']['CN'] ) ) { |
399 } elseif ( ! empty( $cert['subject']['CN'] ) ) { |
395 // Only use the CN when the certificate includes no subjectAltName extension. |
400 // Only use the CN when the certificate includes no subjectAltName extension. |
396 $certificate_hostnames[] = strtolower( $cert['subject']['CN'] ); |
401 $certificate_hostnames[] = strtolower( $cert['subject']['CN'] ); |
397 } |
402 } |
398 |
403 |
399 // Exact hostname/IP matches. |
404 // Exact hostname/IP matches. |
400 if ( in_array( strtolower( $host ), $certificate_hostnames ) ) { |
405 if ( in_array( strtolower( $host ), $certificate_hostnames, true ) ) { |
401 return true; |
406 return true; |
402 } |
407 } |
403 |
408 |
404 // IP's can't be wildcards, Stop processing. |
409 // IP's can't be wildcards, Stop processing. |
405 if ( 'ip' == $host_type ) { |
410 if ( 'ip' === $host_type ) { |
406 return false; |
411 return false; |
407 } |
412 } |
408 |
413 |
409 // Test to see if the domain is at least 2 deep for wildcard support. |
414 // Test to see if the domain is at least 2 deep for wildcard support. |
410 if ( substr_count( $host, '.' ) < 2 ) { |
415 if ( substr_count( $host, '.' ) < 2 ) { |