Server plug-in API

his chapter describes the server plug-in application programming interface (API). It describes how to create and compile your plug-in functions and how to use the functions you create. It also lists the header files you need when programming your plug-in functions.

Note:
Before creating plug-in functions, you should be familiar with the server configuration files and the built-in functions. See Appendix B, "Server configuration files" for information on the configuration files called magnus.conf and obj.conf.
Of the systems the Netscape FastTrack and Enterprise servers support, the following systems can load functions into the server at run time and can therefore use plug-in functions:

A thread is a single independent stream of execution within a program. A critical section is a section of program code that must be executed by only one thread at a time. Portions of code that perform such tasks as switching between threads or updating a record in a database are critical sections. A condition variable is a thread-synchronization variable that lets the user specify arbitrary conditions on which to block.

What is the server plug-in API?

The server plug-in API is a set of functions and header files that help you create functions to use with the directives in server configuration files. The Netscape FastTrack and Enterprise servers use this API to build the regular functions for the directives used in both magnus.conf (the server configuration file) and obj.conf (the object configuration file). These regular functions are described in the Administrator's Guide.

The servers use this API, so by becoming familiar with the API, you can learn how the servers work. This means you can override the server functionality, add to it, or customize your own functions. For example, you can create functions that use a custom database for access control or you can create functions that create custom log files with special entries.

The following steps are a brief overview of the process for creating your plug-in functions:

  1. You write code for the plug-in functions you want to use. Each function you create is written specifically for the directive it will be used with in the configuration files.
  2. You compile your code to create a shared object file (.so file).
  3. In obj.conf you tell the server to load your shared object file.
  4. You use your plug-in functions in your server configuration file (obj.conf). Before you program your functions, you should understand how the server handles requests. The following section describes this process.

How the server handles client requests

When the server is started, it loads the magnus.conf file, which contains information the server uses to configure itself. For example, the file tells the server what port to bind with, what the server name is, what user account to use after start-up, and so on. You can find a sample of the magnus.conf file on page 67.

Once it has started running, the server waits for requests to come in from client applications (such as Netscape Navigator). When a request comes in, the server uses another file, obj.conf, to determine if and how it should service the request. You can find a sample obj.con file on page 69

For example, the obj.conf file tells the server whether a user has access to the document they're requesting, and if they have access, it determines (among other things) if the server should attach information to the document when it sends it to the client. This process is much more detailed, as described here andin the following sections.

The Netscape server responds to a client request by following a prescribed process in which each step is done once for all objects matching the request, then another step is done for all objects, and so on. For example, authorization translation (the first step) is performed on all objects in obj.conf, and then name translation (the second step) is done for all objects.

How the server uses directives and functions in an object

For more information on creating functions for these directives, see page 81.

The process steps are as follows:
  1. Authorization translation (AuthTrans class) Translate any authorization information sent by the client into a user and a group. If necessary, decode the message to get the actual request. Also, user authorization is available through user databases.

  2. Name translation (NameTrans class) A URL can simply be kept intact and retrieved as-is (the typical server case), or it can be translated into one of the following:

  3. Path checks (PathCheck class) Perform various tests on the resulting path. Typically, this is done to make sure that it's safe for the given client to retrieve the document (only for local access).

  4. Object types (ObjectType class) Identify the MIME type information for the given document. MIME types can be registered document types such as text/html and image/gif, or they can be internal document identification types.

    Internal types always begin with magnus-internal/, and are used to select a server function to use to decode the document. (Only used for local access; the server system calls these routines automatically when necessary.)

  5. Service (Service class) Select an internal server function to send the result back to the client. This function can be the normal server-service routine, it can return a custom document, or it can run a CGI program.

  6. Add Log (AddLog class) Record information about the transaction.

  7. Error Controls how the server responds to the client when it encounters an error.

    The first six of these steps map directly to six of the configuration directives allowed for each object. There is a seventh configuration directive (send-error) that controls how the server responds to the client when it encounters an error.

    If at any time one of these steps fails, another step must be taken to handle the error and inform the client about what happened. In this case, the Netscape server lets you customize the response that is sent with more site-specific information about the error.

What are server application functions?

To accomplish each of the request response steps, a set of server application functions (SAFs) are used in each step. These functions take the client's request and the server's configuration variables as input and return a response to the server as output. The Netscape FastTrack and Enterprise servers provide a set of built-in SAFs, and you can use the API to write your own plug-in SAFs

Each server application function belongs to a particular class that corresponds to the directive that is used to call it in the configuration files. The functions appear after the directive type in obj.conf. You use the format:

directive fn="ourfunction" value="v1" ... value ="vn"
The directive is the directive the function belongs to (for example, the AuthTrans directive). You can then send any number of optional values to your function as arguments you specify in obj.conf. For example, you could create a function for the NameTrans directive called myfunc1 that takes a URL path as a value and maps it to a hard-coded path specified in your function. The entry in obj.conf might look like this:

NameTrans fn="myfunc1" URLpath="/special/docs"
The response returned by the function can tell the server to do the following (REQ stands for request):

Configuring the server

When you use the Server Manager, the forms act as an interface to the configuration files. The changes you make in the Server Manager are saved in these configuration files. Although you can use these files to manually configure the server, you should use the Server Manager to configure your server.

You might need to manually configure the server for various reasons. If you accidentally lock your hosts out of the administrative forms or if you forget your administrative password, you'll have to manually change information in the server's configuration files.

Before you can edit any of the configuration files, you must have permission to read and write to the files. This probably means you need to log in as root. The magnus.conf, obj.conf, and mime.types files are kept in the config directory in your server root directory; the admpw file is in the admserv directory in your server root directory.

The magnus.conf file

The technical configuration file, called magnus.conf, controls all server operations not related to handling of documents or directories. (The obj.conf file handles these.) All of the items in magnus.conf are global and apply to the entire server, as opposed to affecting only one document or set of documents.

Every command line in the file has the format:

Directive Value

Sample magnus.conf file

# Sample magnus.conf file for Netscape server 2.0 
# The server's home--its root directory 
ServerRoot /usr/ns-home/https-server1 
# The server's name 
ServerName www.netscape.com 
# Which port? 
Port 443 
# This tells the server to get its objects from obj.conf, and use  
# the "default" object as the default. 
LoadObjects obj.conf 
RootObject default 
# The logfile for errors, and the file where it should keep 
# the pid of the master server process. 
ErrorLog /usr/ns-home/https-server1/logs/errors 
PidLog /usr/ns-home/https-server1/logs/pid 
# Which user should the server run as? This is the UNIX user 
# account name. 
User http 
# Processes - number of processes to spawn 
MaxProcs 1 
MinThreads 4 
MaxThreads 128 
# Use DNS? (turn this off for performance reasons) 
DNS on 
# Security directives: is security on, where is my keyfile, 
# which ciphers should I support (Ciphers directive is on the 
# US version only) 
Security on 
Keyfile ServerKey.db 
Certfile ServerCert
For the details about the directives, see "Directives in magnus.conf" on page 161.

The obj.conf file

The object configuration file, called obj.conf, uses objects to control how the server handles documents.

Objects (also referred to as resources) are settings that tell the server how to treat all documents, CGI programs, directories, imagemap files, and so on. You can define objects in two ways:

The structure of obj.conf

The obj.conf file must have four specific objects in it. You can add other objects to this file. To specify an object, you use the following format:

<Object ppath=wildcardpattern>
Directives

  <Client dns=wildcardpattern>
    Directives
   
  </Client>
</Object>
<Object name=cgi>
Directives

</Object>
You use wildcard patterns to control what is grouped in the object, or you use a name to create a template. You then specify one or more directives to control what the server does when it encounters anything that uses the template or that matches the wildcard pattern specified with ppath.

You can also set options for specific client hosts. This is a powerful feature because, unlike other servers where a host either can or cannot access a document, you make the server act differently for a client depending on the document they access. Although you don't need any <Client> sections in an object section, you can specify more than one--so the server acts differently based both on who requests something and what they request.

Directive syntax
Each directive line (regardless of where it appears) has the format:

Directive fn=function [parameter1=value1][parameterN=valueN]
Directive identifies an aspect of server operation. This string is case insensitive and must appear at the beginning of a line.

Function is a function and parameters given to the directive. Its format depends on the directive.

Comment lines begin with a # character with no leading white space. Directive lines can contain white space at the beginning of the line and between the directive and value, but trailing white space after the value might confuse the server. Long lines (which should only occur with the Init directive) can be continued with a space before the linefeed.

A sample object
The following sample object applies to the /user/public/ directory.

When the server receives a request for a document in this directory, it doesn't send the document. Instead, it denies the existence of any files or subdirectories and displays the "not found" error message.

<Object ppath="/user/public/*"> 
		PathCheck		fn=deny-existence 
		Service		fn=server-retrieve 
</Object>
The Service directive tells the server to get the documents by default.

Sample obj.conf file

# 
# Sample obj.conf file for Netscape server 2.0. 
# 
# This file was automatically generated by the server. 
# Edit at your own risk. 
# The default object. This is what the server uses if none of the other 
# objects fit. 
# 
# This one has a CGI directory specified in /usr/local/bin/cgi, 
# a directory mapping to a bigger disk in /gig-drive/sales, 
# and a document root of /usr/http-docs 
# 
# Initializations, such as log files and loading NSAPI libraries 
Init format.access="%Ses->client.ip% - %Req->vars.auth-user% [%SYSDATE%] 
\"%Req->reqpb.clf-request%\" %Req->srvhdrs.clf-status% %Req->srvhdrs.content-length%" 
fn="flex-init" access="/usr/ns-home/httpd-80/logs/access" 
Init fn=load-types mime-types=mime.types 
Init fn="dns-cache-init" cache-size="512" expire="1200" 
 
<Object name="default"> 
NameTrans from="/ns-icons" fn="pfx2dir" dir="/usr/ns-home/ns-icons" 
NameTrans from="/cgi-bin" dfn="pfx2dir" ir="/usr/local/bin/cgi" name="cgi" 
NameTrans from="/sales" fn="pfx2dir" dir="/gig-drive/sales" 
NameTrans root="/usr/http-docs" fn="document-root"  
PathCheck fn="unix-uri-clean" 
PathCheck fn="find-pathinfo" 
PathCheck index-names="index.html,home.html" fn="find-index"  
ObjectType fn="type-by-extension" 
ObjectType fn="force-type" type="text/plain" 
Service fn="imagemap" method="(GET|HEAD)" type="magnus-internal/imagemap" 
Service fn="index-common" method="(GET|HEAD)" type="magnus-internal/directory" 
Service fn="send-cgi" type="magnus-internal/cgi" 
Service fn="send-file" method="(GET|HEAD)" type="*~magnus-internal/*" 
AddLog fn="flex-log" name="access" 
</Object> 
 
# All CGI directories have these configuration options set 
<Object name="cgi"> 
ObjectType fn="force-type" type="magnus-internal/cgi" 
Service fn="send-cgi" 
</Object> 

Required objects for obj.conf

There are certain objects that must be in the obj.conf file to make the Administration forms work for your server. These functions control local file access and CGI execution.

The following sections describe the objects that must be in obj.conf.

The default object
<Object name="default"> 
NameTrans fn="pfx2dir" from="/mc-icons" dir="/usr/home/mc-icons" 
NameTrans fn="document-root" root="/usr/http-docs" 
PathCheck fn="unix-uri-clean" 
PathCheck fn="find-pathinfo" 
ObjectType fn="type-by-extension" 
ObjectType fn="force-type" type="text/plain" 
Service fn="imagemap" method="(GET|HEAD)"  
        type="magnus-internal/imagemap" 
Service fn="index-common" method="(GET|HEAD)"  
        type="magnus-internal/directory" 
Service fn="send-file" method="(GET|HEAD)"  
        type="*~magnus-internal/*" 
</Object>
CGI Object
This object controls the admin form handler scripts and should read exactly as follows:

  <Object name="cgi"> 
  ObjectType fn="force-type" type="magnus-internal/cgi" 
  Service fn="send-cgi" 
  </Object>

Programming plug-in functions

This section describes how to begin programming your plug-in functions. This section also describes the header files you need to include in your code. See the section "Compiling your code" on page 88 for additional information.

The server root directory has a subdirectory called /nsapi that contains sample code, the header files, and a makefile. You should familiarize yourself with the code and samples. This documentation is written as a starting point for exploring that code.

The hierarchy of server plug-in API header filest

The server and its header files are written in ANSI C. On some systems, such as Windows NT, you must have an import list that specifies all global variables and functions you need to access from the server binary.

The server plug-in API header files

This section describes the header files you can include when programming your plug-in functions. This section is intended as a starting point for learning the functions included in the header files.

Header files are stored in two directories:

Header files in the base directory

Header File

Description

buffer.h

Contains functions that buffer I/O for a file or a socket descriptor.

crit.h

Contains functions for managing critical sections, an abstraction that facilitates the management of threaded servers.

cinfo.h

Contains functions for object typing, specifically mapping files to MIME types.

daemon.h

Contains functions called from other header files. It also contains functions that manage group processes that run the server.

ereport.h

Contains functions that handle low-level errors.

file.h

Contains functions to handle file I/O.

net.h

Contains functions for I/O with the client software over the network.

netsite.h

Contains miscellaneous functions and some vital definitions. Be sure to include this in all your .c files, to make sure that the necessary definitions (#defines) are established.

pblock.h

Contains functions that manage parameter passing and server internal variables. It also contains functions to get values from a user via the server.

sem.h

Contains semaphores in platform-independent ways (they prevent two processes from doing the same thing).

session.h

Contains session data structures for IP addresses, security, and so on.

shexp.h

Contains functions to customize wildcard patterns through parsed data.

systems.h

Contains functions that handle systems information.

util.h

Contains utility functions.

Header files in the frame directory

Header File

Description

conf.h

Contains functions to access magnus.conf (for example, to get port number, internal global variables).

func.h

Contains data structures. This file is rarely used.

http.h

Contains functions for the HTTP protocol. Most of these functions are called from functions in protocol.h.

log.h

Contains functions for logging errors.

object.h

Contains functions for reading and writing obj.conf. You'll rarely use these functions.

objset.h

Contains functions for reading and writing obj.conf. You'll rarely use these functions.

protocol.h

Contains functions that perform protocol-specific actions.

req.h

Contains request data structures.

Getting data from the server: the parameter block

The server stores variables in name-value pairs. The parameter block, or pblock, is a hash table keyed on the name string. The pblock maps these name strings onto their value character strings.

Basically, your plug-in functions use parameter blocks to get, change, add, and remove name-value pairs of data. In order to use the functions to do these actions, you need to know a bit about how the hash table is formed and how the data structures are managed.

The pb_param structure is used to manage the name-value pairs for each client request. The pb_entry structure creates linked lists of pb_param structures. See "The Session data structure" on page 155

Passing parameters to server application functions

All server application functions (regardless of class) are described by the following prototype:

int function(pblock *pb, Session *sn, Request *rq);

pb is the parameter block containing the parameters given by the site administrator for this function invocation.

Caution!
This parameter should be considered read-only, and any data modification should be performed on copies of the data. Doing otherwise is unsafe in threaded server architectures, and will yield unpredictable results in multiprocess server architectures.

Public functions

When adding, removing, editing, and creating name-value pairs, you use the following functions. This list might seem overwhelming, but you'll use only a handful of these functions in your plug-in functions. For a more detailed look, see Chapter 5, "Server Plug-in API function definitions".

The param_create function creates a parameter with the given name and value. If the name and value aren't null, they are copied and placed into the new pb_param structure.

The param_free function frees a given parameter if it's non-NULL. It returns 1 if the parameter was non-NULL, and returns 0 if the parameter was NULL. This is also useful for error checking before using the pblock_remove function.

The pblock_create function creates a new parameter block with a hash table of a chosen size. It returns the newly allocated parameter block.

The pblock_free function frees a given parameter block and any entries inside it.

The pblock_find function finds the name-value entry with the given name in a given parameter block. If it's successful, it returns the parameter block. If not, it returns NULL. You then use the pblock_findval function to get the actual value in the name-value pair.

The pblock_findval function finds the name-value entry with a given name in a given parameter block, and returns its value; otherwise, it returns NULL.

The pblock_remove function behaves like the pblock_find function, but when it finds the given parameter block, it removes it.

The pblock_nninsert and pblock_nvinsert functions both create a new parameter with a given name and value, and insert it into a given parameter block. The name and value in the parameter are also newly allocated. The pblock_nninsert function requires that the value be an integer, but the pblock_nvinsert function accepts a string. Both functions return the parameter that was allocated (in case you need it).

The pblock_pinsert function inserts a parameter into a parameter block.

The pblock_str2pblock function scans the given string for parameter pairs in the format name=value or name="value". This function returns the number of parameters added to the table, or it returns -1 if it encounters an error.

The pblock_pblock2str function places all of the parameters in the given parameter block into the given string (NULL if it needs creation). It reallocates more space for the string. Each parameter is of the form name="value" and is separated by a space from any adjacent parameter.

Data structures and data access functions

The data structures are Session (see "The Session data structure" on page 155) and Request (see "The Request data structure" on page 157). The data access function is request_header.

The Request->vars parameter block contains the server's working variables. The set of active variables is different depending on which step of the request the server is processing, as discussed on page 81.

The Request->reqpb parameter block contains the request parameters that are sent by the client:

The request_header function
The request_header function finds the parameter block that contains the client's HTTP headers.

#include "frame/req.h"
int request_header(char *name, char **value, Session *sn, Request *rq);
The name parameter should be the lowercase header name string to look for, and value is a pointer to your char * that should contain the header. If no header with the given name was sent, value is set to NULL.

The Request->srvhdrs parameter block is the set of HTTP headers for the server to send back. This parameter block can be modified by any function.

The last three entries in the Request structure should be considered transparent to application code because they are used by the server's base code.

After the server has a path for the file it intends to return, application functions should use the following call to obtain stat( ) information about the file:

 
#include "frame/req.h" 
 
/* 
* request_stat_path tries to stat path. If path is NULL, it will look in 
* the rq->vars pblock for "path". If the stat is successful, it returns the 
* stat structure. If not, returns NULL and copies an error message into err.  
* If a previous call to this function was successful, and path is the same, 
* the function will simply return the previously found value. 
* 
	* Application functions should not free this structure. 
	*/ 
 
struct stat *request_stat_path(char *path, char *err, Request *rq);
Using this function avoids multiple, unnecessary calls to the stat function.

Application function return codes

When your plug-in function is done working with the name-value pairs, it must return a status code that tells the server how to proceed with the request. These integer return codes are discussed on page 66.

Reporting errors to the server

When problems occur, the server application functions should set server status codes that give the client an idea of what went wrong. The function should also log an error in the server error log file.

There are two interfaces for reporting errors: setting a response code and reporting an error.

Setting a response status code
The protocol_status function sets the status to the code and reason string. If the reason is NULL, the server attempts to match a string with the given status code (see Table 5.5). If it can't find a string, it uses "Unknown error"

#include "frame/protocol.h" 
void protocol_status(Session *sn, Request *rq, int n, char *r);
Generally, protocol_status will be called with a NULL reason string, and one of the following status codes defined in the protocol.h file. (If no status is set, the default is PROTOCOL_SERVER_ERROR.)

Status codes used with protocol_status

Status Code

Definition

PROTOCOL_OK

Normal status, the request will be fulfilled normally. This should only be set by Service class functions.

PROTOCOL_REDIRECT

The client should be directed to a new URL, which your function should insert into the rq->vars parameter block as url.

PROTOCOL_NOT_MODIFIED

If the client gave a conditional request, such as an HTTP request with the if-modified-since header, then this indicates that the client should use its local copy of the data.

PROTOCOL_BAD_REQUEST

The request was unintelligible. Used primarily in the framework library.

PROTOCOL_UNAUTHORIZED

The client did not give sufficient authorization for the action it was trying to perform. A WWW-authenticate header should be present in the rq->srvhdrs parameter block that indicates to the client the level of authorization it needs to perform its action.

PROTOCOL_FORBIDDEN

The client is explicitly forbidden to access the object and should be informed of this fact.

PROTOCOL_NOT_FOUND

The server was unable to locate the item requested.

PROTOCOL_SERVER_ERROR

Some sort of server-side error has occurred. Possible causes include misconfiguration, resource unavailability, and so on. Any error unrelated to the client generally falls under this rather broad category.

PROTOCOL_NOT_IMPLEMENTED

The client has asked the server to perform an action that it knows it cannot do. Generally, you would use this to indicate your refusal to implement an HTTP feature.

Error reporting
When errors occur, it's customary to report them in the server's error log file. To do this, your plug-in functions should call log_error. This logs an error and then returns to tell you if the log was recorded successfully (a return value of 0 means success, -1 means failure).

#include "frame/log.h" 
 
/* 
* log_error formats the arguments with the printf() style fmt.   
 * Returns whether the log was successful. 
 * It also records the current date. 
 * sn and rq are optional parameters. If specified, information 
 * about the client is reported. 
*/ 
int log_error(int degree, char *func, Session *sn, Request *rq,  
			char *fmt, ...);
You can give log_error any printf( ) style string to describe the error. If an error occurs after a system call, use the following function to translate an error number to an error string:

#include "base/file.h" 
char *system_errmsg(SYS_FILE fd);
Note:
The fd parameter is vestigial under Unix and might need to be changed for other operating systems. Therefore, it is best to set fd to zero.

Conditions for each function class

The following sections list the special conditions that exist in the variables of the Request structure, and the return codes for each class of server function.

AuthTrans

Authorization is split into two steps. First, the authorization data sent by the client (if any) is decoded and verified against internal databases. If the data is valid, it should be stored into the Request->vars parameter block. In the second step, this data is compared against the required authorization for the requested path (performed in a PathCheck class function).

There is no reason that both of these steps could not be performed in one AuthTrans or one PathCheck directive; however, the steps are split for flexibility and for possible future support the SHTTP protocol.

There are no variables active in the Request->vars parameter block upon entry to an AuthTrans class function. Upon successful translation and function exit, the following variables are customarily set:

NameTrans

The NameTrans class functions are used to translate a virtual path sent by the client into a physical path as used by the server.

The following variables are active in the Request->vars parameter block for NameTrans class functions:

PathCheck

The PathCheck class functions are used to verify that a given path is safe to return to a given client. This class of functions performs various system-specific URL filtering and screening actions, authorization checks, access control, searches for CGI extra path information, and other functions.

The system only defines one variable for PathCheck class functions in Request->vars parameter block: the value of path is the path resulting from the execution of all NameTrans directives. Any variables that have been created by previous NameTrans or AuthTrans directives are also active.

On return from a PathCheck class function, a code other than REQ_ABORTED is considered a success.

ObjectType

The ObjectType class functions take the path resulting from the previous directives and locate a file system object for the path. If none exists and none of the PathCheck directives have looked yet, the current ObjectType routines return a default type to the client.

The ObjectType directives receive no special variables aside from path in Request->vars and the variables defined by the previous directives. Upon return from an ObjectType class function, a code other than REQ_ABORTED indicates success.

The server's base functionality library provides mechanisms for typing files natively to the machine's operating system. Under most systems, this consists of file name extension recognition code.

Service

The Service class functions send the server's reply to the client. The function is generally selected according to the directory the file resides in or the type of the file being sent. Most files are simply mapped into memory and sent to the client. Some types of documents run as system programs and others are parsed before being sent to the client.

Service directives receive no special variables aside from path in Request->vars and the variables defined by the previous directives.

Upon return from a Service class function, the REQ return codes have the following meaning:

Initialization functions

This special Init class of server application functions initialize static data for the server to use on startup. These functions are called by the base server daemon process, and their data, sockets, or file descriptors are inherited by child processes or threads. This class of function initializes the logging, file typing, and some of the name translation functions.

Init functions receive their parameters just like the other functions, except that the Session and Request parameters are NULL. The parameter pblock is filled with information from the server's technical configuration file (obj.conf).

Static data should be limited to read-only data if possible. Other mediums will need to employ some method of locking to ensure that only one process or thread is accessing the data at a time. The routines in the base/sem.h header file are used to coordinate processes, and those in the base/crit.h file are used to coordinate threads.

Upon failure, plug-in Init-class functions should return REQ_ABORTED and insert into their parameter block a variable named error that contains a string describing the error. Any other return code is considered success.

If the server is restarted, these modules might need to be terminated before they are started again by the server code. For this purpose, the server provides the following function:

void magnus_atrestart(void (*fn)(void *), void *data)
The given callback fn is called by the server when the server is restarted. The data pointer is given to the function as its argument.

Note:
Netscape does not call the termination callbacks upon server termination, only upon restart.

Threading

In addition to the NT servers being multithreaded, all of the 2.0 UNIX Web servers are multithreaded. There are new abstractions in base/systhr.h and base/crit.h for threads, critical sections, and condition variables.

The UNIX servers are still capable of running in multiple-process mode. Each process then has a pool of threads that it uses to answer requests. If your server plug-in modules are not thread-safe, you can run the server with one thread per process to simulate the 1.1 server architecture; however this can result in a performance loss. You can run one process with multiple threads to simplify programming. This lets you share context without using shared memory. However, high traffic sites that use CGI should use several processes (2-8) with fewer maximum threads per process. For example, if you normally run one process with 128 threads, run 4 processes with 32 maximum threads each if your site serves a very high volume. This avoids large virtual address spaces for a single process.

New thread manipulation calls exist in base/systhr.h. The following functions can manipulate threads:

Caching

The 2.0 server attempts to cache requests that will create the same response when requested by multiple clients at different times. That is, if a client requests /foo/bar/baz.txt, and then another client requests /foo/bar/baz.txt, the server's response will be the same as long as /foo/bar/baz.txt doesn't change between the requests. When the server can avoid calling the NSAPI functions for a request, it can return the responses faster.

There is a new variable called directive_is_cachable in the Request structure in 2.0. By default, this variable is set to 0 when calling your NSAPI functions. If you do not set this variable to 1 before your function returns, the server will not try to cache the request, and each subsequent request will call your function again. If you set it to 1, the server may not call your function when the next client makes the same request.

You should cache requests that do not depend on some aspect of the client to determine how they are returned. If your function performs access control, logging, switches data based on user-agent, or anything similar, you should not mark your directive as cachable. If your function is doing something which does not depend on the client IP address or the headers the client sends, then you can mark your directive as cachable.

Software multihoming

Netscape Navigator 2.0 and some other browsers now send a Host: header telling the server which URL host and port is being accessed. In addition to the 1.1 functionality of making your obj.conf directives conditional on the client's IP address, you can also specify that they only be executed when the URL host is a specific pattern. For example,

<Client urlhost=www.(any|such).com> NameTrans fn=my-nametrans from=/anysuchdir </Client>

placed in the default object will call my-nametrans only when the browser thinks it is accessing www.any.com or www.auch.com. Similarly,

<Client urlhost=*~www.(any|such).com> NameTrans fn=my-nametrans from=/anysuchdir </Client>

will apply my-nametrans to everyone except those who access www.any.com and www.such.com including browsers that don't send the Host: header.

Compiling your code

This section lists the linking options you need to use in order to create a Unix shared object that the server can load from magnus.conf.

See the makefile in the /nsapi/include directory.

The following table describes the commands used to link object files into a shared object under the various Unix platforms. In these examples, the compiled object files t.o and u.o are linked to form a shared object called test.so.
Linking options

System

Compile options

IRIX

ld -shared t.o u.o -o test.so

SunOS

ld -assert pure-text t.o u.o -o test.so

Solaris

ld -G t.o u.o -o test.so

OSF/1

ld -all -shared -expect_unresolved "*" t.o u.o -o test.so

HP-UX

ld -b t.o u.o -o test.so

When compiling your code, you must also use the +z flag to the HP C compiler.

AIX

cc -bM:SRE -berok t.o u.o -o test.so -bE:ext.exp -lc

The ext.exp file must be a text file with the name of a function that is externally accessible for each line.

Loading your shared object

After you've compiled your code, you need to tell the server to load the shared object and its functions so that you can begin using your plug-in functions in obj.conf.

When the server starts, it uses obj.conf to get its configuration information. To tell the server to load your shared object and functions in the shared object, you add the following line to obj.conf:

Init fn=load-modules shlib=[path]filename.so  
funcs="function1,function1,...,functionN"
This initialization function opens the given shared object file and loads the functions function1, function2, and so on. You then use the functions function1 and function2 in the server configuration files (either magnus.conf or obj.conf). Remember to use the functions only with the directives you programmed them for, as described in the following section.

Using your plug-in functions

This section describes how to use your functions in the server configuration files. All functions are called as follows:

Directive fn=function [name1=value1] ... [nameN=valueN]