| <?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 |
| * @version $Id: Client.php 8064 2008-02-16 10:58:39Z thomas $ |
| * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) |
| * @license http://framework.zend.com/license/new-bsd New BSD License |
| */ |
| |
| require_once 'external/Zend/Loader.php'; |
| require_once 'external/Zend/Uri.php'; |
| require_once 'external/Zend/Http/Client/Adapter/Interface.php'; |
| require_once 'external/Zend/Http/Response.php'; |
| |
| /** |
| * Zend_Http_Client is an implemetation of an HTTP client in PHP. The client |
| * supports basic features like sending different HTTP requests and handling |
| * redirections, as well as more advanced features like proxy settings, HTTP |
| * authentication and cookie persistance (using a Zend_Http_CookieJar object) |
| * |
| * @todo Implement proxy settings |
| * @category Zend |
| * @package Zend_Http |
| * @subpackage Client |
| * @throws Zend_Http_Client_Exception |
| * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) |
| * @license http://framework.zend.com/license/new-bsd New BSD License |
| */ |
| class Zend_Http_Client { |
| /** |
| * HTTP request methods |
| */ |
| const GET = 'GET'; |
| const POST = 'POST'; |
| const PUT = 'PUT'; |
| const HEAD = 'HEAD'; |
| const DELETE = 'DELETE'; |
| const TRACE = 'TRACE'; |
| const OPTIONS = 'OPTIONS'; |
| const CONNECT = 'CONNECT'; |
| |
| /** |
| * Supported HTTP Authentication methods |
| */ |
| const AUTH_BASIC = 'basic'; |
| //const AUTH_DIGEST = 'digest'; <-- not implemented yet |
| |
| |
| /** |
| * HTTP protocol versions |
| */ |
| const HTTP_1 = '1.1'; |
| const HTTP_0 = '1.0'; |
| |
| /** |
| * POST data encoding methods |
| */ |
| const ENC_URLENCODED = 'application/x-www-form-urlencoded'; |
| const ENC_FORMDATA = 'multipart/form-data'; |
| |
| /** |
| * Configuration array, set using the constructor or using ::setConfig() |
| * |
| * @var unknown_type |
| */ |
| protected $config = array('maxredirects' => 5, 'strictredirects' => false, |
| 'useragent' => 'Zend_Http_Client', 'timeout' => 10, |
| 'adapter' => 'Zend_Http_Client_Adapter_Socket', 'httpversion' => self::HTTP_1, |
| 'keepalive' => false, 'storeresponse' => true, 'strict' => true); |
| |
| /** |
| * The adapter used to preform the actual connection to the server |
| * |
| * @var Zend_Http_Client_Adapter_Interface |
| */ |
| protected $adapter = null; |
| |
| /** |
| * Request URI |
| * |
| * @var Zend_Uri_Http |
| */ |
| protected $uri; |
| |
| /** |
| * Associative array of request headers |
| * |
| * @var array |
| */ |
| protected $headers = array(); |
| |
| /** |
| * HTTP request method |
| * |
| * @var string |
| */ |
| protected $method = self::GET; |
| |
| /** |
| * Associative array of GET parameters |
| * |
| * @var array |
| */ |
| protected $paramsGet = array(); |
| |
| /** |
| * Assiciative array of POST parameters |
| * |
| * @var array |
| */ |
| protected $paramsPost = array(); |
| |
| /** |
| * Request body content type (for POST requests) |
| * |
| * @var string |
| */ |
| protected $enctype = null; |
| |
| /** |
| * The raw post data to send. Could be set by setRawData($data, $enctype). |
| * |
| * @var string |
| */ |
| protected $raw_post_data = null; |
| |
| /** |
| * HTTP Authentication settings |
| * |
| * Expected to be an associative array with this structure: |
| * $this->auth = array('user' => 'username', 'password' => 'password', 'type' => 'basic') |
| * Where 'type' should be one of the supported authentication types (see the AUTH_* |
| * constants), for example 'basic' or 'digest'. |
| * |
| * If null, no authentication will be used. |
| * |
| * @var array|null |
| */ |
| protected $auth; |
| |
| /** |
| * File upload arrays (used in POST requests) |
| * |
| * An associative array, where each element is of the format: |
| * 'name' => array('filename.txt', 'text/plain', 'This is the actual file contents') |
| * |
| * @var array |
| */ |
| protected $files = array(); |
| |
| /** |
| * The client's cookie jar |
| * |
| * @var Zend_Http_CookieJar |
| */ |
| protected $cookiejar = null; |
| |
| /** |
| * The last HTTP request sent by the client, as string |
| * |
| * @var string |
| */ |
| protected $last_request = null; |
| |
| /** |
| * The last HTTP response received by the client |
| * |
| * @var Zend_Http_Response |
| */ |
| protected $last_response = null; |
| |
| /** |
| * Redirection counter |
| * |
| * @var int |
| */ |
| protected $redirectCounter = 0; |
| |
| /** |
| * Contructor method. Will create a new HTTP client. Accepts the target |
| * URL and optionally and array of headers. |
| * |
| * @param Zend_Uri_Http|string $uri |
| * @param array $headers Optional request headers to set |
| */ |
| public function __construct($uri = null, $config = null) { |
| if ($uri !== null) $this->setUri($uri); |
| if ($config !== null) $this->setConfig($config); |
| } |
| |
| /** |
| * Set the URI for the next request |
| * |
| * @param Zend_Uri_Http|string $uri |
| * @return Zend_Http_Client |
| * @throws Zend_Http_Client_Exception |
| */ |
| public function setUri($uri) { |
| if (is_string($uri)) { |
| $uri = Zend_Uri::factory($uri); |
| } |
| |
| if (! $uri instanceof Zend_Uri_Http) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception('Passed parameter is not a valid HTTP URI.'); |
| } |
| |
| // We have no ports, set the defaults |
| if (! $uri->getPort()) { |
| $uri->setPort(($uri->getScheme() == 'https' ? 443 : 80)); |
| } |
| |
| $this->uri = $uri; |
| |
| return $this; |
| } |
| |
| /** |
| * Get the URI for the next request |
| * |
| * @param boolean $as_string If true, will return the URI as a string |
| * @return Zend_Uri_Http|string |
| */ |
| public function getUri($as_string = false) { |
| if ($as_string && $this->uri instanceof Zend_Uri_Http) { |
| return $this->uri->__toString(); |
| } else { |
| return $this->uri; |
| } |
| } |
| |
| /** |
| * Set configuration parameters for this HTTP client |
| * |
| * @param array $config |
| * @return Zend_Http_Client |
| */ |
| public function setConfig($config = array()) { |
| if (! is_array($config)) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception('Expected array parameter, given ' . gettype($config)); |
| } |
| |
| foreach ($config as $k => $v) |
| $this->config[strtolower($k)] = $v; |
| |
| return $this; |
| } |
| |
| /** |
| * Set the next request's method |
| * |
| * Validated the passed method and sets it. If we have files set for |
| * POST requests, and the new method is not POST, the files are silently |
| * dropped. |
| * |
| * @param string $method |
| * @return Zend_Http_Client |
| */ |
| public function setMethod($method = self::GET) { |
| if (! preg_match('/^[A-Za-z_]+$/', $method)) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("'{$method}' is not a valid HTTP request method."); |
| } |
| |
| if ($method == self::POST && $this->enctype === null) $this->setEncType(self::ENC_URLENCODED); |
| |
| $this->method = $method; |
| |
| return $this; |
| } |
| |
| /** |
| * Set one or more request headers |
| * |
| * This function can be used in several ways to set the client's request |
| * headers: |
| * 1. By providing two parameters: $name as the header to set (eg. 'Host') |
| * and $value as it's value (eg. 'www.example.com'). |
| * 2. By providing a single header string as the only parameter |
| * eg. 'Host: www.example.com' |
| * 3. By providing an array of headers as the first parameter |
| * eg. array('host' => 'www.example.com', 'x-foo: bar'). In This case |
| * the function will call itself recursively for each array item. |
| * |
| * @param string|array $name Header name, full header string ('Header: value') |
| * or an array of headers |
| * @param mixed $value Header value or null |
| * @return Zend_Http_Client |
| */ |
| public function setHeaders($name, $value = null) { |
| // If we got an array, go recusive! |
| if (is_array($name)) { |
| foreach ($name as $k => $v) { |
| if (is_string($k)) { |
| $this->setHeaders($k, $v); |
| } else { |
| $this->setHeaders($v, null); |
| } |
| } |
| } else { |
| // Check if $name needs to be split |
| if ($value === null && (strpos($name, ':') > 0)) list($name, $value) = explode(':', $name, 2); |
| |
| // Make sure the name is valid if we are in strict mode |
| if ($this->config['strict'] && (! preg_match('/^[a-zA-Z0-9-]+$/', $name))) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("{$name} is not a valid HTTP header name"); |
| } |
| |
| $normalized_name = strtolower($name); |
| |
| // If $value is null or false, unset the header |
| if ($value === null || $value === false) { |
| unset($this->headers[$normalized_name]); |
| |
| // Else, set the header |
| } else { |
| // Header names are storred lowercase internally. |
| if (is_string($value)) $value = trim($value); |
| $this->headers[$normalized_name] = array($name, $value); |
| } |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Get the value of a specific header |
| * |
| * Note that if the header has more than one value, an array |
| * will be returned. |
| * |
| * @param unknown_type $key |
| * @return string|array|null The header value or null if it is not set |
| */ |
| public function getHeader($key) { |
| $key = strtolower($key); |
| if (isset($this->headers[$key])) { |
| return $this->headers[$key][1]; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Set a GET parameter for the request. Wrapper around _setParameter |
| * |
| * @param string|array $name |
| * @param string $value |
| * @return Zend_Http_Client |
| */ |
| public function setParameterGet($name, $value = null) { |
| if (is_array($name)) { |
| foreach ($name as $k => $v) |
| $this->_setParameter('GET', $k, $v); |
| } else { |
| $this->_setParameter('GET', $name, $value); |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Set a POST parameter for the request. Wrapper around _setParameter |
| * |
| * @param string|array $name |
| * @param string $value |
| * @return Zend_Http_Client |
| */ |
| public function setParameterPost($name, $value = null) { |
| if (is_array($name)) { |
| foreach ($name as $k => $v) |
| $this->_setParameter('POST', $k, $v); |
| } else { |
| $this->_setParameter('POST', $name, $value); |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Set a GET or POST parameter - used by SetParameterGet and SetParameterPost |
| * |
| * @param string $type GET or POST |
| * @param string $name |
| * @param string $value |
| */ |
| protected function _setParameter($type, $name, $value) { |
| $parray = array(); |
| $type = strtolower($type); |
| switch ($type) { |
| case 'get': |
| $parray = &$this->paramsGet; |
| break; |
| case 'post': |
| $parray = &$this->paramsPost; |
| break; |
| } |
| |
| if ($value === null) { |
| if (isset($parray[$name])) unset($parray[$name]); |
| } else { |
| $parray[$name] = $value; |
| } |
| } |
| |
| /** |
| * Get the number of redirections done on the last request |
| * |
| * @return int |
| */ |
| public function getRedirectionsCount() { |
| return $this->redirectCounter; |
| } |
| |
| /** |
| * Set HTTP authentication parameters |
| * |
| * $type should be one of the supported types - see the self::AUTH_* |
| * constants. |
| * |
| * To enable authentication: |
| * <code> |
| * $this->setAuth('shahar', 'secret', Zend_Http_Client::AUTH_BASIC); |
| * </code> |
| * |
| * To disable authentication: |
| * <code> |
| * $this->setAuth(false); |
| * </code> |
| * |
| * @see http://www.faqs.org/rfcs/rfc2617.html |
| * @param string|false $user User name or false disable authentication |
| * @param string $password Password |
| * @param string $type Authentication type |
| * @return Zend_Http_Client |
| */ |
| public function setAuth($user, $password = '', $type = self::AUTH_BASIC) { |
| // If we got false or null, disable authentication |
| if ($user === false || $user === null) { |
| $this->auth = null; |
| |
| // Else, set up authentication |
| } else { |
| // Check we got a proper authentication type |
| if (! defined('self::AUTH_' . strtoupper($type))) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("Invalid or not supported authentication type: '$type'"); |
| } |
| |
| $this->auth = array('user' => (string)$user, 'password' => (string)$password, |
| 'type' => $type); |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Set the HTTP client's cookie jar. |
| * |
| * A cookie jar is an object that holds and maintains cookies across HTTP requests |
| * and responses. |
| * |
| * @param Zend_Http_CookieJar|boolean $cookiejar Existing cookiejar object, true to create a new one, false to disable |
| * @return Zend_Http_Client |
| */ |
| public function setCookieJar($cookiejar = true) { |
| if (! class_exists('Zend_Http_CookieJar')) require_once 'external/Zend/Http/CookieJar.php'; |
| |
| if ($cookiejar instanceof Zend_Http_CookieJar) { |
| $this->cookiejar = $cookiejar; |
| } elseif ($cookiejar === true) { |
| $this->cookiejar = new Zend_Http_CookieJar(); |
| } elseif (! $cookiejar) { |
| $this->cookiejar = null; |
| } else { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception('Invalid parameter type passed as CookieJar'); |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Return the current cookie jar or null if none. |
| * |
| * @return Zend_Http_CookieJar|null |
| */ |
| public function getCookieJar() { |
| return $this->cookiejar; |
| } |
| |
| /** |
| * Add a cookie to the request. If the client has no Cookie Jar, the cookies |
| * will be added directly to the headers array as "Cookie" headers. |
| * |
| * @param Zend_Http_Cookie|string $cookie |
| * @param string|null $value If "cookie" is a string, this is the cookie value. |
| * @return Zend_Http_Client |
| */ |
| public function setCookie($cookie, $value = null) { |
| if (! class_exists('Zend_Http_Cookie')) require_once 'external/Zend/Http/Cookie.php'; |
| |
| if (is_array($cookie)) { |
| foreach ($cookie as $c => $v) { |
| if (is_string($c)) { |
| $this->setCookie($c, $v); |
| } else { |
| $this->setCookie($v); |
| } |
| } |
| |
| return $this; |
| } |
| |
| if ($value !== null) $value = urlencode($value); |
| |
| if (isset($this->cookiejar)) { |
| if ($cookie instanceof Zend_Http_Cookie) { |
| $this->cookiejar->addCookie($cookie); |
| } elseif (is_string($cookie) && $value !== null) { |
| $cookie = Zend_Http_Cookie::fromString("{$cookie}={$value}", $this->uri); |
| $this->cookiejar->addCookie($cookie); |
| } |
| } else { |
| if ($cookie instanceof Zend_Http_Cookie) { |
| $name = $cookie->getName(); |
| $value = $cookie->getValue(); |
| $cookie = $name; |
| } |
| |
| if (preg_match("/[=,; \t\r\n\013\014]/", $cookie)) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("Cookie name cannot contain these characters: =,; \t\r\n\013\014 ({$cookie})"); |
| } |
| |
| $value = addslashes($value); |
| |
| if (! isset($this->headers['cookie'])) $this->headers['cookie'] = array('Cookie', ''); |
| $this->headers['cookie'][1] .= $cookie . '=' . $value . '; '; |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Set a file to upload (using a POST request) |
| * |
| * Can be used in two ways: |
| * |
| * 1. $data is null (default): $filename is treated as the name if a local file which |
| * will be read and sent. Will try to guess the content type using mime_content_type(). |
| * 2. $data is set - $filename is sent as the file name, but $data is sent as the file |
| * contents and no file is read from the file system. In this case, you need to |
| * manually set the content-type ($ctype) or it will default to |
| * application/octet-stream. |
| * |
| * @param string $filename Name of file to upload, or name to save as |
| * @param string $formname Name of form element to send as |
| * @param string $data Data to send (if null, $filename is read and sent) |
| * @param string $ctype Content type to use (if $data is set and $ctype is |
| * null, will be application/octet-stream) |
| * @return Zend_Http_Client |
| */ |
| public function setFileUpload($filename, $formname, $data = null, $ctype = null) { |
| if ($data === null) { |
| if (($data = @file_get_contents($filename)) === false) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("Unable to read file '{$filename}' for upload"); |
| } |
| |
| if (! $ctype && function_exists('mime_content_type')) $ctype = mime_content_type($filename); |
| } |
| |
| // Force enctype to multipart/form-data |
| $this->setEncType(self::ENC_FORMDATA); |
| |
| if ($ctype === null) $ctype = 'application/octet-stream'; |
| $this->files[$formname] = array(basename($filename), $ctype, $data); |
| |
| return $this; |
| } |
| |
| /** |
| * Set the encoding type for POST data |
| * |
| * @param string $enctype |
| * @return Zend_Http_Client |
| */ |
| public function setEncType($enctype = self::ENC_URLENCODED) { |
| $this->enctype = $enctype; |
| |
| return $this; |
| } |
| |
| /** |
| * Set the raw (already encoded) POST data. |
| * |
| * This function is here for two reasons: |
| * 1. For advanced user who would like to set their own data, already encoded |
| * 2. For backwards compatibilty: If someone uses the old post($data) method. |
| * this method will be used to set the encoded data. |
| * |
| * @param string $data |
| * @param string $enctype |
| * @return Zend_Http_Client |
| */ |
| public function setRawData($data, $enctype = null) { |
| $this->raw_post_data = $data; |
| $this->setEncType($enctype); |
| |
| return $this; |
| } |
| |
| /** |
| * Clear all GET and POST parameters |
| * |
| * Should be used to reset the request parameters if the client is |
| * used for several concurrent requests. |
| * |
| * @return Zend_Http_Client |
| */ |
| public function resetParameters() { |
| // Reset parameter data |
| $this->paramsGet = array(); |
| $this->paramsPost = array(); |
| $this->files = array(); |
| $this->raw_post_data = null; |
| |
| // Clear outdated headers |
| if (isset($this->headers['content-type'])) unset($this->headers['content-type']); |
| if (isset($this->headers['content-length'])) unset($this->headers['content-length']); |
| |
| return $this; |
| } |
| |
| /** |
| * Get the last HTTP request as string |
| * |
| * @return string |
| */ |
| public function getLastRequest() { |
| return $this->last_request; |
| } |
| |
| /** |
| * Get the last HTTP response received by this client |
| * |
| * If $config['storeresponse'] is set to false, or no response was |
| * stored yet, will return null |
| * |
| * @return Zend_Http_Response or null if none |
| */ |
| public function getLastResponse() { |
| return $this->last_response; |
| } |
| |
| /** |
| * Load the connection adapter |
| * |
| * While this method is not called more than one for a client, it is |
| * seperated from ->request() to preserve logic and readability |
| * |
| * @param Zend_Http_Client_Adapter_Interface|string $adapter |
| */ |
| public function setAdapter($adapter) { |
| if (is_string($adapter)) { |
| try { |
| Zend_Loader::loadClass($adapter); |
| } catch (Zend_Exception $e) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("Unable to load adapter '$adapter': {$e->getMessage()}"); |
| } |
| |
| $adapter = new $adapter(); |
| } |
| |
| if (! $adapter instanceof Zend_Http_Client_Adapter_Interface) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception('Passed adapter is not a HTTP connection adapter'); |
| } |
| |
| $this->adapter = $adapter; |
| $config = $this->config; |
| unset($config['adapter']); |
| $this->adapter->setConfig($config); |
| } |
| |
| /** |
| * Send the HTTP request and return an HTTP response object |
| * |
| * @param string $method |
| * @return Zend_Http_Response |
| */ |
| public function request($method = null) { |
| if (! $this->uri instanceof Zend_Uri_Http) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception('No valid URI has been passed to the client'); |
| } |
| |
| if ($method) $this->setMethod($method); |
| $this->redirectCounter = 0; |
| $response = null; |
| |
| // Make sure the adapter is loaded |
| if ($this->adapter == null) $this->setAdapter($this->config['adapter']); |
| |
| // Send the first request. If redirected, continue. |
| do { |
| // Clone the URI and add the additional GET parameters to it |
| $uri = clone $this->uri; |
| if (! empty($this->paramsGet)) { |
| $query = $uri->getQuery(); |
| if (! empty($query)) $query .= '&'; |
| $query .= http_build_query($this->paramsGet, null, '&'); |
| |
| $uri->setQuery($query); |
| } |
| |
| $body = $this->prepare_body(); |
| $headers = $this->prepare_headers(); |
| |
| // Open the connection, send the request and read the response |
| $this->adapter->connect($uri->getHost(), $uri->getPort(), ($uri->getScheme() == 'https' ? true : false)); |
| |
| $this->last_request = $this->adapter->write($this->method, $uri, $this->config['httpversion'], $headers, $body); |
| |
| $response = $this->adapter->read(); |
| if (! $response) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception('Unable to read response, or response is empty'); |
| } |
| |
| $response = Zend_Http_Response::fromString($response); |
| if ($this->config['storeresponse']) $this->last_response = $response; |
| |
| // Load cookies into cookie jar |
| if (isset($this->cookiejar)) $this->cookiejar->addCookiesFromResponse($response, $uri); |
| |
| // If we got redirected, look for the Location header |
| if ($response->isRedirect() && ($location = $response->getHeader('location'))) { |
| |
| // Check whether we send the exact same request again, or drop the parameters |
| // and send a GET request |
| if ($response->getStatus() == 303 || ((! $this->config['strictredirects']) && ($response->getStatus() == 302 || $response->getStatus() == 301))) { |
| |
| $this->resetParameters(); |
| $this->setMethod(self::GET); |
| } |
| |
| // If we got a well formed absolute URI |
| if (Zend_Uri_Http::check($location)) { |
| $this->setHeaders('host', null); |
| $this->setUri($location); |
| |
| } else { |
| |
| // Split into path and query and set the query |
| if (strpos($location, '?') !== false) { |
| list($location, $query) = explode('?', $location, 2); |
| } else { |
| $query = ''; |
| } |
| $this->uri->setQuery($query); |
| |
| // Else, if we got just an absolute path, set it |
| if (strpos($location, '/') === 0) { |
| $this->uri->setPath($location); |
| |
| // Else, assume we have a relative path |
| } else { |
| // Get the current path directory, removing any trailing slashes |
| $path = $this->uri->getPath(); |
| $path = rtrim(substr($path, 0, strrpos($path, '/')), "/"); |
| $this->uri->setPath($path . '/' . $location); |
| } |
| } |
| ++ $this->redirectCounter; |
| |
| } else { |
| // If we didn't get any location, stop redirecting |
| break; |
| } |
| |
| } while ($this->redirectCounter < $this->config['maxredirects']); |
| |
| return $response; |
| } |
| |
| /** |
| * Prepare the request headers |
| * |
| * @return array |
| */ |
| protected function prepare_headers() { |
| $headers = array(); |
| |
| // Set the host header |
| if (! isset($this->headers['host'])) { |
| $host = $this->uri->getHost(); |
| |
| // If the port is not default, add it |
| if (! (($this->uri->getScheme() == 'http' && $this->uri->getPort() == 80) || ($this->uri->getScheme() == 'https' && $this->uri->getPort() == 443))) { |
| $host .= ':' . $this->uri->getPort(); |
| } |
| |
| $headers[] = "Host: {$host}"; |
| } |
| |
| // Set the connection header |
| if (! isset($this->headers['connection'])) { |
| if (! $this->config['keepalive']) $headers[] = "Connection: close"; |
| } |
| |
| // Set the Accept-encoding header if not set - depending on whether |
| // zlib is available or not. |
| if (! isset($this->headers['accept-encoding'])) { |
| if (function_exists('gzinflate')) { |
| $headers[] = 'Accept-encoding: gzip, deflate'; |
| } else { |
| $headers[] = 'Accept-encoding: identity'; |
| } |
| } |
| |
| // Set the content-type header |
| if ($this->method == self::POST && (! isset($this->headers['content-type']) && isset($this->enctype))) { |
| |
| $headers[] = "Content-type: {$this->enctype}"; |
| } |
| |
| // Set the user agent header |
| if (! isset($this->headers['user-agent']) && isset($this->config['useragent'])) { |
| $headers[] = "User-agent: {$this->config['useragent']}"; |
| } |
| |
| // Set HTTP authentication if needed |
| if (is_array($this->auth)) { |
| $auth = self::encodeAuthHeader($this->auth['user'], $this->auth['password'], $this->auth['type']); |
| $headers[] = "Authorization: {$auth}"; |
| } |
| |
| // Load cookies from cookie jar |
| if (isset($this->cookiejar)) { |
| $cookstr = $this->cookiejar->getMatchingCookies($this->uri, true, Zend_Http_CookieJar::COOKIE_STRING_CONCAT); |
| |
| if ($cookstr) $headers[] = "Cookie: {$cookstr}"; |
| } |
| |
| // Add all other user defined headers |
| foreach ($this->headers as $header) { |
| list($name, $value) = $header; |
| if (is_array($value)) $value = implode(', ', $value); |
| |
| $headers[] = "$name: $value"; |
| } |
| |
| return $headers; |
| } |
| |
| /** |
| * Prepare the request body (for POST and PUT requests) |
| * |
| * @return string |
| */ |
| protected function prepare_body() { |
| // According to RFC2616, a TRACE request should not have a body. |
| if ($this->method == self::TRACE) { |
| return ''; |
| } |
| |
| // If we have raw_post_data set, just use it as the body. |
| if (isset($this->raw_post_data)) { |
| $this->setHeaders('Content-length', strlen($this->raw_post_data)); |
| return $this->raw_post_data; |
| } |
| |
| $body = ''; |
| |
| // If we have files to upload, force enctype to multipart/form-data |
| if (count($this->files) > 0) $this->setEncType(self::ENC_FORMDATA); |
| |
| // If we have POST parameters or files, encode and add them to the body |
| if (count($this->paramsPost) > 0 || count($this->files) > 0) { |
| switch ($this->enctype) { |
| case self::ENC_FORMDATA: |
| // Encode body as multipart/form-data |
| $boundary = '---ZENDHTTPCLIENT-' . md5(microtime()); |
| $this->setHeaders('Content-type', self::ENC_FORMDATA . "; boundary={$boundary}"); |
| |
| // Get POST parameters and encode them |
| $params = $this->_getParametersRecursive($this->paramsPost); |
| foreach ($params as $pp) { |
| $body .= self::encodeFormData($boundary, $pp[0], $pp[1]); |
| } |
| |
| // Encode files |
| foreach ($this->files as $name => $file) { |
| $fhead = array('Content-type' => $file[1]); |
| $body .= self::encodeFormData($boundary, $name, $file[2], $file[0], $fhead); |
| } |
| |
| $body .= "--{$boundary}--\r\n"; |
| break; |
| |
| case self::ENC_URLENCODED: |
| // Encode body as application/x-www-form-urlencoded |
| $this->setHeaders('Content-type', self::ENC_URLENCODED); |
| $body = http_build_query($this->paramsPost, '', '&'); |
| break; |
| |
| default: |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("Cannot handle content type '{$this->enctype}' automatically." . " Please use Zend_Http_Client::setRawData to send this kind of content."); |
| break; |
| } |
| } |
| |
| if ($body) $this->setHeaders('Content-length', strlen($body)); |
| return $body; |
| } |
| |
| /** |
| * Helper method that gets a possibly multi-level parameters array (get or |
| * post) and flattens it. |
| * |
| * The method returns an array of (key, value) pairs (because keys are not |
| * necessarily unique. If one of the parameters in as array, it will also |
| * add a [] suffix to the key. |
| * |
| * @param array $parray The parameters array |
| * @param bool $urlencode Whether to urlencode the name and value |
| * @return array |
| */ |
| protected function _getParametersRecursive($parray, $urlencode = false) { |
| if (! is_array($parray)) return $parray; |
| $parameters = array(); |
| |
| foreach ($parray as $name => $value) { |
| if ($urlencode) $name = urlencode($name); |
| |
| // If $value is an array, iterate over it |
| if (is_array($value)) { |
| $name .= ($urlencode ? '%5B%5D' : '[]'); |
| foreach ($value as $subval) { |
| if ($urlencode) $subval = urlencode($subval); |
| $parameters[] = array($name, $subval); |
| } |
| } else { |
| if ($urlencode) $value = urlencode($value); |
| $parameters[] = array($name, $value); |
| } |
| } |
| |
| return $parameters; |
| } |
| |
| /** |
| * Encode data to a multipart/form-data part suitable for a POST request. |
| * |
| * @param string $boundary |
| * @param string $name |
| * @param mixed $value |
| * @param string $filename |
| * @param array $headers Associative array of optional headers @example ("Content-transfer-encoding" => "binary") |
| * @return string |
| */ |
| public static function encodeFormData($boundary, $name, $value, $filename = null, $headers = array()) { |
| $ret = "--{$boundary}\r\n" . 'Content-disposition: form-data; name="' . $name . '"'; |
| |
| if ($filename) $ret .= '; filename="' . $filename . '"'; |
| $ret .= "\r\n"; |
| |
| foreach ($headers as $hname => $hvalue) { |
| $ret .= "{$hname}: {$hvalue}\r\n"; |
| } |
| $ret .= "\r\n"; |
| |
| $ret .= "{$value}\r\n"; |
| |
| return $ret; |
| } |
| |
| /** |
| * Create a HTTP authentication "Authorization:" header according to the |
| * specified user, password and authentication method. |
| * |
| * @see http://www.faqs.org/rfcs/rfc2617.html |
| * @param string $user |
| * @param string $password |
| * @param string $type |
| * @return string |
| */ |
| public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC) { |
| $authHeader = null; |
| |
| switch ($type) { |
| case self::AUTH_BASIC: |
| // In basic authentication, the user name cannot contain ":" |
| if (strpos($user, ':') !== false) { |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("The user name cannot contain ':' in 'Basic' HTTP authentication"); |
| } |
| |
| $authHeader = 'Basic ' . base64_encode($user . ':' . $password); |
| break; |
| |
| //case self::AUTH_DIGEST: |
| /** |
| * @todo Implement digest authentication |
| */ |
| // break; |
| |
| |
| default: |
| require_once 'external/Zend/Http/Client/Exception.php'; |
| throw new Zend_Http_Client_Exception("Not a supported HTTP authentication type: '$type'"); |
| } |
| |
| return $authHeader; |
| } |
| } |