$v) { $this->_config[strtolower($k)] = $v; } return $this; } /** * 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_TIMEOUT, $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, $http_ver = '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 (isset($this->_config['curloptions'][CURLOPT_INFILE])) { 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."); } // 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 (stristr($header, "Content-Length:") !== false) { unset($headers[$k]); } } $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; 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"); } // get http version to use $curlHttp = ($http_ver = 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); // 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); } // 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 $this->_response = curl_exec($this->_curl); $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); } // TODO: Probably the pattern for multiple handshake requests is always the same, several HTTP codes in the response. Use that information? // cURL automactically handles Expect: 100-continue; and its responses. Delete the HTTP 100 CONTINUE from a response // because it messes up Zend_Http_Response parsing if (stripos($this->_response, "HTTP/1.1 100 Continue\r\n\r\n") !== false) { $this->_response = str_ireplace("HTTP/1.1 100 Continue\r\n\r\n", '', $this->_response); } // 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); } }