Initial revision

This commit is contained in:
Tomas Guisasola 2003-12-02 15:11:02 +00:00
commit 9a829802f0
10 changed files with 1490 additions and 0 deletions

17
Makefile Executable file
View File

@ -0,0 +1,17 @@
VERSION= 1.0a
PKG= luaxmlrpc-$(VERSION)
TAR_FILE= $(PKG).tar.gz
ZIP_FILE= $(PKG).zip
SRCS= README Makefile \
xmlrpc.lua xrh.lua xrc.lua test.lua \
index.html manual.html license.html lua.png
dist:
mkdir $(PKG)
cp $(SRCS) $(PKG)
tar -czf $(TAR_FILE) $(PKG)
zip -lq $(ZIP_FILE) $(PKG)
rm -rf $(PKG)
clean:
rm $(TAR_FILE) $(ZIP_FILE)

19
README Executable file
View File

@ -0,0 +1,19 @@
LuaXMLRPC is a library of functions to deal with XML-RPC.
This library depends on LuaExpat.
The standalone server depends on LuaSocket for Lua 5.0 also.
Here goes a small description of the files in the distribution
index.html -- Home page
license.html -- Copyright & License
lua.png -- Lua Logo
Makefile -- Makefile for Unix systems
manual.html -- Reference manual
README -- This file
test.lua -- Overall API test script
xmlrpc.lua -- Source file
xrc.lua -- Extension server library over CGI
xrh.lua -- Extension server library over HTTP
$Id$

126
doc/index.html Executable file
View File

@ -0,0 +1,126 @@
<! See Copyright Notice in license.html>
<html>
<!$Id$>
<head>
<style type="text/css">
ul { list-style-type: disc };
</style>
</head>
<body bgcolor="#FFFFFF">
<hr>
<center>
<table border=0 cellspacing=2 cellpadding=2>
<tr><td align=center><a href="http://www.lua.org">
<img border=0 alt="The Lua language" src="lua.png"></a>
<tr><td align=center><big><b>LuaXMLRPC</b></big>
<tr><td align=center valign=top>A Lua library for XML-RPC
</table>
</center>
<p>
<center><small>
<a href=#over>overview</a> &middot;
<a href=#version>current version</a> &middot;
<a href=#download>download</a> &middot;
<a href=#new>what's new</a> &middot;
<a href=#installation>installation</a> &middot;
<a href="manual.html">manual</a> &middot;
<a href=#hist>history</a>
</small></center>
<p>
<hr>
<h2>Contents</h2>
<p>
<ul>
<li> <a href=#over>Overview</a>
<li> <a href=#version>Current Version</a>
<li> <a href=#download>Download</a>
<li> <a href=#new>What's new</a>
<li> <a href=#installation>Installation</a>
<li> <a href="manual.html">User's manual</a>
<ul>
<li> <a href="manual.html#introduction">Introduction</a>
<li> <a href="manual.html#examples">Examples</a>
</ul>
<li> <a href=#hist>History</a>
</ul>
</p>
<a name=over>
<h2>Overview</h2>
<p>
LuaXMLRPC is a library to make remote procedure calls over
<a href="http://www.xmlrpc.com">XML-RPC</a>.
<a name=version>
<h2>Current version</h2>
<p>
Current version is 1.0 alpha.
</p>
<a name=download>
<h2>Download</h2>
LuaXMLRPC can be downloaded in source code from the following links: <p>
<blockquote>
<a href="http://poison.les.inf.puc-rio.br/luaxmlrpc/luaxmlrpc-1.0a.tar.gz">luaxmlrpc-1.0a.tar.gz</a><br>
<a href="http://poison.les.inf.puc-rio.br/luaxmlrpc/luaxmlrpc-1.0a.zip">luaxmlrpc-1.0a.zip</a>
</blockquote><p>
<a name=new>
<h2>What's new</h2>
<p>
<ul>
<li>[?/?/2003] Version 1.0 alpha released
</ul>
</p>
<a name="installation"></a>
<h2>Installation</h2>
<p>
LuaXMLRPC is a Lua library so the installation is very simple.
</p>
<a name=hist>
<h2>History</h2>
<p>
LuaXMLRPC development was sponsored by
<a href="http://www.fabricadigital.com.br">Fábrica Digital</a>
and FINEP.
</p>
<p>
<center><small>
<a href=#over>overview</a> &middot;
<a href=#version>current version</a> &middot;
<a href=#download>download</a> &middot;
<a href=#new>what's new</a> &middot;
<a href=#installation>installation</a> &middot;
<a href="manual.html">manual</a> &middot;
<a href=#hist>history</a>
</small></center>
<p>
<hr>
<small>
Last modified on
Tue Oct 28 15:28:41 BRST 2003
</small>
</body>
</html>

80
doc/license.html Executable file
View File

@ -0,0 +1,80 @@
<! See Copyright Notice at the end of this file>
<HTML>
<HEAD>
<TITLE>LuaXMLRPC: license</TITLE>
</HEAD>
<!$Id$>
<BODY BGCOLOR="#FFFFFF">
<HR>
<H1>
License
</H1>
LuaXMLRPC
is free software:
it can be used for both academic and commercial purposes at absolutely no cost.
There are no royalties or GNU-like "copyleft" restrictions.
LuaXMLRPC qualifies as
<A HREF="http://www.opensource.org/docs/definition.html">Open Source</A>
software.
Its licenses are compatible with
<A HREF="http://www.gnu.org/licenses/gpl.html">GPL</A>.
LuaXMLRPC is not in the public domain and
<a href="http://www.keplerproject.org">The Kepler Project</a>
keep its copyright.
The legal details are below.
<P>
The spirit of the license is that
you are free to use LuaXMLRPC for any purpose at no cost without having to ask us.
The only requirement is that
if you do use LuaXMLRPC,
then you should give us credit by including the appropriate copyright notice
somewhere in your product or its documentation.
<P>
The LuaXMLRPC library is designed and implemented
by
Roberto Ierusalimschy,
Andr&eacute; Carregal and
Tom&aacute;s Guisasola.
The implementation is not derived from licensed software.
<P>
<!-- ===================================================================== -->
<HR>
Copyright &copy; 2003 The Kepler Project.
<P>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
<P>
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
<P>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
<P>
<!-- ===================================================================== -->
<HR>
<SMALL>
Last update:
Fri Nov 28 15:57:05 BRST 2003
</SMALL>
</BODY>
</HTML>

288
doc/manual.html Executable file
View File

@ -0,0 +1,288 @@
<! See Copyright Notice in license.html>
<html>
<!$Id$>
<head>
<style type="text/css">
ul { list-style-type: disc };
</style>
</head>
<body bgcolor="#FFFFFF">
<hr>
<center>
<table border=0 cellspacing=2 cellpadding=2>
<tr><td align=center><a href="http://www.lua.org">
<img border=0 alt="The Lua language" src="lua.png"></a>
<tr><td align=center><big><b>LuaXMLRPC Reference Manual</b></big>
<tr><td align=center valign=top>A Lua library for Remote Procedure Call over HTTP
</table>
</center>
<p>
<center><small>
<a href="index.html">home</a> &middot;
<a href="#data_types">data types</a> &middot;
<a href="#client">client</a> &middot;
<a href="#server">server</a> &middot;
<a href="#toxml">xml elements</a> &middot;
<a href="#xmlrpc_parser">xml-rpc parser</a> &middot;
<a href="#examples">example</a> &middot;
<a href="#related_docs">related docs</a>
</small></center>
<p>
<hr>
<a name="introduction"></a>
<h2>Introduction</h2>
<p>
LuaXMLRPC is a <a href="http://www.lua.org">Lua</a> library
to manage <a href="http://www.xmlrpc.com">XML-RPC</a> clients and servers.
It enables a Lua program to:
<ul>
<li> Make a remote procedure call to a XML-RPC server
without having to deal with XML code
<li> Register Lua functions/scripts ??????
to respond to remote procedure calls
<li> Transform Lua objects into XML-RPC data types and vice-versa
</ul>
<p>
LuaXMLRPC provides simple API and an abstraction layer over XML
avoiding manipulation of string representation of data structures.
It also offers ways to express everything when needed.
<p>
The library is divided into four Lua files:
<ul>
<li> <b><code>xmlrpc.lua</code></b> basic support:
build client requests and server responses;
and disassemble client requests and server responses.
<li> <b><code>toxml.lua</code></b> convert Lua objects to XML
<li> <b><code>xrp.lua</code></b> parse XML-RPC objects to Lua
</ul>
<p>
LuaXMLRPC is based on
<a href="http://www.tecgraf.puc-rio.br/luasocket">LuaSocket</a>,
<a href="http://poison.les.inf.puc-rio.br/luaexpat">LuaExpat</a>
and on <a href="http://www.lua.org">Lua 5.0</a>.
<a name="data_types"></a>
<h2>Data types</h2>
<p>
XML-RPC elements are usually represented by the simplest correspondent
Lua object.
When the correspondance is not obvious,
a Lua table is used with a field specifying the element.
<a name="xr2lua"</a>
<h3>From XML-RPC to Lua</h3>
<p>
When converting from XML-RPC element to a Lua object,
a table with a field <code>tag</code> is used.
The child elements are stored at numbered indexes
and white space is ignored.
<table border="1">
<tr>
<td>XML-RPC data type
<td>Lua object
</tr>
<tr>
<td>double<br>int<br>i4
<td>number
</tr>
<tr>
<td>string
<td>string
</tr>
<tr>
<td>boolean
<td>boolean
</tr>
<tr>
<td>struct<br>arrray
<td>table
</tr>
<tr>
<td>other elements
<td><pre>{
tag = "element name",
[1] = &lt;first child&gt;,
[2] = &lt;second child&gt;,
[3] = ...,
}</pre>
</tr>
</table>
<a name="lua2xr"</a>
<h3>From Lua to XML-RPC</h3>
<p>
A convertion from a Lua object to an XML-RPC can be made automatically
or explicitly.
The automatic conversion rules are:
<table border="1">
<tr>
<td>Lua object
<td>XML-RPC data type
</tr>
<tr>
<td>number
<td>double
</tr>
<tr>
<td>string
<td>string
</tr>
<tr>
<td>boolean
<td>boolean
</tr>
<tr>
<td><code>{ key = val }</code>
<td><pre>
&lt;struct&gt;
&lt;member&gt;
&lt;name&gt;key&lt;/name&gt;
&lt;value&gt;<i>val</i>&lt;/value&gt;
&lt;/member&gt;
&lt;/struct&gt;</pre>
<small><i>val</i> is converted according to the same rules.</small>
</tr>
</table>
In case of a table that has numeric keys,
the resulting struct will have numbers as keys (but they will be
treated as strings).
<p>
Explicit conversions can be forced by the creation of <i>typed values</i>
(see function <code><a href="#createtypedvalue">createTypedValue</a></code>).
<a name="basic"></a>
<h2>Basic support</h2>
<p>
<ul>
<a name="client_encode"></a>
<li> <b><code>client_encode (method_name, params*) => method_call</code></b> <br>
Build a XML-RPC document containing a <code>methodCall</code> element.
It receives a string with the method's name and an optional list of
parameters.
The result is a string containing the XML-RPC document.
<a name="client_decode"></a>
<li> <b><code>client_decode (method_response) => ok, list_results</code></b> <br>
Disassemble the server response into a Lua object.
It receives a string containing the XML-RPC document representing
the <code>methodResponse</code> element.
The result is a boolean indicating wether the call was successful or not
and a Lua table containing the results of the call,
equivalent to the <code>params</code> element.
<a name="server_decode"></a>
<li> <b><code>server_decode (method_call) => method_name, list_params</code></b> <br>
Disassemble the client request into a method's name and a Lua object.
It receives a string containing the XML-RPC document representing
the <code>methodCall</code> element.
The result is a string with the name of the method to be called
and a Lua table with all the arguments to be passed to it.
<a name="server_encode"></a>
<li> <b><code>server_encode (object) => method_response</code></b> <br>
Build a XML-RPC document containing a <code>methodResponse</code> element.
It receives a Lua object (a number, a string, a table etc.) with the
response of the call to be sent.
The result is a string containing the XML-RPC document.
<a name="createtypedvalue"></a>
<li> <b><code>createTypeValue (value, type)</code></b> <br>
</ul>
<a name="client"></a>
<h2>Client side</h2>
<p>
The client side library offers the following functions:
<ul>
<a name="call"></a>
<li> <b><code>call (url, method, params*)</code></b> <br>
Execute the call to <code>method</code> at location <code>url</code>
with the given <code>params</code> (if any).
The result is a Lua object containing the response.
It could be a <code>&lt;params&gt;</code> or a <code>&lt;fault&gt;</code>
XML-RPC element.
</ul>
<a name="server"></a>
<h2>Server side</h2>
<p>
<a name="toxml"></a>
<h2>Creating XML elements</h2>
<p>
<a name="xmlrpc_parser"></a>
<h2>Parsing XML-RPC elements</h2>
<p>
<a name="examples"></a>
<h2>Example</h2>
Below is a small sample code displaying the basic use of the library.
<blockquote>
<pre>
require "xmlrpc"
</pre>
</blockquote>
<a name="related_docs"></a>
<h2>Related documentation</h2>
Here is a list of related documentation:
<ul>
<li> <a href="http://www.xmlrpc.com">http://www.xmlrpc.com</a>
</ul>
<a name="contents"></a>
<h2>Contents</h2>
<p>
<ul>
<li> <a href="#introduction">Introduction</a>
<li> <a href="#examples">Example</a>
<li> <a href="#related_docs">Related documentation</a>
</ul>
</p>
<p>
<center><small>
<a href="index.html">home</a> &middot;
<a href="#examples">example</a> &middot;
<a href="#related_docs">related docs</a>
</small></center>
<p>
<hr>
<small>
Last modified on
Mon Sep 1 15:54:23 BRT 2003
</small>
</body>
</html>

BIN
lua.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

589
src/xmlrpc.lua Executable file
View File

@ -0,0 +1,589 @@
---------------------------------------------------------------------
-- XML-RPC implementation for Lua.
-- See Copyright Notice in license.html
-- $Id$
---------------------------------------------------------------------
require "lxp"
require "dom"
local assert, error, ipairs, pairs, type, tonumber, unpack = assert, error, ipairs, pairs, type, tonumber, unpack
local format, gsub, strfind, strsub = string.format, string.gsub, string.find, string.sub
local concat, getn, tinsert = table.concat, table.getn, table.insert
local ceil = math.ceil
local parse = dom.parse
local Public = {}
setmetatable (Public, {__newindex == function (n)
error ("undeclared variable "..n, 2)
end})
xmlrpc = Public
setfenv (1, Public)
---------------------------------------------------------------------
-- XML-RPC Parser
---------------------------------------------------------------------
---------------------------------------------------------------------
local function trim (s)
return (type(s) == "string" and gsub (s, "^%s*(.-)%s*$", "%1"))
end
---------------------------------------------------------------------
local function is_space (s)
return type(s) == "string" and trim(s) == ""
end
---------------------------------------------------------------------
-- Get next non-space element from tab starting from index i.
-- @param tab Table.
-- @param i Numeric index.
-- @return Object and its position on table; nil and an invalid index
-- when there is no more elements.
---------------------------------------------------------------------
function next_nonspace (tab, i)
if not i then i = 1 end
while is_space (tab[i]) do i = i+1 end
return tab[i], i
end
---------------------------------------------------------------------
-- Get next element of tab with the given tag starting from index i.
-- @param tab Table.
-- @param tag String with the name of the tag.
-- @param i Numeric index.
-- @return Object and its position on table; nil and an invalid index
-- when there is no more elements.
---------------------------------------------------------------------
local function next_tag (tab, tag, i)
if not i then i = 1 end
while tab[i] do
if type (tab[i]) == "table" and tab[i].tag == tag then
return tab[i], i
end
i = i + 1
end
return nil, i
end
---------------------------------------------------------------------
local function x2number (tab)
if tab.tag == "int" or tab.tag == "i4" or tab.tag == "double" then
return tonumber (next_nonspace (tab, 1), 10)
end
end
---------------------------------------------------------------------
local function x2boolean (tab)
if tab.tag == "boolean" then
local v = next_nonspace (tab, 1)
return v == true or v == "true" or false
end
end
---------------------------------------------------------------------
local function x2string (tab)
return tab.tag == "string" and tab[1]
end
---------------------------------------------------------------------
local function x2date (tab)
return tab.tag == "dateTime.iso8601" and next_nonspace (tab, 1)
end
---------------------------------------------------------------------
local function x2base64 (tab)
return tab.tag == "base64" and next_nonspace (tab, 1)
end
---------------------------------------------------------------------
local function x2name (tab)
return tab.tag == "name" and next_nonspace (tab, 1)
end
local x2value
---------------------------------------------------------------------
-- Disassemble a member object in its name and value parts.
-- @param tab Table with a DOM representation.
-- @return String (name) and Object (value).
-- @see x2name, x2value.
---------------------------------------------------------------------
local function x2member (tab)
return
x2name (next_tag(tab,"name")),
x2value (next_tag(tab,"value"))
end
---------------------------------------------------------------------
-- Disassemble a struct object into a Lua table.
-- @param tab Table with DOM representation.
-- @return Table with "name = value" pairs.
---------------------------------------------------------------------
local function x2struct (tab)
if tab.tag == "struct" then
local res = {}
for i = 1, getn (tab) do
if not is_space (tab[i]) then
local name, val = x2member (tab[i])
res[name] = val
end
end
return res
end
end
---------------------------------------------------------------------
-- Disassemble an array object into a Lua table.
-- @param tab Table with DOM representation.
-- @return Table.
---------------------------------------------------------------------
local function x2array (tab)
if tab.tag == "array" then
local d = next_tag (tab, "data")
local res = {}
for i = 1, getn (d) do
if not is_space (d[i]) then
tinsert (res, x2value (d[i]))
end
end
return res
end
end
---------------------------------------------------------------------
local xmlrpc_types = {
int = x2number,
i4 = x2number,
boolean = x2boolean,
string = x2string,
double = x2number,
["dateTime.iso8601"] = x2date,
base64 = x2base64,
struct = x2struct,
array = x2array,
}
local x2param
---------------------------------------------------------------------
-- Disassemble a methodResponse into a Lua object.
-- @param tab Table with DOM representation.
-- @return Boolean (indicating wether the response was successful)
-- and (a Lua table with the return values OR the fault string and
-- the fault code).
---------------------------------------------------------------------
local function x2methodResponse (tab)
assert (type(tab) == "table", "Not a table")
assert (tab.tag == "methodResponse",
"Not a `methodResponse' tag: "..tab.tag)
local t = next_nonspace (tab, 1)
if t.tag == "params" then
return true, x2param (t)
elseif t.tag == "fault" then
local f = x2fault (t)
return false, f.faultString, f.faultCode
else
error ("Couldn't find a <params> nor a <fault> element")
end
end
---------------------------------------------------------------------
-- Disassemble a value element into a Lua object.
-- @param tab Table with DOM representation.
-- @return Object.
---------------------------------------------------------------------
x2value = function (tab)
local t = tab.tag
assert (t == "value", "Not a `value' tag: "..t)
local n = next_nonspace (tab)
if type(n) == "string" or type(n) == "number" then
return n
elseif type (n) == "table" then
local t = n.tag
local get = xmlrpc_types[t]
if not get then error ("Invalid <"..t.."> element") end
return get (next_nonspace (tab))
end
end
---------------------------------------------------------------------
-- Disassemble a fault element into a Lua object.
-- @param tab Table with DOM representation.
-- @return Object.
---------------------------------------------------------------------
local function x2fault (tab)
assert (tab.tag == "fault", "Not a `fault' tag: "..tab.tag)
return x2value (next_nonspace (tab))
end
---------------------------------------------------------------------
-- Disassemble a param element into a Lua object.
-- Ignore white spaces between elements.
-- @param tab Table with DOM representation.
-- @return Object.
---------------------------------------------------------------------
x2param = function (tab)
assert (tab.tag == "params", "Not a `params' tag")
local res = {}
local p, i = next_nonspace (tab, 1)
while p do
if p.tag == "param" then
tinsert (res, x2value (next_tag (p, "value")))
end
p, i = next_nonspace (tab, i+1)
end
return res
end
---------------------------------------------------------------------
-- Disassemble a methodName element into a Lua object.
-- @param tab Table with DOM representation.
-- @return Object.
---------------------------------------------------------------------
local function x2methodName (tab)
assert (tab.tag == "methodName", "Not a `methodName' tag: "..tab.tag)
return (next_nonspace (tab, 1))
end
---------------------------------------------------------------------
-- Disassemble a methodCall element into its name and a list of parameters.
-- @param tab Table with DOM representation.
-- @return Object.
---------------------------------------------------------------------
local function x2methodCall (tab)
assert (tab.tag == "methodCall", "Not a `methodCall' tag: "..tab.tag)
return
x2methodName (next_tag (tab,"methodName")),
x2param (next_tag (tab,"params"))
end
---------------------------------------------------------------------
-- End of XML-RPC Parser
---------------------------------------------------------------------
---------------------------------------------------------------------
-- Convert a Lua Object into an XML-RPC string.
---------------------------------------------------------------------
---------------------------------------------------------------------
local formats = {
boolean = "<boolean>%d</boolean>",
number = "<double>%d</double>",
string = "<string>%s</string>",
array = "<array><data>\n%s\n</data></array>",
double = "<double>%s</double>",
int = "<int>%s</int>",
struct = "<struct>%s</struct>",
member = "<member><name>%s</name>%s</member>",
value = "<value>%s</value>",
param = "<param>%s</param>",
params = [[
<params>
%s
</params>]],
fault = [[
<fault>
%s
</fault>]],
methodCall = [[
<?xml version="1.0"?>
<methodCall>
<methodName>%s</methodName>
%s
</methodCall>
]],
methodResponse = [[
<?xml version="1.0"?>
<methodResponse>
%s
</methodResponse>]],
}
formats.table = formats.struct
local toxml = {}
toxml.double = function (v,t) return format (formats.double, v) end
toxml.int = function (v,t) return format (formats.int, v) end
toxml.string = function (v,t) return format (formats.string, v) end
---------------------------------------------------------------------
-- Build a XML-RPC representation of a boolean.
-- @param v Object.
-- @return String.
---------------------------------------------------------------------
function toxml.boolean (v)
local n = (v and 1) or 0
return format (formats.boolean, n)
end
---------------------------------------------------------------------
-- Build a XML-RPC representation of a number.
-- @param v Object.
-- @param t Object representing the XML-RPC type of the value.
-- @return String.
---------------------------------------------------------------------
function toxml.number (v, t)
local tt = type(t) == "table" and t["*type"]
if tt == "int" or tt == "i4" then
return toxml.int (v, t)
elseif tt == "double" then
return toxml.double (v, t)
elseif v == ceil(v) then
return toxml.int (v, t)
else
return toxml.double (v, t)
end
end
---------------------------------------------------------------------
-- @param typ Object representing a type.
-- @return Function that generate an XML element of the given type.
-- The object could be a string (as usual in Lua) or a table with
-- a field named "type" that should be a string with the XML-RPC
-- type name.
---------------------------------------------------------------------
local function format_func (typ)
if type (typ) == "table" then
return toxml[typ.type]
else
return toxml[typ]
end
end
---------------------------------------------------------------------
-- @param val Object representing an array of values.
-- @param typ Object representing the type of the value.
-- @return String representing the equivalent XML-RPC value.
---------------------------------------------------------------------
function toxml.array (val, typ)
local ret = {}
local et = typ.elemtype
local f = format_func (et)
for i,v in ipairs (val) do
tinsert (ret, format (formats.value, f (v, et)))
end
return format (formats.array, concat (ret, '\n'))
end
---------------------------------------------------------------------
---------------------------------------------------------------------
function toxml.struct (val, typ)
local ret = {}
if type (typ) == "table" then
for n,t in pairs (typ.elemtype) do
local f = format_func (t)
tinsert (ret, format (formats.member, n, f (val[n], t)))
end
else
for i, v in pairs (val) do
tinsert (ret, toxml.member (i, v))
end
end
return format (formats.struct, concat (ret))
end
toxml.table = toxml.struct
---------------------------------------------------------------------
---------------------------------------------------------------------
function toxml.member (n, v)
return format (formats.member, n, toxml.value (v))
end
---------------------------------------------------------------------
-- Get type and value of object.
---------------------------------------------------------------------
local function type_val (obj)
local t = type (obj)
local v = obj
if t == "table" then
t = obj["*type"] or "table"
v = obj["*value"] or obj
end
return t, v
end
---------------------------------------------------------------------
-- Convert a Lua object to a XML-RPC object (plain string).
---------------------------------------------------------------------
function toxml.value (obj)
local to, val = type_val (obj)
if type(to) == "table" then
return format (formats.value, toxml[to.type] (val, to))
else
-- primitive (not structured) types.
--return format (formats[to], val)
return format (formats.value, toxml[to] (val, to))
end
end
---------------------------------------------------------------------
-- @param params_list Table with list of parameters.
-- @return String representing the `params' XML-RPC element.
---------------------------------------------------------------------
function toxml.params (params_list)
for i = 1, getn (params_list) do
params_list[i] = format (formats.param, toxml.value (params_list[i]))
end
return format (formats.params, concat (params_list, '\n '))
end
---------------------------------------------------------------------
-- @param method String with method's name.
-- @param params_list Table with list of parameters.
-- @return String representing the `methodCall' XML-RPC element.
---------------------------------------------------------------------
function toxml.methodCall (method, params_list)
local idx = strfind (method, "[^A-Za-z_.:/0-9]")
if idx then
error (format ("Invalid character `%s'", strsub (method, idx, idx)))
end
return format (formats.methodCall, method, toxml.params (params_list))
end
---------------------------------------------------------------------
-- @param err String with error message.
-- @return String representing the `fault' XML-RPC element.
---------------------------------------------------------------------
function toxml.fault (err)
local code
local message = err
if type (err) == "table" then
code = err.code
message = err.message
end
return format (formats.fault, toxml.struct {
faultCode = { ["*type"] = "int", ["*value"] = code or faultCode or 1 },
faultString = message or faultString or "fatal error",
})
end
---------------------------------------------------------------------
-- @param ok Boolean indicating if the response was correct or a
-- fault one.
-- @param params_list Object containing the response contents.
-- @return String representing the `methodResponse' XML-RPC element.
---------------------------------------------------------------------
function toxml.methodResponse (ok, params_list)
local resp
if ok then
resp = toxml.params (params_list)
else
resp = toxml.fault (params_list)
end
return format (formats.methodResponse, resp)
end
---------------------------------------------------------------------
-- End of converter from Lua to XML-RPC.
---------------------------------------------------------------------
---------------------------------------------------------------------
-- Create a representation of an array with the given element type.
---------------------------------------------------------------------
function createArray (elemtype)
return { type = "array", elemtype = elemtype, }
end
---------------------------------------------------------------------
-- Create a representation of a structure with the given members.
---------------------------------------------------------------------
function createStruct (members)
return { type = "struct", elemtype = members, }
end
---------------------------------------------------------------------
-- Create a representation of a value according to a type.
-- @param val Any Lua value.
-- @param typ A XML-RPC type.
---------------------------------------------------------------------
function createTypedValue (val, typ)
return { ["*type"] = typ, ["*value"] = val }
end
---------------------------------------------------------------------
-- Create the XML-RPC string used to call a method.
-- @param method String with method name.
-- @param ... Parameters to the call.
-- @return String with the XML string/document.
---------------------------------------------------------------------
function client_encode (method, ...)
return toxml.methodCall (method, arg)
end
---------------------------------------------------------------------
-- Convert the method response document to a Lua table.
-- @param meth_resp String with XML document.
-- @return Boolean indicating whether the call was successful or not;
-- and a Lua table with the converted response element.
---------------------------------------------------------------------
function client_decode (meth_resp)
local d = parse (meth_resp)
if type(d) ~= "table" then
error ("Not an XML document: "..meth_resp)
end
return x2methodResponse (d)
end
---------------------------------------------------------------------
-- Convert the method call (client request) document to a name and
-- a list of parameters.
-- @param request String with XML document.
-- @return String with method's name AND a table with the parameters.
---------------------------------------------------------------------
function server_decode (request)
local d = parse (request)
if type(d) ~= "table" then
error ("Not an XML document: "..request)
end
return x2methodCall (d)
end
---------------------------------------------------------------------
-- Convert a table into an XML-RPC methodReponse element.
-- @param obj Lua object.
-- @param is_fault Boolean indicating wether the result should be
-- a `fault' element (default = false).
-- @return String with XML-RPC responde.
---------------------------------------------------------------------
function server_encode (obj, is_fault)
local ok = not (is_fault or false)
return toxml.methodResponse (ok, { obj })
end
---------------------------------------------------------------------
-- Register the methods.
-- @param tab_or_func Table or mapping function.
-- If a table is given, it can have one level of objects and then the
-- methods;
-- if a function is given, it will be used as the dispatcher.
-- The given function should return a function.
---------------------------------------------------------------------
local dispatch = error
function server_methods (tab_or_func)
local t = type (tab_or_func)
if t == "function" then
dispatch = tab_or_func
elseif t == "table" then
dispatch = function (name)
local ok, _, obj, method = string.find (name, "^([^.]+)%.(.+)$")
if not ok then
return tab_or_func[name]
else
return tab_or_func[obj][method]
end
end
else
error ("Argument is neither a table nor a function")
end
end

202
tests/test.lua Executable file
View File

@ -0,0 +1,202 @@
#!/usr/local/bin/lua.5.0
-- See Copyright Notice in license.html
require "xmlrpc"
require "xrh"
function table.print (tab, indent, spacing)
spacing = spacing or ""
indent = indent or "\t"
io.write ("{\n")
for nome, val in pairs (tab) do
io.write (spacing..indent)
local t = type(nome)
if t == "string" then
io.write (string.format ("[%q] = ", tostring (nome)))
elseif t == "number" or t == "boolean" then
io.write (string.format ("[%s] = ", tostring (nome)))
else
io.write (t)
end
t = type(val)
if t == "string" or t == "number" then
io.write (string.format ("%q", val))
elseif t == "table" then
table.print (val, indent, spacing..indent)
else
io.write (t)
end
io.write (",\n")
end
io.write (spacing.."}")
end
function table.equal (t1, t2)
if type(t1) ~= "table" or type(t2) ~= "table" then
return false
end
for key, v1 in t1 do
local v2 = rawget (t2, key)
if type(v1) == "table" and type(v2) == "table" then
if not table.equal (v1, v2) then
return false
end
elseif v1 ~= v2 then
return false
end
end
return true
end
function call_test (xml_call, method, ...)
local xc = string.gsub (xml_call, "(%p)", "%%%1")
xc = string.gsub (xc, "\r?\n%s*", "%%s*")
arg.n = nil
-- client enconding test
local meth_call = xmlrpc.client_encode (method, unpack (arg))
local s = string.gsub (meth_call, xc, "")
s = string.gsub (s, "%s*", "")
assert (s == "", s)
-- server decoding test
local meth_call, param = xmlrpc.server_decode (xml_call)
assert (meth_call == method, meth_call)
assert (table.equal (arg, param))
end
function response_test (xml_resp, lua_obj)
-- client decoding test
local ok, obj = xmlrpc.client_decode (xml_resp)
assert (table.equal (obj, { lua_obj }))
-- server encoding test
xml_resp = string.gsub (xml_resp, "(%p)", "%%%1")
xml_resp = string.gsub (xml_resp, "\r?\n%s*", "%%s*")
local meth_resp = xmlrpc.server_encode (lua_obj)
local s = string.gsub (meth_resp, xml_resp, "")
s = string.gsub (s, "%s*", "")
assert (s == "", s)
end
---------------------------------------------------------------------
-- call tests.
---------------------------------------------------------------------
call_test ([[<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><int>41</int></value>
</param>
</params>
</methodCall>]], "examples.getStateName", 41)
call_test ([[<?xml version="1.0"?>
<methodCall>
<methodName>examples.getSomething</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>lowerBound</name>
<value><int>18</int></value>
</member>
<member>
<name>upperBound</name>
<value><int>139</int></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>]], "examples.getSomething", { lowerBound = 18, upperBound = 139 })
--[[
call_test ([[<?xml version="1.0"?>
<methodCall>
<methodName>insertTable</methodName>
<params>
<param><value><string>people</string></value></param>
<param><value>
<array><data>
<value>
<struct>
<member>
<name>name</name>
<value><string>Fulano</string></value>
</member>
<member>
<name>email</name>
<value><string>fulano@nowhere.world</string></value>
</member>
</struct>
</value>
<value>
<struct>
<member>
<name>name</name>
<value><string>Beltrano</string></value>
</member>
<member>
<name>email</name>
<value><string>beltrano@nowhere.world</string></value>
</member>
</struct>
</value>
<value>
<struct>
<member>
<name>name</name>
<value><string>Cicrano</string></value>
</member>
<member>
<name>email</name>
<value><string>cicrano@nowhere.world</string></value>
</member>
</struct>
</value>
</data></array>
</value></param>
</params>
</methodCall>]],
"insertTable",
{
{ name = "Fulano", email = "fulano@nowhere.world", },
{ name = "Beltrano", email = "beltrano@nowhere.world", },
{ name = "Cicrano", email = "cicrano@nowhere.world", },
})
--]]
---------------------------------------------------------------------
-- response tests.
---------------------------------------------------------------------
response_test ([[<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>South Dakota</string></value>
</param>
</params>
</methodResponse>]], "South Dakota")
response_test ([[<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value>
<struct>
<member>
<name>lowerBound</name>
<value><int>18</int></value>
</member>
<member>
<name>upperBound</name>
<value><int>139</int></value>
</member>
</struct>
</value>
</param>
</params>
</methodResponse>]], { lowerBound = 18, upperBound = 139 })

32
xrc.lua Executable file
View File

@ -0,0 +1,32 @@
-- See Copyright Notice in license.html
require"xmlrpc"
local _assert = assert
function assert (cond, msg)
if not cond then
io.stdout:write (xmlrpc.server_encode (
{ code = 2, message = msg, },
true
))
os.exit() -- !!!!!!!!!!!
end
end
local doc = parsepostdata ()
local method, arg_table = xmlrpc.server_decode (doc)
assert (type(method) == "string")
assert (type(arg_table) == "table")
local func = xmlrpc.dispatch (method)
assert (type(func) == "function")
local result = { pcall (func, unpack (arg_table)) }
local ok = result[1]
tremove (result, 1)
if not ok then
result = { code = 3, message = result[2], }
end
io.stdout:write (xmlrpc.server_encode (result, not ok))

137
xrh.lua Executable file
View File

@ -0,0 +1,137 @@
---------------------------------------------------------------------
-- XML-RPC over HTTP.
-- See Copyright Notice in license.html
-- $Id$
---------------------------------------------------------------------
require"luasocket"
require"xmlrpc"
local post = socket.http.post
xrh = {}
---------------------------------------------------------------------
-- Call a remote method.
-- @param url String with the location of the server.
-- @param method String with the name of the method to be called.
-- @return Table with the response (could be a `fault' or a `params'
-- XML-RPC element).
---------------------------------------------------------------------
function xrh.call (url, method, ...)
local body, headers, code, err = post {
url = url,
body = xmlrpc.client_encode (method, unpack (arg)),
headers = {
["User-agent"] = "LuaXMLRPC",
["Content-type"] = "text/xml",
},
}
if tonumber (code) == 200 then
return xmlrpc.client_decode (body)
else
error (err or code)
end
end
---------------------------------------------------------------------
---------------------------------------------------------------------
local clients = {}
local send_clients = {}
function xrh.serve (methods)
local s = assert (socket.bind ("localhost", "8080"))
s:settimeout (.01)
while true do
-- look for new clients
local client = s:accept ()
if client then
client:settimeout (1)
table.insert (clients, client)
end
-- receiving clients
local rec_cli, _, err = socket.select (clients, nil, .01)
if err and err ~= "timeout" then
print ("!!", err)
end
if rec_cli then
-- process requests
for i, cli in rec_cli do
local data, err = cli:receive()
if err then
print ("!!", err, "(",cli,")")
table.remove (clients, i) -- !!!!!!!!!!!!!!!!
else
local resp = [[<?xml version="1.0"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>1</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Still debugging</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>]]
local err, n = cli:send (string.format ([[HTTP/1.1 200 OK
Date: %s
Server: Me
Content-Type: text/xml
Content-Length: %d
Connection: close
%s
]], os.date(), string.len(resp), resp))
print (">>", n, "(",string.len(resp),")")
cli:close()
table.remove (clients, i)
end
end
end
end
end
function eca ()
local c = assert (s:accept ())
local req = {}
-- headers
local r, err
repeat
r, err = c:receive ()
print(">>", '['..r..']', err, r=='') io.flush()
table.insert (req, r)
until r == ""
print(">>", table.concat(req)) io.flush()
local err, n = c:send [[HTTP/1.1 200 OK
Connection: close
Content-Length: 158
Content-Type: text/xml
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>South Dakota</string></value>
</param>
</params>
</methodResponse>]]
repeat
r, err = c:receive ()
print(">>", '['..r..']', err, r=='') io.flush()
table.insert (req, r)
until r == ""
--[[
while not err do
print(">>", '['..req..']', err, req=='') io.flush()
req, err = c:receive ()
end
--]]
end