libxmlrpc_client

This chapter describes the functions in the libxmlrpc_client 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_client library provides functions for use in an program that is an XML-RPC client. These functions take care of all the protocol related things so the calling program can be very simple.

When using libxmlrpc_client, you must also use the libxmlrpc library. It contains additional facilities that an XML-RPC client needs but are general to XML-RPC and not specific to XML-RPC clients. Besides, the libxmlrpc_client library routines depend on it.

Chapter Contents

Interface Header File

The <xmlrpc-c/xmlrpc_client.h> header file declares the interface to libxmlrpc_client.

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.

Because the libxmlrpc library is a prerequisite, you'll also need its header file (xmlrpc.h).

Linking The Library

The classic Unix name for the file containing the libxmlrpc_client library is libxmlrpc_client.a or libxmlrpc_client.so. The classic linker option to cause the library to be linked into your program is -l xmlrpc_client. 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. Or use xmlrpc-c-config.

The following libraries are prerequisites of libxmlrpc_client, 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

A complete example of an XML-RPC client program that uses libxmlrpc_client is here.

Here is an example of the main part of the same program using the slightly more complex but preferred private client method:


#include <xmlrpc-c/base.h>
#include <xmlrpc-c/client.h>

#include "config.h"  /* information about this build environment */

#define NAME "XML-RPC C Test Client"
#define VERSION "1.0"

int 
main(int           const argc, 
     const char ** const argv) {

    xmlrpc_env env;
    xmlrpc_client * clientP;
    xmlrpc_value * resultP;
    int sum;
    char * const url = "http://localhost:8080/RPC2";
    char * const methodName = "sample.add";

    /* Initialize our error-handling environment. */
    xmlrpc_env_init(&env);

    xmlrpc_client_setup_global_const(&env);

    xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION, NULL, 0,
                         &clientP);
    die_if_fault_occurred(&env);

    /* Make the remote procedure call */
    xmlrpc_client_call2f(&env, clientP, url, methodName, &resultP,
                "(ii)", (xmlrpc_int32) 5, (xmlrpc_int32) 7);
    die_if_fault_occurred(&env);
    
    /* Get our sum and print it out. */
    xmlrpc_read_int(&env, resultP, &sum);
    die_if_fault_occurred(&env);
    printf("The sum  is %d\n", sum);
    
    /* Dispose of our result value. */
    xmlrpc_DECREF(resultP);

    /* Clean up our error-handling environment. */
    xmlrpc_env_clean(&env);
    
    xmlrpc_client_destroy(clientP);

    xmlrpc_client_teardown_global_const();

    return 0;
}


Testing, Experimenting

As you build an XML-RPC client using libxmlrpc_client, it's good to be able to try it out by talking to a server.

One way to do this is to use the one of the server programs in the examples directory of the Xmlrpc-c source tree. For example, xmlrpc_sample_add_server runs a server on a local TCP port of your choosing. If you choose Port 8080, for example, you can direct your client program to URL http://localhost:8080/RPC2 and execute a system.listMethods method.

Or you can use a server that already exists on the Internet. The Xmlrpc-c project operates one at http://giraffe-data.com/xmlrpc_sample_add_server.cgi. This is Apache with a CGI program that uses Xmlrpc-c's libxmlrpc_server_cgi library. (You can find the code for that program as examples/xmlrpc_sample_add_server_cgi.c in the Xmlrpc-c source tree.

There is a more elaborate example server at http://phpxmlrpc.sourceforge.io/server.php. This one has lots of methods. You can use its system.listMethods and system.methodHelp methods to find out more. Note that this server does not use Xmlrpc-c and has nothing to do with the Xmlrpc-c project.

An advantage of running your own server is that you can do tracing on the server side to help you understand why your client isn't doing what you expect.

Also see Debugging.

Global Constants

libxmlrpc_client has global constants that you must set up. The global initialization function is xmlrpc_client_setup_global_const(). The global termination function is xmlrpc_client_teardown_global_const().

See Global Constants for an explanation of why you need these and how to use them.

If you use the global client object (i.e. xmlrpc_client_init2()), creating the global client and setting up the global constants are merged into one operation, so you need not call xmlrpc_client_setup_global_const().

Client Object

You perform XML-RPC client functions through an xmlrpc-c client object. You create this object with xmlrpc_client_create() and destroy it with xmlrpc_client_destroy(). A handle for this object is an argument to the functions that perform RPCs.

A slightly simpler method is available in which the client is implied by a static global variable. You can't use it in a modular program because all users of libxmlrpc_client in the program would be sharing the same global variable and would conflict with each other. But before Xmlrpc-c 1.05 (March 2006), this is the only interface available.

The private client method is requires a few more lines of code (a global constant setup and teardown, and an extra argument on many functions), but is the cleaner option by far. Code that uses a private client can be modular and it is obvious to a reader of the code where the state is being kept, as opposed to the global client method where it is in a hidden global variable.

An example of using the global client object is here.

An example of using a private client object is here.

Private Client (Versatile Interface)

A client object is represented by a data structure of type xmlrpc_client. A pointer to the object is a handle that you use to identify it.

Function: xmlrpc_client_create()

Prototype:


void 
xmlrpc_client_create(xmlrpc_env *                envP,
                     int                         flags,
                     char *                      appname,
                     char *                      appversion,
                     struct xmlrpc_clientparms * clientparmsP,
                     unsigned int                parmSize,
                     xmlrpc_client **            clientPP);


This creates an Xmlrpc-c client object and returns a handle for it.

You use this handle with various other library functions.

You can undo this — destroy the object — with xmlrpc_client_destroy().

envP is an error environment variable pointer.

appname and appversion are meaningful only if the client XML transport is libwww, and are in the parameter list (instead of the libwww transport-specific parameters) only for historical reasons. These values control the User-Agent HTTP header field in the XML-RPC call. (The User-Agent HTTP header field normally tells through what program the user made the HTTP request; for classic web browsing, it identifies the web browser program, such as Internet Explorer). The value in the User-Agent header field is appname/appversion, plus the name and version number of the libwww library. For the other HTTP transports, see the documentation of the individual transport for information on controlling User-Agent.

The name and version number of your program would be appropriate values for these.

These are asciiz strings.

clientparmsP is a pointer to a structure that contains other parameters of the client. parmSize is the size in bytes of that structure. More precisely, it is the amount of the structure that you have filled with meaningful information. Details are below.


struct xmlrpc_clientparms {
    const char *               transport;
    struct xmlrpc_xportparms * transportparmsP;
    size_t                     transportparmSize;
    const struct xmlrpc_client_transport_ops * transportOpsP;
    xmlrpc_client_transport *  transportP;
    xmlrpc_dialect             dialect;
    xmlrpc_curl_progress_fn *  progress_fn
};

This structure may contain more information in future versions of libxmlrpc_client. If you want defaults for everything in this structure, you may specify a null pointer as clientparmsP.

For parmSize, use the XMLRPC_CPSIZE macro. This macro gives the size of the xmlrpc_clientparms structure up through the member you name. Name the last member in the structure that you set. You must set every member up through that one, but for every member after it, xmlrpc_client_create() will assume a default.

The reason it's important to use XMLRPC_CPSIZE instead of just setting all the members and using sizeof(struct xmlrpc_clientparms) is forward compatibility. Future versions of libxmlrpc_client might add new members, and you want the client program you write today and compile with that future library to work. XMLRPC_CPSIZE also allows you to not worry about members at the end of the structure that you don't care about.

It isn't so easy to omit parameters you don't care about from the middle of the structure, but the members are defined so that a value encoded as zero bits is the same as the member not being present, so you can just set the whole structure to zero bits and then set the members you care about to something else.

xmlrpc_client_create() was new in Xmlrpc-c 1.05 (March 2006). Before that, you must use the global client.

Specifying the XML Transport

The transport, transportparmsP, transportparm_size, transportOpsP, and transportP parameters are all for you specify what to use for the XML transport. The XML transport is the thing that delivers XML to the XML-RPC server and gets XML back. See Client XML Transports. There are two ways to specify the XML transport. In the first, you name a built-in transport class and xmlrpc_client_create() creates one for you. In the other, you supply your own XML transport.

Using a Built-in XML Transport Class

Select this option by making transportP null or not present.

transport is the name of the XML transport class that you want the client to use, as an asciiz string. The available transport classes (by name) are:

libwww
Transports of this class use the traditional World Wide Web Consortium (w3c) HTTP library. This is the default.
curl
Transports of this class use Curl, an HTTP library generally superior to the w3c libwww one.
wininet
Transports of this class use Wininet, an HTTP library for Windows that is generally superior to the w3c libwww one.

transportparmsP and transportparmSize are the address and size, respectively, of a structure that describes parameters whose meanings are specific to the particular transport you are using. The definition of struct xmlrpc_xportparms is not "complete" (in a C sense) you always cast between that and a type specific to a transport, named like "xmlrpc_TRANSPORT_xportparms". Transports typically take variable length parameter structures such that the members at the end of the structure are optional, so transportparmSize tells how much parameter data you are specifying. You normally use a special macro instead of sizeof to generate the value for transportparmSize (for example, XMLRPC_CXPSIZE for the Curl transport).

The transport parameters are described in Client XML Transports.

If your clientparms structure is too small to include transportparmsP or transportparmsP is a null pointer, that selects defaults for all transport-specific parameters.

The whole transport-specific parameters interface was new in Xmlrpc-c 1.02 (March 2005). Before that, all the parameters that are now transport-specific parameters were always defaults.

Supplying Your Own XML Transport

Select this option by making transport a null pointer and transportP non-null.

Details on how to create a transport class are not in this manual. Instead, just look at the interface header files and the source code for the built-in transport classes.

transportOpsP points to an operation vector that serves to define the class of XML transport.

transportP is a handle for the transport object. Its meaning is entirely determined by the functions identified by transportOpsP.

The ability to supply your own transport was introduced in Xmlrpc-c 1.09 (December 2006).

XML-RPC Dialect

dialect selects the dialect that the client will use when it generates the XML for a method call parameter.

Note that this has no effect on the dialect the client is able to interpret in responses from a server. The client understands all the dialects.

The default is i8.

This parameter was new in Xmlrpc-c 1.11 (June 2007). Before that, the dialect is always i8.

Progress Function

You can specify a function to be called periodically to report the progress of each RPC. This works only if you use the Curl transport (If you don't, you can specify the function, but it will never get called) and only with the asynchronous RPC facility.

progress_fn is a function (a function pointer to one, actually) that the client calls periodically while a transaction is in progress to tell you how much progress has been made toward transporting your call and response.

NULL means don't call anything. This is the default.

A progress function (type xmlrpc_progress_fn) has the following prototype:


    void
    progress(struct xmlrpc_progress_data data);

The data argument tells you the progress of the current transaction. Its type is as follows.


    struct xmlrpc_transfer_progress {
        double total;
        double now;
    };
    
    struct xmlrpc_progress_data {
        struct xmlrpc_transfer_progress call;
        struct xmlrpc_transfer_progress response;
    };

Note that only the progress of data transfer gets reported. That's because data transfer is normally the only thing that progresses at observable speed. If it takes a while to process data coming or going, or to look up the IP address of the server, or to establish a TCP connection, you won't have any indication of that progress (except that you won't see any data moving). Likewise, in the time between when the call has been transported and the response has not begun to be transmitted, you will have no indication of the progress the server is making executing the RPC.

The transport doesn't necessarily know the "total" values. For example, before the RPC has executed, the transport couldn't possibly know how much response data there will be. In those cases, the value is zero.

If the XML transport is Curl, this function is essentially a passthrough of the similar function in the Curl library. So for details, check out the Curl library (option CURLOPT_PROGRESS_FUNCTION). (But if you find something there worthy of putting here in the Xmlrpc-c manual, be sure to report that to the Xmlrpc-c maintainter).

This parameter was new in Xmlrpc-c 1.24 (September 2010).

Function: xmlrpc_client_destroy()

Prototype:


void 
xmlrpc_client_destroy(xmlrpc_client * clientP);

This function destroys a client object. It releases resources used by the object.

clientP is the handle of the client to be destroyed. You got it from a prior xmlrpc_client_create() call.

You must not use the client handle for anything after you execute xmlrpc_client_destroy().

You must not call this on a client that has RPCs in progress via the asynchronous RPC facility. To ensure there are none, call xmlrpc_client_event_loop_finish().

xmlrpc_client_destroy() was new in Xmlrpc-c 1.05 (March 2006). Before that, you must use the global client.

Global Client (Simpler Interface)

There can be zero or one global client per program (A "program" is all the code and threads that share one memory space).

To create the global client, call xmlrpc_client_init2(). This additionally sets up library global constants, i.e. it has the effect of calling xmlrpc_client_setup_global_const().

You call this once at the beginning of your program, before calling anything else in libxmlrpc_client, and while your program is still one thread.

When you're done using the object, call xmlrpc_client_cleanup(). After that, you can start over with a new object by calling xmlrpc_client_init2() again. xmlrpc_client_cleanup() destroys the global client and abandons global constants (i.e. has the effect of calling xmlrpc_client_teardown_global_const(). In truth, this call is unnecessary. All it does is free resources. If your program is sloppy enough to use the global client (as opposed to creating a private client of its own), it might as well be more sloppy and let the operating system clean up the global client automatically as the program exits.

Function: xmlrpc_client_init2()

Prototype:


void 
xmlrpc_client_init2(xmlrpc_env *                envP,
                    int                         flags,
                    char *                      appname,
                    char *                      appversion,
                    struct xmlrpc_clientparms * clientparmsP,
                    unsigned int                parmSize);



This creates the global Xmlrpc-c client object. You must not call this while the global Xmlrpc-c client object already exists.

This function is identical to xmlrpc_create_client() except that it creates the global client rather than a private one, and returns no handle. (With the global client, you use library functions designed for the global client, so they know implicitly to use this client).

This function is not thread-safe. Results are undefined not only if you call it and another libxmlrpc_client function at the same time from separate threads, but if you call it while any other thread is running at all. The reason for this restriction is that the function internally calls non-threadsafe functions in other libraries and you don't even know what libraries those are, so the only way you know another thread isn't calling that other library simultaneously is if there is no other thread running. So you typically call xmlrpc_client_init2() at the beginning of a threaded program, while it is still just one thread and let the relevant threads inherit the global client.

Function: xmlrpc_client_init()

This is an older, less functional version of xmlrpc_client_init2(). It exists for backward compatibility. Don't use that in new code, but if you're maintaining old code, you can easily guess what it does based on the documentation of xmlrpc_client_init2().

Function: xmlrpc_client_cleanup()

Prototype:


void 
xmlrpc_client_cleanup(void);

This destroys the global Xmlrpc-c client object. You call this before exit from the program to release resources. You may also call it in order to create a new global Xmlrpc-c client object, since you can't have more than one existing at once.

You must not call this if the global client has RPCs in progress via the asynchronous RPC facility. To ensure there are none, call xmlrpc_client_event_loop_finish_asynch().

This function is thread-unsafe in a way analogous to xmlrpc_client_init2().

Performing An RPC

The purpose of using libxmlrpc_client is to perform RPCs. The functions in this section do that. All the other functions in the library are just overhead to support these functions.

When we say "make an XML-RPC call," we refer only to delivering the XML for it to the server. The server's implementation of it, and the response side of the transaction are not included.

"Perform an RPC" means to conduct the entire transaction: make the call, have the server do its thing, and receive the response.

Synchronous RPCs

The easiest function to use to perform an RPC call is xmlrpc_client_call2f(). As arguments, you supply the URL of your server, the name of the XML-RPC method you are invoking, and the XML-RPC parameters. You supply those parameters as a format string followed by a variable number of arguments as required by the format string. You get the response back as an XML-RPC value.

Function: xmlrpc_client_call2f()

Example:


    xmlrpc_value * resultP;
    xmlrpc_client_call2f(&env, clientP, "http://example.com/RPC2", "sample.add", &resultP,
                "(ii)", (xmlrpc_int32) 5, (xmlrpc_int32) 7);

Prototype:

    void
    xmlrpc_client_call2f(xmlrpc_env *    envP,
                         xmlrpc_client * clientP,
                         const char *    serverUrl,
                         const char *    methodName,
                         xmlrpc_value ** resultPP,
                         const char *    format,
                         ...);

clientP is the handle of the client to use. You got it from a prior xmlrpc_client_create() call.

server_url is the same as the argument to xmlrpc_server_info_new(). (But it's just what you'd expect it to be, so don't feel you have to go read that).

method_name is the name of the XML-RPC method you are invoking (it's a name defined by the particular XML-RPC server). This is an ASCIIZ string.

resultPP is a pointer to the variable in which the function returns the handle for the XML-RPC result.

format is a format string that describes the XML-RPC parameters. You need this even if there are no parameters — to specify that fact.

The variable arguments (...) are the values for the XML-RPC parameters. Their number, type, and meaning are determined by format.

format and the variable arguments together describe an XML-RPC value. That value must be of the array type. Each element of the array is an XML-RPC parameter of the RPC, in array index order. This odd use of an XML-RPC value is a historical mistake (at one time, the xmlrpc_value type was meant to be a general purpose data structure -- an extension to the C language, rather than just an entity for XML-RPC use). Do not let it fool you into thinking that you're specifying an array as the single parameter of the RPC. In XML-RPC, an RPC takes any number of parameters, and the elements of this array are those parameters. If the RPC has no parameters, this is an empty array.

If the RPC fails at the server (i.e. the server's response is an XML-RPC fault), xmlrpc_client_call2f() fails. The error code and description in *envP in that case are what the server said in its fault response.

xmlrpc_client_call2f() was new in Xmlrpc-c 1.05 (March 2006). Before that, you must use the global client.

Function: xmlrpc_client_call2f_va()

Example:


     void my_varargs_function(xmlrpc_client * clientP,
                              const char *    format,
                              ...) {

        xmlrpc_env env;
    
        va_list args;

        va_start(args, format);
  
        xmlrpc_value * resultP;
    
        xmlrpc_client_call2f_va(&env, clientP, "http://example.com/RPC2", "sample.add", &resultP, args);

        ...
        }

Prototype:

    void
    xmlrpc_client_call2f_va(xmlrpc_env *    envP,
                            xmlrpc_client * clientP,
                            const char *    serverUrl,
                            const char *    methodName,
                            xmlrpc_value ** resultPP,
                            const char *    format,
                            va_list         args);

This is the same as xmlrpc_client_call2f(), except it takes the RPC arguments as a va_list instead of C variable arguments so you can call it from another function that has C variable arguments.

Function: xmlrpc_client_call2()

This is like xmlrpc_client_call2f(), but is more flexible in your ability to specify the XML-RPC parameters and the server information.

Prototype:


void
xmlrpc_client_call2(xmlrpc_env *               envP,
                    struct xmlrpc_client *     clientP,
                    const xmlrpc_server_info * serverInfoP,
                    const char *               methodName,
                    xmlrpc_value *             paramArrayP,
                    xmlrpc_value **            resultPP);

Example:


    xmlrpc_env env;
    xmlrpc_client * clientP;
    xmlrpc_value * resultP;
    xmlrpc_server_info * serverInfoP;
    xmlrpc_value * paramArrayP;
    xmlrpc_value * addend1P;
    xmlrpc_value * addend2P;

    serverInfoP = xmlrpc_server_info_new(
        &env, "http::/localhost:8080/RPC2");
    
    paramArrayP = xmlrpc_array_new(&env);

    addend1P = xmlrpc_int_new(&env, 5);
    addend2P = xmlrpc_int_new(&env, 7);

    xmlrpc_array_append_item(&env, myArrayP, addend1P);
    xmlrpc_array_append_item(&env, myArrayP, addend2P);

    xmlrpc_DECREF(addend1P);
    xmlrpc_DECREF(addend2P);

    xmlrpc_client_call2(&env, clientP, serverInfoP, "sample.add",
                        paramArrayP, &resultP);

    xmlrpc_DECREF(paramArrayP);
    xmlrpc_server_info_free(serverInfoP);

For the XML-RPC parameters, you supply an xmlrpc_value of array type paramArrayP, in which each element of the array is an XML-RPC parameter. This value has the same meaning as the one you specify via format string with xmlrpc_client_call2f().

Note that xmlrpc_client_call2f() is useless when you don't know at compile time what kinds of parameters the method requires. But xmlrpc_client_call2() lets you build up the parameter list using runtime program intelligence.

For the server information, you supply serverInfoP. While xmlrpc_client_call2f() lets you specify only the server's URL, serverInfoP can specify more information necessary to work with the server. For example, you may need to authenticate yourself to the server, so you may have to supply some credentials. serverInfoP can do that. See the description of a xmlrpc_server_info object.

xmlrpc_client_call2() was new in Xmlrpc-c 1.05 (March 2006). Before that, you must use the global client.

Function: xmlrpc_client_call()

This is identical to xmlrpc_client_call2f() except that it uses the global client. Ergo, there is no clientP argument.

Also, it uses the more traditional and compact, but harder to read, form in which the return value of the function is used to return information.

Prototype:


    xmlrpc_value * 
    xmlrpc_client_call(xmlrpc_env * envP,
                       const char * serverUrl,
                       const char * methodName,
                       const char * format,
                       ...);

Function: xmlrpc_client_call_params()

This is like xmlrpc_client_call() except that you specify the XML-RPC method parameters like you do for xmlrpc_client_call2(), which is more flexible.

Prototype:


    xmlrpc_value * 
    xmlrpc_client_call_params(xmlrpc_env *   envP,
                              const char *   serverUrl,
                              const char *   methodName,
                              xmlrpc_value * paramArrayP);
                              

Function: xmlrpc_client_call_server()

This is like xmlrpc_client_call() except that you specify the server information like you do for xmlrpc_client_call2(), which is more expressive.

Prototype:


    xmlrpc_value * 
    xmlrpc_client_call_server(xmlrpc_env *         envP,
                              xmlrpc_server_info * serverInfoP,
                              const char *         methodName,
                              const char *         format,
                              ...);

Function: xmlrpc_client_call_server_params()

This is like xmlrpc_client_call() except that you specify the XML-RPC parameters and server information like you do for xmlrpc_client_call2().

Prototype:


    xmlrpc_value * 
    xmlrpc_client_call_server_params(
        xmlrpc_env *         envP,
        xmlrpc_server_info * serverInfoP,
        const char *         methodName,
        xmlrpc_value *       paramArrayP);

Asynchronous RPCs

All the preceding functions for performing an RPC do the entire thing while the caller waits. But some programs want to use a type of explicit threading where the function returns immediately while the RPC is still in progress so the caller can proceed to different work (perhaps starting more RPCs) and the caller syncs up with the RPC later.

libxmlrpc_client provides an asynchronous RPC facility for that.

But: the asynchronous client facility is not so asynchronous that you can just start an RPC and forget about it. When you start an RPC, you must eventually finish it and recognize that it has completed. Until then, it is using resources and may not even progress. RPCs are inherently synchronous, in that there's a response that tells the client that the server has completed its work. If you want a communication style where you just throw a message out and walk away and it arrives when it arrives, you should consider something other than an RPC protocol. A simple UDP packet sometimes works. JSON-RPC, despite its name, has facilities for that too.

If you're just looking for a way to avoid waiting indefinitely for a slow RPC, the asynchronous RPC facility is not for you. You probably want the RPC interruption facility.

By the way, there's no such thing as "an asynchronous call." "asynchronous" describes the overall relationship of the RPCs to the execution of the caller. The relationship is asynchronous because the two are not in lock step. Saying that an individual call is asynchronous is like saying that an individual note of a song is in 4/4 time.

The proper appellation of a call that returns before all the work is done is a "no-wait" call.

There is a no-wait version of each of the RPC functions mentioned above. For private clients, the functions are xmlrpc_client_start_rpc() and xmlrpc_client_start_rpcf() (analogous to xmlrpc_client_call2() and xmlrpc_client_call2f()). For the global client, the functions are xmlrpc_client_call_asynch(), xmlrpc_client_call_server_async(), and xmlrpc_client_call_asynch_params() (note the inconsistency in these names -- it was a mistake).

For these no-wait versions, we need to define another entity -- the "RPC request." An RPC request is a request through libxmlrpc_client for an RPC. The main thing an RPC request does is the RPC, but in pathological conditions an RPC request might not do an RPC at all. The RPC request exists before and after the RPC does.

The no-wait version of the RPC calls makes an RPC request, as opposed to performing an RPC. Performing of the RPC typically comes later. So there is an additional argument with which you identify a function to be called to handle the response to the XML-RPC call. This is called a response handler. There is another argument that supplies an argument to be passed to the response handler. The response handler gets that argument in addition to information from the XML-RPC response. The response handler is slightly misnamed, because it handles all completions of RPC requests. For example, if a problem prevents libxmlrpc_client from even starting the RPC, which means there is no XML-RPC response, the response handler still gets called.

It's natural to believe that the response handler is a completion function that gets called the moment the RPC request completes, like an I/O interrupt. But that's not what it is.

When you start an RPC (e.g. by calling xmlrpc_client_start_rpc()), you must eventually call an RPC finishing function such as xmlrpc_client_event_loop_finish(). What that does is finish all RPC requests that have been started. This includes waiting for RPCs to complete if they haven't already. For each of these RPC requests (when the RPC has completed), the RPC finishing function calls the response handler. It also does other things that are necessary to complete the RPC request, so you must call it eventually even if you don't care about the completions. An RPC finishing function doesn't specify a particular RPC to finish. One call finishes all RPCs a particular client has started.

For a private client, the RPC finishing functions are xmlrpc_client_event_loop_finish() and xmlrpc_client_event_loop_finish_timeout(). The former waits as long as it takes for all the client's RPCs to complete; the latter returns after a timeout you specify so you can do other stuff and come back to it.

For the global client, the equivalent finishing functions are xmlrpc_client_event_loop_finish_asynch() and xmlrpc_client_event_loop_finish_asynch_timeout().

If you find yourself needing timeouts, you should consider dumping the whole aynchronous RPC facility and using general purpose threading as recommended above. But it does allow you to intersperse XML-RPC transactions with other work in simple ways. It does not, however, give you a way to abandon a long-running RPC. It gives you a way to temporarily suspend waiting for an RPC to complete, but you must eventually wait for every RPC to complete. There is no such thing as a cancelled RPC in this facility.

The timeout RPC finishing functions give you no way to know whether the finishing function timed out or the RPCs completed. If you need to know all the RPCs are completed, call xmlrpc_client_event_loop_finish_async().

An RPC finishing function will return early if the process receives a signal (assuming the signal does not terminate the process). The interface does not give you any good way to know whether the function returned because a wait was interrupted by a signal, because all the RPCs completed, or because the wait timed out. But as with the timeout, interrupting the wait for the RPC to complete does not excuse you from eventually completing the RPC. So you have to use your imagination to find a way to make sure you eventually give an RPC finishing function a chance to complete every RPC in spite of interruptions. Some day, we will fix this interface.

Don't confuse interrupting the finishing function with interrupting the RPCs it's trying to finish. When you set a client's interrupt flag, that causes all the client's RPCs to abort. You still need a subsequent call to a finishing function to cause the RPCs to finish failing and free resources and such, but that call will be quick, as the RPCs are no longer trying to do anything but clean up.

The exact nature of the asynchronicity depends highly on the client XML transport involved. The no-wait call may in actuality wait for the RPC to finish. Or it might not even start it, and the RPC finishing function might do all the work. See the sections on the transports (e.g. curl) for details.

By the way, people sometimes like to refer to the response handler as a callback. It isn't. A callback is something that gets called within the context of the call with which it is associated. I.e. A calls into B, specifying a C callback. Before returning to A, B calls C. An example of using a callback is a sort routine. The sort routine gets two arguments: a set of values and a collating function that compares two such values. The sort routine calls the collating function on various pairs of values as part of putting them all in order. The calling of the collating function is a callback.

Function: xmlrpc_start_rpc()

This is a no-wait version of xmlrpc_client_call2().

Prototype:


void
    xmlrpc_client_start_rpc(xmlrpc_env *               envP,
                            struct xmlrpc_client *     clientP,
                            const xmlrpc_server_info * serverInfoP,
                            const char *               methodName,
                            xmlrpc_value *             paramArrayP,
                            xmlrpc_response_handler    responseHandler,
                            void *                     userData);

The arguments are the same as for xmlrpc_client_call2() except that intead of resultP argument, there is responseHandler and userData. responseHandler is the function that gets called to report the result of the RPC and userData is a pointer, opaque to libxmlrpc_client, that gets passed to that function.

Function: xmlrpc_start_rpcf()

This is a no-wait version of xmlrpc_client_call2f().

Prototype:


    void
    xmlrpc_client_start_rpcf(xmlrpc_env *            envP,
                             xmlrpc_client *         clientP,
                             const char *            serverUrl,
                             const char *            methodName,
                             xmlrpc_response_handler responseHandler,
                             void *                  userData,
                             const char *            format,
                             ...) {

The arguments are the same as for xmlrpc_client_call2f() except that intead of resultP argument, there is responseHandler and userData, the same as for xmlrpc_client_start_rpc().

Function: xmlrpc_start_rpcf_va()

This is a no-wait version of xmlrpc_client_call2f_va().

Prototype:


  void
  xmlrpc_client_start_rpcf_va(xmlrpc_env *            envP,
                              xmlrpc_client *         clientP,
                              const char *            serverUrl,
                              const char *            methodName,
                              xmlrpc_response_handler responseHandler,
                              void *                  userData,
                              const char *            format,
                              va_list                 args) {

The arguments are the same as for xmlrpc_client_call2f_va() except that instead of resultP argument, there is responseHandler and userData, the same as for xmlrpc_client__start_rpc().

Response Handler

Whenever you make an RPC request via one of the functions that does not wait for it to complete, you supply a response handler function and a parameter for it. If the function succeeds, the response handler eventually gets called. If the function fails, the response handler never gets called. Note that I'm talking about the library function itself failing. The RPC or RPC request might fail even though the function that made the request succeeded, and in that case the response handler definitely gets called.

The primary purpose of the response handler is to process the XML-RPC response to the XML-RPC call that was requested. But in pathological cases, the request does not result in any XML-RPC call being made; or any XML-RPC response being received; or the response being capable of being processed. In those cases, the response handler handles the failure.

The xmlrpc_response_handler type is a function pointer to a response handler. The prototype of response handler is as follows:


void (*xmlrpc_response_handler) (const char *   server_url,
                                 const char *   method_name,
                                 xmlrpc_value * param_array,
                                 void *         user_data,
                                 xmlrpc_env *   faultP,
                                 xmlrpc_value * resultP);

server_url, method_name, and param_array are the information you provided to describe the RPC when you requested it.

user_data is the response handler argument you specified when you requested the RPC.

faultP is a pointer to an error environment variable that describes either the error response to the XML-RPC call or the client-side failure of libxmlrpc_client to make the XML-RPC call or process its response or indicates that the RPC and RPC request were successful.

resultP is a pointer to an XML-RPC value which is the result of the RPC. This is undefined unless faultP indicates success.

None of the objects passed to the response handler have references to them that belong to the response handler, so there is no reference for the response handler to release. The caller naturally maintains its own reference on the objects for the duration of the call, so you know they aren't going to go away.

A call to any client function may wait for a response handler to run for any RPC of that same client, whether the RPC is related to the call or not. Keep that in mind in ordering your resources to avoid deadlock. In particular, you cannot call any client function against the same client within your response handler.

xmlrpc_client_event_loop_finish()

This function finishes all outstanding RPCs.

Prototype:


    void
    xmlrpc_client_event_loop_finish(xmlrpc_client * clientP);

clientP is the handle of the client whose RPCs are to be finished.

The function finishes all RPCs that were outstanding when the function was called, and may finish additional ones that were started while it was running. To be sure all RPCs are finished, you must stop starting RPCs, then call xmlrpc_client_event_loop_finish().

xmlrpc_client_event_loop_finish_timeout()

This function finishes all outstanding RPCs, except that the time it runs is bounded. Whatever RPCs in cannot finish within that time constraint, it leaves unfinished.

Prototype:


    void
    xmlrpc_client_event_loop_finish_timeout(xmlrpc_client * clientP,
                                            unsigned long   milliseconds);

clientP is the handle of the client whose RPCs are to be finished.

This function is the same as xmlrpc_client_event_loop_finish() except that the time it runs is bounded. It will not run longer than milliseconds milliseconds. In actuality, some parts of finishing an RPC are not interruptible, so it may run longer than that, but at least it won't wait for a server to respond to an RPC after this period has passed.

You cannot tell directly whether the function returned because all the RPCs are finished or because it timed out. If you need to know whether all the RPCs have finished, you can either keep track with your RPC response handlers (see Response Handler) of what RPCs are outstanding or call xmlrpc_client_event_loop_finish() instead.

Warning: some people come away with the idea that the timeout on this function is a timeout on the asynchronous RPCs - that somehow it forces slow RPCs to finish. In fact, all it does is cut short the wait for the RPCs to finish, giving the calling thread the opportunity to do other work before coming back to finishing the RPCs.

xmlrpc_client_event_loop_finish_asynch()

This is the same as xmlrpc_client_event_loop_finish() except that it works on the global client. Hence, it has no clientP argument.

Prototype:


    void
    xmlrpc_client_event_loop_finish_asynch(void);

xmlrpc_client_event_loop_finish_asynch_timeout()

This is the same as xmlrpc_client_event_loop_finish_timeout() except that it works on the global client. Hence, it has no clientP argument.

Prototype:


    void
    xmlrpc_client_event_loop_finish_asynch_timeout(unsigned long milliseconds);

Roll Your Own RPC

You can do an RPC at the XML level if you want: build your own call XML and parse the response XML.

xmlrpc_client_transport_call() merely transports XML to the server and collects the XML the server sends back. It does not look at the XML at all; in fact, it need not even be XML.

Example:


    xmlrpc_mem_block * respXmlP;
    xmlrpc_mem_block * callXmlP;
    xmlrpc_value * paramP;
    xmlrpc_value * sumP;

    paramP = xmlrpc_build_value(&env, "(ii)", 5, 7);

    XMLRPC_MEMBLOCK_NEW(char, callXmlP, 0);

    xmlrpc_serialize_call(&env, callXmlP, "sample.add", paramP);

    xmlrpc_client_transport_call(&env, serverP, callXml, &respXml);

    sumP = xmlrpc_parse_response(&env, 
                                 XMLRPC_MEMBLOCK_CONTENTS(char, respXmlP),
                                 XMLRPC_MEMBLOCK_SIZE(char, respXmlP));

    XMLRPC_MEMBLOCK_FREE(responseXmlP);
    XMLRPC_MEMBLOCK_FREE(callXmlP);

Server Information Object

An xmlrpc_server_info structure is an object that describes an XML-RPC server. It identifies the server and tells how to talk to it. You can use an xmlrpc_server_info object as input to a function that makes an XML-RPC call to the indicated server. This is not an object that represents the server itself -- just information about it. The key distinction is that the object contains no information about the state of the server or of the client's use of the server. There is no expectation that all access to that server will be via this object.

An xmlrpc_server_info object contains the following information about a server:

Basic xmlrpc_server_info functions

To create an xmlrpc_server_info object, call xmlrpc_server_info_new(). To destroy one, call xmlrpc_server_info_free().

Function: xmlrpc_server_info_new()

Overview:

xmlrpc_server_info *
xmlrpc_server_info_new(xmlrpc_env * envP,
                       const char * serverUrl);

This creates an xmlrpc_server_info object.

You must eventually destroy the object with xmlrpc_server_info_free to free its resources.

The object created says no identification is required.

An example URL argument is:

  http://www.oreillynet.com/meerkat/xml-rpc/server.php

The URL must be an absolute URL (you can recognize an absolute URL by the fact that has a double slash after the "http:") As the URL of an XML-RPC server, it must be an HTTP URL (that means, among other things, that the URL specifies a scheme of "http"). While XML-RPC requires "http", there is a common variation on XML-RPC where the scheme is "https".

You can create an xmlrpc_server_info object containing the same information as an existing one with xmlrpc_server_info_copy().

Function: xmlrpc_server_info_free()

Overview:

void
xmlrpc_server_info_free(xmlrpc_server_info * serverP);

This destroys an xmlrpc_server_info object.

Function: xmlrpc_server_info_copy()

Overview:

xmlrpc_server_info * 
xmlrpc_server_info_copy(xmlrpc_env *         envP,
                        xmlrpc_server_info * serverP);

This creates a new xmlrpc_server_info object which is a copy of the one pointed to by serverP.

You must eventually destroy the object with xmlrpc_server_info_free to free its resources.

Authentication

In an xmlrpc_server_info object, you indicate what kind of authentication (and identification) you want to do with the server.

A freshly created xmlrpc_server_info specifies no authentication at all. Functions in this section declare that you're willing to authenticate various other ways. It is up to the client XML transport whether actually to do it or not; not all transports know how to do all of them. Furthermore, there is negotiation with the server involved. Both the server and client have to be willing to use a particular method.

All of the XML transports can do HTTP basic authentication. Only the Curl transport can do the others. And depending on the version of Curl library with which you link your program, it may not be able to do some of those. Any Curl library built after 2005 should at least be able to do digest authentication.

Function: xmlrpc_server_info_set_user()

Overview:

void 
xmlrpc_server_info_set_user(xmlrpc_env *         envP,
                            xmlrpc_server_info * serverInfoP,
                            const char *         username,
                            const char *         password);

This sets the username and password to be used in identifying and authenticating the client, for those authentication methods that involve usernames and passwords.

This function by itself does not enable any authentication. You must separately call a function such as xmlrpc_server_info_allow_auth_basic as well.

envP is an error environment variable pointer.

serverP identifies the xmlrpc_server_info object in which the information is to be changed.

username and password are the username and password values as defined by HTTP basic authentication.

When the function fails, it leaves the xmlrpc_server_info object unchanged.

This function was new in Xmlrpc-c 1.13 (December 2007).

Function: xmlrpc_server_info_allow_auth_basic()


void 
void 
xmlrpc_server_info_allow_auth_basic(xmlrpc_env *         envP,
                                    xmlrpc_server_info * serverInfoP);

This sets the xmlrpc_server_info object to indicate that HTTP basic authentication is allowed with the server.

You must set a username and password with xmlrpc_server_info_set_user() before calling this, or it will fail.

Use xmlprc_server_info_disallow_auth_basic() to undo this.

envP is an error environment variable pointer.

serverP identifies the xmlrpc_server_info object in which the information is to be changed.

This function was new in Xmlrpc-c 1.13 (December 2007).

Function: xmlrpc_server_info_disallow_auth_basic()

Overview:

void 
void 
xmlrpc_server_info_disallow_auth_basic(xmlrpc_env *         envP,
                                       xmlrpc_server_info * serverInfoP);

This sets the xmlrpc_server_info object to indicate that HTTP basic authentication is not allowed with the server.

This undoes what xmlprc_server_info_allows_auth_basic() does.

This function was new in Xmlrpc-c 1.13 (December 2007).

Function: xmlrpc_server_info_allow_auth_digest()

This is analogous to xmlrpc_server_info_allow_auth_basic(), except for HTTP digest authentication.

Function: xmlrpc_server_info_allow_auth_digest()

This is analogous to xmlrpc_server_info_disallow_auth_basic(), except for HTTP digest authentication.

Function: xmlrpc_server_info_allow_auth_negotiate()

This is analogous to xmlrpc_server_info_allow_auth_basic(), except for HTTP GSS-Negotiate authentication.

Function: xmlrpc_server_info_allow_auth_negotiate()

This is analogous to xmlrpc_server_info_disallow_auth_basic(), except for HTTP GSS-Negotiate authentication.

Function: xmlrpc_server_info_allow_auth_ntlm()

This is analogous to xmlrpc_server_info_allow_auth_basic(), except for HTTP NTLM authentication.

Function: xmlrpc_server_info_allow_auth_ntlm()

This is analogous to xmlrpc_server_info_disallow_auth_basic(), except for HTTP NTLM authentication.

Function: xmlrpc_server_info_set_basic_auth()

This function is obsolete. In new code, use xmlrpc_server_info_allow_auth_basic and xmlrpc_server_info_set_user instead.


void 
xmlrpc_server_info_set_basic_auth(xmlrpc_env *         envP,
                                  xmlrpc_server_info * serverP,
                                  const char *         username,
                                  const char *         password);


This has the same effect as and xmlrpc_server_info_set_user followed by xmlrpc_server_info_allow_auth_basic.

envP is an error environment variable pointer.

serverP identifies the xmlrpc_server_info object in which the information is to be changed.

username and password are the username and password values as defined by HTTP basic authentication.

When the function fails, it leaves the xmlrpc_server_info object unchanged.

Miscellaneous

Function: xmlrpc_server_info_set_unix_socket()

Overview:

void 
xmlrpc_server_info_set_unix_socket(xmlrpc_env *         envP,
                                   xmlrpc_server_info * serverInfoP,
                                   const char *         socketPath);

This makes the transaction use a Unix socket instead of a TCP/IP socket and identifies the Unix socket to use. This means the transaction will not be true HTTP and therefore not true XML-RPC. It also means the URL (though still required) does not determine what server is accessed. (A Unix socket communicates with whatever server, which must be within the same OS image, is listening on that same socket).

envP is an error environment variable pointer.

serverP identifies the xmlrpc_server_info object in which the information is to be changed.

socketPath is the path name (i.e. file name) of the socket to use.

When the function fails, it leaves the xmlrpc_server_info object unchanged.

This function was new in Xmlrpc-c 1.63 (June 2024).

When Server Doesn't Respond Properly

If the server is broken or even nonexistent, your RPC may take a long time or forever to complete or complete unlike you expect.

See Dealing With Broken Servers

SSL (HTTPS)

An XML-RPC client uses HTTP, which is not encrypted. However, a common variation of the protocol substitutes HTTPS, which uses SSL to encrypt the connections so that people can't snoop on them and clients and servers can't impersonate others.

To use libxmlrpc_client with HTTPs, use the Curl XML transport, which uses the SSL facilities of the Curl library. See the section Curl XML Transport - SSL.

Interrupting

There is a facility for interrupting long-running client functions and RPCs with a signal. Two examples of how this is useful:

Another way to put a time limit on an RPC is use the timeout parameter of the Curl transport.

How To Interrupt A Function or RPC

The program interrupted_client in the examples directory of the Xmlrpc-c source tree is a complete example of a client program whose long-running RPCs can be interrupted. It was new in Xmlrpc-c 1.13 (December 2007).

The way it works is that you set up a C variable somewhere that tells whether you want an interruption or not. It's called an interrupt flag. Set it to 0 to mean "carry on" and 1 to mean "interrupt." Tell Xmlrpc-c in advance where your flag is, with xmlrpc_client_set_interrupt(). Before you call an Xmlrpc-c function, set the interrupt flag to 0. Set up a signal handler that sets the interrupt flag to 1 and returns.

Xmlrpc-c library functions such as xmlrpc_client_call2() check that flag at certain times and, seeing it set, abort what they are doing and return a failure. So the only thing left to consider is just when the library function checks the interrupt flag. Ideally, it checks it before doing anything that could take a while, and shortly after every signal is handled. But it doesn't always meet that ideal; see Limitations.

If you are using the Curl client XML transport, an interruption usually looks like this to your program: Your libxmlrpc_client function call completes as a failure. The error message says that libcurl failed to execute an HTTP POST transaction and gave as a reason "Callback aborted".

There is no way to interrupt a function of the global client.

Remember that if you don't set up a signal handler, a signal typically terminates the process. And if you set up to block a signal, the process never receives it, so your signal handler does not run and a long-running system call keeps running.

When you set the interrupt flag to 1, all RPCs then in progress via the client terminate soon, aborting and failing if necessary. This means in addition to a libxmlrpc_client function returning soon, the server also may see an unfinished RPC. XML-RPC has no concept of aborting an RPC, so what the server sees may just be an abruptly truncated conversation.

This is different from the timeout in the asynchronous RPC interface. That timeout simply makes xmlrpc_client_event_loop_finish_timeout() return before the RPCs are finished. The RPCs still exist and you can (and must) eventually finish them with another call.

Note that in the asynchronous RPC interface, the interrupt flag interrupts the RPCs and does not interrupt finishing functions per se. A finishing function such as xmlrpc_client_event_loop_finish() proceeds to finish RPCs in spite of the interrupt flag, but if the interrupt flag is set, those RPCs should finish quickly.

Function: xmlrpc_client_set_interrupt()

Example:


    static int interrupt;
    xmlrpc_client * clientP;

    xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION, NULL, 0,
                         &clientP);

    xmlrpc_client_set_interrupt(clientP, &interrupt);

    interrupt = 0;

Prototype:


    void
    xmlrpc_client_set_interrupt(xmlrpc_client * clientP,
                                int *           interruptP);

This function declares an interrupt flag for a client. Henceforth, you can interrupt various client operations by setting that flag to a nonzero value.

To clear the interrupt flag, specify NULL for interruptP.

If you call this while the client is in the middle of something, results are undefined. Normally, you call this only as part of setting up a client, shortly after you create it.

This function was new in Xmlrpc-c 1.10 (March 2007).

Limitations

The system is imperfect. Only some things are eligible for interruption. Other long-running things may just ignore signals and keep you waiting.

One particular thing that is interruptible is, with the curl XML transport, the wait for the XML-RPC server to respond after the client has sent the XML-RPC call.

When a library function runs for a long time, it's usually because it is executing a system call that won't complete until something external happens, such as the system receives an HTTP response over the network. The nature of Unix signals is such that a signal will usually interrupt any system call taking place at the time of the signal. So after your signal handler returns, the system call returns (fails). The libxmlrpc_client function typically checks the interrupt flag soon after such a system call completes. If the flag is set, the function fails immediately. Otherwise, it just repeats the failed system call and the wait goes on. So you should see the Xmlrpc-c library function return soon after the signal.

In many cases that an Xmlrpc-c function ignores or delays response to a signal, it's because Xmlrpc-c uses a subordinate library that does not respect signals. If a library function that Xmlrpc-c calls does not return when the process receives a signal, there is nothing Xmlrpc-c can do to respond to the signal.

The library of greatest significance, with the Curl transport, is the Curl library ("libcurl"), which performs HTTP functions. Before March 2007, that library has a limitation in that it may take up to a second after a signal arrives for it to abort its wait (e.g. for a response from the server). (In case you're curious, this limitation is due to the way the interruptibility function evolved from another library function -- the ability to make periodic progress report callbacks, which itself evolved from a library function that actually prints progress reports on the terminal, once per second).

Another case where the interruptibility is not what you would expect is where the underlying system is older Linux. Older Linux kernels do not have a pselect() system call. Consequently, the GNU C Library on a system with one of these kernels cannot implement POSIX pselect(). libxmlrpc_client depends upon POSIX pselect() for its interruptibility. On these systems, the GNU C Library implements its pselect() function with select() and changes the signal mask before and after calling select(). This differs from what POSIX requires in that if a signal arrives before or just after the select() begins, it will not stop the select() from waiting. Consequently, if a signal arrives within a very narrow window of time, and your signal handler signals that libxmlrpc_client should abandon whatever it's doing, the libxmlrpc_client function that calls the GNU C Library's pselect() function will wait anyway.

Thus, on such a system you cannot depend upon a signal interrupting a libxmrpc_client call. It is still useful in many cases, though. For example, if you're trying to respond to control-C, this just means that on extremely rare occasions, the user will have to hit Control-C again. For timeouts based on alarm signals, you may want to have your signal handler reschedule an alarm signal for a short while later just in case the one it's handling falls into one of those blind windows.

A pselect() system call showed up in kernel.org Linux in an early 2007 release. I don't know what if any GNU C libraries and Linux operating systems take advantage of it.

Before Release 1.11 (June 2007), libxmlrpc_client may delay responding to a signal by up to a second when it arrives while a synchronous call function (e.g. xmlrpc_client_call2()) is executing, even with a current Curl library. (That's because the older libxmlrpc_client uses libcurl's curl_easy_perform() synchronous interface for that, and curl_easy_perform() is not properly interruptible. All it does is poll for interruptions once per second. Current libxmlrpc_client uses the curl "multi" interface instead).

Before Xmlrpc-c 1.10 (March 2007), there is no way to interrupt a libxmlrpc_client call (and not have the OS terminate your program).

Client XML Transports

The layer of libxmlrpc_client that delivers the XML for an RPC to the server and receives the XML for the response back is the client XML transport, and you can choose among several of them. In order for the RPC to be true XML-RPC, this transport must use HTTP to transport the XML, but in theory it could be something else entirely.

This section describes the individual client XML transports.

A normal libxmlrpc_client can use any of these; you choose one when you create the client with xmlrpc_client_create() or xmlrpc_client_init2(). But people often create a variation on libxmlrpc_client that omits transports they don't want or for which they don't have the prerequisites.

If you don't care (and if you're not doing anything fancy, there's really no reason to care), you can let libxmlrpc_client choose a transport for you by specifying NULL in place of the transport pointer on your xmlrpc_client_create() or xmlrpc_client_init2() call.

Curl

This transport uses the widely used Curl library to transport the XML over HTTP.

People usually render the name of this library as "cURL". We use standard typography instead in this manual, because it is easier to read.

Curl Sessions

The Curl library has a concept of a session (it's represented in the API by a CURL handle). The Xmlrpc-c Curl transport uses sessions like this:

The transport uses a single Curl session for the life of the transport (which is normally the life of the Xmlrpc-c client) for all RPCs you perform through the synchronous interface (the xmlrpc_client_call2() function). But every RPC you perform via the asynchronous interface gets its own Curl session. This latter situation is not desirable; it exists because of limitations of the Curl API -- A session cannot be used simultaneously by multiple threads.

Curl sessions matter for these reasons:

Cookies

The cookie mechanism is a feature of HTTP that adds stateful connections to this protocol that is fundamentally designed not to have them. There are sociological reasons for this corruption. The mechanism works as follows: In a response, an HTTP server includes an arbitrary key/value pair which is meaningful only to the server. The client essentially includes that pair in all future requests to that server.

The transport stores cookies the server sends and includes them in future requests, as defined by HTTP cookie standards. But it does so only within the scope of a Curl session. A cookie session is a Curl session and the Curl transport treats persistent cookies as session cookies -- they do not outlive the Curl session.

SSL

The transport works with SSL servers, i.e. https: URLs.

By default, the Curl transport will refuse to talk to the server (i.e. abort and fail your RPC) unless the server proves it is who you expect it to be.

There are two parts to establishing identity: identification and authentication (of identity). Identification is claiming to be someone. Authentication is proving the claim. You control these two things independently with the Curl transport.

In SSL, a server identifies itself by presenting a certificate. The certificate contains a Common Name and optionally Subject Alternate Names, which are normally host names -- the same names you put in a URL to identify the server. An SSL server authenticates its certificate by providing a digital signature. It may also provide a signature from someone else authenticating the server's signature, and so on up to someone whose signature you recognize. The Curl library comes with a few high level signatures, so as long as you trust whoever gave you the Curl library, the chain of trust will normally end with a signature you recognize.

You can make the Curl transport bypass the authentication of the server's identity (i.e. bypass making sure the server is who its certificate says it is) with the ssl_no_verifypeer option.

You can make the Curl transport bypass identification (i.e. bypass making sure the host name that the server claims via its certificate matches your URL) with the no_ssl_verifyhost option.

Note that there isn't much point to authenticating the server's certificate if you aren't going to use the authenticated host name. The Curl transport doesn't give you any way to use it except to abort the RPC if it doesn't match the URL.

Similarly, there isn't a whole lot of reason to verify an unauthenticated host name, because any crook who would accept your request addressed to someone else would also forge a certificate claiming he is that someone else.

It is common to disable authentication and identification to work around a technical problem wherein you're unable to confirm the server's identity, but don't really think there's any risk that the server is an impostor. A common technical problem that requires you to use no_ssl_verifypeer in order to do any RPCs is that you don't have certificate authority (CA) certificates properly installed on your client system.

The details of the verification of server identity, including what files you need on your system to make it work, are all handled by the Curl library. See Curl documentation for details. The Curl transport has a bunch of transport parameters to control the details of the SSL verification.

Timeout

You can limit the amount of time the RPC can take with the timeout transport parameter.

You can further restrict the amount of time Xmlrpc-c can take just to establish the TCP connection with the connect_timeout transport parameter. One significant source of delay in making the TCP connection is looking up the server name in DNS.

With some Curl libraries, Xmlrpc-c is not capable of controlling the time allowed for a DNS lookup. With such a Curl library, an RPC waits until the underlying networking system gives up, which might be never. In particular, if the Curl library is configured to use the ARES name-lookup library, Xmlrpc-c can control the DNS lookup timeout, whereas with any other Curl library, it cannot.

Older Curl
If your Curl library is older than 7.10, you cannot limit the time an RPC takes. If you specify either timeout or connect_timeout, the RPC fails immediately.
Using SIGALRM Instead

Note that as a matter of good design, it is often better to use an alarm signal (SIGALRM) to interrupt a transport operation instead of the timeout parameter. An alarm signal sets a master timeout on a whole sequence of operations without all the layers having to be aware of it. I.e. you don't have to have "timeout" arguments on all your functions and have them all watch the clock.

If you are writing code that doesn't own the whole environment (e.g. a general purpose library), you can't generally set up an alarm, but in that case you probably don't want to establish an arbitrary time limit either, because the appropriate limit depends upon context. It's often better to have the top level code, which does own the whole environment, set up an alarm and just have your code be interruptible by signals (as libxmlrpc_client is).

On the other hand, a hardcoded timeout value is by far the easiest to code solution to the annoying problem of unresponsive servers.

Older Xmlrpc-c

In Xmlrpc-c before 1.41 (March 2015), the situation is much more complicated when DNS lookup takes a long time. What happens depends upon your timeout value, whether your Curl library is the kind that uses the ARES name-lookup library, and whether your program is multi-threaded.

The simplest case is where the Curl library uses ARES. In that case, the DNS lookup is simply limited to 5 minutes, in addition to the overall limit for the RPC requested by your timeout parameter.

For the non-ARES case, it depends upon whether you specify timeout:

If you specify timeout, then regardless of the value you specify, there will no be no limit on the time the DNS lookup can take, other than limits imposed by the underlying networking system.

If you do not specify timeout, the DNS lookup is limited to 5 minutes, and if your program is multithreaded, it will probably crash if the lookup needs more time than that.

It is hard to explain why it is this way, but it has to do with the fact that there are only two ways Curl can limit the duration of the DNS lookup: using a SIGALRM signal under the covers and using the ARES library. Using a signal under the covers has a number of architectural problems, one of which is that it typically doesn't work in a multithreaded program. Xmlrpc-c sometimes uses Curl's CURLOPT_NOSIGNAL option to keep Curl from using the signal. (Current Xmlrpc-c always uses CURLOPT_NOSIGNAL).

User Agent

In HTTP, the "user agent" is the program through which the user makes an HTTP request. For classic web browsing, it is the web browser program, such as Internet Explorer 6.0. An optional header field in the HTTP request, the User-Agent header field, identifies the user agent. It does this with a value that looks like this: "prog1/1.0 prog2/2.1 prog3/0.9beta". It is a sequence of name/version pairs, identifying components at successively lower layers.

The Xmlrpc-c Curl transport by default inludes a User-Agent header field in its requests, whose value is a name/version pair for Xmlrpc-c followed by a name/version pair for Curl, separated by spaces.

You can cause it to add additional text to identify your code, using the user_agent transport parameter. The transport prepends the value of user_agent, verbatim, separating it from the rest with a space. When user_agent is null, which is the default, the transport prepends nothing.

If the dont_advertise Curl transport option is true, the transport does not include information about Xmlrpc-c and Curl.

If dont_advertise is true and user_agent is null, the transport does not include a User-Agent header field in the request at all.

The behavior is different before Xmlrpc-c 1.21 (December 2009). In these older releases, the default is not to include a User-Agent header field. If you specify non-null user_agent, the transport includes the full User-Agent header field, including the Xmlrpc-c and Curl information and user_agent value. There is no dont_advertise option.

Referer

In HTTP, the "referer" of a document is the document that referred the user to the subject document, normally by containing a hyperlink to it. A request for a document (an XML-RPC call is considered a request for a document) may optionally say what page referred the client to it, identifying the page by the URL the browser used to load it.

Some web servers, for security reasons relating to cross-site scripting, require every request to it to identify a referer, so if your target XML-RPC server is also one of these web servers, your RPCs will be rejected unless you use this option.

The Xmlrpc-c Curl transport by default does not identify a referer.

If you specify this option, the transport specifies the string you specify as the referer in all its HTTP requests. You are responsible for ensuring the referer value is meaningful to the server.

This option was new in Xmlrpc-c 1.29 (December 2011).

Expect/Continue

Because of the Expect/Continue bug that some XML-RPC servers have, the Curl transport never sends an HTTP Expect header field (and, more importantly, never expects a continue response).

BUT: Before Xmlrpc-c 1.19 (June 2009), with a recent libcurl library and a large XML-RPC call, the transport does send the Expect and waits up to 3 seconds for the continue response. If the server doesn't send the response, the transport goes ahead and the only problem with the transaction is that it takes 3 seconds longer than it should. (I'm being coy about what version of libcurl and what size of body because I don't know).

Curl Transport-specific Parameters


struct xmlrpc_curl_xportparms {
    const char * network_interface;
    xmlrpc_bool  no_ssl_verifypeer;
    xmlrpc_bool  no_ssl_verifyhost;
    const char * user_agent
    const char * ssl_cert;
    const char * sslcerttype;
    const char * sslcertpasswd;
    const char * sslkey;
    const char * sslkeytype;
    const char * sslkeypasswd;
    const char * sslengine;
    xmlrpc_bool  sslengine_default;
    enum xmlrpc_sslversion sslversion;
    const char * cainfo;
    const char * capath;
    const char * randomfile;
    const char * egdsocket;
    const char * ssl_cipher_list;
    unsigned int timeout;
    const char * proxy;
    unsigned int proxy_port;
    enum xmlrpc_httpproxytype proxy_type;
    unsigned int proxy_auth;
    const char * proxy_userpwd;
    xmlrpc_bool  gssapi_delegation;
    const char * referer;
    unsigned int connect_timeout;
    xmlrpc_bool  tcp_keepalive;
    unsigned int tcp_keepidle_sec;
    unsigned int tcp_keepintvl sec;
};

Example:


struct xmlrpc_clientparms clientParms;
struct xmlrpc_curl_xportparms curlParms;
xmlrpc_client * clientP;

curlParms.network_interface = "eth1";
curlParms.no_ssl_verifypeer = TRUE;
curlParms.no_ssl_verifyhost = TRUE;
curlParms.user_agent        = "myprog/1.0";

clientParms.transport          = "curl";
clientParms.transportparmsP    = &curlParms;
clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent);

xmlrpc_client_create(&env, 0, "myprog", "1", 
                     &clientParms, XMLRPC_CPSIZE(transportparm_size),
                     &clientP);


For parmSize, use the XMLRPC_CXPSIZE macro. This macro gives the size of the xmlrpc_curl_xportparms structure up through the member you name. Name the last member in the structure that you set. You must set every member up through that one, but for every member after it, libxmlrpc_client will assume a default.

The reason it's important to use XMLRPC_CXPSIZE instead of just setting all the members and using sizeof(struct xmlrpc_curl_xportparms) is forward compatibility. Future versions of libxmlrpc_client might add new members, and you want the client program you write today and compile with that future library to work. XMLRPC_CXPSIZE also allows you to not worry about members at the end of the structure that you don't care about.

It isn't so easy to omit parameters you don't care about from the middle of the structure, but the members are defined so that a value encoded as bits of zero is the same as the member not being present, so you can just set the whole structure to zero bits and then set the members you care about to something else.

network_interface

network_interface is the Curl library's "interface" option. See documentation of the Curl API for details (the best documentation of Curl API options, by the way, is the manual for the 'curl' program that comes with it and has command line options to control all the API options).

But essentially, it chooses the local network interface through which to send RPCs to the server. It causes the Curl library to perform a "bind" operation on the socket it uses for the communication. It can be the name of a network interface (e.g. on Linux, "eth1") or an IP address of the interface or a host name that resolves to the IP address of the interface. Unfortunately, you can't explicitly state which form you're specifying, so there's some ambiguity.

Examples: eth1, 64.171.19.66, giraffe.giraffe-data.com.

The value is an ASCIIZ string. You can free its storage as soon as the call that creates the transport returns.

If this parameter is NULL or not present, the Curl transport uses whatever is the default of the Curl library.

This parameter was new in Xmlrpc-c 1.02.

no_ssl_verify_peer, no_ssl_verifyhost

no_ssl_verifyhost and no_ssl_verifypeer are meaningful only for an SSL connection (a connection to a server with a "https:" URL). They control how secure the connection is.

no_ssl_verifyhost = true means the Curl transport doesn't care if the server is the one it was trying to reach. False means the Curl transport will refuse to connect to the server if the server's certificate does not match its URL.

no_ssl_verifypeer = true means the Curl transport just believes that the server is who its certificate says it is. False means the Curl transport will refuse to connect to the server if it can't prove that it is who it says it is. True also means the transport doesn't care if you have proper CA certificates installed on the client system.

Note that all combinations of these two options are meaningful (though not necessarily useful).

The default value is false.

These options interact poorly with ssl_cipher_list. See the explanation of that option for details.

These parameters were new in Xmlrpc-c 1.03 (June 2005). Before that, the Curl transport did whatever is the default for the Curl library involved. In reasonably modern Curl, that's the same as the parameters being false, but in really old Curl, the default was different.

ssl_cipher_list

This option lists the ciphers (or cipher suites) the transport is allowed to use in its SSL connection. Ciphers are means of encrypting the data and authenticating the parties, and there are lots from which to choose. The syntax of this value, which is a text string, is the same as that for the cipherlist command line argument for the openssl program's cipher subprogram.

For example, you could specify ADH-AES256-SHA to indicate you will accept only a connection that does anonymous Diffie-Hellman key exchange, 256-bit AES encryption, and a message authentication code that uses a SHA1 hash. Or you could specify AECDH:ADH to indicate you will accept any connection that does anonymous Elliptical Curve Diffie Hellman or anonymous Diffie Hellman key exchange.

A null string requests the default.

The default is whatever the Curl library's default is, which happens to be whatever the OpenSSL library's default is, which can vary but is typically almost all ciphers that are not anonymous.

Curl does not properly hide the OpenSSL library, though, which means the Xmlrpc-c Curl transport does not either, so you must be cognizant of the effect this option value has on the Curl transport's ssl_no_verify_host and ssl_no_verify_peer options. If you set either of those to false, the Curl transport will direct the Curl library to inspect the certificate sent by the server. But if your ssl_cipher_list lists ciphers that don't require a certificate, the server may choose such a cipher for the connection and therefor not send a certificate. Curl is oblivious to the fact that it allowed the server to choose a cipher that doesn't involve a certificate, so will try to get the server's certificate from OpenSSL in order to inspect it. That attempt will fail, since the server did not send a certificate, and that makes Curl declare the connection failed. You'll see "SSL: couldn't get peer certificate!" in the error messages from Xmlrpc-c telling why it could not connect to the server.

So if you specify false for either ssl_no_verify_host or ssl_no_verify_peer, be sure to set ssl_cipher_list to something that insists on a certificate-using cipher, such as !aNULL. (This is usually the default).

user_agent

user_agent is a string that identifies the user of the Xmlrpc-c client object, for purposes of a User-Agent HTTP header field.

If the parameter is NULL or not present, the HTTP request has no User-Agent header field. See the discussion of user agents above.

The user_agent parameter was new in Xmlrpc-c 1.03 (June 2005). Before that, the Curl transport never used a User-Agent header field.

SSL Details

These are options that control the details of SSL verification of the server identity. You can usually do without any of these; the defaults are the Curl library defaults, which are typically all you need.

All of these options correlate directly to Curl options of the same name, so rather than document them here, we refer you to the Curl documentation.

All of these parameters were new in Xmlrpc-c 1.04 (November 2005).

timeout

See Timeout.

timeout is the maximum time in milliseconds that the transport will allow for network communications. If it takes longer than that, the transaction fails without waiting any more. This is a precise limit on certain aspects of network communication, but as for the time that the server takes between receiving a call and sending the response, it could take up to 3 seconds longer than this for the RPC execution to fail.

Exactly which parts of network communication (name server lookup, ARP, TCP connection, data transfer, etc.) are subject to this time limit varies from one system to another and I can't say any more specifically what is covered. But note that it is not specific to Xmlrpc-c in any way; the function of this parameter is determined by the Curl library and the platform below it. There is evidence that on Windows, much of the network activity is not subject to the timeout; for example, your client will try for 90 seconds to make contact with a nonexistent server regardless of timeout.

The actual timeout is what you requested rounded up to the next second. In future Xmlrpc-c, it may have better resolution.

If timeout is 0 or not present, there is no timeout -- The transport waits as long as it takes.

The Curl library must be Release 7.10 or newer. If it is not and you specify timeout, creation of the transport fails.

This parameter was new in Xmlrpc-c 1.13 (December 2007).

connect_timeout

See Timeout.

This parameter is very similar to timeout, but limits only the time Curl will take to make the initial connection, including querying DNS servers.

If connect_timeout is 0 or not present, the time allowed for connection is controlled by timeout. If both connect_timeout and timeout are present and nonzero, Xmlrpc-c enforces both time restrictions.

This parameter was new in Xmlrpc-c 1.41 (March 2015).

In Xmlrpc-c before 1.41 (March 2015), the situation is much more complicated when DNS lookup takes a long time. See Timeouts

TCP keepalive

Keepalive is a feature of TCP in which a client conducts a transaction with the server periodically just to determine if the server is still there. It does this only during intervals when there is no regular traffic on a connection. The Linux kernel implements the feature and the Curl library provides controls for it, which are also available in the Xmlrpc-c API.

The two parameters of the TCP keepalive feature are how long the connection is idle (i.e. there are no messages on it) before the client starts trying to determine if the server is still there and the frequency with which the client sends messages to the server in that attempt. When the client has sent a certain number of messages without getting a response, it considers the connection broken.

You request TCP keepalive on a transport's connections with the tcp_keepalive option. You control the idle period with tcp_keepidle_sec and the message period with tcp_keepintvl_sec, both of which are numbers of seconds. Zero means accept the OS' default.

These parameters were new in Xmlrpc-c 1.57 (December 2019). You also need Curl library Version 7.25 or newer. If you set tcp_keepalive true or tcp_keepidle_sec or tcp_keepintvl_sec to nonzero and your Curl library is older than that, client XML transport creation fails.

HTTP Proxy Control

Curl knows how to conduct an HTTP transaction through an HTTP proxy. You control that with the following parameters:

proxy_type has one of these values:

proxy_auth is an OR of these values (it represents a set of authentication methods -- Curl will try them all).

These correlate directly to Curl options of the same name, except as explained below, so rather than document them fully here, we refer you to the Curl documentation.

The proxy_auth default is different between Xmlrpc-c and Curl. In Curl, the default is HTTP basic authentication. With Xmlrpc-c, the default is no authentication. That fits better with the Xmlrpc-c client XML transport concept of a zero value being the default to make it easier to construct the transport parameter.

The special value 0 for proxy_port means the Curl default (whatever that is), and there is no way with this parameter to specify port 0. Note that in any case (as with the Curl equivalent options), proxy_port is meaningless when you include the port in the value of proxy.

All of these parameters were new in Xmlrpc-c 1.23 (June 2010).

GSSAPI

When you use GSS authentication, you can give your GSS credentials to the server so that it can act on your behalf. Of course, you do this only when you trust the server. To do that, set gssapi_delegation to true.

The default is no delegation.

You can't always choose to delegate or not to delegate; it depends upon the capabilities of the Curl library. Older Curl libraries always delegate; you can't stop it. gssapi_delegation has no effect if you're running with one of these Curl libraries. Curl version 7.21.6 (April 2011) and earlier is like this. Curl version 7.22.0 and later gives you the choice, and gssapi_delegation works normally. In between Curl version 7.21.7 (June 2011) never delegates. If you specify gssapi_delegation and you have Curl 7.21.7, Xmlrpc-c client creation fails with a message telling you need a different Curl.

And that's all assuming you actually compiled libxmlrpc_client with a current Curl. If you compile with a Curl older than 7.22.0 and run with a Curl newer that 7.21.6, and specify gssapi_delegation, client creation will always fail.

gssapi_delegation was new in Xmlrpc-c 1.28 (September 2011). Before that, you get whatever your Curl library does by default (which means no delegation with Curl 7.21.7 and later, and delegation with older Curl).

Concurrency

The Curl transport before Xmlrpc-c 1.04 serializes RPCs. This means your program as a whole will never start an RPC before the previous one has completed.

In 1.04 and later, the Curl transport performs RPCs concurrently (via the Curl library's "multi" facility). But this applies only to the asynchronous interface. If you use multiple operating system threads to request multiple RPCs simultaneously via the synchronous interface, the Xmlrpc-c Curl transport will serialize them -- A Curl transport will never start an RPC before the previous one has completed.

But starting in 1.05, you can have multiple Curl transports in a program (one per client). So your program as a whole might have multiple concurrent RPCs going, via the synchronous interface, by using multiple operating system threads, each using its own client).

The Curl transport does one DNS server host name lookup at a time, and the timeouts in xmlrpc_client_event_loop_finish_asynch_timeout() and xmlrpc_client_event_loop_finish_timeout() are ineffective against long-running name lookups. This is due to a weakness in the Curl library (at least as late as version 7.16.1 -- January 2007).

Before Xmlrpc-c 1.04 (November 2005), the timeouts don't work at all; the two finish_async functions are identical.

Interference With Other Curl Uses

Your use of libxmlrpc_client with the Curl transport may interfere with other uses in your program of the Curl library.

This is primarily because of a weakness in current Curl (at least as late as Curl 7.12.2, November 2005) in which a call to its global constant teardown routine, curl_global_cleanup() tears down the global constants for every user of the library in the program. xmlrpc_client_teardown_global_const() makes such a call to curl_global_cleanup().

Therefore, you must make sure that you do not call xmlrpc_client_teardown_global_const() while anything else in your program is using the Curl library.

As of December 2005, a request is being considered by the maintainer of the Curl library to change it such that it remembers how many modules are referring to its constants and tears them down only when the last referrer goes away. That would eliminate this interference.

Also note the thread-unsafety of xmlrpc_client_setup_global_const() and xmlrpc_client_teardown_global_const() (explained in their respective sections), which is particularly relevant when you have other threads using the Curl library.

xmlrpc_client_init2() and xmlrpc_client_cleanup() do the same things as xmlrpc_client_setup_global_const() and xmlrpc_client_teardown_global_const() in this respect, so the same caution applies to them.

Debugging

Set the XMLRPC_TRACE_CURL environment variable to 1 to cause the Curl library to print details of its processing to Standard Error. This environment variable causes the Xmlrpc-c Curl transport to set the misnamed CURLOPT_VERBOSE option for every Curl session.

This feature was new in Xmlrpc-c 1.21 (December 2009).

Libwww

This transport uses the classic Libwww (W3C Libwww to be exact) libraries to transport the XML over HTTP. This is less convenient, less documented, and less functional than Curl, so I don't know any reason to use it unless you have easier access to the Libwww libraries than to the Curl libraries, and therefore built a special version of libxmlrpc_client that doesn't have Curl capability.

One way in particular that using Curl is easier is that when something prevents Curl from communicating with the server, it reports a fairly specific indication of why (which libxmlrpc_client then forwards to you. In contrast, Libwww often just tells you "something failed" and you have to guess or trace the Libwww code.

Cookies

The cookie mechanism is a feature of HTTP that adds stateful connections to this protocol that is fundamentally designed not to have them. There are sociological reasons for this corruption. The mechanism works as follows: In a response, an HTTP server includes an arbitrary key/value pair which is meaningful only to the server. The client essentially includes that pair in all future requests to that server.

The transport ignores cookies the server sends and sends no cookies to the server.

In Xmlrpc-c 1.00 (October 2004) through 1.02 (April 2005), the transport recognizes a single cookie named "auth". This is a cookie shared by all servers; the transport ignores the domain that the server tries to associate with the cookie. The cookie lives for the life of the transport (i.e. the life of the client). The function was created for some special purpose lost in history, during Xmlrpc-c's pre-1.00 dark age. The Xmlrpc-c maintainer eventually studied it enough to determine that it was more detrimental than beneficial.

Concurrency

The asynchronous facility, in at least one experiment Bryan did, was rather disappointing used with the libwww transport. The no-wait call had no visible effect, and the RPC finishing function did each RPC serially (waits for one to complete before starting the next one). Because libwww is undocumented and its code too complex to read easily, Bryan did not determine if there are circumstances under which it behaves better.

Wininet

This is available on Windows only.

This transport has a 30 second timeout on XML-RPC calls. If the server does not respond within that period, the call fails.

Wininet Transport-specific Parameters


struct xmlrpc_wininet_xportparms {
    int allowInvalidSSLCerts;
};

Example:


struct xmlrpc_clientparms clientParms;
struct xmlrpc_wininet_xportparms wininetParms;
xmlrpc_client * clientP;

wininetParms.allowInvalidSSLCerts = TRUE;

clientParms.transport          = "wininet";
clientParms.transportparmsP    = &wininetParms;
clientParms.transportparm_size = XMLRPC_WXPSIZE(allowInvalidSSLCerts);

xmlrpc_client_create(&env, 0, "myprog", "1", 
                     &clientParms, XMLRPC_CPSIZE(transportparm_size),
                     &clientP);


Miscellaneous

xmlrpc_client_get_default_transport()

This function tells you the name of the default XML transport.

It's always "libwww", but in future versions of libxmlrpc_client or other libraries that are variations on libxmlrpc_client, it might be something else.

The default XML transport is the one that gets used if you don't specify a particular XML transport when you create the Xmlrpc-c client object.


const char * 
xmlrpc_client_get_default_transport(xmlrpc_env * envP);

Concurrency

libxmlrpc_client thread safety

The library is mostly thread-safe. See Thread Safety for general information.

But the objects provided by the library are thread-safe only if you use the Curl XML transport. With Curl, you can call a function that operates on a client object or RPC object while another thread is also calling a function that operates on the same client or RPC. But with the other transports, you can't.

If you are using a thread-unsafe XML transport, you can still do RPCs from multiple threads; you just have to give each thread its own client so that a particular client is accessed by only one thread. And you cannot have multiple threads creating clients. You must have one thread create all the client objects and give them to the threads that will use them. And you can't share RPCs between threads either (though it probably would never occur to you to do that anyway).

You can get multiple RPCs running concurrently without using multiple operating system threads. libxmlrpc_client's Asynchronous RPC facility allows a single operating system thread, using a single client, to have multiple RPCs in progress at once. (It is, in fact, a threading facility).

OpenSSL Thread Safety

If you use the Curl XML transport and you use an https URL for an XML-RPC server and your Curl library is built to use OpenSSL to perform its SSL and TLS functions, then libxmlrpc_client is not thread-safe unless your program makes certain calls to the OpenSSL library to configure threading facilities. This is explained in the OpenSSL manual.

This is because of a flaw in the design of the OpenSSL library. The library is designed to be used within an arbitrary threading system (i.e. not necessarily POSIX threads), so its user must supply thread synchronization (locking) facilities. But the only means OpenSSL has for supplying those facilities is for the user to call library functions, and those functions record the information as process-global state. That means the Curl library cannot supply the synchronization facilities - there might be parties in the program other than the Curl objects using the OpenSSL library so any call to these functions would interfere with them. For the same reason, Xmlrpc-c libraries cannot make the necessary OpenSSL calls either.

Only the highest level code in the program can properly call the necessary OpenSSL functions to make the OpenSSL library, and by extension the libraries that use OpenSSL, thread-safe.

Though OpenSSL is designed to be used within an arbitrary threading system, the only system with which you as an Xmlrpc-c user will use it is POSIX threads on Unix, or Windows threads on Windows, because Xmlrpc-c itself doesn't work with any other threading system. Therefore, there is no election the Xmlrpc-c user has to make. A program that uses Xmlrpc-c must call the OpenSSL functions to set up OpenSSL to work with POSIX or Windows threads.

To help you with this on Unix, Xmlrpc-c provides the library libxmlrpc_openssl. All you have to do is call function xmlrpc_openssl_thread_setup from that library befor it calls any Xmlrpc-c library functions, while the program is still one thread. It should also call xmlrpc_openssl_thread_cleanup after the last Xmlrpc-c call, when the program is again one thread.

Use the package name xmlrpc_openssl with pkg-config to get the necessary build information to use libxmlrpc_openssl. Or add the feature name openssl to a xmlrpc-c-config call.

These functions are not specific to XML-RPC or to the rest of Xmlrpc-c; they are merely conveniences since users of Xmlrpc-c often need to make these calls (the functions might just as well have been distibuted as part of OpenSSL).

xmlrpc_openssl_thread_safety was new in Xmlrpc-c 1.45 (March 2016). If you are using Xmlrpc-c older than that, you must code the necessary OpenSSL calls in your own code. (One easy way to do this is to get the source code out of a current version of Xmlrpc-c).

Practical Concurrency

Just how concurrent two RPCs are depends upon the Xmlrpc-c XML transport you use and on the servers and network. Multiple operating system threads and the asynchronous interface make it possible for multiple RPCs to be in progress from your program's point of view (you can start one without waiting for the previous one to complete). But the XML transport may very well serialize the RPCs -- keep them in a list and perform them one at a time. And if the XML transport doesn't do that, the server may do it -- complete one RPC before accepting the HTTP connection for the next.

See the Curl transport documentation for details on the concurrency characteristics of that transport type.

Dealing With Broken Servers

Client Is Immune To Broken Server

It is a basic design goal of Xmlrpc-c that no matter how broken a server is, a client of the server will tolerate it. The server cannot make the client crash. When a client operation fails because of a bad server, failure information tells you the server is the problem.

However, it is of course possible for your own client code that uses Xmlrpc-c libraries to be written poorly enough that a bad server would bring it down. For example, if an RPC produces a negative integer result and Xmlrpc-c client facilities dutifully pass that up to your code, but your code is expecting a positive integer, you could have big trouble.

Client Hang Because Of Bad Server

One common form of server defect that is really hard for a client to tolerate is where the server simply doesn't respond. The server could be hung in an infinite loop or deadlock and to the client it looks just like an RPC that takes a long time to execute.

A very common form of this is where the server is actually non-existent. You call xmlrpc_client_call2() to execute an RPC on this hypothetical server and while you'd expect the call to fail immediately with a fault string telling you there's no such server, it just hangs. In truth, it normally doesn't really hang. It does in fact fail eventually, but it can take as long as several minutes to do it.

What this is is a weakness of TCP. The Xmlrpc-c code is simply trying to make an HTTP connection to the server and the operating system TCP connection-making service takes all this time to admit that there's no server there. It's probably doing a lot of retrying in the meantime, thinking the lack of response may be due to packets getting lost in the network. IP generally doesn't have a means to indicate to a sender that a packet can't be delivered because the "to" address doesn't exist; rather, the network merely discards the packet and the sender is supposed to give up if it doesn't get a response after a while. In some situations, it is actually possible for the network to tell the client that the server isn't there, but for security reasons, it doesn't (information is power; the network wants to keep the hackers guessing).

Now, this several minutes of waiting is probably annoying because you know that if there hasn't been a response to a couple of tries within a few seconds, there isn't going to be one. But it's a function that's buried deep in your operating system, so there is no way Xmlrpc-c can spare you the wait.

What you can do about this is the general solution to all of the client hangs caused by bad servers: Use an alarm signal. On Unix, a process can ask the OS to send an alarm signal to it at a specified time in the future (see the standard C library function alarm()). You can make that signal interrupt your call to libxmlrpc_client

When you interrupt a libxmlrpc_client function that was waiting on a server, it breaks whatever TCP connection there was, so if the server really is functioning, it will find out you aborted the RPC and you don't have to worry about orphan results coming in later.

By the way, another apparent solution that you may think of is to use the asynchronous RPC interface. That doesn't help with the nonexistent server problem, though, because an RPC start function doesn't return until it has at least delivered the call to the server.

A slightly more sophisticated approach to the venerable but primitive alarm signal is to use OS threads. Create a thread to do the RPC. Then wait, with timeout, for the thread to complete. If the wait times out, send a signal to the RPC thread you created to interrupt it.

Finally, with the Curl XML transport, you can simply specify a timeout value for all XML transportation functions, using the timeout transport-specific client parameter. This is easy, but not as flexible as using a signal.

Bogus Content Length

Per the XML-RPC spec, an HTTP response which is an XML-RPC response must have a Content-Length header field and its value must be the size of the XML-RPC response message. libxmlrpc_client facilities are thus allowed to fail the XML-RPC call if the response does not have the required Content-Length header field or does not contain the specified amount of contents. With the Curl or Libwww XML transport, libxmlrpc_client does not require a Content-Length header field, but with the Wininet XML transport, it does.

An HTTP chunked response does not have a Content-Length header field, so cannot be an XML-RPC response. But there are some pseudo-XML-RPC servers that send an HTTP chunked response without a Content-Length header field or a pseudo-HTTP chunked response with a Content-Length header field with a liberal (greater than actual) value. libxmlrpc_client facilities accept and process naturally an HTTP chunked response, with the Curl and Libwww XML transports. With the Wininet transport, libxmlrpc_client accepts a pseudo-HTTP response which is chunked and also has a Content-Length header field. That header field must specify the actual content length (i.e. the number of characters after decoding according to the Transfer-Encoding header field).

Even though an HTTP server never sends a chunked response with a Content-Length header field, a true HTTP client ignores the Content-Length header field in a chunked response -- i.e. it tolerates the server error. An XML-RPC client need not. This is a rare case where an XML-RPC client is excused from also being an HTTP client. As stated above, the Wininet XML transport takes advantage of that excuse, so a libxmlrpc_client-based XML-RPC client with a Wininet transport is not an HTTP client.

Python Expect/Continue Bug (3 Second Delay)

In HTTP 1.1 (but not 1.0), the client can send the header field "Expect: 100-continue", which tells the server that the client isn't going to send the body of the HTTP request until the server tells it to by sending a "continue" response. The server is obligated to send that response.

The point of this is that the client doesn't want to spend a lot of resources generating and sending a body that the server is just going to reject based on the header (for example because the body, according to the header, is too big for the server to handle).

But some servers that are otherwise HTTP 1.1 don't hold up their end of the deal: they just ignore the Expect header field and leave the client hanging. (This is presumably just a matter of implementation mistake or expedience).

Users have reported this using Python 2.7 and 3.2 (note that we are not saying the bug is in Python). No users of Python servers have reported not seeing this bug.

Xmlrpc-c's Curl client XML transport works around this problem as described here, but note that in some cases the bug still causes RPCs to take 3 seconds.

We don't know whether other XML transports are susceptible to this bug.

Debugging

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

Error Environment Variable

First of all, if a library call fails, look at the error message! Almost every Xmlrpc-c library call returns a detailed English description of why it failed. See Error Environment Variable.

And you must check each call for failure. If you fail to notice that a library call has failed, you will be quite confused about the results of subsequent calls. Note that example code often doesn't check each call for failure, because it clutters code which is trying to demonstrate something else. So if the example doesn't work for you, insert error checking in the obvious places.

Standard Error

The trace facilities described here write messages to the Standard Error file descriptor via the Standard Error stream of the standard C library (stderr). So make sure you have one. Many server processes don't (they explicitly close the one that the system setup code provides).

XMLRPC_TRACE_XML environment variable

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

To be exact, what the transport prints is the bytes that are presented as XML -- it doesn't know or care at this point whether it is valid XML. The transport writes to Standard Error each byte that it recognizes as a printable ASCII character (except backslash). For every other byte, it writes a traditional backslash escape sequence (for example, newline is "\n"). For backslash ("\"), the transport prints a double backslash ("\\") so you know it is not part of an escape sequence for a nonprintable character.

The transport prints each line of the XML on a separate line (i.e. after it prints "\n" for a newline in the XML, it prints an actual newline).

Note that this same environment variable does the same thing for Xmlrpc-c servers.

XMLRPC_TRACE_CURL environment variable

If you are using the Curl XML transport, XMLRPC_TRACE_CURL can be useful.

Library Version

xmlrpc_client_version() tells you what version (release, level) of libxmlrpc_client is linked to your program.

Example:


    unsigned int major, minor, point;
    xmlrpc_client_version(&major, &minor, &point);

    printf("libxmlrpc_client version %u.%u.%u\n", major, minor, point);

Prototype:


    void
    xmlrpc_client_version(unsigned int * majorP,
                          unsigned int * minorP,
                          unsigned int * pointP);

This is declared in <xmlrpc-c/client.h> as follows:

The numbers returned are those you see in regular text references to the version of XML-RPC For C/++ from which that libxmlrpc_client comes, E.g. "1.16.31."

This function was new in Xmlrpc-c 1.25 (December 2010). Before that, you can use the following external integer variables (Still available, but now deprecated, because it's not possible to export integer variables directly in a Windows DLL).


    extern unsigned int const xmlrpc_client_version_major;
    extern unsigned int const xmlrpc_client_version_minor;
    extern unsigned int const xmlrpc_client_version_point;

These symbols were new in Xmlrpc-c 1.13 (December 2007).