Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Perl Web Development - From Novice To Professional (2006)

.pdf
Скачиваний:
56
Добавлен:
17.08.2013
Размер:
2.9 Mб
Скачать

200C H A P T E R 1 0 A PA C H E A N D M O D _ P E R L

every time they are run. As such, namespaces are cleared with every run, thus resolving many of the problems that cause the programs to run incorrectly under Apache::Registry. Obviously, since the programs are not cached under Apache::PerlRun, the performance is not as good as it is with Apache::Registry.

Modifying the Apache configuration for Apache::PerlRun is easy. Only two lines need to be changed, namely the lines that refer to Apache::Registry:

Alias /perl/ /home/suehring/perl/

PerlModule Apache::PerlRun

<Location /perl/> SetHandler perl-script

PerlHandler Apache::PerlRun

Options +ExecCGI PerlSendHeader On Allow from all

</Location>

Security Considerations with mod_perl

mod_perl introduces a certain set of security issues into the mix of securing a web server. When using mod_perl, the namespaces are shared within Apache’s memory. It is possible for attackers to gain access to areas of the memory that they shouldn’t be able to reach. This is especially the case on shared hosting servers.

Securing Apache is an expansive subject area best left to a book on Apache. If you’re running a web server, I do recommend running it within a chroot environment to limit the damage from

a successful attack. (For more information about chroot, see http://www.braingia.org/projects/.)

Summary

This chapter introduced the mod_perl Apache module. mod_perl enables high-performance execution of Perl programs in Apache and is meant to replace the normal mod_cgi Apache module for executing Perl programs and CGI scripts. mod_perl gains access to the entire Apache request object and can therefore be used for much more than just executing CGI programs. For example, mod_perl enables the developer to write Apache modules in Perl rather than in the traditional C language.

This chapter began with a look at how Apache handles requests and continued into the installation and configuration of mod_perl. The next chapter covers development in mod_perl.

C H A P T E R 1 1

■ ■ ■

Development with mod_perl

The previous chapter showed how to install mod_perl with Apache in order to achieve better performance from web applications created in Perl. Recall that mod_perl embeds the Perl interpreter into the Apache process, thus making the execution of Perl programs much faster than when you use the normal mod_cgi module. But mod_perl is much more than a way to get CGI programs to run faster. mod_perl also enables programs to access many parts of the request/ response cycle and manipulate those in order to not only increase performance, but fundamentally change the way that the server itself operates.

In this chapter, you’ll learn how to build programs that run under mod_perl and take advantage of its features. As this book was wrapping up, mod_perl version 2.0 was officially released. As with the choice to cover the Apache 1.3 series, I chose to cover the mod_perl 1.0 series due to the sheer number of mod_perl 1.0 series installations available today.

Thinking in mod_perl

Ideally, every CGI script that you’ve already written will work “out of the box” with mod_perl. If you’ve incorporated the use strict pragma throughout your CGI programs, then you’re a good way toward having them work in mod_perl. However, in practice, it’s rare to have complex programs written for mod_cgi work in mod_perl without at least some debugging. This section begins with a look at some initial considerations for converting programs from mod_cgi to mod_perl, followed by a more detailed look at the Apache::Registry and Apache::PerlRun modules.

Note In the world of mod_perl version 2.0, Apache::Registry has been renamed to Modperl::Registry.

Initial Considerations

Recall from Chapter 10 that mod_perl can be used to create handlers for any phase during the Apache request process. The actual serving of the content—what you might consider the web page—is done during this process. During the request process, one of the handlers, PerlHandler, is invoked. Through PerlHandler, you specify exactly how you would like mod_perl to serve the content.

201

202 C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

Recall also that there are two Perl modules primarily used for PerlHandler: Apache::Registry and Apache::PerlRun. Apache::Registry is the better performing of the two modules. Apache::PerlRun is the module most often used during the transition phase between mod_cgi- based programs and mod_perl-based programs.

Warnings and Taint Checking

It’s common to use the -w option to enable warnings for Perl programs, as well as to enable taint checking with -T. This is usually accomplished on the shebang line, like this:

#!/usr/bin/perl -wT

However, many programs written for mod_perl don’t use a shebang line. Therefore, there are two directives to enable warnings and taint mode. These are placed in the Apache configuration file should you choose to use them:

PerlWarn On

PerlTaintCheck On

Variable Scoping and Environment Variables

Think locally and not globally when coding for mod_perl. Global variables inevitably lead to unpredictable results when executing programs through mod_perl. If you have not thought about and coded for variable scoping, there’s a chance that your programs won’t work correctly every time when executing through a mod_perl server.

Environment variables, such as those found in %ENV and used by many CGI programs, are sent by default with mod_perl. In some cases, you might want to turn off this behavior. Use PerlSetupEnv within your Apache configuration file to disable this:

PerlSetupEnv Off

You can also set specific environment variables using PerlSetEnv. For example, if you have a CGI program that relies on an environment variable to be set, place the correct directive in the Apache configuration file:

PerlSetEnv DATASOURCE /home/suehring/customers

If the Apache process itself already has an environment variable that you merely want to take advantage of, use the PerlPassEnv directive in your Apache configuration file. For example, assume that the httpd parent process has an environment variable of DATASOURCE already set. You could access that variable with this statement:

PerlPassEnv DATASOURCE

Apache::Registry vs. Apache::PerlRun, Revisited

Remember the example in Chapter 10 that showed a script that didn’t clean up its namespace very well. The broken version of that script is shown here again, so you don’t have to flip back to refresh your memory:

C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

203

use CGI;

$query = CGI->new();

if ($required_name) { print header;

print "name is $required_name\n"; #do something else

}

else {

$required_name = $query->param("name");

}

Under Apache::Registry, the better performing of the two modules, the variable $required_name wouldn’t be null except on the very first execution of the program. On subsequent executions, that variable, having already been assigned a value, would always be set and thus would never get a new value. If you modified the program, Apache::Registry would indeed notice the change though.

Using Apache::PerlRun, namespaces are flushed after each run of the program, thus assisting in the transition from mod_cgi to mod_perl. This means that variables such as $required_name that aren’t properly initialized are cleared, just as they would be under mod_cgi. Figure 11-1 shows the same script now run under mod_perl with Apache::PerlRun.

Figure 11-1. The sample script run under Apache::PerlRun

204 C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

When submitted, the program works as expected, as illustrated in Figure 11-2.

Figure 11-2. The name Test1 is used for the first run of the program.

Figure 11-3 shows what happens when the program is run a second time, this time using the name Test2.

ogram.

C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

205

When submitted, you can see from Figure 11-4 that the name is now Test2, indicating that the namespace was cleared as expected by Apache::PerlRun.

Figure 11-4. The correct name is used by the program on the second run.

Missing from the example shown in this section is the use strict pragma. Had use strict been used for the sample script, an error would have been raised, because $required_name wasn’t declared prior to its first use.

Although the example shown here seems contrived, in reality, it’s all too common, especially with legacy CGI programs.

Apache::PerlRun

Using Apache::PerlRun should be considered to be a quick-and-dirty workaround or hack into the world of mod_perl. It shouldn’t be considered the end solution for running a CGI program with mod_perl. Rather, Apache::PerlRun should be an interim approach while the program is revised to clean up its namespaces.

Enabling Apache::PerlRun is a matter of setting the PerlHandler to Apache::PerlRun within the Apache configuration file. Replace the following:

PerlHandler Apache::Registry

with this:

PerlHandler Apache::PerlRun

Another area where namespace pollution shows up is with shared CGI programs and older common library files. If your program uses an older library, there is a chance that it won’t properly initialize its namespace. When this happens, your first option will likely be to invoke Apache::PerlRun.

206 C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

PerlRunOnce

Sometimes, even Apache::PerlRun by itself won’t fix the problems. In such instances, you can also invoke PerlRunOnce option. With PerlRunOnce enabled, the process responsible for executing the Perl program is spawned only for the lifetime of that execution and dies thereafter. If you believe that this would cause nontrivial performance degradation, you are correct.

You set the PerlRunOnce option from within the Apache configuration, specifically within the mod_perl section. Recall the sample configuration for Apache and mod_perl from Chapter 10, now modified to use Apache::PerlRun:

<Location /perl/> SetHandler perl-script

PerlHandler Apache::PerlRun Options +ExecCGI PerlSendHeader On

Allow from all </Location>

To enable PerlRunOnce, you use the PerlSetVar option:

PerlSetVar PerlRunOnce On

The configuration now looks like this:

<Location /perl/> SetHandler perl-script

PerlHandler Apache::PerlRun PerlSetVar PerlRunOnce On Options +ExecCGI PerlSendHeader On

Allow from all </Location>

Apache::Registry

Apache::Registry is the better performing module of the two modules discussed here for serving content through PerlHandler configuration directive. Examples shown throughout the remainder of this chapter will assume the use of Apache::Registry.

Each program executed through Apache::Registry is done so by each Apache child, which compiles the program only once. This initial compile may result in the very first load of a program taking incrementally longer before the subsequent runs take advantage of that precompile.

The Apache configuration to mod_perl with Apache::Registry is typically as follows (replace /home/suehring/perl with the appropriate location for your installation):

Alias /perl/ /home/suehring/perl/ <Location /perl/>

SetHandler perl-script PerlHandler Apache::Registry

C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

207

Options +ExecCGI PerlSendHeader On Allow from all

</Location>

Preloading Perl Modules

Another area for optimization using mod_perl is to preload Perl modules, rather than loading them with each program. For example, the CGI module or the DBI module could be loaded by Apache, thus giving even better performance for programs that use those modules.

Preloading Apache::DBI

As you learned in Chapter 3, the DBI provides database access through Perl. You can preload the DBI, and even the database-dependent bits through the DBD, to enhance performance. However, in the case of the DBI, you employ another method for initialization, using a specialized module called Apache::DBI. You load this module by using the following line in the Apache configuration file:

PerlModule Apache::DBI

With that line in the Apache configuration file, the Apache::DBI module will be preloaded and will overload the DBI namespace, enabling your programs to forego the use DBI pragma and also providing just that much more speed to the program.

Resurrecting an example from Chapter 3, Listing 11-1 (Newdb.cgi) shows that the same code can be used, but notice that the use DBI pragma is now missing from the program.

Listing 11-1. Preloading the DBI

use strict;

use CGI qw/:standard/;

my $username = "dbuser"; my $password = "dbpass";

my $dsn = "dbi:mysql:mysql:192.168.1.10";

my $dbh = DBI->connect($dsn,$username,$password)

or die "Cannot connect to database: $DBI::errstr";

my $sth = $dbh->prepare("SELECT host,user FROM mysql.user");

$sth->execute()

or die "Cannot execute sth: $DBI::errstr"; print header,

start_html('MySQL Hosts and Users'), table({-border=>1}),

Tr({-align=>'CENTER',-valign=>'TOP'},

208 C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

[

th(['User','Host'])

]);

while (my ($hostname,$username) = $sth->fetchrow_array()) { if ($username eq "") {

$username = "<b>undef</b>";

}

print Tr({-align=>'CENTER',-valign=>'TOP'}, [td(["$hostname","$username"])

]);

}

print end_html;

$dbh->disconnect();

As before, the code results in an HTML table, as shown in Figure 11-5.

Figure 11-5. Code to enumerate MySQL hosts and users run through Apache::DBI

For more information about Apache::DBI, including some caveats on its use, see perldoc Apache::DBI.

C H A P T E R 1 1 D E V E L O P M E N T W I T H M O D _ P E R L

209

Preloading Other Modules and Methods

You can also preload other modules and preload your own handlers from within the Apache httpd.conf configuration file. It’s also common on dedicated mod_perl servers (as opposed to shared servers) to create a separate file with the use() statements and other handlers and modules. For example, you might create a file called mod_perlenv.pl containing the following:

#!/usr/bin/perl

use Apache::DBI;

use Apache::Registry; use My::Package;

1;

You could then include this file from the httpd.conf configuration file with the PerlRequire directive:

PerlRequire mod_perlenv.pl

Although this type of configuration isn’t required, it’s a small thing you can do to try to eke out that much more performance from mod_perl.

Working with the Apache Request Object

As you learned in Chapter 10, central to programming in mod_perl is the Apache request object. The Apache request object gives you access to the various portions of the request/response lifecycle.

Note See the src/include/httpd.h file included with the Apache source code for more information about the request/response lifecycle.

You access the Apache request object through the request() method of the Apache::Request class. The Apache::Request class, and many of the classes used throughout this chapter, are found in the libapreq module available on CPAN.

You’ll commonly see Apache::Request referred to as $r in documentation, and I see no reason to change that convention here. Instantiation of the request object is commonly done like this:

my $r = Apache->request();

As stated previously, the request object is central to programming with mod_perl. Therefore, the request object is the first argument passed into mod_perl handlers. This, in turn, makes it possible to also grab the object off the stack:

my $r = shift;