Re: [PATCH] XML_RPC to use pluggable transport methods

From: Date: Wed, 10 Jul 2002 04:57:14 +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-7605@lists.php.net to get a copy of this message
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 -- Stig Sæther Bakken, Fast Search & Transfer ASA, Trondheim, Norway http://pear.php.net/wishlist.php/ssb

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