Show Contents Previous Page Next Page Chapter 8 - Customizing the Apache Configuration Process / The Apache Configuration Directive API As a full example of creating custom configuration directives, we're going to reimplement the standard mod_mime module in Perl. It has a total of seven different directives, each with a different argument syntax. In addition to showing you how to handle a complex configuration setup, this example will show you in detail what goes on behind the scenes as mod_mime associates a content handler with each URI request. This module replaces the standard mod_mime module. You do not have to remove mod_mime from the standard compiled-in modules in order to test this module. However, if you wish to remove mod_mime anyway in order to convince yourself that the replacement actually works, the easiest way to do this is to compile mod_mime as a dynamically loaded module and then comment out the lines in httpd.conf that load it. In either case, install Apache::MIME as the default MIME-checking phase handler by putting this line in perl.conf or one of the other configuration files: PerlTypeHandler Apache::MIME Like the previous example, the configuration information is contained in two files. Makefile.PL (Example 8-3) describes the directives, and Apache/MIME.pm (Example 8-4) defines the callbacks for processing the directives at runtime. In order to reimplement mod_mime, we need to reimplement a total of seven directives, including SetHandler, AddHandler, AddType, and AddEncoding. Makefile.PL defines the seven directives using the anonymous hash
method. All but one of the directives is set to use the Running perl Makefile.PL will now create a Example 8-3. Makefile.PL for Apache::MIME package Apache::MIME; # File: Makefile.PL use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. use Apache::src (); use Apache::ExtUtils qw(command_table); my @directives = ( { name => 'SetHandler', errmsg => 'a handler name', req_override => 'OR_FILEINFO' }, { name => 'AddHandler', errmsg => 'a handler name followed by one or more file extensions', command_table \@directives; WriteMakefile( 'NAME' => __PACKAGE__, 'VERSION_FROM' => 'MIME.pm', 'INC' => Apache::src->new->inc, ); __END__ Turning to Example 8-4, we start by bringing in the DynaLoader and Apache::ModuleConfig modules as we did in the overview example at the beginning of this section: package Apache::MIME; # File: Apache/MIME.pm use strict; use vars qw($VERSION @ISA); use LWP::MediaTypes qw(read_media_types guess_media_type add_type add_encoding); @ISA = qw(DynaLoader); $VERSION = '0.01'; if($ENV{MOD_PERL}) { no strict; @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); } We also bring in Apache, Apache::Constants, and an LWP
library called LWP::Media-Types. The Apache and Apache::Constants
libraries will be used within the handler() subroutine, while the
LWP library provides utilities for guessing MIME types, languages, and encodings
from file extensions. As before, Apache::MIME needs to call bootstrap()
immediately after loading other modules in order to bring in its compiled
Let's skip over handler() for the moment and look at the seven configuration callbacks: TypesConfig(), AddType(), AddEncoding(), and so on. sub TypesConfig ($$$) { my($cfg, $parms, $file) = @_; my $types_config = Apache->server_root_relative($file); read_media_types($types_config); #to co-exist with mod_mime.c return DECLINE_CMD if Apache->module("mod_mime.c"); } TypesConfig() has a function prototype of Another important detail is that the TypesConfig handler will return
sub AddType ($$@;@) { my($cfg, $parms, $type, $ext) = @_; add_type($type, $ext); } The AddType() directive callback is even shorter. Its function
prototype is AddType application/x-chicken-feed .corn .barley .oats the function will be called three times. Each time the callback is invoked its third argument will be application/x-chicken-feed and the fourth argument will be successively set to .corn, .barley, and .oats. The function recovers the third and fourth parameters and passes them to the LWP::MediaTypes function add_type(). This simply adds the file type and extension to LWP's internal table. sub AddEncoding ($$@;@) { my($cfg, $parms, $enc, $ext) = @_; add_encoding($enc, $ext); } AddEncoding() is similar to AddType() but uses the LWP::MediaTypesadd_encoding() function to associate a series of file extensions with a MIME encoding. More interesting are the SetHandler() and AddHandler() callbacks: sub SetHandler ($$$) { my($cfg, $parms, $handler) = @_; $cfg->{'handler'} = $handler; } sub AddHandler ($$@;@) { my($cfg, $parms, $handler, $ext) = @_; $cfg->{'handlers'}->{$ext} = $handler; } The job of the SetHandler directive is to force requests for the
specified path to be passed to the indicated content handler, no questions
asked. AddHandler(), in contrast, adds a series of file extensions
to the table consulted by the MIME type checker when it attempts to choose
the proper content handler for the request. In both cases, the configuration
information is needed again at request time, so we have to keep it in long-term
storage within the SetHandler() is again a sub ForceType ($$$) { my($cfg, $parms, $type) = @_; $cfg->{'type'} = $type; } The ForceType directive is used to force all documents in a path
to be a particular MIME type, regardless of their file extensions. It's often
used within a <Directory> section to force the type of all
documents contained within and is helpful for dealing with legacy documents
that don't have informative file extensions. The ForceType() callback
uses a sub AddLanguage ($$@;@) { my($cfg, $parms, $language, $ext) = @_; $ext =~ s/^\.//; $cfg->{'language_types'}->{$ext} = lc $language; } The last directive handler, AddLanguage(), implements the AddLanguage
directive, in which a series of file extensions are associated with a language
code (e.g., "fr" for French, "en" for English). It is an Now we turn our attention to the handler() subroutine itself. This code will be called at request time during the MIME type checking phase. It has five responsibilities:
Items 1 through 3 are important but not critical. The content type, encoding, and language may well be changed during the response phase by the content handler. In particular, the MIME type is very frequently changed (e.g., by CGI scripts). Item 4, however, is crucial since it determines what code will be invoked to respond to the request. It is also necessary to detect and treat requests for directory names specially, using a pseudo-MIME type to initiate Apache's directory handling. sub handler { my $r = shift; if(-d $r->finfo) { $r->content_type(DIR_MAGIC_TYPE); return OK; } handler() begins by shifting the Apache request object off the
subroutine stack. The subroutine now does a series of checks on the requested
document. First, it checks whether $r->finfo() refers to a directory.
If so, then handler() sets the request content type to a pseudo-MIME
type defined by the constant my($type, @encoding) = guess_media_type($r->filename); $r->content_type($type) if $type; unshift @encoding, $r->content_encoding if $r->content_encoding; $r->content_encoding(join ", ", @encoding) if @encoding; If the file is not a directory, then we try to guess its MIME type and encoding. We call on the LWP::MediaTypes function guess_media_type() to do the work, passing it the filename and receiving a MIME type and list of encodings in return. Although unusual, it is theoretically possible for a file to have multiple encodings, and LWP::MediaTypes allows this. The returned type is immediately used to set the MIME type of the requested document by calling the request object's content_type() method. Likewise, the list of encodings is added to the request using content_encoding() after joining them together into a comma-delimited string. The only subtlety here is that we honor any previously defined encoding for the requested document by adding it to the list of encodings returned by guess_media_type(). This is in case the handler for a previous phase happened to add some content encoding. Now comes some processing that depends on the values in the configuration
hash, so we recover the my $cfg = Apache::ModuleConfig->get($r); The next task is to parse out the requested file's extensions and use them to set the file's MIME type and/or language. for my $ext (LWP::MediaTypes::file_exts($r->filename)) { if(my $type = $cfg->{'language_types'}->{$ext}) { my $ltypes = $r->content_languages; push @$ltypes, $type; $r->content_languages($ltypes); } Using the LWP::MediaTypes function file_exts(), we split
out all the extensions in the requested document's filename and loop through
them. This allows a file named We first check whether the extension matches one of the extensions in the configuration object's language_types key. If so, we use the extension to set the language code for the document. Although it is somewhat unusual, the HTTP specification allows a document to specify multiple languages in its Content-Language field, so we go to some lengths to merge multiple language codes into one long list which we then set with the request object's content_languages() method. if(my $type = $cfg->{'handlers'}->{$ext} and !$r->proxyreq) { $r->handler($type); } } While still in the loop, we deal with the content handler for the request.
We check whether the extension is among the ones defined in the configuration
variable's $r->content_type($cfg->{'type'}) if $cfg->{'type'}; After looping through the file extensions, we handle the ForceType
and SetHandler directives, which have the effect of overriding file
extensions. If the configuration key return OK; } At the end of handler(), we return Although this module was presented mainly as an exercise, with minimal work it can be used to improve on mod_mime. For example, you might have noticed that the standard mod_mime has no ForceEncoding or ForceLanguage directives that allow you to override the file extension mappings in the way that you can with ForceType. This is easy enough to fix in Apache::MIME by adding the appropriate directive definitions and callbacks. Example 8-4. Apache::MIME Reimplements the Standard mod_mime Module package Apache::MIME; # File: Apache/MIME.pm use strict; use vars qw($VERSION @ISA); use LWP::MediaTypes qw(read_media_types guess_media_type add_type add_encoding); @ISA = qw(DynaLoader); $VERSION = '0.01'; if($ENV{MOD_PERL}) { no strict; @ISA = qw(DynaLoader); __PACKAGE__->bootstrap($VERSION); } sub handler { my $r = shift; if(-d $r->finfo) { $r->content_type(DIR_MAGIC_TYPE); return OK; } my($type, @encoding) = guess_media_type($r->filename); $r->content_type($type) if $type; unshift @encoding, $r->content_encoding if $r->content_encoding; $r->content_encoding(join ", ", @encoding) if @encoding; my $cfg = Apache::ModuleConfig->get($r); for my $ext (LWP::MediaTypes::file_exts($r->filename)) { if(my $type = $cfg->{'language_types'}->{$ext}) { my $ltypes = $r->content_languages; push @$ltypes, $type; $r->content_languages($ltypes); } if(my $type = $cfg->{'handlers'}->{$ext} and !$r->proxyreq) { $r->handler($type); } } $r->content_type($cfg->{'type'}) if $cfg->{'type'}; $r->handler($cfg->{'handler'}) if $cfg->{'handler'}; return OK; } sub TypesConfig ($$$) { my($cfg, $parms, $file) = @_; my $types_config = Apache->server_root_relative($file); read_media_types($types_config); #to co-exist with mod_mime.c return DECLINE_CMD if Apache->module("mod_mime.c"); } sub AddType ($$@;@) { my($cfg, $parms, $type, $ext) = @_; add_type($type, $ext); } sub AddEncoding ($$@;@) { my($cfg, $parms, $enc, $ext) = @_; add_encoding($enc, $ext); } sub SetHandler ($$$) { my($cfg, $parms, $handler) = @_; $cfg->{'handler'} = $handler; } sub AddHandler ($$@;@) { my($cfg, $parms, $handler, $ext) = @_; $cfg->{'handlers'}->{$ext} = $handler; } sub ForceType ($$$) { my($cfg, $parms, $type) = @_; $cfg->{'type'} = $type; } sub AddLanguage ($$@;@) { my($cfg, $parms, $language, $ext) = @_; $ext =~ s/^\.//; $cfg->{'language_types'}->{$ext} = lc $language; } 1; __END__Show Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |
HIVE: All information for read only. Please respect copyright! |