This document describes the various options for porting a mod_perl 1.0
Apache module so that it runs on a Apache 2.0 / mod_perl 2.0
server. It's also helpful to those who start developing mod_perl 2.0
handlers.

In the vast majority of cases, a perl Apache module that runs under
mod_perl 1.0 will not run under mod_perl 2.0 without at least some
degree of modification.

Even a very simple module that does not in itself need any changes
will at least need the mod_perl 2.0 Apache modules loaded, because in
mod_perl 2.0 basic functionality, such as access to the request object
and returning an HTTP status, is not found where, or implemented how
it used to be in mod_perl 1.0.

Most real-life modules will in fact need to deal with the following
changes:

methods that have moved to a different (new) package

methods that must be called differently (due to changed prototypes)

methods that have ceased to exist (functionality provided in some
other way)

Do not be alarmed! One way to deal with all of these issues is to
load the Apache2::compat
compatibility layer bundled with mod_perl 2.0. This magic spell will
make almost any 1.0 module run under 2.0 without further changes. It
is by no means the solution for every case, however, so please read
carefully the following discussion of this and other options.

There are three basic options for porting. Let's take a quick look at
each one and then discuss each in more detail.

Run the module on 2.0 under Apache2::compat with no further changes

As we have said mod_perl 2.0 ships with a module,
Apache2::compat, that provides a
complete drop-in compatibility layer for 1.0
modules. Apache2::compat does the following:

Loads all the mod_perl 2.0 Apache2:: modules

Adjusts method calls where the prototype has changed

Provides Perl implementation for methods that no longer exist in 2.0

The drawback to using Apache2::compat is the performance hit, which
can be significant.

Authors of CPAN and other publicly distributed modules should not use
Apache2::compat since this forces its use in environments where the
administrator may have chosen to optimize memory use by making all
code run natively under 2.0.

Modify the module to run only under 2.0

If you are not interested in providing backwards compatibility with
mod_perl 1.0, or if you plan to leave your 1.0 module in place and
develop a new version compatible with 2.0, you will need to make
changes to your code. How significant or widespread the changes are
depends largely of course on your existing code.

Several sections of this document provide detailed information on how
to rewrite your code for mod_perl 2.0 Several tools are provided to
help you, and it should be a relatively painless task and one that you
only have to do once.

Modify the module so that it runs under both 1.0 and 2.0

You need to do this if you want to keep the same version number for
your module, or if you distribute your module on CPAN and want to
maintain and release just one codebase.

This is a relatively simple enhancement of option (2) above. The module
tests to see which version of mod_perl is in use and then executes the
appropriate method call.

The following sections provide more detailed information and
instructions for each of these three porting strategies.

The Apache2::compat module tries
to hide the changes in API prototypes between version 1.0 and 2.0 of
mod_perl, and implements "virtual methods" for the methods and
functions that actually no longer exist.

Apache2::compat is extremely easy to use. Either add at the very
beginning of startup.pl:

use Apache2::compat;

or add to httpd.conf:

PerlModule Apache2::compat

That's all there is to it. Now you can run your 1.0 module unchanged.

Remember, however, that using Apache2::compat will make your module
run slower. It can create a larger memory footprint than you need and
it implements functionality in pure Perl that is provided in much
faster XS in mod_perl 1.0 as well as in 2.0. This module was really
designed to assist in the transition from 1.0 to 2.0. Generally you
will be better off if you port your code to use the mod_perl 2.0 API.

It would certainly be nice to have our mod_perl 1.0 code run on the
mod_perl 2.0 server unmodified. So first of all, try your luck and
test the code.

It's almost certain that your code won't work when you try, however,
because mod_perl 2.0 splits functionality across many more modules
than version 1.0 did, and you have to load these modules before the
methods that live in them can be used. So the first step is to figure
out which these modules are and use() them.

The ModPerl::MethodLookup
module provided with mod_perl 2.0 allows you to find out which module
contains the functionality you are looking for. Simply provide it with
the name of the mod_perl 1.0 method that has moved to a new module,
and it will tell you what the module is.

For example, let's say we have a mod_perl 1.0 code snippet:

$r->content_type('text/plain');
$r->print("Hello cruel world!");

If we run this, mod_perl 2.0 will complain that the method
content_type() can't be found. So we use ModPerl::MethodLookup
to figure out which module provides this method. We can just run this
from the command line:

% perl -MModPerl::MethodLookup -e print_method content_type

This prints:

to use method 'content_type' add:
use Apache2::RequestRec ();

We do what it says and add this use() statement to our code,
restart our server (unless we're using
Apache2::Reload), and mod_perl
will no longer complain about this particular method.

Since you may need to use this technique quite often you may want to
define an alias. Once
defined the last command line lookup can be accomplished with:

Some methods exists in several classes. For example this is the case
with the print() method. We know the drill:

% lookup print

This prints:

There is more than one class with method 'print'
try one of:
use Apache2::RequestIO ();
use Apache2::Filter ();

So there is more than one package that has this method. Since we know
that we call the print() method with the $r object, it must be
the Apache2::RequestIO module that we are after. Indeed, loading
this module solves the problem.

The issue of picking the right module, when more than one matches, can
be resolved when using ModPerl::MethodLookup programmatically --
lookup_method
accepts an object as an optional second argument, which is used if
there is more than one module that contains the method in
question. ModPerl::MethodLookup knows that Apache2::RequestIO and
and Apache2::Filter expect an object of type Apache2::RequestRec
and type Apache2::Filter respectively. So in a program running under
mod_perl we can call:

ModPerl::MethodLookup::lookup_method('print', $r);

Now only one module will be matched.

This functionality can be used in
AUTOLOAD, for
example, although most users will not have a need for this robust of
solution.

Now if you use a wide range of methods and functions from the mod_perl
1.0 API, the process of finding all the modules that need to be loaded
can be quite frustrating. In this case you may find the function
preload_all_modules()
to be the right tool for you. This function preloads all mod_perl
2.0 modules, implementing their API in XS.

While useful for testing and development, it is not recommended to use
this function in production systems. Before going into production you
should remove the call to this function and load only the modules that
are used, in order to save memory.

CPAN module developers should not be tempted to call this function
from their modules, because it prevents the user of their module from
optimizing her system's memory usage.

The mod_perl 2.0 API is modeled even more closely upon the Apache API
than was mod_perl version 1.0. Just as the Apache 2.0 API is
substantially different from that of Apache 1.0, therefore, the
mod_perl 2.0 API is quite different from that of mod_perl
1.0. Unfortunately, this means that certain method calls and functions
that were present in mod_perl version 1.0 are missing or modified in
mod_perl 2.0.

If mod_perl 2.0 tells you that some method is missing and it can't be
found using
ModPerl::MethodLookup,
it's most likely because the method doesn't exist in the mod_perl 2.0
API. It's also possible that the method does still exist, but
nevertheless it doesn't work, since its usage has changed (e.g. its
prototype has changed, or it requires different arguments, etc.).

Some methods that existed in mod_perl 1.0 simply do not exist anywhere
in version 2.0 and you must therefore call a different method o
methods to get the functionality you want.

For example, suppose we have a mod_perl 1.0 code snippet:

$r->log_reason("Couldn't open the session file: $@");

If we try to run this under mod_perl 2.0 it will complain about the
call to log_reason(). But when we use ModPerl::MethodLookup to see
which module to load in order to call that method, nothing is found:

Some methods still exist, but their usage has been modified, and your
code must call them in the new fashion or it will generate an
error. Most often the method call requires new or different arguments.

For example, say our mod_perl 1.0 code said:

$parsed_uri = Apache2::URI->parse($r, $r->uri);

This code causes mod_perl 2.0 to complain first about not being able
to load the method parse() via the package Apache2::URI. We use the
tools described above to discover that the package containing our
method has moved and change our code to load and use APR::URI:

$parsed_uri = APR::URI->parse($r, $r->uri);

But we still get an error. It's a little cryptic, but it gets the
point across:

p is not of type APR::Pool at /path/to/OurModule.pm line 9.

What this is telling us is that the method parse requires an
APR::Pool object as its first argument. (Some methods whose usage has
changed emit more helpful error messages prefixed with "Usage: ...")
So we change our code to:

If it is not possible to make your code run under both mod_perl
versions (see below), you will have to maintain two separate versions
of your own code. While you can change the name of the module for the
new version, it's best to try to preserve the name and use some
workarounds.

Even if you have followed the recommendation and eschewed use of the
Apache2::compat module, you may
find it useful to learn how the API has been changed and how to modify
your own code. Simply look at the Apache2::compat source code and see
how the functionality should be implemented in mod_perl 2.0.

For example, mod_perl 2.0 doesn't provide the Apache->gensym
method. As we can see if we look at the Apache2/compat.pm source,
the functionality is now available via the core Perl module Symbol
and its gensym() function. (Since mod_perl 2.0 works only with Perl
versions 5.6 and higher, and Symbol.pm is included in the core Perl
distribution since version 5.6.0, there was no reason to keep
providing Apache->gensym.)

Apache::MP3 is an elaborate application that uses a lot of mod_perl
API. After porting it, I have realized that if you go through the
notes or even better try to do it by yourself, referring to the notes
only when in trouble, you will most likely be able to port any other
mod_perl 1.0 module to run under mod_perl 2.0. So here the log of what
I have done while doing the porting.

Please notice that this tutorial should be considered as-is and I'm
not claiming that I have got everything polished, so if you still find
problems, that's absolutely OK. What's important is to try to learn
from the process, so you can attack other modules on your own.

I've started to work with Apache::MP3 version 3.03 which you can
retrieve from Lincoln's CPAN directory:
http://search.cpan.org/CPAN/authors/id/L/LD/LDS/Apache-MP3-3.03.tar.gz
Even though by the time you'll read this there will be newer versions
available it's important that you use the same version as a starting
point, since if you don't, the notes below won't make much sense.

First of all, I scratched most of mine httpd.conf and startup.pl
leaving the bare minimum to get mod_perl started. This is needed to
ensure that once I've completed the porting, the module will work
correct on other users systems. For example if my httpd.conf and
startup.pl were loading some other modules, which in turn may load
modules that a to-be-ported module may rely on, the ported module may
work for me, but once released, it may not work for others. It's the
best to create a new httpd.conf when doing the porting putting only
the required bits of configuration into it.

tells Apache2::Reload to monitor only modules in the ModPerl::
and Apache:: namespaces. So Apache::MP3 will be monitored. If
your module is named Foo::Bar, make sure to include the right
pattern for the ReloadModules directive. Alternatively simply have:

PerlSetVar ReloadAll On

which will monitor all modules in %INC, but will be a bit slower,
as it'll have to stat(3) many more modules on each request.

Finally, Apache::MP3 uses constant subroutines. Because of that you
will get lots of warnings every time the module is modified, which I
wanted to avoid. I can safely shut those warnings off, since I'm not
going to change those constants. Therefore I've used the setting

Before I even started porting Apache::MP3, I've added the warnings
pragma to Apache/MP3.pm (which wasn't there because mod_perl 1.0
had to work with Perl versions prior to 5.6.0, which is when the
warnings pragma was added):

From now on, I'm going to use unified diffs which you can apply using
patch(1). Though you may have to refer to its manpage on your
platform since the usage flags may vary. On linux I'd apply the above
patch as:

The problem is that handler wasn't invoked as method, but had $r
passed to it (we can tell because new() was invoked as
Apache2::RequestRec::new(), whereas it should have been
Apache::MP3::new(). Why Apache::MP3 wasn't passed as the first
argument? I go to the mod_perl 1.0 backward compatibility document and find that
method handlers
are now marked using the method subroutine attribute. So I modify
the code:

This time we get a bunch of looping redirect responses, due to a bug
in mod_dir which kicks in to handle the existing dir and messing up
with $r->path_info keeping it empty at all times. I thought I
could work around this by not having the same directory and location
setting, e.g. by moving the location to be /songs/ while keeping
the physical directory with mp3 files as $DocumentRoot/mp3/, but
Apache::MP3 won't let you do that. So a solution suggested by
Justin Erenkrantz is to simply shortcut that piece of code with:

which is equivalent to removing this code, until the bug is fixed (it
was still there as of Apache 2.0.46). But the module still works
without this code, because if you issue a request to /mp3 (w/o
trailing slash) mod_dir, will do the redirect for you, replacing the
code that we just removed. In any case this got me past this problem.

Since I have turned on the warnings pragma now I was getting loads of
uninitialized value warnings from $r->dir_config() whose
return value were used without checking whether they are defined or
not. But you'd get them with mod_perl 1.0 as well, so they are just an
example of not-so clean code, not really a relevant obstacle in my
pursuit to port this module to mod_perl 2.0. Unfortunately they were
cluttering the log file so I had to fix them. I've defined several
convenience functions:

and replaced them as you can see in this patch:
code/apache_mp3_2.diff, it was 194 lines long so I didn't inline it
here, but it was quick to create with a few regexes search-n-replace
manipulations in xemacs.

Now I have the browsing of the root /mp3/ directory and its
sub-directories working. If I click on 'Fetch' of a particular song
it works too. However if I try to 'Stream' a song, I get a 500
response with error_log telling me:

It would be certainly nice for Socket.pm to use Carp::carp()
instead of warn() so we will know where in the Apache::MP3 code
this problem was triggered. However reading the Socket.pm manpage
reveals that sockaddr_in() in the list context is the same as
calling an explicit unpack_sockaddr_in(), and in the scalar context
it's calling pack_sockaddr_in(). So I have found sockaddr_in was
the only Socket.pm function used in Apache::MP3 and I have found
this code in the function is_local():

Since something is wrong with function calls
$r->connection->local_addr and/or
$r->connection->remote_addr and I referred to the mod_perl 1.0 backward compatibility document and found the relevant entry
on these two functions. Indeed the API have changed. Instead of
returning a packed SOCKADDR_IN string, Apache now returns an
APR::SockAddr object, which I can
query to get the bits of information I'm interested in. So I applied
this patch:

And voila, the streaming option now works. I get a warning on 'Use
of uninitialized value' on line 1516 though, but again this is
unrelated to the porting issues, just a flow logic problem, which
wasn't triggered without the warnings mode turned on. I have fixed it
with:

This completes the first part of the porting. I have tried to use all
the visible functions of the interface and everything seemed to work
and I haven't got any warnings logged. Certainly I may have missed
some usage patterns which may be still problematic. But this is good
enough for this tutorial.

and indeed, even though I've commented out the loading of
Apache2::compat from startup.pl, this module was still getting
loaded. I knew that because the request to /mp3 were failing with
the error message:

Apache2::compat is loaded loaded at ...

There are several ways to find the guilty party, you can grep(1)
for it in the perl libraries, you can override
CORE::GLOBAL::require() in startup.pl:

I've used this last technique, since it's the safest one to use.
Remember that Apache2::compat can also be loaded with:

do "Apache2/compat.pm";

in which case, neither grep(1)'ping for Apache2::compat, nor
overriding require() will do the job.

When I've restarted the server and tried to use Apache::MP3 (I
wasn't preloading it at the server startup since I wanted the server
to start normally and cope with problem when it's running), the
error_log had an entry:

Apache2::compat is loaded by at .../Apache2/compat.pm line 6
Apache2::compat::BEGIN() called at .../Apache2/compat.pm line 8
eval {...} called at .../Apache2/compat.pm line 8
require Apache2/compat.pm called at .../5.9.0/CGI.pm line 169
require CGI.pm called at .../site_perl/5.9.0/Apache/MP3.pm line 8
Apache::MP3::BEGIN() called at .../Apache2/compat.pm line 8

(I've trimmed the whole paths of the libraries and the trace itself,
to make it easier to understand.)

We could have used Carp::carp() which would have told us only the
fact that Apache2::compat was loaded by CGI.pm, but by using
Carp::cluck() we've obtained the whole stack backtrace so we also
can learn which module has loaded CGI.pm.

Here I've learned that I had an old version of CGI.pm (2.89) which
automatically loaded Apache2::compat (which should be never done by CPAN modules). Once
I've upgraded CGI.pm to version 2.93 and restarted the server,
Apache2::compat wasn't getting loaded any longer.

Notice that I did have mod_perl 1.0 installed, so the
Apache::Constant module from mod_perl 1.0 couldn't find the
boot() method which doesn't exist in mod_perl 2.0. If you don't
have mod_perl 1.0 installed the error would simply say, that it can't
find Apache/Constants.pm in @INC. In any case, we are going to
replace this code with mod_perl 2.0 equivalent:

and I also had to adjust the constants, since what used to be OK,
now has to be Apache2::Const::OK, mainly because in mod_perl 2.0 there is
an enormous amount of constants (coming from Apache and APR) and most
of them are grouped in Apache2:: or APR:: namespaces. The
Apache2::Const and
APR::Const manpage provide more
information on available constants.

The porting document quickly
reveals
me that header_in() and its brothers header_out() and
err_header_out() are R.I.P. and that I have to use the
corresponding functions headers_in(), headers_out() and
err_headers_out() which are available in mod_perl 1.0 API as well.

The AUTOLOAD code helped me to trace the modules that contain the
existing APIs, however I still have to deal with APIs that no longer
exist. Rightfully the helper code says that it doesn't know which
module defines the method: send_http_header() because it no longer
exists in Apache 2.0 vocabulary:

This technique is no longer needed in 2.0, since Apache 2.0
automatically discards the body if the request is of type HEAD --
the handler should still deliver the whole body, which helps to
calculate the content-length if this is relevant to play nicer with
proxies. So you may decide not to make a special case for HEAD
requests.

At this point I was able to browse the directories and play files via
most options without relying on Apache2::compat.

There were a few other APIs that I had to fix in the same way, while
trying to use the application, looking at the error_log referring
to the porting document and
applying the suggested fixes. I'll make sure to send all these fixes
to Lincoln Stein, so the new versions will work correctly with
mod_perl 2.0. I also had to fix other Apache::MP3:: files, which
come as a part of the Apache-MP3 distribution, pretty much using
the same techniques explained here. A few extra fixes of interest in
Apache::MP3 were:

send_fd()

As of this writing we don't have this function in the core, because
Apache 2.0 doesn't have it (it's in Apache2::compat but implemented
in a slow way). However we may provide one in the future. Currently
one can use the function sendfile() which requires a filename as an
argument and not the file descriptor. So I have fixed the code:

I have found the porting process to be quite interesting, especially
since I have found several bugs in Apache 2.0 and documented a few
undocumented API changes. It was also fun, because I've got to listen
to mp3 files when I did things right, and was getting silence in my
headphones and a visual irritation in the form of error_log
messages when I didn't ;)

Sometimes code needs to work with both mod_perl versions. For example
this is the case with CPAN module developers who wish to continue to
maintain a single code base, rather than supplying two separate
implementations.

Some modules, like CGI.pm may work under mod_perl and without it,
and will want to use the mod_perl 1.0 API if that's available, or
mod_perl 2.0 API otherwise. So the following idiom could be used for
this purpose.

It sets the constant MP_GEN to 0 if mod_perl is not available, to 1
if running under mod_perl 1.0 and 2 for mod_perl 2.0.

Here's another way to find out the mod_perl version. In the server
configuration file you can use a special configuration "define" symbol
MODPERL2, which is magically enabled internally, as if the server
had been started with -DMODPERL2.

From within Perl code this can be tested with
Apache2::exists_config_define(). For example, we can use this method
to decide whether or not to call $r->send_http_header(), which
no longer exists in mod_perl 2.0:

Method handlers in mod_perl are declared using the 'method' attribute. However
if you want to have the same code base for mod_perl 1.0 and 2.0
applications, whose handler has to be a method, you will need to do
the following trick:

Distributors should mark the different generations of mod_perl core as
conflicting, so only one version can be installed using the binary
package. Users requiring more than one installation should do a manual
install.

In order to have any of the 3rd party modperl modules installed users
need to have the correct modperl package installed. So there is no
need to mark the 3rd party modules as conflicting, since their most
important prerequisite (the modperl-core) is already handling that.

Of course packagers can decide to make the two generation packages as
non-conflicting, by building all mp2 core and 3rd party modules into
Apache2/ subdir, in which case the two will always co-exist. But
this is not the most logical approach since 99% of users will want
only one generation of mod_perl core and 3rd party modules.