Changes in trunk/wp-includes/class-http.php [18254:17282]
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-includes/class-http.php
r18254 r17282 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 * 30 34 * @package WordPress 31 35 * @subpackage HTTP … … 33 37 */ 34 38 class WP_Http { 39 40 /** 41 * PHP4 style Constructor - Calls PHP5 Style Constructor 42 * 43 * @since 2.7.0 44 * @return WP_Http 45 */ 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 to 54 * be used for the static call. The transport are set up to save time and will only be created 55 * once. This class can be created many times without having to go through the step of finding 56 * which transports are available. 57 * 58 * @since 2.7.0 59 * @return WP_Http 60 */ 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 be 70 * used later. 71 * 72 * The order for the GET/HEAD requests are HTTP Extension, cURL, Streams, Fopen, and finally 73 * 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 way 75 * the other transports also handle redirects. 76 * 77 * There are currently issues with "localhost" not resolving correctly with DNS. This may cause 78 * an error "failed to open stream: A connection attempt failed because the connected party did 79 * 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.0 83 * @access private 84 * 85 * @param array $args Request args, default us an empty array 86 * @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 else 120 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 caches 127 * that object to be used later. This is for posting content to a URL and 128 * is used when there is a body. The plain Fopen Transport can not be used 129 * to send content, but the streams transport can. This is a limitation that 130 * is addressed here, by just not including that transport. 131 * 132 * @since 2.7.0 133 * @access private 134 * 135 * @param array $args Request args, default us an empty array 136 * @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 else 167 return $blocking_transport; 168 } 35 169 36 170 /** … … 77 211 * @param string $url URI resource. 78 212 * @param str|array $args Optional. Override the defaults. 79 * @return array |object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error213 * @return array containing 'headers', 'body', 'response', 'cookies' 80 214 */ 81 215 function request( $url, $args = array() ) { … … 94 228 'compress' => false, 95 229 'decompress' => true, 96 'sslverify' => true, 97 'stream' => false, 98 'filename' => null 230 'sslverify' => true 99 231 ); 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;108 232 109 233 $r = wp_parse_args( $args, $defaults ); 110 234 $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'];114 235 115 236 // Allow plugins to short-circuit the request … … 134 255 $r['local'] = $homeURL['host'] == $arrURL['host'] || 'localhost' == $arrURL['host']; 135 256 unset( $homeURL ); 136 137 // If we are streaming to a file but no filename was given drop it in the WP temp dir138 // and pick it's name using the basename of the $url139 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 directory143 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 }148 257 149 258 if ( is_null( $r['headers'] ) ) … … 178 287 if ( ($r['method'] == 'POST' || $r['method'] == 'PUT') && ! isset( $r['headers']['Content-Length'] ) ) 179 288 $r['headers']['Content-Length'] = 0; 289 290 // The method is ambiguous, because we aren't talking about HTTP methods, the "get" in 291 // this case is simply that we aren't sending any bodies and to get the transports that 292 // don't support sending bodies along with those which do. 293 $transports = WP_Http::_getTransport( $r ); 180 294 } else { 181 295 if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) { 182 $r['body'] = http_build_query( $r['body'], null, '&' ); 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, '&' ); 183 300 $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ); 184 301 $r['headers']['Content-Length'] = strlen( $r['body'] ); … … 187 304 if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) ) 188 305 $r['headers']['Content-Length'] = strlen( $r['body'] ); 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 ); 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; 262 327 } 263 328 … … 272 337 * @param string $url URI resource. 273 338 * @param str|array $args Optional. Override the defaults. 274 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error339 * @return boolean 275 340 */ 276 341 function post($url, $args = array()) { … … 290 355 * @param string $url URI resource. 291 356 * @param str|array $args Optional. Override the defaults. 292 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error357 * @return boolean 293 358 */ 294 359 function get($url, $args = array()) { … … 308 373 * @param string $url URI resource. 309 374 * @param str|array $args Optional. Override the defaults. 310 * @return array|object Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error375 * @return boolean 311 376 */ 312 377 function head($url, $args = array()) { … … 329 394 $res = explode("\r\n\r\n", $strResponse, 2); 330 395 331 return array('headers' => $res[0], 'body' => isset($res[1]) ? $res[1] : '');396 return array('headers' => isset($res[0]) ? $res[0] : array(), 'body' => isset($res[1]) ? $res[1] : ''); 332 397 } 333 398 … … 370 435 $cookies = array(); 371 436 $newheaders = array(); 372 foreach ( (array)$headers as $tempheader ) {437 foreach ( $headers as $tempheader ) { 373 438 if ( empty($tempheader) ) 374 439 continue; 375 440 376 441 if ( false === strpos($tempheader, ':') ) { 377 $stack = explode(' ', $tempheader, 3); 378 $stack[] = ''; 379 list( , $response['code'], $response['message']) = $stack; 442 list( , $response['code'], $response['message']) = explode(' ', $tempheader, 3); 380 443 continue; 381 444 } … … 562 625 * @param string $url URI resource. 563 626 * @param str|array $args Optional. Override the defaults. 564 * @return array 'headers', 'body', ' response', 'cookies' and 'filename' keys.627 * @return array 'headers', 'body', 'cookies' and 'response' keys. 565 628 */ 566 629 function request($url, $args = array()) { … … 686 749 687 750 $strResponse = ''; 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 ); 751 while ( ! feof($handle) ) 752 $strResponse .= fread($handle, 4096); 753 754 fclose($handle); 726 755 727 756 if ( true === $secure_transport ) 728 757 error_reporting($error_reporting); 729 758 730 $arrHeaders = WP_Http::processHeaders( $process['headers'] ); 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']); 731 765 732 766 // If location is found, then assume redirect and redirect to location. 733 if ( isset($arrHeaders['headers']['location']) && 0 !== $r['_redirection']) {767 if ( 'HEAD' != $r['method'] && isset($arrHeaders['headers']['location']) ) { 734 768 if ( $r['redirection']-- > 0 ) { 735 769 return $this->request($arrHeaders['headers']['location'], $r); … … 746 780 $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); 747 781 748 return array( 'headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies'], 'filename' => $r['filename']);782 return array('headers' => $arrHeaders['headers'], 'body' => $process['body'], 'response' => $arrHeaders['response'], 'cookies' => $arrHeaders['cookies']); 749 783 } 750 784 … … 757 791 */ 758 792 function test( $args = array() ) { 759 if ( ! function_exists( 'fsockopen' ) )760 return false;761 762 793 if ( false !== ($option = get_option( 'disable_fsockopen' )) && time()-$option < 43200 ) // 12 hours 763 794 return false; 764 795 765 $is_ssl = isset( $args['ssl'] ) && $args['ssl']; 766 767 if ( $is_ssl && ! extension_loaded( 'openssl' ) ) 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')) ) 768 933 return false; 769 934 770 return apply_filters( 'use_fsockopen_transport', true, $args ); 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); 771 955 } 772 956 } … … 793 977 * @param string $url 794 978 * @param str|array $args Optional. Override the defaults. 795 * @return array 'headers', 'body', ' response', 'cookies' and 'filename' keys.979 * @return array 'headers', 'body', 'cookies' and 'response' keys. 796 980 */ 797 981 function request($url, $args = array()) { … … 866 1050 } 867 1051 1052 if ( 'HEAD' == $r['method'] ) // Disable redirects for HEAD requests 1053 $arrContext['http']['max_redirects'] = 1; 1054 868 1055 if ( ! empty($r['body'] ) ) 869 1056 $arrContext['http']['content'] = $r['body']; … … 889 1076 } 890 1077 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 ); 1078 $strResponse = stream_get_contents($handle); 1079 $meta = stream_get_meta_data($handle); 1080 1081 fclose($handle); 911 1082 912 1083 $processedHeaders = array(); … … 916 1087 $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']); 917 1088 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 923 1089 if ( ! empty( $strResponse ) && isset( $processedHeaders['headers']['transfer-encoding'] ) && 'chunked' == $processedHeaders['headers']['transfer-encoding'] ) 924 1090 $strResponse = WP_Http::chunkTransferDecode($strResponse); … … 927 1093 $strResponse = WP_Http_Encoding::decompress( $strResponse ); 928 1094 929 return array( 'headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies'], 'filename' => $r['filename']);1095 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response'], 'cookies' => $processedHeaders['cookies']); 930 1096 } 931 1097 … … 939 1105 * @return boolean False means this class can not be used, true means it can. 940 1106 */ 941 function test( $args = array()) {942 if ( ! function_exists( 'fopen') )1107 function test($args = array()) { 1108 if ( ! function_exists('fopen') || (function_exists('ini_get') && true != ini_get('allow_url_fopen')) ) 943 1109 return false; 944 1110 945 if ( ! function_exists( 'ini_get' ) || true != ini_get( 'allow_url_fopen') )1111 if ( version_compare(PHP_VERSION, '5.0', '<') ) 946 1112 return false; 947 1113 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 ); 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); 954 1127 } 955 1128 } 956 1129 957 1130 /** 958 * HTTP request method uses Curl extension to retrieve the url. 959 * 960 * Requires the Curl extension to be installed. 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. 961 1136 * 962 1137 * @package WordPress 963 1138 * @subpackage HTTP 964 * @since 2.7 1139 * @since 2.7.0 965 1140 */ 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. 1141 class WP_Http_ExtHttp { 1142 /** 1143 * Send a HTTP request to a URI using HTTP extension. 1144 * 1145 * Does not support non-blocking. 979 1146 * 980 1147 * @access public 981 * @since 2.7 .01148 * @since 2.7 982 1149 * 983 1150 * @param string $url 984 1151 * @param str|array $args Optional. Override the defaults. 985 * @return array 'headers', 'body', ' response', 'cookies' and 'filename' keys.1152 * @return array 'headers', 'body', 'cookies' and 'response' keys. 986 1153 */ 987 1154 function request($url, $args = array()) { … … 1003 1170 } 1004 1171 1005 // Construct Cookie: header if any cookies are set .1172 // Construct Cookie: header if any cookies are set 1006 1173 WP_Http::buildCookieHeader( $r ); 1007 1174 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 } 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); 1024 1194 1025 1195 $is_local = isset($args['local']) && $args['local']; … … 1030 1200 $ssl_verify = apply_filters('https_ssl_verify', $ssl_verify); 1031 1201 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_verify 1213 ) 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 timeouts 1234 $strResponse = @http_request($r['method'], $url, $r['body'], $options, $info); 1235 else 1236 $strResponse = http_request($r['method'], $url, $r['body'], $options, $info); //Emits warning level notices for max redirects and timeouts 1237 1238 // Error may still be set, Response may return headers or partial document, and error 1239 // 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 else 1257 $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 * @static 1274 * @since 2.7.0 1275 * 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 WordPress 1289 * @subpackage HTTP 1290 * @since 2.7 1291 */ 1292 class WP_Http_Curl { 1293 1294 /** 1295 * Send a HTTP request to a URI using cURL extension. 1296 * 1297 * @access public 1298 * @since 2.7.0 1299 * 1300 * @param string $url 1301 * @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 1032 1357 1033 1358 // CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since … … 1039 1364 curl_setopt( $handle, CURLOPT_URL, $url); 1040 1365 curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); 1041 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false);1366 curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, $ssl_verify ); 1042 1367 curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); 1043 1368 curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] ); … … 1059 1384 1060 1385 if ( true === $r['blocking'] ) 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 } 1386 curl_setopt( $handle, CURLOPT_HEADER, true ); 1387 else 1388 curl_setopt( $handle, CURLOPT_HEADER, false ); 1075 1389 1076 1390 // The option doesn't work with safe mode or when open_basedir is set. 1077 if ( !ini_get('safe_mode') && !ini_get('open_basedir') && 0 !== $r['_redirection'] ) 1391 // Disable HEAD when making HEAD requests. 1392 if ( !ini_get('safe_mode') && !ini_get('open_basedir') && 'HEAD' != $r['method'] ) 1078 1393 curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true ); 1079 1394 … … 1104 1419 1105 1420 $theResponse = curl_exec( $handle ); 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)) ) { 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 { 1114 1435 if ( $curl_error = curl_error($handle) ) 1115 1436 return new WP_Error('http_request_failed', $curl_error); 1116 1437 if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array(301, 302) ) ) 1117 1438 return new WP_Error('http_request_failed', __('Too many redirects.')); 1118 } 1119 1120 unset( $this->headers ); 1439 1440 $theHeaders = array( 'headers' => array(), 'cookies' => array() ); 1441 $theBody = ''; 1442 } 1121 1443 1122 1444 $response = array(); … … 1126 1448 curl_close( $handle ); 1127 1449 1128 if ( $r['stream'] )1129 fclose( $stream_handle );1130 1131 1450 // See #11305 - When running under safe mode, redirection is disabled above. Handle it manually. 1132 if ( ! empty( $theHeaders['headers']['location'] ) && ( ini_get( 'safe_mode' ) || ini_get( 'open_basedir' ) ) && 0 !== $r['_redirection']) {1451 if ( !empty($theHeaders['headers']['location']) && (ini_get('safe_mode') || ini_get('open_basedir')) ) { 1133 1452 if ( $r['redirection']-- > 0 ) { 1134 return $this->request( $theHeaders['headers']['location'], $r);1453 return $this->request($theHeaders['headers']['location'], $r); 1135 1454 } else { 1136 return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ));1455 return new WP_Error('http_request_failed', __('Too many redirects.')); 1137 1456 } 1138 1457 } … … 1141 1460 $theBody = WP_Http_Encoding::decompress( $theBody ); 1142 1461 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 ); 1462 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response, 'cookies' => $theHeaders['cookies']); 1158 1463 } 1159 1464 … … 1166 1471 * @return boolean False means this class can not be used, true means it can. 1167 1472 */ 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 ); 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; 1181 1478 } 1182 1479 } … … 1190 1487 * 1191 1488 * Please note that only BASIC authentication is supported by most transports. 1192 * cURL MAY support more methods (such as NTLM authentication) depending on your environment.1489 * cURL and the PHP HTTP Extension MAY support more methods (such as NTLM authentication) depending on your environment. 1193 1490 * 1194 1491 * The constants are as follows: … … 1423 1720 */ 1424 1721 var $domain; 1722 1723 /** 1724 * PHP4 style Constructor - Calls PHP5 Style Constructor. 1725 * 1726 * @access public 1727 * @since 2.8.0 1728 * @param string|array $data Raw cookie data. 1729 */ 1730 function WP_Http_Cookie( $data ) { 1731 $this->__construct( $data ); 1732 } 1425 1733 1426 1734 /**
Note: See TracChangeset
for help on using the changeset viewer.