Why mod_perl 2.0 Sucks Why mod_perl 2.0 Rocks Geoffrey Young geoff@modperlcookbook.org http://www.modperlcookbook.org/
A Long Time Ago... I figured it was time to take mod_perl 2.0 for a test drive... http://www.modperlcookbook.org/
Laziness, Impatience, Hubris "I know mod_perl pretty well..." "Compat layers are for weenies..." "It's still HTTP, so not much can change..." "This will be easy." http://www.modperlcookbook.org/
The Adventure Begins So, I take an existing, complex handler and just plug it in... http://www.modperlcookbook.org/
Let's Try Again "Hmm, maybe something simpler won't dump core." So, I try a new, but simple, program... http://www.modperlcookbook.org/
use Apache::Constants qw(OK); use strict; sub handler { my $r = shift; package My::Foo; use Apache::Constants qw(OK); use strict; sub handler { my $r = shift; my $log = $r->server->log; $log->info('*** processing uri ', $r->uri); my $foo = $r->dir_config->get('Foo'); $r->content_type('text/plain'); $r->print("found $foo"); return OK; } 1; http://www.modperlcookbook.org/
use Apache::Const qw(OK); use Apache::RequestRec (); package My::Foo; use Apache::Const qw(OK); use Apache::RequestRec (); use Apache::Server (); use Apache::Log (); use Apache::RequestUtil (); use APR::Table (); use Apache::RequestIO (); use strict; sub handler { my $r = shift; my $log = $r->server->log; $log->info('*** processing uri ', $r->uri); my $foo = $r->dir_config->get('Foo'); $r->content_type('text/plain'); $r->print("found $foo"); return OK; } 1; http://www.modperlcookbook.org/
Gripes Different un-DWIMmy http://www.modperlcookbook.org/
DWIMmy use Apache::Constants qw(OK); http://www.modperlcookbook.org/
un-DWIMmy use Apache::Const qw(OK); use Apache::RequestRec (); use Apache::Server (); use Apache::Log (); use Apache::RequestUtil (); use APR::Table (); use Apache::RequestIO (); http://www.modperlcookbook.org/
un-DWIMmy use Apache::Const qw(OK); use Apache::RequestRec (); use Apache::Server (); use Apache::Log (); use Apache::RequestUtil (); use APR::Table (); use Apache::Util (); use Apache::Response (); use Apache::ServerUtil (); use Apache::Process (); use APR::Pool (); use base(Apache::Filter); http://www.modperlcookbook.org/
speaking of use base... sub handler { ... } http://www.modperlcookbook.org/
Subroutine Attributes sub handler : method { ... } http://www.modperlcookbook.org/
this space intentionally left blank sub handler : FilterRequestHandler FilterHasInitHandler(\&init) { ... } http://www.modperlcookbook.org/
Gripes Different un-DWIMmy Examples and docs difficult to find Too many calls to grep(1) Incomplete http://www.modperlcookbook.org/
Something Better Here? use Apache::Const qw(OK); use Apache::RequestRec (); use Apache::Server (); use Apache::Log (); use Apache::RequestUtil (); use APR::Table (); use Apache::RequestIO (); http://www.modperlcookbook.org/
Apache::RequestRec Access to request_rec structure only Makes $r more like r Reinforces what mod_perl really is access to the Apache C API in Perl http://www.modperlcookbook.org/
Apache::RequestRec Contains only methods that mirror C request_rec slots $r->notes() $r->content_type() $r->headers_out() $r->per_dir_config() $r->pool() http://www.modperlcookbook.org/
This ROCKS! Yes, mod_perl 2.0 has an assbackwards() method Access to assbackwards slot in the request_rec http://www.modperlcookbook.org/
Assbackwards In Apache-speak, a request that is assbackwards is an HTTP/0.9 request Apache still supports HTTP/0.9 $ telnet localhost 80 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET /cgi-bin/sayhello.cgi Hi http://www.modperlcookbook.org/
The assbackwards Flag Apache marks HTTP/0.9 requests with the assbackwards flag in the request record If r->assbackwards is set, Apache doesn't send any headers mod_perl 1.0 did not provide a way to access the assbackwards flag but it used it when convenient my $sub = $r->lookup_uri('/layline.html'); $sub->assbackwards(1); $sub->run(); http://www.modperlcookbook.org/
This ROCKS! Yes, mod_perl 2.0 has an assbackwards() method Access to assbackwards slot in the request_rec Saying assbackwards is cool Indicative of something even cooler... http://www.modperlcookbook.org/
Access to Everything The goal of mod_perl has always been complete access to the Apache C API 1.0 - API partially opened over time 2.0 - entire API is open by default autogenerated accessors/mutators only closed where a Perl API isn't appropriate Complete access just rocks http://www.modperlcookbook.org/
APR::Pool Access to Apache pools (finally) Old features, new syntax $r->pool->cleanup_register($cv); http://www.modperlcookbook.org/
Handy New Features Per-connection cleanups Reconfiguration cleanups $r->connection->pool->cleanup_register($cv); Reconfiguration cleanups $r->process->pconf->cleanup_register($cv); Pool management { my $pool = APR::Pool->new(); $pool->cleanup_register($cv); } # $cv executes when $pool is DESTROY()ed http://www.modperlcookbook.org/
Output Filters New in Apache 2.0 mod_perl has been able to filter content for years limited to mod_perl generated content mod_perl can do lots, like CGI and SSI Output filters let you filter everything no matter who generates the content http://www.modperlcookbook.org/
Perl Output Filters Complete access to the Apache API raw bucket brigade manipulations mod_perl also makes Apache Perl-ish streaming filter API Streaming filters are insanely easy to write http://www.modperlcookbook.org/
Streaming Filter API package My::Filter; use Apache::Filter (); use Apache::Const qw(OK); sub handler { my $f = shift; while ($f->read(my $buffer, 1024)) { # do something with $buffer $f->print($buffer); } return OK; 1; http://www.modperlcookbook.org/
httpd.conf PerlOutputFilterHandler My::Filter http://www.modperlcookbook.org/
httpd.conf # alter _all_ PHP pages (just a bit) PerlOutputFilterHandler Apache::Hijack http://www.modperlcookbook.org/
package Apache::Hijack; use Apache::Filter (); use Apache::RequestRec (); use Apache::Const -compile => qw(OK DECLINED); use strict; sub handler { my $f = shift; my $r = $f->r; return Apache::DECLINED unless $r->handler eq 'php-script' or $r->handler eq 'application/x-httpd-php'; while ($f->read(my $buffer, 1024)) { $buffer =~ s!(<body>)!$1<h1>got mod_perl?</h1>!i; $f->print($buffer); } return Apache::OK; 1; http://www.modperlcookbook.org/
package Apache::Hijack; use Apache::Filter (); use Apache::RequestRec (); use APR::Table (); use Apache::Const -compile => qw(OK DECLINED); use strict; sub handler { my $f = shift; my $r = $f->r; return Apache::DECLINED unless $r->handler eq 'php-script' or $r->handler eq 'application/x-httpd-php'; unless ($f->ctx) { $r->headers_out->unset('Content-Length'); $r->headers_out->set('X-Powered-By' => 'mod_perl 2.0'); $f->ctx(1); } while ($f->read(my $buffer, 1024)) { $buffer =~ s!(<body>)!$1<h1>got mod_perl?</h1>!i; $f->print($buffer); return Apache::OK; 1; http://www.modperlcookbook.org/
http://www.modperlcookbook.org/ package Apache::Hijack; use Apache::Filter (); use Apache::RequestRec (); use APR::Table (); use Apache::Const -compile => qw(OK DECLINED); use strict; sub handler { my $f = shift; my $r = $f->r; return Apache::DECLINED unless $r->handler eq 'php-script' or $r->handler eq 'application/x-httpd-php'; my $context; unless ($f->ctx) { $r->headers_out->unset('Content-Length'); $r->headers_out->set('X-Powered-By' => 'mod_perl 2.0'); $context = { extra => undef }; } $context ||= $f->ctx; while ($f->read(my $buffer, 1024)) { $buffer = $context->{extra} . $buffer if $context->{extra}; if (($context->{extra}) = $buffer =~ m/(<[^>]*)$/) { $buffer = substr($buffer, 0, - length($context->{extra})); $buffer =~ s!(<body>)!$1<h1>got mod_perl?</h1>!i; $f->print($buffer); $f->ctx($context) unless $f->seen_eos; return Apache::OK; 1; http://www.modperlcookbook.org/
Stacked Perl Handlers mod_perl supports the idea of stacked handlers PerlAuthenHandler My::One My::Two Idea borrowed from Apache mod_perl 1.0 didn't get it quite right http://www.modperlcookbook.org/
Stacked C Handlers In Apache, how the module list is traversed depends on the phase Some phases run until the handler list is exhausted fixups loggers Some phases terminate on the first OK translation authentication http://www.modperlcookbook.org/
Problems in 1.0 Calling mechanism in mod_perl 1.0 did not allow for early phase termination via OK PerlAuthenHandler My::One My::Two 2.0 does the right thing Phases now mirror Apache's design More API crossover http://www.modperlcookbook.org/
mod_perl as Glue mod_perl's main goal is to provide a Perl version of the Apache C API Also provides glue between Perl and Apache access the Apache API via XS mod_perl 2.0's internals are cleaner easier to access (yet) unsupported Apache features in Perl http://www.modperlcookbook.org/
#perl SSI support In Apache 1.3, mod_include provided support for the #perl SSI tag No direct support in 2.0 mod_include provides a hooking mechanism instead mod_perl is required to create its own #perl implementation (relatively) easy in XS http://www.modperlcookbook.org/
IncludeHook.xs handler = modperl_handler_new(p, apr_pstrdup(p, sub)); status = modperl_callback(aTHX_ handler, p, r, s, av); MODULE = Apache::IncludeHook PACKAGE = Apache::IncludeHook PROTOTYPES: DISABLE BOOT: static const char * const aszPre[] = { "mod_include.c", NULL }; ap_hook_post_config(register_perl, aszPre, NULL, APR_HOOK_FIRST); http://www.modperlcookbook.org/
Makefile.PL Writing portable Makefile.PLs for XS modules in 1.0 was a pain http://www.modperlcookbook.org/
use ExtUtils::MakeMaker; use Apache::src (); use Config; use strict; #!perl use ExtUtils::MakeMaker; use Apache::src (); use Config; use strict; my %config; $config{INC} = Apache::src->new->inc; if ($^O =~ /Win32/) { require Apache::MyConfig; $config{DEFINE} = ' -D_WINSOCK2API_ -D_MSWSOCK_ '; $config{DEFINE} .= ' -D_INC_SIGNAL -D_INC_MALLOC ' if $Config{usemultiplicity}; $config{LIBS} = qq{ -L"$Apache::MyConfig::Setup{APACHE_LIB}" -lApacheCore } . qq{ -L"$Apache::MyConfig::Setup{MODPERL_LIB}" -lmod_perl}; } WriteMakefile( NAME => 'Apache::Assbackwards', VERSION_FROM => 'Assbackwards.pm', PREREQ_PM => { mod_perl => 1.26 }, %config, ); http://www.modperlcookbook.org/
Makefile.PL Writing portable Makefile.PLs for XS modules in 1.0 was a pain 2.0 introduces ModPerl::MM::WriteMakefile http://www.modperlcookbook.org/
ModPerl::MM::WriteMakefile( NAME => 'Apache::IncludeHook', use Apache2 (); use ModPerl::MM (); ModPerl::MM::WriteMakefile( NAME => 'Apache::IncludeHook', VERSION_FROM => 'IncludeHook.pm', PREREQ_PM => { mod_perl => 1.99_10, }, ); http://www.modperlcookbook.org/
Apache-Test Single best part of mod_perl 2.0 Framework for unit testing Apache-based applications mod_perl handlers CGI scripts SOAP servers more So useful, it became the basis of the httpd-test project http://www.modperlcookbook.org/
Speaking of Tests... Apache-Test used in mod_perl 2.0 development from the start More, better tests in 2.0 http://www.modperlcookbook.org/
mod_perl 1.0 All tests successful, 8 tests skipped. Files=36, Tests=401 http://www.modperlcookbook.org/
mod_perl 2.0 All tests successful, 7 tests skipped. Files=145, Tests=749 All tests successful, 1 test skipped. Files=12, Tests=63 http://www.modperlcookbook.org/
Speaking of Tests... Apache-Test used in mod_perl 2.0 development from the start More, better tests in 2.0 Comes very close to exercising every part of the external API More tests == better software http://www.modperlcookbook.org/
Bigger Community 1.0 had one (brilliant) primary developer 2.0 is more community based more core developers more interested parties httpd-test apreq p5p more platforms far better win32 support "Many eyes" == better software http://www.modperlcookbook.org/
Why mod_perl 2.0 Rocks Better software product "mod_perl: do more" http://www.modperlcookbook.org/