mod_perl FAQ

The
mod_perl
FAQ

This is a list of answers to Frequently Asked Questions concerning mod_perl.

This FAQ is maintained by Patrick Michael Kane, please send all updates to modus@enews.com.

Additional information can be found at the mod_perl homepage at: http://www.osf.org/~dougm/apache/.

Section 1 - General

Section 2 - Installation and Compilation

Section 3 - Writing and porting mod_perl scripts

Section 4 - Writing Apache modules in Perl with mod_perl


Section 1 - General

Q1.1. What is mod_perl?

mod_perl is an Apache module that embeds a Perl interpreter into the Apache HTTP server.

Q1.2. Why would I want to use mod_perl?

From the mod_perl README:

The benefit of this [mod_perl] is that we are able to run scripts without going through the expensive (fork/exec/parameter passing/parsing, etc.) CGI interface. The scripts will run faster and they have direct access to the C API of the server.

Since mod_perl scripts have access to the server's C API, full-fledged Apache modules can be written in Perl! For example, the current mod_perl package has bundled with it an example module, written in Perl, that replicates the behavior of the Apache 1.1.1 server side includes (SSI) module, and lets Perl programmers extend the module from the comfort of a Perl programming environment instead of C.

Q1.3. Can I use my existing CGI scripts unmodified with mod_perl?

No. mod_perl is not CGI and requires some changes to your existing scripts to get them running. However, much work has been done to make this task easier:

For more technical information on porting your CGI to mod_perl, see Q3.1

Q1.4. Is there a mod_perl mailing list?

Yes. You can subscribe by sending mail to to majordomo@listproc.itribe.net with the string "subscribe modperl" in the body. The list is archived at http://www.coe.missouri.edu/~faq/lists/modperl/.

It's a good idea to check both this FAQ and the mail archives before posting a question to the mailing list.

Q1.5. Who has contributed to this FAQ?

Heartfelt thanks go out to the following people for their FAQ contributions:

Doug MacEachern
Rob Hartill
Jeff Rowe


Section 2 - Installation and Compilation

Q2.1. What do I need to use mod_perl?

You will need:

  1. Apache 1.1.1 or higher;
    Apache 1.2 is recommended.

  2. Perl5.003 or higher.

  3. If you do not have Perl5.003_01 or higher, you will need to fetch the ExtUtils::Embed module from:
    http://www.perl.com/CPAN/modules/by-module/ExtUtils/ExtUtils-Embed-1.07.tar.gz.

Note: mod_perl includes a patch for Perl5.003 that fixes an obscure filehandle bug. This bug was fixed in subsequent releases of Perl5.


Section 3 - Writing and porting mod_perl scripts

Q3.1. What changes do I need to make to my CGI script to get it to run under mod_perl?

In order to simply get your scripts running, only a few changes are necessary:

The recommended configuration is as follows:

 Alias /perl/  /real/path/to/perl-scripts/

 <Location /perl>
 SetHandler perl-script
 PerlHandler Apache::Registry
 Options ExecCGI
 </Location>

Now, any file that lives under /perl will be compiled by mod_perl, provided the file has the executable bit set and server configuration allows execution of that file. The file is re-compiled only if it has changed on disk.

By default, mod_perl does not send any headers by itself, however, you may wish to change this:

    PerlSendHeader On	

By default, mod_perl does not setup %ENV, you may wish to change this:

    PerlSetupEnv On

Apache's i/o is not stream oriented. So, by default, you cannot print() to STDOUT from your script, use $r->print() instead. Nor can you read() from STDIN, use $r->read*() or the $r->content methods to read POST data. As of Perl5.003_02, there are two mechanisms in place for redirecting the STDIN and STDOUT streams. (See section 1.3.)
This is all very straightforward but a little tedious for new users. Try not to let it let it put you off.

With this configuration, it is possible to write scripts that look just like CGI scripts:
$r->print "Content-type: text/html\n\n";

$r->print "Date: ", scalar localtime, "\n";

$r->print "%ENV: \n", map { "$_ = $ENV{$_} \n" } keys %ENV;

Q3.2 Why does this happen with mod_perl but not CGI ?

mod_perl is not CGI. this is a lesson you must learn. CGI scripts written in Perl are standalone processes that are called once and die after one request. Of course, you now know that this is a tad inefficient and that there's a lot to be gained by mod_perl's approach of reusing the same pre-"compiled" Perl code to serve multiple requests. However, many experienced CGI script writers encounter some unexpected (though reasonable once understood) quirks that the one off CGI processes are immune from.

Writing safe mod_perl code

Properly scope your variables. Stop and read that sentence again. Conventional CGI scripts can be as sloppy with their namespace as they want, since they are restarted anew for each request. Your mod_perl script has a much longer lifetime (potentially as long as your Apache server is running), and requires much more care. Scope everything except long-lived variables with my() and use strict; so Perl will demand that you recognize your global variables.

Localize global variables. If you change any of Perl's global variables (e.g. $/ to change the input record separator), or even your own global variables remember to reset them or better still, always localize global variables before using them, e.g. local($/) = undef. If you can, reduce your dependency on global variables.

Q3.3 When I call a subroutine from inside of a subroutine, the inner subroutine doesn't change any of the variables it uses. Why not?

You are probably declaring a variable in the outer subroutine using my() and then attempting it to alter it in your inner subroutine.

A local() variable is just a local copy of a local variable that can be seen by other subroutines. A my() variable only exists within the scope of a block, in this case the subroutine. If you use the -w flag for Perl (or set $^W=1; in mod_perl), you'll see something like:

Variable "$var" will not stay shared at (eval 17) line 3206.

This means the inner subroutine cannot change $var because $var is scoped using my() in the outer subroutine. Try passing $var to the subroutine as a parameter. See 'perlref' in the Perl man pages.

Q3.4 Great, but in some mod_perl programs using Perl with sfio, I call a subroutine from the top level program, not a subroutine, and the variables still won't change!

If you are using Apache::Registry, your routines are being called from a subroutine. Although it appears your script is just a script, it is compiled into a subroutine by Apache::Registry, only re-compiled when you script changes on disk. That's where mod_perl gets some its speed from! So, based on that example, all of your mod_perl program is stuffed inside

sub something {}

but 'something' is actually the filename translated into a valid package name. Your top level program is actually a subroutine. It pays to remember this when using global variables in subroutines (See section 3.2).

Section 4 - Writing Apache modules in Perl with mod_perl

Introduction

The Apache server module API breaks down a request into a number of stages, provides hooks at each stage where a module may choose to step, have a look at what's going on, and decide to take action or decline to do anything.

mod_perl provides a hook for each stage, where a perl subroutine callback may be defined to handle that stage on per-directory basis. The stages and coresponding perl hooks are as follows:

We've seen one example of an Apache/Perl module implementing a PerlHandler in section 3.1, the Apache::Registry module. Here's an example of how to write your own module:

PerlScript  /usr/local/httpd/myperl/handlers.pl

<Location /myperl/>
  SetHandler perl-script
  PerlHandler MyPackage::handler
</Location>
This tells Apache to call the mod_perl subroutine 'MyPackage::handler' whenever it sees requests that are prefixed by '/myperl/'. The subroutine 'MyPackage::handler' can look at the URL and decide what to do next (in this example it'd probably call another subroutine that acts like a standalone CGI script.
'/usr/local/httpd/myperl/handlers.pl' is the file holding the Perl code. You can call it whatever you want and keep it elsewhere.
You may find it easier to manage your Apache/Perl modules by writing them as Perl modules. In this case, you'd have a file named MyPackage.pm somewhere in Perl's @INC path. The configuration would look like so:
PerlModule MyPackage

<Location /myperl/>
  SetHandler perl-script
  PerlHandler MyPackage::handler
</Location>
If PerlHandler is not a defined subroutine, mod_perl assumes it is a class name which defines a handler subroutine. This allows you to reduce configuration to simply:
<Location /myperl/>
  SetHandler perl-script
  PerlHandler MyPackage
</Location>

  • Note that anything outside of a subroutine in your PerlScript or a PerlModule will be run by mod_perl and Apache when you start the server (useful for reading in other Perl files (require/use) so that they are preinitialized before your subroutines are called).

    For the '/myperl/' example above, you might try something like this:

    # this is written in perl. It doesn't need #!/usr/local/bin/perl
    package MyPackage;
    
    sub handler {
        my($r) = @_;    # grab the request info "object" that mod_perl has
                        # has kindly passed as a subroutine argument.
    
        %ENV  = $r->cgi_env;    # setup the ENVironment variables for
    			    # the request. Easy eh ?
    
        my $uri = $r->uri;      # what does the URI (URL) look like ?
        if ($uri =~ /hello/) {
    	say_hello($r);
        } elsif ($uri =~ /goodbye/) {
    	say_goodbye($r);
        }
        return 1;    # tell mod_perl that we did our stuff error free
    }
    
    sub say_hello {
        my($r) = @_;
    
        $r->status = 200;       # All's ok, so set a "200 OK" status
        $r->send_http_header;   # Now send the http headers.
        
        $r->print("Hello from mod_perl");
    }
    
    sub say_goodbye {
        my($r) = @_;
    
        $r->status = 200;       # All's ok, so set a "200 OK" status
        $r->send_http_header;   # Now send the http headers.
    
        $r->print("Goodbye from mod_perl");
    }
    
    
    mod_perl ships with several modules that step in during various stages of a request:

    • Implementing PerlHandler:
      • Apache::Status - Embedded interpreter runtime status
      • Apache::SSI - Implement server-side includes in Perl
    • Implementing PerlAuthenHandler:
      • Apache::AuthenDBI - Authenticate via Perl's DBI
      • Apache::Authen - Perl Apache authentication utilities
    • Implementing PerlAuthzHandler:
      • Apache::AuthzAge - Authorize based on age
    • Implementing PerlAccessHandler:
      • Apache::AccessLimitNum - Limit user access by number of requests
    • Implementing PerlTransHandler:
      • Apache::MsqlProxy - Translate URI's into mSQL database queries