Control Flow in the Rover Server

This document is divided into several sections:

  1. Structure of the Rover Server
  2. Functional Differences Between the Standalone Daemon and the Forked Servers
  3. Request Reception
  4. Main Request Loop Processing
  5. Command Processing
  6. An Example Plug-In Module: Irolo

Structure of the Rover Server

The Rover Server consists of several modules: 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.
  1. The Standalone Daemon server is manually started, while the Forked server is started automatically by the httpd server.
  2. 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).
  3. The Standalone Daemon server only handles POST method requests, while the Forked server handles requests with either the POST or GET methods.
  4. 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.
  5. 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:
  1. Reset the global variables.
  2. Parse and insert the TOKEN=VALUE request pairs into a global TCL hash table, rqpairs.
  3. Initialize the ClientUser, rhost, and Service global variables from the request pairs.
  4. Perform an authentication check and switch groups and user id to the target user.
  5. Initialize the output buffer.
  6. Initialize the LogID, UserTime, UserServer, and Object global variables from the request pairs.
  7. If the target user is nobody, invoke the perform_nobody_command command processor. Otherwise, invoke the perform_command command processor.
  8. If the command has not flushed the output buffer, flush the buffer back to the client.
  9. 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 */

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