~canonical-sysadmins/wordpress/4.8.1

« back to all changes in this revision

Viewing changes to wp-includes/class-http.php

  • Committer: Barry Price
  • Date: 2016-08-17 04:50:12 UTC
  • mfrom: (1.1.18 upstream)
  • Revision ID: barry.price@canonical.com-20160817045012-qfui81zhqnqv2ba9
Merge WP4.6 from upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
 * @since 2.7.0
8
8
 */
9
9
 
 
10
if ( ! class_exists( 'Requests' ) ) {
 
11
        require( ABSPATH . WPINC . '/class-requests.php' );
 
12
 
 
13
        Requests::register_autoloader();
 
14
        Requests::set_certificate_path( ABSPATH . WPINC . '/certificates/ca-bundle.crt' );
 
15
}
 
16
 
10
17
/**
11
18
 * Core class used for managing HTTP transports and making HTTP requests.
12
19
 *
110
117
         *                                             Default '1.0'.
111
118
         *     @type string       $user-agent          User-agent value sent.
112
119
         *                                             Default WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ).
113
 
         *     @type bool         $reject_unsafe_urls  Whether to pass URLs through {@see wp_http_validate_url()}.
 
120
         *     @type bool         $reject_unsafe_urls  Whether to pass URLs through wp_http_validate_url().
114
121
         *                                             Default false.
115
122
         *     @type bool         $blocking            Whether the calling code requires the result of the request.
116
123
         *                                             If set to false, the request will be sent to the remote server,
146
153
                $defaults = array(
147
154
                        'method' => 'GET',
148
155
                        /**
149
 
                         * Filter the timeout value for an HTTP request.
 
156
                         * Filters the timeout value for an HTTP request.
150
157
                         *
151
158
                         * @since 2.7.0
152
159
                         *
155
162
                         */
156
163
                        'timeout' => apply_filters( 'http_request_timeout', 5 ),
157
164
                        /**
158
 
                         * Filter the number of redirects allowed during an HTTP request.
 
165
                         * Filters the number of redirects allowed during an HTTP request.
159
166
                         *
160
167
                         * @since 2.7.0
161
168
                         *
163
170
                         */
164
171
                        'redirection' => apply_filters( 'http_request_redirection_count', 5 ),
165
172
                        /**
166
 
                         * Filter the version of the HTTP protocol used in a request.
 
173
                         * Filters the version of the HTTP protocol used in a request.
167
174
                         *
168
175
                         * @since 2.7.0
169
176
                         *
172
179
                         */
173
180
                        'httpversion' => apply_filters( 'http_request_version', '1.0' ),
174
181
                        /**
175
 
                         * Filter the user agent value sent with an HTTP request.
 
182
                         * Filters the user agent value sent with an HTTP request.
176
183
                         *
177
184
                         * @since 2.7.0
178
185
                         *
180
187
                         */
181
188
                        'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ) ),
182
189
                        /**
183
 
                         * Filter whether to pass URLs through wp_http_validate_url() in an HTTP request.
 
190
                         * Filters whether to pass URLs through wp_http_validate_url() in an HTTP request.
184
191
                         *
185
192
                         * @since 3.6.0
186
193
                         *
210
217
 
211
218
                $r = wp_parse_args( $args, $defaults );
212
219
                /**
213
 
                 * Filter the arguments used in an HTTP request.
 
220
                 * Filters the arguments used in an HTTP request.
214
221
                 *
215
222
                 * @since 2.7.0
216
223
                 *
224
231
                        $r['_redirection'] = $r['redirection'];
225
232
 
226
233
                /**
227
 
                 * Filter whether to preempt an HTTP request's return value.
 
234
                 * Filters whether to preempt an HTTP request's return value.
228
235
                 *
229
236
                 * Returning a non-false value from the filter will short-circuit the HTTP request and return
230
237
                 * early with that value. A filter should return either:
247
254
                        return $pre;
248
255
 
249
256
                if ( function_exists( 'wp_kses_bad_protocol' ) ) {
250
 
                        if ( $r['reject_unsafe_urls'] )
 
257
                        if ( $r['reject_unsafe_urls'] ) {
251
258
                                $url = wp_http_validate_url( $url );
 
259
                        }
252
260
                        if ( $url ) {
253
261
                                $url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) );
254
262
                        }
256
264
 
257
265
                $arrURL = @parse_url( $url );
258
266
 
259
 
                if ( empty( $url ) || empty( $arrURL['scheme'] ) )
 
267
                if ( empty( $url ) || empty( $arrURL['scheme'] ) ) {
260
268
                        return new WP_Error('http_request_failed', __('A valid URL was not provided.'));
 
269
                }
261
270
 
262
 
                if ( $this->block_request( $url ) )
 
271
                if ( $this->block_request( $url ) ) {
263
272
                        return new WP_Error( 'http_request_failed', __( 'User has blocked requests through HTTP.' ) );
264
 
 
265
 
                /*
266
 
                 * Determine if this is a https call and pass that on to the transport functions
267
 
                 * so that we can blacklist the transports that do not support ssl verification
268
 
                 */
269
 
                $r['ssl'] = $arrURL['scheme'] == 'https' || $arrURL['scheme'] == 'ssl';
270
 
 
271
 
                // Determine if this request is to OUR install of WordPress.
272
 
                $homeURL = parse_url( get_bloginfo( 'url' ) );
273
 
                $r['local'] = 'localhost' == $arrURL['host'] || ( isset( $homeURL['host'] ) && $homeURL['host'] == $arrURL['host'] );
274
 
                unset( $homeURL );
275
 
 
276
 
                /*
277
 
                 * If we are streaming to a file but no filename was given drop it in the WP temp dir
278
 
                 * and pick its name using the basename of the $url.
279
 
                 */
280
 
                if ( $r['stream']  && empty( $r['filename'] ) ) {
281
 
                        $r['filename'] = get_temp_dir() . wp_unique_filename( get_temp_dir(), basename( $url ) );
282
273
                }
283
274
 
284
 
                /*
285
 
                 * Force some settings if we are streaming to a file and check for existence and perms
286
 
                 * of destination directory.
287
 
                 */
 
275
                // If we are streaming to a file but no filename was given drop it in the WP temp dir
 
276
                // and pick its name using the basename of the $url
288
277
                if ( $r['stream'] ) {
 
278
                        if ( empty( $r['filename'] ) ) {
 
279
                                $r['filename'] = get_temp_dir() . basename( $url );
 
280
                        }
 
281
 
 
282
                        // Force some settings if we are streaming to a file and check for existence and perms of destination directory
289
283
                        $r['blocking'] = true;
290
 
                        if ( ! wp_is_writable( dirname( $r['filename'] ) ) )
 
284
                        if ( ! wp_is_writable( dirname( $r['filename'] ) ) ) {
291
285
                                return new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
 
286
                        }
292
287
                }
293
288
 
294
 
                if ( is_null( $r['headers'] ) )
 
289
                if ( is_null( $r['headers'] ) ) {
295
290
                        $r['headers'] = array();
 
291
                }
296
292
 
 
293
                // WP allows passing in headers as a string, weirdly.
297
294
                if ( ! is_array( $r['headers'] ) ) {
298
 
                        $processedHeaders = self::processHeaders( $r['headers'], $url );
 
295
                        $processedHeaders = WP_Http::processHeaders( $r['headers'] );
299
296
                        $r['headers'] = $processedHeaders['headers'];
300
297
                }
301
298
 
302
 
                if ( isset( $r['headers']['User-Agent'] ) ) {
303
 
                        $r['user-agent'] = $r['headers']['User-Agent'];
304
 
                        unset( $r['headers']['User-Agent'] );
305
 
                }
306
 
 
307
 
                if ( isset( $r['headers']['user-agent'] ) ) {
308
 
                        $r['user-agent'] = $r['headers']['user-agent'];
309
 
                        unset( $r['headers']['user-agent'] );
310
 
                }
311
 
 
312
 
                if ( '1.1' == $r['httpversion'] && !isset( $r['headers']['connection'] ) ) {
313
 
                        $r['headers']['connection'] = 'close';
314
 
                }
315
 
 
316
 
                // Construct Cookie: header if any cookies are set.
317
 
                self::buildCookieHeader( $r );
318
 
 
319
 
                // Avoid issues where mbstring.func_overload is enabled.
320
 
                mbstring_binary_safe_encoding();
321
 
 
322
 
                if ( ! isset( $r['headers']['Accept-Encoding'] ) ) {
323
 
                        if ( $encoding = WP_Http_Encoding::accept_encoding( $url, $r ) )
324
 
                                $r['headers']['Accept-Encoding'] = $encoding;
325
 
                }
326
 
 
327
 
                if ( ( ! is_null( $r['body'] ) && '' != $r['body'] ) || 'POST' == $r['method'] || 'PUT' == $r['method'] ) {
328
 
                        if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) {
329
 
                                $r['body'] = http_build_query( $r['body'], null, '&' );
330
 
 
331
 
                                if ( ! isset( $r['headers']['Content-Type'] ) )
332
 
                                        $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' );
333
 
                        }
334
 
 
335
 
                        if ( '' === $r['body'] )
336
 
                                $r['body'] = null;
337
 
 
338
 
                        if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) )
339
 
                                $r['headers']['Content-Length'] = strlen( $r['body'] );
340
 
                }
341
 
 
342
 
                $response = $this->_dispatch_request( $url, $r );
343
 
 
344
 
                reset_mbstring_encoding();
345
 
 
346
 
                if ( is_wp_error( $response ) )
347
 
                        return $response;
348
 
 
349
 
                // Append cookies that were used in this request to the response
 
299
                // Setup arguments
 
300
                $headers = $r['headers'];
 
301
                $data = $r['body'];
 
302
                $type = $r['method'];
 
303
                $options = array(
 
304
                        'timeout' => $r['timeout'],
 
305
                        'useragent' => $r['user-agent'],
 
306
                        'blocking' => $r['blocking'],
 
307
                        'hooks' => new Requests_Hooks(),
 
308
                );
 
309
 
 
310
                // Ensure redirects follow browser behaviour.
 
311
                $options['hooks']->register( 'requests.before_redirect', array( get_class(), 'browser_redirect_compatibility' ) );
 
312
 
 
313
                if ( $r['stream'] ) {
 
314
                        $options['filename'] = $r['filename'];
 
315
                }
 
316
                if ( empty( $r['redirection'] ) ) {
 
317
                        $options['follow_redirects'] = false;
 
318
                } else {
 
319
                        $options['redirects'] = $r['redirection'];
 
320
                }
 
321
 
 
322
                // Use byte limit, if we can
 
323
                if ( isset( $r['limit_response_size'] ) ) {
 
324
                        $options['max_bytes'] = $r['limit_response_size'];
 
325
                }
 
326
 
 
327
                // If we've got cookies, use and convert them to Requests_Cookie.
350
328
                if ( ! empty( $r['cookies'] ) ) {
351
 
                        $cookies_set = wp_list_pluck( $response['cookies'], 'name' );
352
 
                        foreach ( $r['cookies'] as $cookie ) {
353
 
                                if ( ! in_array( $cookie->name, $cookies_set ) && $cookie->test( $url ) ) {
354
 
                                        $response['cookies'][] = $cookie;
355
 
                                }
356
 
                        }
357
 
                }
358
 
 
359
 
                return $response;
 
329
                        $options['cookies'] = WP_Http::normalize_cookies( $r['cookies'] );
 
330
                }
 
331
 
 
332
                // SSL certificate handling
 
333
                if ( ! $r['sslverify'] ) {
 
334
                        $options['verify'] = false;
 
335
                } else {
 
336
                        $options['verify'] = $r['sslcertificates'];
 
337
                }
 
338
 
 
339
                // All non-GET/HEAD requests should put the arguments in the form body.
 
340
                if ( 'HEAD' !== $type && 'GET' !== $type ) {
 
341
                        $options['data_format'] = 'body';
 
342
                }
 
343
 
 
344
                /**
 
345
                 * Filters whether SSL should be verified for non-local requests.
 
346
                 *
 
347
                 * @since 2.8.0
 
348
                 *
 
349
                 * @param bool $ssl_verify Whether to verify the SSL connection. Default true.
 
350
                 */
 
351
                $options['verify'] = apply_filters( 'https_ssl_verify', $options['verify'] );
 
352
 
 
353
                // Check for proxies.
 
354
                $proxy = new WP_HTTP_Proxy();
 
355
                if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
 
356
                        $options['proxy'] = new Requests_Proxy_HTTP( $proxy->host() . ':' . $proxy->port() );
 
357
 
 
358
                        if ( $proxy->use_authentication() ) {
 
359
                                $options['proxy']->use_authentication = true;
 
360
                                $options['proxy']->user = $proxy->username();
 
361
                                $options['proxy']->pass = $proxy->password();
 
362
                        }
 
363
                }
 
364
 
 
365
                try {
 
366
                        $requests_response = Requests::request( $url, $headers, $data, $type, $options );
 
367
 
 
368
                        // Convert the response into an array
 
369
                        $http_response = new WP_HTTP_Requests_Response( $requests_response, $r['filename'] );
 
370
                        $response = $http_response->to_array();
 
371
 
 
372
                        // Add the original object to the array.
 
373
                        $response['http_response'] = $http_response;
 
374
                }
 
375
                catch ( Requests_Exception $e ) {
 
376
                        $response = new WP_Error( 'http_request_failed', $e->getMessage() );
 
377
                }
 
378
 
 
379
                /**
 
380
                 * Fires after an HTTP API response is received and before the response is returned.
 
381
                 *
 
382
                 * @since 2.8.0
 
383
                 *
 
384
                 * @param array|WP_Error $response HTTP response or WP_Error object.
 
385
                 * @param string         $context  Context under which the hook is fired.
 
386
                 * @param string         $class    HTTP transport used.
 
387
                 * @param array          $args     HTTP request arguments.
 
388
                 * @param string         $url      The request URL.
 
389
                 */
 
390
                do_action( 'http_api_debug', $response, 'response', 'Requests', $r, $url );
 
391
                if ( is_wp_error( $response ) ) {
 
392
                        return $response;
 
393
                }
 
394
 
 
395
                if ( ! $r['blocking'] ) {
 
396
                        return array(
 
397
                                'headers' => array(),
 
398
                                'body' => '',
 
399
                                'response' => array(
 
400
                                        'code' => false,
 
401
                                        'message' => false,
 
402
                                ),
 
403
                                'cookies' => array(),
 
404
                                'http_response' => null,
 
405
                        );
 
406
                }
 
407
 
 
408
                /**
 
409
                 * Filters the HTTP API response immediately before the response is returned.
 
410
                 *
 
411
                 * @since 2.9.0
 
412
                 *
 
413
                 * @param array  $response HTTP response.
 
414
                 * @param array  $r        HTTP request arguments.
 
415
                 * @param string $url      The request URL.
 
416
                 */
 
417
                return apply_filters( 'http_response', $response, $r, $url );
 
418
        }
 
419
 
 
420
        /**
 
421
         * Normalizes cookies for using in Requests.
 
422
         *
 
423
         * @since 4.6.0
 
424
         * @access public
 
425
         * @static
 
426
         *
 
427
         * @param array $cookies List of cookies to send with the request.
 
428
         * @return Requests_Cookie_Jar Cookie holder object.
 
429
         */
 
430
        public static function normalize_cookies( $cookies ) {
 
431
                $cookie_jar = new Requests_Cookie_Jar();
 
432
 
 
433
                foreach ( $cookies as $name => $value ) {
 
434
                        if ( $value instanceof WP_Http_Cookie ) {
 
435
                                $cookie_jar[ $value->name ] = new Requests_Cookie( $value->name, $value->value, $value->get_attributes() );
 
436
                        } elseif ( is_string( $value ) ) {
 
437
                                $cookie_jar[ $name ] = new Requests_Cookie( $name, $value );
 
438
                        }
 
439
                }
 
440
 
 
441
                return $cookie_jar;
 
442
        }
 
443
 
 
444
        /**
 
445
         * Match redirect behaviour to browser handling.
 
446
         *
 
447
         * Changes 302 redirects from POST to GET to match browser handling. Per
 
448
         * RFC 7231, user agents can deviate from the strict reading of the
 
449
         * specification for compatibility purposes.
 
450
         *
 
451
         * @since 4.6.0
 
452
         * @access public
 
453
         * @static
 
454
         *
 
455
         * @param string            $location URL to redirect to.
 
456
         * @param array             $headers  Headers for the redirect.
 
457
         * @param array             $options  Redirect request options.
 
458
         * @param Requests_Response $original Response object.
 
459
         */
 
460
        public static function browser_redirect_compatibility( $location, $headers, $data, &$options, $original ) {
 
461
                // Browser compat
 
462
                if ( $original->status_code === 302 ) {
 
463
                        $options['type'] = Requests::GET;
 
464
                }
360
465
        }
361
466
 
362
467
        /**
374
479
                $transports = array( 'curl', 'streams' );
375
480
 
376
481
                /**
377
 
                 * Filter which HTTP transports are available and in what order.
 
482
                 * Filters which HTTP transports are available and in what order.
378
483
                 *
379
484
                 * @since 3.7.0
380
485
                 *
432
537
 
433
538
                $response = $transports[$class]->request( $url, $args );
434
539
 
435
 
                /**
436
 
                 * Fires after an HTTP API response is received and before the response is returned.
437
 
                 *
438
 
                 * @since 2.8.0
439
 
                 *
440
 
                 * @param array|WP_Error $response HTTP response or WP_Error object.
441
 
                 * @param string         $context  Context under which the hook is fired.
442
 
                 * @param string         $class    HTTP transport used.
443
 
                 * @param array          $args     HTTP request arguments.
444
 
                 * @param string         $url      The request URL.
445
 
                 */
 
540
                /** This action is documented in wp-includes/class-http.php */
446
541
                do_action( 'http_api_debug', $response, 'response', $class, $args, $url );
447
542
 
448
543
                if ( is_wp_error( $response ) )
449
544
                        return $response;
450
545
 
451
546
                /**
452
 
                 * Filter the HTTP API response immediately before the response is returned.
 
547
                 * Filters the HTTP API response immediately before the response is returned.
453
548
                 *
454
549
                 * @since 2.9.0
455
550
                 *
643
738
         *
644
739
         * Based off the HTTP http_encoding_dechunk function.
645
740
         *
646
 
         * @link http://tools.ietf.org/html/rfc2616#section-19.4.6 Process for chunked decoding.
 
741
         * @link https://tools.ietf.org/html/rfc2616#section-19.4.6 Process for chunked decoding.
647
742
         *
648
743
         * @access public
649
744
         * @since 2.7.0
718
813
                // Don't block requests back to ourselves by default.
719
814
                if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) ) {
720
815
                        /**
721
 
                         * Filter whether to block local requests through the proxy.
 
816
                         * Filters whether to block local requests through the proxy.
722
817
                         *
723
818
                         * @since 2.8.0
724
819
                         *