summaryrefslogtreecommitdiff
path: root/zend/library/Zend/Http/Client/Adapter
diff options
context:
space:
mode:
Diffstat (limited to 'zend/library/Zend/Http/Client/Adapter')
-rw-r--r--zend/library/Zend/Http/Client/Adapter/Curl.php510
-rw-r--r--zend/library/Zend/Http/Client/Adapter/Exception.php38
-rw-r--r--zend/library/Zend/Http/Client/Adapter/Interface.php78
-rw-r--r--zend/library/Zend/Http/Client/Adapter/Proxy.php343
-rw-r--r--zend/library/Zend/Http/Client/Adapter/Socket.php543
-rwxr-xr-xzend/library/Zend/Http/Client/Adapter/Stream.php46
-rw-r--r--zend/library/Zend/Http/Client/Adapter/Test.php248
7 files changed, 1806 insertions, 0 deletions
diff --git a/zend/library/Zend/Http/Client/Adapter/Curl.php b/zend/library/Zend/Http/Client/Adapter/Curl.php
new file mode 100644
index 0000000..a66b386
--- /dev/null
+++ b/zend/library/Zend/Http/Client/Adapter/Curl.php
@@ -0,0 +1,510 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Curl.php 24593 2012-01-05 20:35:02Z matthew $
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Uri_Http
+ */
+require_once 'Zend/Uri/Http.php';
+
+/**
+ * @see Zend_Http_Client_Adapter_Interface
+ */
+require_once 'Zend/Http/Client/Adapter/Interface.php';
+/**
+ * @see Zend_Http_Client_Adapter_Stream
+ */
+require_once 'Zend/Http/Client/Adapter/Stream.php';
+
+/**
+ * An adapter class for Zend_Http_Client based on the curl extension.
+ * Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Curl implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream
+{
+ /**
+ * Parameters array
+ *
+ * @var array
+ */
+ protected $_config = array();
+
+ /**
+ * What host/port are we connected to?
+ *
+ * @var array
+ */
+ protected $_connected_to = array(null, null);
+
+ /**
+ * The curl session handle
+ *
+ * @var resource|null
+ */
+ protected $_curl = null;
+
+ /**
+ * List of cURL options that should never be overwritten
+ *
+ * @var array
+ */
+ protected $_invalidOverwritableCurlOptions;
+
+ /**
+ * Response gotten from server
+ *
+ * @var string
+ */
+ protected $_response = null;
+
+ /**
+ * Stream for storing output
+ *
+ * @var resource
+ */
+ protected $out_stream;
+
+ /**
+ * Adapter constructor
+ *
+ * Config is set using setConfig()
+ *
+ * @return void
+ * @throws Zend_Http_Client_Adapter_Exception
+ */
+ public function __construct()
+ {
+ if (!extension_loaded('curl')) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('cURL extension has to be loaded to use this Zend_Http_Client adapter.');
+ }
+ $this->_invalidOverwritableCurlOptions = array(
+ CURLOPT_HTTPGET,
+ CURLOPT_POST,
+ CURLOPT_PUT,
+ CURLOPT_CUSTOMREQUEST,
+ CURLOPT_HEADER,
+ CURLOPT_RETURNTRANSFER,
+ CURLOPT_HTTPHEADER,
+ CURLOPT_POSTFIELDS,
+ CURLOPT_INFILE,
+ CURLOPT_INFILESIZE,
+ CURLOPT_PORT,
+ CURLOPT_MAXREDIRS,
+ CURLOPT_CONNECTTIMEOUT,
+ CURL_HTTP_VERSION_1_1,
+ CURL_HTTP_VERSION_1_0,
+ );
+ }
+
+ /**
+ * Set the configuration array for the adapter
+ *
+ * @throws Zend_Http_Client_Adapter_Exception
+ * @param Zend_Config | array $config
+ * @return Zend_Http_Client_Adapter_Curl
+ */
+ public function setConfig($config = array())
+ {
+ if ($config instanceof Zend_Config) {
+ $config = $config->toArray();
+
+ } elseif (! is_array($config)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Array or Zend_Config object expected, got ' . gettype($config)
+ );
+ }
+
+ if(isset($config['proxy_user']) && isset($config['proxy_pass'])) {
+ $this->setCurlOption(CURLOPT_PROXYUSERPWD, $config['proxy_user'].":".$config['proxy_pass']);
+ unset($config['proxy_user'], $config['proxy_pass']);
+ }
+
+ foreach ($config as $k => $v) {
+ $option = strtolower($k);
+ switch($option) {
+ case 'proxy_host':
+ $this->setCurlOption(CURLOPT_PROXY, $v);
+ break;
+ case 'proxy_port':
+ $this->setCurlOption(CURLOPT_PROXYPORT, $v);
+ break;
+ default:
+ $this->_config[$option] = $v;
+ break;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve the array of all configuration options
+ *
+ * @return array
+ */
+ public function getConfig()
+ {
+ return $this->_config;
+ }
+
+ /**
+ * Direct setter for cURL adapter related options.
+ *
+ * @param string|int $option
+ * @param mixed $value
+ * @return Zend_Http_Adapter_Curl
+ */
+ public function setCurlOption($option, $value)
+ {
+ if (!isset($this->_config['curloptions'])) {
+ $this->_config['curloptions'] = array();
+ }
+ $this->_config['curloptions'][$option] = $value;
+ return $this;
+ }
+
+ /**
+ * Initialize curl
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ * @return void
+ * @throws Zend_Http_Client_Adapter_Exception if unable to connect
+ */
+ public function connect($host, $port = 80, $secure = false)
+ {
+ // If we're already connected, disconnect first
+ if ($this->_curl) {
+ $this->close();
+ }
+
+ // If we are connected to a different server or port, disconnect first
+ if ($this->_curl
+ && is_array($this->_connected_to)
+ && ($this->_connected_to[0] != $host
+ || $this->_connected_to[1] != $port)
+ ) {
+ $this->close();
+ }
+
+ // Do the actual connection
+ $this->_curl = curl_init();
+ if ($port != 80) {
+ curl_setopt($this->_curl, CURLOPT_PORT, intval($port));
+ }
+
+ // Set timeout
+ curl_setopt($this->_curl, CURLOPT_CONNECTTIMEOUT, $this->_config['timeout']);
+
+ // Set Max redirects
+ curl_setopt($this->_curl, CURLOPT_MAXREDIRS, $this->_config['maxredirects']);
+
+ if (!$this->_curl) {
+ $this->close();
+
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to Connect to ' . $host . ':' . $port);
+ }
+
+ if ($secure !== false) {
+ // Behave the same like Zend_Http_Adapter_Socket on SSL options.
+ if (isset($this->_config['sslcert'])) {
+ curl_setopt($this->_curl, CURLOPT_SSLCERT, $this->_config['sslcert']);
+ }
+ if (isset($this->_config['sslpassphrase'])) {
+ curl_setopt($this->_curl, CURLOPT_SSLCERTPASSWD, $this->_config['sslpassphrase']);
+ }
+ }
+
+ // Update connected_to
+ $this->_connected_to = array($host, $port);
+ }
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $uri
+ * @param float $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string $request
+ * @throws Zend_Http_Client_Adapter_Exception If connection fails, connected to wrong host, no PUT file defined, unsupported method, or unsupported cURL option
+ */
+ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $body = '')
+ {
+ // Make sure we're properly connected
+ if (!$this->_curl) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
+ }
+
+ if ($this->_connected_to[0] != $uri->getHost() || $this->_connected_to[1] != $uri->getPort()) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong host");
+ }
+
+ // set URL
+ curl_setopt($this->_curl, CURLOPT_URL, $uri->__toString());
+
+ // ensure correct curl call
+ $curlValue = true;
+ switch ($method) {
+ case Zend_Http_Client::GET:
+ $curlMethod = CURLOPT_HTTPGET;
+ break;
+
+ case Zend_Http_Client::POST:
+ $curlMethod = CURLOPT_POST;
+ break;
+
+ case Zend_Http_Client::PUT:
+ // There are two different types of PUT request, either a Raw Data string has been set
+ // or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
+ if(is_resource($body)) {
+ $this->_config['curloptions'][CURLOPT_INFILE] = $body;
+ }
+ if (isset($this->_config['curloptions'][CURLOPT_INFILE])) {
+ // Now we will probably already have Content-Length set, so that we have to delete it
+ // from $headers at this point:
+ foreach ($headers AS $k => $header) {
+ if (preg_match('/Content-Length:\s*(\d+)/i', $header, $m)) {
+ if(is_resource($body)) {
+ $this->_config['curloptions'][CURLOPT_INFILESIZE] = (int)$m[1];
+ }
+ unset($headers[$k]);
+ }
+ }
+
+ if (!isset($this->_config['curloptions'][CURLOPT_INFILESIZE])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Cannot set a file-handle for cURL option CURLOPT_INFILE without also setting its size in CURLOPT_INFILESIZE.");
+ }
+
+ if(is_resource($body)) {
+ $body = '';
+ }
+
+ $curlMethod = CURLOPT_PUT;
+ } else {
+ $curlMethod = CURLOPT_CUSTOMREQUEST;
+ $curlValue = "PUT";
+ }
+ break;
+
+ case Zend_Http_Client::DELETE:
+ $curlMethod = CURLOPT_CUSTOMREQUEST;
+ $curlValue = "DELETE";
+ break;
+
+ case Zend_Http_Client::OPTIONS:
+ $curlMethod = CURLOPT_CUSTOMREQUEST;
+ $curlValue = "OPTIONS";
+ break;
+
+ case Zend_Http_Client::TRACE:
+ $curlMethod = CURLOPT_CUSTOMREQUEST;
+ $curlValue = "TRACE";
+ break;
+
+ case Zend_Http_Client::HEAD:
+ $curlMethod = CURLOPT_CUSTOMREQUEST;
+ $curlValue = "HEAD";
+ break;
+
+ default:
+ // For now, through an exception for unsupported request methods
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Method currently not supported");
+ }
+
+ if(is_resource($body) && $curlMethod != CURLOPT_PUT) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception("Streaming requests are allowed only with PUT");
+ }
+
+ // get http version to use
+ $curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
+
+ // mark as HTTP request and set HTTP method
+ curl_setopt($this->_curl, $curlHttp, true);
+ curl_setopt($this->_curl, $curlMethod, $curlValue);
+
+ if($this->out_stream) {
+ // headers will be read into the response
+ curl_setopt($this->_curl, CURLOPT_HEADER, false);
+ curl_setopt($this->_curl, CURLOPT_HEADERFUNCTION, array($this, "readHeader"));
+ // and data will be written into the file
+ curl_setopt($this->_curl, CURLOPT_FILE, $this->out_stream);
+ } else {
+ // ensure headers are also returned
+ curl_setopt($this->_curl, CURLOPT_HEADER, true);
+
+ // ensure actual response is returned
+ curl_setopt($this->_curl, CURLOPT_RETURNTRANSFER, true);
+ }
+
+ // set additional headers
+ $headers['Accept'] = '';
+ curl_setopt($this->_curl, CURLOPT_HTTPHEADER, $headers);
+
+ /**
+ * Make sure POSTFIELDS is set after $curlMethod is set:
+ * @link http://de2.php.net/manual/en/function.curl-setopt.php#81161
+ */
+ if ($method == Zend_Http_Client::POST) {
+ curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
+ } elseif ($curlMethod == CURLOPT_PUT) {
+ // this covers a PUT by file-handle:
+ // Make the setting of this options explicit (rather than setting it through the loop following a bit lower)
+ // to group common functionality together.
+ curl_setopt($this->_curl, CURLOPT_INFILE, $this->_config['curloptions'][CURLOPT_INFILE]);
+ curl_setopt($this->_curl, CURLOPT_INFILESIZE, $this->_config['curloptions'][CURLOPT_INFILESIZE]);
+ unset($this->_config['curloptions'][CURLOPT_INFILE]);
+ unset($this->_config['curloptions'][CURLOPT_INFILESIZE]);
+ } elseif ($method == Zend_Http_Client::PUT) {
+ // This is a PUT by a setRawData string, not by file-handle
+ curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
+ } elseif ($method == Zend_Http_Client::DELETE) {
+ // This is a DELETE by a setRawData string
+ curl_setopt($this->_curl, CURLOPT_POSTFIELDS, $body);
+ }
+
+ // set additional curl options
+ if (isset($this->_config['curloptions'])) {
+ foreach ((array)$this->_config['curloptions'] as $k => $v) {
+ if (!in_array($k, $this->_invalidOverwritableCurlOptions)) {
+ if (curl_setopt($this->_curl, $k, $v) == false) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception(sprintf("Unknown or erroreous cURL option '%s' set", $k));
+ }
+ }
+ }
+ }
+
+ // send the request
+ $response = curl_exec($this->_curl);
+
+ // if we used streaming, headers are already there
+ if(!is_resource($this->out_stream)) {
+ $this->_response = $response;
+ }
+
+ $request = curl_getinfo($this->_curl, CURLINFO_HEADER_OUT);
+ $request .= $body;
+
+ if (empty($this->_response)) {
+ require_once 'Zend/Http/Client/Exception.php';
+ throw new Zend_Http_Client_Exception("Error in cURL request: " . curl_error($this->_curl));
+ }
+
+ // cURL automatically decodes chunked-messages, this means we have to disallow the Zend_Http_Response to do it again
+ if (stripos($this->_response, "Transfer-Encoding: chunked\r\n")) {
+ $this->_response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $this->_response);
+ }
+
+ // Eliminate multiple HTTP responses.
+ do {
+ $parts = preg_split('|(?:\r?\n){2}|m', $this->_response, 2);
+ $again = false;
+
+ if (isset($parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi", $parts[1])) {
+ $this->_response = $parts[1];
+ $again = true;
+ }
+ } while ($again);
+
+ // cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
+ if (stripos($this->_response, "HTTP/1.0 200 Connection established\r\n\r\n") !== false) {
+ $this->_response = str_ireplace("HTTP/1.0 200 Connection established\r\n\r\n", '', $this->_response);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Return read response from server
+ *
+ * @return string
+ */
+ public function read()
+ {
+ return $this->_response;
+ }
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close()
+ {
+ if(is_resource($this->_curl)) {
+ curl_close($this->_curl);
+ }
+ $this->_curl = null;
+ $this->_connected_to = array(null, null);
+ }
+
+ /**
+ * Get cUrl Handle
+ *
+ * @return resource
+ */
+ public function getHandle()
+ {
+ return $this->_curl;
+ }
+
+ /**
+ * Set output stream for the response
+ *
+ * @param resource $stream
+ * @return Zend_Http_Client_Adapter_Socket
+ */
+ public function setOutputStream($stream)
+ {
+ $this->out_stream = $stream;
+ return $this;
+ }
+
+ /**
+ * Header reader function for CURL
+ *
+ * @param resource $curl
+ * @param string $header
+ * @return int
+ */
+ public function readHeader($curl, $header)
+ {
+ $this->_response .= $header;
+ return strlen($header);
+ }
+}
diff --git a/zend/library/Zend/Http/Client/Adapter/Exception.php b/zend/library/Zend/Http/Client/Adapter/Exception.php
new file mode 100644
index 0000000..6416175
--- /dev/null
+++ b/zend/library/Zend/Http/Client/Adapter/Exception.php
@@ -0,0 +1,38 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter_Exception
+ * @version $Id: Exception.php 24593 2012-01-05 20:35:02Z matthew $
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Http_Client_Exception
+ */
+require_once 'Zend/Http/Client/Exception.php';
+
+/**
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Exception extends Zend_Http_Client_Exception
+{
+ const READ_TIMEOUT = 1000;
+}
diff --git a/zend/library/Zend/Http/Client/Adapter/Interface.php b/zend/library/Zend/Http/Client/Adapter/Interface.php
new file mode 100644
index 0000000..1a0eddd
--- /dev/null
+++ b/zend/library/Zend/Http/Client/Adapter/Interface.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Interface.php 24593 2012-01-05 20:35:02Z matthew $
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * An interface description for Zend_Http_Client_Adapter classes.
+ *
+ * These classes are used as connectors for Zend_Http_Client, performing the
+ * tasks of connecting, writing, reading and closing connection to the server.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Http_Client_Adapter_Interface
+{
+ /**
+ * Set the configuration array for the adapter
+ *
+ * @param array $config
+ */
+ public function setConfig($config = array());
+
+ /**
+ * Connect to the remote server
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ */
+ public function connect($host, $port = 80, $secure = false);
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $url
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as text
+ */
+ public function write($method, $url, $http_ver = '1.1', $headers = array(), $body = '');
+
+ /**
+ * Read response from server
+ *
+ * @return string
+ */
+ public function read();
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close();
+}
diff --git a/zend/library/Zend/Http/Client/Adapter/Proxy.php b/zend/library/Zend/Http/Client/Adapter/Proxy.php
new file mode 100644
index 0000000..2ca3012
--- /dev/null
+++ b/zend/library/Zend/Http/Client/Adapter/Proxy.php
@@ -0,0 +1,343 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Proxy.php 25273 2013-03-06 08:02:21Z frosch $
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Uri_Http
+ */
+require_once 'Zend/Uri/Http.php';
+/**
+ * @see Zend_Http_Client
+ */
+require_once 'Zend/Http/Client.php';
+/**
+ * @see Zend_Http_Client_Adapter_Socket
+ */
+require_once 'Zend/Http/Client/Adapter/Socket.php';
+
+/**
+ * HTTP Proxy-supporting Zend_Http_Client adapter class, based on the default
+ * socket based adapter.
+ *
+ * Should be used if proxy HTTP access is required. If no proxy is set, will
+ * fall back to Zend_Http_Client_Adapter_Socket behavior. Just like the
+ * default Socket adapter, this adapter does not require any special extensions
+ * installed.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Proxy extends Zend_Http_Client_Adapter_Socket
+{
+ /**
+ * Parameters array
+ *
+ * @var array
+ */
+ protected $config = array(
+ 'ssltransport' => 'ssl',
+ 'sslcert' => null,
+ 'sslpassphrase' => null,
+ 'sslusecontext' => false,
+ 'proxy_host' => '',
+ 'proxy_port' => 8080,
+ 'proxy_user' => '',
+ 'proxy_pass' => '',
+ 'proxy_auth' => Zend_Http_Client::AUTH_BASIC,
+ 'persistent' => false,
+ );
+
+ /**
+ * Whether HTTPS CONNECT was already negotiated with the proxy or not
+ *
+ * @var boolean
+ */
+ protected $negotiated = false;
+
+ /**
+ * Stores the last CONNECT handshake request
+ *
+ * @var string
+ */
+ protected $connectHandshakeRequest;
+
+ /**
+ * Connect to the remote server
+ *
+ * Will try to connect to the proxy server. If no proxy was set, will
+ * fall back to the target server (behave like regular Socket adapter)
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ */
+ public function connect($host, $port = 80, $secure = false)
+ {
+ // If no proxy is set, fall back to Socket adapter
+ if (!$this->config['proxy_host']) {
+ return parent::connect($host, $port, $secure);
+ }
+
+ /* Url might require stream context even if proxy connection doesn't */
+ if ($secure) {
+ $this->config['sslusecontext'] = true;
+ }
+
+ // Connect (a non-secure connection) to the proxy server
+ return parent::connect(
+ $this->config['proxy_host'],
+ $this->config['proxy_port'],
+ false
+ );
+ }
+
+ /**
+ * Send request to the proxy server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $uri
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as string
+ * @throws Zend_Http_Client_Adapter_Exception
+ */
+ public function write(
+ $method, $uri, $http_ver = '1.1', $headers = array(), $body = ''
+ )
+ {
+ // If no proxy is set, fall back to default Socket adapter
+ if (!$this->config['proxy_host']) {
+ return parent::write($method, $uri, $http_ver, $headers, $body);
+ }
+
+ // Make sure we're properly connected
+ if (!$this->socket) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Trying to write but we are not connected'
+ );
+ }
+
+ $host = $this->config['proxy_host'];
+ $port = $this->config['proxy_port'];
+
+ if ($this->connected_to[0] != "tcp://$host"
+ || $this->connected_to[1] != $port
+ ) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Trying to write but we are connected to the wrong proxy server'
+ );
+ }
+
+ // Add Proxy-Authorization header
+ if ($this->config['proxy_user']) {
+ // Check to see if one already exists
+ $hasProxyAuthHeader = false;
+ foreach ($headers as $k => $v) {
+ if ((string) $k == 'proxy-authorization'
+ || preg_match("/^proxy-authorization:/i", $v)
+ ) {
+ $hasProxyAuthHeader = true;
+ break;
+ }
+ }
+ if (!$hasProxyAuthHeader) {
+ $headers[] = 'Proxy-authorization: '
+ . Zend_Http_Client::encodeAuthHeader(
+ $this->config['proxy_user'],
+ $this->config['proxy_pass'], $this->config['proxy_auth']
+ );
+ }
+ }
+
+ // if we are proxying HTTPS, preform CONNECT handshake with the proxy
+ if ($uri->getScheme() == 'https' && (!$this->negotiated)) {
+ $this->connectHandshake(
+ $uri->getHost(), $uri->getPort(), $http_ver, $headers
+ );
+ $this->negotiated = true;
+ }
+
+ // Save request method for later
+ $this->method = $method;
+
+ // Build request headers
+ if ($this->negotiated) {
+ $path = $uri->getPath();
+ if ($uri->getQuery()) {
+ $path .= '?' . $uri->getQuery();
+ }
+ $request = "$method $path HTTP/$http_ver\r\n";
+ } else {
+ $request = "$method $uri HTTP/$http_ver\r\n";
+ }
+
+ // Add all headers to the request string
+ foreach ($headers as $k => $v) {
+ if (is_string($k)) $v = "$k: $v";
+ $request .= "$v\r\n";
+ }
+
+ if(is_resource($body)) {
+ $request .= "\r\n";
+ } else {
+ // Add the request body
+ $request .= "\r\n" . $body;
+ }
+
+ // Send the request
+ if (!@fwrite($this->socket, $request)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Error writing request to proxy server'
+ );
+ }
+
+ if(is_resource($body)) {
+ if(stream_copy_to_stream($body, $this->socket) == 0) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Error writing request to server'
+ );
+ }
+ }
+
+ return $request;
+ }
+
+ /**
+ * Preform handshaking with HTTPS proxy using CONNECT method
+ *
+ * @param string $host
+ * @param integer $port
+ * @param string $http_ver
+ * @param array $headers
+ * @return void
+ * @throws Zend_Http_Client_Adapter_Exception
+ */
+ protected function connectHandshake(
+ $host, $port = 443, $http_ver = '1.1', array &$headers = array()
+ )
+ {
+ $request = "CONNECT $host:$port HTTP/$http_ver\r\n" .
+ "Host: " . $this->config['proxy_host'] . "\r\n";
+
+ // Process provided headers, including important ones to CONNECT request
+ foreach ($headers as $k => $v) {
+ switch (strtolower(substr($v,0,strpos($v,':')))) {
+ case 'proxy-authorization':
+ // break intentionally omitted
+
+ case 'user-agent':
+ $request .= $v . "\r\n";
+ break;
+
+ default:
+ break;
+ }
+ }
+ $request .= "\r\n";
+
+ // @see ZF-3189
+ $this->connectHandshakeRequest = $request;
+
+ // Send the request
+ if (!@fwrite($this->socket, $request)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Error writing request to proxy server'
+ );
+ }
+
+ // Read response headers only
+ $response = '';
+ $gotStatus = false;
+ while ($line = @fgets($this->socket)) {
+ $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
+ if ($gotStatus) {
+ $response .= $line;
+ if (!chop($line)) {
+ break;
+ }
+ }
+ }
+
+ // Check that the response from the proxy is 200
+ if (Zend_Http_Response::extractCode($response) != 200) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Unable to connect to HTTPS proxy. Server response: ' . $response
+ );
+ }
+
+ // If all is good, switch socket to secure mode. We have to fall back
+ // through the different modes
+ $modes = array(
+ STREAM_CRYPTO_METHOD_TLS_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
+ STREAM_CRYPTO_METHOD_SSLv2_CLIENT
+ );
+
+ $success = false;
+ foreach($modes as $mode) {
+ $success = stream_socket_enable_crypto($this->socket, true, $mode);
+ if ($success) {
+ break;
+ }
+ }
+
+ if (!$success) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Unable to connect to HTTPS server through proxy: could not '
+ . 'negotiate secure connection.'
+ );
+ }
+ }
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close()
+ {
+ parent::close();
+ $this->negotiated = false;
+ }
+
+ /**
+ * Destructor: make sure the socket is disconnected
+ *
+ */
+ public function __destruct()
+ {
+ if ($this->socket) {
+ $this->close();
+ }
+ }
+}
diff --git a/zend/library/Zend/Http/Client/Adapter/Socket.php b/zend/library/Zend/Http/Client/Adapter/Socket.php
new file mode 100644
index 0000000..b2a0e9b
--- /dev/null
+++ b/zend/library/Zend/Http/Client/Adapter/Socket.php
@@ -0,0 +1,543 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Socket.php 24593 2012-01-05 20:35:02Z matthew $
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Uri_Http
+ */
+require_once 'Zend/Uri/Http.php';
+/**
+ * @see Zend_Http_Client_Adapter_Interface
+ */
+require_once 'Zend/Http/Client/Adapter/Interface.php';
+/**
+ * @see Zend_Http_Client_Adapter_Stream
+ */
+require_once 'Zend/Http/Client/Adapter/Stream.php';
+
+/**
+ * A sockets based (stream_socket_client) adapter class for Zend_Http_Client. Can be used
+ * on almost every PHP environment, and does not require any special extensions.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream
+{
+ /**
+ * The socket for server connection
+ *
+ * @var resource|null
+ */
+ protected $socket = null;
+
+ /**
+ * What host/port are we connected to?
+ *
+ * @var array
+ */
+ protected $connected_to = array(null, null);
+
+ /**
+ * Stream for storing output
+ *
+ * @var resource
+ */
+ protected $out_stream = null;
+
+ /**
+ * Parameters array
+ *
+ * @var array
+ */
+ protected $config = array(
+ 'persistent' => false,
+ 'ssltransport' => 'ssl',
+ 'sslcert' => null,
+ 'sslpassphrase' => null,
+ 'sslusecontext' => false
+ );
+
+ /**
+ * Request method - will be set by write() and might be used by read()
+ *
+ * @var string
+ */
+ protected $method = null;
+
+ /**
+ * Stream context
+ *
+ * @var resource
+ */
+ protected $_context = null;
+
+ /**
+ * Adapter constructor, currently empty. Config is set using setConfig()
+ *
+ */
+ public function __construct()
+ {
+ }
+
+ /**
+ * Set the configuration array for the adapter
+ *
+ * @param Zend_Config | array $config
+ */
+ public function setConfig($config = array())
+ {
+ if ($config instanceof Zend_Config) {
+ $config = $config->toArray();
+
+ } elseif (! is_array($config)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Array or Zend_Config object expected, got ' . gettype($config)
+ );
+ }
+
+ foreach ($config as $k => $v) {
+ $this->config[strtolower($k)] = $v;
+ }
+ }
+
+ /**
+ * Retrieve the array of all configuration options
+ *
+ * @return array
+ */
+ public function getConfig()
+ {
+ return $this->config;
+ }
+
+ /**
+ * Set the stream context for the TCP connection to the server
+ *
+ * Can accept either a pre-existing stream context resource, or an array
+ * of stream options, similar to the options array passed to the
+ * stream_context_create() PHP function. In such case a new stream context
+ * will be created using the passed options.
+ *
+ * @since Zend Framework 1.9
+ *
+ * @param mixed $context Stream context or array of context options
+ * @return Zend_Http_Client_Adapter_Socket
+ */
+ public function setStreamContext($context)
+ {
+ if (is_resource($context) && get_resource_type($context) == 'stream-context') {
+ $this->_context = $context;
+
+ } elseif (is_array($context)) {
+ $this->_context = stream_context_create($context);
+
+ } else {
+ // Invalid parameter
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ "Expecting either a stream context resource or array, got " . gettype($context)
+ );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get the stream context for the TCP connection to the server.
+ *
+ * If no stream context is set, will create a default one.
+ *
+ * @return resource
+ */
+ public function getStreamContext()
+ {
+ if (! $this->_context) {
+ $this->_context = stream_context_create();
+ }
+
+ return $this->_context;
+ }
+
+ /**
+ * Connect to the remote server
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ */
+ public function connect($host, $port = 80, $secure = false)
+ {
+ // If the URI should be accessed via SSL, prepend the Hostname with ssl://
+ $host = ($secure ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
+
+ // If we are connected to the wrong host, disconnect first
+ if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
+ if (is_resource($this->socket)) $this->close();
+ }
+
+ // Now, if we are not connected, connect
+ if (! is_resource($this->socket) || ! $this->config['keepalive']) {
+ $context = $this->getStreamContext();
+ if ($secure || $this->config['sslusecontext']) {
+ if ($this->config['sslcert'] !== null) {
+ if (! stream_context_set_option($context, 'ssl', 'local_cert',
+ $this->config['sslcert'])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to set sslcert option');
+ }
+ }
+ if ($this->config['sslpassphrase'] !== null) {
+ if (! stream_context_set_option($context, 'ssl', 'passphrase',
+ $this->config['sslpassphrase'])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option');
+ }
+ }
+ }
+
+ $flags = STREAM_CLIENT_CONNECT;
+ if ($this->config['persistent']) $flags |= STREAM_CLIENT_PERSISTENT;
+
+ $this->socket = @stream_socket_client($host . ':' . $port,
+ $errno,
+ $errstr,
+ (int) $this->config['timeout'],
+ $flags,
+ $context);
+
+ if (! $this->socket) {
+ $this->close();
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Unable to Connect to ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr);
+ }
+
+ // Set the stream timeout
+ if (! stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout');
+ }
+
+ // Update connected_to
+ $this->connected_to = array($host, $port);
+ }
+ }
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $uri
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as string
+ */
+ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
+ {
+ // Make sure we're properly connected
+ if (! $this->socket) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected');
+ }
+
+ $host = $uri->getHost();
+ $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
+ if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong host');
+ }
+
+ // Save request method for later
+ $this->method = $method;
+
+ // Build request headers
+ $path = $uri->getPath();
+ if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
+ $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
+ foreach ($headers as $k => $v) {
+ if (is_string($k)) $v = ucfirst($k) . ": $v";
+ $request .= "$v\r\n";
+ }
+
+ if(is_resource($body)) {
+ $request .= "\r\n";
+ } else {
+ // Add the request body
+ $request .= "\r\n" . $body;
+ }
+
+ // Send the request
+ if (! @fwrite($this->socket, $request)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
+ }
+
+ if(is_resource($body)) {
+ if(stream_copy_to_stream($body, $this->socket) == 0) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
+ }
+ }
+
+ return $request;
+ }
+
+ /**
+ * Read response from server
+ *
+ * @return string
+ */
+ public function read()
+ {
+ // First, read headers only
+ $response = '';
+ $gotStatus = false;
+
+ while (($line = @fgets($this->socket)) !== false) {
+ $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
+ if ($gotStatus) {
+ $response .= $line;
+ if (rtrim($line) === '') break;
+ }
+ }
+
+ $this->_checkSocketReadTimeout();
+
+ $statusCode = Zend_Http_Response::extractCode($response);
+
+ // Handle 100 and 101 responses internally by restarting the read again
+ if ($statusCode == 100 || $statusCode == 101) return $this->read();
+
+ // Check headers to see what kind of connection / transfer encoding we have
+ $headers = Zend_Http_Response::extractHeaders($response);
+
+ /**
+ * Responses to HEAD requests and 204 or 304 responses are not expected
+ * to have a body - stop reading here
+ */
+ if ($statusCode == 304 || $statusCode == 204 ||
+ $this->method == Zend_Http_Client::HEAD) {
+
+ // Close the connection if requested to do so by the server
+ if (isset($headers['connection']) && $headers['connection'] == 'close') {
+ $this->close();
+ }
+ return $response;
+ }
+
+ // If we got a 'transfer-encoding: chunked' header
+ if (isset($headers['transfer-encoding'])) {
+
+ if (strtolower($headers['transfer-encoding']) == 'chunked') {
+
+ do {
+ $line = @fgets($this->socket);
+ $this->_checkSocketReadTimeout();
+
+ $chunk = $line;
+
+ // Figure out the next chunk size
+ $chunksize = trim($line);
+ if (! ctype_xdigit($chunksize)) {
+ $this->close();
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Invalid chunk size "' .
+ $chunksize . '" unable to read chunked body');
+ }
+
+ // Convert the hexadecimal value to plain integer
+ $chunksize = hexdec($chunksize);
+
+ // Read next chunk
+ $read_to = ftell($this->socket) + $chunksize;
+
+ do {
+ $current_pos = ftell($this->socket);
+ if ($current_pos >= $read_to) break;
+
+ if($this->out_stream) {
+ if(stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) {
+ $this->_checkSocketReadTimeout();
+ break;
+ }
+ } else {
+ $line = @fread($this->socket, $read_to - $current_pos);
+ if ($line === false || strlen($line) === 0) {
+ $this->_checkSocketReadTimeout();
+ break;
+ }
+ $chunk .= $line;
+ }
+ } while (! feof($this->socket));
+
+ $chunk .= @fgets($this->socket);
+ $this->_checkSocketReadTimeout();
+
+ if(!$this->out_stream) {
+ $response .= $chunk;
+ }
+ } while ($chunksize > 0);
+ } else {
+ $this->close();
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' .
+ $headers['transfer-encoding'] . '" transfer encoding');
+ }
+
+ // We automatically decode chunked-messages when writing to a stream
+ // this means we have to disallow the Zend_Http_Response to do it again
+ if ($this->out_stream) {
+ $response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response);
+ }
+ // Else, if we got the content-length header, read this number of bytes
+ } elseif (isset($headers['content-length'])) {
+
+ // If we got more than one Content-Length header (see ZF-9404) use
+ // the last value sent
+ if (is_array($headers['content-length'])) {
+ $contentLength = $headers['content-length'][count($headers['content-length']) - 1];
+ } else {
+ $contentLength = $headers['content-length'];
+ }
+
+ $current_pos = ftell($this->socket);
+ $chunk = '';
+
+ for ($read_to = $current_pos + $contentLength;
+ $read_to > $current_pos;
+ $current_pos = ftell($this->socket)) {
+
+ if($this->out_stream) {
+ if(@stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) {
+ $this->_checkSocketReadTimeout();
+ break;
+ }
+ } else {
+ $chunk = @fread($this->socket, $read_to - $current_pos);
+ if ($chunk === false || strlen($chunk) === 0) {
+ $this->_checkSocketReadTimeout();
+ break;
+ }
+
+ $response .= $chunk;
+ }
+
+ // Break if the connection ended prematurely
+ if (feof($this->socket)) break;
+ }
+
+ // Fallback: just read the response until EOF
+ } else {
+
+ do {
+ if($this->out_stream) {
+ if(@stream_copy_to_stream($this->socket, $this->out_stream) == 0) {
+ $this->_checkSocketReadTimeout();
+ break;
+ }
+ } else {
+ $buff = @fread($this->socket, 8192);
+ if ($buff === false || strlen($buff) === 0) {
+ $this->_checkSocketReadTimeout();
+ break;
+ } else {
+ $response .= $buff;
+ }
+ }
+
+ } while (feof($this->socket) === false);
+
+ $this->close();
+ }
+
+ // Close the connection if requested to do so by the server
+ if (isset($headers['connection']) && $headers['connection'] == 'close') {
+ $this->close();
+ }
+
+ return $response;
+ }
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close()
+ {
+ if (is_resource($this->socket)) @fclose($this->socket);
+ $this->socket = null;
+ $this->connected_to = array(null, null);
+ }
+
+ /**
+ * Check if the socket has timed out - if so close connection and throw
+ * an exception
+ *
+ * @throws Zend_Http_Client_Adapter_Exception with READ_TIMEOUT code
+ */
+ protected function _checkSocketReadTimeout()
+ {
+ if ($this->socket) {
+ $info = stream_get_meta_data($this->socket);
+ $timedout = $info['timed_out'];
+ if ($timedout) {
+ $this->close();
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ "Read timed out after {$this->config['timeout']} seconds",
+ Zend_Http_Client_Adapter_Exception::READ_TIMEOUT
+ );
+ }
+ }
+ }
+
+ /**
+ * Set output stream for the response
+ *
+ * @param resource $stream
+ * @return Zend_Http_Client_Adapter_Socket
+ */
+ public function setOutputStream($stream)
+ {
+ $this->out_stream = $stream;
+ return $this;
+ }
+
+ /**
+ * Destructor: make sure the socket is disconnected
+ *
+ * If we are in persistent TCP mode, will not close the connection
+ *
+ */
+ public function __destruct()
+ {
+ if (! $this->config['persistent']) {
+ if ($this->socket) $this->close();
+ }
+ }
+}
diff --git a/zend/library/Zend/Http/Client/Adapter/Stream.php b/zend/library/Zend/Http/Client/Adapter/Stream.php
new file mode 100755
index 0000000..98f4410
--- /dev/null
+++ b/zend/library/Zend/Http/Client/Adapter/Stream.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Stream.php 24593 2012-01-05 20:35:02Z matthew $
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * An interface description for Zend_Http_Client_Adapter_Stream classes.
+ *
+ * This interface decribes Zend_Http_Client_Adapter which supports streaming.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+interface Zend_Http_Client_Adapter_Stream
+{
+ /**
+ * Set output stream
+ *
+ * This function sets output stream where the result will be stored.
+ *
+ * @param resource $stream Stream to write the output to
+ *
+ */
+ public function setOutputStream($stream);
+}
diff --git a/zend/library/Zend/Http/Client/Adapter/Test.php b/zend/library/Zend/Http/Client/Adapter/Test.php
new file mode 100644
index 0000000..aa041af
--- /dev/null
+++ b/zend/library/Zend/Http/Client/Adapter/Test.php
@@ -0,0 +1,248 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @version $Id: Test.php 24593 2012-01-05 20:35:02Z matthew $
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+
+/**
+ * @see Zend_Uri_Http
+ */
+require_once 'Zend/Uri/Http.php';
+/**
+ * @see Zend_Http_Response
+ */
+require_once 'Zend/Http/Response.php';
+/**
+ * @see Zend_Http_Client_Adapter_Interface
+ */
+require_once 'Zend/Http/Client/Adapter/Interface.php';
+
+/**
+ * A testing-purposes adapter.
+ *
+ * Should be used to test all components that rely on Zend_Http_Client,
+ * without actually performing an HTTP request. You should instantiate this
+ * object manually, and then set it as the client's adapter. Then, you can
+ * set the expected response using the setResponse() method.
+ *
+ * @category Zend
+ * @package Zend_Http
+ * @subpackage Client_Adapter
+ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Http_Client_Adapter_Test implements Zend_Http_Client_Adapter_Interface
+{
+ /**
+ * Parameters array
+ *
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * Buffer of responses to be returned by the read() method. Can be
+ * set using setResponse() and addResponse().
+ *
+ * @var array
+ */
+ protected $responses = array("HTTP/1.1 400 Bad Request\r\n\r\n");
+
+ /**
+ * Current position in the response buffer
+ *
+ * @var integer
+ */
+ protected $responseIndex = 0;
+
+ /**
+ * Wether or not the next request will fail with an exception
+ *
+ * @var boolean
+ */
+ protected $_nextRequestWillFail = false;
+
+ /**
+ * Adapter constructor, currently empty. Config is set using setConfig()
+ *
+ */
+ public function __construct()
+ { }
+
+ /**
+ * Set the nextRequestWillFail flag
+ *
+ * @param boolean $flag
+ * @return Zend_Http_Client_Adapter_Test
+ */
+ public function setNextRequestWillFail($flag)
+ {
+ $this->_nextRequestWillFail = (bool) $flag;
+
+ return $this;
+ }
+
+ /**
+ * Set the configuration array for the adapter
+ *
+ * @param Zend_Config | array $config
+ */
+ public function setConfig($config = array())
+ {
+ if ($config instanceof Zend_Config) {
+ $config = $config->toArray();
+
+ } elseif (! is_array($config)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Array or Zend_Config object expected, got ' . gettype($config)
+ );
+ }
+
+ foreach ($config as $k => $v) {
+ $this->config[strtolower($k)] = $v;
+ }
+ }
+
+
+ /**
+ * Connect to the remote server
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ * @param int $timeout
+ * @throws Zend_Http_Client_Adapter_Exception
+ */
+ public function connect($host, $port = 80, $secure = false)
+ {
+ if ($this->_nextRequestWillFail) {
+ $this->_nextRequestWillFail = false;
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception('Request failed');
+ }
+ }
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend_Uri_Http $uri
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as string
+ */
+ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
+ {
+ $host = $uri->getHost();
+ $host = (strtolower($uri->getScheme()) == 'https' ? 'sslv2://' . $host : $host);
+
+ // Build request headers
+ $path = $uri->getPath();
+ if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
+ $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
+ foreach ($headers as $k => $v) {
+ if (is_string($k)) $v = ucfirst($k) . ": $v";
+ $request .= "$v\r\n";
+ }
+
+ // Add the request body
+ $request .= "\r\n" . $body;
+
+ // Do nothing - just return the request as string
+
+ return $request;
+ }
+
+ /**
+ * Return the response set in $this->setResponse()
+ *
+ * @return string
+ */
+ public function read()
+ {
+ if ($this->responseIndex >= count($this->responses)) {
+ $this->responseIndex = 0;
+ }
+ return $this->responses[$this->responseIndex++];
+ }
+
+ /**
+ * Close the connection (dummy)
+ *
+ */
+ public function close()
+ { }
+
+ /**
+ * Set the HTTP response(s) to be returned by this adapter
+ *
+ * @param Zend_Http_Response|array|string $response
+ */
+ public function setResponse($response)
+ {
+ if ($response instanceof Zend_Http_Response) {
+ $response = $response->asString("\r\n");
+ }
+
+ $this->responses = (array)$response;
+ $this->responseIndex = 0;
+ }
+
+ /**
+ * Add another response to the response buffer.
+ *
+ * @param string Zend_Http_Response|$response
+ */
+ public function addResponse($response)
+ {
+ if ($response instanceof Zend_Http_Response) {
+ $response = $response->asString("\r\n");
+ }
+
+ $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)) {
+ require_once 'Zend/Http/Client/Adapter/Exception.php';
+ throw new Zend_Http_Client_Adapter_Exception(
+ 'Index out of range of response buffer size');
+ }
+ $this->responseIndex = $index;
+ }
+
+ /**
+ * Retrieve the array of all configuration options
+ *
+ * @return array
+ */
+ public function getConfig()
+ {
+ return $this->config;
+ }
+}