Re: [PATCH] XML_RPC to use pluggable transport methods
| From: | James E. Flemer | 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
>