Re: [PATCH] XML_RPC to use pluggable transport methods
| From: | Stig S. Bakken | 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