XML-RPC is a system for making function calls through HTTP using XML message formats. XML-RPC is very different from the REST style of architecture; while both can be elegant solutions in the right hands, they have almost opposite design principles. Where REST invites you to think about the nature of the web and design your application accordingly, XML-RPC tries to make web services fit into the way most developers already think about programming: you call functions and get back results.
In XML-RPC, a function call is encoded as XML and sent to the server using an HTTP POST request. The server then replies with an XML document containing the result of the function call. Here's an example XML-RPC request, calling the function echo with the string argument 'cheese':
POST /RPC2 HTTP/1.0 Host: localhost Content-Type: text/xml Content-length: 190 echo cheese
And here's the response sent back from the server:
HTTP/1.1 200 OK Connection: close Content-Length: 158 Content-Type: text/xml Date: Mon, 6 Jun 2005 12:55:00 GMT You said: cheese
The XML format is simple and uses a limited set of common data types. For more information on XML-RPC, see the book Programming Web Services with XML-RPC, by Simon St.Laurent, Joe Johnston, and Edd Dumbill (O'Reilly).
XML-RPC has the advantage of being extremely easy to learn and use. Its limited set of supported data types makes it easy to implement, so you can find XML-RPC modules for almost any programming language. The twisted.web package supports XML-RPC out of the box, making it very easy to support XML-RPC web services in your application.
5.3.1. How Do I Do That?
The twisted.web.xmlrpc.XMLRPC class is a subclass of twisted.web.resource.Resource that does just about all the work of handling XML-RPC. All you have to do is define methods beginning with xmlrpc_, and it will make them available as XML-RPC functions. The script in Example 5-3 imports the wiki.py Wiki server from Example 5-1 and adds an XML-RPC interface.
Example 5-3. xmlrpc_server.py
import wiki from twisted.web import server, xmlrpc class WikiXmlRpc(xmlrpc.XMLRPC): def _ _init_ _(self, wikiData): self.wikiData = wikiData def xmlrpc_hasPage(self, path): return self.wikiData.hasPage(path) def xmlrpc_listPages(self): return self.wikiData.listPages( ) def xmlrpc_getPage(self, path): return self.wikiData.getPage(path) def xmlrpc_getRenderedPage(self, path): return self.wikiData.getRenderedPage(path) def xmlrpc_setPage(self, path, data): self.wikiData.setPage(path, data) # wikiData.setPage returns None, which has no xmlrpc # representation, so you have to return something else return path if __name__ == "_ _main_ _": import sys from twisted.internet import reactor datafile = sys.argv[1] wikiData = wiki.WikiData(datafile) siteRoot = rest_wiki.RootResource(wikiData) siteRoot.putChild('RPC2', WikiXmlRpc(wikiData)) reactor.listenTCP(8082, server.Site(siteRoot)) reactor.run( )
Run xmlrpc_server.py with a single argument, the name of your Wiki data file:
python xmlrpc_server.py
This command runs a web server on port 8082 that works just like wiki.py from Example 5-1, but with the addition of a resource providing XML-RPC access at /RPC2 (which is somewhat of a standard location for XML-RPC services).
You can test the XML-RPC interface using the xmlrpclib module in the Python standard library:
$ python Python 2.4.1 (#2, Apr 14 2005, 09:13:52) [GCC 4.0.0 20050413 (prerelease) (Debian 4.0-0pre11)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import xmlrpclib >>> wiki = xmlrpclib.ServerProxy('http://localhost:8082/RPC2') >>> wiki.listPages( ) ['WikiHome', 'CurlTest', 'WgetTest', 'RestTest'] >>> wiki.setPage('PythonXmlRpcTest', 'Creating this through XmlRpc!') 'PythonXmlRpcTest' >>> wiki.getRenderedPage('PythonXmlRpcTest') "Creating this through <a href="XmlRpc">XmlRpc</a>!" >>> wiki.listPages( ) ['WikiHome', 'CurlTest', 'WgetTest', 'PythonXmlRpcTest', 'RestTest']
5.3.2. How Does That Work?
The xmlrpc .XMLRPC class is a Resource designed to handle XML-RPC requests. When it receives a POST request, it parses the incoming XML to extract the name of the function being called and the arguments. Then it looks for a matching method with an xmlrpc_ prefix and calls it with the arguments provided by the client. This function can return a Deferred result or a direct value. The XMLRPC class encodes the result as XML and returns it to the client.
XML-RPC has a limited set of data types. You can return strings, integers, floating-point numbers, Booleans, dictionaries, tuples, or lists, and they will be converted automatically to their XML-RPC representations. To send a date value, wrap a date tuple such as the result of time.localtime( ) in an xmlrpc.DateTime object. To send binary data, wrap a binary string in an xmlrpc.Binary object. Other data types, including None, have no equivalent in XML-RPC. Attempting to return an unsupported data type will result in an error, so make sure that you limit your return values to the supported types.
Getting Started
Building Simple Clients and Servers
Web Clients
Web Servers
Web Services and RPC
Authentication
Mail Clients
Mail Servers
NNTP Clients and Servers
SSH
Services, Processes, and Logging