Re: [PATCH] XML_RPC to use pluggable transport methods

From: Date: Wed, 10 Jul 2002 19:16:48 +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-7650@lists.php.net to get a copy of this message
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 >

« previous php.pear.dev (#7650) next »