This chapter describes the functions in the libxmlrpc_server_abyss++ function library, which is part of XML-RPC For C/C++ (Xmlrpc-c). Also see General Library Information - C++
The libxmlrpc_server_abyss++ library provides C++ classes for use in a program that is an XML-RPC server based on the Abyss HTTP server.
When using libxmlrpc_server_abyss++, you must also use the libxmlrpc++ library. It contains additional facilities that an XML-RPC server needs but are general to XML-RPC and not specific to XML-RPC servers. Besides, the libxmlrpc_server_abyss++ classes depend on it.
Similarly, you will need the libxmlrpc_server++ library. It contains C++ classes for XML-RPC servers that are not specific to Abyss-based servers. And libxmlrpc_server_abyss++ classes depend on it.
Finally, you will need Abyss itself, which is in the library libxmlrpc_abyss, which is part of Xmlrpc-c. Note that there is no C++ version of Abyss. libxmlrpc_abyss is straight C.
The <xmlrpc-c/xmlrpc_server_abyss.hpp header file declares the interface to libxmlrpc_server_abyss.
You'll have to figure out where on your system this file lives and how to make your compiler look there for it. Or use xmlrpc-c-config.
The classic Unix name for the file containing the libxmlrpc_server_abyss++ library is libxmlrpc_server_abyss++.a or libxmlrpc_server_abyss++.so. The classic linker option to cause the library to be linked into your program is -l xmlrpc_server_abyss++. These are hints; you'll have to modify this according to conventions of your particular platform. You'll also have to figure out where the library resides and how to make your linker look there for it.
Because the libxmlrpc++, libxmlrpc_server++, and libxmlrpc_abyss libraries are prerequisites, you'll also need to link those, and their prerequisite libraries, into your XML-RPC server program.
You can use xmlrpc-c-config, specifying the "c++2" and "abyss-server" features, to find out what libraries to link. This is designed to be used in a build program such as a make file. When properly installed, it tells exactly how to link on your particular system.
See Introductory Examples for a complete example of a simple XML-RPC server based on Abyss written in C++.
See C library abyss server documentation for general information about Abyss and the various options you have for building an XML-RPC server based on it. Xmlrpc-c provides a C++ version of only the highest level of those methods: A C++ alternative to xmlrpc_server_abyss().
An object of class xmlrpc_c::serverAbyss is an XML-RPC server based on the Abyss HTTP server.
The server responds to XML-RPC calls addressed to a particular URI path (usually "/RPC2", but you choose when you create the object).
You supply the XML-RPC methods for the server to execute as an Xmlrpc-c method registry.
There are essentially three ways to use a serverAbyss object:
You choose between these models by your choice to specify the portNumber, socketFd, or neither option to the constructor.
The simplest thing is to use portNumber and let serverAbyss do all the work. But it's often good design to leave binding of a socket to a separate program. For one thing, it's an operation common to all TCP servers, and it's nice to avoid duplicating it in your XML-RPC server program. Also, it's better for security: The binding program can have privileges needed to bind a reserved port number, while the XML-RPC server program does not.
Using individual connected sockets (don't specify either portNumber or socketFd) works for an inetd-based server and is also useful if you have some other sophisticated connection management system.
See Introductory Examples for a complete example of a simple XML-RPC server that uses xmlrpc_c::serverAbyss.
Overview:
serverAbyss::serverAbyss(constrOpt const& opt);
Examples:
Listens for and processes RPCs on Port 8080:
xmlrpc_c::registry myRegistry;
...
xmlrpc_c::serverAbyss abyssServer(xmlrpc_c::serverAbyss::constrOpt()
.registryP(&myRegistry)
.portNumber(8080)
.logFileName("/tmp/xmlrpc_log")
);
Same, using an automatic pointer to manage registry lifetime:
xmlrpc_c::registryPtr myRegistryP(new xmlrpc_c::registry);
...
xmlrpc_c::serverAbyss abyssServer(xmlrpc_c::serverAbyss::constrOpt()
.registryPtr(myRegistryP)
.portNumber(8080)
);
You bind the socket yourself, then server accepts connections on it:
...
int fd;
fd = socket(...);
...
bind(fd, ...);
xmlrpc_c::serverAbyss abyssServer(xmlrpc_c::serverAbyss::constrOpt()
.registryPtr(myRegistryP)
.socketFd(fd)
);
You pass connections to the server one at a time, like inetd:
...
xmlrpc_c::serverAbyss abyssServer(xmlrpc_c::serverAbyss::constrOpt()
.registryPtr(myRegistryP)
);
This constructor uses the constrOpt paradigm to make specifying options easy and flexible, though technically there is just one C++ parameter.
The option methods are:
You must specify either registryPtr or registry, and not both.
You must specify either registryPtr or registry, and not both.
You cannot specify sockaddrP or portNumber if you specify socketFd.
If you specify neither socketFd nor sockAddrP nor portNumber, instead of a server that listens on IP Version 4 Port 8080, you get a server that does not listen at all. It does not accept connections. Your program must feed complete connections to the server one at a time.
If you specify neither socketFd nor sockAddrP nor portNumber, instead of a server that listens on IP Version 4 Port 8080, you get a server that does not listen at all. It does not accept connections. Your program must feed complete connections to the server one at a time.
If you specify neither socketFd nor sockAddrP nor portNumber, instead of a server that listens on IP Version 4 Port 8080, you get a server that does not listen at all. It does not accept connections. Your program must feed complete connections to the server one at a time.
This option was new in Xmlrpc-c 1.30 (March 2012).
This option was new in Xmlrpc-c 1.30 (March 2012).
This names the URI path of the XML-RPC server. For example, if a client will make an XML-RPC call using the URL "http://www.google.com/RPC2", the URI path is "/RPC2". The Abyss server will process as an XML-RPC call anything POSTed to that URI path and reject requests to any other URI path.
The default is "/RPC2".
For more information on what a URI path is, see xmlrpc_server_abyss_set_handlers2().
This option was new in Xmlrpc-c 1.06 (June 2006). Before that, the URI path is always "/RPC2".
Default is true.
This option was new in Xmlrpc-c 1.19 (June 2009).
This is meaningful only when serverOwnsSignals is false (otherwise, you don't know anything about signals). Assuming it is, you ordinarily want to call sigchld like this and specify expectSigchld(true) to let the object know you will. See the section signals.
Default is false.
This option was new in Xmlrpc-c 1.19 (June 2009).
Here's an older, more primitive constructor:
serverAbyss(
xmlrpc_c::registry const& registry,
unsigned int const portNumber = 8080,
std::string const& logFileName = "",
unsigned int const keepaliveTimeout = 0,
unsigned int const keepaliveMaxConn = 0,
unsigned int const timeout = 0,
bool const dontAdvertise = false,
bool const socketBound = false,
xmlrpc_socket const socketFd = 0
);
registry defines the methods that the server will execute. See The Registry Object.
The rest of the parameters have the same meaning as those of the same name used with the xmlrpc_server_abyss() C interface.
The registry argument is somewhat unconventional and tricky to use, because of a design mistake. While you don't pass a pointer, the serverAbyss object remembers its address and refers to it throughout its life. So you must ensure that your registry continues to exist as long as the serverAbyss object does. You should really just use the newer constrOpt-based constructor instead.
The server object so created listens on the relevant socket for connections. The operating system will accept and queue some connections. But the object does not take and process any. For that, you need to call a method such as run.
This method runs the server. run runs the functions of the server: It accepts connections as they arrive and executes the RPCs that come over the connections. It executes RPCs in parallel, not serially; i.e. if RPC 2 arrives when the server is still executing the prior RPC 1, the server begins executing RPC 2 immediately; it does not wait to finish RPC 1.
Example:
xmlrpc_c::serverAbyss myAbyssServer(
xmlrpc_c::serverAbyss::constrOpt()
.portNumber(8080)
...);
myAbyssServer.run();
Prototype:
void
run();
If you need the server to execute RPCs serially, see runOnce.
run returns when the server shuts down, which happens when you call its terminate method or the server executes a system.shutdown system method (provided you have set up an appropriate shutdown object).
As an alternative, you can use runOnce in a loop and use various methods to break the loop when you want the server to end.
run may throw an error if it is or becomes impossible to run the server.
By default, run expects to own all the signals in the process. This is of course very un-object-like behavior. If you set up signal handling yourself, see the discussion of signals.
This method causes the server object to process one RPC (waiting for one if necessary). It waits for the next HTTP connection on the server's listening socket, accepts it, reads the HTTP POST request, executes the indicated RPC, and closes the connection. Note that because the listening socket is perpetually listening, the operating system will accept and queue connections on its own. runOnce() processes a previously accepted connection.
Example:
xmlrpc_c::serverAbyss myAbyssServer(
xmlrpc_c::serverAbyss::constrOpt()
.portNumber(8080)
...);
myAbyssServer.runOnce();
Prototype:
void
runOnce();
runOnce is a good way to make sure your server executes no more than one RPC at a time, when your method function is written to be single threaded. It is also a good way to ensure that all RPCs execute against the same memory, as opposed to running in separate processes. And it's the easiest to debug. All new server programs should start out using this interface, before graduating to the more demanding run approach.
runOnce aborts waiting for a connection request and returns immediately if the process receives a signal. Note that unless you have a handler for that signal, the signal will probably kill the whole process, so set up a signal handler — even one that does nothing — if you want to exploit this. But before Xmlrpc-c 1.06 (June 2006), signals have no effect — there is no way to make runOnce abort the wait and return.
runOnce was new in Xmlrpc-c 1.05 (March 2006).
This method causes the server object to process one HTTP request from the read/write Abyss socket you supply. For normal HTTP, the socket must be for a new TCP connection, with nothing having been read or written on the connection yet. runConn() reads an HTTP request from the socket, performs it, and writes the response to the socket.
Example:
xmlrpc_c::serverAbyss myAbyssServer(
xmlrpc_c::serverAbyss::constrOpt()
.registryP(&myRegistry));
myAbyssServer.runConn(STDIN_FILENO);
Prototype:
void
runConn(int socketFd);
socketFd is a file descriptor (such as you get from a standard C library open call) of the aforementioned socket. It should be a stream socket in connected state.
This would be useful for an Inetd application. Inetd is a server common on Unix systems that listens for connections on various ports simultaneously. When it gets one, it accepts it and forks a process and execs a program that provides whatever service is supposed to be available on the port in question. For example, Inetd might be configured to accept connections to the FTP port, among others. When someone tries to connect to the FTP port, Inetd accepts the connection and forks a process running an FTP server program. It continues listening for further FTP connections and forks other FTP server processes to handle any others.
Inetd passes the connected stream socket to the processor program as its Standard Input. The processor program must be designed to expect a connected stream socket on its Standard Input. One way to construct such a program is to have it call the runConn method with the file descriptor number of Standard Input (0) as its argument.
runConn was new in Xmlrpc-c 1.05 (March 2006).
This method informs the server object that the process has received a SIGCHLD signal, possibly for a child process it created. You don't use this unless you explicitly created the server object to require it.
See section signals for information on how and why to use this.
Example:
static void
sigchld(int const signalClass) {
/*----------------------------------------------
This is a signal handler for a SIGCHLD signal
----------------------------------------------*/
// Reap zombie children / report to Abyss until there aren't any more.
bool zombiesExist;
bool error;
assert(signalClass == SIGCHLD);
zombiesExist = true; // initial assumption
error = false; // no error yet
while (zombiesExist && !error) {
int status;
pid_t const pid = waitpid((pid_t) -1, &status, WNOHANG);
if (pid == 0)
zombiesExist = false;
else if (pid < 0) {
/* because of ptrace */
if (errno == EINTR) {
// This is OK - it's a ptrace notification
} else
error = true;
} else
myServerP->sigchld(pid);
}
}
myServerP->sigchld(pid);
Prototype:
void sigchld(pid_t pid);
This method was new in Xmlrpc-c 1.19 (June 2009).
This method causes an Abyss XML-RPC server that is running to terminate, and to terminate immediately if it starts running in the future.
Because a program that starts a server running does not regain control as long as the server is running, this is meant to be called from a separate thread or signal handler.
Example:
myServerP->terminate();
Prototype:
void terminate();
If you have a signal handler that calls terminate() and the process receives a signal while the server is running, the server will terminate (run() will return) soon after the signal handler returns. If the server is processing any RPCs when the process receives the signal, it will not terminate until it has completed those RPCs.
You can also call this from an XML-RPC method (i.e. from the execute method of a xmlrpc_c::method object), as long as that object is in the same process that waits for and accepts new connections. If, on the other hand, you are running the server with run() and you have a system in which it does that by way of a Unix fork, a terminate() from inside an Abyss HTTP request handler has no effect.
This method was new in Xmlrpc-c 1.11 (June 2007). Before that, there is no way to terminate a running xmlrpc_c::serverAbyss server.
Before Xmlrpc-c 1.14 (March 2008), terminate can't actually interrupt anything the server is doing -- the server won't notice it is supposed to terminate as long as it is asleep waiting for an RPC. So the server terminates after the next RPC.
The same is true on Windows until Xmlrpc-c 1.25 (December 2010) — terminate can't interrupt anything.
This class is for enabling the system.shutdown system method.
It is a derived class of xmlrpc_c::registry::shutdown, so you can pass an object of this class to the xmlrpc_c::registry::setShutdown of your method registry before running the Abyss server. If you do, when the server executes the system.shutdown method, it has the effect of calling the server's terminate method.
Example:
xmlrpc_c::registry myRegistry;
xmlrpc_c::serverAbyss myServer(xmlrpc_c::serverAbyss::constrOpt()
.registryP(&myRegistry)
.portNumber(8080)
);
xmlrpc_c::serverAbyss::shutdown shutdown(&myServer);
registry.setShutdown(&shutdown);
The only method you will call is the constructor:
Prototype:
shutdown(xmlrpc_c::serverAbyss * const serverP);
serverP identifies the server that you want the object to terminate. It doesn't make sense to specify any server other than the one that you set up to use this registry.
This class was new in Xmlrpc-c 1.13 (December 2007). Before that, there is no way for system.shutdown to shut down a serverAbyss server.
Before Xmlrpc-c 1.14 (March 2008) on Unix, and on Windows before Xmlrpc-c 1.25 (December 2010), system.shutdown can't actually interrupt anything the server is doing — the server won't notice it is supposed to terminate as long as it is asleep waiting for an RPC. So you normally have to call it twice -- once to request shutdown, and once to wake up the master thread so it notices the request. But if you're running the server in a single-thread manner, you don't have this problem because the server will notice the termination after the system.shutdown completes and before it waits for the next RPC.
This method queries the socket address on which the server is listening. The socket address is an IP address and TCP port number, except that it can instead of IP address, it can indicate "any address to which this computer answers").
This isn't normally useful because when you created the server, you told it to listen on a particular port. But it is also possible to tell the server to choose a free port on its own, and if you did that, you could use this method to find out what port it chose so you could tell clients.
Example:
struct sockaddr * listenNameP;
sockaddr_len listenNameLen;
getListenName(&listenNameP, &listenNameLen);
Prototype:
void
getListenName(struct sockaddr ** sockaddrPP,
size_t * sockaddrLenP);
This method was new in Xmlrpc-c 1.46 (March 2016).
An object of class callinfo_serverAbyss is something that gets passed to your XML-RPC method (i.e. to the execute method of a xmlrpc_c::method object) to give it information about how the XML-RPC call arrived. It is an instance of xmlrpc_c::callInfo specific to serverAbyss servers.
You can use this, among other things, to find out the IP address of the XML-RPC client.
This class was new in Xmlrpc-c Release 1.19 (June 2009). Before that, information about how the XML-RPC call arrived is not available to your XML-RPC method.
You don't construct or destroy a callInfo_serverAbyss. Methods of serverAbyss do that — you just follow a pointer to it and use it.
Member serverAbyssP is a pointer to the serverAbyss object that is the XML-RPC server through which the call arrived.
serverAbyss per se doesn't contain any members that are useful to your XML-RPC method, but derived class members might. For example, you might have defined a derived class of serverAbyss called serverAbyssXyz and endowed it with information you want your XML-RPC method to have. Your XML-RPC method, then, need only do a dynamic cast of serverAbyssP * to serverAbyssXyz * and use that pointer to access your information.
Member sessionAbyssP is a pointer to the Abyss session object (type TSession) through which the XML-RPC call arrived. An Abyss session is essentially an HTTP transaction. In the simplest HTTP, it is also a TCP connection; but more sophisticated HTTP has persistent connections, which means a single TCP connection can host multiple Abyss sessions.
You can use libxmlrpc_abyss's SessionGetRequestInfo() and SessionGetChannelInfo to get information about how the RPC arrived.
You're not supposed to use the Abyss session object in any other way — libxmlrpc_server_abyss++ owns it.
Here is an example of getting the IP address and TCP port number of the XML-RPC client, assuming you're using a conventional Abyss server that uses conventional Unix sockets:
class testCallInfoMethod : public xmlrpc_c::method2 {
public:
void
execute(xmlrpc_c::paramList const& paramList,
const xmlrpc_c::callInfo * const callInfoPtr,
xmlrpc_c::value * const retvalP) {
const xmlrpc_c::callInfo_serverAbyss * const callInfoP(
dynamic_cast<const xmlrpc_c::callInfo_serverAbyss *>(callInfoPtr));
void * chanInfoPtr;
SessionGetChannelInfo(callInfoP->abyssSessionP, &chanInfoPtr);
struct abyss_unix_chaninfo * const chanInfoP(
static_cast<struct abyss_unix_chaninfo *>(chanInfoPtr));
struct sockaddr const clientAddr(chanInfoP->peerAddr);
// Now pick apart 'clientAddr' in the usual way to get to the
// IP address and port number. It is the same type of data you
// would get from POSIX accept().
...
}
There is a complete working example of this in the Xmlrpc-c source tree as examples/cpp/callinfo_abyss_server.cpp . It implements an XML-RPC method getCallInfo which returns a text message containing the IP address and TCP port number of the XML-RPC client.
xmlrpc_c::server_abyss_set_handlers is a function that marries an Xmlrpc-c XML-RPC method registry object to an Abyss server object so that the Abyss server executes the XML-RPC methods.
void xmlrpc_c::xmlrpc_server_abyss_set_handlers( TServer * const srvP, xmlrpc_c::registry * const registryP, std::string const uriPath = "/RPC2" );
void xmlrpc_c::xmlrpc_server_abyss_set_handlers( TServer * const srvP, xmlrpc_c::registryPtr const registryP, std::string const uriPath = "/RPC2" );
void xmlrpc_c::xmlrpc_server_abyss_set_handlers( TServer * const srvP, xmlrpc_c::registry const& registry, std::string const uriPath = "/RPC2" );
This function adds to the Abyss server identied by srvP a request handler for a URI path you specify. This handler executes a POST request as an XML-RPC call as defined by the method registry identified by registryP. It rejects requests with other HTTP methods for /RPC2 with an HTTP "Method Not Allowed" (code 405) failure.
This function also adds a default request handler, which handles requests to every URI path other than the one specified. All that handler does is fail the request with an HTTP "file not found" (code 404) error.
You normally call this once per server. If you call it a second time, it adds another handler, later in Abyss' search order, for exactly the same requests, so it has no effect.
Note that you do not use this with a serverAbyss object. With a serverAbyss, you never see an Abyss object. A serverAbyss object has the Abyss server buried inside it and takes care of setting whatever handlers it needs.
Regardless of how you pass the registry, (reference, simple pointer, or registryPtr), you must ensure that the registry continues to exist as along as the Abyss server does.
Class serverAbyss has the same considerations for signals as the C equivalent.
By default, the run method takes care of all the signal requirements of the server, but it also expects to own all of the signals of the program, which means it may not be compatible with the rest of your program. In particular, if your program receives SIGCHLD signals that aren't from Abyss children, and you need to see them, you must specify a serverOwnsSignals(false) constructor option when you construct the serverAbyss object.
serverOwnsSignals(false) causes the serverAbyss object not to touch signal handlers, and places the responsibility on you to handle signals as serverAbyss requires. You have two responsibilities:
Install a handler for SIGCHLD and make it call the serverAbyss object's sigchld method. It needs to do this at least for signals that announce the death of a child of the Abyss server, but it doesn't hurt to call it for all SIGCHLD signals, since the server knows which ones are for its children. Specify a expectSigchld(true) constructor option when you construct the serverAbyss object to confirm that you have this handler.
If for whatever reason you can't do the SIGCHLD handler, then you can specify expectSigchld(false) and Abyss will do its best to live without the signals, but it may cost performance and resources.
serverOwnsSignals was new in Xmlrpc-c 1.19 (June 2009). Before that, run always sets up signal handlers and there is no way to run a server than handles multiple RPCs at once without messing with your signals. The best you can do in those releases is use runOnce in a loop.
runOnce and runConn don't set up any kind of signal handling. They don't require any SIGCHLD handling, so the only thing you have to do is set up SIGPIPE to be ignored or handled.
This section contains special topics of interest to users of the facilities described above.
HTTP authentication is the concept of the XML-RPC client, as an HTTP client, identifying and authenticating itself to the XML-RPC server, as an HTTP server, using features of the HTTP protocol.
libxmlrpc_server_abyss++ provides no facilities for doing any of that.
See further comments on this situation in the discussion of how the C libraries don't have this function either.
If you set the XMLRPC_TRACE_ABYSS environment variable to 1, libxmlrpc_server_abyss's Abyss request handler prints a message to Standard Error each time Abyss calls it.
You can also use the XMLRPC_TRACE_XML environment variable to trace the handler's calls to the libxmlrpc_server registry.