Ticket #4779: 4779.r8519.diff
File 4779.r8519.diff, 21.5 KB (added by , 16 years ago) |
---|
-
http.php
1 1 <?php 2 2 /** 3 * Simple HTTP request fallback system3 * Simple and uniform HTTP request API. 4 4 * 5 * Will eventually replace and standardize the WordPress HTTP requests made. 6 * 5 7 * @package WordPress 6 8 * @subpackage HTTP 7 9 * @since 2.7 … … 9 11 */ 10 12 11 13 /** 12 * Abstract class for all of the fallback implementation 13 * classes. The implementation classes will extend this class 14 * to keep common API methods universal between different 15 * functionality. 14 * WordPress HTTP Class for managing HTTP Transports and making HTTP requests. 16 15 * 16 * This class is called for the functionality of making HTTP requests and should 17 * replace Snoopy functionality, eventually. There is no available functionality 18 * to add HTTP transport implementations, since most of the HTTP transports are 19 * added and available for use. 20 * 21 * The exception is that cURL is not available as a transport and lacking an 22 * implementation. It will be added later and should be a patch on the WordPress 23 * Trac. 24 * 25 * There are no properties, because none are needed and for performance reasons. 26 * Some of the functions are static and while they do have some overhead over 27 * functions in PHP4, the purpose is maintainability. When PHP5 is finally the 28 * requirement, it will be easy to add the static keyword to the code. It is not 29 * as easy to convert a function to a method after enough code uses the old way. 30 * 17 31 * @package WordPress 18 32 * @subpackage HTTP 19 33 * @since 2.7 … … 33 47 /** 34 48 * PHP5 style Constructor - Setup available transport if not available. 35 49 * 50 * PHP4 does not have the 'self' keyword and since WordPress supports PHP4, 51 * the class needs to be used for the static call. 52 * 53 * The transport are setup to save time. This should only be called once, so 54 * the overhead should be fine. 55 * 36 56 * @since 2.7 37 57 * @return WP_Http 38 58 */ 39 59 function __construct() { 40 60 WP_Http::_getTransport(); 61 WP_Http::_postTransport(); 41 62 } 42 63 43 64 /** … … 46 67 * Tests all of the objects and returns the object that passes. Also caches 47 68 * that object to be used later. 48 69 * 70 * The order for the GET/HEAD requests are Streams, HTTP Extension, Fopen, 71 * and finally Fsockopen. fsockopen() is used last, because it has the most 72 * overhead in its implementation. There isn't any real way around it, since 73 * redirects have to be supported, much the same way the other transports 74 * also handle redirects. 75 * 76 * There are currently issues with "localhost" not resolving correctly with 77 * DNS. This may cause an error "failed to open stream: A connection attempt 78 * failed because the connected party did not properly respond after a 79 * period of time, or established connection failed because connected host 80 * has failed to respond." 81 * 49 82 * @since 2.7 50 83 * @access private 51 84 * … … 75 108 * that object to be used later. This is for posting content to a URL and 76 109 * is used when there is a body. The plain Fopen Transport can not be used 77 110 * to send content, but the streams transport can. This is a limitation that 78 * is addressed here .111 * is addressed here, by just not including that transport. 79 112 * 80 113 * @since 2.7 81 114 * @access private … … 98 131 } 99 132 100 133 /** 101 * Retrieve the location and set the class properties after the site has been retrieved.134 * Send a HTTP request to a URI. 102 135 * 136 * The only URI that are supported in the HTTP Transport implementation are 137 * the HTTP and HTTPS protocols. HTTP and HTTPS are assumed so the server 138 * might not know how to handle the send headers. Other protocols are 139 * unsupported and most likely will fail. 140 * 141 * The defaults are 'method', 'timeout', 'redirection', 'httpversion', 142 * 'blocking' and 'user-agent'. 143 * 144 * Accepted 'method' values are 'GET', 'POST', and 'HEAD', some transports 145 * technically allow others, but should not be assumed. The 'timeout' is 146 * used to sent how long the connection should stay open before failing when 147 * no response. 'redirection' is used to track how many redirects were taken 148 * and used to sent the amount for other transports, but not all transports 149 * accept setting that value. 150 * 151 * The 'httpversion' option is used to sent the HTTP version and accepted 152 * values are '1.0', and '1.1' and should be a string. Version 1.1 is not 153 * supported, because of chunk response. The 'user-agent' option is the 154 * user-agent and is used to replace the default user-agent, which is 155 * 'WordPress/WP_Version', where WP_Version is the value from $wp_version. 156 * 157 * 'blocking' is the default, which is used to tell the transport, whether 158 * it should halt PHP while it performs the request or continue regardless. 159 * Actually, that isn't entirely correct. Blocking mode really just means 160 * whether the fread should just pull what it can whenever it gets bytes or 161 * if it should wait until it has enough in the buffer to read or finishes 162 * reading the entire content. It doesn't actually always mean that PHP will 163 * continue going after making the request. 164 * 103 165 * @access public 104 166 * @since 2.7 105 167 * 106 * @param string $url 168 * @param string $url URI resource. 169 * @param str|array $args Optional. Override the defaults. 107 170 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized. 108 171 * @param string $body Optional. The body that should be sent. Expected to be already processed. 109 * @param str|array $type Optional. Should be an already processed array with HTTP arguments.110 172 * @return boolean 111 173 */ 112 174 function request($url, $args = array(), $headers = null, $body = null) { … … 114 176 115 177 $defaults = array( 116 178 'method' => 'GET', 'timeout' => 3, 117 'redirection' => 5, 'redirected' => false, 118 'httpversion' => '1.0', 'blocking' => true 179 'redirection' => 5, 'httpversion' => '1.0', 180 'user-agent' => apply_filters('http_headers_useragent', 'WordPress/' . $wp_version ), 181 'blocking' => true 119 182 ); 120 183 121 184 $r = wp_parse_args( $args, $defaults ); … … 128 191 } 129 192 130 193 if ( ! isset($headers['user-agent']) || ! isset($headers['User-Agent']) ) 131 $headers['user-agent'] = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version );194 $headers['user-agent'] = $r['user-agent']; 132 195 133 if ( is_null($body) )196 if ( is_null($body) || count($headers) > 1 ) 134 197 $transport = WP_Http::_getTransport(); 135 198 else 136 199 $transport = WP_Http::_postTransport(); … … 146 209 * @access public 147 210 * @since 2.7 148 211 * 149 * @param string $url The location of the site and page to retrieve. 212 * @param string $url URI resource. 213 * @param str|array $args Optional. Override the defaults. 150 214 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. 151 215 * @param string $body Optional. The body that should be sent. Expected to be already processed. 152 216 * @return boolean … … 165 229 * @access public 166 230 * @since 2.7 167 231 * 232 * @param string $url URI resource. 233 * @param str|array $args Optional. Override the defaults. 168 234 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. 169 235 * @param string $body Optional. The body that should be sent. Expected to be already processed. 170 236 * @return boolean … … 183 249 * @access public 184 250 * @since 2.7 185 251 * 252 * @param string $url URI resource. 253 * @param str|array $args Optional. Override the defaults. 186 254 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. 187 255 * @param string $body Optional. The body that should be sent. Expected to be already processed. 188 256 * @return boolean … … 227 295 /** 228 296 * Whether the headers returned a redirect location. 229 297 * 298 * Actually just checks whether the location header exists. 299 * 230 300 * @access public 231 301 * @static 232 302 * @since 2.7 … … 243 313 /** 244 314 * Transform header string into an array. 245 315 * 246 * Will overwrite the last header value, if it is not empty. 316 * If an array is given, then it will be immediately passed through with no 317 * changes. This is to prevent overhead in processing headers that don't 318 * need to be processed. That and it is unknown what kind of effect 319 * processing the array will have since there is no checking done on whether 320 * ':' does not exist within the array string. 247 321 * 322 * Checking could be added, but it is easier and faster to just passed the 323 * array through and assume that it has already been processed. 324 * 248 325 * @access public 249 326 * @static 250 327 * @since 2.7 251 328 * 252 329 * @param string|array $headers 253 * @return array 330 * @return array Processed string headers 254 331 */ 255 332 function processHeaders($headers) { 256 333 if ( is_array($headers) ) 257 334 return $headers; 258 335 259 $headers = explode("\n", str_replace(array("\r "), '', $headers) );336 $headers = explode("\n", str_replace(array("\r\n", "\r"), "\n", $headers) ); 260 337 261 338 $response = array('code' => 0, 'message' => ''); 262 339 … … 285 362 /** 286 363 * HTTP request method uses fsockopen function to retrieve the url. 287 364 * 288 * Preferred method since it works with all WordPress supported PHP versions. 365 * This would be the preferred method, but the fsockopen implementation has the 366 * most overhead of all the HTTP transport implementations. 289 367 * 290 368 * @package WordPress 291 369 * @subpackage HTTP … … 293 371 */ 294 372 class WP_Http_Fsockopen { 295 373 /** 296 * Retrieve the location and set the class properties after the site has been retrieved.374 * Send a HTTP request to a URI using fsockopen(). 297 375 * 376 * Does not support non-blocking mode. 377 * 378 * @see WP_Http::retrieve For default options descriptions. 379 * 298 380 * @since 2.7 299 381 * @access public 300 * @param string $url 382 * @param string $url URI resource. 383 * @param str|array $args Optional. Override the defaults. 301 384 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized. 302 385 * @param string $body Optional. The body that should be sent. Expected to be already processed. 303 * @param str|array $type Optional. Should be an already processed array with HTTP arguments. 304 * @return boolean 386 * @return array 'headers', 'body', and 'response' keys. 305 387 */ 306 388 function request($url, $args = array(), $headers = null, $body = null) { 307 389 $defaults = array( … … 322 404 if ( ! isset($arrURL['port']) ) { 323 405 if ( ($arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https') && extension_loaded('openssl') ) { 324 406 $arrURL['host'] = 'ssl://' . $arrURL['host']; 325 $arrURL['port'] = apply_filters('http_request_ default_port', 443);407 $arrURL['port'] = apply_filters('http_request_port', 443); 326 408 $secure_transport = true; 327 409 } else { 328 410 $arrURL['port'] = apply_filters('http_request_default_port', 80); … … 331 413 $arrURL['port'] = apply_filters('http_request_port', $arrURL['port']); 332 414 } 333 415 416 // There are issues with the HTTPS and SSL protocols that cause errors 417 // that can be safely ignored and should be ignored. 334 418 if ( true === $secure_transport ) 335 419 $error_reporting = error_reporting(0); 336 420 … … 360 444 361 445 fwrite($handle, $strHeaders); 362 446 363 if ( ! $r['blocking'] ) 364 return array( 'headers' => array(), 'body' => '', 'response' => array() ); 447 if ( ! $r['blocking'] ) { 448 fclose($handle); 449 return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') ); 450 } 365 451 366 452 $strResponse = ''; 367 453 while ( ! feof($handle) ) … … 410 496 * for $context support, but should still be okay, to write the headers, before 411 497 * getting the response. Also requires that 'allow_url_fopen' to be enabled. 412 498 * 413 * Second preferred method for handling the retrieving the url for PHP 4.3+.414 *415 499 * @package WordPress 416 500 * @subpackage HTTP 417 501 * @since 2.7 418 502 */ 419 503 class WP_Http_Fopen { 420 504 /** 421 * Retrieve the location and set the class properties after the site has been retrieved.505 * Send a HTTP request to a URI using fopen(). 422 506 * 507 * This transport does not support sending of headers and body, therefore 508 * should not be used in the instances, where there is a body and headers. 509 * 510 * Notes: Does not support non-blocking mode. Ignores 'redirection' option. 511 * 512 * @see WP_Http::retrieve For default options descriptions. 513 * 423 514 * @access public 424 515 * @since 2.7 425 516 * 426 * @param string $url 517 * @param string $url URI resource. 518 * @param str|array $args Optional. Override the defaults. 427 519 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized. 428 520 * @param string $body Optional. The body that should be sent. Expected to be already processed. 429 * @param str|array $type Optional. Should be an already processed array with HTTP arguments. 430 * @return boolean 521 * @return array 'headers', 'body', and 'response' keys. 431 522 */ 432 523 function request($url, $args = array(), $headers = null, $body = null) { 433 524 global $http_response_header; … … 453 544 if ( function_exists('stream_set_timeout') ) 454 545 stream_set_timeout($handle, apply_filters('http_request_timeout', $r['timeout']) ); 455 546 456 if ( ! $r['blocking'] ) 457 return array( 'headers' => array(), 'body' => '', 'response' => array() ); 547 if ( ! $r['blocking'] ) { 548 fclose($handle); 549 return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') ); 550 } 458 551 459 552 $strResponse = ''; 460 553 while ( ! feof($handle) ) 461 554 $strResponse .= fread($handle, 4096); 462 555 556 fclose($handle); 557 463 558 $theHeaders = ''; 464 559 if ( function_exists('stream_get_meta_data') ) { 465 560 $meta = stream_get_meta_data($handle); … … 467 562 } else { 468 563 $theHeaders = $http_response_header; 469 564 } 470 fclose($handle);565 $processedHeaders = WP_Http::processHeaders($theHeaders); 471 566 472 $processedHeaders = WP_Http::processHeaders($theHeaders);473 567 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']); 474 568 } 475 569 … … 501 595 */ 502 596 class WP_Http_Streams { 503 597 /** 504 * Retrieve the location and set the class properties after the site has been retrieved.598 * Send a HTTP request to a URI using streams with fopen(). 505 599 * 506 600 * @access public 507 601 * @since 2.7 … … 510 604 * @param str|array $args Optional. Override the defaults. 511 605 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized. 512 606 * @param string $body Optional. The body that should be sent. Expected to be already processed. 513 * @return boolean607 * @return array 'headers', 'body', and 'response' keys. 514 608 */ 515 609 function request($url, $args = array(), $headers = null, $body = null) { 516 610 $defaults = array( … … 548 642 if ( ! $handle) 549 643 return new WP_Error('http_request_failed', sprintf(__('Could not open handle for fopen() to %s'), $url)); 550 644 551 if ( ! $r['blocking'] ) 552 return array( 'headers' => array(), 'body' => '', 'response' => array() ); 645 if ( ! $r['blocking'] ) { 646 fclose($handle); 647 return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') ); 648 } 553 649 554 650 $strResponse = stream_get_contents($handle); 555 651 $meta = stream_get_meta_data($handle); 652 $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']); 556 653 557 654 fclose($handle); 558 655 559 $processedHeaders = WP_Http::processHeaders($meta['wrapper_data']);560 656 return array('headers' => $processedHeaders['headers'], 'body' => $strResponse, 'response' => $processedHeaders['response']); 561 657 } 562 658 … … 583 679 /** 584 680 * HTTP request method uses HTTP extension to retrieve the url. 585 681 * 586 * Requires the HTTP extension to be installed. 682 * Requires the HTTP extension to be installed. This would be the preferred 683 * transport since it can handle a lot of the problems that forces the others to 684 * use the HTTP version 1.0. Even if PHP 5.2+ is being used, it doesn't mean 685 * that the HTTP extension will be enabled. 587 686 * 588 * Last ditch effort to retrieve the URL before complete failure.589 * Does not support non-blocking requests.590 *591 687 * @package WordPress 592 688 * @subpackage HTTP 593 689 * @since 2.7 594 690 */ 595 691 class WP_Http_ExtHTTP { 596 692 /** 597 * Retrieve the location and set the class properties after the site has been retrieved.693 * Send a HTTP request to a URI using HTTP extension. 598 694 * 695 * Does not support non-blocking. 696 * 599 697 * @access public 600 698 * @since 2.7 601 699 * … … 603 701 * @param str|array $args Optional. Override the defaults. 604 702 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized. 605 703 * @param string $body Optional. The body that should be sent. Expected to be already processed. 606 * @return boolean704 * @return array 'headers', 'body', and 'response' keys. 607 705 */ 608 706 function request($url, $args = array(), $headers = null, $body = null) { 609 707 global $wp_version; … … 611 709 $defaults = array( 612 710 'method' => 'GET', 'timeout' => 3, 613 711 'redirection' => 5, 'httpversion' => '1.0', 614 'blocking' => true , 'user_agent' => apply_filters('http_headers_useragent', 'WordPress/' . $wp_version)712 'blocking' => true 615 713 ); 616 714 617 715 $r = wp_parse_args( $args, $defaults ); 618 716 619 if ( isset($headers['User-Agent']) ) 717 if ( isset($headers['User-Agent']) ) { 718 $r['user-agent'] = $headers['User-Agent']; 620 719 unset($headers['User-Agent']); 720 } else if( isset($headers['user-agent']) ) { 721 $r['user-agent'] = $headers['user-agent']; 722 unset($headers['user-agent']); 723 } else { 724 $r['user-agent'] = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version ); 725 } 621 726 622 727 switch ( $r['method'] ) { 623 728 case 'GET': … … 639 744 $url = str_replace($arrURL['scheme'], 'http', $url); 640 745 641 746 $options = array( 642 'timeout' => $ this->timeout,643 'connecttimeout' => apply_filters('http_request_stream_timeout', $this->timeout),644 'redirect' => apply_filters('http_request_redirect', 3),747 'timeout' => $r['timeout'], 748 'connecttimeout' => $r['timeout'], 749 'redirect' => $r['redirection'], 645 750 'useragent' => $r['user_agent'], 646 751 'headers' => $headers, 647 752 ); … … 651 756 if ( false === $strResponse ) 652 757 return new WP_Error('http_request_failed', $info['response_code'] . ': ' . $info['error']); 653 758 759 if ( ! $r['blocking'] ) 760 return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') ); 761 654 762 list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2); 655 763 $theHeaders = WP_Http::processHeaders($theHeaders); 656 764 … … 678 786 } 679 787 680 788 /** 789 * HTTP request method uses Curl extension to retrieve the url. 790 * 791 * Requires the Curl extension to be installed. 792 * 793 * @package WordPress 794 * @subpackage HTTP 795 * @since 2.7 796 */ 797 class WP_Http_Curl { 798 /** 799 * Send a HTTP request to a URI using cURL extension. 800 * 801 * @access public 802 * @since 2.7 803 * 804 * @param string $url 805 * @param str|array $args Optional. Override the defaults. 806 * @param string|array $headers Optional. Either the header string or array of Header name and value pairs. Expects sanitized. 807 * @param string $body Optional. The body that should be sent. Expected to be already processed. 808 * @return array 'headers', 'body', and 'response' keys. 809 */ 810 function request($url, $args = array(), $headers = null, $body = null) { 811 global $wp_version; 812 813 $defaults = array( 814 'method' => 'GET', 'timeout' => 3, 815 'redirection' => 5, 'httpversion' => '1.0', 816 'blocking' => true 817 ); 818 819 $r = wp_parse_args( $args, $defaults ); 820 821 if ( isset($headers['User-Agent']) ) { 822 $r['user-agent'] = $headers['User-Agent']; 823 unset($headers['User-Agent']); 824 } else if( isset($headers['user-agent']) ) { 825 $r['user-agent'] = $headers['user-agent']; 826 unset($headers['user-agent']); 827 } else { 828 $r['user-agent'] = apply_filters('http_headers_useragent', 'WordPress/' . $wp_version ); 829 } 830 831 $handle = curl_init(); 832 curl_setopt( $handle, CURLOPT_URL, $url); 833 834 if ( true === $r['blocking'] ) { 835 curl_setopt( $handle, CURLOPT_HEADER, true ); 836 } else { 837 curl_setopt( $handle, CURLOPT_HEADER, false ); 838 curl_setopt( $handle, CURLOPT_NOBODY, true ); 839 } 840 841 curl_setopt( $handle, CURLOPT_RETURNTRANSFER, 1 ); 842 curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] ); 843 curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, 1 ); 844 curl_setopt( $handle, CURLOPT_TIMEOUT, $r['timeout'] ); 845 curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true ); 846 curl_setopt( $handle, CURLOPT_MAXREDIRS, $r['redirection'] ); 847 848 if( ! is_null($headers) ) 849 curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers ); 850 851 if ( $r['httpversion'] == '1.0' ) 852 curl_setopt( $headle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); 853 else 854 curl_setopt( $headle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 ); 855 856 if ( ! $r['blocking'] ) { 857 curl_close( $handle ); 858 return array( 'headers' => array(), 'body' => '', 'response' => array('code', 'message') ); 859 } 860 861 $theResponse = curl_exec( $handle ); 862 863 list($theHeaders, $theBody) = explode("\r\n\r\n", $strResponse, 2); 864 $theHeaders = WP_Http::processHeaders($theHeaders); 865 866 $response = array(); 867 $response['code'] = curl_getinfo( $handle, CURLINFO_HTTP_CODE ); 868 $response['message'] = get_status_header_desc($response['code']); 869 870 curl_close( $handle ); 871 872 return array('headers' => $theHeaders['headers'], 'body' => $theBody, 'response' => $response); 873 } 874 875 /** 876 * Whether this class can be used for retrieving an URL. 877 * 878 * @static 879 * @since 2.7 880 * 881 * @return boolean False means this class can not be used, true means it can. 882 */ 883 function test() { 884 if ( function_exists('curl_init') ) 885 return true; 886 887 return false; 888 } 889 } 890 891 /** 681 892 * Returns the initialized WP_Http Object 682 893 * 683 894 * @since 2.7