Adding Gateways

Principle #1: Simple things should be simple

Gateways are very simple to add to plexus. In the simplest case, you need only add a map line in local.conf and make sure your gateway calls &main'MIME_header('ok', 'content/type'); (where content/type is one of the MIME content types) before doing any output. MIME_header does all the work dealing with HTTP/1.0 headers.

If your gateway encounters an error it should call &error($status, $msg); which will deal with reporting the error back to the user and exiting. &error does not return. Fatal errors can also be reported with the standard perl die command and those will be handled by &report_error which will send email to webmaster by default so you should only use in cases where something is broken enough that someone should look at it.

The fortune gateway is a good example to start with because it's very simple. Note that it is careful to extract only the information it needs from a user query. This is to prevent the users from being able to execute arbitrary commands on your server. Any data that comes from the user must be carefully screened in this way to prevent security holes.

With this in mind I added a few new routines to make creating secure gateways easier. &safeopen($FH, $file); will safely open a file for reading and should be used in all cases where client data is involved. @fields = &main'splitquery($query); will split a user query into fields and convert any %## escapes it contains. Most gateways can simply use something like $query = join(" ", &main'splitquery($query));. Also, there is $pstring = &main'printable($string); which will convert a string of binary data into something that is printable using the %## notation for non-printable characters.

Principle #2: Complex things should be possible

There are many support routines provided in the base code for making your life easier. The search module makes use of several. First, it uses &main'clear_timeout; to clear the default timeout since it is about to do a search operation that may take a little longer than normal operations and we are pretty sure that it isn't going to hang unless there are major system problems. The main reason for the timeout is when you are doing I/O on the socket to the client which might hang and we don't want a bunch of processes hanging around not doing anything, so always make sure the timer is running when you do I/O with the client. You can use &main'set_timeout to restart the timer if you have stopped it.

Principle #3: A configurable command is a usable command

It's best to make things configurable. You can use the set command in local.conf which sets the value in the associtive array %plexus so you can extract values from it. But it's even better to simply pass the data directly into your command as arguments to map (this allows the data to be configurable on a per map basis). For some examples see the sample finger and search gateway configurations in local.conf.

Principle #4: Thou shall not scrunge others variables

Perl uses dynamic scoping (the value of a variable is inherited down the call tree). Unfortunatly, Plexus makes use of this in a couple of different cases. The good news is that most servers need only concern themselves with a small number of variables because they are actually run in a seperate process from the main server (so each invocation gets a fresh environment). Mainly, watch out for $version, %in_headers, and %out_headers which are all used in the HTTP/1.0 handling code in &MIME_header. %in_headers is the list of HTTP/1.0 headers (if any) we got with the request and %out_headers is used by &MIME_header to create the outgoing reply, it can be appended to using &add_header(*out_headers, "Sample: header") (which must be called before &MIME_header) for gateways that need to augment the standard reply headers.

Another side effect of dynamic scoping is that subroutines you call can modify your variables. I've tried to carefully local() all variables in the support routines. An easy way to avoid conflict is to run in your own package as do all the sample gateways.

Users writing new methods must be more careful as they run in the main server thread and cannot use package to good effect. You will probably be ok if you just prepend the method name to all variable names, and be careful.


Tony Sanders