Lua XML-RPC Services

XML-RPC Home Page
XML-RPC @ Wikipedia

Creating a XML-RPC web service is virtually identical to how you create a JSON-RPC web service. The difference between the XML-RPC and the JSON-RPC is the name.

The following example illustrates the differences:

-- Load the JSON-RPC stack
local jsonrpc = require"jsonrpc"
-- Load the XML-RPC stack
local xmlrpc = require"xmlrpc"

-- Create the JSON-RPC service
local jsonService = jsonrpc.new("service name", serviceDescription)
-- Create the XML-RPC service
local xmlService  =  xmlrpc.new("service name", serviceDescription)

Just like the JSON-RPC stack parses the received JSON into Lua objects, the XML-RPC stack parses the received XML into identical Lua objects. The response is handled in a similar manner, the JSON-RPC stack automatically translates the response data to JSON and the XML-RPC stack automatically translates the response data to XML.

It is for this reason possible to use the same Lua service functions for serving as a XML-RPC service and as a JSON-RPC service. The following example extends the JSON-RPC directory service example to also serve as a XML-RPC service.

-- Load the JSON-RPC stack
local jsonrpc = require"jsonrpc"
-- Load the XML-RPC stack
local xmlrpc = require"xmlrpc"

-- The "add" JSON-RPC and XML-RPC service
local function add(a,b)
   return a + b
end

-- The "subtract" JSON-RPC and XML-RPC service
local function subtract(a,b)
   return a - b
end

-- The Service Description Object used by
-- the JSON-RPC and XML-RPC constructors
local serviceDescription = {
   math={
      add=subtract,
      subtract=subtract,
   }
}

-- Create the JSON-RPC service
local jsonService = jsonrpc.new("JSON-RPC service", serviceDescription)
-- Create the XML-RPC service
local xmlService = xmlrpc.new("XML-RPC service", serviceDescription)

-- The directory callback function.
-- Service JSON-RPC at relative path 'json-rpc' and
-- XML-RPC at relative path 'xml-rpc'
local function webservice(dir,func,path)
   if path == "json-rpc" then
      jsonService:execute(request, response)
   elseif path == "xml-rpc" then
      xmlService:execute(request, response)
   else -- No other services
      response:senderror(404)
   end
end

 -- Create directory: "web-service"
dir = ba.create.dir("web-service", webservice)
ba.dirtop():insert(dir) -- Insert into first root directory

In the above example, the math service is made available at the following URLs:
JSON-RPC clients: http://localhost/web-service/json-rpc
XML-RPC clients: http://localhost/web-service/xml-rpc

Binary Data and Time Conversion

Though JSON-RPC and XML-RPC provide similar capabilities, XML-RPC provides two types that cannot be represented in JSON. The two XML-RPC types dateTime.iso8601 and base64 requires special handling.

dateTime.iso8601

Time can be sent using ISO 8601 encoding. A limitation with the Barracuda XML-RPC stack is that time can only be represented as UNIX time, in other words time cannot be before January 1, 1970.

Receiving:

A dateTime.iso8601 encoded XML-RPC time is automatically converted to a number if the time is within the accepted UNIX time range. A date/time outside of the UNIX time range is returned as a string.

Sending:

You can explicitly tell the XML-RPC stack to return a UNIX time i.e. a number as dateTime.iso8601 encoding by calling xmlrpc.iso8601

The following service function illustrates how to return iso8601 encoded data/time:

local function currentTimeService()
   return xmlrpc.iso8601(os.time())
end

base64

B64 encoding is typically used when sending binary data. A string in Lua may contain any bytes and one can therefore store binary data in Lua strings.

Receiving:

When the XML-RPC stack receives B64 encoded data, the data is automatically converted to a Lua string.

Sending:

When the XML-RPC stack converts the response from a Lua XML-RPC service function to XML, strings are by default converted to the XML-RPC string type. The XML-RPC stack can send a Lua string as B64 encoded data, but you must explicitly tell the XML-RPC stack to do the conversion. The conversion is done by calling xmlrpc.base64

The following two service functions return response data as a XML-RPC string and as XML-RPC base64 encoded data respectively:

local function myService1()
   return "This is a string"
end

local function myService2()
   return xmlrpc.base64("This is a string encoded as B64")
end