home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / class-http.php < prev    next >
Encoding:
PHP Script  |  2017-10-18  |  35.3 KB  |  1,024 lines

  1. <?php
  2. /**
  3.  * HTTP API: WP_Http class
  4.  *
  5.  * @package WordPress
  6.  * @subpackage HTTP
  7.  * @since 2.7.0
  8.  */
  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.  
  17. /**
  18.  * Core class used for managing HTTP transports and making HTTP requests.
  19.  *
  20.  * This class is used to consistently make outgoing HTTP requests easy for developers
  21.  * while still being compatible with the many PHP configurations under which
  22.  * WordPress runs.
  23.  *
  24.  * Debugging includes several actions, which pass different variables for debugging the HTTP API.
  25.  *
  26.  * @since 2.7.0
  27.  */
  28. class WP_Http {
  29.  
  30.     // Aliases for HTTP response codes.
  31.     const HTTP_CONTINUE                   = 100;
  32.     const SWITCHING_PROTOCOLS             = 101;
  33.     const PROCESSING                      = 102;
  34.  
  35.     const OK                              = 200;
  36.     const CREATED                         = 201;
  37.     const ACCEPTED                        = 202;
  38.     const NON_AUTHORITATIVE_INFORMATION   = 203;
  39.     const NO_CONTENT                      = 204;
  40.     const RESET_CONTENT                   = 205;
  41.     const PARTIAL_CONTENT                 = 206;
  42.     const MULTI_STATUS                    = 207;
  43.     const IM_USED                         = 226;
  44.  
  45.     const MULTIPLE_CHOICES                = 300;
  46.     const MOVED_PERMANENTLY               = 301;
  47.     const FOUND                           = 302;
  48.     const SEE_OTHER                       = 303;
  49.     const NOT_MODIFIED                    = 304;
  50.     const USE_PROXY                       = 305;
  51.     const RESERVED                        = 306;
  52.     const TEMPORARY_REDIRECT              = 307;
  53.     const PERMANENT_REDIRECT              = 308;
  54.  
  55.     const BAD_REQUEST                     = 400;
  56.     const UNAUTHORIZED                    = 401;
  57.     const PAYMENT_REQUIRED                = 402;
  58.     const FORBIDDEN                       = 403;
  59.     const NOT_FOUND                       = 404;
  60.     const METHOD_NOT_ALLOWED              = 405;
  61.     const NOT_ACCEPTABLE                  = 406;
  62.     const PROXY_AUTHENTICATION_REQUIRED   = 407;
  63.     const REQUEST_TIMEOUT                 = 408;
  64.     const CONFLICT                        = 409;
  65.     const GONE                            = 410;
  66.     const LENGTH_REQUIRED                 = 411;
  67.     const PRECONDITION_FAILED             = 412;
  68.     const REQUEST_ENTITY_TOO_LARGE        = 413;
  69.     const REQUEST_URI_TOO_LONG            = 414;
  70.     const UNSUPPORTED_MEDIA_TYPE          = 415;
  71.     const REQUESTED_RANGE_NOT_SATISFIABLE = 416;
  72.     const EXPECTATION_FAILED              = 417;
  73.     const IM_A_TEAPOT                     = 418;
  74.     const MISDIRECTED_REQUEST             = 421;
  75.     const UNPROCESSABLE_ENTITY            = 422;
  76.     const LOCKED                          = 423;
  77.     const FAILED_DEPENDENCY               = 424;
  78.     const UPGRADE_REQUIRED                = 426;
  79.     const PRECONDITION_REQUIRED           = 428;
  80.     const TOO_MANY_REQUESTS               = 429;
  81.     const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
  82.     const UNAVAILABLE_FOR_LEGAL_REASONS   = 451;
  83.  
  84.     const INTERNAL_SERVER_ERROR           = 500;
  85.     const NOT_IMPLEMENTED                 = 501;
  86.     const BAD_GATEWAY                     = 502;
  87.     const SERVICE_UNAVAILABLE             = 503;
  88.     const GATEWAY_TIMEOUT                 = 504;
  89.     const HTTP_VERSION_NOT_SUPPORTED      = 505;
  90.     const VARIANT_ALSO_NEGOTIATES         = 506;
  91.     const INSUFFICIENT_STORAGE            = 507;
  92.     const NOT_EXTENDED                    = 510;
  93.     const NETWORK_AUTHENTICATION_REQUIRED = 511;
  94.  
  95.     /**
  96.      * Send an HTTP request to a URI.
  97.      *
  98.      * Please note: The only URI that are supported in the HTTP Transport implementation
  99.      * are the HTTP and HTTPS protocols.
  100.      *
  101.      * @since 2.7.0
  102.      *
  103.      * @param string       $url  The request URL.
  104.      * @param string|array $args {
  105.      *     Optional. Array or string of HTTP request arguments.
  106.      *
  107.      *     @type string       $method              Request method. Accepts 'GET', 'POST', 'HEAD', or 'PUT'.
  108.      *                                             Some transports technically allow others, but should not be
  109.      *                                             assumed. Default 'GET'.
  110.      *     @type int          $timeout             How long the connection should stay open in seconds. Default 5.
  111.      *     @type int          $redirection         Number of allowed redirects. Not supported by all transports
  112.      *                                             Default 5.
  113.      *     @type string       $httpversion         Version of the HTTP protocol to use. Accepts '1.0' and '1.1'.
  114.      *                                             Default '1.0'.
  115.      *     @type string       $user-agent          User-agent value sent.
  116.      *                                             Default 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ).
  117.      *     @type bool         $reject_unsafe_urls  Whether to pass URLs through wp_http_validate_url().
  118.      *                                             Default false.
  119.      *     @type bool         $blocking            Whether the calling code requires the result of the request.
  120.      *                                             If set to false, the request will be sent to the remote server,
  121.      *                                             and processing returned to the calling code immediately, the caller
  122.      *                                             will know if the request succeeded or failed, but will not receive
  123.      *                                             any response from the remote server. Default true.
  124.      *     @type string|array $headers             Array or string of headers to send with the request.
  125.      *                                             Default empty array.
  126.      *     @type array        $cookies             List of cookies to send with the request. Default empty array.
  127.      *     @type string|array $body                Body to send with the request. Default null.
  128.      *     @type bool         $compress            Whether to compress the $body when sending the request.
  129.      *                                             Default false.
  130.      *     @type bool         $decompress          Whether to decompress a compressed response. If set to false and
  131.      *                                             compressed content is returned in the response anyway, it will
  132.      *                                             need to be separately decompressed. Default true.
  133.      *     @type bool         $sslverify           Whether to verify SSL for the request. Default true.
  134.      *     @type string       sslcertificates      Absolute path to an SSL certificate .crt file.
  135.      *                                             Default ABSPATH . WPINC . '/certificates/ca-bundle.crt'.
  136.      *     @type bool         $stream              Whether to stream to a file. If set to true and no filename was
  137.      *                                             given, it will be droped it in the WP temp dir and its name will
  138.      *                                             be set using the basename of the URL. Default false.
  139.      *     @type string       $filename            Filename of the file to write to when streaming. $stream must be
  140.      *                                             set to true. Default null.
  141.      *     @type int          $limit_response_size Size in bytes to limit the response to. Default null.
  142.      *
  143.      * }
  144.      * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'.
  145.      *                        A WP_Error instance upon error.
  146.      */
  147.     public function request( $url, $args = array() ) {
  148.         $defaults = array(
  149.             'method' => 'GET',
  150.             /**
  151.              * Filters the timeout value for an HTTP request.
  152.              *
  153.              * @since 2.7.0
  154.              *
  155.              * @param int $timeout_value Time in seconds until a request times out.
  156.              *                           Default 5.
  157.              */
  158.             'timeout' => apply_filters( 'http_request_timeout', 5 ),
  159.             /**
  160.              * Filters the number of redirects allowed during an HTTP request.
  161.              *
  162.              * @since 2.7.0
  163.              *
  164.              * @param int $redirect_count Number of redirects allowed. Default 5.
  165.              */
  166.             'redirection' => apply_filters( 'http_request_redirection_count', 5 ),
  167.             /**
  168.              * Filters the version of the HTTP protocol used in a request.
  169.              *
  170.              * @since 2.7.0
  171.              *
  172.              * @param string $version Version of HTTP used. Accepts '1.0' and '1.1'.
  173.              *                        Default '1.0'.
  174.              */
  175.             'httpversion' => apply_filters( 'http_request_version', '1.0' ),
  176.             /**
  177.              * Filters the user agent value sent with an HTTP request.
  178.              *
  179.              * @since 2.7.0
  180.              *
  181.              * @param string $user_agent WordPress user agent string.
  182.              */
  183.             'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) ),
  184.             /**
  185.              * Filters whether to pass URLs through wp_http_validate_url() in an HTTP request.
  186.              *
  187.              * @since 3.6.0
  188.              *
  189.              * @param bool $pass_url Whether to pass URLs through wp_http_validate_url().
  190.              *                       Default false.
  191.              */
  192.             'reject_unsafe_urls' => apply_filters( 'http_request_reject_unsafe_urls', false ),
  193.             'blocking' => true,
  194.             'headers' => array(),
  195.             'cookies' => array(),
  196.             'body' => null,
  197.             'compress' => false,
  198.             'decompress' => true,
  199.             'sslverify' => true,
  200.             'sslcertificates' => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
  201.             'stream' => false,
  202.             'filename' => null,
  203.             'limit_response_size' => null,
  204.         );
  205.  
  206.         // Pre-parse for the HEAD checks.
  207.         $args = wp_parse_args( $args );
  208.  
  209.         // By default, Head requests do not cause redirections.
  210.         if ( isset($args['method']) && 'HEAD' == $args['method'] )
  211.             $defaults['redirection'] = 0;
  212.  
  213.         $r = wp_parse_args( $args, $defaults );
  214.         /**
  215.          * Filters the arguments used in an HTTP request.
  216.          *
  217.          * @since 2.7.0
  218.          *
  219.          * @param array  $r   An array of HTTP request arguments.
  220.          * @param string $url The request URL.
  221.          */
  222.         $r = apply_filters( 'http_request_args', $r, $url );
  223.  
  224.         // The transports decrement this, store a copy of the original value for loop purposes.
  225.         if ( ! isset( $r['_redirection'] ) )
  226.             $r['_redirection'] = $r['redirection'];
  227.  
  228.         /**
  229.          * Filters whether to preempt an HTTP request's return value.
  230.          *
  231.          * Returning a non-false value from the filter will short-circuit the HTTP request and return
  232.          * early with that value. A filter should return either:
  233.          *
  234.          *  - An array containing 'headers', 'body', 'response', 'cookies', and 'filename' elements
  235.          *  - A WP_Error instance
  236.          *  - boolean false (to avoid short-circuiting the response)
  237.          *
  238.          * Returning any other value may result in unexpected behaviour.
  239.          *
  240.          * @since 2.9.0
  241.          *
  242.          * @param false|array|WP_Error $preempt Whether to preempt an HTTP request's return value. Default false.
  243.          * @param array               $r        HTTP request arguments.
  244.          * @param string              $url      The request URL.
  245.          */
  246.         $pre = apply_filters( 'pre_http_request', false, $r, $url );
  247.  
  248.         if ( false !== $pre )
  249.             return $pre;
  250.  
  251.         if ( function_exists( 'wp_kses_bad_protocol' ) ) {
  252.             if ( $r['reject_unsafe_urls'] ) {
  253.                 $url = wp_http_validate_url( $url );
  254.             }
  255.             if ( $url ) {
  256.                 $url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) );
  257.             }
  258.         }
  259.  
  260.         $arrURL = @parse_url( $url );
  261.  
  262.         if ( empty( $url ) || empty( $arrURL['scheme'] ) ) {
  263.             return new WP_Error('http_request_failed', __('A valid URL was not provided.'));
  264.         }
  265.  
  266.         if ( $this->block_request( $url ) ) {
  267.             return new WP_Error( 'http_request_failed', __( 'User has blocked requests through HTTP.' ) );
  268.         }
  269.  
  270.         // If we are streaming to a file but no filename was given drop it in the WP temp dir
  271.         // and pick its name using the basename of the $url
  272.         if ( $r['stream'] ) {
  273.             if ( empty( $r['filename'] ) ) {
  274.                 $r['filename'] = get_temp_dir() . basename( $url );
  275.             }
  276.  
  277.             // Force some settings if we are streaming to a file and check for existence and perms of destination directory
  278.             $r['blocking'] = true;
  279.             if ( ! wp_is_writable( dirname( $r['filename'] ) ) ) {
  280.                 return new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) );
  281.             }
  282.         }
  283.  
  284.         if ( is_null( $r['headers'] ) ) {
  285.             $r['headers'] = array();
  286.         }
  287.  
  288.         // WP allows passing in headers as a string, weirdly.
  289.         if ( ! is_array( $r['headers'] ) ) {
  290.             $processedHeaders = WP_Http::processHeaders( $r['headers'] );
  291.             $r['headers'] = $processedHeaders['headers'];
  292.         }
  293.  
  294.         // Setup arguments
  295.         $headers = $r['headers'];
  296.         $data = $r['body'];
  297.         $type = $r['method'];
  298.         $options = array(
  299.             'timeout' => $r['timeout'],
  300.             'useragent' => $r['user-agent'],
  301.             'blocking' => $r['blocking'],
  302.             'hooks' => new WP_HTTP_Requests_Hooks( $url, $r ),
  303.         );
  304.  
  305.         // Ensure redirects follow browser behaviour.
  306.         $options['hooks']->register( 'requests.before_redirect', array( get_class(), 'browser_redirect_compatibility' ) );
  307.  
  308.         // Validate redirected URLs.
  309.         if ( function_exists( 'wp_kses_bad_protocol' ) && $r['reject_unsafe_urls'] ) {
  310.             $options['hooks']->register( 'requests.before_redirect', array( get_class(), 'validate_redirects' ) );
  311.         }
  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.
  328.         if ( ! empty( $r['cookies'] ) ) {
  329.             $options['cookies'] = WP_Http::normalize_cookies( $r['cookies'] );
  330.         }
  331.  
  332.         // SSL certificate handling
  333.         if ( ! $r['sslverify'] ) {
  334.             $options['verify'] = false;
  335.             $options['verifyname'] = false;
  336.         } else {
  337.             $options['verify'] = $r['sslcertificates'];
  338.         }
  339.  
  340.         // All non-GET/HEAD requests should put the arguments in the form body.
  341.         if ( 'HEAD' !== $type && 'GET' !== $type ) {
  342.             $options['data_format'] = 'body';
  343.         }
  344.  
  345.         /**
  346.          * Filters whether SSL should be verified for non-local requests.
  347.          *
  348.          * @since 2.8.0
  349.          *
  350.          * @param bool $ssl_verify Whether to verify the SSL connection. Default true.
  351.          */
  352.         $options['verify'] = apply_filters( 'https_ssl_verify', $options['verify'] );
  353.  
  354.         // Check for proxies.
  355.         $proxy = new WP_HTTP_Proxy();
  356.         if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
  357.             $options['proxy'] = new Requests_Proxy_HTTP( $proxy->host() . ':' . $proxy->port() );
  358.  
  359.             if ( $proxy->use_authentication() ) {
  360.                 $options['proxy']->use_authentication = true;
  361.                 $options['proxy']->user = $proxy->username();
  362.                 $options['proxy']->pass = $proxy->password();
  363.             }
  364.         }
  365.  
  366.         // Avoid issues where mbstring.func_overload is enabled
  367.         mbstring_binary_safe_encoding();
  368.  
  369.         try {
  370.             $requests_response = Requests::request( $url, $headers, $data, $type, $options );
  371.  
  372.             // Convert the response into an array
  373.             $http_response = new WP_HTTP_Requests_Response( $requests_response, $r['filename'] );
  374.             $response = $http_response->to_array();
  375.  
  376.             // Add the original object to the array.
  377.             $response['http_response'] = $http_response;
  378.         }
  379.         catch ( Requests_Exception $e ) {
  380.             $response = new WP_Error( 'http_request_failed', $e->getMessage() );
  381.         }
  382.  
  383.         reset_mbstring_encoding();
  384.  
  385.         /**
  386.          * Fires after an HTTP API response is received and before the response is returned.
  387.          *
  388.          * @since 2.8.0
  389.          *
  390.          * @param array|WP_Error $response HTTP response or WP_Error object.
  391.          * @param string         $context  Context under which the hook is fired.
  392.          * @param string         $class    HTTP transport used.
  393.          * @param array          $r        HTTP request arguments.
  394.          * @param string         $url      The request URL.
  395.          */
  396.         do_action( 'http_api_debug', $response, 'response', 'Requests', $r, $url );
  397.         if ( is_wp_error( $response ) ) {
  398.             return $response;
  399.         }
  400.  
  401.         if ( ! $r['blocking'] ) {
  402.             return array(
  403.                 'headers' => array(),
  404.                 'body' => '',
  405.                 'response' => array(
  406.                     'code' => false,
  407.                     'message' => false,
  408.                 ),
  409.                 'cookies' => array(),
  410.                 'http_response' => null,
  411.             );
  412.         }
  413.  
  414.         /**
  415.          * Filters the HTTP API response immediately before the response is returned.
  416.          *
  417.          * @since 2.9.0
  418.          *
  419.          * @param array  $response HTTP response.
  420.          * @param array  $r        HTTP request arguments.
  421.          * @param string $url      The request URL.
  422.          */
  423.         return apply_filters( 'http_response', $response, $r, $url );
  424.     }
  425.  
  426.     /**
  427.      * Normalizes cookies for using in Requests.
  428.      *
  429.      * @since 4.6.0
  430.      * @static
  431.      *
  432.      * @param array $cookies List of cookies to send with the request.
  433.      * @return Requests_Cookie_Jar Cookie holder object.
  434.      */
  435.     public static function normalize_cookies( $cookies ) {
  436.         $cookie_jar = new Requests_Cookie_Jar();
  437.  
  438.         foreach ( $cookies as $name => $value ) {
  439.             if ( $value instanceof WP_Http_Cookie ) {
  440.                 $cookie_jar[ $value->name ] = new Requests_Cookie( $value->name, $value->value, $value->get_attributes() );
  441.             } elseif ( is_scalar( $value ) ) {
  442.                 $cookie_jar[ $name ] = new Requests_Cookie( $name, $value );
  443.             }
  444.         }
  445.  
  446.         return $cookie_jar;
  447.     }
  448.  
  449.     /**
  450.      * Match redirect behaviour to browser handling.
  451.      *
  452.      * Changes 302 redirects from POST to GET to match browser handling. Per
  453.      * RFC 7231, user agents can deviate from the strict reading of the
  454.      * specification for compatibility purposes.
  455.      *
  456.      * @since 4.6.0
  457.      * @static
  458.      *
  459.      * @param string            $location URL to redirect to.
  460.      * @param array             $headers  Headers for the redirect.
  461.      * @param string|array      $data     Body to send with the request.
  462.      * @param array             $options  Redirect request options.
  463.      * @param Requests_Response $original Response object.
  464.      */
  465.     public static function browser_redirect_compatibility( $location, $headers, $data, &$options, $original ) {
  466.         // Browser compat
  467.         if ( $original->status_code === 302 ) {
  468.             $options['type'] = Requests::GET;
  469.         }
  470.     }
  471.  
  472.     /**
  473.      * Validate redirected URLs.
  474.      *
  475.      * @since 4.7.5
  476.      *
  477.      * @throws Requests_Exception On unsuccessful URL validation
  478.      * @param string $location URL to redirect to.
  479.      */
  480.     public static function validate_redirects( $location ) {
  481.         if ( ! wp_http_validate_url( $location ) ) {
  482.             throw new Requests_Exception( __('A valid URL was not provided.'), 'wp_http.redirect_failed_validation' );
  483.         }
  484.     }
  485.  
  486.     /**
  487.      * Tests which transports are capable of supporting the request.
  488.      *
  489.      * @since 3.2.0
  490.      *
  491.      * @param array $args Request arguments
  492.      * @param string $url URL to Request
  493.      *
  494.      * @return string|false Class name for the first transport that claims to support the request. False if no transport claims to support the request.
  495.      */
  496.     public function _get_first_available_transport( $args, $url = null ) {
  497.         $transports = array( 'curl', 'streams' );
  498.  
  499.         /**
  500.          * Filters which HTTP transports are available and in what order.
  501.          *
  502.          * @since 3.7.0
  503.          *
  504.          * @param array  $transports Array of HTTP transports to check. Default array contains
  505.          *                           'curl', and 'streams', in that order.
  506.          * @param array  $args       HTTP request arguments.
  507.          * @param string $url        The URL to request.
  508.          */
  509.         $request_order = apply_filters( 'http_api_transports', $transports, $args, $url );
  510.  
  511.         // Loop over each transport on each HTTP request looking for one which will serve this request's needs.
  512.         foreach ( $request_order as $transport ) {
  513.             if ( in_array( $transport, $transports ) ) {
  514.                 $transport = ucfirst( $transport );
  515.             }
  516.             $class = 'WP_Http_' . $transport;
  517.  
  518.             // Check to see if this transport is a possibility, calls the transport statically.
  519.             if ( !call_user_func( array( $class, 'test' ), $args, $url ) )
  520.                 continue;
  521.  
  522.             return $class;
  523.         }
  524.  
  525.         return false;
  526.     }
  527.  
  528.     /**
  529.      * Dispatches a HTTP request to a supporting transport.
  530.      *
  531.      * Tests each transport in order to find a transport which matches the request arguments.
  532.      * Also caches the transport instance to be used later.
  533.      *
  534.      * The order for requests is cURL, and then PHP Streams.
  535.      *
  536.      * @since 3.2.0
  537.      *
  538.      * @static
  539.      *
  540.      * @param string $url URL to Request
  541.      * @param array $args Request arguments
  542.      * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
  543.      */
  544.     private function _dispatch_request( $url, $args ) {
  545.         static $transports = array();
  546.  
  547.         $class = $this->_get_first_available_transport( $args, $url );
  548.         if ( !$class )
  549.             return new WP_Error( 'http_failure', __( 'There are no HTTP transports available which can complete the requested request.' ) );
  550.  
  551.         // Transport claims to support request, instantiate it and give it a whirl.
  552.         if ( empty( $transports[$class] ) )
  553.             $transports[$class] = new $class;
  554.  
  555.         $response = $transports[$class]->request( $url, $args );
  556.  
  557.         /** This action is documented in wp-includes/class-http.php */
  558.         do_action( 'http_api_debug', $response, 'response', $class, $args, $url );
  559.  
  560.         if ( is_wp_error( $response ) )
  561.             return $response;
  562.  
  563.         /**
  564.          * Filters the HTTP API response immediately before the response is returned.
  565.          *
  566.          * @since 2.9.0
  567.          *
  568.          * @param array  $response HTTP response.
  569.          * @param array  $args     HTTP request arguments.
  570.          * @param string $url      The request URL.
  571.          */
  572.         return apply_filters( 'http_response', $response, $args, $url );
  573.     }
  574.  
  575.     /**
  576.      * Uses the POST HTTP method.
  577.      *
  578.      * Used for sending data that is expected to be in the body.
  579.      *
  580.      * @since 2.7.0
  581.      *
  582.      * @param string       $url  The request URL.
  583.      * @param string|array $args Optional. Override the defaults.
  584.      * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
  585.      */
  586.     public function post($url, $args = array()) {
  587.         $defaults = array('method' => 'POST');
  588.         $r = wp_parse_args( $args, $defaults );
  589.         return $this->request($url, $r);
  590.     }
  591.  
  592.     /**
  593.      * Uses the GET HTTP method.
  594.      *
  595.      * Used for sending data that is expected to be in the body.
  596.      *
  597.      * @since 2.7.0
  598.      *
  599.      * @param string $url The request URL.
  600.      * @param string|array $args Optional. Override the defaults.
  601.      * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
  602.      */
  603.     public function get($url, $args = array()) {
  604.         $defaults = array('method' => 'GET');
  605.         $r = wp_parse_args( $args, $defaults );
  606.         return $this->request($url, $r);
  607.     }
  608.  
  609.     /**
  610.      * Uses the HEAD HTTP method.
  611.      *
  612.      * Used for sending data that is expected to be in the body.
  613.      *
  614.      * @since 2.7.0
  615.      *
  616.      * @param string $url The request URL.
  617.      * @param string|array $args Optional. Override the defaults.
  618.      * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error
  619.      */
  620.     public function head($url, $args = array()) {
  621.         $defaults = array('method' => 'HEAD');
  622.         $r = wp_parse_args( $args, $defaults );
  623.         return $this->request($url, $r);
  624.     }
  625.  
  626.     /**
  627.      * Parses the responses and splits the parts into headers and body.
  628.      *
  629.      * @static
  630.      * @since 2.7.0
  631.      *
  632.      * @param string $strResponse The full response string
  633.      * @return array Array with 'headers' and 'body' keys.
  634.      */
  635.     public static function processResponse($strResponse) {
  636.         $res = explode("\r\n\r\n", $strResponse, 2);
  637.  
  638.         return array('headers' => $res[0], 'body' => isset($res[1]) ? $res[1] : '');
  639.     }
  640.  
  641.     /**
  642.      * Transform header string into an array.
  643.      *
  644.      * If an array is given then it is assumed to be raw header data with numeric keys with the
  645.      * headers as the values. No headers must be passed that were already processed.
  646.      *
  647.      * @static
  648.      * @since 2.7.0
  649.      *
  650.      * @param string|array $headers
  651.      * @param string $url The URL that was requested
  652.      * @return array Processed string headers. If duplicate headers are encountered,
  653.      *                     Then a numbered array is returned as the value of that header-key.
  654.      */
  655.     public static function processHeaders( $headers, $url = '' ) {
  656.         // Split headers, one per array element.
  657.         if ( is_string($headers) ) {
  658.             // Tolerate line terminator: CRLF = LF (RFC 2616 19.3).
  659.             $headers = str_replace("\r\n", "\n", $headers);
  660.             /*
  661.              * Unfold folded header fields. LWS = [CRLF] 1*( SP | HT ) <US-ASCII SP, space (32)>,
  662.              * <US-ASCII HT, horizontal-tab (9)> (RFC 2616 2.2).
  663.              */
  664.             $headers = preg_replace('/\n[ \t]/', ' ', $headers);
  665.             // Create the headers array.
  666.             $headers = explode("\n", $headers);
  667.         }
  668.  
  669.         $response = array('code' => 0, 'message' => '');
  670.  
  671.         /*
  672.          * If a redirection has taken place, The headers for each page request may have been passed.
  673.          * In this case, determine the final HTTP header and parse from there.
  674.          */
  675.         for ( $i = count($headers)-1; $i >= 0; $i-- ) {
  676.             if ( !empty($headers[$i]) && false === strpos($headers[$i], ':') ) {
  677.                 $headers = array_splice($headers, $i);
  678.                 break;
  679.             }
  680.         }
  681.  
  682.         $cookies = array();
  683.         $newheaders = array();
  684.         foreach ( (array) $headers as $tempheader ) {
  685.             if ( empty($tempheader) )
  686.                 continue;
  687.  
  688.             if ( false === strpos($tempheader, ':') ) {
  689.                 $stack = explode(' ', $tempheader, 3);
  690.                 $stack[] = '';
  691.                 list( , $response['code'], $response['message']) = $stack;
  692.                 continue;
  693.             }
  694.  
  695.             list($key, $value) = explode(':', $tempheader, 2);
  696.  
  697.             $key = strtolower( $key );
  698.             $value = trim( $value );
  699.  
  700.             if ( isset( $newheaders[ $key ] ) ) {
  701.                 if ( ! is_array( $newheaders[ $key ] ) )
  702.                     $newheaders[$key] = array( $newheaders[ $key ] );
  703.                 $newheaders[ $key ][] = $value;
  704.             } else {
  705.                 $newheaders[ $key ] = $value;
  706.             }
  707.             if ( 'set-cookie' == $key )
  708.                 $cookies[] = new WP_Http_Cookie( $value, $url );
  709.         }
  710.  
  711.         // Cast the Response Code to an int
  712.         $response['code'] = intval( $response['code'] );
  713.  
  714.         return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies);
  715.     }
  716.  
  717.     /**
  718.      * Takes the arguments for a ::request() and checks for the cookie array.
  719.      *
  720.      * If it's found, then it upgrades any basic name => value pairs to WP_Http_Cookie instances,
  721.      * which are each parsed into strings and added to the Cookie: header (within the arguments array).
  722.      * Edits the array by reference.
  723.      *
  724.      * @since 2.8.0
  725.      * @static
  726.      *
  727.      * @param array $r Full array of args passed into ::request()
  728.      */
  729.     public static function buildCookieHeader( &$r ) {
  730.         if ( ! empty($r['cookies']) ) {
  731.             // Upgrade any name => value cookie pairs to WP_HTTP_Cookie instances.
  732.             foreach ( $r['cookies'] as $name => $value ) {
  733.                 if ( ! is_object( $value ) )
  734.                     $r['cookies'][ $name ] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value ) );
  735.             }
  736.  
  737.             $cookies_header = '';
  738.             foreach ( (array) $r['cookies'] as $cookie ) {
  739.                 $cookies_header .= $cookie->getHeaderValue() . '; ';
  740.             }
  741.  
  742.             $cookies_header = substr( $cookies_header, 0, -2 );
  743.             $r['headers']['cookie'] = $cookies_header;
  744.         }
  745.     }
  746.  
  747.     /**
  748.      * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
  749.      *
  750.      * Based off the HTTP http_encoding_dechunk function.
  751.      *
  752.      * @link https://tools.ietf.org/html/rfc2616#section-19.4.6 Process for chunked decoding.
  753.      *
  754.      * @since 2.7.0
  755.      * @static
  756.      *
  757.      * @param string $body Body content
  758.      * @return string Chunked decoded body on success or raw body on failure.
  759.      */
  760.     public static function chunkTransferDecode( $body ) {
  761.         // The body is not chunked encoded or is malformed.
  762.         if ( ! preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', trim( $body ) ) )
  763.             return $body;
  764.  
  765.         $parsed_body = '';
  766.  
  767.         // We'll be altering $body, so need a backup in case of error.
  768.         $body_original = $body;
  769.  
  770.         while ( true ) {
  771.             $has_chunk = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $body, $match );
  772.             if ( ! $has_chunk || empty( $match[1] ) )
  773.                 return $body_original;
  774.  
  775.             $length = hexdec( $match[1] );
  776.             $chunk_length = strlen( $match[0] );
  777.  
  778.             // Parse out the chunk of data.
  779.             $parsed_body .= substr( $body, $chunk_length, $length );
  780.  
  781.             // Remove the chunk from the raw data.
  782.             $body = substr( $body, $length + $chunk_length );
  783.  
  784.             // End of the document.
  785.             if ( '0' === trim( $body ) )
  786.                 return $parsed_body;
  787.         }
  788.     }
  789.  
  790.     /**
  791.      * Block requests through the proxy.
  792.      *
  793.      * Those who are behind a proxy and want to prevent access to certain hosts may do so. This will
  794.      * prevent plugins from working and core functionality, if you don't include api.wordpress.org.
  795.      *
  796.      * You block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL as true in your wp-config.php
  797.      * file and this will only allow localhost and your site to make requests. The constant
  798.      * WP_ACCESSIBLE_HOSTS will allow additional hosts to go through for requests. The format of the
  799.      * WP_ACCESSIBLE_HOSTS constant is a comma separated list of hostnames to allow, wildcard domains
  800.      * are supported, eg *.wordpress.org will allow for all subdomains of wordpress.org to be contacted.
  801.      *
  802.      * @since 2.8.0
  803.      * @link https://core.trac.wordpress.org/ticket/8927 Allow preventing external requests.
  804.      * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_ACCESSIBLE_HOSTS
  805.      *
  806.      * @staticvar array|null $accessible_hosts
  807.      * @staticvar array      $wildcard_regex
  808.      *
  809.      * @param string $uri URI of url.
  810.      * @return bool True to block, false to allow.
  811.      */
  812.     public function block_request($uri) {
  813.         // We don't need to block requests, because nothing is blocked.
  814.         if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL )
  815.             return false;
  816.  
  817.         $check = parse_url($uri);
  818.         if ( ! $check )
  819.             return true;
  820.  
  821.         $home = parse_url( get_option('siteurl') );
  822.  
  823.         // Don't block requests back to ourselves by default.
  824.         if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) ) {
  825.             /**
  826.              * Filters whether to block local requests through the proxy.
  827.              *
  828.              * @since 2.8.0
  829.              *
  830.              * @param bool $block Whether to block local requests through proxy.
  831.              *                    Default false.
  832.              */
  833.             return apply_filters( 'block_local_requests', false );
  834.         }
  835.  
  836.         if ( !defined('WP_ACCESSIBLE_HOSTS') )
  837.             return true;
  838.  
  839.         static $accessible_hosts = null;
  840.         static $wildcard_regex = array();
  841.         if ( null === $accessible_hosts ) {
  842.             $accessible_hosts = preg_split('|,\s*|', WP_ACCESSIBLE_HOSTS);
  843.  
  844.             if ( false !== strpos(WP_ACCESSIBLE_HOSTS, '*') ) {
  845.                 $wildcard_regex = array();
  846.                 foreach ( $accessible_hosts as $host )
  847.                     $wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) );
  848.                 $wildcard_regex = '/^(' . implode('|', $wildcard_regex) . ')$/i';
  849.             }
  850.         }
  851.  
  852.         if ( !empty($wildcard_regex) )
  853.             return !preg_match($wildcard_regex, $check['host']);
  854.         else
  855.             return !in_array( $check['host'], $accessible_hosts ); //Inverse logic, If it's in the array, then we can't access it.
  856.  
  857.     }
  858.  
  859.     /**
  860.      * Used as a wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7.
  861.      *
  862.      * @deprecated 4.4.0 Use wp_parse_url()
  863.      * @see wp_parse_url()
  864.      *
  865.      * @param string $url The URL to parse.
  866.      * @return bool|array False on failure; Array of URL components on success;
  867.      *                    See parse_url()'s return values.
  868.      */
  869.     protected static function parse_url( $url ) {
  870.         _deprecated_function( __METHOD__, '4.4.0', 'wp_parse_url()' );
  871.         return wp_parse_url( $url );
  872.     }
  873.  
  874.     /**
  875.      * Converts a relative URL to an absolute URL relative to a given URL.
  876.      *
  877.      * If an Absolute URL is provided, no processing of that URL is done.
  878.      *
  879.      * @since 3.4.0
  880.      *
  881.      * @static
  882.      *
  883.      * @param string $maybe_relative_path The URL which might be relative
  884.      * @param string $url                 The URL which $maybe_relative_path is relative to
  885.      * @return string An Absolute URL, in a failure condition where the URL cannot be parsed, the relative URL will be returned.
  886.      */
  887.     public static function make_absolute_url( $maybe_relative_path, $url ) {
  888.         if ( empty( $url ) )
  889.             return $maybe_relative_path;
  890.  
  891.         if ( ! $url_parts = wp_parse_url( $url ) ) {
  892.             return $maybe_relative_path;
  893.         }
  894.  
  895.         if ( ! $relative_url_parts = wp_parse_url( $maybe_relative_path ) ) {
  896.             return $maybe_relative_path;
  897.         }
  898.  
  899.         // Check for a scheme on the 'relative' url
  900.         if ( ! empty( $relative_url_parts['scheme'] ) ) {
  901.             return $maybe_relative_path;
  902.         }
  903.  
  904.         $absolute_path = $url_parts['scheme'] . '://';
  905.  
  906.         // Schemeless URL's will make it this far, so we check for a host in the relative url and convert it to a protocol-url
  907.         if ( isset( $relative_url_parts['host'] ) ) {
  908.             $absolute_path .= $relative_url_parts['host'];
  909.             if ( isset( $relative_url_parts['port'] ) )
  910.                 $absolute_path .= ':' . $relative_url_parts['port'];
  911.         } else {
  912.             $absolute_path .= $url_parts['host'];
  913.             if ( isset( $url_parts['port'] ) )
  914.                 $absolute_path .= ':' . $url_parts['port'];
  915.         }
  916.  
  917.         // Start off with the Absolute URL path.
  918.         $path = ! empty( $url_parts['path'] ) ? $url_parts['path'] : '/';
  919.  
  920.         // If it's a root-relative path, then great.
  921.         if ( ! empty( $relative_url_parts['path'] ) && '/' == $relative_url_parts['path'][0] ) {
  922.             $path = $relative_url_parts['path'];
  923.  
  924.         // Else it's a relative path.
  925.         } elseif ( ! empty( $relative_url_parts['path'] ) ) {
  926.             // Strip off any file components from the absolute path.
  927.             $path = substr( $path, 0, strrpos( $path, '/' ) + 1 );
  928.  
  929.             // Build the new path.
  930.             $path .= $relative_url_parts['path'];
  931.  
  932.             // Strip all /path/../ out of the path.
  933.             while ( strpos( $path, '../' ) > 1 ) {
  934.                 $path = preg_replace( '![^/]+/\.\./!', '', $path );
  935.             }
  936.  
  937.             // Strip any final leading ../ from the path.
  938.             $path = preg_replace( '!^/(\.\./)+!', '', $path );
  939.         }
  940.  
  941.         // Add the Query string.
  942.         if ( ! empty( $relative_url_parts['query'] ) )
  943.             $path .= '?' . $relative_url_parts['query'];
  944.  
  945.         return $absolute_path . '/' . ltrim( $path, '/' );
  946.     }
  947.  
  948.     /**
  949.      * Handles HTTP Redirects and follows them if appropriate.
  950.      *
  951.      * @since 3.7.0
  952.      * @static
  953.      *
  954.      * @param string $url The URL which was requested.
  955.      * @param array $args The Arguments which were used to make the request.
  956.      * @param array $response The Response of the HTTP request.
  957.      * @return false|object False if no redirect is present, a WP_HTTP or WP_Error result otherwise.
  958.      */
  959.     public static function handle_redirects( $url, $args, $response ) {
  960.         // If no redirects are present, or, redirects were not requested, perform no action.
  961.         if ( ! isset( $response['headers']['location'] ) || 0 === $args['_redirection'] )
  962.             return false;
  963.  
  964.         // Only perform redirections on redirection http codes.
  965.         if ( $response['response']['code'] > 399 || $response['response']['code'] < 300 )
  966.             return false;
  967.  
  968.         // Don't redirect if we've run out of redirects.
  969.         if ( $args['redirection']-- <= 0 )
  970.             return new WP_Error( 'http_request_failed', __('Too many redirects.') );
  971.  
  972.         $redirect_location = $response['headers']['location'];
  973.  
  974.         // If there were multiple Location headers, use the last header specified.
  975.         if ( is_array( $redirect_location ) )
  976.             $redirect_location = array_pop( $redirect_location );
  977.  
  978.         $redirect_location = WP_Http::make_absolute_url( $redirect_location, $url );
  979.  
  980.         // POST requests should not POST to a redirected location.
  981.         if ( 'POST' == $args['method'] ) {
  982.             if ( in_array( $response['response']['code'], array( 302, 303 ) ) )
  983.                 $args['method'] = 'GET';
  984.         }
  985.  
  986.         // Include valid cookies in the redirect process.
  987.         if ( ! empty( $response['cookies'] ) ) {
  988.             foreach ( $response['cookies'] as $cookie ) {
  989.                 if ( $cookie->test( $redirect_location ) )
  990.                     $args['cookies'][] = $cookie;
  991.             }
  992.         }
  993.  
  994.         return wp_remote_request( $redirect_location, $args );
  995.     }
  996.  
  997.     /**
  998.      * Determines if a specified string represents an IP address or not.
  999.      *
  1000.      * This function also detects the type of the IP address, returning either
  1001.      * '4' or '6' to represent a IPv4 and IPv6 address respectively.
  1002.      * This does not verify if the IP is a valid IP, only that it appears to be
  1003.      * an IP address.
  1004.      *
  1005.      * @link http://home.deds.nl/~aeron/regex/ for IPv6 regex
  1006.      *
  1007.      * @since 3.7.0
  1008.      * @static
  1009.      *
  1010.      * @param string $maybe_ip A suspected IP address
  1011.      * @return integer|bool Upon success, '4' or '6' to represent a IPv4 or IPv6 address, false upon failure
  1012.      */
  1013.     public static function is_ip_address( $maybe_ip ) {
  1014.         if ( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $maybe_ip ) )
  1015.             return 4;
  1016.  
  1017.         if ( false !== strpos( $maybe_ip, ':' ) && preg_match( '/^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i', trim( $maybe_ip, ' []' ) ) )
  1018.             return 6;
  1019.  
  1020.         return false;
  1021.     }
  1022.  
  1023. }
  1024.