Remote Procedure Calls
Contents
RPC
Remote Procedure Calls (RPC) is a general computing paradigm for invoking procedures on one computer from another. There are many forms of RPC technology, and we will focus on XML-RPC.
XML-RPC
XML-RPC is an RPC protocol that uses XML encoding of RPC requests and responses over HTTP.
Calling Remote Procedures
The calling procedure requests invokation of a function on a remote host using XML. A typical XML call looks something like this:
<?xml version="1.0"?>
<methodCall>
<methodName>FunctionName</methodName>
<params>
<param>
<value><DATATYPE>DATAVALUE</DATATYPE></value>
</param>
</params>
</methodCall>
The server invokes the function with the specified parameters then returns a response like this:
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><DATATYPE>VALUE</DATATYPE></value>
</param>
</params>
</methodResponse>
Data Types
XML-RPC is generalized enough to transmit most any kind of data. The data types are specified like
<DATATYPE>VALUE</DATATYPE>
The types include boolean, dateTime,double,int,string. Binary data has to be encoded at the sender in BASE-64 and decoded at the end by the receiver. The encoded binary data is sent in a base64 tag. It also provides arrays and structs.
XML-RPC Implementations
In order to use XML-RPC, you need to have an XML-RPC library installed for your platform and language. There are several available libraries, depending on your needs. You can find the list at http://www.xmlrpc.com/directory/1568/implementations.
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
}
?>