The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

nsapi_perl - Integrate Perl with Netscape servers

SYNOPSIS

In obj.conf

 Init fn="load-modules" shlib="/path/nsapi_perl.so"
      funcs="nsapi_perl_init,nsapi_perl_handler"
 Init fn="nsapi_perl_init" conf="/path/nsapi_perl.conf"

 <Object ppath="/document/root/path/*">
 Directive fn="nsapi_perl_handler"
      module="Netscape::ModuleName" sub="sub_name"
 </Object>

In nsapi_perl.conf

 package Netscape::Config;
 use Netscape::Server::Request;
 use Netscape::Server::Session;
 use Netscape::ModuleName;

DESCRIPTION

nsapi_perl is the collective name for a set of Netscape Server API (NSAPI) functions and Perl modules that integrate Perl with the Netscape web server family. This is achieved by embedding within the server executable a Perl interpreter, much as mod_perl does for the Apache web server.

Once the interpreter has been embedded the server can be configured so that at any point during a client session a Perl subroutine can be called. These Perl subroutines are passed as arguments Perl objects that allow direct access to the server's API.

At this point in time nsapi_perl is still embryonic. The interface to the server API is provided, but as yet no modules using this interface are provided with the distribution. This is where you come in: if you write a module that uses nsapi_perl let me (Ben Sugars, bsugars@canoe.ca) know about it so it widely distributed and so that you can get a prominent position in the CREDITS file.

The rest of this document describes how to configure and use nsapi_perl; it also provides a couple of examples. It is hoped that as real-world examples start to roll in this document can evolve into a sort of cookbook illustrating how approach common nsapi_perl tasks.

CONFIGURATION

Orientation

nsapi_perl consists of the following components

nsapi_perl.so

This is the shared object containing the NSAPI functions that embed a Perl interpreter in the server and run the Perl subroutines.

nsapi_perl.conf

This is a small Perl program that is executed by a function within nsapi_perl.so when the server starts. nsapi_perl.conf loads in the requisite modules needed by nsapi_perl subroutines. It can also be used to define global configuration variables.

Netscape::Server

This is a Perl module that provides the basic framework for the Perl interface to the NSAPI.

Netscape::Server::Session, Netscape::Server::Request

These are Perl classes from which objects that are passed as arguments to nsapi_perl subroutines are instantiated.

Netscape::Registry

This module allows you run run CGI scripts unmodified from within the Netscape server httpd process. This provides a large performance boost. Netscape::Registry has its own man page.

Initialization

For the server to embed a Perl interpreter it must be told at startup to link with nsapi_perl.so This is done by adding the following to the Server's obj.conf file.

 Init fn="load-modules" shlib="/path/nsapi_perl.so"
      funcs="nsapi_perl_init,nsapi_perl_handler"

(You can wrap it on to multiple lines so long as the second line starts with whitespace). Here, /path is the full path to where you installed nsapi_perl.so. (For Netscape 1.x servers this should be added to the file magnus.conf and should be on a single line.)

After linking to nsapi_perl.so the server must then be told to run the function nsapi_perl_init. This function initializes the Perl interpreter which then parses and runs the nsapi_perl.conf program. This is done by adding the following line the the server's obj.conf file.

 Init fn="nsapi_perl_init" conf="/path/nsapi_perl.conf"

where /path is the full path to where you installed nsapi_perl.conf. (Again for Netscape 1.x servers this is to be done in magnus.conf.)

nsapi_perl.conf is responsible for loading the Perl code you wish to have executed by the server. For instance, if you wish to run subroutines in the module Netscape::ModuleName, add a line like this to nsapi_perl.conf.

 use Netscape::ModuleName;

The module's subroutines will then be compiled when it comes time for the server to run them.

Since nsapi_perl.conf is a Perl program that is executed when the server starts, you can use it to perform a multitude of initialization tasks like opening log files, defining global configuration variables, and so on.

With the above lines inserted into obj.conf and with a properly configured nsapi_perl.conf you should see a message like this when starting your server.

 nsapi_perl_init: loaded a perl version 5.00401 interpreter

At this point Perl is primed and the server is ready to run any of the Perl subroutines defined during initialization. Cool.

USAGE

How Netscape Serves

To know how to tell the server when to run a subroutine, it helps to know a little of how Netscape servers process requests from clients. Netscape, like Apache, breaks the processing of requests into discrete steps.

1. AuthTrans

During this stage authorization information from the client is checked.

2. NameTrans

The virtual path sent by the client as part of its URL is mapped to physical path on the server's file system.

3. PathCheck

Given the authorization and path information, the server checks whether the client is allowed access or not.

4. ObjectType

The mime type of the file requested is determined.

5. Service

The client's request is processed, usually by sending them the requested file but sometimes by doing something more fun such as running a CGI program.

6. AddLog

The request is logged.

If at any of these stages an error occurs, such as failed authentication or a file not being found, the sequence is aborted and error processing occurs.

Normally obj.conf is set up in such a way that the server's built-in functions handle each stage of the request. However, you can modify obj.conf so that for certain paths special processing occurs. For instance, adding the following to obj.conf overrides the normal list of Service functions for files within the /document/root/foo directory

 <Object ppath="/document/root/foo/*">
 Service fn="special_function" argument="value"
 </Object>

This would cause all client requests of the form http://my.server.domain/foo/* to be serviced by special_function.

Basically each part of the web site that needs special processing is declared as an object. For each object you can then specify one or more directives. A directive is the name of one of 6 processing steps listed above, such as AuthTrans, NameTrans, etc. In the example above, the directive is Service. After each directive comes a sequence like fn="special_function" indicating the name of the function to fulfill that stage of the request for that object. Following this is a sequence of zero or more name=value pairs that will be passed to the function when it executes.

There are other ways to define an object that are beyond the scope of this document; for full details on how to specify special processing for certain parts of your web site I refer you to your Netscape documentation. From here on, I will assume you have at least a rough idea of how to do it.

Calling Your Perl Subroutines

To call Perl subroutines from the server, you need to create an object and have that object call the nsapi_perl function nsapi_perl_handler passing the module and subroutine names as arguments. For example, the sequence

 <Object ppath="/document/root/perl/*">
 Service fn="nsapi_perl_handler" module="Netscape::PerlService
     sub="send_stuff"
 </Object>

would call the Perl subroutine &Netscape::PerlService::send_stuff for all requests that have a URI beginning with /perl.

To make mod_perl enthusiasts feel at home, if the name of the subroutine is omitted the name handler is assumed. Therefore, the following is an equivalent declaration to the one above:

 <Object ppath="/document/root/perl/*">
 Service fn="nsapi_perl_handler" module="Netscape::Service"
 </Object>

You can have more than one directive for an object including more than one directive of a single type. The following would all requests to /perl be handled entirely by Perl:

 <Object ppath="/document/root/perl/*">
 AuthTrans fn="nsapi_perl_handler" module="Netscape::AuthTrans
 NameTrans fn="nsapi_perl_handler" module="Netscape::NameTrans
 PathCheck fn="nsapi_perl_handler" module="Netscape::PathCheck
 ObjectType fn="nsapi_perl_handler" module="Netscape::ObjectType
 Service fn="nsapi_perl_handler" module="Netscape::Service
 AddLog fn="nsapi_perl_handler" module="Netscape::AddLog
 </Object>

Any module name can be chosen to process a request (so long as it's been use()d by nsapi_perl.conf during server startup. I recommend, however, that all modules used with nsapi_perl be named somewhere in the Netscape:: hierarchy.

SUBROUTINES

Arguments passed to subroutines.

When a Perl subroutine is called by nsapi_perl it is passed three arguments:

 sub handler {
     my($pb, $sn, $rq) = @_;
     ...
 }

$pb is a reference to a hash that contains the key=value pairs passed as arguments to nsapi_perl_handler. For instance, if the following was in your obj.conf,

 NameTrans fn="nsapi_perl_handler" module="Netscape::Redirect"
     from="/special" alt="/default/home.html" url="/for_mozilla/home.html"

then the following all would be true:

 $pb->{'fn'} eq 'nsapi_perl_handler';
 $pb->{'module'} eq 'Netscape::Redirect';
 $pb->{'from'} eq '/special';
 $pb->{'alt'} eq '/default/home.html';
 $pb->{'url'} eq '/for_mozilla/home.html';

The contents of %{$pb} should be treated read-only.

$sn is an instance of Netscape::Server::Session which has its own man page. Basically a Session object contains information about the connection to the client host such as its IP address, its socket descriptor and so on. See Netscape::Server::Session for full details, or bear with me here for a while since you'll soon see an example.

$rq is an instance of Netscape::Server::Request which also has its own man page. A Request object contains information derived from the client's http header and is where information constructed by the server in its response is stored. Again, see Netscape::Server::Request or hang on just a little while longer.

The general idea is to use the values in %{$pb} and to implement methods on $sn and $rq to process the request in special ways. The man pages for each of Netscape::Server::Session and Netscape::Server::Request list the available methods.

Anyone whose done NSAPI C programming will recognize the parallel between how nsapi_perl subroutines are called and how NSAPI C functions are called.

Subroutine Return Values

NSAPI C functions are expected to return one of a set of constants indicating success, failure, bewilderment, or whatever. These constants are termed request-response codes. Any nsapi_perl subroutine should also always return one of these constants. The constants are defined in the Netscape::Server module which should therefore always be use()d by your nsapi_perl module.

The following will import the request-response codes from Netscape::Server.

 use Netscape::Server qw/:request_codes/;

The constants are described in detail in Netscape::Server; I will list them here for reference:

 REQ_ABORTED, REQ_EXIT, REQ_NOACTION, REQ_PROCEED

The interpretation of the value returned by the subroutine to the server depends on what stage of the request was being processed, as the following table indicates.

 Request   |            Subroutine returns
 Stage     |REQ_ABORTED|REQ_EXIT|REQ_NOACTION|REQ_PROCEED
 --------------------------------------------------------
 AuthTrans |     x     |   x    |     n      |     s
 NameTrans |     x     |   x    |     n      |     s
 PathCheck |     x     |   x    |     s      |     s
 ObjectType|     x     |   x    |     s      |     s
 Service   |     x     |   x    |     n      |     s
 AddLog    |     ?     |   ?    |     ?      |     ?
 --------------------------------------------------------

 x - request is aborted entirely
 s - request skips to next stage
 n - request goes to next directive in same stage

Basically REQ_ABORTED and REQ_EXIT each indicate an error, but you should only use REQ_EXIT if there was an I/O error when talking to the client. REQ_PROCEED always causes processing to move to the next stage. REQ_NOACTION sometimes causes the next directive in the same stage to be called. This could be used to implement, for instance, a sequence of NameTrans functions each mapping in their own way the request path.

I have never seen any documentation about how the server interprets values returned by AddLog functions. I suppose since it is the last step anyway, it may not really matter.

Netscape::Server also provides other constants that can be used to set the http status of the request or the severity of errors; see Netscape::Server for full details.

EXAMPLES

Hello World

In obj.conf:

 <Object ppath="/document/root/greetings/*">
 ObjectType fn="nsapi_perl_handler" module="Netscape::HelloWorld"
     sub="content_type" type="text/html"
 Service fn="nsapi_perl_handler" module="Netscape::HelloWorld"
 </Object>

In nsapi_perl.conf: package Netscape::Server::Config; use Netscape::Server::Request; use Netscape::Server::Session; use Netscape::HelloWorld;

In HelloWorld.pm:

 package Netscape::HelloWorld;
 use strict;
 use Netscape::Server qw/:all/;

 sub content_type {
     my($pb, $sn, $rq) = @_;
  
     # --- Set the content type as configured   
     my $type = $pb->{'type'};
     defined $type or
         return REQ_ABORTED;
     $rq->srvhdrs('content-type', $type);
     return REQ_PROCEED;
 }

 sub handler {
     my($pb, $sn, $rq) = @_;
     my($proceed);

     # --- Set status to 200 OK
     $sn->protocol_status($rq, PROTOCOL_OK);

     # --- Initiate response
     $proceed = $sn->protocol_start_response($rq);
     if ($proceed == REQ_PROCEED) {
         $sn->net_write("<h1>Hello World</h1>\n");
         return REQ_PROCEED;
     } elsif ($proceed == REQ_NOACTION) {
         # --- Client probably did an if-modified request
         return REQ_PROCEED;
     } else {
         # --- Yikes! Something bad has happened
         return $proceed;
     }
 }

 1;

Redirect

Here's a Perl equivalent to the https_redirect function listed on page 143 of the Enterprise 2.0 Unix Programmer Guide. Note that I don't particularly care for the logic of this subroutine, or the original C function from which it was derived. It is presented here merely to illustrate the potential advantages of nsapi_perl.

In obj.conf:

 NameTrans fn="nsapi_perl_handler" module="Netscape::Redirect"
     from="/special" alt="/default/home.html" url="/for_mozilla/home.html"

In nsapi_perl.conf:

 package Netscape::Server::Config;
 use Netscape::Server::Request;
 use Netscape::Server::Session; 
 use Netscape::Redirect;

In Redirect.pm:

 package Netscape::Redirect;
 use strict;
 use Netscape::Server qw/:all/;

 sub handler {
     my($pb, $sn, $rq) = @_;

     my $ppath = $rq->vars('ppath');
     my $from = $pb->{'from'};
     my $url = $pb->{'url'};
     my $alt = $pb->{'alt'};

     if (not defined $from or not defined $url) {
         log_error(LOG_MISCONFIG, "handler", $sn, $rq,
                   'missing parameter (need from, url)');
         return REQ_ABORTED;
     }

     # --- Here's where the poor sucker using raw NSAPI has to
     # --- resort to the utterly bogus shexp_cmp()
     $ppath =~ /^$from/o or
         return REQ_NOACTION;

     # --- Get the user agent
     defined(my $ua = $rq->headers('user-agent')) or
         return REQ_ABORTED;

     # --- NSAPI has a built-in that looks for Mozilla-like browser,
     # --- but MSIE fools it.  However, now we can use a full Perl
     # --- regular expression here if we want
     if ($ua =~ /Mozilla/ and $ua !~ /MSIE/) {
         $rq->protocol_status($sn, PROTOCOL_REDIRECT);
         $rq->vars('url', $url);
         return REQ_ABORTED;
     }
     
     # --- No match.  Could be MSIE or Lynx or whomever.
     if (defined $alt) {
         # --- Rewrite the request string
         $rq->vars('ppath', $alt);
         return REQ_NOACTION;
     }
     
     # --- Else do nothing
     return REQ_NOACTION;
 }

 1;

BUGS

Send bug reports, comments, or questions to the author, Ben Sugars (bsugars@canoe.ca).

Here are the major issues at this time:

o

The installation process needs to be improved. In particular, there's no reason why nsapi_perl can't be made to build "out-of-the-box" on Win32.

o

I can only get extension modules to work if I recompile them adding '-lperl' to the cc options.

o

Threading. Grrrr. Netscape *used* to let you run their multithreaded servers as multiprocess single-threaded servers if you wanted to. The 3.x line doesn't let you do this, AFAIK. So, we need a thread-safe libperl.so. I know this is being worked on, but in the meantime, does anyone know if the libperl.so with 5.004 is OK to use?

AUTHOR

Benjamin Sugars <bsugars@canoe.ca>

SEE ALSO

Netscape::Server, Netscape::Server::Session, Netscape::Server::Request, Netscape::Registry

perl(1)

Netscape documentation about their line of web servers and NSAPI.