libxmlrpc_abyss

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

The libxmlrpc_abyss library provides functions to create a whole HTTP server. While such a server is useful as an XML-RPC server, this library in particular has nothing to do with XML-RPC. You could use it to create an ordinary web server, like a small Apache.

The functions of libxmlrpc_abyss are collectively known as Abyss. See History Of Abyss for an explanation of why Abyss has a name.

Abyss can handle an SSL or TLS connection (https).

If you use it for an XML-RPC server, you'll want to use libxmlrpc_server_abyss as well.

This chapter is incomplete. It covers only some Abyss functions. For complete information, the Xmlrpc-c example code, interface header files, and source code are the best you can do.

Chapter Contents

History of Abyss

The history of the Abyss HTTP server is worth knowing:

Originally, Abyss was a separately developed and distributed open source web server. Its point was to be a lightweight web server for those who didn't need all of Apache's power. The Xmlrpc-c project exploited Abyss for its HTTP server needs, and included a copy of the code in the Xmlrpc-c package for convenience. That was 2001.

Subsequently, work on the Abyss open source project terminated and its creator began to develop Abyss as proprietary software. It is still (2005) available as such.

Therefore, the Xmlrpc-c project continued developing a fork of Abyss strictly for purposes of Xmlrpc-c. We should probably change the name of the Xmlrpc-c fork eventually, but for now it is still just called Abyss.

The Abyss open source project apparently never produced any documentation, and that's why there is very little here today.

Interface Header File

The <xmlrpc-c/abyss.h> header file declares the interface to libxmlrpc_abyss.

You'll have to figure out where on your system this file lives and how to make your compiler look there for it. Or use xmlrpc-c-config.

Linking The Library

The classic Unix name for the file containing the libxmlrpc_abyss library is libxmlrpc_abyss.a or libxmlrpc_abyss.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_abyss, 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.

Persistent Connections

Persistent connections is a feature of HTTP (new in HTTP 1.1) in which multiple HTTP requests can happen over the same TCP connection to save time and resources.

In classic HTTP operation, a client makes a TCP connection to the server, they conduct one transaction, then they close the connection. If a client needs to do 10 transactions in a row (e.g. a user at a web browser clicks on 10 successive links), it involves 10 separate TCP connections.

With persistent connections, after the client and server complete a transaction, they keep the TCP connection open for a while. If during that time the client decides to do another request on the same server, it just uses the existing connection.

For a connection to be kept open in this way, the client must request it in the previous request, and the server must agree. And either side may close the idle connection at any time.

Abyss does persistent connections.

If you use Abyss in the way that you call Abyss to accept and process a single connection from a channel switch (e.g. by calling ServerRunOnce), Abyss ignores any client request for a persistent connection; it closes the connection and returns to your program as soon as it has processed one HTTP request.

Abyss limits the number of requests it will allow on a single TCP connection. By default, the maximum is 30, but you can control it with ServerSetKeepaliveMaxConn and the keepalive configuration file option. Likewise, Abyss limits the period that it will keep a TCP connection open anticipating a subsequent request. The default is 15 seconds, but you can control it with ServerSetKeepaliveTimeout.

HTTPS

To create a server that does HTTPS, you use an OpenSSL channel switch.

Example:


  TChanSwitch * chanSwitchP;
  const char * error;
  SSL_CTX * sslCtxP;
  TServer abyssServer;

  sslCtxP = ...

  ChanSwitchOpenSslCreateIpV4Port(8443, sslCtxP, &chanSwitchP, &error);

  ServerCreateSwitch(&abyssServer, chanSwitchP, &error);
    

The line sslCtxP = ... glosses over a tremendous amount of work, but it is work that is not specific to Xmlrpc-c, so it beyond the scope of this manual. To provide a very simple interface for the very complex world of secure sockets, Abyss exposes to you the OpenSSL library. You are responsible for knowing how OpenSSL works and supplying an SSL_CTX object that includes the copious SSL and TLS options for the server. SSL_CTX is a type defined by OpenSSL, and you use OpenSSL functions such as SSL_CTX_new and SSL_CTX_use_certificate_chain_file to create it. An example of the options you choose via SSL_CTX is the certificate the server will use to prove its identity to clients.

There is a complete working example of a simple HTTPS server in the examples directory of the Xmlrpc-c source tree, named ssl_server.c. There is another more complex one that actually provides secure communication named ssl_secure_server.c.

You cannot have the same Abyss server take both HTTP and HTTPS requests, but you can have two Abyss servers in the same program, one for HTTP and one for HTTPS.

The OpenSSL type of abyss channel was introduced in Xmlrpc-c 1.46 (March 2016). In Xmlrpc-c before that, there is no way for Abyss to server HTTPS. But you can combine Stunnel with Abyss to create an HTTPS server in which Abyss sees HTTP.

Channels

A channel is what Abyss uses to communicate with a client. A channel is just a pipe that carries bytes back and forth, in one stream in each direction. It's a lot like a TCP connection. The way Abyss uses channels, the stream is HTTP.

Abyss' channel concept is abstract enough that a channel could be implemented any number of ways using any of various network protocols, or OS inter-process communication facilities, or carrier pigeons. But in practice, the only kinds of channel Abyss implements are built upon a TCP connection so Abyss can implement true HTTP or on an SSL or TLS connection so Abyss can implement HTTPS.

A channel switch is an entity that sets up a channel between two parties that want to talk to each other. Somehow one party identifies the other and says he wants a channel and the switch negotiates and creates a connection between them. In the case of an Abyss HTTP server, with TCP-based channels, the channel switch is what listens for TCP connection requests from clients, negotiates the TCP connection, and ultimately creates a channel out of the TCP connection and passes that channel to an Abyss HTTP request handler.

In some ways of using Abyss, e.g. ServerCreate()/ServerRun(), you never see the channels and switches. Abyss creates them under the covers as necessary. In other applications, e.g. ServerCreateSwitch()/ServerRun), your code creates a channel switch and Abyss code takes and processes the channels it creates as clients connect via the switch. In still others, e.g. ServerCreateNoAccept()/ServerRunChannel(), you create a channel (probably via some means more rudimentary than an Abyss channel switch) and pass that channel to Abyss so Abyss can handle HTTP requests that arrive over that channel.

Associated with every channel is a record of "channel information." The meaning of the contents of that record are entirely dependent on the type of channel. It is metadata about the channel; the information is not necessary for transmitting the byte streams, but may be useful in dealing with the content of the streams. Typically, the information tells who is on the other far side of the channel. You typically hand the channel information to someone along with the channel.

Channels and channel switches were new in Xmlrpc-c 1.07 (October 2006). There are two earlier generations of client/server communication control. In Xmlrpc-c 1.06, there are sockets, and before that you use native Operating system entities such as POSIX socket file descriptors. The channel abstraction allows us to expand Abyss to fit more applications. For example, it made it easy to add SSL capability.

The macro HAVE_CHANSWITCH tells a C program that the libxmlrpc_abyss is new enough to have channels and channel switches.

TChannel

TChannel is the C type for an Abyss channel. There are various types of TChannels; they all look the same from the outside but are implemented differently. For example, three kinds exist today: one uses Unix stream sockets and TCP to send and receive its byte streams. Another uses an OpenSSL connection object. A third uses Winsock.

You create a TChannel by calling a constructor function for the particular type of TChannel you want. The constructor function has arguments that make sense for that particular type. Any TChannel you create you must destroy after Abyss no longer needs it. You do that with the type-independent function ChannelDestroy().

Note that in some modes of operation, you don't have to create a TChannel at all; Abyss services do that all under the covers.

ChannelUnixCreateFd()

Example:


  TChannel channelP;
  struct abyss_unix_chaninfo * channelInfoP;
  const char * error;
  int fd;

  fd = socket(...);
  bind(fd, ...);
  listen(fd, 16);
  accept(fd, NULL, NULL);

  ChannelUnixCreateFd(fd, &channelP, &channelInfoP, &error);

  ...

  ChannelDestroy(channelP);
  free(channelInfoP);


Prototype:


    void
    ChannelUnixCreateFd(int                           fd,
                        TChannel **                   channelPP,
                        struct abyss_unix_chaninfo ** channelInfoPP,
                        const char **                 errorP);

This function creates a channel that uses a Unix (POSIX) stream socket and thereby TCP connection to transmit its byte streams back and forth. You supply a connected stream socket by file descriptor for the channel to use.

Note that POSIX sockets are screwy in that they are both channels and channel switches; they treat these very different functions as modes of a single entity. So it is important that the socket you pass to ChannelUnixCreateFd() be in the "connected" state. You can't make a channel out of any other kind.

fd is the file descriptor of the socket.

The function returns the handle of the channel it creates as *channelPP.

The function returns as *channelInfoPP a pointer to a structure that identifies what's at the other end of channel (to wit, the peer's IP address and TCP port number). This structure is in malloc'ed memory; you must free it when you are done with it. A typical use of this structure is to pass it to ServerRunChannel() and then have an HTTP request handler get and use it with SessionGetChannelInfo(). The type of information is specific to a Unix type channel and has this format:


    struct abyss_unix_chaninfo {
        size_t peerAddrLen;
        struct sockaddr peerAddr;
    };

A type like the above is defined in <xmlrpc-c/abyss_unixsock.h>.

If the function succeeds, it returns a null pointer as *errorP. If it fails, it generates a text string explaining why, in malloc'ed storage, and returns the pointer to it as *errorP. You must free this storage when you are done with it. When the function fails, no other return values are valid and nothing else is allocated or created.

ChannelOpenSslCreateSsl()

Example:


  TChannel channelP;
  struct abyss_openSsl_chaninfo * channelInfoP;
  const char * error;
  SSL * sslP;

  sslP = SSL_new(...);

  ChannelOpenSslCreateSsl(fd, &channelP, &channelInfoP, &error);

  ...

  ChannelDestroy(channelP);
  free(channelInfoP);


Prototype:


    void
    ChannelOpenSslCreateSsl(SSL *                        sslP,
                            TChannel **                      channelPP,
                            struct abyss_openSsl_chaninfo ** channelInfoPP,
                            const char **                    errorP);

This function creates a channel that uses an OpenSSL connection object to transmit its byte streams back and forth. Thus, it can carry on an SSL or TLS conversation over TCP. You supply the functioning OpenSSL connection object for the channel to use.

sslP is a pointer to the OpenSSL (libssl) connection object.

The function returns the handle of the channel it creates as *channelPP.

The function returns as *channelInfoPP a pointer to a structure that identifies what's at the other end of channel (to wit, the peer's IP address and TCP port number, as well as the SSL identification information, along with authentication information) and various information about the encryption. This structure is in malloc'ed memory; you must free it when you are done with it. A typical use of this structure is to pass it to ServerRunChannel() and then have an HTTP request handler get and use it with SessionGetChannelInfo(). The type of information is specific to an OpenSSL type channel and has this format:


    struct abyss_openSsl_chaninfo {
        size_t peerAddrLen;
        struct sockaddr peerAddr;
        SSL * sslP;
    };

A type like the above is defined in <xmlrpc-c/abyss_openssl.h>.

peerAddr is the network address (on an IP network, an IP address) of the client. peerAddrLen is the size of peerAddr. This address is just what is supplied by the network layer. To the extent the network is untrustworthy, this is too. For a better authenticated identity, you can use SSL information you get via sslP.

sslP is the handle of the OpenSSL connection object underlying the Abyss channel. You can use this to get all sorts of neat information about the connection, such as the verified certificate the client presented, using the OpenSSL library. (For example, to find out the authenticated name of the client, use SSL_get_peer_certificate(), and use X509_get_subject name() with the result of that).

This is kind of a modularity violation, which we don't mind because it is so easy and flexible. But note that it is the Abyss design intent that you use the SSL object only to get information about the channel.

If the function succeeds, it returns a null pointer as *errorP. If it fails, it generates a text string explaining why, in malloc'ed storage, and returns the pointer to it as *errorP. You must free this storage when you are done with it. When the function fails, no other return values are valid and nothing else is allocated or created.

This function was new in Xmlrpc-c 1.46 (March 2016).

ChannelDestroy()

Prototype:


    void
    ChannelDestroy(TChannel * channelP);

This function destroys a TChannel object of any type.

TChanSwitch

TChanSwitch is the C type for an Abyss channel switch. There are various types of TChanSwitches; they all look the same from the outside but are implemented differently. For example, three kinds exist today: one switches Unix socket-based channels, using a POSIX stream listen socket to hook up with clients. Another uses the same kind of socket to switch OpenSSL-based channels. A third type switches Winsock-based channels.

You create a TChanSwitch by calling a constructor function for the particular type of TChanSwitch you want. The constructor function has arguments that make sense for that particular type. Any TChanSwitch you create you must destroy after Abyss no longer needs it. You do that with the type-independent function ChanSwitchDestroy().

Note that in some modes of operation, you don't have to create a TChanSwitch at all; Either Abyss services create one under the covers or you do the switching your own way and pass ready-made channels to Abyss.

Plain POSIX Stream Socket

The functions in this section create a channel switch for channels that use a Unix (POSIX) stream socket and thereby TCP connection to transmit its byte streams back and forth. They also use a POSIX stream socket to listen for and accept new TCP connections.

ChanSwitchUnixCreate()

Example:


    TChanSwitch * chanSwitchP;
    const char * error;

    ChanSwitchUnixCreate(8080, &chanSwitchP, &error);

    if (error) {
        fprintf(stderr, "Couldn't create.  %s", error);
        free(error);
    } else {
        ...

        ChanSwitchDestroy(chanSwitchP);
    }

Prototype:


void
    ChanSwitchUnixCreate(uint16_t       portNumber,
                         TChanSwitch ** chanSwitchPP,
                         const char **  errorP);

This function creates a channel switch that accepts TCP connections that clients request and generates a unix-type channel based on each. It's like a POSIX socket in listen mode.

The switch accepts connections requested of IP Version 4 TCP port portNumber, via any network interface, to any IP address. If you want to listen on a particular network interface (at a particular IP address), even an IP Version 6 one, use the newer ChanSwitchUnixCreate2().

ChanSwitchUnixCreateIpV6Port()

This is the same as ChanSwitchUnixCreate except that it listens for IP Version 6 connections.

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

ChanSwitchUnixCreate2()

Example:


    TChanSwitch * chanSwitchP;
    const char * error;

    uint32_t const localhostIpAddr = 0x7f000001;

    struct sockaddr_in sockaddr;
    sockaddr.sin_family      = AF_INET;
    sockaddr.sin_addr.s_addr = htonl(localhostIpAddr);
    sockaddr.sin_port        = htons(8080);

    ChanSwitchUnixCreate2(PF_INET, (const struct sockaddr *)&sockaddr,
                          sizeof(sockaddr), &chanSwitchP, &error);

    if (error) {
        fprintf(stderr, "Couldn't create.  %s", error);
        free(error);
    } else {
        ...

        ChanSwitchDestroy(chanSwitchP);
    }

Prototype:


    void
    ChanSwitchUnixCreate2(int                     protocolFamily,
                          const struct sockaddr * sockAddrP,
                          socklen_t               sockAddrLen,
                          TChanSwitch **          chanSwitchPP,
                          const char **           errorP);

This function creates a channel switch that accepts TCP connections that clients request and generates a unix-type channel based on each. It's like a POSIX socket in listen mode.

The switch accepts connections requested of the TCP port (or ports) identified by the conventional POSIX socket address pointed to by sockAddrP, whose size in bytes is sockAddrLen. It accepts them in the POSIX network protocol protocolFamily (for example, PF_INET for IP Version 4).

These data structure types are defined in POSIX header file <sys/socket.h>, with implementations for particular protocols in other header files. IP (Versions 4 and 6) is covered by <netinet/in.h>. These are a little tricky to use, and we don't explain any further here because that trickiness is not specific to Xmlrpc-c. If you know how to code a program using a POSIX bind() call, you can code one using ChanSwitchUnitCreate2().

A far easier to use alternative if you just want to listen on a particular TCP port on every IP Version 4 or 6 interface is ChanSwitchUnixCreate() or ChanSwitchUnixCreateIpV6Port()

This function was new in Xmlrpc-c 1.30 (March 2012).

ChanSwitchUnixCreateFd()

Example:


    int fd;
    TChanSwitch * chanSwitchP;
    const char * error;

    fd = socket(...);
    bind(fd, ...);

    ChanSwitchUnixCreateFd(fd, &chanSwitchP, &error);

    if (error) {
        fprintf(stderr, "Couldn't create.  %s", error);
        free(error);
    } else {
        ...

        ChanSwitchDestroy(chanSwitchP);
    }

Prototype:


void
    ChanSwitchUnixCreateFd(int            fd,
                           TChanSwitch ** chanSwitchPP,
                           const char **  errorP);

This function creates a channel switch like ChanSwitchUnixCreate() does. The difference is that you supply a bound socket (by file descriptor fd) for the switch to use. The switch listens on that socket for client connections.

This is handy when the server program does not have the privilege required to bind a socket to the desired port number (which means that ChanSwitchUnixCreate() would fail) -- a more privileged program can create and bind the socket and let the less privileged server program inherit the open socket.

The socket you supply must be bound to a local address (that determines which connection requests the operating system passes on to the channel switch), but must be idle -- not in connected or listening state.

You must ensure that the socket continues to exist (the file descriptor stays open) as long as the channel switch exists. You must not access the socket in any way while the channel switch exists.

OpenSSL

ChanSwitchOpenSslCreate()

Example:


    SSL_CTX * sslCtxP;
    struct sockaddr_in sockaddr;
    TChanSwitch * chanSwitchP;
    const char * error;

    sslCtxP = SSL_CTX_new(SSLv23_server_method());

    sockaddr.sin_family      = AF_INET;
    sockaddr.sin_addr.s_addr = htonl(localhostIpAddr);
    sockaddr.sin_port        = htons(8080);

    ChanSwitchOpenSslCreate(PF_INET, (const struct sockaddr *)&sockaddr,
                            sizeof(sockaddr), sslCtxP,
                            &chanSwitchP, &error);

    if (error) {
        fprintf(stderr, "Couldn't create.  %s", error);
        free(error);
    } else {
        ...

        ChanSwitchDestroy(chanSwitchP);
    }

Prototype:


    void
    ChanSwitchOpenSslCreate(int                     protocolFamily,
                            const struct sockaddr * sockAddrP,
                            socklen_t               sockAddrLen,
                            SSL_CTX *               sslCtxP,
                            TChanSwitch **          chanSwitchPP,
                            const char **           errorP);

This function creates a channel switch that accepts TCP connections that clients request and generates an OpenSSL-type channel based on each.

It is analogous to ChanSwitchUnixCreate2 except that it adds the SSL/TLS authentication and encryption to the channel.

sslCtxP is a handle for an OpenSSL context object, as defined by the OpenSSL library.

See section HTTPS for more information.

This function was new in Xmlrpc-c 1.46 (March 2016).

ChanSwitchOpenSslCreateIpV4Port()

Example:


    SSL_CTX * sslCtxP;
    TChanSwitch * chanSwitchP;
    const char * error;

    sslCtxP = SSL_CTX_new(SSLv23_server_method());

    ChanSwitchOpenSslCreateIpV4Port(8080, sslCtxP,
                                    &chanSwitchP, &error);

    if (error) {
        fprintf(stderr, "Couldn't create.  %s", error);
        free(error);
    } else {
        ...

        ChanSwitchDestroy(chanSwitchP);
    }

Prototype:


    void
    ChanSwitchOpenSslCreateIpV4Port(unsigned short portNumber,
                                    SSL_CTX *      sslCtxP,
                                    TChanSwitch ** chanSwitchPP,
                                    const char **  errorP);

This function creates a channel switch that accepts TCP connections that clients request and generates an OpenSSL-type channel based on each.

It is analogous to ChanSwitchUnixCreate except that it adds the SSL/TLS authentication and encryption to the channel.

sslCtxP is a handle for an OpenSSL context object, as defined by the OpenSSL library.

See section HTTPS for more information.

This function was new in Xmlrpc-c 1.46 (March 2016).

ChanSwitchOpenSslCreateIpV6Port()

Example:


    SSL_CTX * sslCtxP;
    TChanSwitch * chanSwitchP;
    const char * error;

    sslCtxP = SSL_CTX_new(SSLv23_server_method());

    ChanSwitchOpenSslCreateIpV6Port(8080, sslCtxP,
                                    &chanSwitchP, &error);

    if (error) {
        fprintf(stderr, "Couldn't create.  %s", error);
        free(error);
    } else {
        ...

        ChanSwitchDestroy(chanSwitchP);
    }

Prototype:


    void
    ChanSwitchOpenSslCreateIpV6Port(unsigned short portNumber,
                                    SSL_CTX *      sslCtxP,
                                    TChanSwitch ** chanSwitchPP,
                                    const char **  errorP);

This function creates a channel switch that accepts TCP connections that clients request and generates an OpenSSL-type channel based on each.

It is analogous to ChanSwitchUnixCreateIpV6Port except that it adds the SSL/TLS authentication and encryption to the channel.

sslCtxP is a handle for an OpenSSL context object, as defined by the OpenSSL library.

See section HTTPS for more information.

This function was new in Xmlrpc-c 1.46 (March 2016).

ChanSwitchOpenSslCreateFd()

Example:


    int fd;
    SSL_CTX * sslCtxP;
    TChanSwitch * chanSwitchP;
    const char * error;

    fd = socket(...);
    bind(fd, ...);

    sslCtxP = SSL_CTX_new(SSLv23_server_method());

    ChanSwitchOpenSslCreateFd(fd, sslCtxP, &chanSwitchP, &error);

    if (error) {
        fprintf(stderr, "Couldn't create.  %s", error);
        free(error);
    } else {
        ...

        ChanSwitchDestroy(chanSwitchP);
    }

Prototype:


    void
    ChanSwitchOpenSslCreateFd(int            fd,
                              SSL_CTX *      sslCtxP,
                              TChanSwitch ** chanSwitchPP,
                              const char **  errorP);

This function creates a channel switch that accepts TCP connections that clients request and generates an OpenSSL-type channel based on each.

It is analogous to ChanSwitchUnixCreateFd except that it adds the SSL/TLS authentication and encryption to the channel.

sslCtxP is a handle for an OpenSSL context object, as defined by the OpenSSL library.

See section HTTPS for more information.

This function was new in Xmlrpc-c 1.46 (March 2016).

General

ChanSwitchDestroy()

Prototype:


    void
    ChanSwitchDestroy(TChanSwitch * chanSwitchP);

This function destroys a TChanSwitch object of any type.

Sockets

Abyss sockets are relevant only to Xmlrpc-c 1.06. Earlier versions use operating system sockets directly instead of the Abyss abstraction of a socket. Later versions use two separate abstractions, one for a "channel," and one for a "channel switch" instead (see Channels). The Abyss socket abstraction continues to exist for backward compatibility in current Xmlrpc-c, though.

The rest of this chapter assumes Xmlrpc-c 1.06.

An Abyss server uses entities modelled after POSIX sockets to communicate with clients. In fact, on a POSIX system, these Abyss sockets are based on actual POSIX sockets; i.e. the Abyss server uses POSIX sockets to communicate with clients.

There are ways to use libxmlrpc_abyss without ever seeing an Abyss socket. But if you want to have any but the simplest Abyss server, you will use functions that take an Abyss socket as an argument and you will create and destroy Abyss sockets explicitly.

As in the POSIX model, there are two very different kinds of Abyss sockets, but they both have the same C type and are in some ways treated as the same kind of thing:

An Abyss socket, of either type, has type TSocket.

Using the same type for both causes confusion, just as it does for POSIX sockets; that's why newer Xmlrpc-c uses the two types TChanSwitch and TChannel instead. TChanSwitch corresponds to a listening socket; TChannel corresponds to a Read/write socket.

This type was new in Xmlrpc-c 1.06 (July 2006). Before that, a different type named TSocket existed. It was just an alias (typedef) for whatever conventional socket class the operating system provides. I.e. it was what TOsSocket is today. To write code that works with both old and new Xmlrpc-c, don't use TSocket at all. Use the regular name for the OS socket data type instead. E.g. for Unix, use "int".

You always refer to a TSocket with a handle, which is simply a C pointer (TSocket *).

The TSocket type is designed to be an abstract type, with various implementations. I.e. all TSockets do the same basic job of managing connections and transmitting data back and forth, but different TSockets may do it different ways. There are only two implementations: one uses TCP via a POSIX (Unix) socket and the other uses TCP via a Winsock socket. Note that while a true HTTP server must use TCP, there could theoretically be a TSocket that uses an entirely different client/server communication method and you could use it to make an Abyss server that serves some variation on HTTP.

There are separate constructor functions for the individual Abyss socket implementations.

Unix Abyss Socket Constructors

These construct a Unix type Abyss socket, which is an Abyss socket based on a conventional Unix (POSIX) socket, such as you would create with a socket() C library call. This type of Abyss socket, and thus these constructors, don't exist on a regular Windows system (but in an environment with suitable Unix emulation, they might).

SocketUnixCreateFd()

Example:


    int socketFd;
    TSocket * socketP;
    socketFd = socket();
    SocketUnixCreateFd(socketFd, &socketP);
    ...
    SocketDestroy(socketP);

This creates an Abyss socket out of a Unix (POSIX) socket, which you identify by its file descriptor. The state of the socket, including whether it is a listening socket or a read/write socket, whether it is connected, and to what address it is bound, comes from the Unix socket.

Winsock Abyss Socket Constructors

These construct a Windows type Abyss socket, which is an Abyss socket based on a conventional Windows (Winsock) socket. This type of Abyss socket, and thus these constructors, don't exist on a Unix system.

socketWinCreateWinsock()

Example:


    SOCKET winsock;
    TSocket * socketP;
    winsock = socket();
    SocketWinCreateWinsock(winsock, &socketP);
    ...
    SocketDestroy(socketP);

This creates an Abyss socket out of a Winsock socket you supply. The state of the socket, including whether it is a listening socket or a read/write socket, whether it is connected, and to what address it is bound, comes from the Winsock socket.

Socket Destructor

You must eventually destroy every socket you create. There is one destructor for a socket, which works regardless of the implementation of the particular socket:

socketDestroy()

Example:


    TSocket * socketP;
    ...
    SocketDestroy(socketP);

Global Constants

libxmlrpc_abyss has global constants that you must set up. The global initialization function is AbyssInit(). The global termination function is AbyssTerm().

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

These functions were new in Xmlrpc-c 1.06 (July 2006). In libraries older than that, there are separate global initialization functions for invidual components of the library and many of the functions don't actually use any global constants.

AbyssInit()

Example:


    const char * error;
    AbyssInit(&error);
    if (error) {
        fprintf(stderr, "Could not initialize Abyss library! %s", error);
        free(error);
    }

Prototype:


    void
    AbyssInit(const char ** errorP);

This is libxmlrpc_abyss's global initialization function.

AbyssTerm()

Example:


    AbyssTerm();

Prototype:


    void
    AbyssTerm();

This is libxmlrpc_abyss's global termination function.

Creating and Running a Server

There are various modes in which you can use libxmlrpc_abyss to make an HTTP server (XML-RPC or otherwise).

The simplest is to say on what TCP port you'd like to accept connections and have Abyss take over. Abyss will listen for TCP connections, accept them as they come in, and process the HTTP transactions. It will spawn threads as needed to handle the transactions so it can handle multiple transactions at once. Your code never runs again until after the Abyss server has been shut down. For details on this mode, see ServerRun.

If that's too much leash to give Abyss, in another mode you can have Abyss process a single transaction and then return. Besides giving your code additional opportunity to participate in the serving, this lets your write a single threaded server program, i.e. a server that processes transactions serially. For some servers, this helps you prevent transactions from interfering with each other. See ServerRunOnce.

Instead of giving Abyss a port number on which to listen, you can give it a socket you bound yourself. This is handy when you want Abyss to server a reserved port number, but don't want to give Abyss the privilege (typically superuser privilege) to bind a reserved port itself.

You can also take care of managing the connections in your own code and just call Abyss to process a transaction after the connection with the client is all set up. See ServerRunChannel(). This is useful for an Inetd-based server.

ServerCreate()

Example:


    TServer abyssServer;
    success = ServerCreate(&abyssServer, "XmlRpcServer", 8080,
                           "/home/http/docs",
                           "/var/log/abyss");

Prototype:


    abyss_bool
    ServerCreate(TServer *    serverP,
                 const char * name,
                 uint16_t     port,
                 const char * filespath,
                 const char * logfilename);

This function creates an Abyss server.

The server is configured to listen for connections to a particular TCP port and accept those connections and process them. However, the server does not run autonomously; i.e. it will not process any HTTP requests all by itself. See e.g. ServerRun().

serverP is a pointer to a server handle you have allocated. ServerCreate() initializes the whole handle; its value on entry is meaningless.

name is a textual name for human consumption. I don't know exactly where this name shows up, but in general you can make up anything you like. A null pointer means to use the default, which is "unnamed". You can override whatever you set here with a subsequent call to ServerSetName() (this argument is a historical artifact -- it predates ServerSetName()).

port is the TCP port number on which the server is to listen for HTTP connections from clients.

filespath is the name of the directory in which web page files live. This is irrelevant for a pure XML-RPC server, but an Abyss server for an Abyss server that can also do basic web page serving, this is where the documents live. For example, if filespath is "/home/http" and a client does an HTTP GET of "http://myserver.com/dir1/mypage.html", the file that the Abyss server will send him is /home/http/dir1/mypage.html. A null pointer means to use the default, which is "/htdocs". You can override whatever you set here with a subsequent call to ServerSetFilesPath() (this argument is a historical artifact -- it predates ServerSetFilesPath()).

logfilename is the filename of the file to which the Abyss server logs. A null pointer means to use not to keep a log file. You can override whatever you set here with a subsequent call to ServerSetLogFileName() (this argument is a historical artifact -- it predates ServerSetLogFileName()).

ServerCreateSwitch()

Example:


    TServer abyssServer;
    TChanSwitch * chanSwitchP;
    const char * error;
    ChanSwitchUnixCreate(8080, &chanSwitchP, &error);
    if (error) {
        fprintf(stderr, "Couldn't create a channel switch!  %s", error);
        free(error);
    } else {
        ServerCreateSwitch(&abyssServer, &chanSwitchP, &error);
        if (error) {
            fprintf(stderr, "Couldn't create a server!  %s", error);
            free(error);
        } else {
            ServerSetName(&abyssServer, "XmlRpcServer");
            ServerSetFilesPath(&abyssServer, "/home/http/docs",
            ServerSetLogFileName(&abyssServer, "/var/log/abyss");

            ...

            ServerFree(abyssServer);
        }
        chanSwitchDestroy(chanSwitchP);
    }

Prototype:


    void
    ServerCreateSwitch(TServer *     serverP,
                       TChanSwitch * chanSwitchP,
                       const char ** errorP);

This function creates an Abyss server. It is the same as ServerCreate() except that instead of specifying a TCP port number on which to listen, you provide a channel switch. While ServerCreate() is limited to accepting connections in a very specific way, the channel switch could be much more flexible. For example, it is the only way you can create an HTTPS server.

The server created has name "unnamed", document root directory "/htdocs", and no log file. You can modify these properties with ServerSetName(), etc.

This function was new in Xmlrpc-c 1.07 (October 2006). In older Xmlrpc-c, use ServerCreateSocket2() for a similar function. The macro HAVE_CHANSWITCH tells a C program that the libxmlrpc_abyss is new enough to have this function.

ServerCreateSocket2()

Example:


    TServer abyssServer;
    TSocket * socketP;
    const char * error;
    SocketUnixCreateFd(STDIN_FILENO, &socketP);
    ServerCreateSocket2(&abyssServer, &socketP, &error);
    if (error) {
        fprintf(stderr, "Couldn't create a server!  %s", error);
        free(error);
    } else {
        ServerSetName(&abyssServer, "XmlRpcServer");
        ServerSetFilesPath(&abyssServer, "/home/http/docs",
        ServerSetLogFileName(&abyssServer, "/var/log/abyss");
    }

Prototype:


    abyss_bool
    ServerCreateSocket2(TServer *     serverP,
                        TSocket *     socketP,
                        const char ** errorP);

This function creates an Abyss server. It is the same as ServerCreate() except that instead of specifying a TCP port number on which to listen, you provide a socket that is already bound to the proper TCP port.

This is handy when the server program does not have the privilege required to bind a socket to the desired port number -- a more privileged program can create and bind the socket and let the less privileged server program inherit the open socket.

The server created has name "unnamed", document root directory "/htdocs", and no log file. You can modify these properties with ServerSetName(), etc.

This function was new in Xmlrpc-c 1.06 (July 2006). In older Xmlrpc-c, use ServerCreateSocket() instead. The macro HAVE_SERVER_CREATE_SOCKET_2 tells a C program that the libxmlrpc_abyss is new enough to have this function.

ServerCreateSocket()

This function is obsolete. Use ServerCreateSwitch() instead if possible.

Example:


    TServer abyssServer;
    success = ServerCreateSocket(&abyssServer, "XmlRpcServer", 5,
                                 "/home/http/docs",
                                 "/var/log/abyss");

Prototype:


    abyss_bool
    ServerCreateSocket(TServer *    serverP,
                       const char * name,
                       TOsSocket    socketFd,
                       const char * filespath,
                       const char * logfilename);

This is the same as ServerCreateSocket2() except that you supply a Unix socket file descriptor instead of an Abyss socket handle and you can set the server name, document root directory, and log file name in the same call.

TOsSocket is a typedef for the type of a conventional socket in the operating system in question. I.e. it is int on Unix and SOCKET on Windows. You should use int or SOCKET instead of TOsSocket in your program.

ServerCreateNoAccept()

Example:


    TServer abyssServer;
    success = ServerCreateNoAccept(&abyssServer, "XmlRpcServer",
                                   "/home/http/docs",
                                   "/var/log/abyss");

Prototype:


    abyss_bool
    ServerCreateNoAccept(TServer *    serverP,
                         const char * name,
                         const char * filespath,
                         const char * logfilename);

This function creates an Abyss server. It is the same as ServerCreate() except that the server it creates cannot accept connections from clients. Instead, you use server functions with it that supply as input an existing client connection.

For example, use ServerRunChannel() with this kind of server and supply a channel to a client, which you created somehow.

ServerFree()

This destroys the object created by ServerCreate(), etc.

The server must not be running when you do this. See Terminating an Abyss Server.

ServerSetName()

This function changes the name of a server.

See ServerCreateSwitch() for more information.

ServerSetFilesPath()

This function sets the root directory for HTTP documents a server will serve.

See ServerCreateSwitch() for more information.

ServerSetLogFileName()

This function changes the name of the file that the server will use as a log file.

See ServerCreateSwitch() for more information.

ServerSetKeepaliveTimeout()

This function sets the amount of time the server will keep a TCP connection with a client open after completing an HTTP transaction, waiting for the next request from the client. See Persistent Connections.

Prototype:


    void
    ServerSetKeepaliveTimeout(TServer * serverP,
                              uint32_t  keepaliveTimeout);

serverP is the handle of the server in question.

keepaliveTimeout is the period, in seconds. It must be greater than zero; the effect of zero on the keepalive timeout setting is undefined.

The setting affects future connections and may or may not affect existing connections.

The default period (in effect if you have never called this function) is 15 seconds.

ServerSetKeepaliveMaxConn()

This function sets the maximum number of requests Abyss will accept on a single TCP connection. See Persistent Connections.

Prototype:


    void
    ServerSetKeepaliveMaxConn(TServer * serverP,
                              uint32_t  keepaliveMaxConn);

serverP is the handle of the server in question.

keepaliveMaxConn is the number of requests.

This may or may not affect connections that already exist.

The default (in effect if you have never called this function) is 30 requests.

ServerSetTimeout()

This function sets how long the server will wait for a client to send its request once the client has connected to the server. If the client does not send a complete request within this time, the server aborts HTTP transaction and terminates the TCP connection.

Prototype:


    void
    ServerSetTimeout(TServer * serverP,
                     uint32_t  timeout);

serverP is the handle of the server in question.

timeout is the period in seconds.

The default (in effect if you have never called this function) is 15 seconds.

This was new in Xmlrpc-c 1.04 (November 2005).

ServerSetAdvertise()

This function tells the server whether or not to indicated, via an HTTP "Server" header, in its responses, that the server is running Xmlrpc-c and which version of it.

Prototype:


    void
    ServerSetAdvertise(TServer *  serverP,
                       abyss_bool advertise);

serverP is the handle of the server in question.

advertise is true to tell the server to send information about itself to clients; false not to.

The default (in effect if you have never called this function) is true.

This was new in Xmlrpc-c 1.04 (November 2005).

ServerSetMaxConn()

This function sets the maximum number of connections (HTTP transactions) the server will allow simultaneously. When there are this many, the server does not accept another one until an existing one ends.

This is meaningful only if you call a libxmlrpc_abyss subroutine that processes multiple HTTP requests at once, e.g. ServerRun.

While the server is not accepting connections because of this limit, the operating system still accepts TCP connections on the server's behalf and queues them. See ServerSetMaxConnBacklog().

Prototype:


    void
    ServerSetMaxConn(TServer *    serverP,
                     unsigned int maxConn);

serverP is the handle of the server in question.

maxConn is the maximum number of simultaneous connections. It must be greater than 0.

The default (in effect if you have never called this function) is 15.

This was new in Xmlrpc-c 1.32 (September 2012).

ServerSetMaxConnBacklog()

This function sets the maximum number of TCP connections the operating system will accept on the server's behalf without the server accepting them from the operating system. The server accepts TCP connections (presumably intended to be HTTP transactions) and queues them for the server to accept when it is ready. If the number queued reaches this limit, the operating system refuses, via the TCP protocol, any additional connection attempts.

In a server that processes HTTP requests one at a time (such as with ServerRunOnce, connections queue in the operating system while the server processes the current request and if the server cannot keep up, this limit comes into play.

In a server that processes multiple HTTP requests in parallel (such as with ServerRun, the limit on the number of simultaneous HTTP transactions it will allow affects the total backlog as seen by clients. See ServerSetMaxConn. The total number of requests that could arrive in a burst without any getting turned away is the sum of those two limits.

Prototype:


    void
    ServerSetMaxConnBacklog(TServer *    serverP,
                            unsigned int maxConnBacklog);

serverP is the handle of the server in question.

maxConnBacklog is the maximum TCP connection backlog in the operating system. It must be greater than 0.

The default (in effect if you have never called this function) is 15.

This was new in Xmlrpc-c 1.32 (September 2012).

ServerInit2()

Example:


TServer abyssServer;
...
const char * error;
ServerInit2(&abyssServer, &error);
if (error) {
  fprintf(stderr, "Failed to initialize.  %s", error);
  free(error);
}

Prototype:


void
ServerInit2(TServer *     serverP,
            const char ** errorP);

This function initializes an Abyss server to accept connections. Before you call this, the OS refuses any attempt to make a TCP connection to the TCP port that belongs to the server. You must call this before running the server (i.e. before calling ServerRun() or ServerRunOnce().

ServerRun() is usable only on a server object that was created to accept connections on its own, i.e. one created with ServerCreate() or ServerCreateSwitch().

If the function fails, it returns a text explanation of the failure as an ASCIIZ string in newly malloced storage and returns the address of that string as *errorP. If the function succeeds, it returns a null string as *errorP

This function was new in Xmlrpc-c 1.31 (June 2012). Before that, use ServerInit() instead.

ServerInit()

Example:


TServer abyssServer;
...
ServerInit(&abyssServer);

Prototype:


void
ServerInit(TServer * serverP);

This is the same as ServerInit2() except that when it fails, it writes an error message to Standard Error and aborts the program.

ServerRun()

Example:


TServer abyssServer;
...
ServerRun(&abyssServer);

Prototype:


void
ServerRun(TServer * serverP);

This function causes an Abyss server to run until shut down. It accepts HTTP connections as clients request them and processes them. It typically processes multiple connections simultaneously. (Use ServerSetMaxConn to control how many).

The function does not normally return until the server's life is over. It returns if something bad enough to prevent it from serving RPCs happens. It returns also if you explicitly terminate the server.

ServerRun() is usable only on a server object that was created to accept connections on its own, i.e. one created with ServerCreate() or ServerCreateSwitch(). Furthermore, the server must have been initialized to accept connections, which means you called ServerInit(). ServerRun() fails if you give it a server object that doesn't meet these requirements.

While running, Abyss spawns threads to handle multiple HTTP requests at once. There is generally a new thread for each client connection. Note that with HTTP persistent connections, it is possible for a single client connection to carry multiple HTTP requests, in serial fashion. These threads are POSIX threads on a Unix platform and Windows threads on Windows. Your request handlers may use the associated locking and other threading facilities to coordinate with each other. The threads that run the request handlers and the thread that invokes ServerRun() are all in the same process, so share the same memory, etc.

There is an alternative form of Abyss that uses POSIX forking for the threading. The Xmlrpc-c source package provides a "configure" option to generate that alternative. Forking Abyss handles separate connections in separate processes, and never the same process as the one that called ServerRun(), so your request handlers all see a copy of the original process' memory as of ServerRun() time, but any updates one makes are private to that connection.

ServerRunOnce()

Example:


TServer abyssServer;
...
ServerRunOnce(&abyssServer);

Prototype:


void
ServerRunOnce(TServer * serverP);

This function causes an Abyss server to process one HTTP connection (waiting for one if necessary). It waits for the next connection on the server's listening socket, reads the HTTP request, executes it, sends the response, and closes the connection. Note that because the listening socket is perpetually listening, the operating system will accept and queue connections on its own. ServerRunOnce() processes a connection that the operating previously accepted.

(ServerSetMaxConnBacklog controls how many connections the operating system will queue before refusing them).

ServerRunOnce() is a good way to make sure your server executes no more than one RPC at a time, when your method function is written to be single threaded. It is also a good way to ensure that all RPCs execute against the same memory, as opposed to running in separate processes. And it's the easiest to debug. All new server programs should start out using this interface, before graduating to the more demanding ServerRun() approach.

Example


    unsigned int const portNumber = 8080;

    TServer abyssServer;
    xmlrpc_registry * registryP;
    xmlrpc_env env;
    
    xmlrpc_env_init(&env);

    registryP = xmlrpc_registry_new(&env);

    xmlrpc_registry_add_method(
        &env, registryP, NULL, "sample.add", &sample_add, NULL);

    MIMETypeInit();

    ServerCreate(&abyssServer, "XmlRpcServer", portNumber), NULL, NULL);
    
    xmlrpc_server_abyss_set_handlers(&abyssServer, registryP);

    ServerInit(&abyssServer);

    setupSignalHandlers();

    while (1) {
        printf("Waiting for next RPC...\n");

        ServerRunOnce(&abyssServer);
    }


ServerRunOnce() is usable only on a server object that was created to accept connections on its own, i.e. one created with ServerCreate() or ServerCreateSwitch(). Furthermore, the server must have been initialized to accept connections, which means you called ServerInit(). ServerRunOnce() fails if you give it a server object that doesn't meet these requirements.

ServerRunOnce() aborts waiting for a connection request and returns immediately if the process receives a signal. Note that unless you have a handler for that signal, the signal will probably kill the whole process, so set up a signal handler -- even one that does nothing -- if you want to exploit this. But before Xmlrpc-c 1.06 (June 2006), signals have no effect -- there is no way to make ServerRunOnce abort the wait and return.

Before Xmlrpc-c 1.04 (November 2005), ServerRunOnce() initiated the connection processing in the background, returning to Caller immediately. But it worked correctly only on a system that does threading with Unix "fork." On others, behavior was undefined.

ServerRunOnce2()

Don't use ServerRunOnce2(). Use ServerRunOnce() instead.

ServerRunOnce2() was invented as an extension to ServerRunOnce() during a period of design upheaval in 2005. ServerRunOnce() was originally intended to fork a new process to process an HTTP connection and return immediately. ServerRunOnce2() was supposed to give the caller a choice of doing that or processing the connection in the foreground.

But it really isn't practical to process a connection in the background, so today ServerRunOnce() processes in the foreground and ServerRunOnce2() does too, ignoring its background/foreground argument. ServerRunOnce2() exists only so some programs that were coded to use it will continue to work without change.

ServerRunOnce2() was new in Xmlrpc-c 1.01 (January 2005) and became foreground-only in Xmlrpc-c 1.04 (November 2005). In between, the background feature worked correctly only on systems that do threading via a Unix "fork."

ServerRunChannel()

Example:


    TChannel * channelP;
    const char * error;
    ChannelUnixCreateFd(&channelP, STDIN_FILENO);
    ServerRunChannel(&abyssServer, &channelP, NULL, &error);
    if (error) {
        fprintf(stderr, "Couldn't run the server.  %s", error);
        free(error);
    }
    ...
    ChannelDestroy(channelP);

Prototype:


void
ServerRunChannel(TServer *     serverP,
                 TSocket *     channelP,
                 void *        channelInfoP,
                 const char ** errorP);

This function causes an Abyss server to process one HTTP request from the channel you supply. For normal HTTP, the channel must be new -- with nothing having been read or written through it yet. ServerRunChannel() reads an HTTP request from the channel, performs it, and writes the response to the channel.

This would be useful for an Inetd application. Inetd is a server common on Unix systems that listens for connections on various ports simultaneously. When it gets one, it accepts it and forks a process and execs a program that provides whatever service is supposed to be available on the port in question. For example, Inetd might be configured to accept connections to the FTP port, among others. When someone tries to connect to the FTP port, Inetd accepts the connection and forks a process running an FTP server program. It continues listening for further FTP connections and forks other FTP server processes to handle any others.

Inetd passes the connected stream socket to the processor program as its Standard Input. The processor program must be designed to expect a connected stream socket on its Standard Input. One way to construct such a program is to have it build an Abyss channel out of Standard Input with ChannelUnixCreateFd() and then run ServerRunChannel() with the Abyss channel as an argument. Have the program simply exit when ServerRunChannel() returns.

ServerRunChannel does not use HTTP persistent connections. I.e. it tells the client to close the TCP connection immediately after receiving the response and does the same itself after sending the response. It is designed to be used with a channel that carries exactly one HTTP transaction.

ServerRunChannel() is usable only on a server object that was created to use user-supplied connections, i.e. one created with ServerCreateNoAccept(). ServerRunChannel() fails if you give it a server object that doesn't meet this requirement.

The serverP argument identifies the server that you want to process HTTP requests from the channel, and channelP identifies the channel.

channelInfoP is a pointer to a structure of information about the channel channelP. The structure is opaque to Abyss; it doesn't care what information is in it. But your HTTP request handler code, which handles a request received over this channel, might later ask Abyss for this information, and know how to interpret it. (It would use SessionGetChannelInfo() to get the information). Typically, the structure includes information about the guy on the other end of the channel (the HTTP client), such as his IP address.

You can specify a null pointer for channelInfoP to indicate that you are not supplying any channel information.

This function was new in Xmlrpc-c 1.07 (October 2006). In older Xmlrpc-c, use ServerRunConn2() instead. The macro HAVE_CHANSWITCH tells a C program that the libxmlrpc_abyss is new enough to have this function.

ServerRunConn2()

Example:


    TSocket * connectedSocketP;
    const char * error;
    SocketUnixCreateFromFd(&connectedSocketP, STDIN_FILENO);
    ServerRunConn2(&abyssServer, &socketP, &error);
    if (error) {
        fprintf(stderr, "Couldn't run the server.  %s", error);
        free(error);
    }
    ...
    SocketDestroy(connectedSocketP);

Prototype:


void
ServerRunConn2(TServer * serverP,
               TSocket * connectedSocketP);

This function causes an Abyss server to process one HTTP request from the read/write Abyss socket you supply. For normal HTTP, the socket must be for a new TCP connection, with nothing having been read or written on the connection yet. ServerRunConn2() reads an HTTP request from the socket, performs it, and writes the response to the socket.

This is useful in the same applications as ServerRunChannel() and works the same way except as described here. It's a little simpler than ServerRunChannel() and exists in older versions of Xmlrpc-c.

This function was new in Xmlrpc-c 1.06 (July 2006). In older Xmlrpc-c, use ServerRunnConn() instead. The macro HAVE_SERVER_RUN_CONN_2 tells a C program that the libxmlrpc_abyss is new enough to have this function.

ServerRunConn()

This function is obsolete. Use ServerRunConn2() instead if possible.

Example:


ServerRunConn(&abyssServer, STDIN_FILENO);

Prototype:


void
ServerRunConn(TServer * serverP,
              TOsSocket connectedSocket);

This function causes an Abyss server to process one HTTP request from the already-connected socket you supply. The socket must be in the TCP connected state at entry, with nothing having been read or written on the connection yet. ServerRunConn() reads an HTTP request from the socket, performs it, writes the response to the socket, shuts down the connection, and closes the socket.

You use this the same way you use ServerRunConn2(), except you use the Stanard Input file descriptor directly instead of creating an Abyss socket.

TOsSocket is a typedef for the type of a conventional socket in the operating system in question. I.e. it is int on Unix and SOCKET on Windows. You should use int or SOCKET instead of TOsSocket in your program.

Terminating an Abyss Server

Terminating an Abyss server means making it stop running, so it won't serve any more requests. It is separate from dstroying the server, which you do with ServerFree().

ServerTerminate()

Example:


TServer abyssServer;

static void
sigtermHandler(int const signalClass) {

  ServerTerminate(&abyssServer);
}

struct sigaction mysigaction;
mysigaction.sa_flags = 0;
mysigaction.sa_handler = sigtermHandler;
SIGACTION(SIGTERM, &mysigaction, NULL);

...

ServerRun(&abyssServer);


Prototype:


void
ServerTerminate(TServer * serverP);

This function causes an Abyss server that is running to terminate, and to terminate immediately if it starts running in the future.

Because a program that starts a server running does not regain control as long as the server is running, this is meant to be called from a separate thread or signal handler or from an Abyss HTTP request handler.

If you have a signal handler that calls ServerTerminate() and the process receives a signal while the server is running, the server will terminate (ServerRun() or the like will return) soon after the signal handler returns. If the server is processing an HTTP request when the process receives the signal, it will not terminate until it is done with that request.

You can call this from an Abyss HTTP request handler, as long as that request handler is running in the same process that waits for and accepts new connections. If, on the other hand, you are running Abyss in a mode in which it processes connections in the background and you have a system in which it does that by way of a Unix fork, a ServerTerminate() from inside an Abyss HTTP request handler has no effect.

If you are running the server in a mode in which it processes connections in the background (i.e. in separate threads or processes), ServerTerminate() affects only the foreground thread that waits for, accepts, and dispatches new connections. The background threads that process connections continue normally to completion in their own time.

This function was new in Xmlrpc-c 1.06 (June 2006). Before that, there is no way to terminate a running Abyss server. Before Xmlrpc-c 1.14 (March 2008) on Unix and Xmlrpc-c 1.25 (December 2010) on Windows, ServerTerminate() does not take effect until after the server has accepted another connection from a client or a signal handler (including the one that calls ServerTerminate()) returns (i.e. ServerTerminate() does not wake up a sleeping server).

ServerResetTerminate()

Example:


TServer abyssServer;

...

ServerResetTerminate(&abyssServer);

Prototype:


void
ServerResetTerminate(TServer * serverP);

This function resets the termination requested status of an Abyss server that you previously set with ServerTerminate(). As long as termination is requested, the server will stop as soon as you start it (e.g. ServerRunOnce() will return immediately without processing any connection).

You can use this function if, after you have terminated a server by calling ServerTerminate(), you want to start it running again.

You can also use this to abort a previously requested termination. If, though you called ServerTerminate(), the server is still running because it is still processing a connection, ServerResetTerminate() will cause it to forget about the termination request and keep running even after it is finally done with that connection.

This function was new in Xmlrpc-c 1.06 (June 2006).

HTTP Request Handlers

By default, Abyss implements HTTP requests from clients in conventional web server ways. For example, when the server gets an HTTP GET request from a client, the server sends the file named in the request (which typically contains a web page in HTML) back to the client.

But you can make an Abyss server handle an HTTP request any way you like by installing an HTTP request handler. You might install a handler for GET requests that generates web page contents computationally instead of just pulling them out of a file.

In particular, you make an XML-RPC server out of an Abyss server by installing an HTTP request handler for POST requests. That handler interprets the document sent by the client as an XML-RPC call, executes the XML-RPC method it specifies, and sends the XML-RPC result back as the HTTP response document.

Abyss itself knows nothing about XML-RPC. Another component of Xmlrpc-c (libxmlrpc_server_abyss) installs the necessary HTTP request handlers to make the server an XML-RPC server.

You register with Abyss any number of HTTP request handlers. Each handler is responsible for examining the HTTP request and deciding whether it is the appropriate handler for it. For each HTTP request, Abyss calls every handler until one returns an indication that it is an appropriate handler for it, and therefore handled it.

If none of your handlers claims the HTTP request, Abyss uses a default handler. By default, the default handler is a builtin one, but you can replace it with one of your own.

Abyss passes to your HTTP request handler a handle for the Abyss session for the HTTP request you are supposed to handle. Abyss provides various services to give you the information about that session that you need to handle the request. For example, you can find out the values of HTTP header fields, and get the body of the request. Other services let your handler tell Abyss how to complete the request. For example, you can supply the HTTP response body.

When an Abyss server terminates, it calls the termination function of every HTTP request handler that has one.

Managing Handlers

You declare the set of HTTP request handlers (not counting those that are built into Abyss) for a server by calling ServerAddHandler3() for each. You must do this before running the server (e.g. by calling ServerRun()). You cannot remove a handler.

A default HTTP request handler is a different thing.

You declare your default HTTP request handler with ServerDefaultHandler(). If you don't, the server supplies a built-in default HTTP request handler of its own that does basic web site serving.

The argument of ServerDefaultHandler() is a function pointer of type THandlerDflt:


    typedef abyss_bool (*THandlerDflt) (TSession *);

The return code is either historical or for future expansion. The server does not use it, but you should return TRUE. Your default HTTP request handler does not have the option of failing or refusing. It must generate an HTTP response, even if that response indicates that the whole request has gone pear-shape.

The function must use at most 128K of program stack space.

You can set the default HTTP request handler multiple times; only the last one counts. You can even use ServerDefaultHandler() to restore the built-in handler after having set something else.

ServerAddHandler3()

Example:


    TServer server;
    static void
    handleRequest(void *        const handler,
                  TSession *    const sessionP,
                  abyss_bool *  const handledP) {

        ResponseStatus(sessionP, 500);  /* HTTP server error */
        ResponseError2(sessionP, "Handler is just an example");
        *handledP = TRUE;
    }
    abyss_bool succeeded;    

    struct ServerReqHandler3 {
        .handleReq = handleRequest;
    } handler;

    ServerCreate(&server, ...);

    ServerAddHandler3(&server,
                      &handler,
                      &succeeded);

Prototype:


    typedef void (*termHandlerFn)(void *);
    
    typedef void (*handleReq3Fn)(void *,
                                 TSession *,
                                 abyss_bool *);

    struct ServerReqHandler3 {
        handleReq3Fn  handleReq;
        void *        userdata;
        size_t        handleReqStackSize;
        termHandlerFn term;
    };
    
    void
    ServerAddHandler3(TServer *                        serverP,
                      const struct ServerReqHandler3 * handlerP,
                      abyss_bool *                     successP);

This function adds an HTTP request handler to an Abyss server.

serverP is the handle of the Abyss server to which you are adding the handler.

handlerP points to a structure the describes your handler. The structure has no higher meaning -- it is just a convenient way to pass arguments to ServerAddHandler3. It is designed so that the value that means "not present" for any optional member is represented as zero bits.

successP is a pointer to a return variable. ServerAddHandler3() returns TRUE if it successfully adds the handler; FALSE otherwise.

Note that the arguments do not tell what kind of HTTP requests the handler handles. The server passes every request to the handler and the code of the handler function (myHandlerFunc() in the example above) decides whether this handler is the right one for that request. (This is explained in detail above).

This function was new in Xmlrpc-c 1.16 (September 2008). With older Xmlrpc-c, use ServerAddHandler2()

ServerAddHandler2()

This is obsolete. Use ServerAddHandler3() instead if possible.

Prototype:


    typedef void (*handleReq2Fn)(struct URIHandler2 *,
                                 TSession *,
                                 abyss_bool *);

    typedef struct URIHandler2 {
        initHandlerFn init;
        termHandlerFn term;
        handleReq2Fn  handleReq2;
        URIHandler    handleReq1;  /* deprecated */
        void *        userdata;
    } URIHandler2;

    void
    ServerAddHandler2(TServer *     srvP,
                      URIHandler2 * handlerP,
                      abyss_bool *  successP);

This function adds an HTTP request handler to an Abyss server. srvP is the handle of the Abyss server to which you are adding the handler. handlerP is a pointer to a description of the handler. It is the same as the identically named argument to ServerAddHandler3 except that it lacks the stack size member and it has an init member. init is a pointer to the initialization function (see below). NULL means none. successP is a pointer to a return variable. ServerAddHandler2() returns TRUE if it successfully adds the handler; FALSE otherwise.

This is a rather odd interface (one of the reasons it's obsolete because it sort of treats the URIHandler2 structure as an object. In an earlier design, it was a pointer to that structure that got passed to the request handler and initialization "methods." Now, what gets passed is a pointer to a copy of that structure, so that the caller of ServerAddHandler2 doesn't have to keep it around, and the only practical member of the structure for the "method" to access is userdata.

When an Abyss server begins to run, it calls the initialization function of each HTTP request handler that has one. The initialization function returns a success or failure indication, and if it returns failure, Abyss never uses that handler. It doesn't have much practical use, but fits logically with the original object oriented nature of this interface.

When an Abyss server terminates, it calls the termination function of every HTTP request handler that has one and whose initialization function returned success.

ServerAddHandler2() was new in Xmlrpc-c 1.03 (June 2005). In older Xmlrpc-c, use ServerAddHandler().

ServerAddHandler()

This is obsolete. Use ServerAddHandler3() instead if possible.

ServerDefaultHandler()

Example:


    TServer server;

    static abyss_bool
    myDefaultHandler(TSession * const serverP) {
        ResponseStatus(sessionP, 500);  /* HTTP server error */
        ResponseError2(sessionP, "Handler is just an example");
        return TRUE;
    }

    ServerCreate(&server, ...);

    ServerDefaultHandler(&server, myDefaultHandler);

Prototype:


    void
    ServerDefaultHandler(TServer *    serverP,
                         THandlerDflt handler);

This function sets the default HTTP request handler for a server.

When you create a server, its default HTTP request handler is a built-in one. You can use this function to override that. You can call this function multiple times for the same server, and only the last one has effect.

serverP identifies the server whose default HTTP request handler you are setting.

handler is a function pointer for your handler function. Specify NULL to select the built-in one. (Since that's the default, the only time you would need this is when for some reason you have previously set the default HTTP request handler to something else).

Your handler function may use up to 128K of stack space.

Request Handler Services

This section describes Abyss services that an HTTP Request Handler can call.

Each of these functions takes an Abyss session handle as an argument. An Abyss session is the entity Abyss uses to track a single HTTP request. Abyss passes the relevant handle to your HTTP request handler as an argument.

SessionGetRequestInfo()

This function gets a variety of information about the HTTP request, mainly from the HTTP header.

Example:


    TRequestInfo * requestInfoP;

    SessionGetRequestInfo(abyssSessionP, &requestInfoP);

    if (requestInfoP->method == m_get)
        printf("Request for file name '%s'!", requestInfoP->uri);

    free(requestInfoP);

Prototype:


void
    SessionGetRequestInfo(TSession *            sessionP,
                          const TRequestInfo ** requestInfoPP);
    

To get the value of any HTTP header field for the request, use RequestHeaderValue. Note that some of this information is also available from SessionGetRequestInfo.

SessionGetChannelInfo()

This function gets information about the channel over which the HTTP request is being conducted. For example, in a typical server, it tells you the IP address and TCP port number of the HTTP client.

Example:


    struct abyss_unix_chaninfo * chanInfoP;
    SessionGetChannelInfo(abyssSessionP, &(void*)chanInfoP);

Prototype:

    void
    SessionGetChannelInfo(TSession * sessionP,
                          void **    channelInfoPP);



The value *channelInfoPP that the function returns is a pointer to a structure whose format and contents is entirely dependent on the type of channel in use, and a particular handler should be designed to expect the channel to be of a particular type. The same code that adds that handler to the Abyss server also determines what sort of channel that Abyss server will use to talk to clients.

If the channel over which the HTTP request is being conducted is one that your program supplied with ServerRunChannel(), the channel information returned is the information you supplied as an argument to ServerRunChannel().

If the channel was created by a Unix type channel switch, the information returned is a struct abyss_unix_chaninfo.

SessionGetChannelInfo() returns a pointer to storage owned by the Abyss server. You must access it only while the handler function that called the function is running, and you do not explicitly release it or free the memory.

SessionGetChannelInfo() was new in Xmlrpc-c 1.07 (October 2006). Before that, there is no way for an HTTP request handler to find out information such as the IP address of the client.

RequestHeaderValue()

This function gets the value of a specified field in the HTTP request header for a session.

Example:


    const char * const contentType =
      RequestHeaderValue(abyssSessionP, "content-type");

Prototype:


    const char *
    RequestHeaderValue(TSession *   sessionP,
                       const char * name);

sessionP identifies the HTTP session whose request header is being queried.

name is the name of the HTTP header field, in lower case. For example, "content-type".

The return value is the value of the HTTP header field. For example, "application/octet-stream".

The the HTTP request header does not contain a field named name, the return value is a null pointer.

RequestAuth()

This function authenticates the requester, in a very simplistic fashion.

If the request specifies basic authentication (via an Authorization header) with username and password equal to values you specify, RequestAuth() considers the requester authentic. Otherwise, it doesn't.

Note that you ask about exactly one username; if you have a database of known users, you have to call RequestAuth() once for every user until one of them passes authentication.

When RequestAuth() doesn't authenticate the user, it sets up an authorization failure reponse (HTTP response status 401) that says the user must supply an identity.

Once RequestAuth() has determined that the requester is authentic, a SessionGetRequestInfo() tells you the requester's username.

Example:


    bool authenticated;

    authenticated = RequestAuth(abyssSessionP, "mydomain",
                                "johnsmith", "passw0rd")

    if (authenticated)
        processRequest(abyssSessionP);


Prototype:


    abyss_bool
    RequestAuth(TSession *   const sessionP,
                const char * const credential,
                const char * const user,
                const char * const pass) {

sessionP identifies the session in which the request in question is executing.

credential names the user identity domain in which you expect the username the user supplied to exist. The only effect of this argument is that when RequestAuth() prepares a failure response because of lack of authentication, that response tells the user to supply a username in this domain.

user and pass are the single username and password the user must have supplied with the HTTP request in order for RequestAuth() to consider the identity authentic.

The return value is true if the identity is authentic; false otherwise.

Debugging

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

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

ABYSS_TRACE_CONN environment variable

If you set the ABYSS_TRACE_CONN environment variable to 1, the Abyss server will print messages to Standard Error showing you the activity on each HTTP connection. In particular, it shows you the HTTP data flow: header and body of both request and response.

If your server is an XML-RPC server based on libxmlrpc_server_abyss and you don't have any reason to question the HTTP part of the protocol, you may prefer to use facilities of libxmlrpc_server to print just the XML-RPC XML (i.e. the HTTP request and response bodies).

This facility was new in Xmlrpc-c 1.03, June 2005, for requests only; responses were added in Xmlrpc-c 1.05 (March 2006).

ABYSS_TRACE_CHANNEL environment variable

If you set the ABYSS_TRACE_CHANNEL environment variable to 1, the Abyss server will print messages to Standard Error showing channel-level operation. You will see where channels get created and destroyed. You will see each read and write of the channel, but you won't see the contents (To see the data, use the higher level ABYSS_TRACE_CONN trace). This is mainly useful if you suspect something funny with the network.

This facility was new in Xmlrpc-c 1.07 (October 2006). Before that, there was ABYSS_TRACE_SOCKET.

ABYSS_TRACE_SWITCH environment variable

If you set the ABYSS_TRACE_SWITCH environment variable to 1, the Abyss server will print messages to Standard Error showing the operation of channel switches. This is mainly useful if you suspect something funny with the network.

This facility was new in Xmlrpc-c 1.07 (October 2006). Before that, there was ABYSS_TRACE_SOCKET.