From: Chuck Hagenbuch Date: Fri, 4 Sep 2009 15:40:48 +0000 (-0400) Subject: Initial refactoring to split backends based on request/response objects X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=258ede7c54e605174c9ff7976039b607f6707b36;p=horde.git Initial refactoring to split backends based on request/response objects --- diff --git a/framework/Http/examples/Horde/Http/get-example-dot-com.php b/framework/Http/examples/Horde/Http/get-example-dot-com.php new file mode 100644 index 000000000..96f45155f --- /dev/null +++ b/framework/Http/examples/Horde/Http/get-example-dot-com.php @@ -0,0 +1,18 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +require 'Horde/Autoloader.php'; + +$client = new Horde_Http_Client(); +$response = $client->get('http://www.example.com/'); +var_dump($response); +echo $response->getBody(); diff --git a/framework/Http/lib/Horde/Http/Client.php b/framework/Http/lib/Horde/Http/Client.php new file mode 100644 index 000000000..dea2a2f09 --- /dev/null +++ b/framework/Http/lib/Horde/Http/Client.php @@ -0,0 +1,193 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +class Horde_Http_Client +{ + /** + * The current HTTP request + * @var Horde_Http_Request_Base + */ + protected $_request; + + /** + * The previous HTTP request + * @var Horde_Http_Request_Base + */ + protected $_lastRequest; + + /** + * The most recent HTTP response + * @var Horde_Http_Response_Base + */ + protected $_lastResponse; + + /** + * Horde_Http_Client constructor. + * + * @param array $args Any Http_Client settings to initialize in the + * constructor. Available settings are: + * client.proxyServer + * client.proxyUser + * client.proxyPass + * client.timeout + * request + * request.uri + * request.headers + * request.method + * request.data + */ + public function __construct($args = array()) + { + // Set or create request object + if (isset($args['request'])) { + $this->_request = $args['request']; + unset($args['request']); + } else { + $this->_request = $this->_createBestAvailableRequest(); + } + + foreach ($args as $key => $val) { + list($object, $objectkey) = explode('.', $key, 2); + $this->$object->$objectkey = $val; + } + } + + /** + * Send a GET request + * + * @return Horde_Http_Response_Base + */ + public function get($uri = null, $headers = array()) + { + return $this->request('GET', $uri, null, $headers); + } + + /** + * Send a POST request + * + * @return Horde_Http_Response_Base + */ + public function post($uri = null, $data = null, $headers = array()) + { + return $this->request('POST', $uri, $data, $headers); + } + + /** + * Send a PUT request + * + * @return Horde_Http_Response_Base + */ + public function put($uri = null, $data = null, $headers = array()) + { + /* @TODO suport method override (X-Method-Override: PUT). */ + return $this->request('PUT', $uri, $data, $headers); + } + + /** + * Send a DELETE request + * + * @return Horde_Http_Response_Base + */ + public function delete($uri = null, $headers = array()) + { + /* @TODO suport method override (X-Method-Override: DELETE). */ + return $this->request('DELETE', $uri, null, $headers); + } + + /** + * Send a HEAD request + * @TODO + * + * @return ? Probably just the status + */ + public function head($uri = null, $headers = array()) + { + return $this->request('HEAD', $uri, null, $headers); + } + + /** + * Send an HTTP request + * + * @param string $method HTTP request method (GET, PUT, etc.) + * @param string $uri URI to request, if different from $this->uri + * @param mixed $data Request data. Can be an array of form data that will be + * encoded automatically, or a raw string. + * @param array $headers Any headers specific to this request. They will + * be combined with $this->_headers, and override + * headers of the same name for this request only. + * + * @return Horde_Http_Response_Base + */ + public function request($method, $uri = null, $data = null, $headers = array()) + { + if ($method !== null) { + $this->request->method = $method; + } + if ($uri !== null) { + $this->request->uri = $uri; + } + if ($data !== null) { + $this->request->data = $data; + } + if (count($headers)) { + $this->request->setHeaders($headers); + } + + $this->_lastRequest = $this->_request; + $this->_lastResponse = $this->_adapter->send($this->_lastRequest); + return $this->_lastResponse; + } + + /** + * Get a client parameter + * + * @param string $name The parameter to get. + * @return mixed Parameter value. + */ + public function __get($name) + { + return isset($this->{'_' . $name}) ? $this->{'_' . $name} : null; + } + + /** + * Set a client parameter + * + * @param string $name The parameter to set. + * @param mixed $value Parameter value. + */ + public function __set($name, $value) + { + $this->{'_' . $name} = $value; + } + + /** + * Find the best available request backend + * + * @return Horde_Http_Request_Base + */ + public function _createBestAvailableRequest() + { + if (class_exists('HttpRequest', false)) { + return new Horde_Http_Request_Peclhttp(); + } elseif (extension_loaded('curl')) { + return new Horde_Http_Request_Curl(); + } elseif (ini_get('allow_url_fopen')) { + return new Horde_Http_Request_Fopen(); + } else { + throw new Horde_Http_Exception('No HTTP request backends are available. You must install pecl_http, curl, or enable allow_url_fopen.'); + } + } +} diff --git a/framework/Http/lib/Horde/Http/Exception.php b/framework/Http/lib/Horde/Http/Exception.php new file mode 100644 index 000000000..d4a6df037 --- /dev/null +++ b/framework/Http/lib/Horde/Http/Exception.php @@ -0,0 +1,41 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +class Horde_Http_Exception extends Exception +{ + /** + * Constructor + */ + public function __construct($message = null, $code_or_lasterror = 0) + { + if (is_array($code_or_lasterror)) { + if ($message) { + $message .= $code_or_lasterror['message']; + } else { + $message = $code_or_lasterror['message']; + } + + $this->file = $code_or_lasterror['file']; + $this->line = $code_or_lasterror['line']; + $code = $code_or_lasterror['type']; + } else { + $code = $code_or_lasterror; + } + + parent::__construct($message, $code); + } + +} diff --git a/framework/Http/lib/Horde/Http/Request/Base.php b/framework/Http/lib/Horde/Http/Request/Base.php new file mode 100644 index 000000000..b566524ca --- /dev/null +++ b/framework/Http/lib/Horde/Http/Request/Base.php @@ -0,0 +1,133 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +abstract class Horde_Http_Request_Base +{ + /** + * URI + * @var string + */ + protected $_uri; + + /** + * Request method + * @var string + */ + protected $_method = 'GET'; + + /** + * Request headers + * @var array + */ + protected $_headers = array(); + + /** + * Request data. Can be an array of form data that will be encoded + * automatically, or a raw string + * @var mixed + */ + protected $_data; + + /** + * Proxy server + * @var string + */ + protected $_proxyServer = null; + + /** + * Proxy username + * @var string + */ + protected $_proxyUser = null; + + /** + * Proxy password + * @var string + */ + protected $_proxyPass = null; + + /** + * HTTP timeout + * @var float + */ + protected $_timeout = 5; + + /** + * Constructor + */ + public function __construct($args = array()) + { + foreach ($args as $key => $value) { + $this->$key = $value; + } + } + + /** + * Get an adapter parameter + * + * @param string $name The parameter to get. + * @return mixed Parameter value. + */ + public function __get($name) + { + return isset($this->{'_' . $name}) ? $this->{'_' . $name} : null; + } + + /** + * Set a request parameter + * + * @param string $name The parameter to set. + * @param mixed $value Parameter value. + */ + public function __set($name, $value) + { + switch ($name) { + case 'headers': + $this->setHeaders($value); + break; + } + + $this->{'_' . $name} = $value; + } + + /** + * Set one or more headers + * + * @param mixed $headers A hash of header + value pairs, or a single header name + * @param string $value A header value + */ + public function setHeaders($headers, $value = null) + { + if (!is_array($headers)) { + $headers = array($headers => $value); + } + + foreach ($headers as $header => $value) { + $this->_headers[$header] = $value; + } + } + + /** + * Get the current value of $header + * + * @param string $header Header name to get + * @return string $header's current value + */ + public function getHeader($header) + { + return isset($this->_headers[$header]) ? $this->_headers[$header] : null; + } +} diff --git a/framework/Http/lib/Horde/Http/Request/Curl.php b/framework/Http/lib/Horde/Http/Request/Curl.php new file mode 100644 index 000000000..c13d7796f --- /dev/null +++ b/framework/Http/lib/Horde/Http/Request/Curl.php @@ -0,0 +1,49 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +class Horde_Http_Request_Curl extends Horde_Http_Request_Base +{ + public function __construct() + { + if (!extension_loaded('curl')) { + throw new Horde_Http_Exception('The curl extension is not installed. See http://php.net/curl.installation'); + } + } + + /** + * Send this HTTP request + * + * @return Horde_Http_Response_Base + */ + public function send() + { + $body = tmpfile(); + $headers = tmpfile(); + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $this->uri); + curl_setopt($curl, CURLOPT_FILE, $body); + curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); + curl_setopt($curl, CURLOPT_WRITEHEADER, $headers); + + $result = curl_exec($curl); + + rewind($body); + rewind($headers); + + return new Horde_Http_Response_Curl($this->uri, $body, stream_get_contents($headers)); + } +} diff --git a/framework/Http/lib/Horde/Http/Request/Fopen.php b/framework/Http/lib/Horde/Http/Request/Fopen.php new file mode 100644 index 000000000..06febb722 --- /dev/null +++ b/framework/Http/lib/Horde/Http/Request/Fopen.php @@ -0,0 +1,82 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +class Horde_Http_Request_Fopen extends Horde_Http_Request_Base +{ + public function __construct() + { + if (!ini_get('allow_url_fopen')) { + throw new Horde_Http_Exception('allow_url_fopen must be enabled'); + } + } + + /** + * Send this HTTP request + * + * @return Horde_Http_Response_Base + */ + public function send() + { + $method = $this->method; + $uri = $this->uri; + $headers = $this->headers; + $data = $this->data; + if (is_array($data)) { + $data = http_build_query($data, '', '&'); + } + + $opts = array('http' => array()); + + // Proxy settings - check first, so we can include the correct headers + if ($this->proxyServer) { + $opts['http']['proxy'] = 'tcp://' . $this->proxyServer; + $opts['http']['request_fulluri'] = true; + if ($this->proxyUser && $this->proxyPass) { + $headers['Proxy-Authorization'] = 'Basic ' . base64_encode($this->proxyUser . ':' . $this->proxyPass); + } + } + + // Concatenate the headers + $hdr = array(); + foreach ($headers as $header => $value) { + $hdr[] = $header . ': ' . $value; + } + + // Stream context config. + $opts['http']['method'] = $method; + $opts['http']['header'] = implode("\n", $hdr); + $opts['http']['content'] = $data; + $opts['http']['timeout'] = $this->timeout; + + $context = stream_context_create($opts); + $stream = @fopen($uri, 'rb', false, $context); + if (!$stream) { + $error = error_get_last(); + if (preg_match('/HTTP\/(\d+\.\d+) (\d{3}) (.*)$/', $error['message'], $matches)) { + // Create a Response for the HTTP error code + return new Horde_Http_Response_Fopen($uri, null, $matches[0]); + } else { + throw new Horde_Http_Exception('Problem with ' . $uri . ': ', $error); + } + } + + $meta = stream_get_meta_data($stream); + $headers = isset($meta['wrapper_data']) ? $meta['wrapper_data'] : array(); + + return new Horde_Http_Response_Fopen($uri, $stream, $headers); + } + +} diff --git a/framework/Http/lib/Horde/Http/Request/Mock.php b/framework/Http/lib/Horde/Http/Request/Mock.php new file mode 100644 index 000000000..e247046c3 --- /dev/null +++ b/framework/Http/lib/Horde/Http/Request/Mock.php @@ -0,0 +1,81 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +class Horde_Http_Request_Mock extends Horde_Http_Request_Base +{ + /** + * Array of mock responses + * @var array + */ + protected $_responses = array(); + + /** + * Current mock response + * @var integer + */ + protected $_responseIndex = 0; + + /** + * Send this HTTP request + * + * @return Horde_Http_Response_Base + * + * @TODO make lastRequest work somehow - not sure if this is still an issue. + */ + public function send() + { + if ($this->_responseIndex >= count($this->_responses)) { + $this->_responseIndex = 0; + } + return $this->_responses[$this->_responseIndex++]; + } + + /** + * Set the HTTP response(s) to be returned by this adapter + * + * @param Horde_Http_Response_Base $response + */ + public function setResponse($response) + { + $this->_responses = array($response); + $this->_responseIndex = 0; + } + + /** + * Add another response to the response buffer. + * + * @param string $response + */ + public function addResponse($response) + { + $this->_responses[] = $response; + } + + /** + * Sets the position of the response buffer. Selects which + * response will be returned on the next call to read(). + * + * @param integer $index + */ + public function setResponseIndex($index) + { + if ($index < 0 || $index >= count($this->_responses)) { + throw new OutOfBoundsException; + } + $this->_responseIndex = $index; + } + +} diff --git a/framework/Http/lib/Horde/Http/Request/Peclhttp.php b/framework/Http/lib/Horde/Http/Request/Peclhttp.php new file mode 100644 index 000000000..5547e5aa7 --- /dev/null +++ b/framework/Http/lib/Horde/Http/Request/Peclhttp.php @@ -0,0 +1,59 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +class Horde_Http_Request_Peclhttp extends Horde_Http_Request_Base +{ + public static $methods = array( + 'GET' => HTTP_METH_GET, + 'HEAD' => HTTP_METH_HEAD, + 'POST' => HTTP_METH_POST, + 'PUT' => HTTP_METH_PUT, + 'DELETE' => HTTP_METH_DELETE, + ); + + public function __construct() + { + if (!class_exists('HttpRequest', false)) { + throw new Horde_Http_Exception('The pecl_http extension is not installed. See http://php.net/http.install'); + } + } + + /** + * Send this HTTP request + * + * @return Horde_Http_Response_Base + */ + public function send() + { + $httpRequest = new HttpRequest($this->uri, self::$methods[$this->method]); + $httpRequest->setHeaders($this->headers); + + $data = $this->data; + if (is_array($data)) { + $httpRequest->setPostFields($data); + } else { + $httpRequest->setRawPostData($data); + } + + try { + $httpResponse = $httpRequest->send(); + } catch (HttpException $e) { + throw new Horde_Http_Exception($e->getMessage(), $e->getCode(), $e); + } + + return new Horde_Http_Response_Peclhttp($httpResponse); + } +} diff --git a/framework/Http/lib/Horde/Http/Response/Base.php b/framework/Http/lib/Horde/Http/Response/Base.php new file mode 100644 index 000000000..1826d4fbe --- /dev/null +++ b/framework/Http/lib/Horde/Http/Response/Base.php @@ -0,0 +1,150 @@ + + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ + +/** + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php BSD + * @category Horde + * @package Horde_Http + */ +class Horde_Http_Response_Base +{ + /** + * Fetched URI + * @var string + */ + public $uri; + + /** + * HTTP protocol version that was used + * @var float + */ + public $httpVersion; + + /** + * HTTP response code + * @var integer + */ + public $code; + + /** + * Response headers + * @var array + */ + public $headers; + + /** + * Response body + * @var stream + */ + protected $_stream; + + /** + * Constructor + */ + public function __construct($uri, $stream, $headers = array()) + { + $this->uri = $uri; + $this->_stream = $stream; + $this->_parseHeaders($headers); + } + + /** + * Parse an array of response headers, mindful of line + * continuations, etc. + * + * @param array $headers + * @return array + */ + protected function _parseHeaders($headers) + { + if (!is_array($headers)) { + $headers = explode("\n", $headers); + } + + $lastHeader = null; + foreach ($headers as $headerLine) { + // stream_get_meta returns all headers generated while processing a + // request, including ones for redirects before an eventually successful + // request. We just want the last one, so whenever we hit a new HTTP + // header, throw out anything parsed previously and start over. + if (preg_match('/^HTTP\/(\d.\d) (\d{3})/', $headerLine, $httpMatches)) { + $this->httpVersion = $httpMatches[1]; + $this->code = (int)$httpMatches[2]; + $this->headers = array(); + $lastHeader = null; + } + + $headerLine = trim($headerLine, "\r\n"); + if ($headerLine == '') { + break; + } + if (preg_match('|^([\w-]+):\s+(.+)|', $headerLine, $m)) { + unset($lastHeader); + $headerName = strtolower($m[1]); + $headerValue = $m[2]; + + if (isset($this->headers[$headerName])) { + if (!is_array($this->headers[$headerName])) { + $this->headers[$headerName] = array($this->headers[$headerName]); + } + + $this->headers[$headerName][] = $headerValue; + } else { + $this->headers[$headerName] = $headerValue; + } + $lastHeader = $headerName; + } elseif (preg_match("|^\s+(.+)$|", $headerLine, $m) && !is_null($lastHeader)) { + if (is_array($this->headers[$lastHeader])) { + end($this->headers[$lastHeader]); + $this->headers[$lastHeader][key($this->headers[$lastHeader])] .= $m[1]; + } else { + $this->headers[$lastHeader] .= $m[1]; + } + } + } + } + + /** + * Return the body of the HTTP response. + * + * @return string HTTP response body. + */ + public function getBody() + { + $content = @stream_get_contents($this->_stream); + if ($content === false) { + throw new Horde_Http_Exception('Problem reading data from ' . $this->uri . ': ' . $php_errormsg); + } + return $content; + } + + /** + * Return a stream pointing to the response body that can be used + * with all standard PHP stream functions. + */ + public function getStream() + { + return $this->_stream; + } + + /** + * Get the value of a single response header. + * + * @param string $header Header name to get ('Content-Type', 'Content-Length', etc.). This is case sensitive. + * + * @return string HTTP header value. + */ + public function getHeader($header) + { + return isset($this->headers[$header]) ? $this->headers[$header] : null; + } + +} diff --git a/framework/Http/lib/Horde/Http/Response/Curl.php b/framework/Http/lib/Horde/Http/Response/Curl.php new file mode 100644 index 000000000..e69de29bb diff --git a/framework/Http/lib/Horde/Http/Response/Fopen.php b/framework/Http/lib/Horde/Http/Response/Fopen.php new file mode 100644 index 000000000..e69de29bb diff --git a/framework/Http/lib/Horde/Http/Response/Peclhttp.php b/framework/Http/lib/Horde/Http/Response/Peclhttp.php new file mode 100644 index 000000000..e69de29bb diff --git a/framework/Http/package.xml b/framework/Http/package.xml new file mode 100644 index 000000000..42fe6224c --- /dev/null +++ b/framework/Http/package.xml @@ -0,0 +1,70 @@ + + + Http + pear.horde.org + Horde HTTP libraries + This package provides a set of classes for making HTTP requests. + + + Chuck Hagenbuch + chuck + chuck@horde.org + yes + + 2009-09-04 + + 0.5.0 + 0.5.0 + + + beta + beta + + BSD + * Refactor to support multiple request transports, including Curl and the PECL http extension. + + + + + + + + + + + + + + + + + + + + + + + + + 5.2.0 + + + 1.5.0 + + + + + + + + + + + + + + + diff --git a/framework/Http/test/Horde/Http/AllTests.php b/framework/Http/test/Horde/Http/AllTests.php new file mode 100644 index 000000000..a49c7b815 --- /dev/null +++ b/framework/Http/test/Horde/Http/AllTests.php @@ -0,0 +1,54 @@ +isFile() && preg_match('/Test.php$/', $file->getFilename())) { + $pathname = $file->getPathname(); + require $pathname; + + $class = str_replace(DIRECTORY_SEPARATOR, '_', + preg_replace("/^$baseregexp(.*)\.php/", '\\1', $pathname)); + $suite->addTestSuite('Horde_Http_' . $class); + } + } + + return $suite; + } + +} + +if (PHPUnit_MAIN_METHOD == 'Horde_Http_AllTests::main') { + Horde_Http_AllTests::main(); +} diff --git a/framework/Http/test/Horde/Http/FopenTest.php b/framework/Http/test/Horde/Http/FopenTest.php new file mode 100644 index 000000000..7bb7d4e14 --- /dev/null +++ b/framework/Http/test/Horde/Http/FopenTest.php @@ -0,0 +1,36 @@ + new Horde_Http_Request_Fopen())); + try { + $response = $client->get('http://doesntexist.example.com/'); + $this->fail(); + } catch (Horde_Http_Exception $e) { + } + } + + public function testReturnsResponseInsteadOfExceptionOn404() + { + $client = new Horde_Http_Client(array('request' => new Horde_Http_Request_Fopen())); + $response = $client->get('http://example.com/doesntexist'); + $this->assertEquals(404, $response->code); + } +} diff --git a/framework/Http_Client/examples/Horde/Http/Client/get-example-dot-com.php b/framework/Http_Client/examples/Horde/Http/Client/get-example-dot-com.php deleted file mode 100644 index 1b55e2a7a..000000000 --- a/framework/Http_Client/examples/Horde/Http/Client/get-example-dot-com.php +++ /dev/null @@ -1,20 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -require 'Horde/Http/Client.php'; -require 'Horde/Http/Client/Exception.php'; -require 'Horde/Http/Client/Response.php'; - -$client = new Horde_Http_Client(); -$response = $client->get('http://www.example.com/'); -var_dump($response); -echo $response->getBody(); diff --git a/framework/Http_Client/lib/Horde/Http/Client.php b/framework/Http_Client/lib/Horde/Http/Client.php deleted file mode 100644 index 0e64cdb41..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client.php +++ /dev/null @@ -1,208 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client -{ - /** - * HTTP Adapter to use for transport - * @var Horde_Http_Client_Adapter - */ - protected $_adapter; - - /** - * The current HTTP request - * @var Horde_Http_Client_Request - */ - protected $_request; - - /** - * The previous HTTP request - * @var Horde_Http_Client_Request - */ - protected $_lastRequest; - - /** - * The most recent HTTP response - * @var Horde_Http_Client_Response - */ - protected $_lastResponse; - - /** - * Horde_Http_Client constructor. - * - * @param array $args Any Http_Client settings to initialize in the - * constructor. Available settings are: - * adapter - * adapter.proxyServer - * adapter.proxyUser - * adapter.proxyPass - * adapter.timeout - * request - * request.uri - * request.headers - * request.method - * request.data - */ - public function __construct($args = array()) - { - // Set or create adapter object - if (isset($args['adapter'])) { - $this->_adapter = $args['adapter']; - unset($args['adapter']); - } else { - $this->_adapter = $this->_createBestAvailableAdapter(); - } - - // Set or create request object - if (isset($args['request'])) { - $this->_request = $args['request']; - unset($args['request']); - } else { - $this->_request = new Horde_Http_Client_Request(); - } - - foreach ($args as $key => $val) { - list($object, $objectkey) = explode('.', $key, 2); - $this->$object->$objectkey = $val; - } - } - - /** - * Send a GET request - * - * @return Horde_Http_Client_Response - */ - public function get($uri = null, $headers = array()) - { - return $this->request('GET', $uri, null, $headers); - } - - /** - * Send a POST request - * - * @return Horde_Http_Client_Response - */ - public function post($uri = null, $data = null, $headers = array()) - { - return $this->request('POST', $uri, $data, $headers); - } - - /** - * Send a PUT request - * - * @return Horde_Http_Client_Response - */ - public function put($uri = null, $data = null, $headers = array()) - { - /* @TODO suport method override (X-Method-Override: PUT). */ - return $this->request('PUT', $uri, $data, $headers); - } - - /** - * Send a DELETE request - * - * @return Horde_Http_Client_Response - */ - public function delete($uri = null, $headers = array()) - { - /* @TODO suport method override (X-Method-Override: DELETE). */ - return $this->request('DELETE', $uri, null, $headers); - } - - /** - * Send a HEAD request - * @TODO - * - * @return ? Probably just the status - */ - public function head($uri = null, $headers = array()) - { - return $this->request('HEAD', $uri, null, $headers); - } - - /** - * Send an HTTP request - * - * @param string $method HTTP request method (GET, PUT, etc.) - * @param string $uri URI to request, if different from $this->uri - * @param mixed $data Request data. Can be an array of form data that will be - * encoded automatically, or a raw string. - * @param array $headers Any headers specific to this request. They will - * be combined with $this->_headers, and override - * headers of the same name for this request only. - * - * @return Horde_Http_Client_Response - */ - public function request($method, $uri = null, $data = null, $headers = array()) - { - if ($method !== null) { - $this->request->method = $method; - } - if ($uri !== null) { - $this->request->uri = $uri; - } - if ($data !== null) { - $this->request->data = $data; - } - if (count($headers)) { - $this->request->setHeaders($headers); - } - - $this->_lastRequest = $this->_request; - $this->_lastResponse = $this->_adapter->send($this->_lastRequest); - return $this->_lastResponse; - } - - /** - * Get a client parameter - * - * @param string $name The parameter to get. - * @return mixed Parameter value. - */ - public function __get($name) - { - return isset($this->{'_' . $name}) ? $this->{'_' . $name} : null; - } - - /** - * Set a client parameter - * - * @param string $name The parameter to set. - * @param mixed $value Parameter value. - */ - public function __set($name, $value) - { - $this->{'_' . $name} = $value; - } - - /** - * Find the best available adapter - * - * @return Horde_Http_Client_Adapter - */ - public function _createBestAvailableAdapter() - { - /*if (class_exists('HttpRequest', false)) { - return new Horde_Http_Client_Adapter_Peclhttp(); - } else*/if (extension_loaded('curl')) { - return new Horde_Http_Client_Adapter_Curl(); - } elseif (ini_get('allow_url_fopen')) { - return new Horde_Http_Client_Adapter_Fopen(); - } else { - throw new Horde_Http_Client_Exception('No HTTP adapters are available. You must install pecl_http, curl, or enable allow_url_fopen.'); - } - } -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Base.php b/framework/Http_Client/lib/Horde/Http/Client/Adapter/Base.php deleted file mode 100644 index 95bf25956..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Base.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Adapter_Base -{ - /** - * Proxy server - * @var string - */ - protected $_proxyServer = null; - - /** - * Proxy username - * @var string - */ - protected $_proxyUser = null; - - /** - * Proxy password - * @var string - */ - protected $_proxyPass = null; - - /** - * HTTP timeout - * @var float - */ - protected $_timeout = 5; - - /** - * Constructor - */ - public function __construct($args = array()) - { - foreach ($args as $key => $value) { - $this->$key = $value; - } - } - - /** - * Get an adapter parameter - * - * @param string $name The parameter to get. - * @return mixed Parameter value. - */ - public function __get($name) - { - return isset($this->{'_' . $name}) ? $this->{'_' . $name} : null; - } - - /** - * Set an adapter parameter - * - * @param string $name The parameter to set. - * @param mixed $value Parameter value. - */ - public function __set($name, $value) - { - $this->{'_' . $name} = $value; - } -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Curl.php b/framework/Http_Client/lib/Horde/Http/Client/Adapter/Curl.php deleted file mode 100644 index 1fc720c43..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Curl.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Adapter_Curl extends Horde_Http_Client_Adapter_Base -{ - public function __construct() - { - if (!extension_loaded('curl')) { - throw new Horde_Http_Client_Exception('The curl extension is not installed. See http://php.net/curl.installation'); - } - } - - /** - * Send an HTTP request - * - * @param Horde_Http_Client_Request HTTP Request object - * - * @return Horde_Http_Client_Response - */ - public function send($request) - { - $body = tmpfile(); - $headers = tmpfile(); - - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, $request->uri); - curl_setopt($curl, CURLOPT_FILE, $body); - curl_setopt($curl, CURLOPT_POSTFIELDS, $request->data); - curl_setopt($curl, CURLOPT_WRITEHEADER, $headers); - - $result = curl_exec($curl); - - rewind($body); - rewind($headers); - - return new Horde_Http_Client_Response($request->uri, $body, stream_get_contents($headers)); - } -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Fopen.php b/framework/Http_Client/lib/Horde/Http/Client/Adapter/Fopen.php deleted file mode 100644 index a735e9373..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Fopen.php +++ /dev/null @@ -1,84 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Adapter_Fopen extends Horde_Http_Client_Adapter_Base -{ - public function __construct() - { - if (!ini_get('allow_url_fopen')) { - throw new Horde_Http_Client_Exception('allow_url_fopen must be enabled'); - } - } - - /** - * Send an HTTP request - * - * @param Horde_Http_Client_Request HTTP Request object - * - * @return Horde_Http_Client_Response - */ - public function send($request) - { - $method = $request->method; - $uri = $request->uri; - $headers = $request->headers; - $data = $request->data; - if (is_array($data)) { - $data = http_build_query($data, '', '&'); - } - - $opts = array('http' => array()); - - // Proxy settings - check first, so we can include the correct headers - if ($this->proxyServer) { - $opts['http']['proxy'] = 'tcp://' . $this->proxyServer; - $opts['http']['request_fulluri'] = true; - if ($this->proxyUser && $this->proxyPass) { - $headers['Proxy-Authorization'] = 'Basic ' . base64_encode($this->proxyUser . ':' . $this->proxyPass); - } - } - - // Concatenate the headers - $hdr = array(); - foreach ($headers as $header => $value) { - $hdr[] = $header . ': ' . $value; - } - - // Stream context config. - $opts['http']['method'] = $method; - $opts['http']['header'] = implode("\n", $hdr); - $opts['http']['content'] = $data; - $opts['http']['timeout'] = $this->timeout; - - $context = stream_context_create($opts); - $stream = @fopen($uri, 'rb', false, $context); - if (!$stream) { - $error = error_get_last(); - if (preg_match('/HTTP\/(\d+\.\d+) (\d{3}) (.*)$/', $error['message'], $matches)) { - // Create a Response for the HTTP error code - return new Horde_Http_Client_Response($uri, null, $matches[0]); - } else { - throw new Horde_Http_Client_Exception('Problem with ' . $uri . ': ', $error); - } - } - - $meta = stream_get_meta_data($stream); - $headers = isset($meta['wrapper_data']) ? $meta['wrapper_data'] : array(); - - return new Horde_Http_Client_Response($uri, $stream, $headers); - } - -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Peclhttp.php b/framework/Http_Client/lib/Horde/Http/Client/Adapter/Peclhttp.php deleted file mode 100644 index ad13739ed..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Adapter/Peclhttp.php +++ /dev/null @@ -1,62 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Adapter_Peclhttp extends Horde_Http_Client_Adapter_Base -{ - public static $methods = array( - 'GET' => HTTP_METH_GET, - 'HEAD' => HTTP_METH_HEAD, - 'POST' => HTTP_METH_POST, - 'PUT' => HTTP_METH_PUT, - 'DELETE' => HTTP_METH_DELETE, - ); - - public function __construct() - { - if (!class_exists('HttpRequest', false)) { - throw new Horde_Http_Client_Exception('The pecl_http extension is not installed. See http://php.net/http.install'); - } - } - - /** - * Send an HTTP request - * - * @param Horde_Http_Client_Request HTTP Request object - * - * @return Horde_Http_Client_Response - */ - public function send($request) - { - $httpRequest = new HttpRequest($request->uri, self::$methods[$request->method]); - $httpRequest->setHeaders($request->headers); - - $data = $request->data; - if (is_array($data)) { - $httpRequest->setPostFields($data); - } else { - $httpRequest->setRawPostData($data); - } - - try { - $httpResponse = $httpRequest->send(); - } catch (HttpException $e) { - throw new Horde_Http_Client_Exception($e->getMessage(), $e->getCode(), $e); - } - - /*@TODO build Horde_Http_Client_Response from $httpResponse */ - return $httpResponse; - } -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Exception.php b/framework/Http_Client/lib/Horde/Http/Client/Exception.php deleted file mode 100644 index b191463f5..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Exception.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Exception extends Exception -{ - /** - * Constructor - */ - public function __construct($message = null, $code_or_lasterror = 0) - { - if (is_array($code_or_lasterror)) { - if ($message) { - $message .= $code_or_lasterror['message']; - } else { - $message = $code_or_lasterror['message']; - } - - $this->file = $code_or_lasterror['file']; - $this->line = $code_or_lasterror['line']; - $code = $code_or_lasterror['type']; - } else { - $code = $code_or_lasterror; - } - - parent::__construct($message, $code); - } - -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Mock.php b/framework/Http_Client/lib/Horde/Http/Client/Mock.php deleted file mode 100644 index 1f7f57a34..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Mock.php +++ /dev/null @@ -1,89 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Mock extends Horde_Http_Client -{ - /** - * Array of mock responses - * @var array - */ - protected $_responses = array(); - - /** - * Current mock response - * @var integer - */ - protected $_responseIndex = 0; - - /** - * Send an HTTP request - * - * @param string $method HTTP request method (GET, PUT, etc.) - * @param string $uri URI to request, if different from $this->uri - * @param mixed $data Request data. Can be an array of form data that will be - * encoded automatically, or a raw string. - * @param array $headers Any headers specific to this request. They will - * be combined with $this->_headers, and override - * headers of the same name for this request only. - * - * @return Horde_Http_Client_Response - * - * @TODO make lastRequest work somehow. - */ - public function request($method, $uri = null, $data = null, $headers = array()) - { - if ($this->_responseIndex >= count($this->_responses)) { - $this->_responseIndex = 0; - } - return $this->_responses[$this->_responseIndex++]; - } - - /** - * Set the HTTP response(s) to be returned by this adapter - * - * @param Horde_Http_Client_Response $response - */ - public function setResponse($response) - { - $this->_responses = array($response); - $this->_responseIndex = 0; - } - - /** - * Add another response to the response buffer. - * - * @param string $response - */ - public function addResponse($response) - { - $this->_responses[] = $response; - } - - /** - * Sets the position of the response buffer. Selects which - * response will be returned on the next call to read(). - * - * @param integer $index - */ - public function setResponseIndex($index) - { - if ($index < 0 || $index >= count($this->_responses)) { - throw new OutOfBoundsException; - } - $this->_responseIndex = $index; - } - -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Request.php b/framework/Http_Client/lib/Horde/Http/Client/Request.php deleted file mode 100644 index b5f699a34..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Request.php +++ /dev/null @@ -1,109 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Request -{ - /** - * URI - * @var string - */ - protected $_uri; - - /** - * Request method - * @var string - */ - protected $_method = 'GET'; - - /** - * Request headers - * @var array - */ - protected $_headers = array(); - - /** - * Request data. Can be an array of form data that will be encoded - * automatically, or a raw string - * @var mixed - */ - protected $_data; - - /** - * Constructor - */ - public function __construct($args = array()) - { - foreach ($args as $key => $value) { - $this->$key = $value; - } - } - - /** - * Get a request parameter - * - * @param string $name The parameter to get. - * @return mixed Parameter value. - */ - public function __get($name) - { - return isset($this->{'_' . $name}) ? $this->{'_' . $name} : null; - } - - /** - * Set a request parameter - * - * @param string $name The parameter to set. - * @param mixed $value Parameter value. - */ - public function __set($name, $value) - { - switch ($name) { - case 'headers': - $this->setHeaders($value); - break; - } - - $this->{'_' . $name} = $value; - } - - /** - * Set one or more headers - * - * @param mixed $headers A hash of header + value pairs, or a single header name - * @param string $value A header value - */ - public function setHeaders($headers, $value = null) - { - if (!is_array($headers)) { - $headers = array($headers => $value); - } - - foreach ($headers as $header => $value) { - $this->_headers[$header] = $value; - } - } - - /** - * Get the current value of $header - * - * @param string $header Header name to get - * @return string $header's current value - */ - public function getHeader($header) - { - return isset($this->_headers[$header]) ? $this->_headers[$header] : null; - } -} diff --git a/framework/Http_Client/lib/Horde/Http/Client/Response.php b/framework/Http_Client/lib/Horde/Http/Client/Response.php deleted file mode 100644 index e158e422e..000000000 --- a/framework/Http_Client/lib/Horde/Http/Client/Response.php +++ /dev/null @@ -1,150 +0,0 @@ - - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ - -/** - * @author Chuck Hagenbuch - * @license http://opensource.org/licenses/bsd-license.php BSD - * @category Horde - * @package Horde_Http_Client - */ -class Horde_Http_Client_Response -{ - /** - * Fetched URI - * @var string - */ - public $uri; - - /** - * HTTP protocol version that was used - * @var float - */ - public $httpVersion; - - /** - * HTTP response code - * @var integer - */ - public $code; - - /** - * Response headers - * @var array - */ - public $headers; - - /** - * Response body - * @var stream - */ - protected $_stream; - - /** - * Constructor - */ - public function __construct($uri, $stream, $headers = array()) - { - $this->uri = $uri; - $this->_stream = $stream; - $this->_parseHeaders($headers); - } - - /** - * Parse an array of response headers, mindful of line - * continuations, etc. - * - * @param array $headers - * @return array - */ - protected function _parseHeaders($headers) - { - if (!is_array($headers)) { - $headers = explode("\n", $headers); - } - - $lastHeader = null; - foreach ($headers as $headerLine) { - // stream_get_meta returns all headers generated while processing a - // request, including ones for redirects before an eventually successful - // request. We just want the last one, so whenever we hit a new HTTP - // header, throw out anything parsed previously and start over. - if (preg_match('/^HTTP\/(\d.\d) (\d{3})/', $headerLine, $httpMatches)) { - $this->httpVersion = $httpMatches[1]; - $this->code = (int)$httpMatches[2]; - $this->headers = array(); - $lastHeader = null; - } - - $headerLine = trim($headerLine, "\r\n"); - if ($headerLine == '') { - break; - } - if (preg_match('|^([\w-]+):\s+(.+)|', $headerLine, $m)) { - unset($lastHeader); - $headerName = strtolower($m[1]); - $headerValue = $m[2]; - - if (isset($this->headers[$headerName])) { - if (!is_array($this->headers[$headerName])) { - $this->headers[$headerName] = array($this->headers[$headerName]); - } - - $this->headers[$headerName][] = $headerValue; - } else { - $this->headers[$headerName] = $headerValue; - } - $lastHeader = $headerName; - } elseif (preg_match("|^\s+(.+)$|", $headerLine, $m) && !is_null($lastHeader)) { - if (is_array($this->headers[$lastHeader])) { - end($this->headers[$lastHeader]); - $this->headers[$lastHeader][key($this->headers[$lastHeader])] .= $m[1]; - } else { - $this->headers[$lastHeader] .= $m[1]; - } - } - } - } - - /** - * Return the body of the HTTP response. - * - * @return string HTTP response body. - */ - public function getBody() - { - $content = @stream_get_contents($this->_stream); - if ($content === false) { - throw new Horde_Http_Client_Exception('Problem reading data from ' . $this->uri . ': ' . $php_errormsg); - } - return $content; - } - - /** - * Return a stream pointing to the response body that can be used - * with all standard PHP stream functions. - */ - public function getStream() - { - return $this->_stream; - } - - /** - * Get the value of a single response header. - * - * @param string $header Header name to get ('Content-Type', 'Content-Length', etc.). This is case sensitive. - * - * @return string HTTP header value. - */ - public function getHeader($header) - { - return isset($this->headers[$header]) ? $this->headers[$header] : null; - } - -} diff --git a/framework/Http_Client/package.xml b/framework/Http_Client/package.xml deleted file mode 100644 index fd1c3f24a..000000000 --- a/framework/Http_Client/package.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - Http_Client - pear.horde.org - Horde HTTP client libraries - This package provides a light wrapper around PHP's stream support for performing HTTP requests. - - - Chuck Hagenbuch - chuck - chuck@horde.org - yes - - 2008-09-06 - - 0.3.0 - 0.3.0 - - - beta - beta - - BSD - * Refactor the constructor and setter/getter methods to be more generic. -* Add support for proxy servers. - - - - - - - - - - - - - - - - - - - - - - - - - - - 5.2.0 - - - 1.5.0 - - - - - - - - - - - - - - - - - diff --git a/framework/Http_Client/test/Horde/Http/Client/Adapter/FopenTest.php b/framework/Http_Client/test/Horde/Http/Client/Adapter/FopenTest.php deleted file mode 100644 index f9f5632fa..000000000 --- a/framework/Http_Client/test/Horde/Http/Client/Adapter/FopenTest.php +++ /dev/null @@ -1,36 +0,0 @@ - new Horde_Http_Client_Adapter_Fopen())); - try { - $response = $client->get('http://doesntexist.example.com/'); - $this->fail(); - } catch (Horde_Http_Client_Exception $e) { - } - } - - public function testReturnsResponseInsteadOfExceptionOn404() - { - $client = new Horde_Http_Client(array('adapter' => new Horde_Http_Client_Adapter_Fopen())); - $response = $client->get('http://example.com/doesntexist'); - $this->assertEquals(404, $response->code); - } -} diff --git a/framework/Http_Client/test/Horde/Http/Client/AllTests.php b/framework/Http_Client/test/Horde/Http/Client/AllTests.php deleted file mode 100644 index f2bc7e3bd..000000000 --- a/framework/Http_Client/test/Horde/Http/Client/AllTests.php +++ /dev/null @@ -1,54 +0,0 @@ -isFile() && preg_match('/Test.php$/', $file->getFilename())) { - $pathname = $file->getPathname(); - require $pathname; - - $class = str_replace(DIRECTORY_SEPARATOR, '_', - preg_replace("/^$baseregexp(.*)\.php/", '\\1', $pathname)); - $suite->addTestSuite('Horde_Http_Client_' . $class); - } - } - - return $suite; - } - -} - -if (PHPUnit_MAIN_METHOD == 'Horde_Http_Client_AllTests::main') { - Horde_Http_Client_AllTests::main(); -}