Control Flow in the Rover Server
This document is divided into several sections:
- Structure of the Rover Server
- Functional Differences Between the Standalone
Daemon and the Forked Servers
- Request Reception
- Main Request Loop Processing
- Command Processing
- An Example Plug-In Module: Irolo
Structure of the Rover Server
The Rover Server consists of several modules:
- The core server module, rover.c, containing connection
management, Tcl interface procedures, and overall control flow.
- util.c containing buffer management and transmission code.
- recover.c containing recovery management code (only in
the Standalone Daemon server).
- rovertcp.c containing TCP and network management code.
- malloc.c, an enhanced memory allocator.
- database.c, the object database and log manipulation code.
- interface.c, the server plug-in interface manager.
- Server plug-in modules, e.g., tclIndex.c, roverhttp.c, e-mail.c,
webcal.c, nntp.c, agent.c and irolo.c modules.
- html.c, an HTML parser library.
- System libraries: libtk, libtcl, libX11, libm, and
libgdbm.
A new server module is added to the Rover Server by using
the server plug-in interface and linking it into
the Standalone Daemon and Forked server executables.
Functional Differences Between the Standalone Daemon and the Forked
Servers
Here is the control flow for the Standalone Daemon and the Forked
Rover Servers. There are several important functional differences
between the two servers.
- The Standalone Daemon server is manually started, while the Forked
server is started automatically by the httpd server.
- The Standalone Daemon server handles requests on TCP port 9090,
while the Forked server handles requests, through the httpd
server, on TCP port 80 (or whatever port the httpd server is
using).
- The Standalone Daemon server only handles
POST
method requests, while the Forked server handles requests with either the
POST or
GET
methods.
- The Standalone Daemon server runs a very simple control loop to
service incoming TCP connections, while the Forked server receives
requests from standard input and environment variables.
- The Standalone Daemon server guarantees FIFO processing of requests
from clients.
Request Reception
Standalone Daemon Server
When the Standalone Daemon server is started it forks a child
process to act as the Standalone Daemon server. The child process
listens for incoming requests on TCP port 9090 (or the port specified
on the command line). When a connection is established, the server
receives the
HTTP
request header. It only parses the
Content-Type
and
Content-Length
entity-header fields. The server places the values of these fields
into a state structure for the connection. The server sets the
REQUEST_METHOD element variable to PUT.
Only POST method requests are accepted.
After reading the header, the server sets stdout and stdin to the
socket for the incoming request and initializes a setjmp return
point. It then invokes the main request processing loop. After
processing a request, the server attempts to restore the original
environment and reset its state.
Because it is single-threaded, the Standalone Daemon server sets an
interval alarm (controlled by TIMEOUT_SECS and
TIMEOUT_USECS in rover.c). If the timer expires
(due to a long running request), a signal handler is invoked. The
handler forks a new thread to handle the request and reconfigures the
server to accept a new request.
The Standalone Daemon server provides FIFO processing of incoming
requests by using the USER, HOST, and MSGID fields of requests.
If an out-of-order request is received, it is decoded and its
information placed in a state structure. The state structure is then
logged to stable storage. The request will not be processed until all
earlier requests from USER@HOST are recieved. Duplicate messages are
discarded.
All actions taken by the Standalone Daemon server are logged to stable
storage. This includes incoming messages, changes to stable variables,
declarations of recovery procedures, and the results of processing
requests. The logged information is used to recover the state of the
server, should a failure occur.
Forked Server
The Forked server is invoked as a
CGI-BIN program by an
httpd server. It initializes a setjmp return point and
invokes the main request processing loop.
Main Request Loop Processing
Both servers perform the same main request loop code. Non-fatal errors
are handled by calling the
errorExit function with an error
message. This function will send the error message back to the client
and longjmp to the setjmp point with a ROVER_ERROR value. The
Forked server will terminate operation, while the Standalone Daemon
server will clean up state and resume listening on its TCP port.
Fatal errors are handled by calling
fatalError and for both servers,
cause them to emit a message to stderr and terminate operation.
The Standalone Daemon server will be restarted by its parent.
Control flow in the main request loop is as follows:
- Reset the global variables.
- Parse and insert the TOKEN=VALUE request pairs into a
global TCL hash table, rqpairs.
- Initialize the ClientUser, rhost, and
Service global variables from the request pairs.
- Perform an authentication check and switch groups and user id to
the target user.
- Initialize the output buffer.
- Initialize the LogID, UserTime,
UserServer, and Object global variables from the
request pairs.
- If the target user is nobody, invoke the
perform_nobody_command command processor. Otherwise, invoke
the perform_command command processor.
- If the command has not flushed the output buffer, flush the buffer
back to the client.
- For the Standalone Daemon server, release the space associated
with the query and the TCL hash table.
Command Processing
There are two command processors: perform_nobody_command and
perform_command. The perform_nobody_command is used
when processing commands as user nobody. Only a restricted
set of operations (IMPORTBINARY, IMPORTDATA, and a
set of testing routines) are allowed when operating as user
nobody.
Rover Server Plug-In Interface
The perform_command command processor is used when processing
a command as a target user and it supports the Rover Server plug-in
module interface. A plug-in module is a server module for handling
Rover operations (e.g., an e-mail application or distributed
calendar). A plug-in module is responsible for a segment of the Rover object namespace.
A plug-in module must provide two functions: a function for importing
objects and a function exporting objects. The C signature for these
functions is:
typedef void (*plugIn)(char *object, char *objclass);
Optionally, a plug-in module may also provide command and recovery
procedures. The command procedure is invoked by the object command
operation, RESET. The C signature for this function is:
typedef void (*plugIn)(char *object, char *objclass);
The recovery procedure is invoked during failure recovery. The C
signature for this procedure is:
typedef void (*plugRec)(CMDtype cmd);
Plug-in module information is stored in the plugs array; each
element of the array has the following structure:
struct _PlugIns {
char *id; /* The module's namespace prefix id */
plugIn import; /* The module's import procedure */
plugIn export; /* The module's export procedure */
plugRec recover; /* The module's recovery procedure */
plugIn command; /* The module's command procedure */
};
typedef struct _PlugIns PlugIns;
A new plug-in module is added to a server by specifying the class of
the plug-in module and the import and export functions and linking
the Standalone Daemon and Forked servers with the module's code. Here
is the current list of plug-in modules:
PRIVATE PlugIns plugs[] = {
{ "Email__", email_import, email_export, NULL, NULL },
{ "Ical__", ical_import, ical_export, NULL, NULL },
{ "Webcal__", webcal_import, webcal_export, NULL, NULL },
{ "Nntp__", nntp_import, nntp_export, NULL, NULL },
{ "Irolo__", irolo_import, irolo_export, NULL, irolo_command },
{ "Agent__", agent_import, agent_export, agent_recover, NULL },
/* Add new plug-in modules here */
{ NULL, NULL, NULL, NULL, NULL }
};
An Example Plug-In Module: Irolo
The Irolo application is a small, simple application that implements a
graphical Rolodex. The server component of the application consists of
a C module and a
Tcl library.
The client component of the application consists of a
generic C module and a
Tcl module.
Last updated by $Author: adj $ on $Date: 1997/12/02 19:44:27 $.
Copyright © 1995-1998 Anthony D. Joseph and Massachusetts
Institute of Technology