<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul, pre { overflow: auto; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<title>[17550] trunk/wp-includes/class-http.php:
Fix WP_HTTP to only make a request upon a working transport,
as well as to allow Unit Testing.</title>
</head>
<body>
<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.wordpress.org/changeset/17550">17550</a></dd>
<dt>Author</dt> <dd>dd32</dd>
<dt>Date</dt> <dd>2011-03-24 02:16:13 +0000 (Thu, 24 Mar 2011)</dd>
</dl>
<h3>Log Message</h3>
<pre>Fix WP_HTTP to only make a request upon a working transport, as well as to allow Unit Testing. Removes the getTransport() & postTransport() methods as they're no longer needed, replaces them with a single _dispatch_request() method. Fixes <a href="http://trac.wordpress.org/ticket/11613">#11613</a></pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkwpincludesclasshttpphp">trunk/wp-includes/class-http.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkwpincludesclasshttpphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/class-http.php (17549 => 17550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/class-http.php        2011-03-24 01:18:41 UTC (rev 17549)
+++ trunk/wp-includes/class-http.php        2011-03-24 02:16:13 UTC (rev 17550)
</span><span class="lines">@@ -38,131 +38,6 @@
</span><span class="cx"> class WP_Http {
</span><span class="cx">
</span><span class="cx">         /**
</span><del>-         * PHP4 style Constructor - Calls PHP5 Style Constructor
-         *
-         * @since 2.7.0
-         * @return WP_Http
-         */
-        function WP_Http() {
-                $this->__construct();
-        }
-
-        /**
-         * PHP5 style Constructor - Set up available transport if not available.
-         *
-         * PHP4 does not have the 'self' keyword and since WordPress supports PHP4, the class needs to
-         * be used for the static call. The transport are set up to save time and will only be created
-         * once. This class can be created many times without having to go through the step of finding
-         * which transports are available.
-         *
-         * @since 2.7.0
-         * @return WP_Http
-         */
-        function __construct() {
-                WP_Http::_getTransport();
-                WP_Http::_postTransport();
-        }
-
-        /**
-         * Tests the WordPress HTTP objects for an object to use and returns it.
-         *
-         * Tests all of the objects and returns the object that passes. Also caches that object to be
-         * used later.
-         *
-         * The order for the GET/HEAD requests are HTTP Extension, cURL, Streams, and finally
-         * Fsockopen. fsockopen() is used last, because it has the most overhead in its implementation.
-         * There isn't any real way around it, since redirects have to be supported, much the same way
-         * the other transports also handle redirects.
-         *
-         * There are currently issues with "localhost" not resolving correctly with DNS. This may cause
-         * an error "failed to open stream: A connection attempt failed because the connected party did
-         * not properly respond after a period of time, or established connection failed because [the]
-         * connected host has failed to respond."
-         *
-         * @since 2.7.0
-         * @access private
-         *
-         * @param array $args Request args, default us an empty array
-         * @return object|null Null if no transports are available, HTTP transport object.
-         */
-        function &_getTransport( $args = array() ) {
-                static $working_transport, $blocking_transport, $nonblocking_transport;
-
-                if ( is_null($working_transport) ) {
-                        if ( true === WP_Http_ExtHttp::test($args) ) {
-                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
-                                $blocking_transport[] = &$working_transport['exthttp'];
-                        } else if ( true === WP_Http_Curl::test($args) ) {
-                                $working_transport['curl'] = new WP_Http_Curl();
-                                $blocking_transport[] = &$working_transport['curl'];
-                        } else if ( true === WP_Http_Streams::test($args) ) {
-                                $working_transport['streams'] = new WP_Http_Streams();
-                                $blocking_transport[] = &$working_transport['streams'];
-                        } else if ( true === WP_Http_Fsockopen::test($args) ) {
-                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
-                                $blocking_transport[] = &$working_transport['fsockopen'];
-                        }
-
-                        foreach ( array('curl', 'streams', 'fsockopen', 'exthttp') as $transport ) {
-                                if ( isset($working_transport[$transport]) )
-                                        $nonblocking_transport[] = &$working_transport[$transport];
-                        }
-                }
-
-                do_action( 'http_transport_get_debug', $working_transport, $blocking_transport, $nonblocking_transport );
-
-                if ( isset($args['blocking']) && !$args['blocking'] )
-                        return $nonblocking_transport;
-                else
-                        return $blocking_transport;
-        }
-
-        /**
-         * Tests the WordPress HTTP objects for an object to use and returns it.
-         *
-         * Tests all of the objects and returns the object that passes. Also caches
-         * that object to be used later. This is for posting content to a URL and
-         * is used when there is a body.
-         *
-         * @since 2.7.0
-         * @access private
-         *
-         * @param array $args Request args, default us an empty array
-         * @return object|null Null if no transports are available, HTTP transport object.
-         */
-        function &_postTransport( $args = array() ) {
-                static $working_transport, $blocking_transport, $nonblocking_transport;
-
-                if ( is_null($working_transport) ) {
-                        if ( true === WP_Http_ExtHttp::test($args) ) {
-                                $working_transport['exthttp'] = new WP_Http_ExtHttp();
-                                $blocking_transport[] = &$working_transport['exthttp'];
-                        } else if ( true === WP_Http_Curl::test($args) ) {
-                                $working_transport['curl'] = new WP_Http_Curl();
-                                $blocking_transport[] = &$working_transport['curl'];
-                        } else if ( true === WP_Http_Streams::test($args) ) {
-                                $working_transport['streams'] = new WP_Http_Streams();
-                                $blocking_transport[] = &$working_transport['streams'];
-                        } else if ( true === WP_Http_Fsockopen::test($args) ) {
-                                $working_transport['fsockopen'] = new WP_Http_Fsockopen();
-                                $blocking_transport[] = &$working_transport['fsockopen'];
-                        }
-
-                        foreach ( array('curl', 'streams', 'fsockopen', 'exthttp') as $transport ) {
-                                if ( isset($working_transport[$transport]) )
-                                        $nonblocking_transport[] = &$working_transport[$transport];
-                        }
-                }
-
-                do_action( 'http_transport_post_debug', $working_transport, $blocking_transport, $nonblocking_transport );
-
-                if ( isset($args['blocking']) && !$args['blocking'] )
-                        return $nonblocking_transport;
-                else
-                        return $blocking_transport;
-        }
-
-        /**
</del><span class="cx">          * Send a HTTP request to a URI.
</span><span class="cx">          *
</span><span class="cx">          * The body and headers are part of the arguments. The 'body' argument is for the body and will
</span><span class="lines">@@ -205,7 +80,7 @@
</span><span class="cx">          *
</span><span class="cx">          * @param string $url URI resource.
</span><span class="cx">          * @param str|array $args Optional. Override the defaults.
</span><del>-         * @return array containing 'headers', 'body', 'response', 'cookies'
</del><ins>+         * @return array|object Array containing 'headers', 'body', 'response', 'cookies'. A WP_Error instance upon error
</ins><span class="cx">          */
</span><span class="cx">         function request( $url, $args = array() ) {
</span><span class="cx">                 global $wp_version;
</span><span class="lines">@@ -281,11 +156,6 @@
</span><span class="cx">                         // header isn't already set.
</span><span class="cx">                         if ( ($r['method'] == 'POST' || $r['method'] == 'PUT') && ! isset( $r['headers']['Content-Length'] ) )
</span><span class="cx">                                 $r['headers']['Content-Length'] = 0;
</span><del>-
-                        // The method is ambiguous, because we aren't talking about HTTP methods, the "get" in
-                        // this case is simply that we aren't sending any bodies and to get the transports that
-                        // don't support sending bodies along with those which do.
-                        $transports = WP_Http::_getTransport( $r );
</del><span class="cx">                 } else {
</span><span class="cx">                         if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) {
</span><span class="cx">                                 $r['body'] = http_build_query( $r['body'], null, '&' );
</span><span class="lines">@@ -295,27 +165,65 @@
</span><span class="cx">
</span><span class="cx">                         if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) )
</span><span class="cx">                                 $r['headers']['Content-Length'] = strlen( $r['body'] );
</span><del>-
-                        // The method is ambiguous, because we aren't talking about HTTP methods, the "post" in
-                        // this case is simply that we are sending HTTP body and to get the transports that do
-                        // support sending the body. Not all do, depending on the limitations of the PHP core
-                        // limitations.
-                        $transports = WP_Http::_postTransport( $r );
</del><span class="cx">                 }
</span><span class="cx">
</span><del>-                do_action( 'http_api_debug', $transports, 'transports_list' );
</del><ins>+                return $this->_dispatch_request($url, $r);
+        }
</ins><span class="cx">
</span><del>-                $response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
-                foreach ( (array) $transports as $transport ) {
-                        $response = $transport->request( $url, $r );
</del><ins>+        /**
+         * Dispatches a HTTP request to a supporting transport.
+         *
+         * Tests each transport in order to find a transport which matches the request arguements.
+         * Also caches the transport instance to be used later.
+         *
+         * The order for blocking requests is HTTP Extension, cURL, Streams, and finally Fsockopen.
+         * The order for non-blocking requests is cURL, Streams, Fsockopen() and finally, HTTP Extension.
+         * The HTTP Extension does not support non-blocking requests, but is included as a final resort.
+         *
+         * There are currently issues with "localhost" not resolving correctly with DNS. This may cause
+         * an error "failed to open stream: A connection attempt failed because the connected party did
+         * not properly respond after a period of time, or established connection failed because [the]
+         * connected host has failed to respond."
+         *
+         * @since 3.2.0
+         * @access private
+         *
+         * @param string $url URL to Request
+         * @param array $args Request arguments
+         * @return array|object Array containing 'headers', 'body', 'response', 'cookies'. A WP_Error instance upon error
+         */
+        private function _dispatch_request($url, $args) {
+                static $transports = null;
+                if ( is_null($transports) )
+                        $transports = array();
</ins><span class="cx">
</span><del>-                        do_action( 'http_api_debug', $response, 'response', get_class( $transport ) );
</del><ins>+                $request_order = isset($r['blocking']) && !$r['blocking'] ?
+                                                        array('curl', 'streams', 'fsockopen', 'exthttp') : // non-blocking order
+                                                        array('exthttp', 'curl', 'streams', 'fsockopen'); // blocking order
</ins><span class="cx">
</span><del>-                        if ( ! is_wp_error( $response ) )
-                                return apply_filters( 'http_response', $response, $r, $url );
</del><ins>+                // Loop over each transport on each HTTP request looking for one which will serve this requests needs
+                foreach ( $request_order as $transport ) {
+                        $class = 'WP_HTTP_' . $transport;
+
+                        // Check to see if this transport is a possibility, calls the transport statically
+                        if ( ! call_user_func( array($class, 'test'), $args, $url) )
+                                continue;
+
+                        // Transport claims to support request, Instantate it and give it a whirl.
+                        if ( empty( $transports[ $transport ] ) )
+                                $transports[ $transport ] = new $class;
+
+                        $response = $transports[ $transport ]->request( $url, $args );
+
+                        do_action( 'http_api_debug', $response, 'response', $class );
+
+                        if ( is_wp_error( $response ) )
+                                return $response;
+
+                        return apply_filters( 'http_response', $response, $args, $url );
</ins><span class="cx">                 }
</span><span class="cx">
</span><del>-                return $response;
</del><ins>+                return new WP_Error('http_failure', __('There are no HTTP transports available which can complete the requested request.') );
</ins><span class="cx">         }
</span><span class="cx">
</span><span class="cx">         /**
</span><span class="lines">@@ -328,7 +236,7 @@
</span><span class="cx">          *
</span><span class="cx">          * @param string $url URI resource.
</span><span class="cx">          * @param str|array $args Optional. Override the defaults.
</span><del>-         * @return boolean
</del><ins>+         * @return array|object Array containing 'headers', 'body', 'response', 'cookies'. A WP_Error instance upon error
</ins><span class="cx">          */
</span><span class="cx">         function post($url, $args = array()) {
</span><span class="cx">                 $defaults = array('method' => 'POST');
</span><span class="lines">@@ -346,7 +254,7 @@
</span><span class="cx">          *
</span><span class="cx">          * @param string $url URI resource.
</span><span class="cx">          * @param str|array $args Optional. Override the defaults.
</span><del>-         * @return boolean
</del><ins>+         * @return array|object Array containing 'headers', 'body', 'response', 'cookies'. A WP_Error instance upon error
</ins><span class="cx">          */
</span><span class="cx">         function get($url, $args = array()) {
</span><span class="cx">                 $defaults = array('method' => 'GET');
</span><span class="lines">@@ -364,7 +272,7 @@
</span><span class="cx">          *
</span><span class="cx">          * @param string $url URI resource.
</span><span class="cx">          * @param str|array $args Optional. Override the defaults.
</span><del>-         * @return boolean
</del><ins>+         * @return array|object Array containing 'headers', 'body', 'response', 'cookies'. A WP_Error instance upon error
</ins><span class="cx">          */
</span><span class="cx">         function head($url, $args = array()) {
</span><span class="cx">                 $defaults = array('method' => 'HEAD');
</span></span></pre>
</div>
</div>
</body>
</html>