nlib
exi/xml-rpc/xml-rpc.cpp

Sample of a front-end that processes an XML-RPC request and generates a response.

The body of the XML-RPC request is XML similar to the following.

<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>

By writing the XML in this way, it attempts to run the following procedure at certain timing:

examples.getStateName(41);

The application that receives the request parses the request, runs the appropriate function, and returns the return value. The response must also be sent back in XML format, and might look something like the following.

<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>South Dakota</string></value>
</param>
</params>
</methodResponse>

This sample sends "South Dakota" as the return value.

It is just a single-threaded sample that implements code for easily generating and handling a XML-RPC request and response from C++. The use of binary XML provides a moderate reduction in overhead for the protocol used to exchange the otherwise verbose data.

The Japanese translation of the XML-RPC specifications can be found here. http://lowlife.jp/yasusii/stories/9.html

The source code of the sample is shown below.

#include <vector>
#include "./xmlrpc_clientserver.h"
#include "./xmlrpc_value.h"
const int BUF_SIZE = 1024 * 128;
unsigned char g_Buf[BUF_SIZE];
class Sum : public XmlRpcServerMethod {
public:
explicit Sum(XmlRpcServer* server) : XmlRpcServerMethod(N("MyFunc.Sum"), server) {}
virtual void Execute(const XmlRpcValue& params, XmlRpcValue* result);
};
void Sum::Execute(const XmlRpcValue& params, XmlRpcValue* result) {
// Sums the int/double values and returns the result as double.
double sum = 0;
const XmlRpcArray* ar = params.AsArray();
size_t n = ar->Size();
for (size_t i = 0; i < n; ++i) {
const XmlRpcValue* item = ar->Get(i);
if (!item) continue;
if (item->GetValueType() == XmlRpcValue::INT) {
sum += *item->AsInt();
} else if (item->GetValueType() == XmlRpcValue::DOUBLE) {
sum += *item->AsDouble();
}
}
result->SetDouble(sum);
}
class MyServer {
XmlRpcServer m_Server;
std::vector<XmlRpcServerMethod*> m_Vec;
public:
MyServer() {}
~MyServer() {
size_t n = m_Vec.size();
for (size_t i = 0; i < n; ++i) delete m_Vec[i];
}
void SetupMethods() {
Sum* sum = new (std::nothrow) Sum(&m_Server);
m_Vec.push_back(sum);
}
XmlRpcServer* GetServer() { return &m_Server; }
};
// Assumes the server in the distant place.....
MyServer g_Server;
bool SampleMain(int, char**) {
g_Server.SetupMethods();
XmlRpcClient client;
// This sample simplifies the server connections....
client.SetServer(g_Server.GetServer());
// Calls the remotely defined function in the server:
// double MyFunc.Sum(list of int/double)
XmlRpcValue params;
params.SetArray();
params.AsArray()->Append()->SetDouble(1.0);
params.AsArray()->Append()->SetInt(2);
params.AsArray()->Append()->SetDouble(3.0);
nlib_printf("MyFunc.Sum([1.0, 2, 3.0])=");
// methodName = L"MyFunc.Sum"
// params = [1.0, 2, 3.0]
//
// Sends the XML below:
// <methodCall>
// <methodName>MyFunc.Sum</methodName>
// <params>
// <param><value><double>1.0</double></value></param>
// <param><value><i4>2</i4></value></param>
// <param><value><double>3.0</double></value></param>
// </params>
// </methodCall>
XmlRpcValue result;
XmlRpcCallResult future;
if (!client.Execute(N("MyFunc.Sum"), params, &future)) {
nlib_printf("client.Execute failed\n");
return false;
}
// Receives the response below:
// <methodResponse>
// <params>
// <param><value><double>6.0000</double></value></param>
// </params>
// </methodResponse>
if (!future.GetResult(&result)) {
nlib_printf("GetResult failed\n");
return false;
}
nlib_printf("%f\n", *result.AsArray()->Get(0)->AsDouble());
return true;
}
NLIB_MAINFUNC