Machines Talking to Machines Again—XML-RPC

The Web 2.0 revolution is largely built on machine-to-machine interfaces, which allow mashups and APIs and all those good things.

This is the basis of 'web services'. You can offer an interface to your site that allows other people to use it to do something for them. To give a simple example, if you set up a 'web service' that converts temperatures in centigrade to Fahrenheit, the client sends in a request with one parameter (the temperature to be converted) and the server returns the converted value. So, anyone can add a temperature conversion function that appears to be on his or her own site, but is actually calling yours.

XML-RPC allows two machines to talk directly. The receiving site creates a simple API (application programming interface). Anyone who wants to talk to it needs to know that API—what methods are available, what parameters they take, and what the syntax is—for addressing them. Many major sites use this system: Google, for instance, allows you to make direct calls to its search engine or to Google Earth via a published API.

Setting up your own private API is relatively easy, thanks to CI. You need two websites to set this up and to test it, which makes it a little more complex than most things. One site (let's call it the 'receiving' site) is the one that offers the API, listens out for requests, and answers them. (In our example, this is one of the remote sites that we are trying to test and manage.) The other site makes the request using the API and gets the answer back. (In our example, this is the test site itself.)

In the XML-RPC protocol, the two sites talk by means of highly structured XML. (Hence the name XML-RPC—it's short for XML Remote Procedure Call.) The client sends an XML packet to the 'receiving site' server, stating the function it wants to use and any arguments or parameters to be passed. The server decodes the XML and, if it fits the API, calls the function and returns a response, also structured as XML, which the client decodes and acts on.

Your API consists of the functions that the receiving site offers, and instructions for how to use them—e.g., what parameters they take, what data type these should be, etc.

On the receiving site, we create an XML-RPC server, which makes the selected internal methods available to external sites. These 'internal methods' are actually just normal functions within one of your controllers: the server's role is to handle the interface between the external call and the internal function.

There are two sets of problems when you set up an XML-RPC process:

• Getting the two sites to talk to each other

• Making sure that the data is transmitted in a suitable format

Both rely heavily on multi-dimensional arrays, which machines can take in their stride, even if humans need to puzzle over them a bit. CI makes it a lot easier — though it's still quite tricky to get right.

Getting the XML-RPC Server and Client in Touch with Each Other

First, you have to set up a server on the remote site, and a client on the requesting site. This can be done with a few simple lines of code. Let's say we are doing the server in a controller called 'mycontroller' (on the receiving site) and the client in a controller called 'xmlrpc_client' (on the requesting site).

In each case, start off by initializing the CI classes within the constructor. There are two; for a client you only need to load the first, for a server you need to load them both:

$this->load->library('xmlrpc'); $this->load->library('xmlrpcs');

Now, for the server. Close your constructor function, and within the 'mycontroller' index() function, define the functions you are offering up to the outside world. You do this by building a 'functions' sub-array (within the main CI $config array) which maps the names of the incoming requests to the actual functions you want to use:

$config['functions,][,call'] = array('function' => 'mycontroller. myfunction');

$config['functions,][,call2'] = array('function' => 'mycontroller. myfunction2');

In this case, there are two named function calls—'call' and 'call2'. This is what the request asks for. (It doesn't ask for the functions by name, but by the name of the call. Of course, you can use the same name if you wish.) For each call, you define a sub-sub-array giving the 'function' within the controller—i.e. 'myfunction' and 'myfunction2' respectively.

You then finish off your server by initializing it and instantiating it:

$this->xmlrpcs->initialize($config); $this->xmlrpcs->serve();

and it is now ready to listen for requests.

Now you need to go to the other website — the client—and set up an XML-RPC client to make the requests. This should be a separate controller on your client site. It's quite short:

$server_url = 'http://www.mysite.com/index.php/mycontroller'; $this->load->library('xmlrpc'); $this->xmlrpc->set_debug(TRUE); $this->xmlrpc->server($server_url, 80); $this->xmlrpc->method('call');

You define the URL of the receiving site, specifying the controller that contains the XML-RPC server that you want. You load the XML-RPC class, define the server, and the method you want to use—this is the name of the call you want to make, not of the actual function you want to use. If the function you are calling needs parameters, you pass them this way:

$request = array('optimisation,/,sites'); As you see, we're passing two here.

Then, you check if a response has been received, and do something with it:

echo $this->xmlrpc->display_error();

print_r($this->xmlrpc->display_response());

The simplest option is to display it; but in a real application you're more likely to want the machine to analyze it, e.g., by using a regex, and then to act on the results. For instance, if the result contains an error message, you might want to record the error in your database, and take action to report it to the human user.

Formatting XML-RPC Exchanges

Let's use a real, if simplified, example. In this section, we will create an XML-RPC call/ response that lets you remotely trigger a database optimization.

The client we wrote above, is asking for a method known as 'call' and supplying two parameters: 'optimisation' and 'sites'.

The server on the receiving site maps this request for 'call' onto a function called 'myfunction'.

Let's have a look at this function. It's basically an ordinary function within the controller. It attempts to optimize a MySQL database table, and returns 'success' or 'failure' depending on the result.

function myfunction($request) {

$parameters = $request->output_parameters(); $function = $parameters['0']; $table = $parameters['1'];

if ($this->db->query("OPTIMIZE TABLE $table")) {

$response = array(

array(

'function' => array($function, 'string'), 'table' => array($table, 'string'),

return $this->xmlrpc->send_response($response); }

Note the $request, set as the function parameter. This contains the $request array from the client—remember, it had two values, 'optimisation' and 'sites'. CI has transformed the array into an object, $request. So you can't get the individual parameters by treating it as an array, instead you have to use the $request ->output_parameters() method of the $request object. This returns an array, which you interrogate in the normal way.

Using this, we have told the function on the receiving site which table we want to optimize, the 'sites' table. We've also told it what to call the function ('optimisation'). It adds a further parameter called 'result', gets the value, and returns all three to us.

The result it sends back to the client site looks something like this:

<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param> <value> <struct> <member>

<name>function</name> <value>

<string>optimisation</string> </value> </member> <member> <name>table</name>

<string>sites</string> </value> </member> <member> <name>result</name> <value>

<string>Success</string> </value> </member> </struct> </value> </param> </params> </methodResponse>

(Except it's not indented: I did that to make the structure clearer.)

As you can see, our simple three word response (optimisation, exercises, success) has been wrapped in verbose layers of tags, in a way sadly typical of XML, to tell a machine exactly what is going on. There are three <member></member> tag pairs. Each has a <name></name> pair ('function', 'table', 'result' respectively). And each of these has a <value></value> pair, which includes (as well as the data type) the actual information we want—i.e. 'optimisation','sites','success'.

Never mind that I don't like it. Computers thrive on this sort of stuff: it is precise, unambiguous, and easy for a machine to read. This chapter is about computers talking to each other, not about user-friendly interfaces.

Now, your XML-RPC client function on your calling site can extract the values it wants and act on them. It's easy to do this with a regex, because each answer is clearly demarcated by XML mark-up brackets.

Note how CI spares you a lot of fiddling around with angle brackets—you didn't need to write any of this stuff.

Debugging

As soon as you start to test your client/sever combination, you will probably get this message:

The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.

Turn on debugging, by including the line:

$this->xmlrpc->set_debug(TRUE);

in your client. This allows you to see exactly what your client-receiving site combination is sending back to you. Be warned, this is where debugging gets quite frustrating.

There are several places where the exchange can go wrong:

• The remote site is not responding properly. (You may have to temporarily set it to display errors in order to work out why it is not responding. This is annoying if it is an active site. The additional Catch 22 is that it will then display — i.e. return as HTML — error messages, which aren't part of the XML response your client expects, so you will get a second set of error messages, caused by the first set... ) Debugging this may involve quite a lot of FTP transfers back and forth, until you get it right.

• The client code may not be working properly.

• You have got the URL wrong. (This needs to be CI's way of addressing the controller in which the XML_RPC server sits—i.e. http://www.mysite.com/ index.php/mycontroller. If you put all the server code in the controller constructor instead of in the index function, it will still work, but you need to address the function you want to call by name — e.g. http://www.mysite.com/ index.php/mycontroller/myfunction).

• The XML interchange may not be exactly right. The set_debug function allows you to see what is being sent back, but you can spend quite a while staring at this trying to work out where it has gone wrong. (Believe me...)

However, once you get all this right, you've done something quite clever. You've built a function in a remote site, and called it remotely.

In other words, you've set up an application that can do maintenance or other operations on remote sites. If you have several remote sites to manage, you can easily replicate this across them, allowing you (for instance) to optimize all your database tables once a day by one action on just one site.

Issues with XML-RPC?

Security is an issue, of course. You would want to password-protect your function calls, so that the client had to send a password as a parameter before the receiving site responded. This can be done simply by sending the password as an additional parameter in the request, and having the called function check it before responding.

If you were exposing critical functions, you might want the whole thing to take place behind an SSL layer. Our example looks harmless—you might not mind if a hacker repeatedly broke in to your site, but all he or she did was tidy up your database for you each time. On the other hand, it would be a good basis for a Denial of Service attack.

It has to be said that XML-RPC is frustrating and time-consuming to set up and debug, even with CI's very considerable help. You are writing and debugging two sites at once, and the XML format for transmitting data between them can only be called picky. It doesn't let you get away with even the smallest mistake.

Some would argue that XML-RPC is a superseded technology, with most new interfaces or APIs being written in more complex languages such as SOAP (which are even more time-consuming to set up) .

However, for our purposes, XML-RPC is ideal. It allows us to make our remote websites perform complex internal functions without bothering us with the details.

Was this article helpful?

+3 0

Responses

Post a comment