Changes in trunk/wp-includes/class-http.php [17282:18254]
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-includes/class-http.php
r17282 r18254 28 28 * Debugging includes several actions, which pass different variables for debugging the HTTP API. 29 29 * 30 * <strong>http_transport_get_debug</strong> - gives working, nonblocking, and blocking transports.31 *32 * <strong>http_transport_post_debug</strong> - gives working, nonblocking, and blocking transports.33 *34 30 * @package WordPress 35 31 * @subpackage HTTP … … 37 33 */ 38 34 class WP_Http { 39 40 /**41 * PHP4 style Constructor - Calls PHP5 Style Constructor42 *43 * @since 2.7.044 * @return WP_Http45 */46 function WP_Http() {47 $this->__construct();48 }49 50 /**51 * PHP5 style Constructor - Set up available transport if not available.52 *53 * PHP4 does not have the 'self' keyword and since WordPress supports PHP4, the class needs to54 * be used for the static call. The transport are set up to save time and will only be created55 * once. This class can be created many times without having to go through the step of finding56 * which transports are available.57 *58 * @since 2.7.059 * @return WP_Http60 */61 function __construct() {62 WP_Http::_getTransport();63 WP_Http::_postTransport();64 }65 66 /**67 * Tests the WordPress HTTP objects for an object to use and returns it.68 *69 * Tests all of the objects and returns the object that passes. Also caches that object to be70 * used later.71 *72 * The order for the GET/HEAD requests are HTTP Extension, cURL, Streams, Fopen, and finally73 * Fsockopen. fsockopen() is used last, because it has the most overhead in its implementation.74 * There isn't any real way around it, since redirects have to be supported, much the same way75 * the other transports also handle redirects.76 *77 * There are currently issues with "localhost" not resolving correctly with DNS. This may cause78 * an error "failed to open stream: A connection attempt failed because the connected party did79 * not properly respond after a period of time, or established connection failed because [the]80 * connected host has failed to respond."81 *82 * @since 2.7.083 * @access private84 *85 * @param array $args Request args, default us an empty array86 * @return object|null Null if no transports are available, HTTP transport object.87 */88 function &_getTransport( $args = array() ) {89 static $working_transport, $blocking_transport, $nonblocking_transport;90 91 if ( is_null($working_transport) ) {92 if ( true === WP_Http_ExtHttp::test($args) ) {93 $working_transport['exthttp'] = new WP_Http_ExtHttp();94 $blocking_transport[] = &$working_transport['exthttp'];95 } else if ( true === WP_Http_Curl::test($args) ) {96 $working_transport['curl'] = new WP_Http_Curl();97 $blocking_transport[] = &$working_transport['curl'];98 } else if ( true === WP_Http_Streams::test($args) ) {99 $working_transport['streams'] = new WP_Http_Streams();100 $blocking_transport[] = &$working_transport['streams'];101 } else if ( true === WP_Http_Fopen::test($args) ) {102 $working_transport['fopen'] = new WP_Http_Fopen();103 $blocking_transport[] = &$working_transport['fopen'];104 } else if ( true === WP_Http_Fsockopen::test($args) ) {105 $working_transport['fsockopen'] = new WP_Http_Fsockopen();106 $blocking_transport[] = &$working_transport['fsockopen'];107 }108 109 foreach ( array('curl', 'streams', 'fopen', 'fsockopen', 'exthttp') as $transport ) {110 if ( isset($working_transport[$transport]) )111 $nonblocking_transport[] = &$working_transport[$transport];112 }113 }114 115 do_action( 'http_transport_get_debug', $working_transport, $blocking_transport, $nonblocking_transport );116 117 if ( isset($args['blocking']) && !$args['blocking'] )118 return $nonblocking_transport;119 else120 return $blocking_transport;121 }122 123 /**124 * Tests the WordPress HTTP objects for an object to use and returns it.125 *126 * Tests all of the objects and returns the object that passes. Also caches127 * that object to be used later. This is for posting content to a URL and128 * is used when there is a body. The plain Fopen Transport can not be used129 * to send content, but the streams transport can. This is a limitation that130 * is addressed here, by just not including that transport.131 *132 * @since 2.7.0133 * @access private134 *135 * @param array $args Request args, default us an empty array136 * @return object|null Null if no transports are available, HTTP transport object.137 */138 function &_postTransport( $args = array() ) {139 static $working_transport, $blocking_transport, $nonblocking_transport;140 141 if ( is_null($working_transport) ) {142 if ( true === WP_Http_ExtHttp::test($args) ) {143 $working_transport['exthttp'] = new WP_Http_ExtHttp();144 $blocking_transport[] = &$working_transport['exthttp'];145 } else if ( true === WP_Http_Curl::test($args) ) {146 $working_transport['curl'] = new WP_Http_Curl();147 $blocking_transport[] = &$working_transport['curl'];148 } else if ( true === WP_Http_Streams::test($args) ) {149 $working_transport['streams'] = new WP_Http_Streams();150 $blocking_transport[] = &$working_transport['streams'];151 } else if ( true === WP_Http_Fsockopen::test($args) ) {152 $working_transport['fsockopen'] = new WP_Http_Fsockopen();153 $blocking_transport[] = &$working_transport['fsockopen'];154 }155 156 foreach ( array('curl', 'streams', 'fsockopen', 'exthttp') as $transport ) {157 if ( isset($working_transport[$transport]) )158 $nonblocking_transport[] = &$working_transport[$transport];159 }160 }161 162 do_action( 'http_transport_post_debug', $working_transport, $blocking_transport, $nonblocking_transport );163 164 if ( isset($args['blocking']) && !$args['blocking'] )165 return $nonblocking_transport;166 else167 return $blocking_transport;168 }169 35 170 36 /** … … 211 77 * @param string $url URI resource. 212 78 * @param str|array $args Optional. Override the defaults. 213 * @return array containing 'headers', 'body', 'response', 'cookies'79 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error 214 80 */ 215 81 function request( $url, $args = array() ) { … … 228 94 'compress' => false, 229 95 'decompress' => true, 230 'sslverify' => true 96 'sslverify' => true, 97 'stream' => false, 98 'filename' => null 231 99 ); 100 101 102 // Pre-parse for the HEAD checks. 103 $args = wp_parse_args( $args ); 104 105 // By default, Head requests do not cause redirections. 106 if ( isset($args['method']) && 'HEAD' == $args['method'] ) 107 $defaults['redirection'] = 0; 232 108 233 109 $r = wp_parse_args( $args, $defaults ); 234 110 $r = apply_filters( 'http_request_args', $r, $url ); 111 112 // Certain classes decrement this, store a copy of the original value for loop purposes. 113 $r['_redirection'] = $r['redirection']; 235 114 236 115 // Allow plugins to short-circuit the request … … 255 134 $r['local'] = $homeURL['host'] == $arrURL['host'] || 'localhost' == $arrURL['host']; 256 135 unset( $homeURL ); 136 137 // If we are streaming to a file but no filename was given drop it in the WP temp dir 138 // and pick it's name using the basename of the $url 139 if ( $r['stream'] && empty( $r['filename'] ) ) 140 $r['filename'] = get_temp_dir() . basename( $url ); 141 142 // Force some settings if we are streaming to a file and check for existence and perms of destination directory 143 if ( $r['stream'] ) { 144 $r['blocking'] = true; 145 if ( ! is_writable( dirname( $r['filename'] ) ) ) 146 return new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) ); 147 } 257 148 258 149 if ( is_null( $r['headers'] ) ) … … 287 178 if ( ($r['method'] == 'POST' || $r['method'] == 'PUT') && ! isset( $r['headers']['Content-Length'] ) ) 288 179 $r['headers']['Content-Length'] = 0; 289 290 // The method is ambiguous, because we aren't talking about HTTP methods, the "get" in291 // this case is simply that we aren't sending any bodies and to get the transports that292 // don't support sending bodies along with those which do.293 $transports = WP_Http::_getTransport( $r );294 180 } else { 295 181 if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) { 296 if ( ! version_compare(phpversion(), '5.1.2', '>=') ) 297 $r['body'] = _http_build_query( $r['body'], null, '&' ); 298 else 299 $r['body'] = http_build_query( $r['body'], null, '&' ); 182 $r['body'] = http_build_query( $r['body'], null, '&' ); 300 183 $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ); 301 184 $r['headers']['Content-Length'] = strlen( $r['body'] ); … … 304 187 if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) ) 305 188 $r['headers']['Content-Length'] = strlen( $r['body'] ); 306 307 // The method is ambiguous, because we aren't talking about HTTP methods, the "post" in 308 // this case is simply that we are sending HTTP body and to get the transports that do 309 // support sending the body. Not all do, depending on the limitations of the PHP core 310 // limitations. 311 $transports = WP_Http::_postTransport( $r ); 312 } 313 314 do_action( 'http_api_debug', $transports, 'transports_list' ); 315 316 $response = array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); 317 foreach ( (array) $transports as $transport ) { 318 $response = $transport->request( $url, $r ); 319 320 do_action( 'http_api_debug', $response, 'response', get_class( $transport ) ); 321 322 if ( ! is_wp_error( $response ) ) 323 return apply_filters( 'http_response', $response, $r, $url ); 324 } 325 326 return $response; 189 } 190 191 return $this->_dispatch_request($url, $r); 192 } 193 194 /** 195 * Tests which transports are capable of supporting the request. 196 * 197 * @since 3.2.0 198 * @access private 199 * 200 * @param array $args Request arguments 201 * @param string $url URL to Request 202 * 203 * @return string|false Class name for the first transport that claims to support the request. False if no transport claims to support the request. 204 */ 205 public function _get_first_available_transport( $args, $url = null ) { 206 $request_order = array( 'curl', 'streams', 'fsockopen' ); 207 208 // Loop over each transport on each HTTP request looking for one which will serve this request's needs 209 foreach ( $request_order as $transport ) { 210 $class = 'WP_HTTP_' . $transport; 211 212 // Check to see if this transport is a possibility, calls the transport statically 213 if ( !call_user_func( array( $class, 'test' ), $args, $url ) ) 214 continue; 215 216 return $class; 217 } 218 219 return false; 220 } 221 222 /** 223 * Dispatches a HTTP request to a supporting transport. 224 * 225 * Tests each transport in order to find a transport which matches the request arguements. 226 * Also caches the transport instance to be used later. 227 * 228 * The order for blocking requests is cURL, Streams, and finally Fsockopen. 229 * The order for non-blocking requests is cURL, Streams and Fsockopen(). 230 * 231 * There are currently issues with "localhost" not resolving correctly with DNS. This may cause 232 * an error "failed to open stream: A connection attempt failed because the connected party did 233 * not properly respond after a period of time, or established connection failed because [the] 234 * connected host has failed to respond." 235 * 236 * @since 3.2.0 237 * @access private 238 * 239 * @param string $url URL to Request 240 * @param array $args Request arguments 241 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error 242 */ 243 private function _dispatch_request( $url, $args ) { 244 static $transports = array(); 245 246 $class = $this->_get_first_available_transport( $args, $url ); 247 if ( !$class ) 248 return new WP_Error( 'http_failure', __( 'There are no HTTP transports available which can complete the requested request.' ) ); 249 250 // Transport claims to support request, instantiate it and give it a whirl. 251 if ( empty( $transports[$class] ) ) 252 $transports[$class] = new $class; 253 254 $response = $transports[$class]->request( $url, $args ); 255 256 do_action( 'http_api_debug', $response, 'response', $class ); 257 258 if ( is_wp_error( $response ) ) 259 return $response; 260 261 return apply_filters( 'http_response', $response, $args, $url ); 327 262 } 328 263 … … 337 272 * @param string $url URI resource. 338 273 * @param str|array $args Optional. Override the defaults. 339 * @return boolean274 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error 340 275 */ 341 276 function post($url, $args = array()) { … … 355 290 * @param string $url URI resource. 356 291 * @param str|array $args Optional. Override the defaults. 357 * @return boolean292 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error 358 293 */ 359 294 function get($url, $args = array()) { … … 373 308 * @param string $url URI resource. 374 309 * @param str|array $args Optional. Override the defaults. 375 * @return boolean310 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error 376 311 */ 377 312 function head($url, $args = array()) { … … 394 329 $res = explode("\r\n\r\n", $strResponse, 2); 395 330 396 return array('headers' => isset($res[0]) ? $res[0] : array(), 'body' => isset($res[1]) ? $res[1] : '');331 return array('headers' => $res[0], 'body' => isset($res[1]) ? $res[1] : ''); 397 332 } 398 333 … … 435 370 $cookies = array(); 436 371 $newheaders = array(); 437 foreach ( $headers as $tempheader ) {372 foreach ( (array) $headers as $tempheader ) { 438 373 if ( empty($tempheader) ) 439 374 continue; 440 375 441 376 if ( false === strpos($tempheader, ':') ) { 442 list( , $response['code'], $response['message']) = explode(' ', $tempheader, 3); 377 $stack = explode(' ', $tempheader, 3); 378 $stack[] = ''; 379 list( , $response['code'], $response['message']) = $stack; 443 380 continue; 444 381 } … … 625 562 * @param string $url URI resource. 626 563 * @param str|array $args Optional. Override the defaults. 627 * @return array 'headers', 'body', ' cookies' and 'response' keys.564 * @return array 'headers', 'body', 'response', 'cookies' and 'filename' keys. 628 565 */ 629 566 function request($url, $args = array()) { … … 749 686 750 687 $strResponse = ''; 751 while ( ! feof($handle) ) 752 $strResponse .= fread($handle, 4096); 753 754 fclose($handle); 688 $bodyStarted = false; 689 690 // If streaming to a file setup the file handle 691 if ( $r['stream'] ) { 692 if ( ! WP_DEBUG ) 693 $stream_handle = @fopen( $r['filename'], 'w+' ); 694 else 695 $stream_handle = fopen( $r['filename'], 'w+' ); 696 if ( ! $stream_handle ) 697 return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) ); 698 699 while ( ! feof($handle) ) { 700 $block = fread( $handle, 4096 ); 701 if ( $bodyStarted ) { 702 fwrite( $stream_handle, $block ); 703 } else { 704 $strResponse .= $block; 705 if ( strpos( $strResponse, "\r\n\r\n" ) ) { 706 $process = WP_Http::processResponse( $strResponse ); 707 $bodyStarted = true; 708 fwrite( $stream_handle, $process['body'] ); 709 unset( $strResponse ); 710 $process['body'] = ''; 711 } 712 } 713 } 714 715 fclose( $stream_handle ); 716 717 } else { 718 while ( ! feof($handle) ) 719 $strResponse .= fread( $handle, 4096 ); 720 721 $process = WP_Http::processResponse( $strResponse ); 722 unset( $strResponse ); 723 } 724 725 fclose( $handle ); 755 726 756 727 if ( true === $secure_transport ) 757 728 error_reporting($error_reporting); 758 729 759 $process = WP_Http::processResponse($strResponse); 760 $arrHeaders = WP_Http::processHeaders($process['headers']); 761 762 // Is the response code within the 400 range? 763 if ( (int) $arrHeaders['response']['code'] >= 400 && (int) $arrHeaders['response']['code'] < 500 ) 764 return new WP_Error('http_request_failed', $arrHeaders['response']['code'] . ': ' . $arrHeaders['response']['message']); 730 $arrHeaders = WP_Http::processHeaders( $process['headers'] ); 765 731 766 732 // If location is found, then assume redirect and redirect to location. 767 if ( 'HEAD' != $r['method'] && isset($arrHeaders['headers']['location'])) {733 if ( isset($arrHeaders['headers']['location']) && 0 !== $r['_redirection'] ) { 768 734 if ( $r['redirection']-- > 0 ) { 769 735 return $this->request($arrHeaders['headers']['location'], $r); … … 780 746 $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); 781 747 782 return array( 'headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']);748 return array( 'headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies'], 'filename' => $r['filename'] ); 783 749 } 784 750 … … 791 757 */ 792 758 function test( $args = array() ) { 759 if ( ! function_exists( 'fsockopen' ) ) 760 return false; 761 793 762 if ( false !== ($option = get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours 794 763 return false; 795 764 796 $is_ssl = isset($args['ssl']) && $args['ssl']; 797 798 if ( ! $is_ssl && function_exists( 'fsockopen' ) ) 799 $use = true; 800 elseif ( $is_ssl && extension_loaded('openssl') && function_exists( 'fsockopen' ) ) 801 $use = true; 802 else 803 $use = false; 804 805 return apply_filters('use_fsockopen_transport', $use, $args); 806 } 807 } 808 809 /** 810 * HTTP request method uses fopen function to retrieve the url. 811 * 812 * Requires PHP version greater than 4.3.0 for stream support. Does not allow for $context support, 813 * but should still be okay, to write the headers, before getting the response. Also requires that 814 * 'allow_url_fopen' to be enabled. 815 * 816 * @package WordPress 817 * @subpackage HTTP 818 * @since 2.7.0 819 */ 820 class WP_Http_Fopen { 821 /** 822 * Send a HTTP request to a URI using fopen(). 823 * 824 * This transport does not support sending of headers and body, therefore should not be used in 825 * the instances, where there is a body and headers. 826 * 827 * Notes: Does not support non-blocking mode. Ignores 'redirection' option. 828 * 829 * @see WP_Http::retrieve For default options descriptions. 830 * 831 * @access public 832 * @since 2.7.0 833 * 834 * @param string $url URI resource. 835 * @param str|array $args Optional. Override the defaults. 836 * @return array 'headers', 'body', 'cookies' and 'response' keys. 837 */ 838 function request($url, $args = array()) { 839 $defaults = array( 840 'method' => 'GET', 'timeout' => 5, 841 'redirection' => 5, 'httpversion' => '1.0', 842 'blocking' => true, 843 'headers' => array(), 'body' => null, 'cookies' => array() 844 ); 845 846 $r = wp_parse_args( $args, $defaults ); 847 848 $arrURL = parse_url($url); 849 850 if ( false === $arrURL ) 851 return new WP_Error('http_request_failed', sprintf(__('Malformed URL: %s'), $url)); 852 853 if ( 'http' != $arrURL['scheme'] && 'https' != $arrURL['scheme'] ) 854 $url = str_replace($arrURL['scheme'], 'http', $url); 855 856 if ( is_null( $r['headers'] ) ) 857 $r['headers'] = array(); 858 859 if ( is_string($r['headers']) ) { 860 $processedHeaders = WP_Http::processHeaders($r['headers']); 861 $r['headers'] = $processedHeaders['headers']; 862 } 863 864 $initial_user_agent = ini_get('user_agent'); 865 866 if ( !empty($r['headers']) && is_array($r['headers']) ) { 867 $user_agent_extra_headers = ''; 868 foreach ( $r['headers'] as $header => $value ) 869 $user_agent_extra_headers .= "\r\n$header: $value"; 870 @ini_set('user_agent', $r['user-agent'] . $user_agent_extra_headers); 871 } else { 872 @ini_set('user_agent', $r['user-agent']); 873 } 874 875 if ( !WP_DEBUG ) 876 $handle = @fopen($url, 'r'); 877 else 878 $handle = fopen($url, 'r'); 879 880 if (! $handle) 881 return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url)); 882 883 $timeout = (int) floor( $r['timeout'] ); 884 $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000; 885 stream_set_timeout( $handle, $timeout, $utimeout ); 886 887 if ( ! $r['blocking'] ) { 888 fclose($handle); 889 @ini_set('user_agent', $initial_user_agent); //Clean up any extra headers added 890 return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); 891 } 892 893 $strResponse = ''; 894 while ( ! feof($handle) ) 895 $strResponse .= fread($handle, 4096); 896 897 if ( function_exists('stream_get_meta_data') ) { 898 $meta = stream_get_meta_data($handle); 899 900 $theHeaders = $meta['wrapper_data']; 901 if ( isset( $meta['wrapper_data']['headers'] ) ) 902 $theHeaders = $meta['wrapper_data']['headers']; 903 } else { 904 //$http_response_header is a PHP reserved variable which is set in the current-scope when using the HTTP Wrapper 905 //see http://php.oregonstate.edu/manual/en/reserved.variables.httpresponseheader.php 906 $theHeaders = $http_response_header; 907 } 908 909 fclose($handle); 910 911 @ini_set('user_agent', $initial_user_agent); //Clean up any extra headers added 912 913 $processedHeaders = WP_Http::processHeaders($theHeaders); 914 915 if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] ) 916 $strResponse = WP_Http::chunkTransferDecode($strResponse); 917 918 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($processedHeaders['headers']) ) 919 $strResponse = WP_Http_Encoding::decompress( $strResponse ); 920 921 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']); 922 } 923 924 /** 925 * Whether this class can be used for retrieving an URL. 926 * 927 * @since 2.7.0 928 * @static 929 * @return boolean False means this class can not be used, true means it can. 930 */ 931 function test($args = array()) { 932 if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) ) 765 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; 766 767 if ( $is_ssl && ! extension_loaded( 'openssl' ) ) 933 768 return false; 934 769 935 if ( isset($args['method']) && 'HEAD' == $args['method'] ) //This transport cannot make a HEAD request 936 return false; 937 938 $use = true; 939 //PHP does not verify SSL certs, We can only make a request via this transports if SSL Verification is turned off. 940 $is_ssl = isset($args['ssl']) && $args['ssl']; 941 if ( $is_ssl ) { 942 $is_local = isset($args['local']) && $args['local']; 943 $ssl_verify = isset($args['sslverify']) && $args['sslverify']; 944 if ( $is_local && true != apply_filters('https_local_ssl_verify', true) ) 945 $use = true; 946 elseif ( !$is_local && true != apply_filters('https_ssl_verify', true) ) 947 $use = true; 948 elseif ( !$ssl_verify ) 949 $use = true; 950 else 951 $use = false; 952 } 953 954 return apply_filters('use_fopen_transport', $use, $args); 770 return apply_filters( 'use_fsockopen_transport', true, $args ); 955 771 } 956 772 } … … 977 793 * @param string $url 978 794 * @param str|array $args Optional. Override the defaults. 979 * @return array 'headers', 'body', ' cookies' and 'response' keys.795 * @return array 'headers', 'body', 'response', 'cookies' and 'filename' keys. 980 796 */ 981 797 function request($url, $args = array()) { … … 1050 866 } 1051 867 1052 if ( 'HEAD' == $r['method'] ) // Disable redirects for HEAD requests1053 $arrContext['http']['max_redirects'] = 1;1054 1055 868 if ( ! empty($r['body'] ) ) 1056 869 $arrContext['http']['content'] = $r['body']; … … 1076 889 } 1077 890 1078 $strResponse = stream_get_contents($handle); 1079 $meta = stream_get_meta_data($handle); 1080 1081 fclose($handle); 891 if ( $r['stream'] ) { 892 if ( ! WP_DEBUG ) 893 $stream_handle = @fopen( $r['filename'], 'w+' ); 894 else 895 $stream_handle = fopen( $r['filename'], 'w+' ); 896 897 if ( ! $stream_handle ) 898 return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) ); 899 900 stream_copy_to_stream( $handle, $stream_handle ); 901 902 fclose( $stream_handle ); 903 $strResponse = ''; 904 } else { 905 $strResponse = stream_get_contents( $handle ); 906 } 907 908 $meta = stream_get_meta_data( $handle ); 909 910 fclose( $handle ); 1082 911 1083 912 $processedHeaders = array(); … … 1087 916 $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']); 1088 917 918 // Streams does not provide an error code which we can use to see why the request stream stoped. 919 // We can however test to see if a location header is present and return based on that. 920 if ( isset($processedHeaders['headers']['location']) && 0 !== $args['_redirection'] ) 921 return new WP_Error('http_request_failed', __('Too many redirects.')); 922 1089 923 if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] ) 1090 924 $strResponse = WP_Http::chunkTransferDecode($strResponse); … … 1093 927 $strResponse = WP_Http_Encoding::decompress( $strResponse ); 1094 928 1095 return array( 'headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']);929 return array( 'headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies'], 'filename' => $r['filename'] ); 1096 930 } 1097 931 … … 1105 939 * @return boolean False means this class can not be used, true means it can. 1106 940 */ 1107 function test( $args = array()) {1108 if ( ! function_exists( 'fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) )941 function test( $args = array() ) { 942 if ( ! function_exists( 'fopen' ) ) 1109 943 return false; 1110 944 1111 if ( version_compare(PHP_VERSION, '5.0', '<') )945 if ( ! function_exists( 'ini_get' ) || true != ini_get( 'allow_url_fopen' ) ) 1112 946 return false; 1113 947 1114 //HTTPS via Proxy was added in 5.1.0 1115 $is_ssl = isset($args['ssl']) && $args['ssl']; 1116 if ( $is_ssl && version_compare(PHP_VERSION, '5.1.0', '<') ) { 1117 $proxy = new WP_HTTP_Proxy(); 1118 /** 1119 * No URL check, as its not currently passed to the ::test() function 1120 * In the case where a Proxy is in use, Just bypass this transport for HTTPS. 1121 */ 1122 if ( $proxy->is_enabled() ) 1123 return false; 1124 } 1125 1126 return apply_filters('use_streams_transport', true, $args); 948 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; 949 950 if ( $is_ssl && ! extension_loaded( 'openssl' ) ) 951 return false; 952 953 return apply_filters( 'use_streams_transport', true, $args ); 1127 954 } 1128 955 } 1129 956 1130 957 /** 1131 * HTTP request method uses HTTP extension to retrieve the url. 1132 * 1133 * Requires the HTTP extension to be installed. This would be the preferred transport since it can 1134 * handle a lot of the problems that forces the others to use the HTTP version 1.0. Even if PHP 5.2+ 1135 * is being used, it doesn't mean that the HTTP extension will be enabled. 958 * HTTP request method uses Curl extension to retrieve the url. 959 * 960 * Requires the Curl extension to be installed. 1136 961 * 1137 962 * @package WordPress 1138 963 * @subpackage HTTP 1139 * @since 2.7 .0964 * @since 2.7 1140 965 */ 1141 class WP_Http_ExtHttp { 1142 /** 1143 * Send a HTTP request to a URI using HTTP extension. 1144 * 1145 * Does not support non-blocking. 966 class WP_Http_Curl { 967 968 /** 969 * Temporary header storage for use with streaming to a file. 970 * 971 * @since 3.2.0 972 * @access private 973 * @var string 974 */ 975 private $headers = ''; 976 977 /** 978 * Send a HTTP request to a URI using cURL extension. 1146 979 * 1147 980 * @access public 1148 * @since 2.7 981 * @since 2.7.0 1149 982 * 1150 983 * @param string $url 1151 984 * @param str|array $args Optional. Override the defaults. 1152 * @return array 'headers', 'body', ' cookies' and 'response' keys.985 * @return array 'headers', 'body', 'response', 'cookies' and 'filename' keys. 1153 986 */ 1154 987 function request($url, $args = array()) { … … 1170 1003 } 1171 1004 1172 // Construct Cookie: header if any cookies are set 1005 // Construct Cookie: header if any cookies are set. 1173 1006 WP_Http::buildCookieHeader( $r ); 1174 1007 1175 switch ( $r['method'] ) { 1176 case 'POST': 1177 $r['method'] = HTTP_METH_POST; 1178 break; 1179 case 'HEAD': 1180 $r['method'] = HTTP_METH_HEAD; 1181 break; 1182 case 'PUT': 1183 $r['method'] = HTTP_METH_PUT; 1184 break; 1185 case 'GET': 1186 default: 1187 $r['method'] = HTTP_METH_GET; 1188 } 1189 1190 $arrURL = parse_url($url); 1191 1192 if ( 'http' != $arrURL['scheme'] && 'https' != $arrURL['scheme'] ) 1193 $url = preg_replace('|^' . preg_quote($arrURL['scheme'], '|') . '|', 'http', $url); 1008 $handle = curl_init(); 1009 1010 // cURL offers really easy proxy support. 1011 $proxy = new WP_HTTP_Proxy(); 1012 1013 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { 1014 1015 curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); 1016 curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() ); 1017 curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() ); 1018 1019 if ( $proxy->use_authentication() ) { 1020 curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); 1021 curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() ); 1022 } 1023 } 1194 1024 1195 1025 $is_local = isset($args['local']) && $args['local']; … … 1200 1030 $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify); 1201 1031 1202 $r['timeout'] = (int) ceil( $r['timeout'] );1203 1204 $options = array(1205 'timeout' => $r['timeout'],1206 'connecttimeout' => $r['timeout'],1207 'redirect' => $r['redirection'],1208 'useragent' => $r['user-agent'],1209 'headers' => $r['headers'],1210 'ssl' => array(1211 'verifypeer' => $ssl_verify,1212 'verifyhost' => $ssl_verify1213 )1214 );1215 1216 if ( HTTP_METH_HEAD == $r['method'] )1217 $options['redirect'] = 0; // Assumption: Docs seem to suggest that this means do not follow. Untested.1218 1219 // The HTTP extensions offers really easy proxy support.1220 $proxy = new WP_HTTP_Proxy();1221 1222 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {1223 $options['proxyhost'] = $proxy->host();1224 $options['proxyport'] = $proxy->port();1225 $options['proxytype'] = HTTP_PROXY_HTTP;1226 1227 if ( $proxy->use_authentication() ) {1228 $options['proxyauth'] = $proxy->authentication();1229 $options['proxyauthtype'] = HTTP_AUTH_ANY;1230 }1231 }1232 1233 if ( !WP_DEBUG ) //Emits warning level notices for max redirects and timeouts1234 $strResponse = @http_request($r['method'], $url, $r['body'], $options, $info);1235 else1236 $strResponse = http_request($r['method'], $url, $r['body'], $options, $info); //Emits warning level notices for max redirects and timeouts1237 1238 // Error may still be set, Response may return headers or partial document, and error1239 // contains a reason the request was aborted, eg, timeout expired or max-redirects reached.1240 if ( false === $strResponse || ! empty($info['error']) )1241 return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']);1242 1243 if ( ! $r['blocking'] )1244 return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() );1245 1246 $headers_body = WP_HTTP::processResponse($strResponse);1247 $theHeaders = $headers_body['headers'];1248 $theBody = $headers_body['body'];1249 unset($headers_body);1250 1251 $theHeaders = WP_Http::processHeaders($theHeaders);1252 1253 if ( ! empty( $theBody ) && isset( $theHeaders['headers']['transfer-encoding'] ) && 'chunked' == $theHeaders['headers']['transfer-encoding'] ) {1254 if ( !WP_DEBUG )1255 $theBody = @http_chunked_decode($theBody);1256 else1257 $theBody = http_chunked_decode($theBody);1258 }1259 1260 if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) )1261 $theBody = http_inflate( $theBody );1262 1263 $theResponse = array();1264 $theResponse['code'] = $info['response_code'];1265 $theResponse['message'] = get_status_header_desc($info['response_code']);1266 1267 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $theResponse, 'cookies' => $theHeaders['cookies']);1268 }1269 1270 /**1271 * Whether this class can be used for retrieving an URL.1272 *1273 * @static1274 * @since 2.7.01275 *1276 * @return boolean False means this class can not be used, true means it can.1277 */1278 function test($args = array()) {1279 return apply_filters('use_http_extension_transport', function_exists('http_request'), $args );1280 }1281 }1282 1283 /**1284 * HTTP request method uses Curl extension to retrieve the url.1285 *1286 * Requires the Curl extension to be installed.1287 *1288 * @package WordPress1289 * @subpackage HTTP1290 * @since 2.71291 */1292 class WP_Http_Curl {1293 1294 /**1295 * Send a HTTP request to a URI using cURL extension.1296 *1297 * @access public1298 * @since 2.7.01299 *1300 * @param string $url1301 * @param str|array $args Optional. Override the defaults.1302 * @return array 'headers', 'body', 'cookies' and 'response' keys.1303 */1304 function request($url, $args = array()) {1305 $defaults = array(1306 'method' => 'GET', 'timeout' => 5,1307 'redirection' => 5, 'httpversion' => '1.0',1308 'blocking' => true,1309 'headers' => array(), 'body' => null, 'cookies' => array()1310 );1311 1312 $r = wp_parse_args( $args, $defaults );1313 1314 if ( isset($r['headers']['User-Agent']) ) {1315 $r['user-agent'] = $r['headers']['User-Agent'];1316 unset($r['headers']['User-Agent']);1317 } else if ( isset($r['headers']['user-agent']) ) {1318 $r['user-agent'] = $r['headers']['user-agent'];1319 unset($r['headers']['user-agent']);1320 }1321 1322 // Construct Cookie: header if any cookies are set.1323 WP_Http::buildCookieHeader( $r );1324 1325 $handle = curl_init();1326 1327 // cURL offers really easy proxy support.1328 $proxy = new WP_HTTP_Proxy();1329 1330 if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) {1331 1332 $isPHP5 = version_compare(PHP_VERSION, '5.0.0', '>=');1333 1334 if ( $isPHP5 ) {1335 curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );1336 curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() );1337 curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() );1338 } else {1339 curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() .':'. $proxy->port() );1340 }1341 1342 if ( $proxy->use_authentication() ) {1343 if ( $isPHP5 )1344 curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY );1345 1346 curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() );1347 }1348 }1349 1350 $is_local = isset($args['local']) && $args['local'];1351 $ssl_verify = isset($args['sslverify']) && $args['sslverify'];1352 if ( $is_local )1353 $ssl_verify = apply_filters('https_local_ssl_verify', $ssl_verify);1354 elseif ( ! $is_local )1355 $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify);1356 1357 1032 1358 1033 // CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since … … 1364 1039 curl_setopt( $handle, CURLOPT_URL, $url); 1365 1040 curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); 1366 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, $ssl_verify);1041 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false ); 1367 1042 curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); 1368 1043 curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] ); … … 1384 1059 1385 1060 if ( true === $r['blocking'] ) 1386 curl_setopt( $handle, CURLOPT_HEADER, true ); 1387 else 1388 curl_setopt( $handle, CURLOPT_HEADER, false ); 1061 curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( &$this, 'stream_headers' ) ); 1062 1063 curl_setopt( $handle, CURLOPT_HEADER, false ); 1064 1065 // If streaming to a file open a file handle, and setup our curl streaming handler 1066 if ( $r['stream'] ) { 1067 if ( ! WP_DEBUG ) 1068 $stream_handle = @fopen( $r['filename'], 'w+' ); 1069 else 1070 $stream_handle = fopen( $r['filename'], 'w+' ); 1071 if ( ! $stream_handle ) 1072 return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) ); 1073 curl_setopt( $handle, CURLOPT_FILE, $stream_handle ); 1074 } 1389 1075 1390 1076 // The option doesn't work with safe mode or when open_basedir is set. 1391 // Disable HEAD when making HEAD requests. 1392 if ( !ini_get('safe_mode') && !ini_get('open_basedir') && 'HEAD' != $r['method'] ) 1077 if ( !ini_get('safe_mode') && !ini_get('open_basedir') && 0 !== $r['_redirection'] ) 1393 1078 curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true ); 1394 1079 … … 1419 1104 1420 1105 $theResponse = curl_exec( $handle ); 1421 1422 if ( !empty($theResponse) ) { 1423 $headerLength = curl_getinfo($handle, CURLINFO_HEADER_SIZE); 1424 $theHeaders = trim( substr($theResponse, 0, $headerLength) ); 1425 if ( strlen($theResponse) > $headerLength ) 1426 $theBody = substr( $theResponse, $headerLength ); 1427 else 1428 $theBody = ''; 1429 if ( false !== strpos($theHeaders, "\r\n\r\n") ) { 1430 $headerParts = explode("\r\n\r\n", $theHeaders); 1431 $theHeaders = $headerParts[ count($headerParts) -1 ]; 1432 } 1433 $theHeaders = WP_Http::processHeaders($theHeaders); 1434 } else { 1106 $theBody = ''; 1107 $theHeaders = WP_Http::processHeaders( $this->headers ); 1108 1109 if ( strlen($theResponse) > 0 && ! is_bool( $theResponse ) ) // is_bool: when using $args['stream'], curl_exec will return (bool)true 1110 $theBody = $theResponse; 1111 1112 // If no response, and It's not a HEAD request with valid headers returned 1113 if ( 0 == strlen($theResponse) && ('HEAD' != $args['method'] || empty($this->headers)) ) { 1435 1114 if ( $curl_error = curl_error($handle) ) 1436 1115 return new WP_Error('http_request_failed', $curl_error); 1437 1116 if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) ) 1438 1117 return new WP_Error('http_request_failed', __('Too many redirects.')); 1439 1440 $theHeaders = array( 'headers' => array(), 'cookies' => array() ); 1441 $theBody = ''; 1442 } 1118 } 1119 1120 unset( $this->headers ); 1443 1121 1444 1122 $response = array(); … … 1448 1126 curl_close( $handle ); 1449 1127 1128 if ( $r['stream'] ) 1129 fclose( $stream_handle ); 1130 1450 1131 // See #11305 - When running under safe mode, redirection is disabled above. Handle it manually. 1451 if ( ! empty($theHeaders['headers']['location']) && (ini_get('safe_mode') || ini_get('open_basedir'))) {1132 if ( ! empty( $theHeaders['headers']['location'] ) && ( ini_get( 'safe_mode' ) || ini_get( 'open_basedir' ) ) && 0 !== $r['_redirection'] ) { 1452 1133 if ( $r['redirection']-- > 0 ) { 1453 return $this->request( $theHeaders['headers']['location'], $r);1134 return $this->request( $theHeaders['headers']['location'], $r ); 1454 1135 } else { 1455 return new WP_Error( 'http_request_failed', __('Too many redirects.'));1136 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); 1456 1137 } 1457 1138 } … … 1460 1141 $theBody = WP_Http_Encoding::decompress( $theBody ); 1461 1142 1462 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']); 1143 return array( 'headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies'], 'filename' => $r['filename'] ); 1144 } 1145 1146 /** 1147 * Grab the headers of the cURL request 1148 * 1149 * Each header is sent individually to this callback, so we append to the $header property for temporary storage 1150 * 1151 * @since 3.2.0 1152 * @access private 1153 * @return int 1154 */ 1155 private function stream_headers( $handle, $headers ) { 1156 $this->headers .= $headers; 1157 return strlen( $headers ); 1463 1158 } 1464 1159 … … 1471 1166 * @return boolean False means this class can not be used, true means it can. 1472 1167 */ 1473 function test($args = array()) { 1474 if ( function_exists('curl_init') && function_exists('curl_exec') ) 1475 return apply_filters('use_curl_transport', true, $args); 1476 1477 return false; 1168 function test( $args = array() ) { 1169 if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) 1170 return false; 1171 1172 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; 1173 1174 if ( $is_ssl ) { 1175 $curl_version = curl_version(); 1176 if ( ! (CURL_VERSION_SSL & $curl_version['features']) ) // Does this cURL version support SSL requests? 1177 return false; 1178 } 1179 1180 return apply_filters( 'use_curl_transport', true, $args ); 1478 1181 } 1479 1182 } … … 1487 1190 * 1488 1191 * Please note that only BASIC authentication is supported by most transports. 1489 * cURL and the PHP HTTP ExtensionMAY support more methods (such as NTLM authentication) depending on your environment.1192 * cURL MAY support more methods (such as NTLM authentication) depending on your environment. 1490 1193 * 1491 1194 * The constants are as follows: … … 1720 1423 */ 1721 1424 var $domain; 1722 1723 /**1724 * PHP4 style Constructor - Calls PHP5 Style Constructor.1725 *1726 * @access public1727 * @since 2.8.01728 * @param string|array $data Raw cookie data.1729 */1730 function WP_Http_Cookie( $data ) {1731 $this->__construct( $data );1732 }1733 1425 1734 1426 /**
Note: See TracChangeset
for help on using the changeset viewer.