Viewing file: HTTP.php (19.92 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php /** * This file contains the code for a HTTP transport layer. * * PHP versions 4 and 5 * * LICENSE: This source file is subject to version 2.02 of the PHP license, * that is bundled with this package in the file LICENSE, and is available at * through the world-wide-web at http://www.php.net/license/2_02.txt. If you * did not receive a copy of the PHP license and are unable to obtain it * through the world-wide-web, please send a note to license@php.net so we can * mail you a copy immediately. * * @category Web Services * @package SOAP * @author Shane Caraveo <Shane@Caraveo.com> * @author Jan Schneider <jan@horde.org> * @copyright 2003-2006 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */
/** * HTTP Transport class * * @package SOAP * @category Web_Services */
/** * Needed Classes */ require_once 'SOAP/Transport.php';
/** * HTTP Transport for SOAP * * @access public * @package SOAP * @author Shane Caraveo <shane@php.net> * @author Jan Schneider <jan@horde.org> */ class SOAP_Transport_HTTP extends SOAP_Transport { /** * Basic Auth string. * * @var array */ var $headers = array();
/** * Cookies. * * @var array */ var $cookies;
/** * Connection timeout in seconds. 0 = none. * * @var integer */ var $timeout = 4;
/** * HTTP-Response Content-Type. */ var $result_content_type;
var $result_headers = array();
var $result_cookies = array();
/** * SOAP_Transport_HTTP Constructor * * @access public * * @param string $url HTTP url to SOAP endpoint. * @param string $encoding Encoding to use. */ function SOAP_Transport_HTTP($url, $encoding = SOAP_DEFAULT_ENCODING) { parent::SOAP_Base('HTTP'); $this->urlparts = @parse_url($url); $this->url = $url; $this->encoding = $encoding; }
/** * Sends and receives SOAP data. * * @access public * * @param string Outgoing SOAP data. * @param array Options. * * @return string|SOAP_Fault */ function send($msg, $options = array()) { $this->fault = null;
if (!$this->_validateUrl()) { return $this->fault; }
if (isset($options['timeout'])) { $this->timeout = (int)$options['timeout']; }
if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) { return $this->_sendHTTP($msg, $options); } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) { return $this->_sendHTTPS($msg, $options); }
return $this->_raiseSoapFault('Invalid url scheme ' . $this->url); }
/** * Sets data for HTTP authentication, creates authorization header. * * @param string $username Username. * @param string $password Response data, minus HTTP headers. * * @access public */ function setCredentials($username, $password) { $this->headers['Authorization'] = 'Basic ' . base64_encode($username . ':' . $password); }
/** * Adds a cookie. * * @access public * @param string $name Cookie name. * @param mixed $value Cookie value. */ function addCookie($name, $value) { $this->cookies[$name] = $value; }
/** * Generates the correct headers for the cookies. * * @access private * * @param array $options Cookie options. If 'nocookies' is set and true * the cookies from the last response are added * automatically. 'cookies' is name-value-hash with * a list of cookies to add. * * @return string The cookie header value. */ function _generateCookieHeader($options) { $this->cookies = array();
if (empty($options['nocookies']) && isset($this->result_cookies)) { // Add the cookies we got from the last request. foreach ($this->result_cookies as $cookie) { if ($cookie['domain'] == $this->urlparts['host']) { $this->cookies[$cookie['name']] = $cookie['value']; } } }
// Add cookies the user wants to set. if (isset($options['cookies'])) { foreach ($options['cookies'] as $cookie) { if ($cookie['domain'] == $this->urlparts['host']) { $this->cookies[$cookie['name']] = $cookie['value']; } } }
$cookies = ''; foreach ($this->cookies as $name => $value) { if (!empty($cookies)) { $cookies .= '; '; } $cookies .= urlencode($name) . '=' . urlencode($value); }
return $cookies; }
/** * Validate url data passed to constructor. * * @access private * @return boolean */ function _validateUrl() { if (!is_array($this->urlparts) ) { $this->_raiseSoapFault('Unable to parse URL ' . $this->url); return false; } if (!isset($this->urlparts['host'])) { $this->_raiseSoapFault('No host in URL ' . $this->url); return false; } if (!isset($this->urlparts['port'])) { if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) { $this->urlparts['port'] = 80; } elseif (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) { $this->urlparts['port'] = 443; }
} if (isset($this->urlparts['user'])) { $this->setCredentials(urldecode($this->urlparts['user']), urldecode($this->urlparts['pass'])); } if (!isset($this->urlparts['path']) || !$this->urlparts['path']) { $this->urlparts['path'] = '/'; }
return true; }
/** * Finds out what the encoding is. * Sets the object property accordingly. * * @access private * @param array $headers Headers. */ function _parseEncoding($headers) { $h = stristr($headers, 'Content-Type'); preg_match_all('/^Content-Type:\s*(.*)$/im', $h, $ct, PREG_SET_ORDER); $n = count($ct); $ct = $ct[$n - 1];
// Strip the string of \r. $this->result_content_type = str_replace("\r", '', $ct[1]);
if (preg_match('/(.*?)(?:;\s?charset=)(.*)/i', $this->result_content_type, $m)) { $this->result_content_type = $m[1]; if (count($m) > 2) { $enc = strtoupper(str_replace('"', '', $m[2])); if (in_array($enc, $this->_encodings)) { $this->result_encoding = $enc; } } }
// Deal with broken servers that don't set content type on faults. if (!$this->result_content_type) { $this->result_content_type = 'text/xml'; } }
/** * Parses the headers. * * @param array $headers The headers. */ function _parseHeaders($headers) { /* Largely borrowed from HTTP_Request. */ $this->result_headers = array(); $headers = split("\r?\n", $headers); foreach ($headers as $value) { if (strpos($value,':') === false) { $this->result_headers[0] = $value; continue; } list($name, $value) = split(':', $value); $headername = strtolower($name); $headervalue = trim($value); $this->result_headers[$headername] = $headervalue;
if ($headername == 'set-cookie') { // Parse a SetCookie header to fill _cookies array. $cookie = array('expires' => null, 'domain' => $this->urlparts['host'], 'path' => null, 'secure' => false);
if (!strpos($headervalue, ';')) { // Only a name=value pair. list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $headervalue)); $cookie['name'] = urldecode($cookie['name']); $cookie['value'] = urldecode($cookie['value']);
} else { // Some optional parameters are supplied. $elements = explode(';', $headervalue); list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $elements[0])); $cookie['name'] = urldecode($cookie['name']); $cookie['value'] = urldecode($cookie['value']);
for ($i = 1; $i < count($elements);$i++) { list($elName, $elValue) = array_map('trim', explode('=', $elements[$i])); if ('secure' == $elName) { $cookie['secure'] = true; } elseif ('expires' == $elName) { $cookie['expires'] = str_replace('"', '', $elValue); } elseif ('path' == $elName OR 'domain' == $elName) { $cookie[$elName] = urldecode($elValue); } else { $cookie[$elName] = $elValue; } } } $this->result_cookies[] = $cookie; } } }
/** * Removes HTTP headers from response. * * @return boolean * @access private */ function _parseResponse() { if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $this->incoming_payload, $match)) { $this->response = $match[2]; // Find the response error, some servers response with 500 for // SOAP faults. $this->_parseHeaders($match[1]);
list($protocol, $code, $msg) = sscanf($this->result_headers[0], '%s %s %s'); unset($this->result_headers[0]);
switch($code) { case 100: // Continue $this->incoming_payload = $match[2]; return $this->_parseResponse(); case 400: $this->_raiseSoapFault("HTTP Response $code Bad Request"); return false; break; case 401: $this->_raiseSoapFault("HTTP Response $code Authentication Failed"); return false; break; case 403: $this->_raiseSoapFault("HTTP Response $code Forbidden"); return false; break; case 404: $this->_raiseSoapFault("HTTP Response $code Not Found"); return false; break; case 407: $this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required"); return false; break; case 408: $this->_raiseSoapFault("HTTP Response $code Request Timeout"); return false; break; case 410: $this->_raiseSoapFault("HTTP Response $code Gone"); return false; break; default: if ($code >= 400 && $code < 500) { $this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg"); return false; } }
$this->_parseEncoding($match[1]);
if ($this->result_content_type == 'application/dime') { // XXX quick hack insertion of DIME if (PEAR::isError($this->_decodeDIMEMessage($this->response, $this->headers, $this->attachments))) { // _decodeDIMEMessage already raised $this->fault return false; } $this->result_content_type = $this->headers['content-type']; } elseif (stristr($this->result_content_type, 'multipart/related')) { $this->response = $this->incoming_payload; if (PEAR::isError($this->_decodeMimeMessage($this->response, $this->headers, $this->attachments))) { // _decodeMimeMessage already raised $this->fault return false; } } elseif ($this->result_content_type != 'text/xml') { $this->_raiseSoapFault($this->response); return false; } // if no content, return false return strlen($this->response) > 0; } $this->_raiseSoapFault('Invalid HTTP Response'); return false; }
/** * Creates an HTTP request, including headers, for th eoutgoing request. * * @access private * * @param string $msg Outgoing SOAP package. * @param array $options Options. * * @return string Outgoing payload. */ function _getRequest($msg, $options) { $this->headers = array();
$action = isset($options['soapaction']) ? $options['soapaction'] : ''; $fullpath = $this->urlparts['path']; if (isset($this->urlparts['query'])) { $fullpath .= '?' . $this->urlparts['query']; } if (isset($this->urlparts['fragment'])) { $fullpath .= '#' . $this->urlparts['fragment']; }
if (isset($options['proxy_host'])) { $fullpath = 'http://' . $this->urlparts['host'] . ':' . $this->urlparts['port'] . $fullpath; }
if (isset($options['proxy_user'])) { $this->headers['Proxy-Authorization'] = 'Basic ' . base64_encode($options['proxy_user'] . ':' . $options['proxy_pass']); }
if (isset($options['user'])) { $this->setCredentials($options['user'], $options['pass']); }
$this->headers['User-Agent'] = $this->_userAgent; $this->headers['Host'] = $this->urlparts['host']; $this->headers['Content-Type'] = "text/xml; charset=$this->encoding"; $this->headers['Content-Length'] = strlen($msg); $this->headers['SOAPAction'] = '"' . $action . '"'; if (isset($options['headers'])) { $this->headers = array_merge($this->headers, $options['headers']); }
$cookies = $this->_generateCookieHeader($options); if ($cookies) { $this->headers['Cookie'] = $cookies; }
$headers = ''; foreach ($this->headers as $k => $v) { $headers .= "$k: $v\r\n"; } $this->outgoing_payload = "POST $fullpath HTTP/1.0\r\n" . $headers . "\r\n" . $msg;
return $this->outgoing_payload; }
/** * Sends the outgoing HTTP request and reads and parses the response. * * @access private * * @param string $msg Outgoing SOAP package. * @param array $options Options. * * @return string Response data without HTTP headers. */ function _sendHTTP($msg, $options) { $this->incoming_payload = ''; $this->_getRequest($msg, $options); $host = $this->urlparts['host']; $port = $this->urlparts['port']; if (isset($options['proxy_host'])) { $host = $options['proxy_host']; $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080; } // Send. if ($this->timeout > 0) { $fp = @fsockopen($host, $port, $this->errno, $this->errmsg, $this->timeout); } else { $fp = @fsockopen($host, $port, $this->errno, $this->errmsg); } if (!$fp) { return $this->_raiseSoapFault("Connect Error to $host:$port"); } if ($this->timeout > 0) { // some builds of PHP do not support this, silence the warning @socket_set_timeout($fp, $this->timeout); } if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { return $this->_raiseSoapFault("Error POSTing Data to $host"); }
// get reponse // XXX time consumer do { $data = fread($fp, 4096); $_tmp_status = socket_get_status($fp); if ($_tmp_status['timed_out']) { return $this->_raiseSoapFault("Timed out read from $host"); } else { $this->incoming_payload .= $data; } } while (!$_tmp_status['eof']);
fclose($fp);
if (!$this->_parseResponse()) { return $this->fault; } return $this->response; }
/** * Sends the outgoing HTTPS request and reads and parses the response. * * @access private * * @param string $msg Outgoing SOAP package. * @param array $options Options. * * @return string Response data without HTTP headers. */ function _sendHTTPS($msg, $options) { /* Check if the required curl extension is installed. */ if (!extension_loaded('curl')) { return $this->_raiseSoapFault('CURL Extension is required for HTTPS'); }
$ch = curl_init();
if (isset($options['proxy_host'])) { $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080; curl_setopt($ch, CURLOPT_PROXY, $options['proxy_host'] . ':' . $port); } if (isset($options['proxy_user'])) { curl_setopt($ch, CURLOPT_PROXYUSERPWD, $options['proxy_user'] . ':' . $options['proxy_pass']); }
if (isset($options['user'])) { curl_setopt($ch, CURLOPT_USERPWD, $options['user'] . ':' . $options['pass']); }
if (!isset($options['soapaction'])) { $options['soapaction'] = ''; } curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml;charset=' . $this->encoding, 'SOAPAction: "' . $options['soapaction'] . '"')); curl_setopt($ch, CURLOPT_USERAGENT, $this->_userAgent);
if ($this->timeout) { curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); }
curl_setopt($ch, CURLOPT_POSTFIELDS, $msg); curl_setopt($ch, CURLOPT_URL, $this->url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_FAILONERROR, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); if (defined('CURLOPT_HTTP_VERSION')) { curl_setopt($ch, CURLOPT_HTTP_VERSION, 1); } if (!ini_get('safe_mode') && !ini_get('open_basedir')) { curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); } $cookies = $this->_generateCookieHeader($options); if ($cookies) { curl_setopt($ch, CURLOPT_COOKIE, $cookies); }
if (isset($options['curl'])) { foreach ($options['curl'] as $key => $val) { curl_setopt($ch, $key, $val); } }
// Save the outgoing XML. This doesn't quite match _sendHTTP as CURL // generates the headers, but having the XML is usually the most // important part for tracing/debugging. $this->outgoing_payload = $msg;
$this->incoming_payload = curl_exec($ch); if (!$this->incoming_payload) { $m = 'curl_exec error ' . curl_errno($ch) . ' ' . curl_error($ch); curl_close($ch); return $this->_raiseSoapFault($m); } curl_close($ch);
if (!$this->_parseResponse()) { return $this->fault; }
return $this->response; }
}
|