Plack::Handler::Apache2 - Apache 2.0 mod_perl handler to run PSGI application
# in your httpd.conf
<Location />
SetHandler perl-script
PerlResponseHandler Plack::Handler::Apache2
PerlSetVar psgi_app /path/to/app.psgi
</Location>
# Optionally preload your apps in startup
PerlPostConfigRequire /etc/httpd/startup.pl
See "STARTUP FILE" for more details on writing a
"startup.pl".
This is a mod_perl handler module to run any PSGI application with mod_perl on
Apache 2.x.
If you want to run PSGI applications behind Apache instead
of using mod_perl, see Plack::Handler::FCGI to run with FastCGI, or use
standalone HTTP servers such as Starman or Starlet proxied with
mod_proxy.
If you want to create a custom handler that loads or creates PSGI applications
using other means than loading from ".psgi"
files, you can create your own handler class and use
"call_app" class method to run your
application.
package My::ModPerl::Handler;
use Plack::Handler::Apache2;
sub get_app {
# magic!
}
sub handler {
my $r = shift;
my $app = get_app();
Plack::Handler::Apache2->call_app($r, $app);
}
Here is an example "startup.pl" to preload
PSGI applications:
#!/usr/bin/env perl
use strict;
use warnings;
use Apache2::ServerUtil ();
BEGIN {
return unless Apache2::ServerUtil::restart_count() > 1;
require lib;
lib->import('/path/to/my/perl/libs');
require Plack::Handler::Apache2;
my @psgis = ('/path/to/app1.psgi', '/path/to/app2.psgi');
foreach my $psgi (@psgis) {
Plack::Handler::Apache2->preload($psgi);
}
}
1; # file must return true!
See
<http://perl.apache.org/docs/2.0/user/handlers/server.html#Startup_File>
for general information on the
"startup.pl" file for preloading perl
modules and your apps.
Some things to keep in mind when writing this file:
- multiple init phases
You have to check that "restart_count" in
Apache2::ServerUtil is "> 1",
otherwise your app will load twice and the env vars you set with
PerlSetEnv
<http://perl.apache.org/docs/2.0/user/config/config.html#C_PerlSetEnv_>
will not be available when your app is loading the first time.
Use the example above as a template.
- @INC
The "startup.pl" file is a
good place to add entries to your @INC. Use lib
to add entries, they can be in your app or
".psgi" as well, but if your modules
are in a local::lib or some such, you will need to add the path for
anything to load.
Alternately, if you follow the example above, you can use:
PerlSetEnv PERL5LIB /some/path
or
PerlSwitches -I/some/path
in your "httpd.conf", which
will also work.
- loading errors
Any exceptions thrown in your
"startup.pl" will stop Apache from
starting at all.
You probably don't want a stray syntax error to bring your
whole server down in a shared or development environment, in which case
it's a good idea to wrap the "preload" call in an eval, using
something like this:
require Plack::Handler::Apache2;
my @psgis = ('/path/to/app1.psgi', '/path/to/app2.psgi');
foreach my $psgi (@psgis) {
eval {
Plack::Handler::Apache2->preload($psgi); 1;
} or do {
my $error = $@ || 'Unknown Error';
# STDERR goes to the error_log
print STDERR "Failed to load psgi '$psgi': $error\n";
};
}
- dynamically loaded modules
Some modules load their dependencies at runtime via e.g.
Class::Load. These modules will not get preloaded into your parent
process by just including the app/module you are using.
As an optimization, you can dump %INC
from a request to see if you are using any such modules and preload them
in your "startup.pl".
Another method is dumping the difference between the
%INC on process start and process exit. You can
use something like this to accomplish this:
my $start_inc = { %INC };
END {
my @m;
foreach my $m (keys %INC) {
push @m, $m unless exists $start_inc->{$m};
}
if (@m) {
# STDERR goes to the error_log
print STDERR "The following modules need to be preloaded:\n";
print STDERR "$_\n" for @m;
}
}
Paul Driver
Ævar Arnfjörð Bjarmason
Rafael Kitover