Re: [PATCH] XML_RPC to use pluggable transport methods
| From: | Shane Caraveo | Date: | Wed, 10 Jul 2002 19:43:39 +0000 |
| Subject: | Re: [PATCH] XML_RPC to use pluggable transport methods | ||
| References: | 1 | Groups: | php.pear.dev |
| Request: | Send a blank email to pear-dev+get-7653@lists.php.net to get a copy of this message | ||
Hmm, I'd like to consider making these pluggable transports work with pear::soap also, and eventualy the php-soap extension. The soap library has pluggable extensions also, probably some minor modifications between teh two systems could centralize that better.
Shane
James E. Flemer wrote:
Sure. I've actually made a few more changes. I'll mkae the constructor BC and re-submit. -James On 10 Jul 2002, Stig S. Bakken wrote:Looks good to me, but I'd like the XML_RPC_Client constructor to be backwards-compatible. Shouldn't be hard to recognize whether the first parameter specifies a transport driver or a host name. - Stig On Mon, 2002-07-08 at 19:00, James E. Flemer wrote:[ Please CC on replies, I am not on pear-dev. ] The attached patch changes the XML_RPC package to use pluggable transport methods. The only current transport is HTTP (as it was before this patch), however I plan to be implementing a UNIX domain socket transport method shortly. This should make adding new transports (ssl, tls, carrier pidgeon, etc) very quick and painless. The only backwords compatibility issue with this patch is the change in the constructor for XML_RPC_Client(). (I think the new constructor is much nicer though.) With the new constructor: (shortcut) $rpcClient = new XML_RPC_Client("HTTP","http://betty.userland.com:80/RPC2");or $rpcClient = new XML_RPC_Client("HTTP",array ('host' = 'betty.userland.com', 'path' = '/RPC2', 'proxy' = 'proxy.example.com', ...));Questions? Comments? -James ----diff -Nbru XML_RPC-1.0.3.orig/RPC.php XML_RPC-1.0.3/RPC.php --- XML_RPC-1.0.3.orig/RPC.php Sun May 19 17:34:14 2002 +++ XML_RPC-1.0.3/RPC.php Mon Jul 8 12:36:34 2002 @@ -100,7 +100,7 @@$top=split("&", $string); $op=""; $i=0; - while($i<sizeof($top)) { + while($i<count($top)) { if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs)) { $op.=ereg_replace("^[#a-zA-Z0-9]+;", XML_RPC_lookup_entity($regs[1]),@@ -363,119 +363,57 @@ class XML_RPC_Client {- var $path; - var $server; - var $port; - var $errno; - var $errstring; - var $debug=0; - var $username=""; - var $password=""; + /** + * Transport object + * + * @var object + */ + var $transport = "";++ var $debug = false;- function XML_RPC_Client($path, $server, $port=80, $proxy="", $proxy_port=8080) + function XML_RPC_Client($transportDriver = "HTTP", $options = "") { - $this->port=$port; - $this->server=$server; - $this->path=$path; - $this->proxy = $proxy; - $this->proxy_port = $proxy_port ; + $this->transport = $this->_factory($transportDriver, $options); }- function setDebug($in) + function setDebug($val) { - if ($in) { - $this->debug=1; - } else { - $this->debug=0; - } + $this->debug = ($val) ? true : false; + $this->transport->setDebug($this->debug); }function setCredentials($u, $p) { - $this->username=$u; - $this->password=$p; + $this->transport->setCredentials($u, $p); }- function send($msg, $timeout=0) + function send($msg, $timeout = 0) { - // where msg is an xmlrpcmsg - $msg->debug=$this->debug; - return $this->sendPayloadHTTP10($msg, $this->server, $this->port, - $timeout, $this->username, - $this->password); + $msg->setDebug($this->debug); + return $this->transport->send($msg, $timeout); }- function sendPayloadHTTP10($msg, $server, $port, $timeout=0, - $username="", $password="") + /** + * Return a storage driver based on $driver and $options + * + * @access private + * @static + * @param string $driver Type of storage class to return + * @param string $options Optional parameters for the storage class + * @return object Object Storage object + */ + function _factory($driver, $options = "") { - // If we're using a proxy open a socket to the proxy server instead to the xml-rpc server - if($this->proxy){ - if($timeout > 0) { - $fp=fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr, $timeout); - } - else { - $fp=fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr); - } - } - else { - if($timeout > 0) { - $fp=fsockopen($server, $port, $this->errno, $this->errstr, $timeout); - } - else { - $fp=fsockopen($server, $port, $this->errno, $this->errstr); - } - }-- if(!$fp && $this->proxy) { - PEAR::raiseError("Connection to proxy server ".$this->proxy.":".$this->proxy_port." failed"); - } - else if(!$fp) { - PEAR::raiseError("Connection to RPC server ".$this->server." failed"); - }-- // Only create the payload if it was not created previously - if(empty($msg->payload)) $msg->createPayload();-- // thanks to Grant Rauscher <grant7@firstworld.net> - // for this - $credentials=""; - if ($username!="") { - $credentials="Authorization: Basic " . - base64_encode($username . ":" . $password) . "\r\n"; - }- -- if($this->proxy) { - $op = "POST http://" . $this->server;-- if($this->proxy_port) { - $op .= ":" . $this->port; - } - } - else { - $op = "POST "; - }-- $op .= $this->path. " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" . - "Host: ". $this->server . "\r\n" . - $credentials . - "Content-Type: text/xml\r\nContent-Length: " . - strlen($msg->payload) . "\r\n\r\n" . - $msg->payload; + $transport_path = "XML/RPC/Transport/" . $driver . ".php"; + $transport_class = "XML_RPC_Transport_" . $driver;- // print($op); + require_once $transport_path;- if (!fputs($fp, $op, strlen($op))) { - $this->errstr="Write error"; - return 0; - } - $resp=$msg->parseResponseFile($fp); - fclose($fp); - return $resp; + return new $transport_class($options); }} - class XML_RPC_Response {var $xv;@@ -533,20 +471,35 @@ class XML_RPC_Message {+ var $dirty; var $payload; + var $payloadlength; var $methodname; var $params = array(); var $debug=0;- function XML_RPC_Message($meth, $pars=0) + function XML_RPC_Message($meth, $pars = 0) { - $this->methodname=$meth; - if (is_array($pars) && sizeof($pars)>0) { - for($i=0; $i<sizeof($pars); $i++) + $this->dirty = true; + $this->methodname = $meth; + if (is_array($pars) && count($pars)>0) { + for($i=0; $i<count($pars); $i++) $this->addParam($pars[$i]); } }+ /** + * En/Dis-able debugging + * + * @param val boolean + * @access public + * @return void + */ + function setDebug($val) + { + $this->debug = ($val) ? true : false; + }+function xml_header() { return "<?xml version=\"1.0\"?>\n<methodCall>\n";@@ -559,11 +512,12 @@function createPayload() { + if ($this->dirty) { $this->payload=$this->xml_header(); $this->payload.="<methodName>" . $this->methodname . "</methodName>\n"; - // if (sizeof($this->params)) { + // if (count($this->params)) { $this->payload.="<params>\n"; - for($i=0; $i<sizeof($this->params); $i++) { + for($i=0; $i<count($this->params); $i++) { $p=$this->params[$i]; $this->payload.="<param>\n" . $p->serialize() . "</param>\n";@@ -572,12 +526,22 @@// } $this->payload.=$this->xml_footer(); $this->payload=str_replace("\n", "\r\n", $this->payload); + $this->payloadlength = strlen($this->payload); + $this->dirty = false; + } + }++ function payloadLength() + { + $this->createPayload(); + return $this->payloadlength; }- function method($meth="") + function method($meth = "") { - if ($meth!="") { - $this->methodname=$meth; + if ($meth != "") { + $this->methodname = $meth; + $this->dirty = true; } return $this->methodname; }@@ -588,9 +552,19 @@return $this->payload; }- function addParam($par) { $this->params[]=$par; } - function getParam($i) { return $this->params[$i]; } - function getNumParams() { return sizeof($this->params); } + function addParam($par) + { + $this->params[]=$par; + $this->dirty = true; + }++ function getParam($i) { + return $this->params[$i]; + }++ function getNumParams() { + return count($this->params); + }function parseResponseFile($fp) {@@ -604,7 +578,7 @@function parseResponse($data="") { - global $XML_RPC_xh,$XML_RPC_err,$XML_RPC_str; + global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str; global $XML_RPC_defencoding;$parser = xml_parser_create($XML_RPC_defencoding);@@ -623,29 +597,7 @@xml_set_default_handler($parser, "XML_RPC_dh"); $xmlrpc_value = new XML_RPC_Value;- $hdrfnd=0; - if ($this->debug) - print "<PRE>---GOT---\n" . htmlspecialchars($data) . - "\n---END---\n</PRE>"; - // see if we got an HTTP 200 OK, else bomb - // but only do this if we're using the HTTP protocol. - if (ereg("^HTTP",$data) && - !ereg("^HTTP/[0-9\.]+ 200 ", $data)) { - $errstr= substr($data, 0, strpos($data, "\n")-1); - error_log("HTTP error, got response: " .$errstr); - $r=new XML_RPC_Response(0, $XML_RPC_err["http_error"], - $XML_RPC_str["http_error"]. " (" . - $errstr . ")"); - xml_parser_free($parser); - return $r; - } - // gotta get rid of headers here - if ((!$hdrfnd) && ereg("^(.*)\r\n\r\n",$data,$XML_RPC_xh[$parser]['ha'])) { - $data=ereg_replace("^.*\r\n\r\n", "", $data); - $hdrfnd=1; - }-- if (!xml_parse($parser, $data, sizeof($data))) { + if (!xml_parse($parser, $data, count($data))) { // thanks to Peter Kocks <peter.kocks@baygate.com> if((xml_get_current_line_number($parser)) == 1) $errstr = "XML error at line 1, check URL";@@ -682,10 +634,8 @@$r=new XML_RPC_Response($v); } } - $r->hdrs=split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]); return $r; }- } @@ -824,7 +774,7 @@case 2: // array $rs.="<array>\n<data>\n"; - for($i=0; $i<sizeof($val); $i++) { + for($i=0; $i<count($val); $i++) { $rs.=$this->serializeval($val[$i]); } $rs.="</data>\n</array>";@@ -945,7 +895,7 @@{ reset($this->me); list($a,$b)=each($this->me); - return sizeof($b); + return count($b); }} diff -Nbru XML_RPC-1.0.3.orig/Transport/HTTP.php XML_RPC-1.0.3/Transport/HTTP.php --- XML_RPC-1.0.3.orig/Transport/HTTP.php Wed Dec 31 19:00:00 1969 +++ XML_RPC-1.0.3/Transport/HTTP.php Mon Jul 8 12:41:25 2002 @@ -0,0 +1,270 @@ +<?php +// by Edd Dumbill (C) 1999-2001 +// <edd@usefulinc.com> +// $Id: RPC.php,v 1.7 2002/05/19 21:34:14 ssb Exp $ + +// License is granted to use or modify this software ("XML-RPC for PHP") +// for commercial or non-commercial use provided the copyright of the author +// is preserved in any distributed or derivative work. + +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Adapted to PEAR standards by Stig Sæther Bakken <stig@php.net> and +// Martin Jansen <mj@php.net> and +// James Flemer <jflemer@php.net> + +require_once "PEAR.php"; + +/** + * Transport driver for XML RPC over HTTP/1.0 + * + * @author James Flemer <jflemer@php.net> + * @package XML_RPC + * @version $Revision: 1.11 $ + */ +class XML_RPC_Transport_HTTP +{+ /** + * Additional options for the transport + * @var array + */ + var $options = array();++ /** + * Socket + * @var int + */ + var $sock;++ /** + * Debug flag + * @var boolean + */ + var $debug;++ /** + * Error number from socket op's + * @var int + */ + var $errno;++ /** + * Error string from socket op's + * @var string + */ + var $errstring;++ /** + * Constructor of the transport class + * + * @param $opt url or array of options + * @return object Returns an error object if something went wrong + */ + function XML_RPC_Transport_HTTP($opt) + { + $this->_setDefaults();++ if (is_string($opt)) { + /* assume this is a url (fragment) */ + if (strpos($opt, "://") == false) + $opt = "http://$opt";++ $opt = parse_url($opt); + }++ if (is_array($opt)) + $this->_parseOptions($opt);++ if ($this->options['host'] == "") + return new PEAR_Error("No connection parameters specified!");++ return true; + }++ /** + * En/Dis-able debugging + * + * @param val boolean + * @access public + * @return void + */ + function setDebug($val) + { + $this->debug = ($val) ? true : false; + }++ /** + * Set username and password for HTTP connection + * + * @param string username + * @param string password + * @access public + * @return void + */ + function setCredentials($username, $password) + { + $this->options['user'] = $username; + $this->optiosn['pass'] = $password; + }++ /** + * Send message + * + * @param object message + * @param int timeout + * @access public + * @return object Returns an XML_RPC_Response object + */ + function send($msg, $timeout) + { + // If we're using a proxy open a socket to the proxy server instead to the xml-rpc server + if ($this->options['proxy'] != ""){ + if($timeout > 0) { + $this->sock = fsockopen($this->options['proxy'], $this->options['proxyport'], + $this->errno, $this->errstr, $timeout); + } + else { + $this->sock = fsockopen($this->options['proxy'], $this->options['proxyport'], + $this->errno, $this->errstr); + } + } + else { + if($timeout > 0) { + $this->sock = fsockopen($this->options['host'], $this->options['port'], + $this->errno, $this->errstr, $timeout); + } + else { + $this->sock = fsockopen($this->options['host'], $this->options['port'], + $this->errno, $this->errstr); + } + }++ if ($this->sock === false && $this->options['proxy'] != "") { + return new PEAR_Error("Connection to HTTP proxy server ".$this->options['proxy'].":".$this->options['proxyport']." failed"); + } else if ($this->sock === false) { + return new PEAR_Error("Connection to HTTP RPC server ".$this->options['host'].":".$this->options['port']." failed"); + }++ $msg->createPayload();++ $this->_sendHTTP10($msg);++ $resp = $this->_recvHTTP10($msg);++ fclose($this->sock); + $this->sock = false;++ return $resp; + }++ /** + * _sendHTTP10 + * + * @access private + */ + function _sendHTTP10($msg) + { + // thanks to Grant Rauscher <grant7@firstworld.net> + // for this + $credentials = ""; + if ($this->options['user'] != "") { + $credentials = "Authorization: Basic " . + base64_encode($this->options['user'] . ":" . $this->options['pass']) . "\r\n"; + }++ if ($this->options['proxy']) { + $op = "POST http://" . $this->options['host'] . ":" . $this->options['port']; + } else { + $op = "POST "; + }++ $op .= $this->options['path'] . " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" . + "Host: ". $this->options['host'] . "\r\n" . + $credentials . + "Content-Type: text/xml\r\n" . + "Content-Length: " . $msg->payloadLength() . "\r\n\r\n";++ if (!fputs($this->sock, $op, strlen($op))) { + PEAR::raiseError("Write error sending HTTP headers"); + }++ if (!fputs($this->sock, $msg->payload, $msg->payloadLength())) { + PEAR::raiseError("Write error sending HTTP headers"); + } + // shutdown($this->sock, 1); + }++ /** + * _recvHTTP10 + * + * @access private + */ + function _recvHTTP10($msg) + { + $buf = "";++ while ($chunk = fread($this->sock, 16384)) { + $buf .= $chunk; + }++ if ($this->debug) + echo "<PRE>---GOT---\n" . htmlspecialchars($buf) . "\n---END---\n</PRE>";++ // see if we got an HTTP 200 OK, else bomb + if (ereg("^HTTP", $buf) && !ereg("^HTTP/[0-9\.]+ 200 ", $buf)) { + $errstr = substr($buf, 0, strpos($buf, "\n")-1); + error_log("HTTP error, got response: " .$errstr); + return new XML_RPC_Response(0, $XML_RPC_err["http_error"], + $XML_RPC_str["http_error"]. " ($errstr)"); + }++ // gotta get rid of headers here + list ($headers, $data) = explode("\r\n\r\n", $buf, 2); + $xmlresp = $msg->parseResponse($data); + $pos = strpos($headers, "\r\n"); + if ($pos !== false) + $xmlresp->hdrs = explode("\r\n", substr($headers, $pos + 2));++ return $xmlresp; + }++ /** + * Set some default options + * + * @access private + */ + function _setDefaults() + { + $this->options['host'] = ""; + $this->options['user'] = ""; + $this->options['pass'] = ""; + $this->options['port'] = 80; + $this->options['path'] = "/"; + $this->options['proxy'] = ""; + $this->options['proxyport'] = 8080; + }++ /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + if (isset($this->options[$key])) { + $this->options[$key] = $value; + } + } + }+} ------ PEAR Development Mailing List (http://pear.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php