Table of Contents | Previous | Next | Index | Bookshelf

NSAPI Programmer's Guide for iPlanet Web Server


Chapter 4
Creating Custom SAFs

This chapter describes how to write your own NSAPI plugins that define custom Server Application Functions (SAFs). Creating plugins allows you to modify or extend the iPlanet Web Server's built-in functionality. For example, you can modify the server to handle user authorization in a special way or generate dynamic HTML pages based on information in a database.

The sections in this chapter are:

Before writing custom SAFs, you should familiarize yourself with the request handling process, as described in Chapter 1, "Basics of Server Operation." Also, before writing a custom SAF, check if a built-in SAF already accomplishes the tasks you have in mind. See Chapter 3, "Predefined SAFs and the Request Handling Process," for a list of the pre-defined SAFs.

For a complete list of the NSAPI routines for implementing custom SAFs, see Chapter 5, "NSAPI Function Reference."

The SAF Interface

All SAFs (custom and built-in) have the same C interface regardless of the request-handling step for which they are written. They are small functions designed for a specific purpose within a specific request-response step. They receive parameters from the directive that invokes them in the obj.conf file, from the server, and from previous SAFs.

Here is the C interface for a SAF:

int function(pblock *pb, Session *sn, Request *rq);
The next section discusses the parameters in detail.

The SAF returns a result code which indicates whether and how it succeeded. The server uses the result code from each function to determine how to proceed with processing the request. See the section "Result Codes" for details of the result codes.

SAF Parameters

This section discusses the SAF parameters in detail. The parameters are:

pb (parameter block)

The pb parameter is a pointer to a pblock data structure that contains values specified by the directive that invokes the SAF. A pblock data structure contains a series of name/value pairs.

For example, a directive that invokes the basic-nsca function might look like:

AuthTrans fn=basic-ncsa auth-type=basic 
dbm=/netscape/server4/userdb/rs
In this case, the pb parameter passed to basic-ncsa contains name/value pairs that correspond to auth-type=basic and dbm=/netscape/server4/userdb/rs.

NSAPI provides a set of functions for working with pblock data structures. For example, pblock_findval() returns the value for a given name in a pblock. See "Parameter Block Manipulation Routines" for a summary of the most commonly used functions for working with parameter blocks.

sn (session)

The sn parameter is a pointer to a Session data structure. This parameter contains variables related to an entire session (that is, the time between the opening and closing of the TCP/IP connection between the client and the server). The same sn pointer is passed to each SAF called within each request for an entire session. The following list describes the most important fields in this data structure.

(See Chapter 5, "NSAPI Function Reference," for information about NSAPI routines for manipulating the Session data structure):

rq (request)

The rq parameter is a pointer to a request data structure. This parameter contains variables related to the current request, such as the request headers, URI, and local file system path. The same request pointer is passed to each SAF called in the request-response process for an HTTP request.

The following list describes the most important fields in this data structure (See Chapter 5, "NSAPI Function Reference," for information about NSAPI routines for manipulating the Request data structure).

The rq parameter is the primary mechanism for passing along information throughout the request-response process. On input to a SAF, rq contains whatever values were inserted or modified by previously executed SAFs. On output, rq contains any modifications or additional information inserted by the SAF. Some SAFs depend on the existence of specific information provided at an earlier step in the process. For example, a PathCheck SAF retrieves values in rq->vars which were previously inserted by an AuthTrans SAF.

Result Codes

Upon completion, a SAF returns a result code. The result code indicates what the server should do next. The result codes are:

Creating and Using Custom SAFs

Custom SAFs are functions in shared libraries that are loaded and called by the server. Follow these steps to create a custom SAF:

  1. Write the Source Code
  2. using the NSAPI functions. Each SAF is written for a specific directive.

  3. Compile and Link
  4. the source code to create a shared library (.so, .sl, or .dll) file.

  5. Load and Initialize the SAF
  6. by editing the obj.conf file to:
    -- Load the shared library file containing your custom SAF(s).
    -- Initialize the SAF if necessary.

  7. Instruct the Server to Call the SAFs
  8. by editing obj.conf to call your custom SAF(s) at the appropriate time.

  9. Stop and Start the Server.
  10. Test the SAF
  11. by accessing your server from a browser with a URL that triggers your function.
The following sections describe these steps in greater detail.

Write the Source Code

Write your custom SAFs using NSAPI functions. For a summary of some of the most commonly used NSAPI functions, see the section "Overview of NSAPI C Functions." Chapter 5, "NSAPI Function Reference," provides information about all of the routines available.

For examples of custom SAFs, see nsapi/examples/ in the server root directory and also see Chapter 6, "Examples of Custom SAFs."

The signature for all SAFs is:

int function(pblock *pb, Session *sn, Request *rq);
For more details on the parameters, see the section "SAF Parameters."

The iPlanet Web Server runs as a multi-threaded single process. On Unix platforms there are actually two processes (a parent and a child) for historical reasons. The parent process performs some initialization and forks the child process. The child process performs further initialization and handles all the HTTP requests.

Keep these things in mind when writing your SAF. Write thread-safe code. Blocking may affect performance. Write small functions with parameters and configure them in obj.conf. Carefully check and handle all errors. Also log them so that you can determine the source of problems and fix them.

If necessary, write an initialization function that performs initialization tasks required by your new SAFs. The initialization function has the same signature as other SAFs:

int function(pblock *pb, Session *sn, Request *rq);
SAFs expect to be able to obtain certain types of information from their parameters. In most cases, parameter block (pblock) data structures provide the fundamental storage mechanism for these parameters A pblock maintains its data as a collection of name-value pairs. For a summary of the most commonly used functions for working with pblock structures, see "Parameter Block Manipulation Routines."

When defining a SAF, you do not specifically state which directive it is written for. However, each SAF must be written for a specific directive (such as Init, AuthTrans, Service and so on). Each directive expects its SAFs to do particular things, and your SAF must conform to the expectations of the directive for which it was written. For details of what each directive expects of its SAFs, see the section "Required Behavior of SAFs for Each Directive."

Compile and Link

Compile and link your code with the native compiler for the target platform. For Windows NT, use Microsoft Visual C++ 6.0 or newer when compiling for iPlanet Web Server 4.x. You must have an import list that specifies all global variables and functions to access from the server binary. Use the correct compiler and linker flags for your platform. Refer to the example Makefile in the nsapi/examples directory. On Windows NT link to nshttpd3x.lib or nshttpd40.lib as appropriate in the plugins/lib directory.

The include directory in the server-root directory in Enterprise Server 3.x or in server-root/plugins in iPlanet Web Server 4.x contains the NSAPI header file. All the NSAPI header information is now contained in one file called nsapi.h.

New in iPlanet Web Server 4.0: For AIX only, plugins built for 3.x versions of the server must be relinked to work with 4.x versions. The files you need, which are in the server_root/plugins/nsapi/examples/ directory, are as follows:

iPlanet Web Server 4.x versions are built on AIX 4.2, which natively supports runtime-linking. Because of this, NSAPI plugins, which reference symbols in the ns-httpd main executable, must be built with the -G option, which specifies that symbols must be resolved at runtime.

Previous versions of Netscape Enterprise Server, however, were built on AIX 4.1, which did not support native runtime-linking. Enterprise Server had specific additional software (provided by IBM AIX development to Netscape) to enable plugins. No special runtime-linking directives were required to build plugins. Because of this, plugins that have been built for previous server versions on AIX will not work with iPlanet Web Server 4.x versions as they are.

However, they can easily be relinked to work with iPlanet Web Server 4.x versions. The relink_36plugin script relinks existing plugins. Only the existing plugin itself is required for the script; original source and .o files are not needed. More specific comments are in the script itself. Since all AIX versions from 4.2 onward natively support runtime-linking, no plugins for iPlanet Web Server versions 4.x and later will need to be relinked.

Load and Initialize the SAF

For each shared library (plugin) containing custom SAFs to be loaded into the iPlanet Web Server, add an Init directive that invokes the load-modules SAF to obj.conf.

The syntax for a directive that calls load-modules is:

Init fn=load-modules shlib=[path]sharedlibname funcs="SAF1,...,SAFn"
For example, if you created a shared library animations.so that defines two SAFs do_small_anim() and do_big_anim() and also defines the initialization function init_my_animations, you would add the following directive to load the plugin:

Init fn=load-modules shlib=[path]animations.so funcs="do_small_anim,do_big_anim,init_my_animations"
If necessary, also add an Init directive that calls the initialization function for the newly loaded plugin. For example, if you defined the function init_my_new_SAF() to perform an operation on the maxAnimLoop parameter, you would a directive such as the following to obj.conf:

Init fn=init_my_animations maxAnimLoop=5

Instruct the Server to Call the SAFs

Next, add directives to obj.conf to instruct the server to call each custom SAF at the appropriate time. The syntax for directives is:

Directive fn=function-name [name1="value1"]...[nameN="valueN"]

Depending on what your new SAF does, you might need to add just one directive to obj.conf or you might need to add more than one directive to provide complete instructions for invoking the new SAF.

For example, if you define a new AuthTrans or PathCheck SAF you could just add an appropriate directive in the default object. However, if you define a new Service SAF to be invoked only when the requested resource is in a particular directory or has a new kind of file extension, you would need to take extra steps.

If your new Service SAF is to be invoked only when the requested resource has a new kind of file extension, you might need to add an entry to the MIME types file so that the type value gets set properly during the ObjectType stage. Then you could add a Service directive to the default object that specifies the desired type value.

If your new Service SAF is to be invoked only when the requested resource is in a particular directory, you might need to define a NameTrans directive that generates a name or ppath value that matches another object, and then in the new object you could invoke the new Service function.

For example, suppose your plugin defines two new SAFs, do_small_anim() and do_big_anim() which both take speed parameters. These functions run animations. All files to be treated as small animations reside in the directory D:/Netscape/server4/docs/animations/small, while all files to be treated as full screen animations reside in the directory D:/Netscape/server4/docs/animations/fullscreen.

To ensure that the new animation functions are invoked whenever a client sends a request for either a small or fullscreen animation, you would add NameTrans directives to the default object to translate the appropriate URLs to the corresponding pathnames and also assign a name to the request.

NameTrans fn=pfx2dir from="/animations/small" 
dir="D:/Netscape/server4/docs/animations/small" name="small_anim"
NameTrans fn=pfx2dir from="/animations/fullscreen" 
dir="D:/Netscape/server4/docs/animations/fullscreen"
name="fullscreen_anim"
You also need to define objects that contain the Service directives that run the animations and specify the speed parameter.

<Object name="small_anim">
Service fn=do_small_anim speed=40
</Object>
<Object name="fullscreen_anim">
Service fn=do_big_anim speed=20
</Object>

Stop and Start the Server

After modifying obj.conf, you need to start and stop the server. On Unix you may execute the shell scripts stop and start in the servers home directory. Do not use restart on Unix since the server will not reload your shared library after it has been loaded once.

On Windows NT you may use the Services Control Panel to stop and start the server. Once you have started the server with your shared library, you'll have to stop it before you can build your shared library again.

You can also use the Server Manager interface to re-load obj.conf and to start and stop the server.

If there are problems during startup, check the error log.

Test the SAF

Test your SAF by accessing your server from a browser with a URL that triggers your function. For example, if your new SAF is triggered by requests to resources in http://server-name/animations/small, try requesting a valid resource that starts with that URI.

You should disable caching in your browser so that the server is sure to be accessed. In Navigator you may hold the shift key while clicking the Reload button to ensure that the cache is not used. (Note that the shift-reload trick does not always force the client to fetch images from source if the images are already in the cache.)

You may also wish to disable the server cache using the cache-init SAF.

Examine the access log and error log to help with debugging.

Overview of NSAPI C Functions

NSAPI provides a set of C functions that are used to implement SAFs. They serve several purposes. They provide platform-independence across Netscape Server operating system and hardware platforms. They provide improved performance. They are thread-safe which is a requirement for SAFs. They prevent memory leaks. And they provide functionality necessary for implementing SAFs. You should always use these NSAPI routines when defining new SAFs.

This section provides an overview of the function categories available and some of the more commonly used routines. All the public routines are detailed in Chapter 5, "NSAPI Function Reference."

The main categories of NSAPI functions are:

Parameter Block Manipulation Routines

The parameter block manipulation functions provide routines for locating, adding, and removing entries in a pblock data structure include:

Protocol Utilities for Service SAFs

Protocol utilities provide functionality necessary to implement Service SAFs:

Memory Management

Memory management routines provide fast, platform-independent versions of the standard memory management routines. They also prevent memory leaks by allocating from a temporary memory (called "pooled" memory) for each request and then disposing the entire pool after each request. There are wrappers for standard memory routines for using permanent memory. To disable pooled memory for debugging, see the built-in SAF pool-init in Chapter 3, "Predefined SAFs and the Request Handling Process."

File I/O

The file I/O functions provides platform-independent, thread-safe file I/O routines.

Network I/O

Network I/O functions provide platform-independent, thread-safe network I/O routines. These routines work with SSL when it's enabled.

Threads

Thread functions include functions for creating your own threads which are compatible with the server's threads. There are also routines for critical sections and condition variables.

Utilities

Utility functions include platform-independent, thread-safe versions of many standard library functions (such as string manipulation) as well as new utilities useful for NSAPI.

Required Behavior of SAFs for Each Directive

When writing a new SAF, you should define it to do certain things, depending on which stage of the request handling process will invoke it. For example, SAFs to be invoked during the Init stage must conform to different requirements than SAFs to be invoked during the Service stage.

The rq parameter is the primary mechanism for passing along information throughout the request-response process. On input to a SAF, rq contains whatever values were inserted or modified by previously executed SAFs. On output, rq contains any modifications or additional information inserted by the SAF. Some SAFs depend on the existence of specific information provided at an earlier step in the process. For example, a PathCheck SAF retrieves values in rq->vars which were previously inserted by an AuthTrans SAF.

This section outlines the expected behavior of SAFs used at each stage in the request handling process.

Init SAFs

AuthTrans SAFs

NameTrans SAFs

PathCheck SAFs

ObjectType SAFs

Service SAFs

Error SAFs

AddLog SAFs

CGI to NSAPI Conversion

You may have a need to convert a CGI into a SAF using NSAPI. Since the CGI environment variables are not available to NSAPI, you'll retrieve them from the NSAPI parameter blocks. The table below indicates how each CGI environment variable can be obtained in NSAPI.

Keep in mind that your code must be thread-safe under NSAPI. You should use NSAPI functions which are thread-safe. Also, you should use the NSAPI memory management and other routines for speed and platform independence.

Table 4.1 
CGI getenv() NSAPI

AUTH_TYPE

pblock_findval("auth-type", rq->vars);

AUTH_USER

pblock_findval("auth-user", rq->vars);

CONTENT_LENGTH

pblock_findval("content-length", rq->srvhdrs);

CONTENT_TYPE

pblock_findval( content-type", rq->srvhdrs);

GATEWAY_INTERFACE

"CGI/1.1"

HTTP_*

pblock_findval( "*", rq->headers); (* is lower-case, dash replaces underscore)

PATH_INFO

pblock_findval("path-info", rq->vars);

PATH_TRANSLATED

pblock_findval( path-translated", rq->vars);

QUERY_STRING

pblock_findval( query", rq->reqpb); (GET only, POST puts query string in body data)

REMOTE_ADDR

pblock_findval("ip", sn->client);

REMOTE_HOST

session_dns(sn) ? session_dns(sn) : pblock_findval("ip", sn->client);

REMOTE_IDENT

pblock_findval( "from", rq->headers); (not usually available)

REMOTE_USER

pblock_findval("auth-user", rq->vars);

REQUEST_METHOD

pblock_findval("method", req->reqpb);

SCRIPT_NAME

pblock_findval("uri", rq->reqpb);

SERVER_NAME

char *util_hostname();

SERVER_PORT

conf_getglobals()->Vport; (as a string)

SERVER_PROTOCOL

pblock_findval("protocol", rq->reqpb);

SERVER_SOFTWARE

MAGNUS_VERSION_STRING

Netscape specific:

CLIENT_CERT

pblock_findval("auth-cert", rq->vars)

HOST

char *session_maxdns(sn); (may be null)

HTTPS

security_active ? "ON" : "OFF";

HTTPS_KEYSIZE

pblock_findval("keysize", sn->client);

HTTPS_SECRETKEYSIZE

pblock_findval("secret-keysize", sn->client);

QUERY

pblock_findval( query", rq->reqpb); (GET only, POST puts query string in entity-body data)

SERVER_URL

http_uri2url_dynamic("","", sn, rq);


Table of Contents | Previous | Next | Index | Bookshelf

Last Updated: 03/01/00 09:22:11

© Copyright © 2000 Sun Microsystems, Inc. Some preexisting portions Copyright © 2000 Netscape Communications Corp. All rights reserved.