2008-08-01 01:00:07 -04:00
< ? php
/**
2008-08-01 14:44:40 -04:00
* Simple and uniform HTTP request API .
*
* Will eventually replace and standardize the WordPress HTTP requests made .
2008-08-01 01:00:07 -04:00
*
2008-08-05 13:01:49 -04:00
* @ link http :// trac . wordpress . org / ticket / 4779 HTTP API Proposal
*
2008-08-01 01:00:07 -04:00
* @ package WordPress
* @ subpackage HTTP
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ author Jacob Santos < wordpress @ santosj . name >
*/
2009-01-22 16:16:11 -05:00
/**
* Implementation for deflate and gzip transfer encodings .
*
* Includes RFC 1950 , RFC 1951 , and RFC 1952.
*
* @ since unknown
* @ package WordPress
* @ subpackage HTTP
*/
class WP_Http_Encoding {
/**
* Compress raw string using the deflate format .
*
* Supports the RFC 1951 standard .
*
* @ since unknown
*
* @ param string $raw String to compress .
* @ param int $level Optional , default is 9. Compression level , 9 is highest .
* @ param string $supports Optional , not used . When implemented it will choose the right compression based on what the server supports .
* @ return string | bool False on failure .
*/
function compress ( $raw , $level = 9 , $supports = null ) {
return gzdeflate ( $raw , $level );
}
/**
* Decompression of deflated string .
*
* Will attempt to decompress using the RFC 1950 standard , and if that fails
* then the RFC 1951 standard deflate will be attempted . Finally , the RFC
* 1952 standard gzip decode will be attempted . If all fail , then the
* original compressed string will be returned .
*
* @ since unknown
*
* @ param string $compressed String to decompress .
* @ param int $length The optional length of the compressed data .
* @ return string | bool False on failure .
*/
function decompress ( $compressed , $length = null ) {
$decompressed = gzinflate ( $compressed );
if ( false !== $decompressed )
return $decompressed ;
$decompressed = gzuncompress ( $compressed );
if ( false !== $decompressed )
return $decompressed ;
$decompressed = gzdecode ( $compressed );
if ( false !== $decompressed )
return $decompressed ;
return $compressed ;
}
/**
* What encoding types to accept and their priority values .
*
* @ since unknown
*
* @ return string Types of encoding to accept .
*/
function accept_encoding () {
$type = array ();
if ( function_exists ( 'gzinflate' ) )
$type [] = 'deflate;q=1.0' ;
if ( function_exists ( 'gzuncompress' ) )
$type [] = 'compress;q=0.5' ;
if ( function_exists ( 'gzdecode' ) )
$type [] = 'gzip;q=0.5' ;
return implode ( ', ' , $type );
}
/**
* What enconding the content used when it was compressed to send in the headers .
*
* @ since unknown
*
* @ return string Content - Encoding string to send in the header .
*/
function content_encoding () {
return 'deflate' ;
}
/**
* Whether the content be decoded based on the headers .
*
* @ since unknown
*
* @ param array | string $headers All of the available headers .
* @ return bool
*/
function should_decode ( $headers ) {
if ( is_array ( $headers ) ) {
if ( array_key_exists ( 'content-encoding' , $headers ) && ! empty ( $headers [ 'content-encoding' ] ) )
return true ;
} else if ( is_string ( $headers ) ) {
return ( stripos ( $headers , 'content-encoding:' ) !== false );
}
return false ;
}
/**
* Whether decompression and compression are supported by the PHP version .
*
* Each function is tested instead of checking for the zlib extension , to
* ensure that the functions all exist in the PHP version and aren ' t
* disabled .
*
* @ since unknown
*
* @ return bool
*/
function is_available () {
return ( function_exists ( 'gzuncompress' ) || function_exists ( 'gzdeflate' ) ||
function_exists ( 'gzinflate' ) );
}
}
2008-08-01 01:00:07 -04:00
/**
2008-08-01 14:44:40 -04:00
* WordPress HTTP Class for managing HTTP Transports and making HTTP requests .
*
* This class is called for the functionality of making HTTP requests and should
* replace Snoopy functionality , eventually . There is no available functionality
* to add HTTP transport implementations , since most of the HTTP transports are
* added and available for use .
*
* The exception is that cURL is not available as a transport and lacking an
* implementation . It will be added later and should be a patch on the WordPress
* Trac .
*
* There are no properties , because none are needed and for performance reasons .
* Some of the functions are static and while they do have some overhead over
* functions in PHP4 , the purpose is maintainability . When PHP5 is finally the
* requirement , it will be easy to add the static keyword to the code . It is not
* as easy to convert a function to a method after enough code uses the old way .
2008-08-01 01:00:07 -04:00
*
2008-12-30 17:45:51 -05:00
* Debugging includes several actions , which pass different variables for
* debugging the HTTP API .
*
* < strong > http_transport_get_debug </ strong > - gives working , nonblocking , and
* blocking transports .
*
* < strong > http_transport_post_debug </ strong > - gives working , nonblocking , and
* blocking transports .
*
2008-08-01 01:00:07 -04:00
* @ package WordPress
* @ subpackage HTTP
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*/
2008-08-01 01:45:13 -04:00
class WP_Http {
2008-08-01 01:00:07 -04:00
/**
* PHP4 style Constructor - Calls PHP5 Style Constructor
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ return WP_Http
*/
2008-08-01 01:45:13 -04:00
function WP_Http () {
2008-08-01 01:00:07 -04:00
$this -> __construct ();
}
/**
* PHP5 style Constructor - Setup available transport if not available .
*
2008-08-01 14:44:40 -04:00
* PHP4 does not have the 'self' keyword and since WordPress supports PHP4 ,
* the class needs to be used for the static call .
*
* The transport are setup to save time . This should only be called once , so
* the overhead should be fine .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ return WP_Http
*/
2008-08-01 01:45:13 -04:00
function __construct () {
2008-08-01 01:00:07 -04:00
WP_Http :: _getTransport ();
2008-08-01 14:44:40 -04:00
WP_Http :: _postTransport ();
2008-08-01 01:00:07 -04:00
}
/**
* Tests the WordPress HTTP objects for an object to use and returns it .
*
* Tests all of the objects and returns the object that passes . Also caches
* that object to be used later .
*
2008-08-01 14:44:40 -04:00
* The order for the GET / HEAD requests are Streams , HTTP Extension , Fopen ,
* and finally Fsockopen . fsockopen () is used last , because it has the most
* overhead in its implementation . There isn ' t any real way around it , since
* redirects have to be supported , much the same way the other transports
* also handle redirects .
*
* There are currently issues with " localhost " not resolving correctly with
* DNS . This may cause an error " failed to open stream: A connection attempt
* failed because the connected party did not properly respond after a
* period of time , or established connection failed because connected host
* has failed to respond . "
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ access private
*
2008-11-08 17:35:00 -05:00
* @ param array $args Request args , default us an empty array
2008-08-01 01:00:07 -04:00
* @ return object | null Null if no transports are available , HTTP transport object .
*/
2008-11-08 17:35:00 -05:00
function & _getTransport ( $args = array () ) {
static $working_transport , $blocking_transport , $nonblocking_transport ;
2008-08-01 01:00:07 -04:00
2008-08-01 01:45:13 -04:00
if ( is_null ( $working_transport ) ) {
2008-11-08 17:35:00 -05:00
if ( true === WP_Http_ExtHttp :: test () && apply_filters ( 'use_http_extension_transport' , true ) ) {
$working_transport [ 'exthttp' ] = new WP_Http_ExtHttp ();
$blocking_transport [] = & $working_transport [ 'exthttp' ];
} else if ( true === WP_Http_Curl :: test () && apply_filters ( 'use_curl_transport' , true ) ) {
$working_transport [ 'curl' ] = new WP_Http_Curl ();
$blocking_transport [] = & $working_transport [ 'curl' ];
} else if ( true === WP_Http_Streams :: test () && apply_filters ( 'use_streams_transport' , true ) ) {
$working_transport [ 'streams' ] = new WP_Http_Streams ();
$blocking_transport [] = & $working_transport [ 'streams' ];
} else if ( true === WP_Http_Fopen :: test () && apply_filters ( 'use_fopen_transport' , true ) ) {
$working_transport [ 'fopen' ] = new WP_Http_Fopen ();
$blocking_transport [] = & $working_transport [ 'fopen' ];
} else if ( true === WP_Http_Fsockopen :: test () && apply_filters ( 'use_fsockopen_transport' , true ) ) {
$working_transport [ 'fsockopen' ] = new WP_Http_Fsockopen ();
$blocking_transport [] = & $working_transport [ 'fsockopen' ];
}
foreach ( array ( 'curl' , 'streams' , 'fopen' , 'fsockopen' , 'exthttp' ) as $transport ) {
if ( isset ( $working_transport [ $transport ]) )
$nonblocking_transport [] = & $working_transport [ $transport ];
}
2008-08-01 01:00:07 -04:00
}
2008-12-30 17:45:51 -05:00
if ( has_filter ( 'http_transport_get_debug' ) )
do_action ( 'http_transport_get_debug' , $working_transport , $blocking_transport , $nonblocking_transport );
2008-11-08 17:35:00 -05:00
if ( isset ( $args [ 'blocking' ]) && ! $args [ 'blocking' ] )
return $nonblocking_transport ;
else
return $blocking_transport ;
2008-08-01 01:00:07 -04:00
}
/**
* Tests the WordPress HTTP objects for an object to use and returns it .
*
* Tests all of the objects and returns the object that passes . Also caches
* that object to be used later . This is for posting content to a URL and
* is used when there is a body . The plain Fopen Transport can not be used
* to send content , but the streams transport can . This is a limitation that
2008-08-01 14:44:40 -04:00
* is addressed here , by just not including that transport .
2008-08-01 01:00:07 -04:00
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ access private
*
2008-11-08 17:35:00 -05:00
* @ param array $args Request args , default us an empty array
2008-08-01 01:00:07 -04:00
* @ return object | null Null if no transports are available , HTTP transport object .
*/
2008-11-08 17:35:00 -05:00
function & _postTransport ( $args = array () ) {
static $working_transport , $blocking_transport , $nonblocking_transport ;
2008-08-01 01:00:07 -04:00
2008-08-01 01:45:13 -04:00
if ( is_null ( $working_transport ) ) {
2008-11-08 17:35:00 -05:00
if ( true === WP_Http_ExtHttp :: test () && apply_filters ( 'use_http_extension_transport' , true ) ) {
2008-11-08 18:13:33 -05:00
$working_transport [ 'exthttp' ] = new WP_Http_ExtHttp ();
2008-11-08 17:35:00 -05:00
$blocking_transport [] = & $working_transport [ 'exthttp' ];
} else if ( true === WP_Http_Streams :: test () && apply_filters ( 'use_streams_transport' , true ) ) {
2008-11-08 18:13:33 -05:00
$working_transport [ 'streams' ] = new WP_Http_Streams ();
2008-11-08 17:35:00 -05:00
$blocking_transport [] = & $working_transport [ 'streams' ];
} else if ( true === WP_Http_Fsockopen :: test () && apply_filters ( 'use_fsockopen_transport' , true ) ) {
2008-11-08 18:13:33 -05:00
$working_transport [ 'fsockopen' ] = new WP_Http_Fsockopen ();
2008-11-08 17:35:00 -05:00
$blocking_transport [] = & $working_transport [ 'fsockopen' ];
}
foreach ( array ( 'streams' , 'fsockopen' , 'exthttp' ) as $transport ) {
if ( isset ( $working_transport [ $transport ]) )
$nonblocking_transport [] = & $working_transport [ $transport ];
2008-12-09 13:03:31 -05:00
}
2008-08-01 01:00:07 -04:00
}
2008-12-30 17:45:51 -05:00
if ( has_filter ( 'http_transport_post_debug' ) )
do_action ( 'http_transport_post_debug' , $working_transport , $blocking_transport , $nonblocking_transport );
2008-11-08 17:35:00 -05:00
if ( isset ( $args [ 'blocking' ]) && ! $args [ 'blocking' ] )
return $nonblocking_transport ;
else
return $blocking_transport ;
2008-08-01 01:00:07 -04:00
}
/**
2008-08-01 14:44:40 -04:00
* Send a HTTP request to a URI .
*
2008-08-12 17:21:11 -04:00
* The body and headers are part of the arguments . The 'body' argument is
* for the body and will accept either a string or an array . The 'headers'
* argument should be an array , but a string is acceptable . If the 'body'
* argument is an array , then it will automatically be escaped using
* http_build_query () .
*
2008-08-01 14:44:40 -04:00
* The only URI that are supported in the HTTP Transport implementation are
* the HTTP and HTTPS protocols . HTTP and HTTPS are assumed so the server
* might not know how to handle the send headers . Other protocols are
* unsupported and most likely will fail .
*
* The defaults are 'method' , 'timeout' , 'redirection' , 'httpversion' ,
* 'blocking' and 'user-agent' .
*
* Accepted 'method' values are 'GET' , 'POST' , and 'HEAD' , some transports
* technically allow others , but should not be assumed . The 'timeout' is
* used to sent how long the connection should stay open before failing when
* no response . 'redirection' is used to track how many redirects were taken
* and used to sent the amount for other transports , but not all transports
* accept setting that value .
*
* The 'httpversion' option is used to sent the HTTP version and accepted
* values are '1.0' , and '1.1' and should be a string . Version 1.1 is not
* supported , because of chunk response . The 'user-agent' option is the
* user - agent and is used to replace the default user - agent , which is
* 'WordPress/WP_Version' , where WP_Version is the value from $wp_version .
*
* 'blocking' is the default , which is used to tell the transport , whether
* it should halt PHP while it performs the request or continue regardless .
* Actually , that isn ' t entirely correct . Blocking mode really just means
* whether the fread should just pull what it can whenever it gets bytes or
* if it should wait until it has enough in the buffer to read or finishes
* reading the entire content . It doesn ' t actually always mean that PHP will
* continue going after making the request .
2008-08-01 01:00:07 -04:00
*
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
2008-08-01 14:44:40 -04:00
* @ param string $url URI resource .
* @ param str | array $args Optional . Override the defaults .
2008-08-01 01:00:07 -04:00
* @ return boolean
*/
2008-08-12 17:21:11 -04:00
function request ( $url , $args = array () ) {
2008-08-01 01:00:07 -04:00
global $wp_version ;
$defaults = array (
2008-10-15 16:36:23 -04:00
'method' => 'GET' ,
'timeout' => apply_filters ( 'http_request_timeout' , 5 ),
'redirection' => apply_filters ( 'http_request_redirection_count' , 5 ),
'httpversion' => apply_filters ( 'http_request_version' , '1.0' ),
'user-agent' => apply_filters ( 'http_headers_useragent' , 'WordPress/' . $wp_version ),
2008-08-12 17:21:11 -04:00
'blocking' => true ,
2009-01-22 16:16:11 -05:00
'headers' => array (),
'body' => null ,
'compress' => false ,
'decompress' => true
2008-08-01 01:00:07 -04:00
);
$r = wp_parse_args ( $args , $defaults );
2009-01-03 23:34:01 -05:00
$r = apply_filters ( 'http_request_args' , $r , $url );
2008-12-09 13:03:31 -05:00
2008-08-12 17:21:11 -04:00
if ( is_null ( $r [ 'headers' ] ) )
$r [ 'headers' ] = array ();
2008-08-05 00:14:55 -04:00
2008-08-12 17:21:11 -04:00
if ( ! is_array ( $r [ 'headers' ]) ) {
$processedHeaders = WP_Http :: processHeaders ( $r [ 'headers' ]);
$r [ 'headers' ] = $processedHeaders [ 'headers' ];
2008-08-01 01:00:07 -04:00
}
2008-08-12 17:21:11 -04:00
if ( isset ( $r [ 'headers' ][ 'User-Agent' ]) ) {
2008-08-16 01:38:57 -04:00
$r [ 'user-agent' ] = $r [ 'headers' ][ 'User-Agent' ];
2008-08-12 17:21:11 -04:00
unset ( $r [ 'headers' ][ 'User-Agent' ]);
2008-08-07 20:02:11 -04:00
}
2008-08-12 17:21:11 -04:00
if ( isset ( $r [ 'headers' ][ 'user-agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'user-agent' ];
unset ( $r [ 'headers' ][ 'user-agent' ]);
}
2008-08-01 01:00:07 -04:00
2009-01-22 16:16:11 -05:00
if ( WP_Http_Encoding :: is_available () )
$r [ 'headers' ][ 'Accept-Encoding' ] = WP_Http_Encoding :: accept_encoding ();
2008-08-12 17:21:11 -04:00
if ( is_null ( $r [ 'body' ]) ) {
2009-01-06 12:29:24 -05:00
// Some servers fail when sending content without the content-length
// header being set.
2008-12-16 17:07:18 -05:00
$r [ 'headers' ][ 'Content-Length' ] = 0 ;
2008-11-08 17:35:00 -05:00
$transports = WP_Http :: _getTransport ( $r );
2008-08-05 00:14:55 -04:00
} else {
2008-08-12 17:21:11 -04:00
if ( is_array ( $r [ 'body' ] ) || is_object ( $r [ 'body' ] ) ) {
2008-08-14 18:54:43 -04:00
$r [ 'body' ] = http_build_query ( $r [ 'body' ], null , '&' );
2008-08-12 17:21:11 -04:00
$r [ 'headers' ][ 'Content-Type' ] = 'application/x-www-form-urlencoded; charset=' . get_option ( 'blog_charset' );
$r [ 'headers' ][ 'Content-Length' ] = strlen ( $r [ 'body' ]);
}
2008-08-04 12:28:12 -04:00
2008-11-17 16:21:35 -05:00
if ( ! isset ( $r [ 'headers' ][ 'Content-Length' ] ) && ! isset ( $r [ 'headers' ][ 'content-length' ] ) )
$r [ 'headers' ][ 'Content-Length' ] = strlen ( $r [ 'body' ]);
2008-11-08 17:35:00 -05:00
$transports = WP_Http :: _postTransport ( $r );
2008-08-05 00:14:55 -04:00
}
2008-08-01 01:00:07 -04:00
2008-12-30 17:45:51 -05:00
if ( has_action ( 'http_api_debug' ) )
do_action ( 'http_api_debug' , $transports , 'transports_list' );
2008-08-04 12:28:12 -04:00
$response = array ( 'headers' => array (), 'body' => '' , 'response' => array ( 'code' , 'message' ) );
foreach ( ( array ) $transports as $transport ) {
2008-08-12 17:21:11 -04:00
$response = $transport -> request ( $url , $r );
2008-08-04 12:28:12 -04:00
2008-12-30 17:45:51 -05:00
if ( has_action ( 'http_api_debug' ) )
do_action ( 'http_api_debug' , $response , 'response' , get_class ( $transport ) );
if ( ! is_wp_error ( $response ) )
2008-08-04 14:45:44 -04:00
return $response ;
2008-08-04 12:28:12 -04:00
}
return $response ;
2008-08-01 01:00:07 -04:00
}
/**
* Uses the POST HTTP method .
2008-08-09 01:36:14 -04:00
*
2008-08-01 01:00:07 -04:00
* Used for sending data that is expected to be in the body .
*
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
2008-08-01 14:44:40 -04:00
* @ param string $url URI resource .
* @ param str | array $args Optional . Override the defaults .
2008-08-01 01:00:07 -04:00
* @ return boolean
*/
2008-08-12 17:21:11 -04:00
function post ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$defaults = array ( 'method' => 'POST' );
$r = wp_parse_args ( $args , $defaults );
2008-08-12 17:21:11 -04:00
return $this -> request ( $url , $r );
2008-08-01 01:00:07 -04:00
}
/**
2008-08-09 01:36:14 -04:00
* Uses the GET HTTP method .
2008-08-01 01:00:07 -04:00
*
* Used for sending data that is expected to be in the body .
*
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
2008-08-01 14:44:40 -04:00
* @ param string $url URI resource .
* @ param str | array $args Optional . Override the defaults .
2008-08-01 01:00:07 -04:00
* @ return boolean
*/
2008-08-12 17:21:11 -04:00
function get ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$defaults = array ( 'method' => 'GET' );
$r = wp_parse_args ( $args , $defaults );
2008-08-12 17:21:11 -04:00
return $this -> request ( $url , $r );
2008-08-01 01:00:07 -04:00
}
/**
2008-08-09 01:36:14 -04:00
* Uses the HEAD HTTP method .
2008-08-01 01:00:07 -04:00
*
* Used for sending data that is expected to be in the body .
*
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
2008-08-01 14:44:40 -04:00
* @ param string $url URI resource .
* @ param str | array $args Optional . Override the defaults .
2008-08-01 01:00:07 -04:00
* @ return boolean
*/
2008-08-12 17:21:11 -04:00
function head ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$defaults = array ( 'method' => 'HEAD' );
$r = wp_parse_args ( $args , $defaults );
2008-08-12 17:21:11 -04:00
return $this -> request ( $url , $r );
2008-08-01 01:00:07 -04:00
}
/**
* Parses the responses and splits the parts into headers and body .
*
* @ access public
* @ static
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param string $strResponse The full response string
* @ return array Array with 'headers' and 'body' keys .
*/
2008-08-01 01:45:13 -04:00
function processResponse ( $strResponse ) {
2008-08-01 01:00:07 -04:00
list ( $theHeaders , $theBody ) = explode ( " \r \n \r \n " , $strResponse , 2 );
return array ( 'headers' => $theHeaders , 'body' => $theBody );
}
/**
* Transform header string into an array .
*
2008-08-01 18:31:57 -04:00
* If an array is given then it is assumed to be raw header data with
* numeric keys with the headers as the values . No headers must be passed
* that were already processed .
2008-08-01 01:00:07 -04:00
*
* @ access public
* @ static
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param string | array $headers
2008-08-09 01:36:14 -04:00
* @ return array Processed string headers
2008-08-01 01:00:07 -04:00
*/
2008-08-01 01:45:13 -04:00
function processHeaders ( $headers ) {
2008-08-01 18:31:57 -04:00
if ( is_string ( $headers ) )
$headers = explode ( " \n " , str_replace ( array ( " \r \n " , " \r " ), " \n " , $headers ) );
2008-08-01 01:00:07 -04:00
$response = array ( 'code' => 0 , 'message' => '' );
$newheaders = array ();
2008-08-01 12:36:50 -04:00
foreach ( $headers as $tempheader ) {
2008-08-01 01:45:13 -04:00
if ( empty ( $tempheader ) )
2008-08-01 01:00:07 -04:00
continue ;
2008-08-01 01:45:13 -04:00
if ( false === strpos ( $tempheader , ':' ) ) {
list ( , $iResponseCode , $strResponseMsg ) = explode ( ' ' , $tempheader , 3 );
2008-08-01 01:00:07 -04:00
$response [ 'code' ] = $iResponseCode ;
$response [ 'message' ] = $strResponseMsg ;
continue ;
}
2008-08-01 01:45:13 -04:00
list ( $key , $value ) = explode ( ':' , $tempheader , 2 );
2008-08-01 01:00:07 -04:00
2008-08-01 01:45:13 -04:00
if ( ! empty ( $value ) )
2008-08-01 01:00:07 -04:00
$newheaders [ strtolower ( $key )] = trim ( $value );
}
return array ( 'response' => $response , 'headers' => $newheaders );
}
2008-08-12 17:21:11 -04:00
/**
* Decodes chunk transfer - encoding , based off the HTTP 1.1 specification .
*
* Based off the HTTP http_encoding_dechunk function . Does not support
* UTF - 8. Does not support returning footer headers . Shouldn ' t be too
* difficult to support it though .
*
* @ todo Add support for footer chunked headers .
2008-08-13 01:09:59 -04:00
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-12 17:21:11 -04:00
* @ static
2008-08-13 01:09:59 -04:00
*
2008-08-12 17:21:11 -04:00
* @ param string $body Body content
2008-12-30 22:02:53 -05:00
* @ return string Chunked decoded body on success or raw body on failure .
2008-08-12 17:21:11 -04:00
*/
function chunkTransferDecode ( $body ) {
$body = str_replace ( array ( " \r \n " , " \r " ), " \n " , $body );
// The body is not chunked encoding or is malformed.
if ( ! preg_match ( '/^[0-9a-f]+(\s|\n)+/mi' , trim ( $body ) ) )
2008-12-07 15:30:59 -05:00
return $body ;
2008-08-12 17:21:11 -04:00
$parsedBody = '' ;
2008-08-13 01:09:59 -04:00
//$parsedHeaders = array(); Unsupported
2008-08-12 17:21:11 -04:00
2008-12-30 22:02:53 -05:00
while ( true ) {
2008-08-12 17:21:11 -04:00
$hasChunk = ( bool ) preg_match ( '/^([0-9a-f]+)(\s|\n)+/mi' , $body , $match );
if ( $hasChunk ) {
2008-12-30 22:02:53 -05:00
if ( empty ( $match [ 1 ] ) )
2008-12-30 17:45:51 -05:00
return $body ;
2008-08-12 17:21:11 -04:00
$length = hexdec ( $match [ 1 ] );
$chunkLength = strlen ( $match [ 0 ] );
2008-11-15 13:10:35 -05:00
$strBody = substr ( $body , $chunkLength , $length );
2008-08-12 17:21:11 -04:00
$parsedBody .= $strBody ;
2008-08-13 01:09:59 -04:00
$body = ltrim ( str_replace ( array ( $match [ 0 ], $strBody ), '' , $body ), " \n " );
2008-12-30 22:02:53 -05:00
if ( " 0 " == trim ( $body ) )
2008-08-12 17:21:11 -04:00
return $parsedBody ; // Ignore footer headers.
} else {
2008-12-30 17:45:51 -05:00
return $body ;
2008-08-12 17:21:11 -04:00
}
2008-12-30 22:02:53 -05:00
}
2008-08-12 17:21:11 -04:00
}
2008-08-01 01:00:07 -04:00
}
/**
* HTTP request method uses fsockopen function to retrieve the url .
*
2008-08-01 14:44:40 -04:00
* This would be the preferred method , but the fsockopen implementation has the
* most overhead of all the HTTP transport implementations .
2008-08-01 01:00:07 -04:00
*
* @ package WordPress
* @ subpackage HTTP
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*/
2008-08-01 01:45:13 -04:00
class WP_Http_Fsockopen {
2008-08-01 01:00:07 -04:00
/**
2008-08-01 14:44:40 -04:00
* Send a HTTP request to a URI using fsockopen () .
*
* Does not support non - blocking mode .
*
2008-08-12 17:21:11 -04:00
* @ see WP_Http :: request For default options descriptions .
2008-08-01 01:00:07 -04:00
*
2008-08-01 01:45:13 -04:00
* @ since 2.7
2008-08-01 01:00:07 -04:00
* @ access public
2008-08-01 14:44:40 -04:00
* @ param string $url URI resource .
* @ param str | array $args Optional . Override the defaults .
* @ return array 'headers' , 'body' , and 'response' keys .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function request ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$defaults = array (
2008-10-15 16:36:23 -04:00
'method' => 'GET' , 'timeout' => 5 ,
2008-08-01 12:36:50 -04:00
'redirection' => 5 , 'httpversion' => '1.0' ,
2008-08-12 17:21:11 -04:00
'blocking' => true ,
'headers' => array (), 'body' => null
2008-08-01 01:00:07 -04:00
);
$r = wp_parse_args ( $args , $defaults );
2008-08-12 17:21:11 -04:00
if ( isset ( $r [ 'headers' ][ 'User-Agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'User-Agent' ];
unset ( $r [ 'headers' ][ 'User-Agent' ]);
} else if ( isset ( $r [ 'headers' ][ 'user-agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'user-agent' ];
unset ( $r [ 'headers' ][ 'user-agent' ]);
}
2008-08-01 01:00:07 -04:00
$iError = null ; // Store error number
$strError = null ; // Store error string
$arrURL = parse_url ( $url );
$secure_transport = false ;
2008-08-01 01:45:13 -04:00
if ( ! isset ( $arrURL [ 'port' ]) ) {
if ( ( $arrURL [ 'scheme' ] == 'ssl' || $arrURL [ 'scheme' ] == 'https' ) && extension_loaded ( 'openssl' ) ) {
$arrURL [ 'host' ] = 'ssl://' . $arrURL [ 'host' ];
2008-08-01 14:44:40 -04:00
$arrURL [ 'port' ] = apply_filters ( 'http_request_port' , 443 );
2008-08-01 01:00:07 -04:00
$secure_transport = true ;
2008-08-01 12:36:50 -04:00
} else {
2008-08-01 01:00:07 -04:00
$arrURL [ 'port' ] = apply_filters ( 'http_request_default_port' , 80 );
2008-08-01 12:36:50 -04:00
}
} else {
2008-08-01 01:00:07 -04:00
$arrURL [ 'port' ] = apply_filters ( 'http_request_port' , $arrURL [ 'port' ]);
2008-08-01 12:36:50 -04:00
}
2008-08-01 01:00:07 -04:00
2008-08-01 14:44:40 -04:00
// There are issues with the HTTPS and SSL protocols that cause errors
// that can be safely ignored and should be ignored.
2008-08-01 01:45:13 -04:00
if ( true === $secure_transport )
2008-08-01 01:00:07 -04:00
$error_reporting = error_reporting ( 0 );
2008-08-16 01:38:57 -04:00
$startDelay = time ();
2008-08-13 19:37:42 -04:00
if ( ! defined ( 'WP_DEBUG' ) || ( defined ( 'WP_DEBUG' ) && false === WP_DEBUG ) )
$handle = @ fsockopen ( $arrURL [ 'host' ], $arrURL [ 'port' ], $iError , $strError , $r [ 'timeout' ] );
else
$handle = fsockopen ( $arrURL [ 'host' ], $arrURL [ 'port' ], $iError , $strError , $r [ 'timeout' ] );
2008-08-01 01:00:07 -04:00
2008-08-16 01:38:57 -04:00
$endDelay = time ();
// If the delay is greater than the timeout then fsockopen should't be
// used, because it will cause a long delay.
$elapseDelay = ( $endDelay - $startDelay ) > $r [ 'timeout' ];
if ( true === $elapseDelay )
add_option ( 'disable_fsockopen' , $endDelay , null , true );
2008-08-01 12:36:50 -04:00
if ( false === $handle )
2008-08-01 01:45:13 -04:00
return new WP_Error ( 'http_request_failed' , $iError . ': ' . $strError );
2008-08-01 01:00:07 -04:00
2008-08-13 19:37:42 -04:00
// WordPress supports PHP 4.3, which has this function. Removed sanity
// checking for performance reasons.
stream_set_timeout ( $handle , $r [ 'timeout' ] );
2008-08-01 01:00:07 -04:00
$requestPath = $arrURL [ 'path' ] . ( isset ( $arrURL [ 'query' ]) ? '?' . $arrURL [ 'query' ] : '' );
2008-08-01 12:36:50 -04:00
$requestPath = empty ( $requestPath ) ? '/' : $requestPath ;
2008-08-01 01:00:07 -04:00
$strHeaders = '' ;
2008-08-01 01:45:13 -04:00
$strHeaders .= strtoupper ( $r [ 'method' ]) . ' ' . $requestPath . ' HTTP/' . $r [ 'httpversion' ] . " \r \n " ;
$strHeaders .= 'Host: ' . $arrURL [ 'host' ] . " \r \n " ;
2008-08-01 01:00:07 -04:00
2008-08-12 17:21:11 -04:00
if ( isset ( $r [ 'user-agent' ]) )
$strHeaders .= 'User-agent: ' . $r [ 'user-agent' ] . " \r \n " ;
if ( is_array ( $r [ 'headers' ]) ) {
foreach ( ( array ) $r [ 'headers' ] as $header => $headerValue )
2008-08-01 01:45:13 -04:00
$strHeaders .= $header . ': ' . $headerValue . " \r \n " ;
2008-08-01 12:36:50 -04:00
} else {
2008-08-12 17:21:11 -04:00
$strHeaders .= $r [ 'headers' ];
2008-08-01 12:36:50 -04:00
}
2008-08-01 01:00:07 -04:00
$strHeaders .= " \r \n " ;
2008-08-12 17:21:11 -04:00
if ( ! is_null ( $r [ 'body' ]) )
$strHeaders .= $r [ 'body' ];
2008-08-01 01:00:07 -04:00
fwrite ( $handle , $strHeaders );
2008-08-01 14:44:40 -04:00
if ( ! $r [ 'blocking' ] ) {
fclose ( $handle );
return array ( 'headers' => array (), 'body' => '' , 'response' => array ( 'code' , 'message' ) );
}
2008-08-01 12:36:50 -04:00
2008-08-01 01:00:07 -04:00
$strResponse = '' ;
2008-08-01 01:45:13 -04:00
while ( ! feof ( $handle ) )
2008-08-01 01:00:07 -04:00
$strResponse .= fread ( $handle , 4096 );
2008-08-01 01:45:13 -04:00
2008-08-01 01:00:07 -04:00
fclose ( $handle );
2008-08-01 01:45:13 -04:00
if ( true === $secure_transport )
2008-08-01 01:00:07 -04:00
error_reporting ( $error_reporting );
$process = WP_Http :: processResponse ( $strResponse );
$arrHeaders = WP_Http :: processHeaders ( $process [ 'headers' ]);
2008-08-13 01:09:59 -04:00
// Is the response code within the 400 range?
if ( ( int ) $arrHeaders [ 'response' ][ 'code' ] >= 400 && ( int ) $arrHeaders [ 'response' ][ 'code' ] < 500 )
2008-08-01 01:45:13 -04:00
return new WP_Error ( 'http_request_failed' , $arrHeaders [ 'response' ][ 'code' ] . ': ' . $arrHeaders [ 'response' ][ 'message' ]);
2008-08-01 01:00:07 -04:00
2008-08-12 17:21:11 -04:00
// If location is found, then assume redirect and redirect to location.
2008-08-01 01:45:13 -04:00
if ( isset ( $arrHeaders [ 'headers' ][ 'location' ]) ) {
2008-08-12 17:21:11 -04:00
if ( $r [ 'redirection' ] -- > 0 ) {
return $this -> request ( $arrHeaders [ 'headers' ][ 'location' ], $r );
2008-10-06 19:10:22 -04:00
} else {
2008-08-01 01:00:07 -04:00
return new WP_Error ( 'http_request_failed' , __ ( 'Too many redirects.' ));
2008-10-06 19:10:22 -04:00
}
2008-08-01 01:00:07 -04:00
}
2008-10-06 19:10:22 -04:00
// If the body was chunk encoded, then decode it.
if ( ! empty ( $process [ 'body' ] ) && isset ( $arrHeaders [ 'headers' ][ 'transfer-encoding' ] ) && 'chunked' == $arrHeaders [ 'headers' ][ 'transfer-encoding' ] )
$process [ 'body' ] = WP_Http :: chunkTransferDecode ( $process [ 'body' ]);
2009-01-22 16:16:11 -05:00
if ( true === $r [ 'decompress' ] && true === WP_Http_Encoding :: should_decode ( $arrHeaders ) )
$process [ 'body' ] = WP_Http_Encoding :: decompress ( $process [ 'body' ] );
2008-08-01 01:00:07 -04:00
return array ( 'headers' => $arrHeaders [ 'headers' ], 'body' => $process [ 'body' ], 'response' => $arrHeaders [ 'response' ]);
}
/**
* Whether this class can be used for retrieving an URL .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ static
* @ return boolean False means this class can not be used , true means it can .
*/
2008-08-01 01:45:13 -04:00
function test () {
2008-08-16 01:38:57 -04:00
if ( false !== ( $option = get_option ( 'disable_fsockopen' )) && time () - $option < 43200 ) // 12 hours
return false ;
2008-08-01 01:45:13 -04:00
if ( function_exists ( 'fsockopen' ) )
2008-08-01 01:00:07 -04:00
return true ;
return false ;
}
}
/**
* HTTP request method uses fopen function to retrieve the url .
*
* Requires PHP version greater than 4.3 . 0 for stream support . Does not allow
* for $context support , but should still be okay , to write the headers , before
* getting the response . Also requires that 'allow_url_fopen' to be enabled .
*
* @ package WordPress
* @ subpackage HTTP
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*/
2008-08-01 01:45:13 -04:00
class WP_Http_Fopen {
2008-08-01 01:00:07 -04:00
/**
2008-08-01 14:44:40 -04:00
* Send a HTTP request to a URI using fopen () .
*
* This transport does not support sending of headers and body , therefore
* should not be used in the instances , where there is a body and headers .
*
* Notes : Does not support non - blocking mode . Ignores 'redirection' option .
*
* @ see WP_Http :: retrieve For default options descriptions .
2008-08-01 01:00:07 -04:00
*
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
2008-08-01 14:44:40 -04:00
* @ param string $url URI resource .
* @ param str | array $args Optional . Override the defaults .
* @ return array 'headers' , 'body' , and 'response' keys .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function request ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
global $http_response_header ;
$defaults = array (
2008-10-15 16:36:23 -04:00
'method' => 'GET' , 'timeout' => 5 ,
2008-08-01 12:36:50 -04:00
'redirection' => 5 , 'httpversion' => '1.0' ,
2008-08-12 17:21:11 -04:00
'blocking' => true ,
'headers' => array (), 'body' => null
2008-08-01 01:00:07 -04:00
);
$r = wp_parse_args ( $args , $defaults );
$arrURL = parse_url ( $url );
2008-08-05 01:45:34 -04:00
if ( false === $arrURL )
return new WP_Error ( 'http_request_failed' , sprintf ( __ ( 'Malformed URL: %s' ), $url ));
2009-01-06 12:29:24 -05:00
if ( 'http' != $arrURL [ 'scheme' ] && 'https' != $arrURL [ 'scheme' ] )
2008-08-01 01:00:07 -04:00
$url = str_replace ( $arrURL [ 'scheme' ], 'http' , $url );
2008-08-05 01:45:34 -04:00
if ( ! defined ( 'WP_DEBUG' ) || ( defined ( 'WP_DEBUG' ) && false === WP_DEBUG ) )
$handle = @ fopen ( $url , 'r' );
else
$handle = fopen ( $url , 'r' );
2008-08-01 01:00:07 -04:00
2008-08-01 01:45:13 -04:00
if ( ! $handle )
return new WP_Error ( 'http_request_failed' , sprintf ( __ ( 'Could not open handle for fopen() to %s' ), $url ));
2008-08-01 01:00:07 -04:00
2008-08-13 19:37:42 -04:00
// WordPress supports PHP 4.3, which has this function. Removed sanity
// checking for performance reasons.
stream_set_timeout ( $handle , $r [ 'timeout' ] );
2008-08-01 01:00:07 -04:00
2008-08-01 14:44:40 -04:00
if ( ! $r [ 'blocking' ] ) {
fclose ( $handle );
return array ( 'headers' => array (), 'body' => '' , 'response' => array ( 'code' , 'message' ) );
}
2008-08-01 12:36:50 -04:00
2008-08-01 01:00:07 -04:00
$strResponse = '' ;
2008-08-01 01:45:13 -04:00
while ( ! feof ( $handle ) )
2008-08-01 01:00:07 -04:00
$strResponse .= fread ( $handle , 4096 );
$theHeaders = '' ;
2008-08-01 01:45:13 -04:00
if ( function_exists ( 'stream_get_meta_data' ) ) {
2008-08-01 01:00:07 -04:00
$meta = stream_get_meta_data ( $handle );
$theHeaders = $meta [ 'wrapper_data' ];
2008-12-07 15:30:59 -05:00
if ( isset ( $meta [ 'wrapper_data' ][ 'headers' ] ) )
$theHeaders = $meta [ 'wrapper_data' ][ 'headers' ];
2008-08-01 01:00:07 -04:00
} else {
2008-12-07 15:30:59 -05:00
if ( ! isset ( $http_response_header ) )
global $http_response_header ;
2008-08-01 01:00:07 -04:00
$theHeaders = $http_response_header ;
}
2008-08-01 14:44:40 -04:00
2008-08-01 17:24:03 -04:00
fclose ( $handle );
2008-08-01 18:31:57 -04:00
$processedHeaders = WP_Http :: processHeaders ( $theHeaders );
2008-08-12 17:21:11 -04:00
if ( ! empty ( $strResponse ) && isset ( $processedHeaders [ 'headers' ][ 'transfer-encoding' ] ) && 'chunked' == $processedHeaders [ 'headers' ][ 'transfer-encoding' ] )
2008-08-13 01:09:59 -04:00
$strResponse = WP_Http :: chunkTransferDecode ( $strResponse );
2008-08-12 17:21:11 -04:00
2009-01-22 16:16:11 -05:00
if ( true === $r [ 'decompress' ] && true === WP_Http_Encoding :: should_decode ( $processedHeaders ) )
$strResponse = WP_Http_Encoding :: decompress ( $strResponse );
2008-08-01 01:00:07 -04:00
return array ( 'headers' => $processedHeaders [ 'headers' ], 'body' => $strResponse , 'response' => $processedHeaders [ 'response' ]);
}
/**
* Whether this class can be used for retrieving an URL .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ static
* @ return boolean False means this class can not be used , true means it can .
*/
2008-08-01 01:45:13 -04:00
function test () {
if ( ! function_exists ( 'fopen' ) || ( function_exists ( 'ini_get' ) && true != ini_get ( 'allow_url_fopen' )) )
2008-08-01 01:00:07 -04:00
return false ;
return true ;
}
}
/**
* HTTP request method uses Streams to retrieve the url .
*
* Requires PHP 5.0 + and uses fopen with stream context . Requires that
* 'allow_url_fopen' PHP setting to be enabled .
*
* Second preferred method for getting the URL , for PHP 5.
*
* @ package WordPress
* @ subpackage HTTP
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*/
2008-08-01 01:45:13 -04:00
class WP_Http_Streams {
2008-08-01 01:00:07 -04:00
/**
2008-08-01 14:44:40 -04:00
* Send a HTTP request to a URI using streams with fopen () .
2008-08-01 01:00:07 -04:00
*
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param string $url
* @ param str | array $args Optional . Override the defaults .
2008-08-01 14:44:40 -04:00
* @ return array 'headers' , 'body' , and 'response' keys .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function request ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$defaults = array (
2008-10-15 16:36:23 -04:00
'method' => 'GET' , 'timeout' => 5 ,
2008-08-01 12:36:50 -04:00
'redirection' => 5 , 'httpversion' => '1.0' ,
2008-08-12 17:21:11 -04:00
'blocking' => true ,
'headers' => array (), 'body' => null
2008-08-01 01:00:07 -04:00
);
$r = wp_parse_args ( $args , $defaults );
2008-08-12 17:21:11 -04:00
if ( isset ( $r [ 'headers' ][ 'User-Agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'User-Agent' ];
unset ( $r [ 'headers' ][ 'User-Agent' ]);
} else if ( isset ( $r [ 'headers' ][ 'user-agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'user-agent' ];
unset ( $r [ 'headers' ][ 'user-agent' ]);
2008-08-04 12:28:12 -04:00
}
2008-08-01 01:00:07 -04:00
$arrURL = parse_url ( $url );
2008-08-05 01:45:34 -04:00
if ( false === $arrURL )
return new WP_Error ( 'http_request_failed' , sprintf ( __ ( 'Malformed URL: %s' ), $url ));
2009-01-05 13:42:02 -05:00
if ( 'http' != $arrURL [ 'scheme' ] && 'https' != $arrURL [ 'scheme' ] )
2008-08-01 01:00:07 -04:00
$url = str_replace ( $arrURL [ 'scheme' ], 'http' , $url );
2008-08-12 17:21:11 -04:00
// Convert Header array to string.
$strHeaders = '' ;
if ( is_array ( $r [ 'headers' ] ) )
foreach ( $r [ 'headers' ] as $name => $value )
$strHeaders .= " { $name } : $value\r\n " ;
else if ( is_string ( $r [ 'headers' ] ) )
$strHeaders = $r [ 'headers' ];
2008-08-09 01:36:14 -04:00
$arrContext = array ( 'http' =>
2008-08-01 01:00:07 -04:00
array (
'method' => strtoupper ( $r [ 'method' ]),
2008-12-05 15:12:51 -05:00
'user_agent' => $r [ 'user-agent' ],
2008-08-01 01:00:07 -04:00
'max_redirects' => $r [ 'redirection' ],
'protocol_version' => ( float ) $r [ 'httpversion' ],
2008-08-12 17:21:11 -04:00
'header' => $strHeaders ,
2008-08-03 17:01:28 -04:00
'timeout' => $r [ 'timeout' ]
2008-08-01 01:00:07 -04:00
)
);
2008-08-12 17:21:11 -04:00
if ( ! is_null ( $r [ 'body' ]) && ! empty ( $r [ 'body' ] ) )
$arrContext [ 'http' ][ 'content' ] = $r [ 'body' ];
2008-08-01 01:00:07 -04:00
$context = stream_context_create ( $arrContext );
2009-01-06 12:29:24 -05:00
if ( ! defined ( 'WP_DEBUG' ) || ( defined ( 'WP_DEBUG' ) && false === WP_DEBUG ) )
2008-08-05 12:33:20 -04:00
$handle = @ fopen ( $url , 'r' , false , $context );
2008-08-05 01:45:34 -04:00
else
2008-08-05 12:33:20 -04:00
$handle = fopen ( $url , 'r' , false , $context );
2008-08-01 01:00:07 -04:00
2008-08-01 12:36:50 -04:00
if ( ! $handle )
2008-08-01 01:45:13 -04:00
return new WP_Error ( 'http_request_failed' , sprintf ( __ ( 'Could not open handle for fopen() to %s' ), $url ));
2008-08-01 01:00:07 -04:00
2008-08-13 19:37:42 -04:00
// WordPress supports PHP 4.3, which has this function. Removed sanity
// checking for performance reasons.
2008-08-04 12:28:12 -04:00
stream_set_timeout ( $handle , $r [ 'timeout' ] );
2008-08-03 17:01:28 -04:00
2008-08-01 14:44:40 -04:00
if ( ! $r [ 'blocking' ] ) {
2008-11-26 06:48:05 -05:00
stream_set_blocking ( $handle , 0 );
2008-08-01 14:44:40 -04:00
fclose ( $handle );
return array ( 'headers' => array (), 'body' => '' , 'response' => array ( 'code' , 'message' ) );
}
2008-08-01 12:36:50 -04:00
2008-08-01 01:00:07 -04:00
$strResponse = stream_get_contents ( $handle );
$meta = stream_get_meta_data ( $handle );
2008-12-07 15:30:59 -05:00
2009-01-22 16:16:11 -05:00
fclose ( $handle );
2008-12-07 15:30:59 -05:00
$processedHeaders = array ();
if ( isset ( $meta [ 'wrapper_data' ][ 'headers' ] ) )
$processedHeaders = WP_Http :: processHeaders ( $meta [ 'wrapper_data' ][ 'headers' ]);
else
$processedHeaders = WP_Http :: processHeaders ( $meta [ 'wrapper_data' ]);
2008-08-01 01:00:07 -04:00
2008-08-12 17:21:11 -04:00
if ( ! empty ( $strResponse ) && isset ( $processedHeaders [ 'headers' ][ 'transfer-encoding' ] ) && 'chunked' == $processedHeaders [ 'headers' ][ 'transfer-encoding' ] )
2008-08-13 01:09:59 -04:00
$strResponse = WP_Http :: chunkTransferDecode ( $strResponse );
2008-08-12 17:21:11 -04:00
2009-01-22 16:16:11 -05:00
if ( true === $r [ 'decompress' ] && true === WP_Http_Encoding :: should_decode ( $processedHeaders ) )
$strResponse = WP_Http_Encoding :: decompress ( $strResponse );
2008-08-01 01:00:07 -04:00
return array ( 'headers' => $processedHeaders [ 'headers' ], 'body' => $strResponse , 'response' => $processedHeaders [ 'response' ]);
}
/**
* Whether this class can be used for retrieving an URL .
*
* @ static
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ return boolean False means this class can not be used , true means it can .
*/
2008-08-01 01:45:13 -04:00
function test () {
if ( ! function_exists ( 'fopen' ) || ( function_exists ( 'ini_get' ) && true != ini_get ( 'allow_url_fopen' )) )
2008-08-01 01:00:07 -04:00
return false ;
2008-08-01 01:45:13 -04:00
if ( version_compare ( PHP_VERSION , '5.0' , '<' ) )
2008-08-01 01:00:07 -04:00
return false ;
return true ;
}
}
/**
* HTTP request method uses HTTP extension to retrieve the url .
*
2008-08-01 14:44:40 -04:00
* Requires the HTTP extension to be installed . This would be the preferred
* transport since it can handle a lot of the problems that forces the others to
* use the HTTP version 1.0 . Even if PHP 5.2 + is being used , it doesn ' t mean
* that the HTTP extension will be enabled .
2008-08-01 01:00:07 -04:00
*
* @ package WordPress
* @ subpackage HTTP
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*/
2008-08-01 01:45:13 -04:00
class WP_Http_ExtHTTP {
2008-08-01 01:00:07 -04:00
/**
2008-08-01 14:44:40 -04:00
* Send a HTTP request to a URI using HTTP extension .
*
* Does not support non - blocking .
2008-08-01 01:00:07 -04:00
*
* @ access public
2008-08-01 01:45:13 -04:00
* @ since 2.7
2008-08-01 01:00:07 -04:00
*
* @ param string $url
* @ param str | array $args Optional . Override the defaults .
2008-08-01 14:44:40 -04:00
* @ return array 'headers' , 'body' , and 'response' keys .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function request ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$defaults = array (
2008-10-15 16:36:23 -04:00
'method' => 'GET' , 'timeout' => 5 ,
2008-08-01 01:00:07 -04:00
'redirection' => 5 , 'httpversion' => '1.0' ,
2008-08-12 17:21:11 -04:00
'blocking' => true ,
'headers' => array (), 'body' => null
2008-08-01 01:00:07 -04:00
);
$r = wp_parse_args ( $args , $defaults );
2008-08-12 17:21:11 -04:00
if ( isset ( $r [ 'headers' ][ 'User-Agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'User-Agent' ];
unset ( $r [ 'headers' ][ 'User-Agent' ]);
} else if ( isset ( $r [ 'headers' ][ 'user-agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'user-agent' ];
unset ( $r [ 'headers' ][ 'user-agent' ]);
2008-08-01 14:44:40 -04:00
}
2008-08-01 01:00:07 -04:00
2008-08-01 01:45:13 -04:00
switch ( $r [ 'method' ] ) {
2008-08-01 01:00:07 -04:00
case 'POST' :
$r [ 'method' ] = HTTP_METH_POST ;
break ;
case 'HEAD' :
$r [ 'method' ] = HTTP_METH_HEAD ;
break ;
2008-10-15 16:36:23 -04:00
case 'GET' :
2008-08-01 01:00:07 -04:00
default :
$r [ 'method' ] = HTTP_METH_GET ;
}
$arrURL = parse_url ( $url );
2008-08-01 01:45:13 -04:00
if ( 'http' != $arrURL [ 'scheme' ] || 'https' != $arrURL [ 'scheme' ] )
2008-08-01 01:00:07 -04:00
$url = str_replace ( $arrURL [ 'scheme' ], 'http' , $url );
$options = array (
2008-08-01 14:44:40 -04:00
'timeout' => $r [ 'timeout' ],
'connecttimeout' => $r [ 'timeout' ],
'redirect' => $r [ 'redirection' ],
2008-08-01 17:24:03 -04:00
'useragent' => $r [ 'user-agent' ],
2008-08-12 17:21:11 -04:00
'headers' => $r [ 'headers' ],
2008-08-01 01:00:07 -04:00
);
2008-10-06 19:10:22 -04:00
if ( ! defined ( 'WP_DEBUG' ) || ( defined ( 'WP_DEBUG' ) && false === WP_DEBUG ) ) //Emits warning level notices for max redirects and timeouts
$strResponse = @ http_request ( $r [ 'method' ], $url , $r [ 'body' ], $options , $info );
else
$strResponse = http_request ( $r [ 'method' ], $url , $r [ 'body' ], $options , $info ); //Emits warning level notices for max redirects and timeouts
2008-08-01 01:00:07 -04:00
2008-10-06 19:10:22 -04:00
if ( false === $strResponse || ! empty ( $info [ 'error' ]) ) //Error may still be set, Response may return headers or partial document, and error contains a reason the request was aborted, eg, timeout expired or max-redirects reached
2008-08-01 01:45:13 -04:00
return new WP_Error ( 'http_request_failed' , $info [ 'response_code' ] . ': ' . $info [ 'error' ]);
2008-08-01 01:00:07 -04:00
2008-08-01 14:44:40 -04:00
if ( ! $r [ 'blocking' ] )
return array ( 'headers' => array (), 'body' => '' , 'response' => array ( 'code' , 'message' ) );
2008-08-01 01:00:07 -04:00
list ( $theHeaders , $theBody ) = explode ( " \r \n \r \n " , $strResponse , 2 );
$theHeaders = WP_Http :: processHeaders ( $theHeaders );
2008-08-05 18:22:07 -04:00
2008-10-06 19:10:22 -04:00
if ( ! empty ( $theBody ) && isset ( $theHeaders [ 'headers' ][ 'transfer-encoding' ] ) && 'chunked' == $theHeaders [ 'headers' ][ 'transfer-encoding' ] ) {
if ( ! defined ( 'WP_DEBUG' ) || ( defined ( 'WP_DEBUG' ) && false === WP_DEBUG ) )
$theBody = @ http_chunked_decode ( $theBody );
else
$theBody = http_chunked_decode ( $theBody );
}
2008-08-01 01:00:07 -04:00
2009-01-22 16:16:11 -05:00
if ( true === $r [ 'decompress' ] && true === WP_Http_Encoding :: should_decode ( $theHeaders ) )
$theBody = http_inflate ( $theBody );
2008-08-01 01:00:07 -04:00
$theResponse = array ();
2008-08-01 18:31:57 -04:00
$theResponse [ 'code' ] = $info [ 'response_code' ];
$theResponse [ 'message' ] = get_status_header_desc ( $info [ 'response_code' ]);
2008-08-01 01:00:07 -04:00
return array ( 'headers' => $theHeaders [ 'headers' ], 'body' => $theBody , 'response' => $theResponse );
}
/**
* Whether this class can be used for retrieving an URL .
*
* @ static
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ return boolean False means this class can not be used , true means it can .
*/
2008-08-01 01:45:13 -04:00
function test () {
if ( function_exists ( 'http_request' ) )
2008-08-01 01:00:07 -04:00
return true ;
return false ;
}
}
2008-08-01 14:44:40 -04:00
/**
* HTTP request method uses Curl extension to retrieve the url .
*
* Requires the Curl extension to be installed .
*
* @ package WordPress
* @ subpackage HTTP
* @ since 2.7
*/
class WP_Http_Curl {
/**
* Send a HTTP request to a URI using cURL extension .
*
* @ access public
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 14:44:40 -04:00
*
* @ param string $url
* @ param str | array $args Optional . Override the defaults .
* @ return array 'headers' , 'body' , and 'response' keys .
*/
2008-08-12 17:21:11 -04:00
function request ( $url , $args = array ()) {
2008-08-01 14:44:40 -04:00
$defaults = array (
2008-10-15 16:36:23 -04:00
'method' => 'GET' , 'timeout' => 5 ,
2008-08-01 14:44:40 -04:00
'redirection' => 5 , 'httpversion' => '1.0' ,
2008-08-12 17:21:11 -04:00
'blocking' => true ,
'headers' => array (), 'body' => null
2008-08-01 14:44:40 -04:00
);
$r = wp_parse_args ( $args , $defaults );
2008-08-12 17:21:11 -04:00
if ( isset ( $r [ 'headers' ][ 'User-Agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'User-Agent' ];
unset ( $r [ 'headers' ][ 'User-Agent' ]);
} else if ( isset ( $r [ 'headers' ][ 'user-agent' ]) ) {
$r [ 'user-agent' ] = $r [ 'headers' ][ 'user-agent' ];
unset ( $r [ 'headers' ][ 'user-agent' ]);
2008-08-01 14:44:40 -04:00
}
2009-01-06 12:29:24 -05:00
// cURL extension will sometimes fail when the timeout is less than 1 as
// it may round down to 0, which gives it unlimited timeout.
2008-12-03 14:56:05 -05:00
if ( $r [ 'timeout' ] > 0 && $r [ 'timeout' ] < 1 )
$r [ 'timeout' ] = 1 ;
2008-08-01 14:44:40 -04:00
$handle = curl_init ();
curl_setopt ( $handle , CURLOPT_URL , $url );
2009-01-06 12:29:24 -05:00
// The cURL extension requires that the option be set for the HEAD to
// work properly.
2008-12-06 05:09:05 -05:00
if ( 'HEAD' === $r [ 'method' ] ) {
curl_setopt ( $handle , CURLOPT_NOBODY , true );
}
2008-08-01 14:44:40 -04:00
if ( true === $r [ 'blocking' ] ) {
curl_setopt ( $handle , CURLOPT_HEADER , true );
2008-08-07 18:57:16 -04:00
curl_setopt ( $handle , CURLOPT_RETURNTRANSFER , 1 );
2008-08-01 14:44:40 -04:00
} else {
curl_setopt ( $handle , CURLOPT_HEADER , false );
curl_setopt ( $handle , CURLOPT_NOBODY , true );
2008-08-07 18:57:16 -04:00
curl_setopt ( $handle , CURLOPT_RETURNTRANSFER , 0 );
2008-08-01 14:44:40 -04:00
}
curl_setopt ( $handle , CURLOPT_USERAGENT , $r [ 'user-agent' ] );
curl_setopt ( $handle , CURLOPT_CONNECTTIMEOUT , 1 );
curl_setopt ( $handle , CURLOPT_TIMEOUT , $r [ 'timeout' ] );
curl_setopt ( $handle , CURLOPT_MAXREDIRS , $r [ 'redirection' ] );
2009-01-06 12:29:24 -05:00
// The option doesn't work with safe mode or when open_basedir is set.
2008-08-04 13:03:05 -04:00
if ( ! ini_get ( 'safe_mode' ) && ! ini_get ( 'open_basedir' ) )
curl_setopt ( $handle , CURLOPT_FOLLOWLOCATION , true );
2008-08-09 01:36:14 -04:00
2008-08-12 17:21:11 -04:00
if ( ! is_null ( $r [ 'headers' ]) )
curl_setopt ( $handle , CURLOPT_HTTPHEADER , $r [ 'headers' ] );
2008-08-01 14:44:40 -04:00
if ( $r [ 'httpversion' ] == '1.0' )
2008-08-01 17:24:03 -04:00
curl_setopt ( $handle , CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_0 );
2008-08-01 14:44:40 -04:00
else
2008-08-01 17:24:03 -04:00
curl_setopt ( $handle , CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_1_1 );
2008-08-01 14:44:40 -04:00
2009-01-06 12:29:24 -05:00
// Cookies are not handled by the HTTP API currently. Allow for plugin
// authors to handle it themselves... Although, it is somewhat pointless
// without some reference.
2008-12-31 17:33:46 -05:00
do_action_ref_array ( 'http_api_curl' , array ( & $handle ) );
2008-12-30 17:45:51 -05:00
2009-01-06 12:29:24 -05:00
// We don't need to return the body, so don't. Just execution request
// and return.
2008-08-01 14:44:40 -04:00
if ( ! $r [ 'blocking' ] ) {
2008-08-07 18:57:16 -04:00
curl_exec ( $handle );
2008-08-01 14:44:40 -04:00
curl_close ( $handle );
return array ( 'headers' => array (), 'body' => '' , 'response' => array ( 'code' , 'message' ) );
}
$theResponse = curl_exec ( $handle );
2008-10-15 12:35:35 -04:00
if ( ! empty ( $theResponse ) ) {
2008-10-06 19:10:22 -04:00
$headerLength = curl_getinfo ( $handle , CURLINFO_HEADER_SIZE );
$theHeaders = trim ( substr ( $theResponse , 0 , $headerLength ) );
$theBody = substr ( $theResponse , $headerLength );
if ( false !== strrpos ( $theHeaders , " \r \n \r \n " ) ) {
$headerParts = explode ( " \r \n \r \n " , $theHeaders );
$theHeaders = $headerParts [ count ( $headerParts ) - 1 ];
}
$theHeaders = WP_Http :: processHeaders ( $theHeaders );
} else {
2008-10-15 12:35:35 -04:00
if ( $curl_error = curl_error ( $handle ) )
return new WP_Error ( 'http_request_failed' , $curl_error );
2008-10-06 19:10:22 -04:00
if ( in_array ( curl_getinfo ( $handle , CURLINFO_HTTP_CODE ), array ( 301 , 302 ) ) )
return new WP_Error ( 'http_request_failed' , __ ( 'Too many redirects.' ));
2008-12-07 15:30:59 -05:00
2008-10-06 19:10:22 -04:00
$theHeaders = array ( 'headers' => array () );
$theBody = '' ;
}
2009-01-22 16:16:11 -05:00
2008-08-01 14:44:40 -04:00
$response = array ();
$response [ 'code' ] = curl_getinfo ( $handle , CURLINFO_HTTP_CODE );
$response [ 'message' ] = get_status_header_desc ( $response [ 'code' ]);
curl_close ( $handle );
2009-01-22 16:16:11 -05:00
if ( true === $r [ 'decompress' ] && true === WP_Http_Encoding :: should_decode ( $theHeaders ) )
$theBody = WP_Http_Encoding :: decompress ( $theBody );
2008-08-01 14:44:40 -04:00
return array ( 'headers' => $theHeaders [ 'headers' ], 'body' => $theBody , 'response' => $response );
}
/**
* Whether this class can be used for retrieving an URL .
*
* @ static
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 14:44:40 -04:00
*
* @ return boolean False means this class can not be used , true means it can .
*/
function test () {
2008-12-11 13:08:58 -05:00
if ( function_exists ( 'curl_init' ) && function_exists ( 'curl_exec' ) )
2008-08-01 14:44:40 -04:00
return true ;
return false ;
}
}
2008-08-01 01:00:07 -04:00
/**
* Returns the initialized WP_Http Object
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
* @ access private
*
* @ return WP_Http HTTP Transport object .
*/
function & _wp_http_get_object () {
static $http ;
2008-08-01 01:45:13 -04:00
if ( is_null ( $http ) )
2008-08-01 01:00:07 -04:00
$http = new WP_Http ();
return $http ;
}
/**
* Retrieve the raw response from the HTTP request .
*
* The array structure is a little complex .
*
* < code >
2008-09-07 21:23:43 -04:00
* $res = array ( 'headers' => array (), 'response' => array ( 'code' , 'message' ) );
2008-08-01 01:00:07 -04:00
* </ code >
*
2008-09-07 21:23:43 -04:00
* All of the headers in $res [ 'headers' ] are with the name as the key and the
* value as the value . So to get the User - Agent , you would do the following .
2008-08-01 01:00:07 -04:00
*
* < code >
2008-09-07 21:23:43 -04:00
* $user_agent = $res [ 'headers' ][ 'user-agent' ];
2008-08-01 01:00:07 -04:00
* </ code >
*
* The body is the raw response content and can be retrieved from $res [ 'body' ] .
*
* This function is called first to make the request and there are other API
* functions to abstract out the above convoluted setup .
*
2008-09-07 21:23:43 -04:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param string $url Site URL to retrieve .
* @ param array $args Optional . Override the defaults .
2009-01-14 16:40:41 -05:00
* @ return WP_Error | string The body of the response or WP_Error on failure .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function wp_remote_request ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$objFetchSite = _wp_http_get_object ();
2008-08-12 17:21:11 -04:00
return $objFetchSite -> request ( $url , $args );
2008-08-01 01:00:07 -04:00
}
/**
* Retrieve the raw response from the HTTP request using the GET method .
*
* @ see wp_remote_request () For more information on the response array format .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param string $url Site URL to retrieve .
* @ param array $args Optional . Override the defaults .
2009-01-14 16:40:41 -05:00
* @ return WP_Error | string The body of the response or WP_Error on failure .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function wp_remote_get ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$objFetchSite = _wp_http_get_object ();
2008-08-12 17:21:11 -04:00
return $objFetchSite -> get ( $url , $args );
2008-08-01 01:00:07 -04:00
}
/**
* Retrieve the raw response from the HTTP request using the POST method .
*
* @ see wp_remote_request () For more information on the response array format .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param string $url Site URL to retrieve .
* @ param array $args Optional . Override the defaults .
2009-01-14 16:40:41 -05:00
* @ return WP_Error | string The body of the response or WP_Error on failure .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function wp_remote_post ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$objFetchSite = _wp_http_get_object ();
2008-08-12 17:21:11 -04:00
return $objFetchSite -> post ( $url , $args );
2008-08-01 01:00:07 -04:00
}
/**
* Retrieve the raw response from the HTTP request using the HEAD method .
*
* @ see wp_remote_request () For more information on the response array format .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param string $url Site URL to retrieve .
* @ param array $args Optional . Override the defaults .
2009-01-14 16:40:41 -05:00
* @ return WP_Error | string The body of the response or WP_Error on failure .
2008-08-01 01:00:07 -04:00
*/
2008-08-12 17:21:11 -04:00
function wp_remote_head ( $url , $args = array ()) {
2008-08-01 01:00:07 -04:00
$objFetchSite = _wp_http_get_object ();
2008-08-12 17:21:11 -04:00
return $objFetchSite -> head ( $url , $args );
2008-08-01 01:00:07 -04:00
}
/**
* Retrieve only the headers from the raw response .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param array $response HTTP response .
* @ return array The headers of the response . Empty array if incorrect parameter given .
*/
function wp_remote_retrieve_headers ( & $response ) {
2008-08-01 01:45:13 -04:00
if ( ! isset ( $response [ 'headers' ]) || ! is_array ( $response [ 'headers' ]))
2008-08-01 01:00:07 -04:00
return array ();
return $response [ 'headers' ];
}
/**
* Retrieve a single header by name from the raw response .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param array $response
* @ param string $header Header name to retrieve value from .
* @ return array The header value . Empty string on if incorrect parameter given .
*/
function wp_remote_retrieve_header ( & $response , $header ) {
2008-08-01 01:45:13 -04:00
if ( ! isset ( $response [ 'headers' ]) || ! is_array ( $response [ 'headers' ]))
2008-08-01 01:00:07 -04:00
return '' ;
2008-08-01 01:45:13 -04:00
if ( array_key_exists ( $header , $response [ 'headers' ]) )
2008-08-01 01:00:07 -04:00
return $response [ 'headers' ][ $header ];
return '' ;
}
/**
* Retrieve only the response code from the raw response .
*
* Will return an empty array if incorrect parameter value is given .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param array $response HTTP response .
* @ return array The keys 'code' and 'message' give information on the response .
*/
function wp_remote_retrieve_response_code ( & $response ) {
2008-08-01 01:45:13 -04:00
if ( ! isset ( $response [ 'response' ]) || ! is_array ( $response [ 'response' ]))
2008-08-01 01:00:07 -04:00
return '' ;
return $response [ 'response' ][ 'code' ];
}
/**
* Retrieve only the response message from the raw response .
*
* Will return an empty array if incorrect parameter value is given .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param array $response HTTP response .
* @ return array The keys 'code' and 'message' give information on the response .
*/
function wp_remote_retrieve_response_message ( & $response ) {
2008-08-01 01:45:13 -04:00
if ( ! isset ( $response [ 'response' ]) || ! is_array ( $response [ 'response' ]))
2008-08-01 01:00:07 -04:00
return '' ;
return $response [ 'response' ][ 'message' ];
}
/**
* Retrieve only the body from the raw response .
*
2008-12-30 17:53:35 -05:00
* @ since 2.7 . 0
2008-08-01 01:00:07 -04:00
*
* @ param array $response HTTP response .
* @ return string The body of the response . Empty string if no body or incorrect parameter given .
*/
function wp_remote_retrieve_body ( & $response ) {
2008-08-01 01:45:13 -04:00
if ( ! isset ( $response [ 'body' ]) )
2008-08-01 01:00:07 -04:00
return '' ;
return $response [ 'body' ];
}
2008-08-05 01:45:34 -04:00
?>