Index: class-http.php
===================================================================
--- class-http.php	(revision 15264)
+++ class-http.php	(working copy)
@@ -167,6 +167,45 @@
 			return $blocking_transport;
 	}
 
+	function get_transports($type = 'blocking') {
+		static $working_transport, $blocking_transport, $nonblocking_transport;
+
+		if ( is_null($working_transport) ) {
+			if ( true === WP_Http_ExtHttp::test() ) {
+				$working_transport['exthttp'] = new WP_Http_ExtHttp();
+				$blocking_transport[] = &$working_transport['exthttp'];
+			} else if ( true === WP_Http_Curl::test() ) {
+				$working_transport['curl'] = new WP_Http_Curl();
+				$blocking_transport[] = &$working_transport['curl'];
+			} else if ( true === WP_Http_Streams::test() ) {
+				$working_transport['streams'] = new WP_Http_Streams();
+				$blocking_transport[] = &$working_transport['streams'];
+			} else if ( true === WP_Http_Fopen::test() ) {
+				$working_transport['fopen'] = new WP_Http_Fopen();
+				$blocking_transport[] = &$working_transport['fopen'];
+			} else if ( true === WP_Http_Fsockopen::test() ) {
+				$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 );
+
+		switch ( $type ) {
+			case 'blocking':
+				return $blocking_transport;
+			case 'nonblocking':
+				return $nonblocking_transport;
+			default:
+				return $working_transport;
+		}
+	}
+
 	/**
 	 * Send a HTTP request to a URI.
 	 *
@@ -175,9 +214,9 @@
 	 * is acceptable. If the 'body' argument is an array, then it will automatically be escaped
 	 * using http_build_query().
 	 *
-	 * The only URI that are supported in the HTTP Transport implementation are the HTTP and HTTPS
-	 * protocols. HTTP and HTTPS are assumed so the server might not know how to handle the send
-	 * headers. Other protocols are unsupported and most likely will fail.
+	 * The only URI that should be used are those that are for the HTTP protocol. Any other URI
+	 * scheme will fail. HTTPS is supported, provided that PHP supports SSL and has the extensions
+	 * installed.
 	 *
 	 * The defaults are 'method', 'timeout', 'redirection', 'httpversion', 'blocking' and
 	 * 'user-agent'.
@@ -314,6 +353,10 @@
 
 		$response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
 		foreach ( (array) $transports as $transport ) {
+			// Really just want to check that able to send body and do SSL.
+			if ( ! $transport->supports_args($r, false) )
+				continue;
+
 			$response = $transport->request($url, $r);
 
 			do_action( 'http_api_debug', $response, 'response', get_class($transport) );
@@ -729,10 +772,7 @@
 			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
 		}
 
-		$strResponse = '';
-		while ( ! feof($handle) )
-			$strResponse .= fread($handle, 4096);
-
+		$strResponse = $this->_get_response(&$handle);
 		fclose($handle);
 
 		if ( true === $secure_transport )
@@ -765,8 +805,28 @@
 	}
 
 	/**
+	 * Retrieves the response message using resource handle.
+	 *
+	 * This should allow for others to inherit and extend the class to implement their own resource
+	 * handling. A request was made a long time ago for handling long streams which will exhaust
+	 * memory, but is not a feature that majority of developers will need.
+	 *
+	 * @param resource $handle Fsockopen resource
+	 * @return string Response message (includes header and body).
+	 */
+	function _get_response(&$handle) {
+		$strResponse = '';
+		while ( ! feof($handle) )
+			$strResponse .= fread($handle, 4096);
+
+		return $strResponse;
+	}
+
+	/**
 	 * Whether this class can be used for retrieving an URL.
 	 *
+	 * The scope of this method is to tell whether or not the transport is supported by the server.
+	 *
 	 * @since 2.7.0
 	 * @static
 	 * @return boolean False means this class can not be used, true means it can.
@@ -775,16 +835,76 @@
 		if ( false !== ($option = get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours
 			return false;
 
+		return $this->supports_args($args);
+	}
+
+	/**
+	 * Whether feature is supported by transport.
+	 *
+	 * The supported feature values are 'ssl', 'non-blocking', 'headers', and 'post'. SSL is whether
+	 * SSL connections can be made. The 'non-blocking' means whether the execution of the HTTP
+	 * request will continue to block the execution of the script. This is important for wp-cron and
+	 * other async requests where speed is important and AJAX workaround would be too complex to
+	 * use.
+	 *
+	 * The 'headers' feature really means whether headers can be sent, which really all of the
+	 * transports can now anyway. The 'post' feature really just means whether the body can be sent
+	 * with the request.
+	 *
+	 * @since {unknown}
+	 *
+	 * @param string $feature Feature that can be used by transport: 'ssl', 'non-blocking', 'headers', and 'post'.
+	 * @param mixed $option Optional. Not used by class.
+	 * @return boolean Whether feature is supported by Transport.
+	 */
+	function supports( $feature, $option = '' ) {
+		$supports = false;
+		switch( strtolower($feature) ) {
+			case 'ssl':
+				$supports = extension_loaded('openssl');
+				break;
+			case 'non-blocking':
+				$supports = false;
+				break;
+			case 'headers':
+				$supports = true;
+				break;
+			case 'post':
+				$supports = true;
+				break;
+		}
+
+		return $supports;
+	}
+
+	/**
+	 * Whether the request arguments are supported by the transport.
+	 *
+	 * This method is useful since you can pass the complete args and it will test it and return
+	 * whether the class can be used for the request.
+	 *
+	 * @param array $args Processed request arguments. Must be the processed arguments!
+	 * @param bool $strict Optional. Whether to check for additional problems that won't fail execution otherwise.
+	 * @return boolean
+	 */
+	function supports_args($args, $strict = false) {
+		$supported = true;
 		$is_ssl = isset($args['ssl']) && $args['ssl'];
 
 		if ( ! $is_ssl && function_exists( 'fsockopen' ) )
-			$use = true;
-		elseif ( $is_ssl && extension_loaded('openssl') && function_exists( 'fsockopen' ) )
-			$use = true;
+			$supported = true;
+		elseif ( $is_ssl && $this->supports('ssl') && function_exists( 'fsockopen' ) )
+			$supported = true;
 		else
-			$use = false;
+			$supported = false;
 
-		return apply_filters('use_fsockopen_transport', $use, $args);
+		if ( $strict ) {
+			if ( isset($args['blocking']) && ! $args['blocking'] ) {
+				$supported = false;
+			}
+		}
+
+		return apply_filters('use_fsockopen_transport', $supported, $args);
 	}
 }
 
@@ -906,34 +1026,102 @@
 	/**
 	 * Whether this class can be used for retrieving an URL.
 	 *
+	 * The scope of this method is to tell whether or not the transport is supported by the server.
+	 *
 	 * @since 2.7.0
 	 * @static
 	 * @return boolean False means this class can not be used, true means it can.
 	 */
 	function test($args = array()) {
+		if ( version_compare(PHP_VERSION, '5.0', '>=') ) // PHP4 only backwards compat.
+			return false;
+
 		if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )
 			return false;
 
+		return $this->supports_args($args);
+	}
+
+	/**
+	 * Whether feature is supported by transport.
+	 *
+	 * The supported feature values are 'ssl', 'non-blocking', 'headers', and 'post'. SSL is whether
+	 * SSL connections can be made. The 'non-blocking' means whether the execution of the HTTP
+	 * request will continue to block the execution of the script. This is important for wp-cron and
+	 * other async requests where speed is important and AJAX workaround would be too complex to
+	 * use.
+	 *
+	 * The 'headers' feature really means whether headers can be sent, which really all of the
+	 * transports can now anyway. The 'post' feature really just means whether the body can be sent
+	 * with the request.
+	 *
+	 * @since {unknown}
+	 *
+	 * @param string $feature Feature that can be used by transport: 'ssl', 'non-blocking', 'headers', and 'post'.
+	 * @param mixed $option Optional. Is used for SSL feature. Should be request args.
+	 * @return boolean Whether feature is supported by Transport.
+	 */
+	function supports( $feature, $option = '' ) {
+		$supports = false;
+		switch ( strtolower( $feature ) ) {
+			case 'ssl':
+				$is_local = isset($option['local']) && $option['local'];
+				$ssl_verify = isset($option['sslverify']) && $option['sslverify'];
+				if ( $is_local && true != apply_filters('https_local_ssl_verify', true) )
+					$supports = true;
+				elseif ( !$is_local && true != apply_filters('https_ssl_verify', true) )
+					$supports = true;
+				elseif ( !$ssl_verify )
+					$supports = true;
+				break;
+			case 'non-blocking':
+				$supports = false;
+				break;
+			case 'headers':
+				$supports = true;
+				break;
+			case 'post':
+				$supports = false;
+				break;
+		}
+
+		return $supports;
+	}
+
+	/**
+	 * Whether the request arguments are supported by the transport.
+	 *
+	 * This method is useful since you can pass the complete args and it will test it and return
+	 * whether the class can be used for the request.
+	 *
+	 * @since {unknown}
+	 *
+	 * @param array $args Processed request arguments. Must be the processed arguments!
+	 * @param bool $strict Optional. Whether to check for additional problems that won't fail execution otherwise.
+	 * @return boolean
+	 */
+	function supports_args($args, $strict = false) {
 		if ( isset($args['method']) && 'HEAD' == $args['method'] ) //This transport cannot make a HEAD request
 			return false;
 
-		$use = true;
+		if ( isset($args['body']) && ! empty($args['body']) ) { // This transport cannot send body.
+			return false;
+		}
+
+		$supported = true;
+
 		//PHP does not verify SSL certs, We can only make a request via this transports if SSL Verification is turned off.
-		$is_ssl = isset($args['ssl']) && $args['ssl'];
-		if ( $is_ssl ) {
-			$is_local = isset($args['local']) && $args['local'];
-			$ssl_verify = isset($args['sslverify']) && $args['sslverify'];
-			if ( $is_local && true != apply_filters('https_local_ssl_verify', true) )
-				$use = true;
-			elseif ( !$is_local && true != apply_filters('https_ssl_verify', true) )
-				$use = true;
-			elseif ( !$ssl_verify )
-				$use = true;
-			else
-				$use = false;
+		if ( isset($args['ssl']) && $args['ssl'] ) {
+			$supported = $this->supports('ssl', $args);
 		}
 
-		return apply_filters('use_fopen_transport', $use, $args);
+		if ( $strict ) {
+			if ( isset($args['blocking']) && ! $args['blocking'] ) {
+				$supported = false;
+			}
+		}
+
+		return apply_filters('use_fopen_transport', $supported, $args);
 	}
 }
 
@@ -1080,6 +1268,8 @@
 	/**
 	 * Whether this class can be used for retrieving an URL.
 	 *
+	 * The scope of this method is to tell whether or not the transport is supported by the server.
+	 *
 	 * @static
 	 * @access public
 	 * @since 2.7.0
@@ -1093,19 +1283,79 @@
 		if ( version_compare(PHP_VERSION, '5.0', '<') )
 			return false;
 
+		return $this->supports_args($args);
+	}
+
+	/**
+	 * Whether feature is supported by transport.
+	 *
+	 * The supported feature values are 'ssl', 'non-blocking', 'headers', and 'post'. SSL is whether
+	 * SSL connections can be made. The 'non-blocking' means whether the execution of the HTTP
+	 * request will continue to block the execution of the script. This is important for wp-cron and
+	 * other async requests where speed is important and AJAX workaround would be too complex to
+	 * use.
+	 *
+	 * The 'headers' feature really means whether headers can be sent, which really all of the
+	 * transports can now anyway. The 'post' feature really just means whether the body can be sent
+	 * with the request.
+	 *
+	 * @since {unknown}
+	 *
+	 * @param string $feature Feature that can be used by transport: 'ssl', 'non-blocking', 'headers', and 'post'.
+	 * @param mixed $option Optional. Not used by class.
+	 * @return boolean Whether feature is supported by Transport.
+	 */
+	function supports( $feature, $option = '' ) {
+		$supports = false;
+		switch( strtolower($feature) ) {
+			case 'ssl':
+				$supports = true;
+				if ( version_compare(PHP_VERSION, '5.1.0', '<') ) {
+					$proxy = new WP_HTTP_Proxy();
+					/**
+					 * No URL check, as its not currently passed to the ::test() function
+					 * In the case where a Proxy is in use, Just bypass this transport for HTTPS.
+					 */
+					if ( $proxy->is_enabled() )
+						$supports = false;
+				}
+				break;
+			case 'non-blocking':
+				$supports = true;
+				break;
+			case 'headers':
+				$supports = true;
+				break;
+			case 'post':
+				$supports = true;
+				break;
+		}
+
+		return $supports;
+	}
+
+	/**
+	 * Whether the request arguments are supported by the transport.
+	 *
+	 * This method is useful since you can pass the complete args and it will test it and return
+	 * whether the class can be used for the request.
+	 *
+	 * @since {unknown}
+	 * @access public
+	 *
+	 * @param array $args Processed request arguments. Must be the processed arguments!
+	 * @param bool $strict Optional. Whether to check for additional problems that won't fail execution otherwise.
+	 * @return boolean
+	 */
+	function supports_args($args, $strict = false) {
+		$supported = true;
 		//HTTPS via Proxy was added in 5.1.0
 		$is_ssl = isset($args['ssl']) && $args['ssl'];
-		if ( $is_ssl && version_compare(PHP_VERSION, '5.1.0', '<') ) {
-			$proxy = new WP_HTTP_Proxy();
-			/**
-			 * No URL check, as its not currently passed to the ::test() function
-			 * In the case where a Proxy is in use, Just bypass this transport for HTTPS.
-			 */
-			if ( $proxy->is_enabled() )
-				return false;
+		if ( $is_ssl && ! $this->supports('ssl') ) {
+			$supported = false;
 		}
 
-		return apply_filters('use_streams_transport', true, $args);
+		return apply_filters('use_streams_transport', $supported, $args);
 	}
 }
 
@@ -1225,7 +1475,7 @@
 		if ( ! $r['blocking'] )
 			return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );
 
-		$headers_body = WP_HTTP::processResponse($strResponse);
+		$headers_body = WP_Http::processResponse($strResponse);
 		$theHeaders = $headers_body['headers'];
 		$theBody = $headers_body['body'];
 		unset($headers_body);
@@ -1252,6 +1502,8 @@
 	/**
 	 * Whether this class can be used for retrieving an URL.
 	 *
+	 * The scope of this method is to tell whether or not the transport is supported by the server.
+	 *
 	 * @static
 	 * @since 2.7.0
 	 *
@@ -1260,6 +1512,69 @@
 	function test($args = array()) {
 		return apply_filters('use_http_extension_transport', function_exists('http_request'), $args );
 	}
+
+	/**
+	 * Whether feature is supported by transport.
+	 *
+	 * The supported feature values are 'ssl', 'non-blocking', 'headers', and 'post'. SSL is whether
+	 * SSL connections can be made. The 'non-blocking' means whether the execution of the HTTP
+	 * request will continue to block the execution of the script. This is important for wp-cron and
+	 * other async requests where speed is important and AJAX workaround would be too complex to
+	 * use.
+	 *
+	 * The 'headers' feature really means whether headers can be sent, which really all of the
+	 * transports can now anyway. The 'post' feature really just means whether the body can be sent
+	 * with the request.
+	 *
+	 * @since {unknown}
+	 *
+	 * @param string $feature Feature that can be used by transport: 'ssl', 'non-blocking', 'headers', and 'post'.
+	 * @param mixed $option Optional. Not used by class.
+	 * @return boolean Whether feature is supported by Transport.
+	 */
+	function supports( $feature, $option = '' ) {
+		$supports = false;
+		switch( strtolower($feature) ) {
+			case 'ssl':
+				$supports = true;
+				break;
+			case 'non-blocking':
+				$supports = false;
+				break;
+			case 'headers':
+				$supports = true;
+				break;
+			case 'post':
+				$supports = true;
+				break;
+		}
+
+		return $supports;
+	}
+
+	/**
+	 * Whether the request arguments are supported by the transport.
+	 *
+	 * This method is useful since you can pass the complete args and it will test it and return
+	 * whether the class can be used for the request.
+	 *
+	 * @since {unknown}
+	 * @access public
+	 *
+	 * @param array $args Processed request arguments. Must be the processed arguments!
+	 * @param bool $strict Optional. Whether to check for additional problems that won't fail execution otherwise.
+	 * @return boolean
+	 */
+	function supports_args($args, $strict = false) {
+		$supported = true;
+		if ( $strict ) {
+			if ( isset($args['blocking']) && ! $args['blocking'] ) {
+				$supported = false;
+			}
+		}
+
+		return apply_filters('use_http_extension_transport', $supported, $args);
+	}
 }
 
 /**
@@ -1447,6 +1762,8 @@
 	/**
 	 * Whether this class can be used for retrieving an URL.
 	 *
+	 * The scope of this method is to tell whether or not the transport is supported by the server.
+	 *
 	 * @static
 	 * @since 2.7.0
 	 *
@@ -1458,6 +1775,62 @@
 
 		return false;
 	}
+
+	/**
+	 * Whether feature is supported by transport.
+	 *
+	 * The supported feature values are 'ssl', 'non-blocking', 'headers', and 'post'. SSL is whether
+	 * SSL connections can be made. The 'non-blocking' means whether the execution of the HTTP
+	 * request will continue to block the execution of the script. This is important for wp-cron and
+	 * other async requests where speed is important and AJAX workaround would be too complex to
+	 * use.
+	 *
+	 * The 'headers' feature really means whether headers can be sent, which really all of the
+	 * transports can now anyway. The 'post' feature really just means whether the body can be sent
+	 * with the request.
+	 *
+	 * @since {unknown}
+	 *
+	 * @param string $feature Feature that can be used by transport: 'ssl', 'non-blocking', 'headers', and 'post'.
+	 * @param mixed $option Optional. Not used by class.
+	 * @return boolean Whether feature is supported by Transport.
+	 */
+	function supports( $feature, $option = '' ) {
+		$supports = false;
+		switch( strtolower($feature) ) {
+			case 'ssl':
+				$supports = true;
+				break;
+			case 'non-blocking':
+				$supports = true;
+				break;
+			case 'headers':
+				$supports = true;
+				break;
+			case 'post':
+				$supports = true;
+				break;
+		}
+
+		return $supports;
+	}
+
+	/**
+	 * Whether the request arguments are supported by the transport.
+	 *
+	 * This method is useful since you can pass the complete args and it will test it and return
+	 * whether the class can be used for the request.
+	 *
+	 * @since {unknown}
+	 * @access public
+	 *
+	 * @param array $args Processed request arguments. Must be the processed arguments!
+	 * @param bool $strict Optional. Whether to check for additional problems that won't fail execution otherwise.
+	 * @return boolean
+	 */
+	function supports_args($args, $strict = false) {
+		return apply_filters('use_curl_transport', true, $args);
+	}
 }
 
 /**
