8.4 Pure mod_perl Programming


Although speed is always desirable, the real power of mod_perl comes in using the mod_perl API to process system information, path information, and posted form data. But the best part is something that can't be done with plain-Jane CGI ”mod_perl allows you to modify the guts of Apache to perform unique and specific tasks during different phases of the Apache request handling process. These phases include access control, authentication, and logging (to name just a few).

The first example, as you might guess, prints "hello, world!" First, create a Perl module. This file must be in a directory that Perl knows about. Earlier we discussed the Apache startup.pl script, and this was one line of that script:

 use lib /var/www/mod_perl; 

This line directs Perl to check for modules. Go into that directory:

 $  cd /var/www/mod_perl  

Create a file named /var/www/mod_perl/HelloWorld.pm (the .pm filename extension means "Perl module"). These are the contents of this file:

 package HelloWorld;  # file: HelloWorld.pm  use strict;  use Apache::Constants :common;  # the handler() method  sub handler {      # shift the argument into $r      my $r = shift;      # set the content type and send the header      $r->content_type(text/html);      $r->send_http_header();      # print the HTML to say hello      $r->print(<<EOHTML);  <html>  <head>  <title>hello, world with mod_perl</title>  </head>  <body bgcolor="#ffffff">  <h1>hello, world!</h1>  hello, world! with mod_perl  </body>  </html>  EOHTML      # return OK to Apache so that it knows      # to continue as normal      return OK;  }  1; 

The first line of this file is package HelloWorld; . This creates a package (also known as a namespace in some languages) named HelloWorld . This line makes sure that any variables and subroutines we define will not clash with those from other mod_perl programs. All mod_perl programs are created as modules. This package begins with the first line and extends to the end of the file. This file will be called HelloWorld.pm .

It's always a good idea to use strict in modules. This enforces good programming style by forcing the declaration of all the variables as my variables (hence no globals ), thus eliminating many pesky problems caused by global variables. Global variables have their uses, but one should think carefully before using them.

The Apache::Constants module contains some useful constant values (such as OK ). When Apache receives a request that is associated with a mod_perl program, it invokes the handler() method, passing it an Apache request object that is stored in $r . This object has available to it the many methods defined in the mod_perl API.

The first request object method executed is content_type() , which tells Apache that the content type is text/html . The HTTP header is sent with send_http_header() . The HTML content is generated using the print() method that prints a here document. Finally, the handler() subroutine returns the value OK , which tells Apache that all is well and to continue as normal.

Again, like all modules we write, the HelloWorld.pm module must end with 1; .

Make sure this file is readable (you must do this for all the .pm files described in this chapter):

 $  chmod a+r HelloWorld.pm  

We need to tell Apache how to use this file. Add the following to the bottom of /etc/httpd/conf/httpd.conf :

 <Location /helloworld>    SetHandler    perl-script    PerlHandler   HelloWorld  </Location> 

This Apache directive says that when the /helloworld URI is requested , Apache should execute the Perl module HelloWorld (stored in the file HelloWorld.pm in the directory /var/www/mod_perl ). Restart Apache:

 #  /etc/init.d/httpd graceful  

and load it in the browser via either http://localhost/helloworld or www.opensourcewebbook.com/helloworld. You should see Figure 8.4.

Figure 8.4. hello, world! with pure mod_perl

graphics/08fig04.gif

8.4.1 An Important Note about mod_perl

Because mod_perl modules are compiled into Apache the first time they are executed, every time a module changes, the server has to be restarted. Even if the program file changes on disk, mod_perl will not recompile it for us. This can be a drawback because it's a hassle to restart the server every time the code is changed. So this is the trade-off you make to get the speed of compiled, persistent code ”you have to restart the server when you change something versus compiling Perl code every single time it's called. Once the program is developed and working properly, restarting for this reason becomes less of an issue.

8.4.2 Handling Posted Form Data with mod_perl

As you have seen, a major task of CGI programs is handling posted form data. mod_perl programs need to be able to perform the same task. Using mod_perl is a bit different from CGI.pm because we use the mod_perl API.

Let's see an example. Place the following in /var/www/mod_perl/HandleCGIData.pm :

 package HandleCGIData;  # file: HandleCGIData.pm  use strict;  use Apache::Constants :common;  sub handler {      # shift the argument into $r      my $r = shift;      # initialize %params, then assign      # it the form data depending on      # the method used      my %params = ();      if ($r->method() eq POST) {          %params = $r->content();      } else{          %params = $r->args();      }      # set the content type and send the      # header      $r->content_type(text/html);      $r->send_http_header();      # print initial HTML      $r->print(<<EOHTML);  <html>  <head>  <title>Processing CGI Parameters</title>  </head>  <body bgcolor="#ffffff">  <h1>Processing CGI Parameters</h1>  EOHTML      # loop through the sorted keys of      # %params, printing the key (form      # widget name) followed by the      # value (form widget value)      foreach my $key (sort keys %params) {          print "$key: <b>$params{$key}</b><br>";      }      # print the final HTML, return OK      $r->print(<<EOHTML);  </body>  </html>  EOHTML      return OK;  }  1; 

The handler() subroutine is invoked to handle the request, so it gets the data and processes it. First the hash %params , which holds the form data received, is declared and initialized . The hash %params is assigned based on the method used to send the data to the program: either POST or GET , to call either content() (for POST ) or args() (for GET ). The end result of both these methods is the same: a list of the parameter names and values. So if the program were invoked with:

 http://localhost/cgidata?name=John+Doe%21&abc=def&ghi=923 

the hash %params would be assigned this data:

 name  => John Doe!,  abc   => def,  ghi   => 923 

N

Both content() and args() automatically decode the URL or posted data for you. In this case, the " + " is converted to the space character, and %21 is converted to " ! ".


After the parameters and their values are received, the content type is set, and the header and the starting HTML are printed.

The posted data in %params is processed using a foreach loop, printing the parameter names and their values, sorted by the names. The closing HTML is then printed, OK is returned to Apache, and the file ends with the ubiquitous 1; .

To tell Apache about this new module, we add the following to /etc/httpd/conf/httpd.conf :

 <Location /cgidata>    SetHandler    perl-script    PerlHandler   HandleCGIData  </Location> 

and restart:

 #  /etc/init.d/httpd graceful  

You can see the result by loading either one of the following URLs: http://localhost/cgidata?name=John+Doe%21&abc=def&ghi=923 or www.opensourcewebbook.com/cgidata?name=John+Doe%21&abc=def&ghi=923. You should see Figure 8.5. Remember, the Perl motto is TMTOWTDI ”There's More Than One Way to Do It. We could have written the if statement where we assign %params as follows :

Figure 8.5. Handling CGI data with mod_perl

graphics/08fig05.gif

 my %params = ($r->args(), $r->content()); 

There is one limitation with the approach we've taken so far: If any of the parameters have more than one value (such as if you have a scrolling list box where more than one choice can be selected), only the last value will be used. To deal with this, we can use the following:

 my %params;  my @args = ($r->args(),$r->content());  while (my($name,$value) = splice(@args, 0, 2)) {      push @{$params{$name}}, $value;  } 

To get at the value for a particular parameter, we do this:

 @values = @{$params{$parameter_name}}; 

There's one problem with this: It's way too complicated! [5] As you might expect, those canny Open Source hackers have come up with a better way, a slight variation on how we write basic CGI programs ” Apache::Request .

[5] This code is dereferencing an array reference, copying its contents into @values . Array references (and hash references, for that matter) are very interesting, fun, and powerful. Unfortunately, they are beyond the scope of this book.

8.4.3 Apache::Request

To use Apache::Request , you must install it, because the module is not installed by default. This is a simple process.

Download the latest version of the module. This module is part of a package called libapreq , and the latest version as of the writing of this book is 0.33 (as usual, this is likely to change over time). You can download the tarball from www.cpan.org/modules/by-module/Apache/libapreq-0.33.tar.gz into a convenient directory ”a good place for this is /tmp . Follow these simple steps to install the module (or use the CPAN modules as shown in Chapter 4):

 $  cd /tmp  $  tar xzvf libapreq-0.33.tar.gz  $  cd libapreq-0.33  $  perl Makefile.PL  $  make  $  su  #  make install  

Voil  ! You just installed the module. By the way, this does not require us to restart Apache; like all other Perl modules (as opposed to mod_perl modules), this module is loaded when asked with use Apache::Request .

To show an example using this module, we return to our directory of mod_perl modules:

 $  cd /var/www/mod_perl  

Create the file HandleCGIData2.pm and put the following code in it:

 package HandleCGIData2;  # file: HandleCGIData2.pm  # use statements, including  # our new Apache::Request module  use strict;  use Apache::Constants :common;  use Apache::Request;  sub handler {      # shift the argument and pass it into      # the new() method in the Apache::Request      # class      my $r = Apache::Request->new(shift);      # we can now call param() with the Apache::Request      # object in a similar way to using CGI.pm      my $name = $r->param(name)  John Doe;      my $age  = $r->param(age)   50;      # set the content type and send the header      $r->content_type(text/html);      $r->send_http_header();      # print the HTML, including our variables      # $name and $age      $r->print(<<EOHTML);  <html>  <head>  <title>Using Apache::Request</title>  </head>  <body bgcolor="#ffffff">  <h1>Using Apache::Request</h1>  name = $name<br>  age = $age  </body>  </html>  EOHTML      return OK;  }  1; 

The difference in this version of handler() with Apache::Request is in how the data passed into the program is obtained. We no longer need to call either content() or args() $r->param() is called in a way that is similar to the way param() was called using CGI.pm. The program then prints the HTML, returns OK , and ends with 1; .

The next thing to do is tell Apache about this new module by adding the following to /etc/httpd/conf/httpd.conf :

 <Location /cgidata2>    SetHandler    perl-script    PerlHandler   HandleCGIData2  </Location> 

Restart Apache:

 #  /etc/init.d/httpd graceful  

Then load one of these two URLs: http://localhost/cgidata2?name=John+Doe&age=39 or www.opensourcewebbook.com/cgidata2?name=John+Doe&age=39. You should see Figure 8.6.

Figure 8.6. Handling CGI data with mod_perl using Apache::Request

graphics/08fig06.gif

8.4.4 Getting the Path Information

The path information can be obtained simply by calling the path_info() method as shown in the following example contained within PathInfo.pm :

 package PathInfo;  # file: PathInfo.pm  use strict;  use Apache::Constants :common;  sub handler {      # the argument into $r      my $r = shift;      # call the path_info() method to get      # the path information      my $path_info = $r->path_info();      # set the content type and send the header      $r->content_type(text/html);      $r->send_http_header();      # print the HTML      $r->print(<<EOHTML);  <html>  <head>  <title>Path Information with mod_perl</title>  </head>  <body bgcolor="#ffffff">  <h1>Path Information with mod_perl</h1>  We were passed this path information: <b>$path_info</b>  </body>  </html>  EOHTML      return OK;  }  1; 

As shown in the handler() method, to obtain the path information (everything after the /pathinfo URI and before the ? ), we execute the path_info() method.

Again, Apache must be reconfigured for this new module. Modify the Apache configuration file by adding this to /etc/httpd/conf/httpd.conf :

 <Location /pathinfo>    SetHandler    perl-script    PerlHandler   PathInfo  </Location> 

Load the altered Apache configuration file:

 #  /etc/init.d/httpd graceful  

Then load one of these two URLs: http://localhost/pathinfo/hello/world or www.opensourcewebbook.com/pathinfo/hello/world. What other path information would we send but /hello/world ? We should see Figure 8.7.

Figure 8.7. Path information with mod_perl

graphics/08fig07.gif



Open Source Development with Lamp
Open Source Development with LAMP: Using Linux, Apache, MySQL, Perl, and PHP
ISBN: 020177061X
EAN: 2147483647
Year: 2002
Pages: 136

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net