# SER Management Interface

## Design Goals

- Implemented as a module.

- API independent of transport protocols.

- Reuse transports available in SER.

- The possibility to encrypt all communication.

- The possibility to authenticate clients.

- Easy integration with existing languages and implementations.

- Easy and straightforward implementation of management functions in SER modules.

## Overview of Operation

The RPC (Remote Procedure Call) interface of SER is based on the XML-RPC de-facto standard. XML-RPC protocol encodes the name of the method to be called along with its parameter in an XML document which is then conveyed using HTTP (Hyper Text Transfer Protocol) to the server. The server will extract the name of the function to be called along with its parameters from the XML document, execute the function, and encode any data returned by the function into another XML document which is then returned to the client in the body of a 200 OK reply to the HTTP request.

XML-RPC is similar to more popular SOAP (Simple Object Access Protocol), which is an XML-based messaging framework used in Web Services developed within the World Wide Web Consortium. Both protocols are using HTTP as the transport protocol for XML documents, but XML-RPC is much simpler and easier to implement than SOAP.

Here is an example of single XML-RPC function call to determine current time:

    POST /RPC2 HTTP/1.0
    User-Agent: Radio UserLand/7.1b7 (WinNT)
    Host: time.xmlrpc.com
    Content-Type: text/xml
    Content-length: 131

    <?xml version="1.0"?>
    <methodCall>
    <methodName>currentTime.getCurrentTime</methodName>
    <params>
    </params>
    </methodCall>

And the response returned by the server:

    HTTP/1.1 200 OK
    Connection: close
    Content-Length: 183
    Content-Type: text/xml
    Date: Wed, 03 Oct 2001 15:53:38 GMT
    Server: UserLand Frontier/7.0.1-WinNT

    <?xml version="1.0"?>
    <methodResponse>
    <params>
    <param>
    <value><dateTime.iso8601>20011003T08:53:38</dateTime.iso8601>
    </value>
    </param>
    </params>
    </methodResponse>

XML-RPC specification spells HTTP as the official transport protocol for XML-RPC documents. SER does not directly support HTTP, it is a SIP server so SIP is the only protocol supported by SER. Because we would like to reuse all transport protocols available in SER, such as TCP and TLS, it would be natural to use modified version of XML-RPC which would run on top of SIP instead of HTTP. XML-RPC documents would be then encoded in the bodies of SIP requests and replies would be sent by the server in the bodies of SIP replies. This way we could reuse all transport protocols (including UDP) and message parsers available in SER.

Although this approach seems to be the logical choice, there is one big drawback. No existing XML-RPC implementations support SIP as the transport protocol, and there are many existing implementations available for vast majority of existing languages. See the XML-RPC implementation page for more details. Extending existing implementations with SIP support would not be easy.

Because extending available XML-RPC implementation would be too expensive, we could also do it the other way around, keep existing XML-RPC implementations and extend SER to support HTTP. Extending SER with HTTP support is easier than it might seem at a first glance, due to the similarity between SIP requests and HTTP requests.

SER already supports TCP, so existing HTTP implementations can send HTTP requests to it. HTTP requests are missing certain mandatory SIP header fields, such as Via, From, and CSeq. The contents of the header fields is mainly used for transaction matching. A SIP server could perform two basic operations when processing an HTTP request:

- Terminate the request, execute the function and send a reply back.

- Forward the request to another SIP server.

Nothing special is needed on the SIP server terminating the request, except that it has to know where it should send the reply. Parsing of HTTP header field bodies would fail because we do not have parsers for them in SER, but that does not matter anyway because all the information is encoded in the body of the request. HTTP requests contain no Via header fields. Via header fields are used by SIP implementations to determine the destination (IP, transport protocol, and port number) for replies. When processing HTTP requests the SIP server needs to create a fake Via header field based on the source IP address and port number of the TCP connection. The SIP server will use this information when sending a reply back.

Forwarding of HTTP requests by SIP proxies is a little bit more complicated and there are several limitations. First of all, we can only use stateless forwarding, no transactional forwarding, because HTTP requests do not contain all the header fields needed for transaction matching. Any attempt to call t_relay on an HTTP requests would fail. HTTP requests always use TCP and thus we could use stateless forwarding on the SIP server, provided that the request will be also forwarded over TCP. Stateless forwarding does not require the mandatory header fields (which are missing here) and it would work. In addition to that, the SIP server would also append fake Via header field to the request and change the contents of the Request-URI. The Request-URI of HTTP requests sent by XML-RPC implementations typically contain something like "/RPC2" and the first SIP server processing the request should rewrite the value with a valid SIP URI.

shows a scenario which involves two SIP servers, one performs HTTP request "normalization" and forwarding, and the other terminates the request, executes corresponding function, and generates a reply.

Example RPC Scenario

Example RPC Scenario

Step 1. An HTTP user agent sends an ordinary HTTP request to a SIP server. The user agent can either establish a connection directly to port 5060 or the SIP server can be configured to listen on port 80. The request contains standard HTTP headers and an XML-RPC document in the body:

    POST / HTTP/1.0.
    Host: localhost:5060
    User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)
    Content-Type: text/xml
    Content-Length: 111

    <?xml version='1.0'?>
    <methodCall>
    <methodName>usrloc.statistics</methodName>
    <params>
    </params>
    </methodCall>

This particular request calls method "statistics" from from usrloc module of SER. The method has no parameters.

The outbound SIP server receives the HTTP request and performs a set of actions called "SIP-normalization". This includes creation of fake Via header field based on the source IP and port of the TCP connection, looking up of the target SIP server that should terminate and process the request, and rewriting of the Request-URI with the SIP URI of the target SIP server. Modified HTTP request will be then forwarded statelessly to the target SIP server.

    POST sip:proxy01.sip-server.net HTTP/1.0
    Via: SIP/2.0/TCP 127.0.0.1:3571
    Host: localhost:5060
    User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)
    Content-Type: text/xml
    Content-Length: 111

    <?xml version='1.0'?>
    <methodCall>
    <methodName>usrloc.statistics</methodName>
    <params>
    </params>
    </methodCall>

Step 2. "normalized" HTTP request is statelessly forwarded to the target SIP server over TCP.

Step 3. The target SIP server receives the HTTP request and executes function called `dispatch_rpc` from xmlrpc SER module. This function will parse the XML-RPC document in the body of the request and lookup the function to be called among all RPC functions exported by the SER core and modules. Function `dispatch_rpc` will be called from the configuration file just like any other function:

    if (method == "POST" || method == "GET") {
        dispatch_rpc();
        break;
    };

This particular configuration snippet executes the function whenever SER receives GET or POST requests. These two method names indicate HTTP.

Step 4. Function `dispatch_rpc` scans through the list of all exported RPC functions searching for `statistics` function of usrloc module. describes in detail how modules export RPC functions.

Step 5. As the RPC function from usrloc module is running and gathering statistics, it calls functions of RPC interface to prepare the result for the caller.

Step 6. Once the RPC function finishes, xmlrpc module will build the XML-RPC document from the data received from usrloc module and generate a reply which will be sent to the caller.

Steps 7. and 8. HTTP reply is sent back to the caller and the remote procedure call finishes.

    HTTP/1.0 200 OK
    Via: SIP/2.0/TCP 127.0.0.1:3571
    Server: Sip EXpress router (0.10.99-janakj_experimental (i386/linux))
    Content-Length: 651
    Warning: 392 127.0.0.1:5060 "Noisy feedback tells:  pid=9975 req_src_ip=127.0.0
    1 req_src_port=3571 in_uri=/ out_uri=sip:proxy01.sip-server.net via_cnt==1"

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
    <params>
    <param><value><array><data>
    <value><struct>
    <member><name>domain</name>
    <value><string>aliases</string></value></member>
    <member><name>users</name>
    <value><i4>0</i4></value></member>
    <member><name>expired</name>
    <value><i4>0</i4></value></member>
    </struct></value>
    <value><struct>
    <member><name>domain</name>
    <value><string>location</string></value></member>
    <member><name>users</name>
    <value><i4>0</i4></value></member>
    <member><name>expired</name>
    <value><i4>0</i4></value></member>
    </struct></value>
    </data></array></value></param>
    </params>
    </methodResponse>

The scenario described on involves two SIP servers. This is just to demonstrate that in setups containing more SIP servers it is possible to forward HTTP requests from one SIP server to another and use standard SIP routing mechanisms to decide which SIP server should process the request. There is no need to have multiple SIP servers in simple setups, because one SIP server can both add fake Via header field and process the RPC at the same time. Modified configuration file snipped could then look like this:

    if (method == "POST" || method == "GET") {
        dispatch_rpc(); # Process the request
        break;
    };

## Module API

Each SER module can export RPC functions just like it can export parameters and functions to be called from the script. Whenever SER receives an RPC request, it will search through the list of exported RPC functions and the function with matching name will be executed. A couple of essential RPC functions is also embedded into the SER core.

This section gives a detailed overview of the whole RPC API. describes the prototype and conventions used in RPC functions. gives a detailed overview of available data types that can be used in function parameters and return value. describes functions of the RPC API that can be used to retrieve parameters of the function, and finally describes functions of the API that can be used to build the result value that will be sent in the reply to the caller.

The whole RPC API is described in header file `sip_router/rpc.h`. This file defines the set of functions that must be implemented by RPC transport modules, as described in , prototypes of RPC functions and structures used for the communication between RPC transport modules and ordinary modules exporting RPC functions.

### RPC Functions

RPC functions are standard C functions with the following prototype:

    typedef void (*rpc_function_t)(rpc_t* rpc);

RPC functions take one parameter, this parameter is a pointer to rpc_t structure and is called RPC context. The context contains references to all API functions available to the RPC function as well as all data necessary to create the response. RPC functions do not return any value, instead the return value is created using functions from the context. The motivation for this decision is the fact that RPC functions should always return a response and even the API functions called from RPC functions should have the possibility to indicate an error (and should not rely on RPC functions doing so).

The RPC transport module creates initial reply before calling any RPC function. The default value of the reply is 200 OK with empty body. This is the value that will be returned to the caller and the function has to explicitly modify it if it wishes to either indicate a failure or add some data to the reply. The reply is sent automatically immediately after the RPC function finishes or the function can send the reply explicitly during its runtime. Even RPC API functions modify the reply upon a failure.

Each RPC function has associated an array of documentation strings. The purpose of the documentation strings is to give a short overview of the function, accepted parameters, and format of the reply. By convention the name of the documentation string array is same as the name of the function with "_doc" suffix.

Each module containing RPC functions has to export all the functions to SER core in order to make them visible to RPC transport modules. For this purpose, the `module_exports` structure of SER module API contains a new attribute called `rpc_methods`:

    struct module_exports {
        char* name;                 /* null terminated module name */
        cmd_export_t* cmds;         /* null terminated array of the exported commands */
        rpc_export_t* rpc_methods;  /* null terminated array of exported rpc methods */
        param_export_t* params;     /* null terminated array of the exported module parameters */

        init_function init_f;         /* Initialization function */
        response_function response_f; /* function used for responses */
        destroy_function destroy_f;   /* function called upon SER shutdown */
        onbreak_function onbreak_f;
        child_init_function init_child_f;  /* function called by all processes after the fork */
    };

    typedef struct rpc_export {
        const char* name;        /* Name of the RPC function (null terminated) */
        rpc_function_t function; /* Pointer to the function */
        const char** doc_str;    /* Documentation strings, method signature and description */
        unsigned int flags;      /* Various flags, reserved for future use */
    } rpc_export_t;

`rpc_methods` is pointer to an array of rpc_export_t structures. The last element of the array is a bumper containing zeroes in all attributes of the structure. The following program listing shows exported RPC functions of usrloc module:

    struct module_exports exports = {
        "usrloc",
        cmds,      /* Exported functions */
        ul_rpc,    /* RPC methods */
        params,    /* Export parameters */
        mod_init,  /* Module initialization function */
        0,         /* Response function */
        destroy,   /* Destroy function */
        0,         /* OnCancel function */
        child_init /* Child initialization function */ };

    rpc_export_t ul_rpc[] = {
        {"usrloc.statistics",      rpc_stats,           rpc_stats_doc,          0},
        {"usrloc.delete_aor",      rpc_delete_aor,      rpc_delete_aor_doc,     0},
        {"usrloc.delete_contact",  rpc_delete_contact,  rpc_delete_contact_doc, 0},
        {"usrloc.dump",            rpc_dump,            rpc_dump_doc,           0},
        {"usrloc.flush",           rpc_flush,           rpc_flush_doc,          0},
        {"usrloc.add_contact",     rpc_add_contact,     rpc_add_contact_doc,    0},
        {"usrloc.show_contacts",   rpc_show_contacts,   rpc_show_contacts_doc,  0},
        {0, 0, 0, 0}
    };

By convention the name of every exported function consists of two parts delimited by a dot. The first part is the name of the module or SER subsystem this function belongs to. The second part is the name of the function.

Attribute `flags` of `rpc_export` structure is reserved for future use and is currently unused.

### Data Types

The RPC API defines several basic and 1 compound data type that can be used in communication with the caller of RPC functions. The RPC API uses formating strings to describe data types. Each data type is described by exactly one character in the formating string. For example, if an RPC function calls function `add` of the RPC API and it passes two parameters to it, the first one of type string and the second one of type integer, the function parameters will look like:

    add("sd", string_param, int_param);

Character "s" in the formating string tells to the function that the 2nd parameter should be interpreted as string, character "d" in the formating string tells to the function that the 3rd parameter should be interpreted as signed integer.

#### Integer

Integer type represents a signed 32-bit integer. Corresponding character in the formating string is "d". This parameter can be stored in C-style variable with type `int`.

#### Float

Float type represents a signed floating point number. Corresponding character in the formating string is "f". Data of this type can be stored in C-style variables of type `double`.

#### String

String type represents a string of characters. The string may contain zeroes. This data type is represented by two characters in the formatting string, either "s" or "S". "s" indicates to the conversion function that the result should be stored in a variable of type `char*` and it should be zero terminated. "S" indicates to the conversion function that the result will be stored in a variable of type `str` which contains both the pointer to the beginning of the string and its length.

#### Structure

Structure is the only compound data type currently defined in the API. A structure is a collection of attributes. Each attribute is identified using name (string) and each attribute can be one of the basic data types, that is integer, float, or string. Nesting of structures is not allowed (in other words, structure attributes cannot be of type struct again). Corresponding character in the formatting string is "{".

#### Data Type Overview

Name | Formating String Char | C-Style Variable
Integer | d | int
Float | f | double
String | s | char*
String | S | str

### Getting Parameters

Each RPC function call can contain parameters. Parameters have no name, their meaning is determined by their position in the parameter set.

You can pass all parameters to a function within a structure if you want to make them position independent. Then each parameter can be retrieved by its name regardless of its position.

There are two functions in the RPC API that can be used to obtain function call parameters: `scan` and `struct_scan`.

#### `scan`

Function `scan` can be used to retrieve parameters from the parameter set. The function accepts variable number of parameters. The first parameter is the formatting string that determines the type of the parameters to be retrieved. Each parameter is represented by exactly one character in the string. The variable part of parameters must contain as many pointers to C variables as there are formatting characters in the formatting string.

The function will crash if you fail to provide enough parameters.

The function indicates success by returning 0. When a failure occurs (incorrect parameter type or no more parameters in the parameter set) then the function will return -1 and it will also automatically change the reply that will be sent to the caller to indicate that a failure has occurred on the server. Prototype of the function is:

    int scan(char* fmt, ...)

It is possible to either call the function once to scan all the parameters:

    rpc->scan("sdf", &string_val, &int_val, &double_val);

Or you can call the same function several times and it will continue where it left off previously:

    rpc->scan("s", &string_val);
    rpc->scan("d", &int_val);
    rpc->scan("f", &double_val);

#### `struct_scan`

Function `struct_scan` can be used to retrieve named attributes from a parameter of type structure. When retrieving a structure parameter from the parameter set:

    rpc->scan("{", &handle);

The corresponding variable (named `handle`in the example above) will contain the index of the structure parameter within the parameter set, but the index cannot be used to retrieve the contents of the structure. To retrieve the contents of the structure you can use function `struct_scan`. The function gets the handle as the first parameter:

    rpc->struct_scan(handle, "sd", "str_attr", &str_val, "int_attr", &int_val);

The second parameter is the formatting string followed by pairs of parameters. First parameter in each pair is the name of the attribute to retrieve (string) and the second parameter in each pair is the pointer to the variable to store the value of the parameter. The function returns 0 on success and -1 on an error (just like `scan` function). The function also indicates an error if a requested attribute is missing in the structure.

#### Retrieving Parameters

    static void rpc_delete_contact(rpc_t* rpc)
    {
        str aor, contact;
        char* table;
        void *handle;
        int   expires;
        double q;

        if (rpc->scan("sS{", &table, &aor, &handle) < 0) {
            /* Reply is set automatically by scan upon failure,
             * no need to do anything here
             */
            return;
        }

        if (rpc->struct_scan(handle, "Sdf", &contact, &expires, &q) < 0) {
            /* Reply is set automatically by struct_scan upon failure,
             * no need to do anything here
             */
            return;
        }

        /* Process retrieved parameters here */
    }

### Building Reply

The RPC API contains several functions that can be used to modify and/or send a reply. The functions use formatting strings and parameter lists just like functions described in .

Each RPC function call must return a reply. The reply can be either a failure reply or success reply. Failure replies contain only the status code and reason phrase. Success replies can have arbitrary amount of data attached to them. Status codes 3xx, 4xx, 5xx, and 6xx indicate failures. Status code 2xx indicates success.

The default reply is 200 OK with no data attached to it. This is what will be returned by the RPC transport module if you do not call any of the reply-related functions described in this section.

#### Sending default reply

    static void rpc_dummy(rpc_t* rpc)
    {
      /* 200 OK with no data will be returned */
    }

#### fault

You can use `fault` function to indicate that an error has occurred on the server to the caller. The function accepts two parameters. The first parameter is the status code and the second parameter is the reason phrase.

    static void rpc_my_function(rpc_t* rpc)
    {
        rpc->fault(600, "Not Yet Implemented");
    }

If your function first creates some result using `add`, or `printf` functions then all the data will be lost once you call `fault` function. Failure replies must not contain any data:

    static void rpc_my_function(rpc_t* rpc)
    {
        rpc->add("s", "result1");
        rpc->add("d", variable);

        /* Reply created by previous functions will be
         * deleted and a failure reply 600 Not Yet Implemented
         * will be created instead
         */
        rpc->fault(600, "Not Yet Implemented");

        /* You can also add data here, but that will have no
         * effect
         */
        rpc->add("s", "result2");
    }

Similarly you can also call `add` or `printf` functions after calling `fault`, in this case they will have no effect:

    static void rpc_my_function(rpc_t* rpc)
    {
        rpc->fault(600, "Not Yet Implemented");

        /* You can also add data here, but that will have no
         * effect and only 600 Not Yet Implemented will be returned
         */
        rpc->add("s", "result2");
    }

#### send

RPC functions can use function `send` to explicitly send the reply. Each RPC function call generates exactly one reply. No reply will be sent after the function finishes if it already sent the reply using `send` function explicitly. This function is especially useful if the RPC function needs to perform some (potentially destructive) actions after the reply has been sent.

##### Kill the server

    static void core_kill(rpc_t* rpc)
    {
        int sig_no;

        if (rpc->scan("d", &sig_no) < 0) return;
        rpc->send();     /* First send a reply */
        kill(0, sig_no); /* Then kill the server */
    }

#### add

Function `add` can be used to add arbitrary data to the result set. Its parameters and use are analogical to `scan` function described in . The first parameter of the function is the formatting string that determines the types of additional parameters:

    static void rpc_func(rpc_t* rpc)
    {
        str str_result;
        int int_result;
        void *handle;
        double float_result;

        if (rpc->add("Sdf{", &str_result, int_result, float_result, &handle) < 0) return;
    }

Naturally you can call this function several times, adding only one piece of data at a time. The function returns 0 on success and -1 on an error. In case of an error the reply is set automatically with corresponding error code and reason phrase.

The last character in the formatting string of the function above indicates that the last data to be added will be a structure. This deserves some clarification. In this case, the function will create an empty structure and the handle to the newly created structure will be stored in `handle` variable (hence the last parameter is pointer to an integer). In this particular example parameters `str_result`, `int_result`, and `float_result` will be used for reading while parameter `handle` will be used for writing by the function.

You can set the attributes of the newly created structure using `struct_add` function described in .

#### printf

`printf` is a convenience function. The function adds data of type string to the result set. The first parameter of the function is again a formatting string, but this time it is `printf`-like formatting string:

    if (rpc->printf("Unable to delete %d entries from table %s", num_entries, table_name) < 0) return;

The return value of the function is the same as of `add` function.

#### struct_add

Function `struct_add` can be used to add attributes to a structure (created previously by `add` function). The first parameter of the function is handle obtained through `add` function, the second parameters is formatting string that determines the types of attributes to be added. There must be two parameters per each character in the formatting string, the first one is the name of the attribute, the second parameter is the value of the attribute. If a parameter with such a name already exist in the structure then it will be overwritten with the new value.

    static void rpc_func(rpc_t* rpc)
    {
        void *handle;

            /* Create empty structure and obtain its handle */
        if (rpc->add("{", &handle) < 0) return;
            /* Fill-in the structure */
        if (rpc->struct_add(handle, "sd", "attr1", str_val, "attr2", int_val) < 0) return;
    }

The function returns -1 on an error (and sets the status code and reason phrase of the reply accordingly) and 0 on success.

### Real World Example

The following example illustrates the use of most of the functions from the API together:

#### Real World Example RPC Function

    static void rpc_register(rpc_t* rpc)
    {
        char* domain;
        str aor;
        contact_t contact, new_contact;
        void *handle;

            /* Extract the domain, address of record from the request */
        if (rpc->scan("sS{", &domain, &aor, &handle) < 0) return;
            /* Extract the structure describing the contact to be processed */
        if (rpc->struct_scan(handle, "Sdf", &contact.c, &contact.expires, &contact.q) < 0) return;

            /* Process the contact, new_contact will contain updated value after processing */
        if (process_contact(domain, &aor, &new_contact, &contact) < 0) {
               /* Processing failed, indicate the failure to the caller */
            rpc->fault(500, "Error While Processing Contact");
            return;
        }

            /* Return the domain and the address of record */
        rpc->add("sS{", &domain, &aor, &handle) < 0) return;
            /* And also add the new values for contact, q, and expires parameters */
        rpc->struct_add(handle, "Sdf", &new_contact.c, &new_contact.expires, &new_contact.q);
    }

## XML-RPC Transport

The XML-RPC transport is implemented in xmlrpc SER module. The purpose of the functions of the module is to convert XML-RPC document carried in the body of HTTP requests into data returned by the RPC interface and back. The module also contains functions necessary to "normalize" HTTP requests. The module uses xmlrpc-c library to perform XML-RPC related functions.

The module always returns 200 OK HTTP reply, it will never return any other HTTP reply. Failures are expressed in XML-RPC documents in the body of the reply. There is basic method introspection support in the module. Currently the module can list all functions exported by the server and for each function it can return the documentation string describing the function.

### Requests

Requests processed by the module are standard XML-RPC requests encoded in bodies of HTTP requests.

    POST / HTTP/1.0
    Host: localhost:5060
    User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)
    Content-Type: text/xml
    Content-Length: 112

    <?xml version='1.0'?>
    <methodCall>
    <methodName>system.listMethods</methodName>
    <params>
    </params>
    </methodCall>

The name of the method to be called in this example is "listMethods". This is one of the introspection methods. SER will call `dispatch_rpc` function of xmlrpc module to handle the request. The function will parse the XML-RPC document, lookup `listMethods` function in the list of all export RPC functions, prepare the context for the function and execute it.

### Replies

The module will always generate 200 OK. Other response codes and classes are reserved for SER. The status code of the XML-RPC reply, response code, and additional data will be encoded in the body of the reply. Failure replies do not contain any data, just the response code and reason phrase:

    HTTP/1.0 200 OK
    Via: SIP/2.0/TCP 127.0.0.1:2464
    Server: Sip EXpress router (0.10.99-janakj_experimental (i386/linux))
    Content-Length: 301

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>

    <fault>
    <value><struct>
    <member><name>faultCode</name>
    <value><i4>501</i4></value></member>
    <member><name>faultString</name>
    <value><string>Method Not Implemented</string></value></member>
    </struct></value>
    </fault>

    </methodResponse>

This particular reply indicates that there is no such RPC method available on the server.

Success replies always contain at least one return value. In our case the simplest success replies contain single boolean with value 1:

    HTTP/1.0 200 OK
    Via: SIP/2.0/TCP 127.0.0.1:4626
    Server: Sip EXpress router (0.10.99-janakj_experimental (i386/linux))
    Content-Length: 150

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
    <params>
    <param><value><boolean>1</boolean></value></param>
    </params>
    </methodResponse>

This is exactly how the reply looks like when an RPC function does not add any data to the reply set.

If an RPC function adds just a single item (it calls `add` once with just one character in the formatting string) then the data will be converted to XML-RPC representation according to the rules described in and the reply will contain just the single value:

    HTTP/1.0 200 OK
    Via: SIP/2.0/TCP 127.0.0.1:3793
    Server: Sip EXpress router (0.10.99-janakj_experimental (i386/linux))
    Content-Length: 216

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
    <params>
    <param><value><string>Server: Sip EXpress router (0.10.99-janakj_experimental (i386/linux))</string></value></param>
    </params>
    </methodResponse>

If an RPC function adds more than one data items to the result set then the module will return an array containing all the data items:

    HTTP/1.0 200 OK
    Via: SIP/2.0/TCP 127.0.0.1:2932
    Server: Sip EXpress router (0.10.99-janakj_experimental (i386/linux))
    Content-Length: 276

    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
    <params>
    <param><value><array><data>
    <value><string>./ser</string></value>
    <value><string>-f</string></value>
    <value><string>ser.cfg</string></value>
    </data></array></value></param>
    </params>
    </methodResponse>

This is probably the most common scenario.

### Type Conversion

The data types of the RPC API are converted to the data types of XML-RPC and vice versa. shows for each RPC API data type corresponding XML-RPC data type.

#### Data Type Conversion

RPC API | XML-RPC | RPC Example | XML-RPC Example
Integer | <i4></i4> | rpc->add("d", 42) | <i4>42</i4>
Float | <double></double> | rpc->add("f", -12.214) | <double>-12.214</double>
String | <string></string> | rpc->add("s","Don't panic") | <string>Don't panic</string>

Struct

<struct></struct>

rpc->struct_add(handle,"sd","param1",42,"param2",-12.214)

    <struct>
      <member>
        <name>param1</name>
        <value>
          <i4>42</i4>
        </value>
      </member>
      <member>
        <name>param2</name>
        <value>
          <double>-12.214</i4>
        </value>
      </member>
    </struct>

### Limitations

SER xmlrpc modules does not implement all data types allowed in XML-RPC. As well it does not implement arrays and nested structures. This simplification is a feature, not bug. In our case the XML-RPC interface will be used mainly for management purposes and we do not need all the bells and whistles of XML-RPC. Parsing and interpreting nested structures is complex and we try to avoid it.

## Client Examples

To be done.

## Implementing New Transports

To be done.