libxmlrpc_server++

This chapter describes the functions in the libxmlrpc_server++ function library, which is part of XML-RPC For C/C++ (Xmlrpc-c). Also see General Library Information - C++

You must know something about XML-RPC (the protocol) to understand this chapter. You don't have to know the details of the protocol, since Xmlrpc-c is meant to spare you from learning that, but you do have to know the kinds of things that make up an XML-RPC transaction.

Everything you need to know about XML-RPC is here.

The libxmlrpc_server++ library provides C++ classes for use in a program that is an XML-RPC server. These classes by themselves are not enough to implement a server; you need other facilities to do the bulk of the work, such as libxmlrpc_server_abyss++. libxmlrpc_server++ contains only facilities that are common to multiple kinds of server implementations.

When using libxmlrpc_server++, 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++ classes depend on it.

Chapter Contents

The <xmlrpc-c/xmlrpc_server.hpp> header file declares the libxmlrpc_server++ classes.

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++ library is libxmlrpc_server++.a or libxmlrpc_server++.so. The classic linker option to cause the library to be linked into your program is -l xmlrpc_server++. 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.

You can use xmlrpc-c-config, specifying the "c++2" feature and a server type feature, 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.

The following libraries are prerequisites of libxmlrpc_server++, so you'll need to link them in too:

And remember that some static linkers care about the order in which you specify the libraries, with the prerequisite libraries having to come after the prerequiring library. xmlrpc-c-config is a good way to make sure you link all the prerequisites in the right order.

Example

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

Method Registry

An XML-RPC server consists of 1) machinery to receive XML-RPC calls and send responses to them (called the XML-RPC protocol driver) and 2) methods. The protocol driver can be constant for various XML-RPC server applications. It's the methods that make a particular XML-RPC server what it is. In the small example server mentioned above, which provides the service of adding two numbers together, the xmlrpc_c::server_abyss object (courtesy of libxmlrpc_server_abyss++) provides the protocol driver, and the "sample_add" C++ object from the example program is what makes it an addition server.

libxmlrpc_server++ provides a facility to connect the XML-RPC protocol driver with the methods. It is called the method registry. It forms a uniform interface that all methods can use to interact with all protocol drivers.

The essential structure of an Xmlrpc-c server program is like this: The program defines a C++ class for for each of a bunch of methods and constructs a single object of each class. The program creates a method registry object and adds registers each of its methods in the registry. The program then constructs a protocol driver object with the full registry as a parameter.

The protocol driver object executes an XML-RPC call through the method registry. It passes the XML for the XML-RPC call to the method registry, and the method registry executes the appropriate registered method and returns the XML for the XML-RPC response to the protocol driver.

System Methods

There are some methods, called system methods, that the registry implements internally (I.e. you don't use xmlrpc_registry_add_method to add one). A system method is concerned with the basic operation of the server rather than whatever job the particular server does.

The registry implements the same system methods as the C version of the registry.

To disable introspection, call the C++ registry object's disableIntrospection() method. Then the server will not have the Introspection XML-RPC methods. In a new registry, introspection is enabled. There is no way to enable it after disabling it.

To set up system.method, call the C++ registry object's setShutdown method.

How To Write A Method

The terminology gets confusing here, because we have both XML-RPC methods and C++ methods. You implement an XML-RPC method with a C++ object, and the meat of that object is one of its C++ methods, called execute.

To write the code for an XML-RPC method, you define a C++ class derived from Xmlrpc-c's xmlrpc_c::method2 class. Your class must have an execute method. That method has the following prototype:


    void
    execute(xmlrpc_c::paramList        paramList,
            const xmlrpc_c::callInfo * callInfoP,
            xmlrpc_c::value *          resultP);

The protocol driver calls your method to perform the guts of an RPC. It passes to your method the RPC's parameters as an xmlrpc_c::paramList object .

Your method executes the XML-RPC method and returns the result as *resultP.

That is all that is required of the class.

Your constructor may optionally set the object's _signature member. This is a std::string that documents the method's use by the server's XML-RPC Introspection function. Its value has the same meaning as the signature argument to xmlrpc_registry_add_method_w_doc function in the equivalent C library.

If your constructor does not set _signature, it is as if you set its value to "?" (i.e. unspecified).

This signature has nothing to do with what parameters the method actually requires or how the method code interprets them. That is determined by the code in your execute method.

Your constructor may optionally set the object's _help member. This is a std::string that describes in human language how to use the method. The server's XML-RPC Introspection function uses this information.

If your constructor does not set _help, the registry uses the text, "No help is available for this method".

Beyond that, you can add members and constructors and destructors to suit your needs. You can even use class members to keep persistent state so an RPC can affect future RPCs of the same XML-RPC method. But that isn't really in the spirit of XML-RPC. RPCs are supposed to stand alone.

You will normally have exactly one object of each XML-RPC method class, and you register that object in the method registry under some unique method name.

Example Method Class


   class sample_add : public xmlrpc_c::method {
   public:
       sample_add() {
           this->_signature = "i:ii";
           this->_help = "This method adds two integers together";
       }
       void
       execute(xmlrpc_c::param_list       const paramList,
               const xmlrpc_c::callInfo * const callInfoP,
               const xmlrpc_c::value *    const retvalP) {
          
           int const addend(paramList.getInt(0));
           int const adder(paramList.getInt(1));

           *retvalP = xmlrpc_c::value(addend + adder);
      }
   };

Failing The RPC; Handling Errors

If your method cannot perform the RPC, it may throw an error. If it throws any kind of object, the protocol driver will complete the RPC as a failed RPC. But it should preferably throw a xmlrpc_c::fault or girerr:error. In the xmlrpc_c::fault case, the protocol driver returns an XML-RPC fault response as described by the thrown object. If it throws a girerr::error, the protocol driver constructs a fault response whose fault string is the error description from the girerr:error object in the XML-RPC response, and whose fault code is zero. If execute() throws anything else, the protocol driver makes up a fault response that describes an internal server error.

Example:


    enum faultCodes { CODE_OVERFLOW=3 };

    void
    execute(xmlrpc_c::param_list       const paramList,
            const xmlrpc_c::callInfo * const callInfoP,
            const xmlrpc_c::value *    const retvalP) {
       
        int const addend(paramList.getInt(0));
        int const adder(paramList.getInt(1));

        if (addend > 0 && adder > 0 & addend + adder < 0)
            throw(xmlrpc_c::fault("Sum is too large.",
                                  CODE_OVERFLOW);

        *retvalP = xmlrpc_c::value(addend + adder);
    

Using A Method Class

A xmlrpc_c::method2 is an auto-object. Class xmlrpc_c::methodPtr is its pointer class. You ordinarily refer to your method object by such a pointer. In fact, it is the only way to register your method object with the method registry.

So create the object following this example:


    xmlrpc_c::methodPtr const sampleAddMethodP(new sample_add);

You will never delete this object; it gets deleted automatically when the last reference goes away.

callInfo object

As you see, one of the arguments to your execute method is a pointer to a xmlrpc_c::callInfo object. It gives you information about how the XML-RPC call arrived. You can usually ignore this as in the example above. The most common thing people use it for is to find out the IP address of the caller.

xmlrpc_c::callInfo is an abstract base class. What callInfo really points to is some derived class of it that is particular to the kind of server in question. For example, if it is an Abyss-based XML-RPC server (via libxmlrpc_server_abyss++), then callInfoP points to an object of that library's xmlrpc_c::callInfo_serverAbyss class.

old method class

xmlrpc_c::method2 was new in Xmlrpc-c 1.19 (June 2009). Before that, you have to use xmlrpc_c::method instead (and it continues to work fine with current Xmlrpc-c). It's the same except that its execute method does not have the callInfoP argument. The information available via callInfoP is not available in any way with method.

The Default Method Object

When the client specifies a method name that is not in the method registry, the protocol driver calls the execute method of the "default method object" that you registered. A registry contains zero or one default method object.

You can use this to bypass much of the work that the registry does and do your own dispatching of methods: just don't register any methods, and all XML-RPC calls will result in a call to your default method object. But if you're going to do that, it would probably be cleaner just to derive your own method registry class from xmlrpc_c::registry and write your code as that class' methods.

If you don't register a default method object and an XML-RPC call specifies a method name that is not in the registry, the registry services automatically return a "no such method" fault response.

A default method object, which is of Class xmlrpc_c::defaultMethod, is just like a regular method object except that its execute method has two more parameters:


    void
    execute(std::string         methodName,
            xmlrpc_c::paramList paramList,
            xmlrpc_c::value *   resultP);

methodName) tells you the method name that the client specified.

There is a class xmlrpc_c::defaultMethodPtr which is analogous to xmlrpc::methodPtr.

The Registry Object

The method registry is an object of Class xmrpc_c::registry.

You construct a registry object and provide it to your server.

A xmlrpc_c::registry is an auto-object. Class xmlrpc_c::registryPtr is its pointer class. The most convenient way to refer to a registry is by such a pointer, unless you can use an automatic (i.e. stack) variable.

Before Xmlrpc-c 1.05 (March 2006), xmlrpc_c::registry is not derived from xmlrpc_c::registry. You must use C++ pointers and explicitly ensure that the registry object continues to exist as long as it is being used.

Constructors

The only constructor is the no-argument constructor. This creates a registry with no methods other than system methods; not even a default method. You build it up from there by calling other methods.

addMethod Method

This method adds an XML-RPC method to the registry.

Example:


    xmlrpc_c::registry myRegistry;
    xmlrpc_c::methodPtr sampleAddMethodP(new sampleAddMethod);
    myRegistry.addMethod("sample.add", sampleAddMethodP);

Prototype:


    void
    addMethod(string              const name,
              xmlrpc_c::methodPtr const methodP);

setDefaultMethod Method

This method sets the registry's default method, as decribed in The Default Method Object.

Example:


    xmlrpc_c::registry myRegistry;
    xmlrpc_c::defaultMethodPtr myDefaultMethodP(new myDefaultMethod);
    myRegistry.setDefaultMethod(myDefaultMethodP);

disableIntrospection Method

This method deletes the built-in introspection methods from the registry. These methods exist in a newly created registry until you delete them. There's no way to add them back. This method is meant to be used as an extension of the registry class' constructor.

Prototype:


    void
    disableIntrospection();

setDialect Method

This method modifies the registry to set the dialect for responses.

Examples:


    xmlrpc_c::registry registry;
    registry.setDialect(xmlrpc_dialect_apache);


    xmlrpc_c::registry registry;
    registry.setDialect(xmlrpc_dialect_i8);

Prototype:


    void
    setDialect(xmlrpc_dialect dialect);

dialect is the dialect you're setting.

By default (if you have never set the dialect), when an XML-RPC server executes an XML-RPC method via the registry, the response goes in the i8 dialect.

This method was new in Xmlrpc-c 1.11 (June 2007).

setShutdown Method

This method modifies the registry to indicate a way to implement the system.shutdown system method. Until you execute this method, system.shutdown fails.

Examples:


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


    xmlrpc_c::registry registry;
    
    class myshutdown : public xmlrpc_c::registry::shutdown {
    public:
        myshutdown(myServerType * const serverHandle) :
            serverHandle(serverHandle) {}

        void doit(string const& comment,
                  void * const) const {

            cerr << "Shutting down because " << comment <<endl;
            shutdownMyServer(serverHandle);
        }
    
    private:
        myServerType * const serverHandle;
    };

    myshutdown shutdown(&myServer);

    registry.setShutdown(&shutdown);

Prototype:


    void
    setShutdown(registry::shutdown * const shutdownP);

shutdownP is a pointer to an object that knows how to shut down the server that is using this registry.

The registry::shutdown class exists just to be an argument to this method. It is an abstract base class with one method: doit. doit shuts down the server.

This is the prototype of doit:


    void doit(string const& comment,
              void * const  callInfo) const;

comment is a comment describing the shutdown (to wit, the parameter of the system.shutdown method -- information from the XML-RPC client).

callInfo is non-XML-RPC transport information about the XML-RPC call that requested the shutdown. A typical purpose of this would be to tell the IP address of the requester, so doit can determine whether the requester has permission to shut down the server.

However, callInfo is not implemented today. Its value is meaningless.

Because it is an abstract base class, you must pass an object of a class derived from it to setShutdown. This derived class is specific to the kind of server that is to use the registry, because its doit method must know how to shut down the server.

This method was new in Xmlrpc-c 1.13 (December 2007). Before that, you cannot use system.shutdown to shut down a server.

Executing an XML-RPC Method

This section describes the client-facing interface to the method registry. This is the interface that an XML-RPC protocol driver uses to talk to the registry.

The processCall() method executes an XML-RPC call, given in XML form, and returns the response, again in XML form. This entails looking up the named method in the registry and calling the execute method of the XML-RPC method C++ object associated with it.

Prototype:


    void
    processCall(std::string   callXml,
                std::string * responseXmlP) const;


For most errors, including if the XML-RPC method object's execute method throws anything at all, processCall returns an XML-RPC fault response as *responseP. But it is possible for some kinds of error in the server program to cause processCall to throw a girerr::error.

callXml is the complete XML of the XML-RPC call. It isn't necessarily valid.

The method returns as *responseXmlP a string whose value is the complete XML of the XML-RPC response to the XML-RPC call. The registry ensures that this is a valid XML-RPC response, regardless of what the method function does. Caller is responsible for deleting the string.

To make this function trace the XML input and output, see the XMLRPC_TRACE_XML environment variable.

Faults

Method registry facilities that execute an RPC may generate a fault response for the RPC. Sometimes the details of that fault are supplied by the registered method object; sometimes they come from Xmlrpc-c directly.

Where Xmlrpc-c generates a fault directly, the fault code is one of the ones enumerated for the xmlrpc_c::fault class. Where Xmlrpc-c takes fault information from the registered method object, the fault code is entirely up to that object. Note that this means Xmlrpc-c does not conform to the Fault Code Interoperability standard.

Debugging

This section describes some facilities and techniques for debugging programs that use libxmlrpc_server.

XMLRPC_TRACE_XML environment variable

If you set the XMLRPC_TRACE_XML environment variable to 1, the libxmlrpc_server registry functions print to Standard Error the XML of the call and of the response, in addition to their normal processing.

It works the same as for the C server library.