Remote Procedure Calls

From CSE330 Wiki
Revision as of 02:36, 11 November 2009 by Wiseman (talk | contribs)
Jump to navigationJump to search

XML-RPC

XML-RPC is a spec and a set of implementations that allow software running on disparate operating systems, running in different environments to make procedure calls over the Internet.

Calling Remote Procedures

In XML-RPC, the programs exchange information through XML. The calling procedure request a function on a remote host using XML. A typical XML remote call will be similar to the following:

<?xml version="1.0"?>
<methodCall>
  <methodName>FunctionName</methodName>
  <params>
    <param>
        <value><DATATYPE>DATAVALUE</DATATYPE></value>
    </param>
  </params>
</methodCall>


Then the remote function returns its response in XML

<?xml version="1.0"?>
<methodResponse>
  <params>
    <param>
        <value><DATATYPE>VALUE</DATATYPE></value>
    </param>
  </params>
</methodResponse>

Data Types

XML-RPC is very powerful and can transmit any data. The data types are specified with

    <DATATYPE>VALUE</DATATYPE> 

The types include boolean, dateTime,double,integer,string. The binary data has to be encoded at the sender origin in BASE-64 and decoded at the end by the receiver. The encoded binary data is send in base64 tag. It also provides arrays and a method to define struct.

XML-RPC Implementations

In order to use XML-RPC, you need to have an XML-RPC library installed in your platform. There are several available libraries depending on your platform. You can find the list at http://www.xmlrpc.com/directory/1568/implementations

A typical RPC system could be a PHP page calling a remote procedure to do some specific task in a server. The passed parameters are usually based on browser's actions.

XML-RPC for PHP(PHPXMLRPC)

PHPXMLRPC is one of the several XML-RPC libraries for PHP. It is accessible at http://phpxmlrpc.sourceforge.net/

In order to use this library, all you need to do is to add the include files xmlrpc.inc (to have a PHP client), xmlrpcs.inc (to implement an RPC server). It also provides several helper and wrapper functions that require other include files. You can look at the documentation to learn more about them. For this module, we will concentrate on client functions.

Creating a client in PHP

xmlrpc_client provides a client object. You can create a new client using $client=new xmlrpc_client ( server_url )

It also provides the basic HTTP authentication

  $client=(  http(s)://user:password@server_url:port/path)
 For example, the following code creates a connection to the demo server

<?
include("xmlrpc.inc");
$client = new xmlrpc_client("http://phpxmlrpc.sourceforge.net/server.php");
?>

PHPXMLRPC variables

The parameters (to or from remote procedures) are specified using xmlrpcval function. This function could be called in several format:

new xmlrpcval ( )
new xmlrpcval ( string  )
new xmlrpcval ( VALUE, TYPE )
new xmlrpcval ( ARRAY, ARRAY_TYPE )

If xmlrpcval is called without a parameter, you need to set the variables by calling addScalar, addArray or addStruct functions to initialize the variable. If xmlrpcval is called with a string, the variable is assumed to be of type string, and the value is specified in the string parameter. A third option is to pass the value and type of a variable. The fourth option is to create complex variables of arrays and structures.

$myInt = new xmlrpcvalue(1267, "int");
$myString = new xmlrpcvalue("Hello, World!", "string");
$myBool = new xmlrpcvalue(1, "boolean");
// note: this will serialize a php float value as xml-rpc string
$myString2 = new xmlrpcvalue(1.24, "string");
$myArray = new xmlrpcval(
 array(
   new xmlrpcval("Tom"),
   new xmlrpcval("Dick"),
   new xmlrpcval("Harry")
 ),
 "array");
// recursive struct
$myStruct = new xmlrpcval(
 array(
   "name" => new xmlrpcval("Tom", "string"),
   "age" => new xmlrpcval(34, "int"),
   "address" => new xmlrpcval(
     array(
       "street" => new xmlrpcval("Fifht Ave", "string"),
       "city" => new xmlrpcval("NY", "string")
     ), 
     "struct")
 ), 
 "struct");


Once you have your variables ready, you can send them to the remote server.

Preparing a message

In order to make an RPC, first you need to prepare a message. This is done through xmlrpcmsg class.

$message = new xmlrpcmsg("REMOTE_FUNCTION", PARAMETER_ARRAY);

Remote function is the name of the function on the remote machine. Parameter array stores the parameters. It can be skipped, if the function is not taking any parameters.

For example, the following PHP code sends a message the demo server to call an existing function:

$myParams=array(new xmlrpcval(23, "int"));
$msg = new xmlrpcmsg("examples.getStateName", $myParams);

Calling the function and receiving response

The actual RPC is done when the client's send function is called.

$client->return_type = 'phpvals';
$resp = $client->send($msg);
if ($resp->faultCode())
  echo '  Error: '.$resp->faultString();
else
  echo 'OK: got '.$resp->value();

client's return_type specify either we want to get the response in XML or as php variable. If there was an error during the call, the response's fault code will be set. Otherwise, the value function returns the response value.

Perl Frontier Module

Perl has two modules for XML-RPC. XML::RPC provides a very basic XML-RPC functionality for both client and server side. An alternative is Frontier::* modules. For this module, we will concentrate on Frontier::Daemon which provides server functions.


You can initialize the daemon by calling new function. The arguments include the port address and function names to serve. For example

use Frontier::Daemon;

$methods = {'myservice.convert' => \&convert
    };

Frontier::Daemon->new(LocalPort => 4873,  methods => $methods)
        or die "Couldn't start HTTP server: $!";

Will set up the daemon at port 4873 and the function convert is called from the remote clients as myservice.convert. In the following example, the daemon receives a Base64 encoded image, decodes,save,modify it and sends the modified image back in Base64. Please note that it assumes just single user is using the system at any time, if multiple users access, the files could be overwritten.

#!/usr/bin/perl
use MIME::Base64;
use Frontier::Daemon;
use Frontier::RPC2;

$|=1;
print "server $$";
$methods = {'myservice.convert' => \&convert
    };

#initialize daemon
Frontier::Daemon->new(LocalPort => 4873,  methods => $methods,ReuseAddr=>1)
        or die "Couldn't start HTTP server: $!";

sub convert{
   ($name,$encoded)=@_; #client sends filename and file in base64
   print "Name:$name\n\n\n\n";
   $decoded = MIME::Base64::decode($encoded);
   open(OUT,">/tmp/$name"); #save the image
     print OUT $decoded;
   close(OUT);
    #put a label over the image
    system("convert -pointsize 40 -draw \"text 200,200 'cse330'\" /tmp/$name /tmp/a2.jpg");
   #read back the image
   open(IN,"</tmp/a2.jpg");
   binmode(IN);
   read IN,$de,1000000;
   close(OUT);
    
   #encode it
   $encoded = MIME::Base64::encode($de);
   #print $encoded;
   #send it back
   return {'success' => "true",'result' =>$encoded};



}

On the client side, you may have an upload page:

<html>
<body>
<form enctype="multipart/form-data" action="image-xmlrpc.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
Choose an image  file to upload: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
</body>
</html>



Finally a php based client that would send the uploaded image to RPC server, and display the modified image.

<?

$uploadfile=  $_FILES['uploadedfile']['tmp_name'];
 $handle = fopen($uploadfile,'r');
       $file_content = fread($handle,filesize($uploadfile)); //read uploaded file
       fclose($handle);
       $encoded = chunk_split(base64_encode($file_content)); //encode it
       include("xmlrpc.inc"); //for xmlrpc operations

$client = new xmlrpc_client("http://127.0.0.1:4873/RPC2"); //your host
 
 $myParams=array(new xmlrpcval($_FILES['uploadedfile']['name'], "string"),
                 new xmlrpcval($encoded, "string")
 );
$message = new xmlrpcmsg("myservice.convert", $myParams);
$resp = $client->send($message);
if ($resp->faultCode())
  echo 'KO. Error: '.$resp->faultString();
else{
  $struct=$resp->value();
   $retvalue = $struct->structmem('success'); //value of the field success
    $resultval = $struct->structmem('result'); //result field
        $result = $resultval->scalarval(); //encoded image from the result

       $decoded = base64_decode($result); //decode the base64 data
$handle = fopen("changed.jpg",'w'); //save it to a new file
       $file_content = fwrite($handle,$decoded);
       fclose($handle);

      echo "<img src='changed.jpg'/><br>"; //display the new image

 }


?>