WordPress.org

Make WordPress Core

Changeset 8520


Ignore:
Timestamp:
08/01/08 18:44:40 (7 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.