NAME

Mod_perl_api - accessing the Apache API via mod_perl ($Date: 2000/03/05 11:57:55 $)


DESCRIPTION

This part of the mod_perl FAQ deals with the Apache Application Programmer's Interface and how to access it from perl via mod_perl.


Why can't the server find the handler I wrote?

Did you enable the required hook?

As described in the mod_perl/INSTALL document, the only callback hook enabled by default is PerlHandler. If you want to intervene at a different stage of request processing you must enable the relevant hook. So to add a special authentication handler, for instance, you would start the installation process with:

  perl Makefile.PL PERL_AUTHEN=1

Is the handler correctly referenced in the configuration?

Apache must be told to load your handler, either as a module with the PerlModule directive or as a script with PerlRequire. The handler subroutine will then be available, but you must also specify which requests it should process. This is done by naming it in one of the Perl*Handler directives (PerlInitHandler, PerlTransHandler, etc.). If this directive is put in access.conf outside of any restrictive context, your handler will be called during the given phase of each request processed by the server. You can make it more selective by restricting it to a directory (-hierarchy) in a <Directory ...> section of access.conf or by putting it in a .htaccess file.

Here is an example of the directives needed to call a handler during Apache's URI to filename translation phase:

  PerlRequire         /full/path/to/script/Trans.pl
  PerlTransHandler   Trans::handler

Trans.pl would start with the statement Package Trans; and define a subroutine called handler.


Where can I find examples to get me started?

Check out the Apache-Perl-contrib tarfile at http://perl.apache.org/src/

Here is an example from Vivek Khera. It allows you to filter files through a perl script based on their location. Rather than having to invoke a CGI script, the user just references the file with a normal URL and it is automagically processed by this code...

  #! /usr/local/bin/perl
  use strict;

  # filter a file before returning it to the web client
  # tell Apache to use the PerlHandler FileFilter on file which need
  # filtering in the htaccess file:
  #
  # <Files *.baz>
  #  SetHandler  perl-script
  #  PerlHandler FileFilter
  # </Files>

  package FileFilter;

  use Apache::Constants ':common';

  # find out the file name, then write it out with our header attached
  sub handler {
    my $r = shift;

    my $fileName = $r->filename;

    open(F,$fileName) or return NOT_FOUND; # file not found

    $r->content_type('text/html');
    $r->no_cache(1);              # don't be caching my dynamic documents!

    $r->send_http_header;

    $r->print("<HEAD><TITLE>This is my personal header!</TITLE></HEAD><BODY>");
    # Now copy the file to the client.  If you do not need to make any
    # changes you can copy it verbatim with the single statement
    #    $r->send_fd(\*F);
    # Otherwise, loop over each line...
    while(<F>) {
      # mangle the contents here if you want
      $r->print ($_);
    }
    close(F);

    $r->print("<HR>Document created: ", scalar localtime time);
    $r->print("</BODY>");

    OK;
  }

  1;


How can I check if mod_perl is available during configuration?

Ralf Engelschall writes:

When you compiled one httpd with and the other without mod_perl, then you can simply use <IfModule mod_perl.c>...</IfModule> to surround the stuff for the httpd compiled with mod_perl. The other then ignores these lines. Example:

  <IfModule mod_perl.c>
  ...stuff for httpd w/ mod_perl...
  </IfModule>
  <IfModule !mod_perl.c>
  ...stuff for httpd w/o mod_perl...
  </IfModule>


How can I terminate a chain of handlers?

During each phase of request processing, apache calls handlers which have registered an interest in looking at and possibly handling the request. In some phases it makes sense to let all of the handlers have a chance to look at the request. In other phases the first handler to return ``OK'' terminates that phase (see the Apache documentation, /manual/misc/API.html).

If you define more than one PerlHandler for a phase, they are placed on a stack and all of the handlers on the stack are called sequentially by mod_perl, as long as they return ``DECLINED'' or ``OK''. Apache sees the return code from the final handler and reacts to it. If a handler wants to terminate the chain and ensure that no other handler is called after it, it should set the corresponding stack to undef. For instance, when a TransHandler has set $r->filename, it should terminate with

  $r->set_handlers(PerlTransHandler => undef);
  return OK;


Why can't my handler see an environment variable that I set in httpd.conf?

The configuration directives SetEnv and PassEnv are handled by apache's mod_env during the fixup stage, so mod_perl handlers that run prior to the fixup-stage don't see variables set with them. You can use PerlSetEnv/PerlPassEnv instead - they are processed as soon as possible during a request.


Why does the server hang when I try to read a FORM?

The $r->content method reads application/x-www-form-urlencoded data directly from the client and it does not keep a copy, so if you (or another handler) call it again, the server will hang. One way of avoiding this, if you do not have full control of all the handlers involved, is to convert the request from POST to GET in the first handler that reads the content:

    use Apache::Constants qw(M_GET);

    sub My::Test::handler {
        my $r = shift;

        if ($r->method eq 'POST') { 
           my $content = $r->content;
           # ...
           #make sure nobody else tries to read POST data now that we have
           $r->method('GET');
           $r->method_number(M_GET);
           $r->headers_in->unset('Content-length');
        }
        # ...
    }