Show Contents Previous Page Next Page Chapter 8 - Customizing the Apache Configuration Process / The Apache Configuration Directive API Apache provides an API for defining configuration directives. You provide the directive's name, syntax, and a string briefly summarizing the directive's intended usage. You may also limit the applicability of the directive to certain parts of the configuration files. Apache parses the directive and passes the parsed structure to your module for processing. Your module will then use this information to set up global variables or initialize whatever it needs.
The process of defining new configuration directives is not as simple as other parts of the Perl API. This is because configuration directives are defined in a compiled C structure that cannot be built dynamically at runtime. In order to work with this restriction,
We'll take you through a short example first to show you the whole process and then get into the details later. Our candidate for adding configuration directives is the Apache::PassThru module (Example 7-10), which transparently maps portions of the server's document tree onto remote servers using Apache's proxy mechanism. As you may recall, Apache::PassThru used a single PerlSetVar variable named Perl-PassThru, which contained a series of URI=>proxy pairs stored in one long string. Although this strategy is adequate, it's not particularly elegant. Our goal here is to create a new first-class configuration directive named Perl-PassThru. Perl-Pass-Thru will take two arguments: a local URI and a remote URI to map it to. You'll be able to repeat the directive to map several local URIs to remote servers. Because it makes no sense for the directory to appear in directory sections or .htaccess files, PerlPassThru will be limited to the main parts of the httpd.conf, srm.conf, and access.conf files, as well as to <VirtualHost> sections. First we'll need something to start with, so we use h2xs to create a skeletal module directory: % h2xs -Af -n Apache::PassThru Writing Apache/PassThru/PassThru.pm Writing Apache/PassThru/PassThru.xs Writing Apache/PassThru/Makefile.PL Writing Apache/PassThru/test.pl Writing Apache/PassThru/Changes Writing Apache/PassThru/MANIFEST The -A and -f command-line switches turn off the generation of autoloader stubs and the C header file conversion steps, respectively. -n gives the module a name. We'll be editing the files Makefile.PL and PassThru.pm. PassThru.xs will be overwritten when we go to make the module, so there's no need to worry about it. The next step is to edit the Makefile.PL script to add the declaration of the Perl-Pass-Thru directive and to arrange for Apache::ExtUtils' command_table() function to be executed at the appropriate moment. Example 8-1 shows a suitable version of the file. Example 8-1. Makefile.PL for the Improved Apache::PassThru package Apache::PassThru; # File: Apache/PassThru/Makefile.PL use ExtUtils::MakeMaker; use Apache::ExtUtils qw(command_table); use Apache::src (); my @directives = ( { name => 'PerlPassThru', errmsg => 'a local path and a remote URI to pass through to', args_how => 'TAKE2', req_override => 'RSRC_CONF' } ); command_table(\@directives); WriteMakefile( 'NAME' => __PACKAGE__, 'VERSION_FROM' => 'PassThru.pm', 'INC' => Apache::src->new->inc, 'INSTALLSITEARCH' => '/usr/local/apache/lib/perl', 'INSTALLSITELIB' => '/usr/local/apache/lib/perl', ); __END__
We've made multiple modifications to the Makefile.PL originally produced by h2xs. First, we've placed a package declaration at the top, putting the whole script in the Apache::PassThru namespace. Then, after the original
Next, we define the new configuration directives themselves. We create a list named
Each element of the list is an anonymous hash containing one or more of the keys
The last key, Having defined our configuration directive array, we pass a reference to it to the command_table() function. When run, this routine writes out a file named PassThru.xs to the current directory. command_table() uses the package information returned by the Perl caller() function to figure out the name of the file to write. This is why it was important to include a package declaration at the top of the script.
The last part of Makefile.PL is the call WriteMakefile(), a routine provided by ExtUtils::MakeMaker and automatically placed in Makefile.PL by h2xs. However, we've modified the autogenerated call in three small but important ways. The The next step is to modify PassThru.pm to accommodate the new configuration directive. We start with the file from Example 7-10 and add the following lines to the top: use Apache::ModuleConfig (); use DynaLoader (); use vars qw($VERSION); $VERSION = '1.00'; if($ENV{MOD_PERL}) { no strict; @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); }
This brings in code for fetching and modifying the current configuration settings and loads the DynaLoader module, which provides the bootstrap() routine for loading shared library code. We test the Next, we add the following configuration processing callback routine to the file: sub PerlPassThru ($$$$) { my($cfg, $parms, $local, $remote) = @_; $cfg->{PassThru}{$local} = $remote; }
The callback (also known as the "directive handler") is a subroutine that will be called each time Apache processes a PerlPassThru directive. It is responsible for stashing the information into a configuration record where it can be retrieved later by the handler() subroutine. The name of the subroutine must exactly match the name of the configuration directive, capitalization included. It should also have a prototype that correctly matches the syntax of the configuration directive. All configuration callbacks are called with at least two scalar arguments, indicated by the function prototype
Callbacks may be passed other parameters as well, corresponding to the arguments of the configuration directive that the callback is responsible for. Because PerlPassThru is a The body of the subroutine is trivial. For all intents and purposes, the configuration object is a hash reference in which you can store arbitrary key/value pairs. The convention is to choose a key with the same name as the configuration directive. In this case, we use an anonymous hash to store the current local and remote URIs into the configuration object at a key named PassThru. This allows us to have multiple mappings while guaranteeing that each local URI is unique. The Apache::PassThru handler() subroutine needs a slight modification as well. We remove this line: my %mappings = split /\s*(?:,|=>)\s*/, $r->dir_config('PerlPassThru'); and substitute the following: my %mappings = (); if(my $cfg = Apache::ModuleConfig->get($r)) { %mappings = %{ $cfg->{PassThru} } if $cfg->{PassThru}; } We call the Apache::ModuleConfig class method get(), passing
it the request object. This retrieves the same configuration object that was
previously processed by PerlPassThru(). We then fetch the value of
the configuration object's PassThru key. If the key is present, we
dereference it and store it into The last step is to arrange for Apache::PassThru to be loaded at server startup time. The easiest way to do this is to load the module with a PerlModule directive: PerlModule Apache::PassThru
The only trick to this is that you must be careful that the PerlModule directive is called before any PerlPassThru directives appear. Otherwise, Apache won't recognize the new directive and will abort with a configuration file syntax error. The other caveat is that PerlModule only works to bootstrap configuration directives in <Perl> use Apache::PassThru (); </Perl> <Perl> sections are described in detail toward the end of this chapter. Now change the old Apache::PassThru configuration to use the first-class Perl-PassThru directive: PerlModule Apache::PassThru PerlTransHandler Apache::PassThru PerlPassThru /CPAN http://www.perl.com/CPAN PerlPassThru /search http://www.altavista.com After restarting the server, you should now be able to test the Apache::PassThru handler to confirm that it correctly proxies the /CPAN and /search URIs. If your server has the mod_info module configured, you should be able to view the entry for the Apache::PassThru module. It will look something like this: Module Name: Apache::PassThru Content handlers: none Configuration Phase Participation: Create Directory Config, Create Server Config Request Phase Participation: none Module Directives: PerlPassThru - a local path and a remote URI to pass through to Current Configuration: httpd.conf PerlPassThru /CPAN http://www.perl.com/CPAN PerlPassThru /search http://www.altavista.com Now try changing the syntax of the PerlPassThru directive. Create a directive that has too many arguments or one that has too few. Try putting the directive inside a <Directory> section or .htaccess file. Any attempt to violate the syntax restrictions we specified in Makefile.PL with the args_how and req_override keys should cause a syntax error at server startup time. Example 8-2. Apache::PassThru with a Custom Configuration Directive package Apache::PassThru; # file: Apache/PassThru.pm; use strict; use vars qw($VERSION); use Apache::Constants qw(:common); use Apache::ModuleConfig (); use DynaLoader (); $VERSION = '1.00'; if($ENV{MOD_PERL}) { no strict; @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); } sub handler { my $r = shift; return DECLINED if $r->proxyreq; my $uri = $r->uri; my %mappings = (); if(my $cfg = Apache::ModuleConfig->get($r)) { %mappings = %{ $cfg->{PassThru} } if $cfg->{PassThru}; foreach my $src (keys %mappings) { next unless $uri =~ s/^$src/$mappings{$src}/; $r->proxyreq(1); $r->uri($uri); $r->filename("proxy:$uri"); $r->handler('proxy-server'); return OK; } return DECLINED; } sub PerlPassThru ($$$$) { my($cfg, $parms, $local, $remote) = @_; unless ($remote =~ /^http:/) { die "Argument '$remote' is not a URL\n"; } $cfg->{PassThru}{$local} = $remote; } 1; __END__Show Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |
HIVE: All information for read only. Please respect copyright! |