Make WordPress Core

Changeset 8520


Ignore:
Timestamp:
08/01/2008 06:44:40 PM (18 years ago)
Author:
ryan
Message:

Initial cURL support. Doc and non-blocking mode fixes. Props santosj. see #4779

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-includes/http.php

    r8519 r8520  
    11<?php
    22/**
    3  * Simple HTTP request fallback system
     3 * Simple and uniform HTTP request API.
     4 *
     5 * Will eventually replace and standardize the WordPress HTTP requests made.
    46 *
    57 * @package WordPress
     
    1012
    1113/**
    12  * Abstract class for all of the fallback implementation
    13  * classes. The implementation classes will extend this class
    14  * to keep common API methods universal between different
    15  * functionality.
     14 * WordPress HTTP Class for managing HTTP Transports and making HTTP requests.
     15 *
     16 * This class is called for the functionality of making HTTP requests and should
     17 * replace Snoopy functionality, eventually. There is no available functionality
     18 * to add HTTP transport implementations, since most of the HTTP transports are
     19 * added and available for use.
     20 *
     21 * The exception is that cURL is not available as a transport and lacking an
     22 * implementation. It will be added later and should be a patch on the WordPress
     23 * Trac.
     24 *
     25 * There are no properties, because none are needed and for performance reasons.
     26 * Some of the functions are static and while they do have some overhead over
     27 * functions in PHP4, the purpose is maintainability. When PHP5 is finally the
     28 * requirement, it will be easy to add the static keyword to the code. It is not
     29 * as easy to convert a function to a method after enough code uses the old way.
    1630 *
    1731 * @package WordPress
     
    3448         * PHP5 style Constructor - Setup available transport if not available.
    3549         *
     50         * PHP4 does not have the 'self' keyword and since WordPress supports PHP4,
     51         * the class needs to be used for the static call.
     52         *
     53         * The transport are setup to save time. This should only be called once, so
     54         * the overhead should be fine.
     55         *
    3656         * @since 2.7
    3757         * @return WP_Http
     
    3959        function __construct() {
    4060                WP_Http::_getTransport();
     61                WP_Http::_postTransport();
    4162        }
    4263
     
    4667         * Tests all of the objects and returns the object that passes. Also caches
    4768         * that object to be used later.
     69         *
     70         * The order for the GET/HEAD requests are Streams, HTTP Extension, Fopen,
     71         * and finally Fsockopen. fsockopen() is used last, because it has the most
     72         * overhead in its implementation. There isn't any real way around it, since
     73         * redirects have to be supported, much the same way the other transports
     74         * also handle redirects.
     75         *
     76         * There are currently issues with "localhost" not resolving correctly with
     77         * DNS. This may cause an error "failed to open stream: A connection attempt
     78         * failed because the connected party did not properly respond after a
     79         * period of time, or established connection failed because connected host
     80         * has failed to respond."
    4881         *
    4982         * @since 2.7
     
    76109         * is used when there is a body. The plain Fopen Transport can not be used
    77110         * to send content, but the streams transport can. This is a limitation that
    78          * is addressed here.
     111         * is addressed here, by just not including that transport.
    79112         *
    80113         * @since 2.7
     
    99132
    100133        /**
    101          * Retrieve the location and set the class properties after the site has been retrieved.
    102          *
    103          * @access public
    104          * @since 2.7
    105          *
    106          * @param string $url
     134         * Send a HTTP request to a URI.
     135         *
     136         * The only URI that are supported in the HTTP Transport implementation are
     137         * the HTTP and HTTPS protocols. HTTP and HTTPS are assumed so the server
     138         * might not know how to handle the send headers. Other protocols are
     139         * unsupported and most likely will fail.
     140         *
     141         * The defaults are 'method', 'timeout', 'redirection', 'httpversion',
     142         * 'blocking' and 'user-agent'.
     143         *
     144         * Accepted 'method' values are 'GET', 'POST', and 'HEAD', some transports
     145         * technically allow others, but should not be assumed. The 'timeout' is
     146         * used to sent how long the connection should stay open before failing when
     147         * no response. 'redirection' is used to track how many redirects were taken
     148         * and used to sent the amount for other transports, but not all transports
     149         * accept setting that value.
     150         *
     151         * The 'httpversion' option is used to sent the HTTP version and accepted
     152         * values are '1.0', and '1.1' and should be a string. Version 1.1 is not
     153         * supported, because of chunk response. The 'user-agent' option is the
     154         * user-agent and is used to replace the default user-agent, which is
     155         * 'WordPress/WP_Version', where WP_Version is the value from $wp_version.
     156         *
     157         * 'blocking' is the default, which is used to tell the transport, whether
     158         * it should halt PHP while it performs the request or continue regardless.
     159         * Actually, that isn't entirely correct. Blocking mode really just means
     160         * whether the fread should just pull what it can whenever it gets bytes or
     161         * if it should wait until it has enough in the buffer to read or finishes
     162         * reading the entire content. It doesn't actually always mean that PHP will
     163         * continue going after making the request.
     164         *
     165         * @access public
     166         * @since 2.7
     167         *
     168         * @param string $url URI resource.
     169         * @param str|array $args Optional. Override the defaults.
    107170         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized.
    108171         * @param string $body Optional. The body that should be sent. Expected to be already processed.
    109          * @param str|array $type Optional. Should be an already processed array with HTTP arguments.
    110172         * @return boolean
    111173         */
     
    115177                $defaults = array(
    116178                        'method' => 'GET', 'timeout' => 3,
    117                         'redirection' => 5, 'redirected' => false,
    118                         'httpversion' => '1.0', 'blocking' => true
     179                        'redirection' => 5, 'httpversion' => '1.0',
     180                        'user-agent' => apply_filters('http_headers_useragent', 'WordPress/' . $wp_version ),
     181                        'blocking' => true
    119182                );
    120183
     
    129192
    130193                if ( ! isset($headers['user-agent']) || ! isset($headers['User-Agent']) )
    131                         $headers['user-agent'] = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version );
    132 
    133                 if ( is_null($body) )
     194                        $headers['user-agent'] = $r['user-agent'];
     195
     196                if ( is_null($body) || count($headers) > 1 )
    134197                        $transport = WP_Http::_getTransport();
    135198                else
     
    147210         * @since 2.7
    148211         *
    149          * @param string $url The location of the site and page to retrieve.
     212         * @param string $url URI resource.
     213         * @param str|array $args Optional. Override the defaults.
    150214         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs.
    151215         * @param string $body Optional. The body that should be sent. Expected to be already processed.
     
    166230         * @since 2.7
    167231         *
     232         * @param string $url URI resource.
     233         * @param str|array $args Optional. Override the defaults.
    168234         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs.
    169235         * @param string $body Optional. The body that should be sent. Expected to be already processed.
     
    184250         * @since 2.7
    185251         *
     252         * @param string $url URI resource.
     253         * @param str|array $args Optional. Override the defaults.
    186254         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs.
    187255         * @param string $body Optional. The body that should be sent. Expected to be already processed.
     
    228296         * Whether the headers returned a redirect location.
    229297         *
     298         * Actually just checks whether the location header exists.
     299         *
    230300         * @access public
    231301         * @static
     
    244314         * Transform header string into an array.
    245315         *
    246          * Will overwrite the last header value, if it is not empty.
     316         * If an array is given, then it will be immediately passed through with no
     317         * changes. This is to prevent overhead in processing headers that don't
     318         * need to be processed. That and it is unknown what kind of effect
     319         * processing the array will have since there is no checking done on whether
     320         * ':' does not exist within the array string.
     321         *
     322         * Checking could be added, but it is easier and faster to just passed the
     323         * array through and assume that it has already been processed.
    247324         *
    248325         * @access public
     
    251328         *
    252329         * @param string|array $headers
    253          * @return array
     330         * @return array Processed string headers
    254331         */
    255332        function processHeaders($headers) {
     
    257334                        return $headers;
    258335
    259                 $headers = explode("\n", str_replace(array("\r"), '', $headers) );
     336                $headers = explode("\n", str_replace(array("\r\n", "\r"), "\n", $headers) );
    260337
    261338                $response = array('code' => 0, 'message' => '');
     
    286363 * HTTP request method uses fsockopen function to retrieve the url.
    287364 *
    288  * Preferred method since it works with all WordPress supported PHP versions.
     365 * This would be the preferred method, but the fsockopen implementation has the
     366 * most overhead of all the HTTP transport implementations.
    289367 *
    290368 * @package WordPress
     
    294372class WP_Http_Fsockopen {
    295373        /**
    296          * Retrieve the location and set the class properties after the site has been retrieved.
    297          *
    298          * @since 2.7
    299          * @access public
    300          * @param string $url
     374         * Send a HTTP request to a URI using fsockopen().
     375         *
     376         * Does not support non-blocking mode.
     377         *
     378         * @see WP_Http::retrieve For default options descriptions.
     379         *
     380         * @since 2.7
     381         * @access public
     382         * @param string $url URI resource.
     383         * @param str|array $args Optional. Override the defaults.
    301384         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized.
    302385         * @param string $body Optional. The body that should be sent. Expected to be already processed.
    303          * @param str|array $type Optional. Should be an already processed array with HTTP arguments.
    304          * @return boolean
     386         * @return array 'headers', 'body', and 'response' keys.
    305387         */
    306388        function request($url, $args = array(), $headers = null, $body = null) {
     
    323405                        if ( ($arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https') && extension_loaded('openssl') ) {
    324406                                $arrURL['host'] = 'ssl://' . $arrURL['host'];
    325                                 $arrURL['port'] = apply_filters('http_request_default_port', 443);
     407                                $arrURL['port'] = apply_filters('http_request_port', 443);
    326408                                $secure_transport = true;
    327409                        } else {
     
    332414                }
    333415
     416                // There are issues with the HTTPS and SSL protocols that cause errors
     417                // that can be safely ignored and should be ignored.
    334418                if ( true === $secure_transport )
    335419                        $error_reporting = error_reporting(0);
     
    361445                fwrite($handle, $strHeaders);
    362446
    363                 if ( ! $r['blocking'] )
    364                         return array( 'headers' => array(), 'body' => '', 'response' => array() );
     447                if ( ! $r['blocking'] ) {
     448                        fclose($handle);
     449                        return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     450                }
    365451
    366452                $strResponse = '';
     
    411497 * getting the response. Also requires that 'allow_url_fopen' to be enabled.
    412498 *
    413  * Second preferred method for handling the retrieving the url for PHP 4.3+.
    414  *
    415499 * @package WordPress
    416500 * @subpackage HTTP
     
    419503class WP_Http_Fopen {
    420504        /**
    421          * Retrieve the location and set the class properties after the site has been retrieved.
    422          *
    423          * @access public
    424          * @since 2.7
    425          *
    426          * @param string $url
     505         * Send a HTTP request to a URI using fopen().
     506         *
     507         * This transport does not support sending of headers and body, therefore
     508         * should not be used in the instances, where there is a body and headers.
     509         *
     510         * Notes: Does not support non-blocking mode. Ignores 'redirection' option.
     511         *
     512         * @see WP_Http::retrieve For default options descriptions.
     513         *
     514         * @access public
     515         * @since 2.7
     516         *
     517         * @param string $url URI resource.
     518         * @param str|array $args Optional. Override the defaults.
    427519         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized.
    428520         * @param string $body Optional. The body that should be sent. Expected to be already processed.
    429          * @param str|array $type Optional. Should be an already processed array with HTTP arguments.
    430          * @return boolean
     521         * @return array 'headers', 'body', and 'response' keys.
    431522         */
    432523        function request($url, $args = array(), $headers = null, $body = null) {
     
    454545                        stream_set_timeout($handle, apply_filters('http_request_timeout', $r['timeout']) );
    455546
    456                 if ( ! $r['blocking'] )
    457                         return array( 'headers' => array(), 'body' => '', 'response' => array() );
     547                if ( ! $r['blocking'] ) {
     548                        fclose($handle);
     549                        return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     550                }
    458551
    459552                $strResponse = '';
    460553                while ( ! feof($handle) )
    461554                        $strResponse .= fread($handle, 4096);
     555
     556                fclose($handle);
    462557
    463558                $theHeaders = '';
     
    468563                        $theHeaders = $http_response_header;
    469564                }
    470                 fclose($handle);
    471 
    472565                $processedHeaders = WP_Http::processHeaders($theHeaders);
     566
    473567                return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
    474568        }
     
    502596class WP_Http_Streams {
    503597        /**
    504          * Retrieve the location and set the class properties after the site has been retrieved.
     598         * Send a HTTP request to a URI using streams with fopen().
    505599         *
    506600         * @access public
     
    511605         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized.
    512606         * @param string $body Optional. The body that should be sent. Expected to be already processed.
    513          * @return boolean
     607         * @return array 'headers', 'body', and 'response' keys.
    514608         */
    515609        function request($url, $args = array(), $headers = null, $body = null) {
     
    549643                        return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url));
    550644
    551                 if ( ! $r['blocking'] )
    552                         return array( 'headers' => array(), 'body' => '', 'response' => array() );
     645                if ( ! $r['blocking'] ) {
     646                        fclose($handle);
     647                        return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     648                }
    553649
    554650                $strResponse = stream_get_contents($handle);
    555651                $meta = stream_get_meta_data($handle);
     652                $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']);
    556653
    557654                fclose($handle);
    558655
    559                 $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']);
    560656                return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']);
    561657        }
     
    584680 * HTTP request method uses HTTP extension to retrieve the url.
    585681 *
    586  * Requires the HTTP extension to be installed.
    587  *
    588  * Last ditch effort to retrieve the URL before complete failure.
    589  * Does not support non-blocking requests.
     682 * Requires the HTTP extension to be installed. This would be the preferred
     683 * transport since it can handle a lot of the problems that forces the others to
     684 * use the HTTP version 1.0. Even if PHP 5.2+ is being used, it doesn't mean
     685 * that the HTTP extension will be enabled.
    590686 *
    591687 * @package WordPress
     
    595691class WP_Http_ExtHTTP {
    596692        /**
    597          * Retrieve the location and set the class properties after the site has been retrieved.
     693         * Send a HTTP request to a URI using HTTP extension.
     694         *
     695         * Does not support non-blocking.
    598696         *
    599697         * @access public
     
    604702         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized.
    605703         * @param string $body Optional. The body that should be sent. Expected to be already processed.
    606          * @return boolean
     704         * @return array 'headers', 'body', and 'response' keys.
    607705         */
    608706        function request($url, $args = array(), $headers = null, $body = null) {
     
    612710                        'method' => 'GET', 'timeout' => 3,
    613711                        'redirection' => 5, 'httpversion' => '1.0',
    614                         'blocking' => true, 'user_agent' => apply_filters('http_headers_useragent', 'WordPress/' . $wp_version)
     712                        'blocking' => true
    615713                );
    616714
    617715                $r = wp_parse_args( $args, $defaults );
    618716
    619                 if ( isset($headers['User-Agent']) )
     717                if ( isset($headers['User-Agent']) ) {
     718                        $r['user-agent'] = $headers['User-Agent'];
    620719                        unset($headers['User-Agent']);
     720                } else if( isset($headers['user-agent']) ) {
     721                        $r['user-agent'] = $headers['user-agent'];
     722                        unset($headers['user-agent']);
     723                } else {
     724                        $r['user-agent'] = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version );
     725                }
    621726
    622727                switch ( $r['method'] ) {
     
    640745
    641746                $options = array(
    642                         'timeout' => $this->timeout,
    643                         'connecttimeout' => apply_filters('http_request_stream_timeout', $this->timeout),
    644                         'redirect' => apply_filters('http_request_redirect', 3),
     747                        'timeout' => $r['timeout'],
     748                        'connecttimeout' => $r['timeout'],
     749                        'redirect' => $r['redirection'],
    645750                        'useragent' => $r['user_agent'],
    646751                        'headers' => $headers,
     
    652757                        return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);
    653758
     759                if ( ! $r['blocking'] )
     760                        return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     761
    654762                list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
    655763                $theHeaders = WP_Http::processHeaders($theHeaders);
     
    672780        function test() {
    673781                if ( function_exists('http_request') )
     782                        return true;
     783
     784                return false;
     785        }
     786}
     787
     788/**
     789 * HTTP request method uses Curl extension to retrieve the url.
     790 *
     791 * Requires the Curl extension to be installed.
     792 *
     793 * @package WordPress
     794 * @subpackage HTTP
     795 * @since 2.7
     796 */
     797class WP_Http_Curl {
     798        /**
     799         * Send a HTTP request to a URI using cURL extension.
     800         *
     801         * @access public
     802         * @since 2.7
     803         *
     804         * @param string $url
     805         * @param str|array $args Optional. Override the defaults.
     806         * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized.
     807         * @param string $body Optional. The body that should be sent. Expected to be already processed.
     808         * @return array 'headers', 'body', and 'response' keys.
     809         */
     810        function request($url, $args = array(), $headers = null, $body = null) {
     811                global $wp_version;
     812
     813                $defaults = array(
     814                        'method' => 'GET', 'timeout' => 3,
     815                        'redirection' => 5, 'httpversion' => '1.0',
     816                        'blocking' => true
     817                );
     818
     819                $r = wp_parse_args( $args, $defaults );
     820
     821                if ( isset($headers['User-Agent']) ) {
     822                        $r['user-agent'] = $headers['User-Agent'];
     823                        unset($headers['User-Agent']);
     824                } else if( isset($headers['user-agent']) ) {
     825                        $r['user-agent'] = $headers['user-agent'];
     826                        unset($headers['user-agent']);
     827                } else {
     828                        $r['user-agent'] = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version );
     829                }
     830
     831                $handle = curl_init();
     832                curl_setopt( $handle, CURLOPT_URL, $url);
     833
     834                if ( true === $r['blocking'] ) {
     835                        curl_setopt( $handle, CURLOPT_HEADER, true );
     836                } else {
     837                        curl_setopt( $handle, CURLOPT_HEADER, false );
     838                        curl_setopt( $handle, CURLOPT_NOBODY, true );
     839                }
     840
     841                curl_setopt( $handle, CURLOPT_RETURNTRANSFER, 1 );
     842                curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] );
     843                curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, 1 );
     844                curl_setopt( $handle, CURLOPT_TIMEOUT, $r['timeout'] );
     845                curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
     846                curl_setopt( $handle, CURLOPT_MAXREDIRS, $r['redirection'] );
     847
     848                if( ! is_null($headers) )
     849                        curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
     850
     851                if ( $r['httpversion'] == '1.0' )
     852                        curl_setopt( $headle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 );
     853                else
     854                        curl_setopt( $headle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 );
     855
     856                if ( ! $r['blocking'] ) {
     857                        curl_close( $handle );
     858                        return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') );
     859                }
     860
     861                $theResponse = curl_exec( $handle );
     862
     863                list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2);
     864                $theHeaders = WP_Http::processHeaders($theHeaders);
     865
     866                $response = array();
     867                $response['code'] = curl_getinfo( $handle, CURLINFO_HTTP_CODE );
     868                $response['message'] = get_status_header_desc($response['code']);
     869
     870                curl_close( $handle );
     871
     872                return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response);
     873        }
     874
     875        /**
     876         * Whether this class can be used for retrieving an URL.
     877         *
     878         * @static
     879         * @since 2.7
     880         *
     881         * @return boolean False means this class can not be used, true means it can.
     882         */
     883        function test() {
     884                if ( function_exists('curl_init') )
    674885                        return true;
    675886
Note: See TracChangeset for help on using the changeset viewer.