class-http.php 38.2 KB
Newer Older
godog's avatar
godog committed
1
2
<?php
/**
lechuck's avatar
lechuck committed
3
 * HTTP API: WP_Http class
godog's avatar
godog committed
4
5
6
7
8
9
 *
 * @package WordPress
 * @subpackage HTTP
 * @since 2.7.0
 */

lucha's avatar
lucha committed
10
if ( ! class_exists( 'Requests' ) ) {
agata's avatar
agata committed
11
	require ABSPATH . WPINC . '/class-requests.php';
lucha's avatar
lucha committed
12
13
14
15
16

	Requests::register_autoloader();
	Requests::set_certificate_path( ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
}

godog's avatar
godog committed
17
/**
lechuck's avatar
lechuck committed
18
 * Core class used for managing HTTP transports and making HTTP requests.
godog's avatar
godog committed
19
 *
lucha's avatar
lucha committed
20
21
22
 * This class is used to consistently make outgoing HTTP requests easy for developers
 * while still being compatible with the many PHP configurations under which
 * WordPress runs.
godog's avatar
godog committed
23
24
25
26
27
28
29
 *
 * Debugging includes several actions, which pass different variables for debugging the HTTP API.
 *
 * @since 2.7.0
 */
class WP_Http {

lechuck's avatar
lechuck committed
30
	// Aliases for HTTP response codes.
samba's avatar
samba committed
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
	const HTTP_CONTINUE       = 100;
	const SWITCHING_PROTOCOLS = 101;
	const PROCESSING          = 102;
	const EARLY_HINTS         = 103;

	const OK                            = 200;
	const CREATED                       = 201;
	const ACCEPTED                      = 202;
	const NON_AUTHORITATIVE_INFORMATION = 203;
	const NO_CONTENT                    = 204;
	const RESET_CONTENT                 = 205;
	const PARTIAL_CONTENT               = 206;
	const MULTI_STATUS                  = 207;
	const IM_USED                       = 226;

	const MULTIPLE_CHOICES   = 300;
	const MOVED_PERMANENTLY  = 301;
	const FOUND              = 302;
	const SEE_OTHER          = 303;
	const NOT_MODIFIED       = 304;
	const USE_PROXY          = 305;
	const RESERVED           = 306;
	const TEMPORARY_REDIRECT = 307;
	const PERMANENT_REDIRECT = 308;
lechuck's avatar
lechuck committed
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

	const BAD_REQUEST                     = 400;
	const UNAUTHORIZED                    = 401;
	const PAYMENT_REQUIRED                = 402;
	const FORBIDDEN                       = 403;
	const NOT_FOUND                       = 404;
	const METHOD_NOT_ALLOWED              = 405;
	const NOT_ACCEPTABLE                  = 406;
	const PROXY_AUTHENTICATION_REQUIRED   = 407;
	const REQUEST_TIMEOUT                 = 408;
	const CONFLICT                        = 409;
	const GONE                            = 410;
	const LENGTH_REQUIRED                 = 411;
	const PRECONDITION_FAILED             = 412;
	const REQUEST_ENTITY_TOO_LARGE        = 413;
	const REQUEST_URI_TOO_LONG            = 414;
	const UNSUPPORTED_MEDIA_TYPE          = 415;
	const REQUESTED_RANGE_NOT_SATISFIABLE = 416;
	const EXPECTATION_FAILED              = 417;
	const IM_A_TEAPOT                     = 418;
	const MISDIRECTED_REQUEST             = 421;
	const UNPROCESSABLE_ENTITY            = 422;
	const LOCKED                          = 423;
	const FAILED_DEPENDENCY               = 424;
	const UPGRADE_REQUIRED                = 426;
	const PRECONDITION_REQUIRED           = 428;
	const TOO_MANY_REQUESTS               = 429;
	const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
	const UNAVAILABLE_FOR_LEGAL_REASONS   = 451;

	const INTERNAL_SERVER_ERROR           = 500;
	const NOT_IMPLEMENTED                 = 501;
	const BAD_GATEWAY                     = 502;
	const SERVICE_UNAVAILABLE             = 503;
	const GATEWAY_TIMEOUT                 = 504;
	const HTTP_VERSION_NOT_SUPPORTED      = 505;
	const VARIANT_ALSO_NEGOTIATES         = 506;
	const INSUFFICIENT_STORAGE            = 507;
	const NOT_EXTENDED                    = 510;
	const NETWORK_AUTHENTICATION_REQUIRED = 511;

godog's avatar
godog committed
96
	/**
lucha's avatar
lucha committed
97
	 * Send an HTTP request to a URI.
godog's avatar
godog committed
98
	 *
lucha's avatar
lucha committed
99
100
	 * Please note: The only URI that are supported in the HTTP Transport implementation
	 * are the HTTP and HTTPS protocols.
godog's avatar
godog committed
101
102
103
	 *
	 * @since 2.7.0
	 *
lucha's avatar
lucha committed
104
105
106
107
	 * @param string       $url  The request URL.
	 * @param string|array $args {
	 *     Optional. Array or string of HTTP request arguments.
	 *
samba's avatar
samba committed
108
109
	 *     @type string       $method              Request method. Accepts 'GET', 'POST', 'HEAD', 'PUT', 'DELETE',
	 *                                             'TRACE', 'OPTIONS', or 'PATCH'.
lucha's avatar
lucha committed
110
111
	 *                                             Some transports technically allow others, but should not be
	 *                                             assumed. Default 'GET'.
agata's avatar
agata committed
112
	 *     @type float        $timeout             How long the connection should stay open in seconds. Default 5.
lucha's avatar
lucha committed
113
114
115
116
117
	 *     @type int          $redirection         Number of allowed redirects. Not supported by all transports
	 *                                             Default 5.
	 *     @type string       $httpversion         Version of the HTTP protocol to use. Accepts '1.0' and '1.1'.
	 *                                             Default '1.0'.
	 *     @type string       $user-agent          User-agent value sent.
lucha's avatar
lucha committed
118
	 *                                             Default 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ).
lucha's avatar
lucha committed
119
	 *     @type bool         $reject_unsafe_urls  Whether to pass URLs through wp_http_validate_url().
lucha's avatar
lucha committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	 *                                             Default false.
	 *     @type bool         $blocking            Whether the calling code requires the result of the request.
	 *                                             If set to false, the request will be sent to the remote server,
	 *                                             and processing returned to the calling code immediately, the caller
	 *                                             will know if the request succeeded or failed, but will not receive
	 *                                             any response from the remote server. Default true.
	 *     @type string|array $headers             Array or string of headers to send with the request.
	 *                                             Default empty array.
	 *     @type array        $cookies             List of cookies to send with the request. Default empty array.
	 *     @type string|array $body                Body to send with the request. Default null.
	 *     @type bool         $compress            Whether to compress the $body when sending the request.
	 *                                             Default false.
	 *     @type bool         $decompress          Whether to decompress a compressed response. If set to false and
	 *                                             compressed content is returned in the response anyway, it will
	 *                                             need to be separately decompressed. Default true.
	 *     @type bool         $sslverify           Whether to verify SSL for the request. Default true.
agata's avatar
agata committed
136
	 *     @type string       $sslcertificates     Absolute path to an SSL certificate .crt file.
lucha's avatar
lucha committed
137
138
139
140
141
142
143
144
145
146
147
	 *                                             Default ABSPATH . WPINC . '/certificates/ca-bundle.crt'.
	 *     @type bool         $stream              Whether to stream to a file. If set to true and no filename was
	 *                                             given, it will be droped it in the WP temp dir and its name will
	 *                                             be set using the basename of the URL. Default false.
	 *     @type string       $filename            Filename of the file to write to when streaming. $stream must be
	 *                                             set to true. Default null.
	 *     @type int          $limit_response_size Size in bytes to limit the response to. Default null.
	 *
	 * }
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'.
	 *                        A WP_Error instance upon error.
godog's avatar
godog committed
148
	 */
lucha's avatar
lucha committed
149
	public function request( $url, $args = array() ) {
godog's avatar
godog committed
150
		$defaults = array(
samba's avatar
samba committed
151
			'method'              => 'GET',
lechuck's avatar
lechuck committed
152
			/**
lucha's avatar
lucha committed
153
			 * Filters the timeout value for an HTTP request.
lechuck's avatar
lechuck committed
154
155
			 *
			 * @since 2.7.0
samba's avatar
samba committed
156
			 * @since 5.1.0 The `$url` parameter was added.
lechuck's avatar
lechuck committed
157
			 *
agata's avatar
agata committed
158
			 * @param float  $timeout_value Time in seconds until a request times out. Default 5.
samba's avatar
samba committed
159
			 * @param string $url           The request URL.
lechuck's avatar
lechuck committed
160
			 */
samba's avatar
samba committed
161
			'timeout'             => apply_filters( 'http_request_timeout', 5, $url ),
lechuck's avatar
lechuck committed
162
			/**
lucha's avatar
lucha committed
163
			 * Filters the number of redirects allowed during an HTTP request.
lechuck's avatar
lechuck committed
164
165
			 *
			 * @since 2.7.0
samba's avatar
samba committed
166
			 * @since 5.1.0 The `$url` parameter was added.
lechuck's avatar
lechuck committed
167
			 *
samba's avatar
samba committed
168
169
			 * @param int    $redirect_count Number of redirects allowed. Default 5.
			 * @param string $url            The request URL.
lechuck's avatar
lechuck committed
170
			 */
samba's avatar
samba committed
171
			'redirection'         => apply_filters( 'http_request_redirection_count', 5, $url ),
lechuck's avatar
lechuck committed
172
			/**
lucha's avatar
lucha committed
173
			 * Filters the version of the HTTP protocol used in a request.
lechuck's avatar
lechuck committed
174
175
			 *
			 * @since 2.7.0
samba's avatar
samba committed
176
			 * @since 5.1.0 The `$url` parameter was added.
lechuck's avatar
lechuck committed
177
			 *
samba's avatar
samba committed
178
179
			 * @param string $version Version of HTTP used. Accepts '1.0' and '1.1'. Default '1.0'.
			 * @param string $url     The request URL.
lechuck's avatar
lechuck committed
180
			 */
samba's avatar
samba committed
181
			'httpversion'         => apply_filters( 'http_request_version', '1.0', $url ),
lechuck's avatar
lechuck committed
182
			/**
lucha's avatar
lucha committed
183
			 * Filters the user agent value sent with an HTTP request.
lechuck's avatar
lechuck committed
184
185
			 *
			 * @since 2.7.0
samba's avatar
samba committed
186
			 * @since 5.1.0 The `$url` parameter was added.
lechuck's avatar
lechuck committed
187
188
			 *
			 * @param string $user_agent WordPress user agent string.
samba's avatar
samba committed
189
			 * @param string $url        The request URL.
lechuck's avatar
lechuck committed
190
			 */
191
			'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; http://wordpress.org' ),
lechuck's avatar
lechuck committed
192
			/**
lucha's avatar
lucha committed
193
			 * Filters whether to pass URLs through wp_http_validate_url() in an HTTP request.
lechuck's avatar
lechuck committed
194
195
			 *
			 * @since 3.6.0
samba's avatar
samba committed
196
			 * @since 5.1.0 The `$url` parameter was added.
lechuck's avatar
lechuck committed
197
			 *
samba's avatar
samba committed
198
199
			 * @param bool   $pass_url Whether to pass URLs through wp_http_validate_url(). Default false.
			 * @param string $url      The request URL.
lechuck's avatar
lechuck committed
200
			 */
samba's avatar
samba committed
201
202
203
204
205
206
207
208
209
210
211
			'reject_unsafe_urls'  => apply_filters( 'http_request_reject_unsafe_urls', false, $url ),
			'blocking'            => true,
			'headers'             => array(),
			'cookies'             => array(),
			'body'                => null,
			'compress'            => false,
			'decompress'          => true,
			'sslverify'           => true,
			'sslcertificates'     => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
			'stream'              => false,
			'filename'            => null,
lechuck's avatar
lechuck committed
212
			'limit_response_size' => null,
godog's avatar
godog committed
213
214
		);

shammash's avatar
shammash committed
215
216
217
		// Pre-parse for the HEAD checks.
		$args = wp_parse_args( $args );

agata's avatar
agata committed
218
		// By default, HEAD requests do not cause redirections.
samba's avatar
samba committed
219
		if ( isset( $args['method'] ) && 'HEAD' == $args['method'] ) {
shammash's avatar
shammash committed
220
			$defaults['redirection'] = 0;
samba's avatar
samba committed
221
		}
shammash's avatar
shammash committed
222

agata's avatar
agata committed
223
		$parsed_args = wp_parse_args( $args, $defaults );
lechuck's avatar
lechuck committed
224
		/**
lucha's avatar
lucha committed
225
		 * Filters the arguments used in an HTTP request.
lechuck's avatar
lechuck committed
226
227
228
		 *
		 * @since 2.7.0
		 *
agata's avatar
agata committed
229
230
		 * @param array  $parsed_args An array of HTTP request arguments.
		 * @param string $url         The request URL.
lechuck's avatar
lechuck committed
231
		 */
agata's avatar
agata committed
232
		$parsed_args = apply_filters( 'http_request_args', $parsed_args, $url );
godog's avatar
godog committed
233

lechuck's avatar
lechuck committed
234
		// The transports decrement this, store a copy of the original value for loop purposes.
agata's avatar
agata committed
235
236
		if ( ! isset( $parsed_args['_redirection'] ) ) {
			$parsed_args['_redirection'] = $parsed_args['redirection'];
samba's avatar
samba committed
237
		}
shammash's avatar
shammash committed
238

lechuck's avatar
lechuck committed
239
		/**
lucha's avatar
lucha committed
240
		 * Filters whether to preempt an HTTP request's return value.
lechuck's avatar
lechuck committed
241
242
243
		 *
		 * Returning a non-false value from the filter will short-circuit the HTTP request and return
		 * early with that value. A filter should return either:
lechuck's avatar
lechuck committed
244
		 *
lechuck's avatar
lechuck committed
245
246
247
248
249
		 *  - An array containing 'headers', 'body', 'response', 'cookies', and 'filename' elements
		 *  - A WP_Error instance
		 *  - boolean false (to avoid short-circuiting the response)
		 *
		 * Returning any other value may result in unexpected behaviour.
lechuck's avatar
lechuck committed
250
251
252
		 *
		 * @since 2.9.0
		 *
agata's avatar
agata committed
253
254
255
		 * @param false|array|WP_Error $preempt     Whether to preempt an HTTP request's return value. Default false.
		 * @param array                $parsed_args HTTP request arguments.
		 * @param string               $url         The request URL.
lechuck's avatar
lechuck committed
256
		 */
agata's avatar
agata committed
257
		$pre = apply_filters( 'pre_http_request', false, $parsed_args, $url );
lechuck's avatar
lechuck committed
258

samba's avatar
samba committed
259
		if ( false !== $pre ) {
godog's avatar
godog committed
260
			return $pre;
samba's avatar
samba committed
261
		}
godog's avatar
godog committed
262

lechuck's avatar
lechuck committed
263
		if ( function_exists( 'wp_kses_bad_protocol' ) ) {
agata's avatar
agata committed
264
			if ( $parsed_args['reject_unsafe_urls'] ) {
lechuck's avatar
lechuck committed
265
				$url = wp_http_validate_url( $url );
lucha's avatar
lucha committed
266
			}
ale's avatar
ale committed
267
268
269
			if ( $url ) {
				$url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) );
			}
lechuck's avatar
lechuck committed
270
		}
lechuck's avatar
lechuck committed
271
272

		$arrURL = @parse_url( $url );
godog's avatar
godog committed
273

lucha's avatar
lucha committed
274
		if ( empty( $url ) || empty( $arrURL['scheme'] ) ) {
agata's avatar
agata committed
275
276
277
278
			$response = new WP_Error( 'http_request_failed', __( 'A valid URL was not provided.' ) );
			/** This action is documented in wp-includes/class-http.php */
			do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
			return $response;
lucha's avatar
lucha committed
279
		}
godog's avatar
godog committed
280

lucha's avatar
lucha committed
281
		if ( $this->block_request( $url ) ) {
agata's avatar
agata committed
282
283
284
285
			$response = new WP_Error( 'http_request_not_executed', __( 'User has blocked requests through HTTP.' ) );
			/** This action is documented in wp-includes/class-http.php */
			do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
			return $response;
lechuck's avatar
lechuck committed
286
		}
shammash's avatar
shammash committed
287

lucha's avatar
lucha committed
288
		// If we are streaming to a file but no filename was given drop it in the WP temp dir
agata's avatar
agata committed
289
		// and pick its name using the basename of the $url.
agata's avatar
agata committed
290
291
292
		if ( $parsed_args['stream'] ) {
			if ( empty( $parsed_args['filename'] ) ) {
				$parsed_args['filename'] = get_temp_dir() . basename( $url );
lucha's avatar
lucha committed
293
294
			}

agata's avatar
agata committed
295
296
			// Force some settings if we are streaming to a file and check for existence
			// and perms of destination directory.
agata's avatar
agata committed
297
298
299
300
301
302
			$parsed_args['blocking'] = true;
			if ( ! wp_is_writable( dirname( $parsed_args['filename'] ) ) ) {
				$response = new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
				/** This action is documented in wp-includes/class-http.php */
				do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
				return $response;
lucha's avatar
lucha committed
303
			}
shammash's avatar
shammash committed
304
305
		}

agata's avatar
agata committed
306
307
		if ( is_null( $parsed_args['headers'] ) ) {
			$parsed_args['headers'] = array();
lucha's avatar
lucha committed
308
		}
godog's avatar
godog committed
309

lucha's avatar
lucha committed
310
		// WP allows passing in headers as a string, weirdly.
agata's avatar
agata committed
311
312
313
		if ( ! is_array( $parsed_args['headers'] ) ) {
			$processedHeaders       = WP_Http::processHeaders( $parsed_args['headers'] );
			$parsed_args['headers'] = $processedHeaders['headers'];
godog's avatar
godog committed
314
315
		}

agata's avatar
agata committed
316
		// Setup arguments.
agata's avatar
agata committed
317
318
319
		$headers = $parsed_args['headers'];
		$data    = $parsed_args['body'];
		$type    = $parsed_args['method'];
lucha's avatar
lucha committed
320
		$options = array(
agata's avatar
agata committed
321
322
323
324
			'timeout'   => $parsed_args['timeout'],
			'useragent' => $parsed_args['user-agent'],
			'blocking'  => $parsed_args['blocking'],
			'hooks'     => new WP_HTTP_Requests_Hooks( $url, $parsed_args ),
lucha's avatar
lucha committed
325
326
327
328
		);

		// Ensure redirects follow browser behaviour.
		$options['hooks']->register( 'requests.before_redirect', array( get_class(), 'browser_redirect_compatibility' ) );
godog's avatar
godog committed
329

lucha's avatar
lucha committed
330
		// Validate redirected URLs.
agata's avatar
agata committed
331
		if ( function_exists( 'wp_kses_bad_protocol' ) && $parsed_args['reject_unsafe_urls'] ) {
lucha's avatar
lucha committed
332
333
334
			$options['hooks']->register( 'requests.before_redirect', array( get_class(), 'validate_redirects' ) );
		}

agata's avatar
agata committed
335
336
		if ( $parsed_args['stream'] ) {
			$options['filename'] = $parsed_args['filename'];
lucha's avatar
lucha committed
337
		}
agata's avatar
agata committed
338
		if ( empty( $parsed_args['redirection'] ) ) {
lucha's avatar
lucha committed
339
340
			$options['follow_redirects'] = false;
		} else {
agata's avatar
agata committed
341
			$options['redirects'] = $parsed_args['redirection'];
godog's avatar
godog committed
342
343
		}

agata's avatar
agata committed
344
		// Use byte limit, if we can.
agata's avatar
agata committed
345
346
		if ( isset( $parsed_args['limit_response_size'] ) ) {
			$options['max_bytes'] = $parsed_args['limit_response_size'];
lucha's avatar
lucha committed
347
348
		}

lucha's avatar
lucha committed
349
		// If we've got cookies, use and convert them to Requests_Cookie.
agata's avatar
agata committed
350
351
		if ( ! empty( $parsed_args['cookies'] ) ) {
			$options['cookies'] = WP_Http::normalize_cookies( $parsed_args['cookies'] );
lucha's avatar
lucha committed
352
		}
godog's avatar
godog committed
353

agata's avatar
agata committed
354
		// SSL certificate handling.
agata's avatar
agata committed
355
		if ( ! $parsed_args['sslverify'] ) {
samba's avatar
samba committed
356
			$options['verify']     = false;
lucha's avatar
lucha committed
357
			$options['verifyname'] = false;
lucha's avatar
lucha committed
358
		} else {
agata's avatar
agata committed
359
			$options['verify'] = $parsed_args['sslcertificates'];
lucha's avatar
lucha committed
360
		}
lucha's avatar
lucha committed
361

lucha's avatar
lucha committed
362
363
364
		// All non-GET/HEAD requests should put the arguments in the form body.
		if ( 'HEAD' !== $type && 'GET' !== $type ) {
			$options['data_format'] = 'body';
lechuck's avatar
lechuck committed
365
		}
godog's avatar
godog committed
366

lucha's avatar
lucha committed
367
368
369
370
		/**
		 * Filters whether SSL should be verified for non-local requests.
		 *
		 * @since 2.8.0
samba's avatar
samba committed
371
		 * @since 5.1.0 The `$url` parameter was added.
lucha's avatar
lucha committed
372
		 *
samba's avatar
samba committed
373
374
		 * @param bool   $ssl_verify Whether to verify the SSL connection. Default true.
		 * @param string $url        The request URL.
lucha's avatar
lucha committed
375
		 */
samba's avatar
samba committed
376
		$options['verify'] = apply_filters( 'https_ssl_verify', $options['verify'], $url );
lucha's avatar
lucha committed
377
378
379
380
381

		// Check for proxies.
		$proxy = new WP_HTTP_Proxy();
		if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
			$options['proxy'] = new Requests_Proxy_HTTP( $proxy->host() . ':' . $proxy->port() );
lechuck's avatar
lechuck committed
382

lucha's avatar
lucha committed
383
384
			if ( $proxy->use_authentication() ) {
				$options['proxy']->use_authentication = true;
samba's avatar
samba committed
385
386
				$options['proxy']->user               = $proxy->username();
				$options['proxy']->pass               = $proxy->password();
godog's avatar
godog committed
387
			}
lucha's avatar
lucha committed
388
		}
godog's avatar
godog committed
389

agata's avatar
agata committed
390
		// Avoid issues where mbstring.func_overload is enabled.
lucha's avatar
lucha committed
391
		mbstring_binary_safe_encoding();
lechuck's avatar
lechuck committed
392

lucha's avatar
lucha committed
393
394
		try {
			$requests_response = Requests::request( $url, $headers, $data, $type, $options );
godog's avatar
godog committed
395

agata's avatar
agata committed
396
			// Convert the response into an array.
agata's avatar
agata committed
397
			$http_response = new WP_HTTP_Requests_Response( $requests_response, $parsed_args['filename'] );
samba's avatar
samba committed
398
			$response      = $http_response->to_array();
lucha's avatar
lucha committed
399

lucha's avatar
lucha committed
400
401
			// Add the original object to the array.
			$response['http_response'] = $http_response;
samba's avatar
samba committed
402
		} catch ( Requests_Exception $e ) {
lucha's avatar
lucha committed
403
404
			$response = new WP_Error( 'http_request_failed', $e->getMessage() );
		}
lucha's avatar
lucha committed
405

lucha's avatar
lucha committed
406
407
		reset_mbstring_encoding();

lucha's avatar
lucha committed
408
409
410
411
412
		/**
		 * Fires after an HTTP API response is received and before the response is returned.
		 *
		 * @since 2.8.0
		 *
agata's avatar
agata committed
413
414
415
416
417
		 * @param array|WP_Error $response    HTTP response or WP_Error object.
		 * @param string         $context     Context under which the hook is fired.
		 * @param string         $class       HTTP transport used.
		 * @param array          $parsed_args HTTP request arguments.
		 * @param string         $url         The request URL.
lucha's avatar
lucha committed
418
		 */
agata's avatar
agata committed
419
		do_action( 'http_api_debug', $response, 'response', 'Requests', $parsed_args, $url );
lucha's avatar
lucha committed
420
		if ( is_wp_error( $response ) ) {
lucha's avatar
lucha committed
421
			return $response;
lucha's avatar
lucha committed
422
		}
lucha's avatar
lucha committed
423

agata's avatar
agata committed
424
		if ( ! $parsed_args['blocking'] ) {
lucha's avatar
lucha committed
425
			return array(
samba's avatar
samba committed
426
427
428
429
				'headers'       => array(),
				'body'          => '',
				'response'      => array(
					'code'    => false,
lucha's avatar
lucha committed
430
431
					'message' => false,
				),
samba's avatar
samba committed
432
				'cookies'       => array(),
lucha's avatar
lucha committed
433
434
435
436
437
438
439
440
441
				'http_response' => null,
			);
		}

		/**
		 * Filters the HTTP API response immediately before the response is returned.
		 *
		 * @since 2.9.0
		 *
agata's avatar
agata committed
442
443
444
		 * @param array  $response    HTTP response.
		 * @param array  $parsed_args HTTP request arguments.
		 * @param string $url         The request URL.
lucha's avatar
lucha committed
445
		 */
agata's avatar
agata committed
446
		return apply_filters( 'http_response', $response, $parsed_args, $url );
lucha's avatar
lucha committed
447
448
449
450
451
452
453
	}

	/**
	 * Normalizes cookies for using in Requests.
	 *
	 * @since 4.6.0
	 *
samba's avatar
samba committed
454
	 * @param array $cookies Array of cookies to send with the request.
lucha's avatar
lucha committed
455
456
457
458
459
460
461
	 * @return Requests_Cookie_Jar Cookie holder object.
	 */
	public static function normalize_cookies( $cookies ) {
		$cookie_jar = new Requests_Cookie_Jar();

		foreach ( $cookies as $name => $value ) {
			if ( $value instanceof WP_Http_Cookie ) {
agata's avatar
agata committed
462
				$cookie_jar[ $value->name ] = new Requests_Cookie( $value->name, $value->value, $value->get_attributes(), array( 'host-only' => $value->host_only ) );
lucha's avatar
lucha committed
463
464
			} elseif ( is_scalar( $value ) ) {
				$cookie_jar[ $name ] = new Requests_Cookie( $name, $value );
lucha's avatar
lucha committed
465
466
467
			}
		}

lucha's avatar
lucha committed
468
469
470
471
472
473
474
475
476
477
478
479
480
481
		return $cookie_jar;
	}

	/**
	 * Match redirect behaviour to browser handling.
	 *
	 * Changes 302 redirects from POST to GET to match browser handling. Per
	 * RFC 7231, user agents can deviate from the strict reading of the
	 * specification for compatibility purposes.
	 *
	 * @since 4.6.0
	 *
	 * @param string            $location URL to redirect to.
	 * @param array             $headers  Headers for the redirect.
lucha's avatar
lucha committed
482
	 * @param string|array      $data     Body to send with the request.
lucha's avatar
lucha committed
483
484
485
486
	 * @param array             $options  Redirect request options.
	 * @param Requests_Response $original Response object.
	 */
	public static function browser_redirect_compatibility( $location, $headers, $data, &$options, $original ) {
agata's avatar
agata committed
487
488
		// Browser compatibility.
		if ( 302 === $original->status_code ) {
lucha's avatar
lucha committed
489
490
			$options['type'] = Requests::GET;
		}
shammash's avatar
shammash committed
491
492
	}

lucha's avatar
lucha committed
493
494
495
496
497
	/**
	 * Validate redirected URLs.
	 *
	 * @since 4.7.5
	 *
agata's avatar
agata committed
498
	 * @throws Requests_Exception On unsuccessful URL validation.
lucha's avatar
lucha committed
499
500
501
502
	 * @param string $location URL to redirect to.
	 */
	public static function validate_redirects( $location ) {
		if ( ! wp_http_validate_url( $location ) ) {
samba's avatar
samba committed
503
			throw new Requests_Exception( __( 'A valid URL was not provided.' ), 'wp_http.redirect_failed_validation' );
lucha's avatar
lucha committed
504
505
506
		}
	}

shammash's avatar
shammash committed
507
508
509
510
511
	/**
	 * Tests which transports are capable of supporting the request.
	 *
	 * @since 3.2.0
	 *
agata's avatar
agata committed
512
513
	 * @param array $args Request arguments.
	 * @param string $url URL to Request.
shammash's avatar
shammash committed
514
	 *
agata's avatar
agata committed
515
516
	 * @return string|false Class name for the first transport that claims to support the request.
	 *                      False if no transport claims to support the request.
shammash's avatar
shammash committed
517
518
	 */
	public function _get_first_available_transport( $args, $url = null ) {
lechuck's avatar
lechuck committed
519
520
		$transports = array( 'curl', 'streams' );

lechuck's avatar
lechuck committed
521
		/**
lucha's avatar
lucha committed
522
		 * Filters which HTTP transports are available and in what order.
lechuck's avatar
lechuck committed
523
524
525
		 *
		 * @since 3.7.0
		 *
agata's avatar
agata committed
526
527
528
529
		 * @param string[] $transports Array of HTTP transports to check. Default array contains
		 *                             'curl' and 'streams', in that order.
		 * @param array    $args       HTTP request arguments.
		 * @param string   $url        The URL to request.
lechuck's avatar
lechuck committed
530
		 */
lechuck's avatar
lechuck committed
531
		$request_order = apply_filters( 'http_api_transports', $transports, $args, $url );
godog's avatar
godog committed
532

lucha's avatar
lucha committed
533
		// Loop over each transport on each HTTP request looking for one which will serve this request's needs.
shammash's avatar
shammash committed
534
		foreach ( $request_order as $transport ) {
lechuck's avatar
lechuck committed
535
536
537
538
			if ( in_array( $transport, $transports ) ) {
				$transport = ucfirst( $transport );
			}
			$class = 'WP_Http_' . $transport;
godog's avatar
godog committed
539

lucha's avatar
lucha committed
540
			// Check to see if this transport is a possibility, calls the transport statically.
samba's avatar
samba committed
541
			if ( ! call_user_func( array( $class, 'test' ), $args, $url ) ) {
shammash's avatar
shammash committed
542
				continue;
samba's avatar
samba committed
543
			}
godog's avatar
godog committed
544

shammash's avatar
shammash committed
545
			return $class;
godog's avatar
godog committed
546
547
		}

shammash's avatar
shammash committed
548
549
550
551
552
553
		return false;
	}

	/**
	 * Dispatches a HTTP request to a supporting transport.
	 *
root's avatar
root committed
554
	 * Tests each transport in order to find a transport which matches the request arguments.
shammash's avatar
shammash committed
555
556
	 * Also caches the transport instance to be used later.
	 *
lucha's avatar
lucha committed
557
	 * The order for requests is cURL, and then PHP Streams.
shammash's avatar
shammash committed
558
559
	 *
	 * @since 3.2.0
samba's avatar
samba committed
560
561
	 * @deprecated 5.1.0 Use WP_Http::request()
	 * @see WP_Http::request()
shammash's avatar
shammash committed
562
	 *
agata's avatar
agata committed
563
564
565
566
567
568
	 * @staticvar array $transports
	 *
	 * @param string $url URL to Request.
	 * @param array $args Request arguments.
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'.
	 *                        A WP_Error instance upon error.
shammash's avatar
shammash committed
569
570
571
572
573
	 */
	private function _dispatch_request( $url, $args ) {
		static $transports = array();

		$class = $this->_get_first_available_transport( $args, $url );
samba's avatar
samba committed
574
		if ( ! $class ) {
shammash's avatar
shammash committed
575
			return new WP_Error( 'http_failure', __( 'There are no HTTP transports available which can complete the requested request.' ) );
samba's avatar
samba committed
576
		}
shammash's avatar
shammash committed
577
578

		// Transport claims to support request, instantiate it and give it a whirl.
samba's avatar
samba committed
579
580
581
		if ( empty( $transports[ $class ] ) ) {
			$transports[ $class ] = new $class;
		}
shammash's avatar
shammash committed
582

samba's avatar
samba committed
583
		$response = $transports[ $class ]->request( $url, $args );
shammash's avatar
shammash committed
584

lucha's avatar
lucha committed
585
		/** This action is documented in wp-includes/class-http.php */
root's avatar
root committed
586
		do_action( 'http_api_debug', $response, 'response', $class, $args, $url );
shammash's avatar
shammash committed
587

samba's avatar
samba committed
588
		if ( is_wp_error( $response ) ) {
shammash's avatar
shammash committed
589
			return $response;
samba's avatar
samba committed
590
		}
shammash's avatar
shammash committed
591

samba's avatar
samba committed
592
		/** This filter is documented in wp-includes/class-http.php */
shammash's avatar
shammash committed
593
		return apply_filters( 'http_response', $response, $args, $url );
godog's avatar
godog committed
594
595
596
597
598
599
600
601
602
	}

	/**
	 * Uses the POST HTTP method.
	 *
	 * Used for sending data that is expected to be in the body.
	 *
	 * @since 2.7.0
	 *
lucha's avatar
lucha committed
603
	 * @param string       $url  The request URL.
lechuck's avatar
lechuck committed
604
	 * @param string|array $args Optional. Override the defaults.
agata's avatar
agata committed
605
606
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'.
	 *                        A WP_Error instance upon error.
godog's avatar
godog committed
607
	 */
samba's avatar
samba committed
608
	public function post( $url, $args = array() ) {
agata's avatar
agata committed
609
610
611
		$defaults    = array( 'method' => 'POST' );
		$parsed_args = wp_parse_args( $args, $defaults );
		return $this->request( $url, $parsed_args );
godog's avatar
godog committed
612
613
614
615
616
617
618
619
620
	}

	/**
	 * Uses the GET HTTP method.
	 *
	 * Used for sending data that is expected to be in the body.
	 *
	 * @since 2.7.0
	 *
lucha's avatar
lucha committed
621
622
	 * @param string $url The request URL.
	 * @param string|array $args Optional. Override the defaults.
agata's avatar
agata committed
623
624
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'.
	 *                        A WP_Error instance upon error.
godog's avatar
godog committed
625
	 */
samba's avatar
samba committed
626
	public function get( $url, $args = array() ) {
agata's avatar
agata committed
627
628
629
		$defaults    = array( 'method' => 'GET' );
		$parsed_args = wp_parse_args( $args, $defaults );
		return $this->request( $url, $parsed_args );
godog's avatar
godog committed
630
631
632
633
634
635
636
637
638
	}

	/**
	 * Uses the HEAD HTTP method.
	 *
	 * Used for sending data that is expected to be in the body.
	 *
	 * @since 2.7.0
	 *
lucha's avatar
lucha committed
639
640
	 * @param string $url The request URL.
	 * @param string|array $args Optional. Override the defaults.
agata's avatar
agata committed
641
642
	 * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'.
	 *                        A WP_Error instance upon error.
godog's avatar
godog committed
643
	 */
samba's avatar
samba committed
644
	public function head( $url, $args = array() ) {
agata's avatar
agata committed
645
646
647
		$defaults    = array( 'method' => 'HEAD' );
		$parsed_args = wp_parse_args( $args, $defaults );
		return $this->request( $url, $parsed_args );
godog's avatar
godog committed
648
649
650
651
652
653
654
	}

	/**
	 * Parses the responses and splits the parts into headers and body.
	 *
	 * @since 2.7.0
	 *
agata's avatar
agata committed
655
656
657
658
659
660
661
	 * @param string $strResponse The full response string.
	 * @return array {
	 *     Array with response headers and body.
	 *
	 *     @type string $headers HTTP response headers.
	 *     @type string $body    HTTP response body.
	 * }
godog's avatar
godog committed
662
	 */
agata's avatar
agata committed
663
	public static function processResponse( $strResponse ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
samba's avatar
samba committed
664
		$res = explode( "\r\n\r\n", $strResponse, 2 );
godog's avatar
godog committed
665

samba's avatar
samba committed
666
667
668
669
		return array(
			'headers' => $res[0],
			'body'    => isset( $res[1] ) ? $res[1] : '',
		);
godog's avatar
godog committed
670
671
672
673
674
	}

	/**
	 * Transform header string into an array.
	 *
agata's avatar
agata committed
675
	 * If an array is given, then it is assumed to be raw header data with numeric keys with the
godog's avatar
godog committed
676
677
678
679
680
	 * headers as the values. No headers must be passed that were already processed.
	 *
	 * @since 2.7.0
	 *
	 * @param string|array $headers
agata's avatar
agata committed
681
	 * @param string $url The URL that was requested.
godog's avatar
godog committed
682
	 * @return array Processed string headers. If duplicate headers are encountered,
agata's avatar
agata committed
683
	 *               then a numbered array is returned as the value of that header-key.
godog's avatar
godog committed
684
	 */
agata's avatar
agata committed
685
	public static function processHeaders( $headers, $url = '' ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
lucha's avatar
lucha committed
686
		// Split headers, one per array element.
samba's avatar
samba committed
687
		if ( is_string( $headers ) ) {
lucha's avatar
lucha committed
688
			// Tolerate line terminator: CRLF = LF (RFC 2616 19.3).
samba's avatar
samba committed
689
			$headers = str_replace( "\r\n", "\n", $headers );
lucha's avatar
lucha committed
690
691
692
693
			/*
			 * Unfold folded header fields. LWS = [CRLF] 1*( SP | HT ) <US-ASCII SP, space (32)>,
			 * <US-ASCII HT, horizontal-tab (9)> (RFC 2616 2.2).
			 */
samba's avatar
samba committed
694
			$headers = preg_replace( '/\n[ \t]/', ' ', $headers );
lucha's avatar
lucha committed
695
			// Create the headers array.
samba's avatar
samba committed
696
			$headers = explode( "\n", $headers );
godog's avatar
godog committed
697
698
		}

samba's avatar
samba committed
699
700
701
702
		$response = array(
			'code'    => 0,
			'message' => '',
		);
godog's avatar
godog committed
703

lucha's avatar
lucha committed
704
705
706
707
		/*
		 * If a redirection has taken place, The headers for each page request may have been passed.
		 * In this case, determine the final HTTP header and parse from there.
		 */
samba's avatar
samba committed
708
709
710
		for ( $i = count( $headers ) - 1; $i >= 0; $i-- ) {
			if ( ! empty( $headers[ $i ] ) && false === strpos( $headers[ $i ], ':' ) ) {
				$headers = array_splice( $headers, $i );
godog's avatar
godog committed
711
712
713
714
				break;
			}
		}

samba's avatar
samba committed
715
		$cookies    = array();
godog's avatar
godog committed
716
		$newheaders = array();
shammash's avatar
shammash committed
717
		foreach ( (array) $headers as $tempheader ) {
samba's avatar
samba committed
718
			if ( empty( $tempheader ) ) {
godog's avatar
godog committed
719
				continue;
samba's avatar
samba committed
720
			}
godog's avatar
godog committed
721

samba's avatar
samba committed
722
723
			if ( false === strpos( $tempheader, ':' ) ) {
				$stack   = explode( ' ', $tempheader, 3 );
shammash's avatar
shammash committed
724
725
				$stack[] = '';
				list( , $response['code'], $response['message']) = $stack;
godog's avatar
godog committed
726
727
728
				continue;
			}

samba's avatar
samba committed
729
			list($key, $value) = explode( ':', $tempheader, 2 );
godog's avatar
godog committed
730

samba's avatar
samba committed
731
			$key   = strtolower( $key );
lechuck's avatar
lechuck committed
732
733
734
			$value = trim( $value );

			if ( isset( $newheaders[ $key ] ) ) {
samba's avatar
samba committed
735
736
737
				if ( ! is_array( $newheaders[ $key ] ) ) {
					$newheaders[ $key ] = array( $newheaders[ $key ] );
				}
lechuck's avatar
lechuck committed
738
739
740
				$newheaders[ $key ][] = $value;
			} else {
				$newheaders[ $key ] = $value;
godog's avatar
godog committed
741
			}
samba's avatar
samba committed
742
			if ( 'set-cookie' == $key ) {
lucha's avatar
lucha committed
743
				$cookies[] = new WP_Http_Cookie( $value, $url );
samba's avatar
samba committed
744
			}
godog's avatar
godog committed
745
746
		}

agata's avatar
agata committed
747
		// Cast the Response Code to an int.
lechuck's avatar
lechuck committed
748
749
		$response['code'] = intval( $response['code'] );

samba's avatar
samba committed
750
751
752
753
754
		return array(
			'response' => $response,
			'headers'  => $newheaders,
			'cookies'  => $cookies,
		);
godog's avatar
godog committed
755
756
757
758
759
	}

	/**
	 * Takes the arguments for a ::request() and checks for the cookie array.
	 *
lucha's avatar
lucha committed
760
761
762
	 * If it's found, then it upgrades any basic name => value pairs to WP_Http_Cookie instances,
	 * which are each parsed into strings and added to the Cookie: header (within the arguments array).
	 * Edits the array by reference.
godog's avatar
godog committed
763
	 *
lucha's avatar
lucha committed
764
	 * @since 2.8.0
godog's avatar
godog committed
765
766
767
	 *
	 * @param array $r Full array of args passed into ::request()
	 */
agata's avatar
agata committed
768
	public static function buildCookieHeader( &$r ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
samba's avatar
samba committed
769
		if ( ! empty( $r['cookies'] ) ) {
lucha's avatar
lucha committed
770
			// Upgrade any name => value cookie pairs to WP_HTTP_Cookie instances.
lucha's avatar
lucha committed
771
			foreach ( $r['cookies'] as $name => $value ) {
samba's avatar
samba committed
772
773
774
775
776
777
778
779
				if ( ! is_object( $value ) ) {
					$r['cookies'][ $name ] = new WP_Http_Cookie(
						array(
							'name'  => $name,
							'value' => $value,
						)
					);
				}
lucha's avatar
lucha committed
780
781
			}

godog's avatar
godog committed
782
783
784
785
			$cookies_header = '';
			foreach ( (array) $r['cookies'] as $cookie ) {
				$cookies_header .= $cookie->getHeaderValue() . '; ';
			}
lucha's avatar
lucha committed
786

samba's avatar
samba committed
787
			$cookies_header         = substr( $cookies_header, 0, -2 );
godog's avatar
godog committed
788
789
790
791
792
793
794
			$r['headers']['cookie'] = $cookies_header;
		}
	}

	/**
	 * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
	 *
lucha's avatar
lucha committed
795
	 * Based off the HTTP http_encoding_dechunk function.
godog's avatar
godog committed
796
	 *
lucha's avatar
lucha committed
797
	 * @link https://tools.ietf.org/html/rfc2616#section-19.4.6 Process for chunked decoding.
lechuck's avatar
lechuck committed
798
	 *
godog's avatar
godog committed
799
800
	 * @since 2.7.0
	 *
agata's avatar
agata committed
801
	 * @param string $body Body content.
godog's avatar
godog committed
802
803
	 * @return string Chunked decoded body on success or raw body on failure.
	 */
agata's avatar
agata committed
804
	public static function chunkTransferDecode( $body ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
lucha's avatar
lucha committed
805
		// The body is not chunked encoded or is malformed.
samba's avatar
samba committed
806
		if ( ! preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', trim( $body ) ) ) {
godog's avatar
godog committed
807
			return $body;
samba's avatar
samba committed
808
		}
godog's avatar
godog committed
809

lucha's avatar
lucha committed
810
		$parsed_body = '';
lucha's avatar
lucha committed
811
812
813

		// We'll be altering $body, so need a backup in case of error.
		$body_original = $body;
godog's avatar
godog committed
814
815

		while ( true ) {
lucha's avatar
lucha committed
816
			$has_chunk = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $body, $match );
samba's avatar
samba committed
817
			if ( ! $has_chunk || empty( $match[1] ) ) {
lucha's avatar
lucha committed
818
				return $body_original;
samba's avatar
samba committed
819
			}
godog's avatar
godog committed
820

samba's avatar
samba committed
821
			$length       = hexdec( $match[1] );
lucha's avatar
lucha committed
822
			$chunk_length = strlen( $match[0] );
godog's avatar
godog committed
823

lucha's avatar
lucha committed
824
			// Parse out the chunk of data.
lucha's avatar
lucha committed
825
			$parsed_body .= substr( $body, $chunk_length, $length );
godog's avatar
godog committed
826

lucha's avatar
lucha committed
827
			// Remove the chunk from the raw data.
lucha's avatar
lucha committed
828
			$body = substr( $body, $length + $chunk_length );
godog's avatar
godog committed
829

lucha's avatar
lucha committed
830
			// End of the document.
samba's avatar
samba committed
831
			if ( '0' === trim( $body ) ) {
lucha's avatar
lucha committed
832
				return $parsed_body;
samba's avatar
samba committed
833
			}
godog's avatar
godog committed
834
835
836
837
		}
	}

	/**
agata's avatar
agata committed
838
	 * Determines whether an HTTP API request to the given URL should be blocked.
godog's avatar
godog committed
839
840
	 *
	 * Those who are behind a proxy and want to prevent access to certain hosts may do so. This will
agata's avatar
agata committed
841
	 * prevent plugins from working and core functionality, if you don't include `api.wordpress.org`.
godog's avatar
godog committed
842
	 *
agata's avatar
agata committed
843
	 * You block external URL requests by defining `WP_HTTP_BLOCK_EXTERNAL` as true in your `wp-config.php`
lechuck's avatar
lechuck committed
844
	 * file and this will only allow localhost and your site to make requests. The constant
agata's avatar
agata committed
845
846
847
	 * `WP_ACCESSIBLE_HOSTS` will allow additional hosts to go through for requests. The format of the
	 * `WP_ACCESSIBLE_HOSTS` constant is a comma separated list of hostnames to allow, wildcard domains
	 * are supported, eg `*.wordpress.org` will allow for all subdomains of `wordpress.org` to be contacted.
godog's avatar
godog committed
848
849
	 *
	 * @since 2.8.0
lechuck's avatar
lechuck committed
850
851
	 * @link https://core.trac.wordpress.org/ticket/8927 Allow preventing external requests.
	 * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_ACCESSIBLE_HOSTS
godog's avatar
godog committed
852
	 *
lechuck's avatar
lechuck committed
853
854
855
	 * @staticvar array|null $accessible_hosts
	 * @staticvar array      $wildcard_regex
	 *
godog's avatar
godog committed
856
857
858
	 * @param string $uri URI of url.
	 * @return bool True to block, false to allow.
	 */
samba's avatar
samba committed
859
	public function block_request( $uri ) {
godog's avatar
godog committed
860
		// We don't need to block requests, because nothing is blocked.
samba's avatar
samba committed
861
		if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL ) {
godog's avatar
godog committed
862
			return false;
samba's avatar
samba committed
863
		}
godog's avatar
godog committed
864

samba's avatar
samba committed
865
866
		$check = parse_url( $uri );
		if ( ! $check ) {
lucha's avatar
lucha committed
867
			return true;
samba's avatar
samba committed
868
		}
godog's avatar
godog committed
869

samba's avatar
samba committed
870
		$home = parse_url( get_option( 'siteurl' ) );
godog's avatar
godog committed
871

lucha's avatar
lucha committed
872
873
		// Don't block requests back to ourselves by default.
		if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) ) {
lechuck's avatar
lechuck committed
874
			/**
agata's avatar
agata committed
875
876
877
			 * Filters whether to block local HTTP API requests.
			 *
			 * A local request is one to `localhost` or to the same host as the site itself.
lechuck's avatar
lechuck committed
878
879
880
			 *
			 * @since 2.8.0
			 *
agata's avatar
agata committed
881
			 * @param bool $block Whether to block local requests. Default false.
lechuck's avatar
lechuck committed
882
883
884
			 */
			return apply_filters( 'block_local_requests', false );
		}
godog's avatar
godog committed
885

samba's avatar
samba committed
886
		if ( ! defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
godog's avatar
godog committed
887
			return true;
samba's avatar
samba committed
888
		}
godog's avatar
godog committed
889

lechuck's avatar
lechuck committed
890
		static $accessible_hosts = null;
samba's avatar
samba committed
891
		static $wildcard_regex   = array();
lechuck's avatar
lechuck committed
892
		if ( null === $accessible_hosts ) {
samba's avatar
samba committed
893
			$accessible_hosts = preg_split( '|,\s*|', WP_ACCESSIBLE_HOSTS );
godog's avatar
godog committed
894

samba's avatar
samba committed
895
			if ( false !== strpos( WP_ACCESSIBLE_HOSTS, '*' ) ) {
root's avatar
root committed
896
				$wildcard_regex = array();
samba's avatar
samba committed
897
				foreach ( $accessible_hosts as $host ) {
lucha's avatar
lucha committed
898
					$wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) );
samba's avatar
samba committed
899
900
				}
				$wildcard_regex = '/^(' . implode( '|', $wildcard_regex ) . ')$/i';
root's avatar
root committed
901
902
903
			}
		}

samba's avatar
samba committed
904
905
906
		if ( ! empty( $wildcard_regex ) ) {
			return ! preg_match( $wildcard_regex, $check['host'] );
		} else {
agata's avatar
agata committed
907
			return ! in_array( $check['host'], $accessible_hosts ); // Inverse logic, if it's in the array, then don't block it.
samba's avatar
samba committed
908
		}
root's avatar
root committed
909

lechuck's avatar
lechuck committed
910
911
	}

lechuck's avatar
lechuck committed
912
	/**
lechuck's avatar
lechuck committed
913
	 * Used as a wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7.
lechuck's avatar
lechuck committed
914
	 *
lechuck's avatar
lechuck committed
915
916
	 * @deprecated 4.4.0 Use wp_parse_url()
	 * @see wp_parse_url()
lechuck's avatar
lechuck committed
917
918
919
920
921
922
	 *
	 * @param string $url The URL to parse.
	 * @return bool|array False on failure; Array of URL components on success;
	 *                    See parse_url()'s return values.
	 */
	protected static function parse_url( $url ) {
lechuck's avatar
lechuck committed
923
924
		_deprecated_function( __METHOD__, '4.4.0', 'wp_parse_url()' );
		return wp_parse_url( $url );
lechuck's avatar
lechuck committed
925
926
927
928
929
930
931
932
933
	}

	/**
	 * Converts a relative URL to an absolute URL relative to a given URL.
	 *
	 * If an Absolute URL is provided, no processing of that URL is done.
	 *
	 * @since 3.4.0
	 *
agata's avatar
agata committed
934
935
	 * @param string $maybe_relative_path The URL which might be relative.
	 * @param string $url                 The URL which $maybe_relative_path is relative to.
lechuck's avatar
lechuck committed
936
937
	 * @return string An Absolute URL, in a failure condition where the URL cannot be parsed, the relative URL will be returned.
	 */
lucha's avatar
lucha committed
938
	public static function make_absolute_url( $maybe_relative_path, $url ) {
samba's avatar
samba committed
939
		if ( empty( $url ) ) {
lechuck's avatar
lechuck committed
940
			return $maybe_relative_path;
samba's avatar
samba committed
941
		}
lechuck's avatar
lechuck committed
942

agata's avatar
agata committed
943
944
		$url_parts = wp_parse_url( $url );
		if ( ! $url_parts ) {
lechuck's avatar
lechuck committed
945
			return $maybe_relative_path;
lechuck's avatar
lechuck committed
946
		}
lechuck's avatar
lechuck committed
947

agata's avatar
agata committed
948
949
		$relative_url_parts = wp_parse_url( $maybe_relative_path );
		if ( ! $relative_url_parts ) {
lechuck's avatar
lechuck committed
950
			return $maybe_relative_path;
lechuck's avatar
lechuck committed
951
		}
lechuck's avatar
lechuck committed
952

agata's avatar
agata committed
953
		// Check for a scheme on the 'relative' URL.
lechuck's avatar
lechuck committed
954
		if ( ! empty( $relative_url_parts['scheme'] ) ) {
lechuck's avatar
lechuck committed
955
			return $maybe_relative_path;
lechuck's avatar
lechuck committed
956
		}
lechuck's avatar
lechuck committed
957

lechuck's avatar
lechuck committed
958
959
		$absolute_path = $url_parts['scheme'] . '://';

agata's avatar
agata committed
960
961
		// Schemeless URLs will make it this far, so we check for a host in the relative URL
		// and convert it to a protocol-URL.
lechuck's avatar
lechuck committed
962
963
		if ( isset( $relative_url_parts['host'] ) ) {
			$absolute_path .= $relative_url_parts['host'];
samba's avatar
samba committed
964
			if ( isset( $relative_url_parts['port'] ) ) {
lechuck's avatar
lechuck committed
965
				$absolute_path .= ':' . $relative_url_parts['port'];