WordPress.org

Make WordPress Core

Ticket #4011: 4011.2.diff

File 4011.2.diff, 40.5 KB (added by jacobsantos, 9 years ago)

HTTP API cleanup with proxy support for HTTP, CURL and Streams transports.

  • http.php

     
    1313 */
    1414
    1515/**
    16  * Implementation for deflate and gzip transfer encodings.
    17  *
    18  * Includes RFC 1950, RFC 1951, and RFC 1952.
    19  *
    20  * @since unknown
    21  * @package WordPress
    22  * @subpackage HTTP
    23  */
    24 class WP_Http_Encoding {
    25 
    26         /**
    27          * Compress raw string using the deflate format.
    28          *
    29          * Supports the RFC 1951 standard.
    30          *
    31          * @since unknown
    32          *
    33          * @param string $raw String to compress.
    34          * @param int $level Optional, default is 9. Compression level, 9 is highest.
    35          * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports.
    36          * @return string|bool False on failure.
    37          */
    38         function compress( $raw, $level = 9, $supports = null ) {
    39                 return gzdeflate( $raw, $level );
    40         }
    41 
    42         /**
    43          * Decompression of deflated string.
    44          *
    45          * Will attempt to decompress using the RFC 1950 standard, and if that fails
    46          * then the RFC 1951 standard deflate will be attempted. Finally, the RFC
    47          * 1952 standard gzip decode will be attempted. If all fail, then the
    48          * original compressed string will be returned.
    49          *
    50          * @since unknown
    51          *
    52          * @param string $compressed String to decompress.
    53          * @param int $length The optional length of the compressed data.
    54          * @return string|bool False on failure.
    55          */
    56         function decompress( $compressed, $length = null ) {
    57                 $decompressed = gzinflate( $compressed );
    58 
    59                 if( false !== $decompressed )
    60                         return $decompressed;
    61 
    62                 $decompressed = gzuncompress( $compressed );
    63 
    64                 if( false !== $decompressed )
    65                         return $decompressed;
    66 
    67                 $decompressed = gzdecode( $compressed );
    68 
    69                 if( false !== $decompressed )
    70                         return $decompressed;
    71 
    72                 return $compressed;
    73         }
    74 
    75         /**
    76          * What encoding types to accept and their priority values.
    77          *
    78          * @since unknown
    79          *
    80          * @return string Types of encoding to accept.
    81          */
    82         function accept_encoding() {
    83                 $type = array();
    84                 if( function_exists( 'gzinflate' ) )
    85                         $type[] = 'deflate;q=1.0';
    86 
    87                 if( function_exists( 'gzuncompress' ) )
    88                         $type[] = 'compress;q=0.5';
    89 
    90                 if( function_exists( 'gzdecode' ) )
    91                         $type[] = 'gzip;q=0.5';
    92 
    93                 return implode(', ', $type);
    94         }
    95 
    96         /**
    97          * What enconding the content used when it was compressed to send in the headers.
    98          *
    99          * @since unknown
    100          *
    101          * @return string Content-Encoding string to send in the header.
    102          */
    103         function content_encoding() {
    104                 return 'deflate';
    105         }
    106 
    107         /**
    108          * Whether the content be decoded based on the headers.
    109          *
    110          * @since unknown
    111          *
    112          * @param array|string $headers All of the available headers.
    113          * @return bool
    114          */
    115         function should_decode($headers) {
    116                 if( is_array( $headers ) ) {
    117                         if( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) )
    118                                 return true;
    119                 } else if( is_string( $headers ) ) {
    120                         return ( stripos($headers, 'content-encoding:') !== false );
    121                 }
    122 
    123                 return false;
    124         }
    125 
    126         /**
    127          * Whether decompression and compression are supported by the PHP version.
    128          *
    129          * Each function is tested instead of checking for the zlib extension, to
    130          * ensure that the functions all exist in the PHP version and aren't
    131          * disabled.
    132          *
    133          * @since unknown
    134          *
    135          * @return bool
    136          */
    137         function is_available() {
    138                 return ( function_exists('gzuncompress') || function_exists('gzdeflate') ||
    139                                  function_exists('gzinflate') );
    140         }
    141 }
    142 
    143 /**
    14416 * WordPress HTTP Class for managing HTTP Transports and making HTTP requests.
    14517 *
    146  * This class is called for the functionality of making HTTP requests and should
    147  * replace Snoopy functionality, eventually. There is no available functionality
    148  * to add HTTP transport implementations, since most of the HTTP transports are
    149  * added and available for use.
     18 * This class is called for the functionality of making HTTP requests and should replace Snoopy
     19 * functionality, eventually. There is no available functionality to add HTTP transport
     20 * implementations, since most of the HTTP transports are added and available for use.
    15021 *
    151  * The exception is that cURL is not available as a transport and lacking an
    152  * implementation. It will be added later and should be a patch on the WordPress
    153  * Trac.
     22 * The exception is that cURL is not available as a transport and lacking an implementation. It will
     23 * be added later and should be a patch on the WordPress Trac.
    15424 *
    155  * There are no properties, because none are needed and for performance reasons.
    156  * Some of the functions are static and while they do have some overhead over
    157  * functions in PHP4, the purpose is maintainability. When PHP5 is finally the
    158  * requirement, it will be easy to add the static keyword to the code. It is not
    159  * as easy to convert a function to a method after enough code uses the old way.
     25 * There are no properties, because none are needed and for performance reasons. Some of the
     26 * functions are static and while they do have some overhead over functions in PHP4, the purpose is
     27 * maintainability. When PHP5 is finally the requirement, it will be easy to add the static keyword
     28 * to the code. It is not as easy to convert a function to a method after enough code uses the old
     29 * way.
    16030 *
    161  * Debugging includes several actions, which pass different variables for
    162  * debugging the HTTP API.
     31 * Debugging includes several actions, which pass different variables for debugging the HTTP API.
    16332 *
    164  * <strong>http_transport_get_debug</strong> - gives working, nonblocking, and
    165  * blocking transports.
     33 * <strong>http_transport_get_debug</strong> - gives working, nonblocking, and blocking transports.
    16634 *
    167  * <strong>http_transport_post_debug</strong> - gives working, nonblocking, and
    168  * blocking transports.
     35 * <strong>http_transport_post_debug</strong> - gives working, nonblocking, and blocking transports.
    16936 *
    17037 * @package WordPress
    17138 * @subpackage HTTP
     
    22895                static $working_transport, $blocking_transport, $nonblocking_transport;
    22996
    23097                if ( is_null($working_transport) ) {
    231                         if ( true === WP_Http_ExtHttp::test() && apply_filters('use_http_extension_transport', true) ) {
     98                        if ( true === WP_Http_ExtHttp::test() ) {
    23299                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
    233100                                $blocking_transport[] = &$working_transport['exthttp'];
    234                         } else if ( true === WP_Http_Curl::test() && apply_filters('use_curl_transport', true) ) {
     101                        } else if ( true === WP_Http_Curl::test() ) {
    235102                                $working_transport['curl'] = new WP_Http_Curl();
    236103                                $blocking_transport[] = &$working_transport['curl'];
    237                         } else if ( true === WP_Http_Streams::test() && apply_filters('use_streams_transport', true) ) {
     104                        } else if ( true === WP_Http_Streams::test() ) {
    238105                                $working_transport['streams'] = new WP_Http_Streams();
    239106                                $blocking_transport[] = &$working_transport['streams'];
    240                         } else if ( true === WP_Http_Fopen::test() && apply_filters('use_fopen_transport', true) && ( isset($args['ssl']) && !$args['ssl'] ) ) {
     107                        } else if ( true === WP_Http_Fopen::test() && ( isset($args['ssl']) && ! $args['ssl'] ) ) {
    241108                                $working_transport['fopen'] = new WP_Http_Fopen();
    242109                                $blocking_transport[] = &$working_transport['fopen'];
    243                         } else if ( true === WP_Http_Fsockopen::test() && apply_filters('use_fsockopen_transport', true) && ( isset($args['ssl']) && !$args['ssl'] ) ) {
     110                        } else if ( true === WP_Http_Fsockopen::test() && ( isset($args['ssl']) && ! $args['ssl'] ) ) {
    244111                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    245112                                $blocking_transport[] = &$working_transport['fsockopen'];
    246113                        }
     
    279146                static $working_transport, $blocking_transport, $nonblocking_transport;
    280147
    281148                if ( is_null($working_transport) ) {
    282                         if ( true === WP_Http_ExtHttp::test() && apply_filters('use_http_extension_transport', true) ) {
     149                        if ( true === WP_Http_ExtHttp::test() ) {
    283150                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
    284151                                $blocking_transport[] = &$working_transport['exthttp'];
    285                         } else if ( true === WP_Http_Curl::test() && apply_filters('use_curl_transport', true) ) {
     152                        } else if ( true === WP_Http_Curl::test() ) {
    286153                                $working_transport['curl'] = new WP_Http_Curl();
    287154                                $blocking_transport[] = &$working_transport['curl'];
    288                         } else if ( true === WP_Http_Streams::test() && apply_filters('use_streams_transport', true) ) {
     155                        } else if ( true === WP_Http_Streams::test() ) {
    289156                                $working_transport['streams'] = new WP_Http_Streams();
    290157                                $blocking_transport[] = &$working_transport['streams'];
    291                         } else if ( true === WP_Http_Fsockopen::test() && apply_filters('use_fsockopen_transport', true) && ( isset($args['ssl']) && !$args['ssl'] ) ) {
     158                        } else if ( true === WP_Http_Fsockopen::test() && ( isset($args['ssl']) && ! $args['ssl'] ) ) {
    292159                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
    293160                                $blocking_transport[] = &$working_transport['fsockopen'];
    294161                        }
     
    311178        /**
    312179         * Send a HTTP request to a URI.
    313180         *
    314          * The body and headers are part of the arguments. The 'body' argument is
    315          * for the body and will accept either a string or an array. The 'headers'
    316          * argument should be an array, but a string is acceptable. If the 'body'
    317          * argument is an array, then it will automatically be escaped using
    318          * http_build_query().
     181         * The body and headers are part of the arguments. The 'body' argument is for the body and will
     182         * accept either a string or an array. The 'headers' argument should be an array, but a string
     183         * is acceptable. If the 'body' argument is an array, then it will automatically be escaped
     184         * using http_build_query().
    319185         *
    320          * The only URI that are supported in the HTTP Transport implementation are
    321          * the HTTP and HTTPS protocols. HTTP and HTTPS are assumed so the server
    322          * might not know how to handle the send headers. Other protocols are
    323          * unsupported and most likely will fail.
     186         * The only URI that are supported in the HTTP Transport implementation are the HTTP and HTTPS
     187         * protocols. HTTP and HTTPS are assumed so the server might not know how to handle the send
     188         * headers. Other protocols are unsupported and most likely will fail.
    324189         *
    325          * The defaults are 'method', 'timeout', 'redirection', 'httpversion',
    326          * 'blocking' and 'user-agent'.
     190         * The defaults are 'method', 'timeout', 'redirection', 'httpversion', 'blocking' and
     191         * 'user-agent'.
    327192         *
    328          * Accepted 'method' values are 'GET', 'POST', and 'HEAD', some transports
    329          * technically allow others, but should not be assumed. The 'timeout' is
    330          * used to sent how long the connection should stay open before failing when
    331          * no response. 'redirection' is used to track how many redirects were taken
    332          * and used to sent the amount for other transports, but not all transports
     193         * Accepted 'method' values are 'GET', 'POST', and 'HEAD', some transports technically allow
     194         * others, but should not be assumed. The 'timeout' is used to sent how long the connection
     195         * should stay open before failing when no response. 'redirection' is used to track how many
     196         * redirects were taken and used to sent the amount for other transports, but not all transports
    333197         * accept setting that value.
    334198         *
    335          * The 'httpversion' option is used to sent the HTTP version and accepted
    336          * values are '1.0', and '1.1' and should be a string. Version 1.1 is not
    337          * supported, because of chunk response. The 'user-agent' option is the
    338          * user-agent and is used to replace the default user-agent, which is
     199         * The 'httpversion' option is used to sent the HTTP version and accepted values are '1.0', and
     200         * '1.1' and should be a string. Version 1.1 is not supported, because of chunk response. The
     201         * 'user-agent' option is the user-agent and is used to replace the default user-agent, which is
    339202         * 'WordPress/WP_Version', where WP_Version is the value from $wp_version.
    340203         *
    341          * 'blocking' is the default, which is used to tell the transport, whether
    342          * it should halt PHP while it performs the request or continue regardless.
    343          * Actually, that isn't entirely correct. Blocking mode really just means
    344          * whether the fread should just pull what it can whenever it gets bytes or
    345          * if it should wait until it has enough in the buffer to read or finishes
    346          * reading the entire content. It doesn't actually always mean that PHP will
    347          * continue going after making the request.
     204         * 'blocking' is the default, which is used to tell the transport, whether it should halt PHP
     205         * while it performs the request or continue regardless. Actually, that isn't entirely correct.
     206         * Blocking mode really just means whether the fread should just pull what it can whenever it
     207         * gets bytes or if it should wait until it has enough in the buffer to read or finishes reading
     208         * the entire content. It doesn't actually always mean that PHP will continue going after making
     209         * the request.
    348210         *
    349211         * @access public
    350212         * @since 2.7.0
     
    400262                        $r['user-agent'] = $r['headers']['user-agent'];
    401263                        unset($r['headers']['user-agent']);
    402264                }
    403                
     265
    404266                // Construct Cookie: header if any cookies are set
    405267                WP_Http::buildCookieHeader( $r );
    406268
     
    514376        /**
    515377         * Transform header string into an array.
    516378         *
    517          * If an array is given then it is assumed to be raw header data with
    518          * numeric keys with the headers as the values. No headers must be passed
    519          * that were already processed.
     379         * If an array is given then it is assumed to be raw header data with numeric keys with the
     380         * headers as the values. No headers must be passed that were already processed.
    520381         *
    521382         * @access public
    522383         * @static
     
    561422
    562423                return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies);
    563424        }
    564        
     425
    565426        /**
    566427         * Takes the arguments for a ::request() and checks for the cookie array.
    567          * If it's found, then it's assumed to contain WP_Http_Cookie objects, which
    568          * are each parsed into strings and added to the Cookie: header (within the
    569          * arguments array). Edits the array by reference.
    570428         *
     429         * If it's found, then it's assumed to contain WP_Http_Cookie objects, which are each parsed
     430         * into strings and added to the Cookie: header (within the arguments array). Edits the array by
     431         * reference.
     432         *
    571433         * @access public
     434         * @version 2.8.0
    572435         * @static
    573436         *
    574437         * @param array $r Full array of args passed into ::request()
     
    583446                        $r['headers']['cookie'] = $cookies_header;
    584447                }
    585448        }
    586        
     449
    587450        /**
    588451         * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification.
    589452         *
    590          * Based off the HTTP http_encoding_dechunk function. Does not support
    591          * UTF-8. Does not support returning footer headers. Shouldn't be too
    592          * difficult to support it though.
     453         * Based off the HTTP http_encoding_dechunk function. Does not support UTF-8. Does not support
     454         * returning footer headers. Shouldn't be too difficult to support it though.
    593455         *
    594456         * @todo Add support for footer chunked headers.
    595457         * @access public
     
    635497/**
    636498 * HTTP request method uses fsockopen function to retrieve the url.
    637499 *
    638  * This would be the preferred method, but the fsockopen implementation has the
    639  * most overhead of all the HTTP transport implementations.
     500 * This would be the preferred method, but the fsockopen implementation has the most overhead of all
     501 * the HTTP transport implementations.
    640502 *
    641503 * @package WordPress
    642504 * @subpackage HTTP
     
    696558                        $arrURL['port'] = apply_filters('http_request_port', $arrURL['port']);
    697559                }
    698560
    699                 // There are issues with the HTTPS and SSL protocols that cause errors
    700                 // that can be safely ignored and should be ignored.
     561                // There are issues with the HTTPS and SSL protocols that cause errors that can be safely
     562                // ignored and should be ignored.
    701563                if ( true === $secure_transport )
    702564                        $error_reporting = error_reporting(0);
    703565
     
    710572
    711573                $endDelay = time();
    712574
    713                 // If the delay is greater than the timeout then fsockopen should't be
    714                 // used, because it will cause a long delay.
     575                // If the delay is greater than the timeout then fsockopen should't be used, because it will
     576                // cause a long delay.
    715577                $elapseDelay = ($endDelay-$startDelay) > $r['timeout'];
    716578                if ( true === $elapseDelay )
    717579                        add_option( 'disable_fsockopen', $endDelay, null, true );
     
    719581                if ( false === $handle )
    720582                        return new WP_Error('http_request_failed', $iError . ': ' . $strError);
    721583
    722                 // WordPress supports PHP 4.3, which has this function. Removed sanity
    723                 // checking for performance reasons.
     584                // WordPress supports PHP 4.3, which has this function. Removed sanity checking for
     585                // performance reasons.
    724586                stream_set_timeout($handle, $r['timeout'] );
    725587
    726588                $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' );
     
    798660                if ( false !== ($option = get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours
    799661                        return false;
    800662
    801                 if ( function_exists( 'fsockopen' ) )
     663                if ( function_exists( 'fsockopen' ) && apply_filters('use_fsockopen_transport', true) )
    802664                        return true;
    803665
    804666                return false;
     
    808670/**
    809671 * HTTP request method uses fopen function to retrieve the url.
    810672 *
    811  * Requires PHP version greater than 4.3.0 for stream support. Does not allow
    812  * for $context support, but should still be okay, to write the headers, before
    813  * getting the response. Also requires that 'allow_url_fopen' to be enabled.
     673 * Requires PHP version greater than 4.3.0 for stream support. Does not allow for $context support,
     674 * but should still be okay, to write the headers, before getting the response. Also requires that
     675 * 'allow_url_fopen' to be enabled.
    814676 *
    815677 * @package WordPress
    816678 * @subpackage HTTP
     
    820682        /**
    821683         * Send a HTTP request to a URI using fopen().
    822684         *
    823          * This transport does not support sending of headers and body, therefore
    824          * should not be used in the instances, where there is a body and headers.
     685         * This transport does not support sending of headers and body, therefore should not be used in
     686         * the instances, where there is a body and headers.
    825687         *
    826688         * Notes: Does not support non-blocking mode. Ignores 'redirection' option.
    827689         *
     
    911773                if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )
    912774                        return false;
    913775
    914                 return true;
     776                return apply_filters('use_fopen_transport', true);
    915777        }
    916778}
    917779
    918780/**
    919781 * HTTP request method uses Streams to retrieve the url.
    920782 *
    921  * Requires PHP 5.0+ and uses fopen with stream context. Requires that
    922  * 'allow_url_fopen' PHP setting to be enabled.
     783 * Requires PHP 5.0+ and uses fopen with stream context. Requires that 'allow_url_fopen' PHP setting
     784 * to be enabled.
    923785 *
    924786 * Second preferred method for getting the URL, for PHP 5.
    925787 *
     
    955817                        $r['user-agent'] = $r['headers']['user-agent'];
    956818                        unset($r['headers']['user-agent']);
    957819                }
    958                
     820
    959821                // Construct Cookie: header if any cookies are set
    960822                WP_Http::buildCookieHeader( $r );
    961823
     
    984846                                'header' => $strHeaders,
    985847                                'timeout' => $r['timeout'],
    986848                                'ssl' => array(
    987                                         'verify_peer' => apply_filters('https_ssl_verify', $r['sslverify']),
    988                                         'verify_host' => apply_filters('https_ssl_verify', $r['sslverify'])
    989                                 )
     849                                                'verify_peer' => apply_filters('https_ssl_verify', $r['sslverify']),
     850                                                'verify_host' => apply_filters('https_ssl_verify', $r['sslverify'])
     851                                )
    990852                        )
    991853                );
    992854
     855                $proxy = new WP_HTTP_Proxy();
     856
     857                if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
     858                        // WTF happens when the user stupidly puts http:// in the host? Bad things. Really,
     859                        // really bad things happen. Not exactly unleashing demons from hell, but you know.
     860                        // Just as bad probably. Actually, none of the other transports should work in that case
     861                        // so no sanitization is done. If there is, it should be done in the
     862                        // WP_HTTP_Proxy::host().
     863                        //
     864                        // On second thought, why is it tcp protocol? Why not http? What if the proxy is http
     865                        // and not tcp? Even though, technically, http is tcp, but just a specialized form of
     866                        // tcp. Behavior is undefined, so it is probably good that not a lot of users will be
     867                        // affected by FUBAR behavior.
     868                        //
     869                        // Is it another WTF? that this multiline comment isn't using multiline comment syntax?
     870                        $arrContext['http']['proxy'] = 'tcp://'.$proxy->host().':'$proxy->port();
     871
     872                        // Yeah, this may or may not work. Good luck.
     873                        if ( $proxy->use_authentication() ) {
     874                                $arrContext['http']['header'] .= $proxy->authentication_header() . "\r\n";
     875                        }
     876                }
     877
    993878                if ( ! is_null($r['body']) && ! empty($r['body'] ) )
    994879                        $arrContext['http']['content'] = $r['body'];
    995880
     
    1003888                if ( ! $handle)
    1004889                        return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url));
    1005890
    1006                 // WordPress supports PHP 4.3, which has this function. Removed sanity
    1007                 // checking for performance reasons.
     891                // WordPress supports PHP 4.3, which has this function. Removed sanity checking for
     892                // performance reasons.
    1008893                stream_set_timeout($handle, $r['timeout'] );
    1009894
    1010895                if ( ! $r['blocking'] ) {
     
    1049934                if ( version_compare(PHP_VERSION, '5.0', '<') )
    1050935                        return false;
    1051936
    1052                 return true;
     937                return apply_filters('use_streams_transport', true);
    1053938        }
    1054939}
    1055940
    1056941/**
    1057942 * HTTP request method uses HTTP extension to retrieve the url.
    1058943 *
    1059  * Requires the HTTP extension to be installed. This would be the preferred
    1060  * transport since it can handle a lot of the problems that forces the others to
    1061  * use the HTTP version 1.0. Even if PHP 5.2+ is being used, it doesn't mean
    1062  * that the HTTP extension will be enabled.
     944 * Requires the HTTP extension to be installed. This would be the preferred transport since it can
     945 * handle a lot of the problems that forces the others to use the HTTP version 1.0. Even if PHP 5.2+
     946 * is being used, it doesn't mean that the HTTP extension will be enabled.
    1063947 *
    1064948 * @package WordPress
    1065949 * @subpackage HTTP
     
    1095979                        $r['user-agent'] = $r['headers']['user-agent'];
    1096980                        unset($r['headers']['user-agent']);
    1097981                }
    1098                
     982
    1099983                // Construct Cookie: header if any cookies are set
    1100984                WP_Http::buildCookieHeader( $r );
    1101985
     
    11281012                        )
    11291013                );
    11301014
     1015                // The HTTP extensions offers really easy proxy support.
     1016                $proxy = new WP_HTTP_Proxy();
     1017
     1018                if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
     1019                        $options['proxyhost'] = $proxy->host();
     1020                        $options['proxyport'] = $proxy->port();
     1021                        $options['proxytype'] = HTTP_PROXY_HTTP;
     1022
     1023                        if ( $proxy->use_authentication() ) {
     1024                                $options['proxyauth'] = $proxy->authentication();
     1025                                $options['proxyauthtype'] = HTTP_AUTH_BASIC;
     1026                        }
     1027                }
     1028
    11311029                if ( !defined('WP_DEBUG') || ( defined('WP_DEBUG') && false === WP_DEBUG ) ) //Emits warning level notices for max redirects and timeouts
    11321030                        $strResponse = @http_request($r['method'], $url, $r['body'], $options, $info);
    11331031                else
    11341032                        $strResponse = http_request($r['method'], $url, $r['body'], $options, $info); //Emits warning level notices for max redirects and timeouts
    11351033
    1136                 if ( false === $strResponse || ! empty($info['error']) ) //Error may still be set, Response may return headers or partial document, and error contains a reason the request was aborted, eg, timeout expired or max-redirects reached
     1034                // Error may still be set, Response may return headers or partial document, and error
     1035                // contains a reason the request was aborted, eg, timeout expired or max-redirects reached.
     1036                if ( false === $strResponse || ! empty($info['error']) )
    11371037                        return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
    11381038
    11391039                if ( ! $r['blocking'] )
     
    11681068         * @return boolean False means this class can not be used, true means it can.
    11691069         */
    11701070        function test() {
    1171                 if ( function_exists('http_request') )
     1071                if ( function_exists('http_request') && apply_filters('use_http_extension_transport', true) )
    11721072                        return true;
    11731073
    11741074                return false;
     
    11851085 * @since 2.7
    11861086 */
    11871087class WP_Http_Curl {
     1088
    11881089        /**
    11891090         * Send a HTTP request to a URI using cURL extension.
    11901091         *
     
    12121113                        $r['user-agent'] = $r['headers']['user-agent'];
    12131114                        unset($r['headers']['user-agent']);
    12141115                }
    1215                
    1216                 // Construct Cookie: header if any cookies are set
     1116
     1117                // Construct Cookie: header if any cookies are set.
    12171118                WP_Http::buildCookieHeader( $r );
    12181119
    1219                 // cURL extension will sometimes fail when the timeout is less than 1 as
    1220                 // it may round down to 0, which gives it unlimited timeout.
     1120                // cURL extension will sometimes fail when the timeout is less than 1 as it may round down
     1121                // to 0, which gives it unlimited timeout.
    12211122                if ( $r['timeout'] > 0 && $r['timeout'] < 1 )
    12221123                        $r['timeout'] = 1;
    12231124
    12241125                $handle = curl_init();
    12251126
     1127                // cURL offers really easy proxy support.
     1128                $proxy = new WP_HTTP_Proxy();
     1129
     1130                if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {
     1131                        curl_setopt( $handle, CURLOPT_HTTPPROXYTUNNEL, true );
     1132
     1133                        $isPHP5 = version_compare(PHP_VERSION, '5.0.0', '>=');
     1134
     1135                        if ( $isPHP5 ) {
     1136                                curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
     1137                                curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() );
     1138                                curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() );
     1139                        } else {
     1140                                curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() .':'. $proxy->port() );
     1141                        }
     1142
     1143                        if ( $proxy->use_authentication() ) {
     1144                                if ( $isPHP5 )
     1145                                        curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_BASIC );
     1146
     1147                                curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );
     1148                        }
     1149                }
     1150
    12261151                curl_setopt( $handle, CURLOPT_URL, $url);
    12271152                curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true );
    12281153                curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, apply_filters('https_ssl_verify', $r['sslverify']) );
     
    12651190                else
    12661191                        curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
    12671192
    1268                 // Cookies are not handled by the HTTP API currently. Allow for plugin
    1269                 // authors to handle it themselves... Although, it is somewhat pointless
    1270                 // without some reference.
     1193                // Cookies are not handled by the HTTP API currently. Allow for plugin authors to handle it
     1194                // themselves... Although, it is somewhat pointless without some reference.
    12711195                do_action_ref_array( 'http_api_curl', array(&$handle) );
    12721196
    1273                 // We don't need to return the body, so don't. Just execute request
    1274                 // and return.
     1197                // We don't need to return the body, so don't. Just execute request and return.
    12751198                if ( ! $r['blocking'] ) {
    12761199                        curl_exec( $handle );
    12771200                        curl_close( $handle );
     
    13201243         * @return boolean False means this class can not be used, true means it can.
    13211244         */
    13221245        function test() {
    1323                 if ( function_exists('curl_init') && function_exists('curl_exec') )
     1246                if ( function_exists('curl_init') && function_exists('curl_exec') && apply_filters('use_curl_transport', true) )
    13241247                        return true;
    13251248
    13261249                return false;
    13271250        }
    13281251}
    13291252
     1253/**
     1254 * Adds Proxy support to the WordPress HTTP API.
     1255 *
     1256 * Proxy support is fringe enough that it isn't support in WordPress by default, meaning no plans
     1257 * were made to implement it. Most WordPress users are not going to benefit with its support, but
     1258 * enough people have made a stink out of the lack of it that it should be supported.
     1259 *
     1260 * There are caveats to proxy support. It requires that defines be made in the wp-config.php file to
     1261 * enable proxy support. There are also a few filters that plugins can hook into for some of the
     1262 * constants.
     1263 *
     1264 * The constants are as follows:
     1265 * <ol>
     1266 * <li>WP_PROXY_HOST - Enable proxy support and host for connecting.</li>
     1267 * <li>WP_PROXY_PORT - Proxy port for connection. No default, must be defined.</li>
     1268 * <li>WP_PROXY_USERNAME - Proxy username, if it requires authentication.</li>
     1269 * <li>WP_PROXY_PASSWORD - Proxy password, if it requires authentication.</li>
     1270 * <li>WP_PROXY_BYPASS_HOSTS - Will prevent the hosts in this list from going through the proxy.
     1271 * You do not need to have localhost and the blog host in this list, because they will not be passed
     1272 * through the proxy.</li>
     1273 * </ol>
     1274 *
     1275 * An example can be as seen below.
     1276 * <code>
     1277 * define('WP_PROXY_HOST', '192.168.84.101');
     1278 * define('WP_PROXY_PORT', '8080');
     1279 * define('WP_PROXY_BYPASS_HOSTS', array('localhost', 'www.example.com'));
     1280 * </code>
     1281 *
     1282 * @link http://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress.
     1283 * @since unknown
     1284 */
     1285class WP_HTTP_Proxy {
    13301286
     1287        function WP_HTTP_Proxy() {
     1288                $this->__construct();
     1289        }
     1290
     1291        function __construct() {
     1292               
     1293        }
     1294
     1295        /**
     1296         * Whether proxy connection should be used.
     1297         *
     1298         * @since unknown
     1299         * @use WP_PROXY_HOST
     1300         * @use WP_PROXY_PORT
     1301         *
     1302         * @return bool
     1303         */
     1304        function is_enabled() {
     1305                return ( defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT') );
     1306        }
     1307
     1308        /**
     1309         * Whether authentication should be used.
     1310         *
     1311         * @since unknown
     1312         * @use WP_PROXY_USERNAME
     1313         * @use WP_PROXY_PASSWORD
     1314         *
     1315         * @return bool
     1316         */
     1317        function use_authentication() {
     1318                return ( defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD') );
     1319        }
     1320
     1321        /**
     1322         * Retrieve the host for the proxy server.
     1323         *
     1324         * @since unknown
     1325         *
     1326         * @return string
     1327         */
     1328        function host()
     1329        {
     1330                if( defined('WP_PROXY_HOST') )
     1331                        return WP_PROXY_HOST;
     1332
     1333                return '';
     1334        }
     1335
     1336        /**
     1337         * Retrieve the port for the proxy server.
     1338         *
     1339         * @since unknown
     1340         *
     1341         * @return string
     1342         */
     1343        function port()
     1344        {
     1345                if( defined('WP_PROXY_PORT') )
     1346                        return WP_PROXY_PORT;
     1347
     1348                return '';
     1349        }
     1350
     1351        /**
     1352         * Retrieve the username for proxy authentication.
     1353         *
     1354         * @since unknown
     1355         *
     1356         * @return string
     1357         */
     1358        function username()
     1359        {
     1360                if( defined('WP_PROXY_USERNAME') )
     1361                        return WP_PROXY_USERNAME;
     1362
     1363                return '';
     1364        }
     1365
     1366        /**
     1367         * Retrieve the password for proxy authentication.
     1368         *
     1369         * @since unknown
     1370         *
     1371         * @return string
     1372         */
     1373        function password()
     1374        {
     1375                if( defined('WP_PROXY_PASSWORD') )
     1376                        return WP_PROXY_PASSWORD;
     1377
     1378                return '';
     1379        }
     1380
     1381        /**
     1382         * Retrieve authentication string for proxy authentication.
     1383         *
     1384         * @since unknown
     1385         *
     1386         * @return string
     1387         */
     1388        function authentication() {
     1389                return $this->username() .':'. $this->password();
     1390        }
     1391
     1392        /**
     1393         * Retrieve header string for proxy authentication.
     1394         *
     1395         * @since unknown
     1396         *
     1397         * @return string
     1398         */
     1399        function authentication_header() {
     1400                return 'Proxy-Authentication: Basic '. base64_encode( $this->authentication() );
     1401        }
     1402
     1403        /**
     1404         * Whether URL should be sent through the proxy server.
     1405         *
     1406         * We want to keep localhost and the blog URL from being sent through the proxy server, because
     1407         * some proxies can not handle this. We also have the constant available for defining other
     1408         * hosts that won't be sent through the proxy.
     1409         *
     1410         * @uses WP_PROXY_BYPASS_HOSTS
     1411         * @since unknown
     1412         *
     1413         * @param string $uri URI to check.
     1414         * @return bool True, to send through the proxy and false if, the proxy should not be used.
     1415         */
     1416        function send_through_proxy( $uri ) {
     1417                // parse_url() only handles http, https type URLs, and will emit E_WARNING on failure.
     1418                // This will be displayed on blogs, which is not reasonable.
     1419                $check = @parse_url($uri);
     1420
     1421                // Malformed URL, can not process, but this could mean ssl, so let through anyway.
     1422                if( $check === false )
     1423                        return true;
     1424
     1425                $home = parse_url( get_bloginfo('site_url') );
     1426
     1427                if ( $uri == 'localhost' || $uri == $home['host'] )
     1428                        return false;
     1429
     1430                if ( defined('WP_PROXY_BYPASS_HOSTS') && is_array( WP_PROXY_BYPASS_HOSTS ) && in_array( $check['host'], WP_PROXY_BYPASS_HOSTS ) ) {
     1431                                return false;
     1432                }
     1433
     1434                return true;
     1435        }
     1436}
     1437
     1438
    13311439/**
    1332  * Internal representation of a cookie.
     1440 * Internal representation of a single cookie.
    13331441 *
    1334  * Returned cookies are represented using this class, and when cookies are
    1335  * set, if they are not already a WP_Http_Cookie() object, then they are turned
    1336  * into one.
     1442 * Returned cookies are represented using this class, and when cookies are set, if they are not
     1443 * already a WP_Http_Cookie() object, then they are turned into one.
    13371444 *
     1445 * @todo The WordPress convention is to use underscores instead of camelCase for function and method
     1446 * names. Need to switch to use underscores instead for the methods.
     1447 *
    13381448 * @package WordPress
    13391449 * @subpackage HTTP
     1450 * @since 2.8.0
     1451 * @author Beau Lebens
    13401452 */
    13411453class WP_Http_Cookie {
    1342         var $name,
    1343                 $value,
    1344                 $expires,
    1345                 $path,
    1346                 $domain;
    1347        
     1454
    13481455        /**
    1349          * PHP4 style Constructor - Calls PHP5 Style Constructor
     1456         * Cookie name.
     1457         *
     1458         * @since 2.8.0
     1459         * @var string
    13501460         */
     1461        var $name;
     1462
     1463        /**
     1464         * Cookie value.
     1465         *
     1466         * @since 2.8.0
     1467         * @var string
     1468         */
     1469        var $value;
     1470
     1471        /**
     1472         * When the cookie expires.
     1473         *
     1474         * @since 2.8.0
     1475         * @var string
     1476         */
     1477        var $expires;
     1478
     1479        /**
     1480         * Cookie URL path.
     1481         *
     1482         * @since 2.8.0
     1483         * @var string
     1484         */
     1485        var $path;
     1486
     1487        /**
     1488         * Cookie Domain.
     1489         *
     1490         * @since 2.8.0
     1491         * @var string
     1492         */
     1493        var $domain;
     1494
     1495        /**
     1496         * PHP4 style Constructor - Calls PHP5 Style Constructor.
     1497         *
     1498         * @access public
     1499         * @since 2.8.0
     1500         * @param string|array $data Raw cookie data.
     1501         */
    13511502        function WP_Http_Cookie( $data ) {
    1352                 return $this->__construct( $data );
     1503                $this->__construct( $data );
    13531504        }
    1354        
     1505
    13551506        /**
    13561507         * Sets up this cookie object.
    13571508         *
     1509         * The parameter $data should be either an associative array containing the indices names below
     1510         * or a header string detailing it.
     1511         *
     1512         * If it's an array, it should include the following elements:
     1513         * <ol>
     1514         * <li>Name</li>
     1515         * <li>Value - should NOT be urlencoded already.</li>
     1516         * <li>Expires - (optional) String or int (UNIX timestamp).</li>
     1517         * <li>Path (optional)</li>
     1518         * <li>Domain (optional)</li>
     1519         * </ol>
     1520         *
    13581521         * @access public
     1522         * @since 2.8.0
    13591523         *
    1360          * @param mixed $data Either an associative array describing the cookie, or a header-string detailing it.
    1361          *              If it's an array, it should include the following elements:
    1362          *                      - name
    1363          *                      - value [should NOT be urlencoded already]
    1364          *                      - expires (optional) String or int (UNIX timestamp)
    1365          *                      - path (optional)
    1366          *                      - domain (optional)
     1524         * @param string|array $data Raw cookie data.
    13671525         */
    13681526        function __construct( $data ) {
    13691527                if ( is_string( $data ) ) {
    13701528                        // Assume it's a header string direct from a previous request
    13711529                        $pairs = explode( ';', $data );
    1372                        
     1530
    13731531                        // Special handling for first pair; name=value. Also be careful of "=" in value
    13741532                        $name  = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) );
    13751533                        $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 );
    13761534                        $this->name  = $name;
    13771535                        $this->value = urldecode( $value );
    13781536                        array_shift( $pairs ); //Removes name=value from items.
    1379                        
     1537
    13801538                        // Set everything else as a property
    13811539                        foreach ( $pairs as $pair ) {
    13821540                                if ( empty($pair) ) //Handles the cookie ending in ; which results in a empty final pair
    13831541                                        continue;
     1542
    13841543                                list( $key, $val ) = explode( '=', $pair );
    13851544                                $key = strtolower( trim( $key ) );
    13861545                                if ( 'expires' == $key )
     
    13901549                } else {
    13911550                        if ( !isset( $data['name'] ) )
    13921551                                return false;
    1393                        
     1552
    13941553                        // Set properties based directly on parameters
    13951554                        $this->name   = $data['name'];
    13961555                        $this->value  = isset( $data['value'] ) ? $data['value'] : '';
    13971556                        $this->path   = isset( $data['path'] ) ? $data['path'] : '';
    13981557                        $this->domain = isset( $data['domain'] ) ? $data['domain'] : '';
     1558
    13991559                        if ( isset( $data['expires'] ) )
    14001560                                $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] );
    14011561                        else
    14021562                                $this->expires = null;
    14031563                }
    14041564        }
    1405        
     1565
    14061566        /**
    14071567         * Confirms that it's OK to send this cookie to the URL checked against.
    14081568         *
    14091569         * Decision is based on RFC 2109/2965, so look there for details on validity.
    14101570         *
    14111571         * @access public
     1572         * @since 2.8.0
    14121573         *
    14131574         * @param string $url URL you intend to send this cookie to
    14141575         * @return boolean TRUE if allowed, FALSE otherwise.
     
    14171578                // Expires - if expired then nothing else matters
    14181579                if ( time() > $this->expires )
    14191580                        return false;
    1420                
     1581
    14211582                // Get details on the URL we're thinking about sending to
    14221583                $url = parse_url( $url );
    14231584                $url['port'] = isset( $url['port'] ) ? $url['port'] : 80;
    14241585                $url['path'] = isset( $url['path'] ) ? $url['path'] : '/';
    1425                
    1426                  // Values to use for comparison against the URL
     1586
     1587                // Values to use for comparison against the URL
    14271588                $path   = isset( $this->path )   ? $this->path   : '/';
    14281589                $port   = isset( $this->port )   ? $this->port   : 80;
    14291590                $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] );
    14301591                if ( false === stripos( $domain, '.' ) )
    14311592                        $domain .= '.local';
    1432                
     1593
    14331594                // Host - very basic check that the request URL ends with the domain restriction (minus leading dot)
    14341595                $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain;
    14351596                if ( substr( $url['host'], -strlen( $domain ) ) != $domain )
    14361597                        return false;
    1437                
     1598
    14381599                // Port - supports "port-lists" in the format: "80,8000,8080"
    14391600                if ( !in_array( $url['port'], explode( ',', $port) ) )
    14401601                        return false;
    1441                
     1602
    14421603                // Path - request path must start with path restriction
    14431604                if ( substr( $url['path'], 0, strlen( $path ) ) != $path )
    14441605                        return false;
    1445                
     1606
    14461607                return true;
    14471608        }
    1448        
     1609
     1610        /**
     1611         * Convert cookie name and value back to header string.
     1612         *
     1613         * @access public
     1614         * @since 2.8.0
     1615         *
     1616         * @return string Header encoded cookie name and value.
     1617         */
    14491618        function getHeaderValue() {
    14501619                if ( empty( $this->name ) || empty( $this->value ) )
    14511620                        return '';
    14521621               
    14531622                return $this->name . '=' . urlencode( $this->value );
    14541623        }
    1455        
     1624
     1625        /**
     1626         * Retrieve cookie header for usage in the rest of the WordPress HTTP API.
     1627         *
     1628         * @access public
     1629         * @since 2.8.0
     1630         *
     1631         * @return string
     1632         */
    14561633        function getFullHeader() {
    14571634                return 'Cookie: ' . $this->getHeaderValue();
    14581635        }
    14591636}
    14601637
    14611638/**
     1639 * Implementation for deflate and gzip transfer encodings.
     1640 *
     1641 * Includes RFC 1950, RFC 1951, and RFC 1952.
     1642 *
     1643 * @since 2.8
     1644 * @package WordPress
     1645 * @subpackage HTTP
     1646 */
     1647class WP_Http_Encoding {
     1648
     1649        /**
     1650         * Compress raw string using the deflate format.
     1651         *
     1652         * Supports the RFC 1951 standard.
     1653         *
     1654         * @since 2.8
     1655         *
     1656         * @param string $raw String to compress.
     1657         * @param int $level Optional, default is 9. Compression level, 9 is highest.
     1658         * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports.
     1659         * @return string|bool False on failure.
     1660         */
     1661        function compress( $raw, $level = 9, $supports = null ) {
     1662                return gzdeflate( $raw, $level );
     1663        }
     1664
     1665        /**
     1666         * Decompression of deflated string.
     1667         *
     1668         * Will attempt to decompress using the RFC 1950 standard, and if that fails
     1669         * then the RFC 1951 standard deflate will be attempted. Finally, the RFC
     1670         * 1952 standard gzip decode will be attempted. If all fail, then the
     1671         * original compressed string will be returned.
     1672         *
     1673         * @since 2.8
     1674         *
     1675         * @param string $compressed String to decompress.
     1676         * @param int $length The optional length of the compressed data.
     1677         * @return string|bool False on failure.
     1678         */
     1679        function decompress( $compressed, $length = null ) {
     1680                $decompressed = gzinflate( $compressed );
     1681
     1682                if( false !== $decompressed )
     1683                        return $decompressed;
     1684
     1685                $decompressed = gzuncompress( $compressed );
     1686
     1687                if( false !== $decompressed )
     1688                        return $decompressed;
     1689
     1690                $decompressed = gzdecode( $compressed );
     1691
     1692                if( false !== $decompressed )
     1693                        return $decompressed;
     1694
     1695                return $compressed;
     1696        }
     1697
     1698        /**
     1699         * What encoding types to accept and their priority values.
     1700         *
     1701         * @since 2.8
     1702         *
     1703         * @return string Types of encoding to accept.
     1704         */
     1705        function accept_encoding() {
     1706                $type = array();
     1707                if( function_exists( 'gzinflate' ) )
     1708                        $type[] = 'deflate;q=1.0';
     1709
     1710                if( function_exists( 'gzuncompress' ) )
     1711                        $type[] = 'compress;q=0.5';
     1712
     1713                if( function_exists( 'gzdecode' ) )
     1714                        $type[] = 'gzip;q=0.5';
     1715
     1716                return implode(', ', $type);
     1717        }
     1718
     1719        /**
     1720         * What enconding the content used when it was compressed to send in the headers.
     1721         *
     1722         * @since 2.8
     1723         *
     1724         * @return string Content-Encoding string to send in the header.
     1725         */
     1726        function content_encoding() {
     1727                return 'deflate';
     1728        }
     1729
     1730        /**
     1731         * Whether the content be decoded based on the headers.
     1732         *
     1733         * @since 2.8
     1734         *
     1735         * @param array|string $headers All of the available headers.
     1736         * @return bool
     1737         */
     1738        function should_decode($headers) {
     1739                if( is_array( $headers ) ) {
     1740                        if( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) )
     1741                                return true;
     1742                } else if( is_string( $headers ) ) {
     1743                        return ( stripos($headers, 'content-encoding:') !== false );
     1744                }
     1745
     1746                return false;
     1747        }
     1748
     1749        /**
     1750         * Whether decompression and compression are supported by the PHP version.
     1751         *
     1752         * Each function is tested instead of checking for the zlib extension, to
     1753         * ensure that the functions all exist in the PHP version and aren't
     1754         * disabled.
     1755         *
     1756         * @since 2.8
     1757         *
     1758         * @return bool
     1759         */
     1760        function is_available() {
     1761                return ( function_exists('gzuncompress') || function_exists('gzdeflate') ||
     1762                                 function_exists('gzinflate') );
     1763        }
     1764}
     1765
     1766/**
    14621767 * Returns the initialized WP_Http Object
    14631768 *
    14641769 * @since 2.7.0