S-XML-RPC

S-XML-RPC is an implementation of XML-RPC in Common Lisp for both client and server. Originally it was written by Sven Van Caekenberghe. It is now being maintained by Sven Van Caekenberghe, Rudi Schlatte and Brian Mastenbrook. S-XML-RPC is using S-XML as parser.

XML-RPC is a de facto standard for making remote procedure calls between software running on disparate operating systems, running in different environments and implemented using different languages. XML-RPC is using HTTP as the transport and XML as the encoding. XML-RPC is designed to be as simple as possible, while allowing complex data structures to be transmitted, processed and returned. The protocol is described in detail on http://www.xmlrpc.com/. Some key features (both positive and negative) of the XML-RPC protocol are:

Download

You can download the LLGPL source code and documentation as s-xml-rpc.tgz (signature: s-xml-rpc.tgz.asc for which the public key can be found in the common-lisp.net keyring) (build and/or install with ASDF). There is also CVS access.

API

The plain API exported by the package S-XML-RPC (automatically generated by LispDoc) is available in S-XML-RPC.html.

Usage

Client

Making an XML-RPC call is a two step process: first you encode the call, then you make the call.

? (xml-rpc-call (encode-xml-rpc-call "currentTime.getCurrentTime") :host "time.xmlrpc.com")
#<XML-RPC-TIME 20021216T06:36:33>

? (xml-rpc-call (encode-xml-rpc-call "examples.getStateName" 41) :host "betty.userland.com")
"South Dakota"
If you are behind an HTTP proxy, you have to pass that information along using keyword arguments. For all keyword arguments, there are default global variables (because most of the time you talk to the same host).
? (xml-rpc-call (encode-xml-rpc-call "currentTime.getCurrentTime") :host "time.xmlrpc.com" 
                                                                   :proxy-host "myproxy" 
                                                                   :proxy-port 8080)
When all goes well, an XML-RPC call returns a result that is decoded into a Common Lisp value. A number of things can go wrong: At the moment, all these cases are reported by the standard error mechanism. Later we could use different condition types. There are two structs, xml-rpc-time and xml-rpc-struct, that represent two XML-RPC types, iso8601.dateTime and struct respectively. Two convenience functions with the same name come in handy when creating these structs:
? (xml-rpc-struct :foo 1 :bar -1)
#<#XML-RPC-STRUCT (:BAR . -1) (:FOO . 1)>
	
? (xml-rpc-time 3000000000)
#<XML-RPC-TIME 19950125T06:20:00>
The function xml-rpc-aserve:xml-rpc-call-aserve does the same thing, but uses the (portable) aserve HTTP client API for the networking.

The unit tests in the subdirectory test can serve as (executable) examples. A more complicated example is the server and client implementation of some tests in validator1.lisp. Remember that XML-RPC method (function) names are case-sensitive, as are the names of XML-RPC structure members.

Server

Only a single function call is needed to get the server up and running:

? (start-xml-rpc-server :port 8080)
From now on, your lisp image becomes an XML-RPC server, listening for HTTP requests on port 8080. By default the functions system.listMethods, system.methodSignature, system.methodHelp and system.multicall are available. You can export additional functions from the server by importing symbols in the package contained in *xml-rpc-package* (by default, this is the package S-XML-RPC-EXPORTS). (use-package :common-lisp :s-xml-rpc-exports) makes all of Common Lisp available via xml-rpc.

In more detail, this is what happens:

Customization points are *xml-rpc-package* and *xml-rpc-call-hook*. Setting the variable xml-rpc::*xml-rpc-debug* to t makes the server more verbose. Note that XML-RPC method names are case sensitive: for example, clients have specify "LISP-IMPLEMENTATION-TYPE" for the corresponding Lisp function; a server has to define a function named |login| if his clients look for an implementation of "login".

AppleScript can make client-side XML-RPC calls. So provided you have your lisp XML-RPC server running and have imported + in XML-RPC-EXPORTS, you can have lisp do the math like this:

tell application "http://localhost:8080/RPC2"
  set call_result to call xmlrpc {method name:"+", parameters:{10, 20, 30}}
end tell
display dialog the call_result buttons {"OK"}
Calling the functions xml-rpc-aserve:start-xml-rpc and xml-rpc-aserve:publish-aserve-xml-rpc-handler does the same thing but uses the (portable) aserve server framework to handle incoming HTTP requests.

Type Mapping

This XML-RPC implementation for Common Lisp maps types as in the following table. There is a small difference between what types are accepted by the encoder and what types are returned by the decoder.

XML-RPC Type Accepted Comon Lisp Type Returned Common Lisp Type
string string string
int, i4 integer integer
boolean t or nil t or nil
double float float
base64 any array of 1 dimension with at least (unsigned-byte 8) as element type an array of 1 dimension with (unsigned-byte 8) as element type
is08601.dateTime struct xml-rpc-time struct xml-rpc-time
array list or vector list
struct struct xml-rpc-struct struct xml-rpc-struct

Later, generic functions to encode and decode arbitrary CLOS instances could be added.

Base64

The code in the package "S-BASE64" is an implementation of Base64 encoding and decoding (part of RFC 1521). Encoding takes bytes (a binary stream or a byte array) and produces characters (a character stream or a string). Decoding goes the other way.

Release History

Mailing Lists

CVS version $Id: index.html,v 1.4 2004/07/08 19:36:53 scaekenberghe Exp $

Valid XHTML 1.0 Strict Valid CSS