Remote Procedure Calls
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, you need to download and unzip the package. A handful of .inc files are included in the distribution for PHP clients and servers. Include them in your PHP scripts to get access to the functionality in the library. For example, xmlrpc.inc is the client library and xmlrpcs.inc is the server library. 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 functionality.
Creating a client in PHP
xmlrpc_client is the XML-RPC client class. You can create a new client using
$client = new xmlrpc_client( server_url )
It also supports basic HTTP authentication
$client=( http(s)://user:password@server_url:port/path)
For example, the following code creates a connection to the PHPXMLRPC 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 the xmlrpcval function. This function can be called in different ways:
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 the 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 xmlrpcval(1267, "int"); $myString = new xmlrpcval("Hello, World!", "string"); $myBool = new xmlrpcval(1, "boolean"); // note: this will serialize a php float value as xml-rpc string $myString2 = new xmlrpcval(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 the 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 formed above. It can be skipped if the function does not take any parameters.
For example, the following PHP code sends a message to 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 a 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();
The client's return_type specifies either that the result should be stored in XML or as PHP variables. If there was an error during the call, the response's error code will be set. Otherwise, the value function returns the response value.
Perl Frontier Module and PHP client
Perl has a few different modules for XML-RPC. We will use the Frontier::* modules, focusing on Frontier::Daemon which provides XML-RPC server functionality.
You can initialize the daemon with the new function. The arguments include the port number to listen on and function names to serve to clients. For example:
use Frontier::Daemon;
$methods = { 'myservice.convert' => \&convert };
Frontier::Daemon->new(LocalPort => 4873, methods => $methods) or die "Couldn't start HTTP server: $!";
This 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, saves, and modifies it, and then sends the modified image back in Base64. Note that it assumes just single user is using the system at any time. If multiple users access the same server 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 the 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
}
?>