libxmlrpc_server_abyss++

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.

Chapter Contents

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.

Linking The Library

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.

Example

See Introductory Examples for a complete example of a simple XML-RPC server based on Abyss written in C++.

About Abyss

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().

Description Of Facilities

class serverAbyss

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.

Example

See Introductory Examples for a complete example of a simple XML-RPC server that uses xmlrpc_c::serverAbyss.

Constructors

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:

registryPtr(xmlrpc_c::registryPtr)
This is a pointer to the method registry the server is to use. The pointer takes care of managing the existence of the registry; you can destroy all your pointers to the registry after the constructor returns.

You must specify either registryPtr or registry, and not both.

registryP(const xmlrpc_c::registry *)
This is a pointer to the method registry the server is to use. You must ensure that the registry to which it points continues to exist as long as the server (xmlrpc_c::serverAbyss object) does.

You must specify either registryPtr or registry, and not both.

socketFd(xmlrpc_socket)
This identifies a TCP socket that is bound to the desired network address, ready to be listened on. It is analogous to the libxmlrpc_server_abyss socket_fd parameter with the following exceptions.

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.

portNumber(unsigned int)
This gives the TCP port number on which the server is to listen for connections from XML-RPC clients. It is analogous to the libxmlrpc_server_abyss port_number parameter with the following exceptions.

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.

sockAddrP(const struct sockaddr *)
This points to a conventional POSIX socket address that tells the network address and port where the server will listen for connections from XML-RPC clients. It is analogous to the libxmlrpc_server_abyss sockaddr_p parameter with the following exceptions.

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).

sockAddrLen(socklen_t)
This goes with sockAddrP. It is analogous to the libxmlrpc_server_abyss sockaddrlen parameter.

This option was new in Xmlrpc-c 1.30 (March 2012).

uriPath(std::string)

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".

logFileName(std::string)
This is analogous to to the libxmlrpc_server_abyss log_file_name parameter.
maxConn(unsigned int)
This is analogous to to the libxmlrpc_server_abyss max_conn parameter. It was new in Xmlrpc-c 1.31 (September 2012).
maxConnBacklog(unsigned int)
This is analogous to to the libxmlrpc_server_abyss max_conn_backlog parameter. It was new in Xmlrpc-c 1.31 (September 2012).
maxRpcMem(size_t)
This is analogous to to the libxmlrpc_server_abyss max_rpc_size parameter. It was new in Xmlrpc-c 1.44 (December 2015).
keepaliveTimeout(unsigned int)
This is analogous to to the libxmlrpc_server_abyss keepalive_timeout parameter.
keepaliveMaxConn(unsigned int)
This is analogous to to the libxmlrpc_server_abyss keepalive_maxconn parameter.
timeout(unsigned int)
This is analogous to to the libxmlrpc_server_abyss timeout parameter.
dontAdvertise(bool)
This is analogous to to the libxmlrpc_server_abyss dontadvertise parameter.
serverOwnsSignals(bool)
This says the server object owns all the signals in the process. It can install signal handlers. If other parts of your program need to use signals, see the section signals.

Default is true.

This option was new in Xmlrpc-c 1.19 (June 2009).

expectSigchld(bool)
This says you will call the object's sigchld method every time the program receives a SIGCHLD signal.

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).

chunkResponse(bool)
This is analogous to to the libxmlrpc_server_abyss chunk_response parameter.
allowOrigin(string)
This is analogous to to the libxmlrpc_server_abyss allow_origin parameter, but doesn't have any analog to the null value. (If you don't want to participate in HTTP access control, just omit this option).
accessControlMaxAge(unsigned int)
This is analogous to to the libxmlrpc_server_abyss access_control_max_age parameter, but also fills the role of access_control_expires: If you specify this option, access control expires. If you don't it doesn't.

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.

run Method

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.

runOnce Method

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).

runConn Method

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).

sigchld Method

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).

terminate Method

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.

shutdown Subclass

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.

getListenName Method

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).

Class callinfo_serverAbyss

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.

Constructors

You don't construct or destroy a callInfo_serverAbyss. Methods of serverAbyss do that — you just follow a pointer to it and use it.

serverAbyssP Member

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.

sessionAbyssP Member

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.

server_abyss_set_handlers function

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.

Overview


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"
);

Description

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.

Signals

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:

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

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.

Debugging

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.