Note:
Before creating plug-in functions, you should be familiar with the server configuration files and the built-in functions. See Chapter 4, "Server operation" and Appendix B, "Server configuration files".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:
|
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 create the functions for the directives used in both magnus.conf
(the server configuration file) and obj.conf
(the object configuration file). These built-in functions are described in Appendix B.
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 own plug-in functions:
.so
file).
obj.conf
, you tell the server to load your shared object file.
obj.conf
).
nsapi/examples/
directory contains C files with examples for each class of function you can create.
nsapi/include/
directory contains all the header files you need to include when writing your plug-in functions.
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.
nsapi/include/base
contains header files that deal with low-level, platform independent functions such as memory, file, and network access.
nsapi/include/frame
contains header files of functions that deal with server and HTTP-specific functions such as handling access to configuration files and dealing with HTTP.
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 193.
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
The pb 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.
Parameter-manipulating 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 6, "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 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.
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.
The pblock_findval function finds the value portion of a name-value entry with a given name in a given parameter block, and returns its value.
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 pblock_nninsert function requires that the value be an integer, but the pblock_nvinsert function accepts a string.
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".
The pblock_pblock2str function places all of the parameters in the given parameter block into the given 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 193) and Request (see "The Request data structure" on page 195). 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 80.
The Request->reqpb
parameter block contains the request parameters that are sent by the client:
The Request->headers
parameter block contains the client's HTTP headers. HTTP sends any number of headers in the form (RFC 822):
If more than one header has the same name, then they are concatenated
with commas as follows:
Name: value
Name: value1, value2
The parameter block is keyed on the fully lowercase version of the name string without the colon.
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 request_stat_path function to obtain stat information about the file. This avoids multiple, unnecessary calls to the stat function.
Application function status codes
When your plug-in function is done working with the name-value pairs, it must return a code that tells the server how to proceed with the request. These integer status codes are discussed on page 99.
Reporting errors to the server
When problems occur, server application functions should set an HTTP response status code to give the client an idea of what went wrong. The function should also log an error in the error log file.
There are two ways of reporting errors: setting a response status code and reporting an error.
Setting an HTTP 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.3). 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 (or the code is set as NULL), the default is PROTOCOL_SERVER_ERROR.)
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"
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 and might need to be changed for operating
systems other than Unix and Windows NT. Therefore, it is best to set fd to zero.
Compiling and linking your code
You can compile your code with any ANSI C compiler. See the makefile in the /nsapi/include
directory. The make file assumes the use of gmake
.
magnus.conf
configuration file.
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.
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 wrote them for, as described in the following section.
Using your plug-in functions
When you have compiled and arranged for the loading of your functions, you need to provide for their execution. All functions are called as follows:
Directive
fn=
function [
name1=
value1] ... [
nameN=
valueN]
fn=
function identifies the function to be called using the function's unique character-string name.
You specify your function in the directive it was written for. For example, the following line uses an AddLog-class plug-in function called myaddlog
that adds an entry to a log file called mylogfile
. The plug-in function accepts another parameter that defines how much information to log.
AddLog fn=myaddlog name="mylogfile" type="maxinfo"