Writing Server - adedov/libiqxmlrpc GitHub Wiki

Defining Methods

A common way implementing server methods is inheriting iqxmlrpc::Method class and customize it's execute virtual function. The execute method accepts two parameters: an STL vector that contains input arguments packaged into Value objects and a reference to Value object that will be sent to a caller as a result of operation.

Following example illustrates trivial method returning sum of two integers.

    #include <libiqxmlrpc/method.h>
    
    class Sum: public iqxmlrpc::Method {
    	void
    	execute(const Param_list& params, Value& result)
    	{
    		result = params[0].get_int() + params[1].get_int();
    	}
    };

One also can turn a free function into a server method. A function should have following signature:

    void FN(
      iqxmlrpc::Method*		method,
      const iqxmlrpc::Param_list&	args,
      iqxmlrpc::Value&		result);

The method argment contains a method object created by a server for specific request.

Reporting Errors

The code in previous example does not contain checks about operation preconditions, e.g. "array of input parameters must contain exactly two elements of integer type" in this case. Library, surely will report a error in case of violation of some of these conditions, but obviously it's response won't be as much informative as it could be. Let's correct that omission.

    void
    Sum::execute(const Param_list& params, Value& result)
    {
    	if (params.size() != 2 ||
    	    !params[0].is_int() ||
    	    !params[1].is_int())
    		throw Fault(123, "Array of two integers required.");
    
    	result = params[0].get_int() + params[1].get_int();
    }

Note: Throwing iqxmlrpc::Fault exception make server generate standard fault response with error code and message specified by user. Actually, server catch up all exceptions from users' code, but throwing Fault is the only way to get control over error code and message that will be placed in response.

Configure Server

TBD

Defining Execution Strategy

Library suggests two ways of request execution:

  • Synchronous execution
  • Execution by a pool of threads

With the first way all requests are executed in the same thread that accepts connections and parses incoming packages. The second way makes main thread do deal with networking and XML parsing, while the actual execution of server methods is being performed within a pool of threads.

User may configure server with particular execution strategy depending on his personal demands.

The constructors of server classes accept a pointer to an object of abstract type iqxmlrpc::Executor_factory_base. A server creates executor object for each request it receives using executor factory. Concrete type of executor factory defines execution strategy used by a server.

An example below demonstrates how one can build appropriate execution strategy depending on specified number of threads parameter.

    #include <libiqxmlrpc/libiqxmlrpc.h>
    #include <libiqxmlrpc/executor.h>
    
    iqxmlrpc::Executor_factory_base*
    build_executor_factory(unsigned num_threads)
    {
    	if (num_threads > 1)
    		return new iqxmlrpc::Pool_executor_factory(num_threads);
    
    	return new iqxmlrpc::Serial_executor_factory;
    }

Note: Please note that server object does not grab ownership over execution factory. So one ought to take care of its destruction. The best decision here is to place executor factory into a smart pointer.

Defining Underlying Transport

There is iqxmlrpc::Server class that can be used for server creation. It has two offsprings - Http_server and Https_server. They ease creation of server objects with specific underlying transport.

The following example demonstrates creation of both HTTP and HTTPS servers depending on users' needs.

    #include <boost/optional.hpp>
    #include <libiqxmlrpc/libiqxmlrpc.h>
    #include <libiqxmlrpc/http_server.h>
    #include <libiqxmlrpc/https_server.h>
    
    struct SSLConfig {
    	std::string cert_file;
    	std::string privkey_file;
    };
    
    typedef boost::optional<SSLConfig> OptSSLConfig;
    
    iqxmlrpc::Server*
    create_server(int port, Executor_factory_base* ef, const OptSSLConfig& ssl)
    {
    	if (ssl) {
    		using iqnet::ssl::ctx;
    		using iqnet::ssl::Ctx;
    
    		ctx = Ctx::server_only(ssl->cert_file, ssl->privkey_file);
    		return new Https_server(port, ef);
    	}
    
    	return new iqxmlrpc::Http_server(port, ef);
    }

Server Sample

    #include <iostream>
    #include <libiqxmlrpc/libiqxmlrpc.h>
    #include <libiqxmlrpc/http_server.h>
    
    // Simple method that just returns back first input parameter
    class Echo: public iqxmlrpc::Method {
    public:
      void execute(const iqxmlrpc::Param_list& params, iqxmlrpc::Value& retval)
      {
        if (params.empty())
          retval = 0;
        else
          retval = params[0];
      }
    };
    
    int main()
    {
      int port = 3344;
    
      iqxmlrpc::Serial_executor_factory ef;
      iqxmlrpc::Http_server server(port, &ef);
    
      iqxmlrpc::register_method<Echo>(server, "echo");
    
      // optional settings
      server.log_errors( &std::cerr );
      server.enable_introspection();
      server.set_max_request_sz(1024*1024);
    
      // start server
      server.work();
    }
⚠️ **GitHub.com Fallback** ⚠️