Writing Clients

Quick Start

Lets start with simple example that will demonstrate basic XML-RPC client. This client example designed to be compatible with server example.

#include <iostream>
#include <libiqxmlrpc/libiqxmlrpc.h>
#include <libiqxmlrpc/client.h>
#include <libiqxmlrpc/http_client.h>
 
int main()
{
  using namespace iqxmlrpc;
 
  // 1: Create a client object
  Client<Http_client_connection> client(iqnet::Inet_addr(3344));
 
  // 2: Fill method parameters
  Param_list pl;
  pl.push_back(Struct());
  pl[0].insert("var1", 1);
  pl[0].insert("var2", "value");
 
  // 3: Call a remote method
  Response r = client.execute("echo", pl);
 
  // 4: Process method output
  assert(r.value()["var1"].get_int() == 1);
  assert(r.value()["var2"].get_string() == "value");
}

Comments with figures state different phases of a process of writing client code with Libiqxmlrpc. Here they are:

  • Create and configure client object
  • Make remote invocations
    • Construct a set of parameters
    • Call a remote method
    • Treat method output

A following sections say more about those phases.

Setting Up a Client

An abstract base class iqxmlrpc::Client_base provides interface for client configuration and execution of remote methods. In summary, this interface allows one to:

  • Configure client connections:
    • Specify Proxy Settings
    • Specify Connection Timeout
    • Specify Keep-alive Flag
    • Manage Client Authorization
  • Method Execution

Create Client

However, the first thing a developer need, is to create an instance of Client_base. Concrete sub-classes of Client_base take care of a transport used to communicate remote service. There is template class iqxmlrpc::Client<TRANSPORT> that should be used for instantiating Client_base children. User must specify appropriate TRANSPORT parameter for his situation, depending on transport required (HTTP or HTTPS).

Possible options are:

  • iqxmlrpc::Http_client_connection {idx: iqxmlrpc::Http_client_connection }
  • iqxmlrpc::Https_client_connection {idx: iqxmlrpc::Https_client_connection }

In a common case client creation code will look like a following:

iqxmlrpc::Client<TRANSPORT> client( iqnet::Inet_addr(host, port), "/RPC2" );

An /RPC2 string is an URI that can be configured by a owner of a remote service.

One can fearlessly substitute TRANSPORT template argument with Http_client_connection in an example above. However, enabling HTTPS needs some additional magic from a developer. I.e. one must initialize SSL subsystem before creating any SSL connection:

#include <libiqxmlrpc/libiqxmlrpc.h>
#include <libiqxmlrpc/https_client.h>
 
using namespace iqxmlrpc;
 
Client_base*
create_https_client(iqnet::Inet_addr addr, const std::string& uri)
{
    // Initialize SSL subsystem
    iqnet::ssl::ctx = iqnet::ssl::Ctx::client_only();
 
    return new Client<Https_client_connection>(addr, uri);
}

Configure Client Connections

Specify Proxy Settings

Client_base::set_proxy
This method allows one set an HTTP proxy host for all client's outgoing connections.

    Client<Http_client_connection> client(Inet_addr("host.com", 8080));
    client.set_proxy(Inet_addr("my.proxy.com", 80));

Specify Connection Timeout

Client_base::set_timeout
This method sets connection timeout in seconds. A negative value means no timeout. Library throws iqxmlrpc::Client_timeout exception during processing remote call in case when time is out.

void timed_call(Client_base& client, int timeout)
{
    client.set_timeout(timeout);
 
    try {
        Response r = client.execute("foo", Value());
        // work with r ...
 
    } catch(const Client_timeout&) {
        // handle connection timeout
    }
}

Specify Keep-alive Flag

Client_base::set_keep_alive
This method manages HTTP keep-alive facility on a client connection. If one enables this flag and remote server supports this facility then all subsequent method execution request will go through the same open connection.

void banch_of_requests(Client_base& client)
{
    // Ask server to not close connection after response
    client.set_keep_alive(true);
 
    client.execute("foo", Value());
 
    // Hope, the same connection.
    // Depending on server implementation.
    client.execute("bar", Value());
}

Manage Client Authorization

Client_base::set_authinfo
This method sets user/password pair to pass through HTTP authentication mechanism.

    client.set_authinfo("admin", "12345");

Note that though library allows use set_authinfo with plain a HTTP connections, one should not rely on it because in this case your credential information may become available to traffic sniffers.

Method Execution

Calling Remote Method

XML-RPC standard allows transfer a number of input parameters to a remote procedure. Actually, quite often XML-RPC APIs take in account only first of them, and require transferring parameters in a key-value form. Just because this method is much more convenient and reflects to a dynamic nature of XML-RPC it-self.

Library provides comfortable way for both approaches. The iqxmlrpc::Client provides two overloaded interface for calling remote methods:

namespace iqxmlrpc {
 
typedef std::vector<Value> Param_list;
 
class Client {
public:
    // ...
    Response execute( const std::string& method, const Param_list& );
    Response execute( const std::string& method, const Value& );
};
 
} // namespace iqxmlrpc

Both methods accept name of calling method as a first parameter. A former allows transfer an array of input parameters (as the standard suggests).

A second one puts specified value into first element of array of parameters and just calls a first one.

Deal with Method Responses

An XML-RPC response may be of two types: method output, and failure report. Objects of iqxmlrpc::Response class serve a destination for result of a remote call and may hold either type of response.

A Response::value method gives user access to a received result. And Response::is_fault allows user check if received XML-RPC packet contained failure notice. If user try access response value with value method and an object is actually containing an XML-RPC failure notice then exception that contain all information about failure would be thrown. This behavior gives user ability write a code in optimistic manner.

Response::fault_code and Response::fault_string methods gives an alternative way to retrieve information about failure.

Please note that Response objects may contain only XML-RPC failures. Failures form a network layer or some HTTP issues are always exceptions.

std::string get_name_of_object(Client_base& client, int object_id)
{
    try {
        Response r = client("get_object_name", object_id);
        return r.value().get_string();
 
    } catch (const Client_timeout&) {
        std::cerr << "Timeout" << std::endl;
        exit(1);
 
    } catch (const Exception& e) {
        std::cerr
            << "XML-RPC error "
            << e.code() << ": " << e.what()
            << std::endl;
        exit(2);
    }
}
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License