WordPress.org

Make WordPress Core

Changeset 8520


Ignore:
Timestamp:
08/01/2008 06:44:40 PM (10 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.