NAME
Apache2::Translation - Configuring Apache dynamically
SYNOPSIS
LoadModule perl_module /path/to/mod_perl.so
PerlLoadModule Apache2::Translation
PerlTransHandler Apache2::Translation
TranslationEvalCache 1000
TranslationKey MyKey
TranslationProvider DB \
database=dbi:mysql:dbname:host \
user=username password=password \
singleton=1 \
table=tablename \
key=keycolumn \
uri=uricolumn \
block=blockcolumn \
order=ordercolumn \
action=actioncolumn \
cachetbl=cachetablename \
cachecol=cachecolumn \
cachesize=1000
DESCRIPTION
As the name implies Apache2::Translation
lives mostly in the URI Translation Phase. It is somehow similar to mod_rewrite
but configuration statements are read at runtime, thus, making it possible to reconfigure a server without restarting it.
The actual configuration statements are read by means of a Translation Provider, a Perl class offering a particular interface, see below. Currently there is one provider implemented, Apache2::Translation::DB. It reads the configuration from a database via DBI.
An Example
Let's begin with an example. Given some database table:
id key uri blk ord action
1 front :PRE: 0 0 Cond: $HOSTNAME !~ /^(?:www\.)xyz\.(?:com|de)$/
2 front :PRE: 0 1 Redirect: 'http://xyz.com'.$URI, 301
3 front :PRE: 1 0 Do: $ctx->{lang}='en'
4 front :PRE: 1 1 Cond: $HOSTNAME =~ /de$/
5 front :PRE: 1 2 Do: $ctx->{lang}='de'
6 front /static 0 0 File: $DOCROOT.'/'.$ctx->{lang}.$MATCHED_PATH_INFO
7 front /appl1 0 0 Proxy: 'http://backend/'.$ctx->{lang}.$URI
8 front /appl2 0 0 Proxy: 'http://backend/'.$URI.'?l='.$ctx->{lang}
9 front / 0 0 Config: ['AuthName "secret"'], ['AuthType Basic']
10 back :PRE: 0 0 Cond: $r->connection->remote_ip ne '127.0.0.1'
11 back :PRE: 0 1 Error: 403, 'Forbidden by Apache2::Translation(11)'
12 back /appl1 0 0 PerlHandler: 'My::Application1'
13 back /appl2 0 0 PerlHandler: 'My::Application2'
The id
column in this table is not really necessary. It is given to refer to single records.
Well, here we have a frontend/backend configuration. The frontend records are labeled with the key front
, the backend records with back
.
When a request comes in first the records with a :PRE:
uri are examined. Suppose, a request for http://abc.com/static/img.png
comes in. Record 1 (id=1) checks the Host
header. The expression afer Cond:
is evaluated as Perl code. It obviously returns true. Cond
stands for condition. But how does it affect the further workflow? Here blk
and ord
come in. All records with the same key
, uri
and blk
form a block. ord
gives an order within this block. Within a block all actions are executed up to the first condition that is false.
Now, because our condition in record 1 is true the action in record 2 (within the same block) is executed. It redirects the browser with a HTTP code of 301 (MOVED PERMANENTLY) to http://xyz.com/static/img.png
.
When the redirected request comes back the condition in record 1 is false. Hence, the next block (key=front, uri=:PRE:, blk=1) is evaluated. First a lang
member of a context hash is set to en
. A Do
action is similar to a condition, only its value is ignored. Record 4 then checks if the Host
header matches /de$/
. If so, then record 5 sets the language to de
.
Now, the records labeled with :PRE:
are finished. The handler starts looking for blocks labeled with the request uri. That is, it looks for a block with key=front, uri=/static/img.png. None is found.
Then it cuts off the last part of the uri (/img.png), repeats the lookup and finds record 6. The File
action sets $r-
filename> to $DOCROOT/en/img.png
. Apache2::Translation
provides some convenience variables. They are tied to members of the request record or to elements of $ctx
. $MATCHED_PATH_INFO
contains the uri part cut off (/img.png
). More on them below.
Now another round is started and the next uri part is cut off. Record 9 matches. We see a Config
action that sets AuthName
and AuthType
.
At the end the translation handler checks if $r-
filename> was set and returns Apache2::Const::OK
or Apache2::Const::DECLINED
respectively.
I think that example gives a general idea, what Apache2::Translation
does.
Processing States
Internally Apache2::Translation
is implemented as a state machine. It starts in the START state, where some variables are initialized. From there it shifts immediately to the PREPOC state. Here all :PRE:
rules are evaluated. From PREPROC it shifts to PROC. Now the rules with real uris are examined. The /
uri is handled in a special state called LAST ROUND. When the DONE state is reached processing is finished.
You can control the current state by means of the State
, Done
and Restart
actions.
Blocks and Lists of Blocks
Above, we have defined a block as all records with the same key
, uri
and block
. The actions within a block are ordered by the order
field.
A list of blocks is then an ordered list of all blocks with the same key
and uri
. The order is given by the block
number.
Actions
An action starts with a key word optionally followed by a colon and some arguments. The key words are case insensitive.
Apache2::Translation
provides some environment for code snippets in actions. They are compiled into perl functions. The compiled result is cached. 2 variables, $r
and $ctx
, are provided plus a few convenience variables. $r
is the current Apache2::RequestRec
. $ctx
points to a hash that can be used to store arbitrary data. All keys beginning with a space character in that hash are reserved for Apache2::Translation
.
- Do: perl_code
-
This is the simplest action. The Perl code is evaluated in scalar context. The return value is ignored.
- Cond: perl_code
-
This is almost the same as
Do
. The return value is taken as boolean. If it is false, the current block is finished. Processing continues with the next block. - Key: string
-
string
is evaluated in scalar context. The result is assigned to the current key. The new key takes effect if the list of blocks matching the current uri is finished.For example:
id key uri blk ord action 1 dflt :PRE: 0 0 Cond: $r->connection->remote_ip eq '192.168.0.1' 2 dflt :PRE: 0 1 Key: 'spec' 3 dflt :PRE: 0 2 Do: $DEBUG=3 4 dflt :PRE: 1 0 Config: 'Options None' 5 dflt / 0 0 File: $DOCROOT.$URI 6 spec / 0 0 File: '/very/special'.$URI
Here an entirely different directory tree is shown to a client with the IP address
192.168.0.1
. In record 2 the current key is set tospec
if the condition in record 1 matches. Also,$DEBUG
is set in this case (record 3).The next block in record 4 is executed for all clients, because the key change is not in effect, yet.
Records 5 and 6 are new lists of blocks. Hence, record 6 is executed only for
192.168.0.1
and record 5 for the rest.The action
Key: 'string'
is equivalent toDo: $KEY='string'
. - State: string
-
This action affects the current state directly. Thus, you can loop back to the PREPROC state from PROC. It is mostly used the prematurely finish the translation handler from the PREPROC state. As the
Key
action it takes effect, when the current list of blocks is finished.string
is evaluated as perl code. It is expected to result in one of the following strings. If not, a warning is printed in theerror_log
. State names are case insensitive:start preproc proc last round done
The
State
action is similar to setting the convenience variable$STATE
. Only in the latter case you must use the state constants, e.g.$STATE=DONE
. - Last
-
This action finishes the current list of blocks (just like a false condition finishes the current block). It is used together with
State
to finish the translation handler from a conditional block in the PREPROC state::PRE: 0 0 Cond: $finish :PRE: 0 1 State: 'done' :PRE: 0 2 Last
Another application of
Last
is as a return from aCall
action, see below. - Done
-
This action is a combination of
State: next_state
andLast
. That means it shifts to the next normal state and finishes the current block list. - Restart: ?uri?
-
Restart
restarts the processing. The optional uri argument is evaluated by perl and assigned to$r-
uri>. - Call: string
-
Well, the name suggests it is calling a subroutine. Assume you have several WEB applications running on the same server, say one application for each department. Each department needs of course some kind of authorization:
#uri blk ord action AUTH 0 0 Config: "AuthName \"$ctx->{name}\"" AUTH 0 1 Config: 'AuthType Basic' AUTH 0 2 Config: 'AuthUserFile /etc/htaccess/user/'.$ctx->{file} /dep1 0 0 Do: @{$ctx}{qw/name file/}=('Department 1', 'dep1') /dep1 0 1 Call: 'AUTH' /dep2 0 0 Do: @{$ctx}{qw/name file/}=('Department 2', 'dep2') /dep2 0 1 Call: 'AUTH'
The
AUTH
in theCall
actions refer to theAUTH
block list in theuri
column.Call
fetches the block list for a given uri and processes it. If aLast
action is executed the processing of that block list is finished. - Redirect: url, ?http_code?
-
The
Redirect
action sends a HTTP redirect response to the client and abort the current request. The optionalhttp_code
specifies the HTTP response code. Default is 302 (MOVED TEMPORARILY). - Error: ?http_code?, ?message?
-
Error
aborts the entire request. A HTTP response is sent to the client. The optionalhttp_code
specifies the HTTP response code. The optionalmessage
is logged as reason to theerror_log
.http_code
defaults to 500 (INTERNAL SERVER ERROR),message
tounspecified error
. - Config: list_of_strings_or_arrays
-
Surprisingly, this is the most complex action of all.
This action changes the Apache configuration regarding the current request. Think of it as a kind of
.htaccess
. Arguments toConfig
can be strings or arrays of one or two elements:Config: 'AuthName "secret"', ['AuthType Basic'], ['ProxyPassReverse http://...', '/path'], ['<Directory />', ''], ['AllowOverride Options', ''], ['</Directory>', '']
To understand the different meaning, you have to know about how Apache applies its configuration to a request. Hence, let's digress a little.
Each Apache directive can be used in certain contexts. Some for example can occur only in server config context, that means outside any
Directory
,Location
or evenVirtualHost
container.Listen
orPidFile
are examples. Other directives insist on being placed in a container, some even want to be only in aDirectory
container. For exampleAllowOverride
demands aDirectory
container.Also, the point in time when a directive takes effect differs for different directives.
PidFile
is clearly be applied during server startup before any request is processed. Hence, ourConfig
action cannot applyPidFile
. It's simply too late.AllowOverride
can be applied to single requests. But since it affects the processing of.htaccess
files it must be applied before that processing takes place. To make things even more confusing some directives take effect at several points in time. ConsiderOptions FollowSymLinks ExecCGI
FollowSymLinks
is applied when Apache looks up a file in the file system, whileExecCGI
influences the way the response is generated ages later.Apache solves this complexity by computing a configuration for each single request. As a starting point it uses the server default configuration. That is the configuration outside any
Location
orDirectory
for a virtual host. This basic configuration is assigned to the request just between the Uri Translation Phase and Map to Storage. At the very end of Map to Storage Apache's core Map to Storage handler incorporates matchingDirectory
containers and.htaccess
files into the request's current configuration.Location
containers are merged after Map to Storage is finished.Our
Config
action is applied early in Map to Storage. That means it affects the way Apache maps the request file name computed in the translation phase to the file system, because that comes later. But it also means, your static configuration (config file based) overrides ourConfig
actions. This limitation can be partly overcome usingFixupConfig
instead ofConfig
.Now, what does the various syntaxes mean? The simplest one:
#uri blk ord action /uri 0 0 Config: 'ProxyPassReverse http://my.backend.org'
is very close to
<Location /uri> ProxyPassReverse http://my.backend.org </Location>
Only, it is applied before any
Directory
container takes effect. Note, the location uri is the value of$MATCHED_URI
, see below. This is also valid if theConfig
action is used from aCall
ed block.The location uri is sometimes important.
ProxyPassReverse
, for example, uses the path given to the location container for its own purpose.All other forms of
Config
are not influenced by$MATCHED_URI
.The next one:
Config: ['ProxyPassReverse http://my.backend.org']
is equivalent to
<Location /> ProxyPassReverse http://my.backend.org </Location>
Note, the location uri differs.
This is also the only form of this action, that is available with mod_perl before 2.0.3.
The next one:
Config: ['ProxyPassReverse http://my.backend.org', '/path']
is equivalent to
<Location /path> ProxyPassReverse http://my.backend.org </Location>
and
Config: ['ProxyPassReverse /path http://my.backend.org', '']
is equivalent to
ProxyPassReverse /path http://my.backend.org
This last form evaluates configuration directives as if they appear outside any container. It is also a little dangerous, because it causes segfaults for some directives that are not prepared to be used this way. But it is the only way to apply an
AllowOverride
, for example. - FixupConfig: list_of_strings_or_arrays
-
Syntax and sematic of this action is equivalent to
Config
. The only difference, it is applied in the fixup phase, just before the response is generated. It can be seen as a hook to override static configuration in yourhttpd.conf
. Suppose yourhttpd.conf
contains these lines:<Directory /> Options None </Directory>
But now you want to run files contained in /web/cgi as CGI scripts.
Config: 'Options ExecCGI'
would not help because it is overridden by the directory container that is merged later. Here:FixupConfig: 'Options ExecCGI'
can be used.
- Uri: string
-
This action sets
$r->uri
to string. It is equivalent toDo: $URI=do{ string }
- File: string
-
This action sets
$r->filename
to string. It is equivalent toDo: $FILENAME=do{ string }
- Proxy: ?url?
-
This tells Apache to forward the request to
url
as a proxy.url
is optional. If ommitted$r->unparsed_uri
is used. That means Apache must be used as a proxy by the browser. - CgiScript (without parameter)
-
is equivalent to
Do: $r->handler( 'cgi-script' ); FixupConfig: ['Options ExecCGI']
- PerlScript (without parameter)
-
is equivalent to
Do: $r->handler( 'perl-script' ); FixupConfig: ['Options ExecCGI'], ['PerlOptions +ParseHeaders']
- PerlHandler: string
-
This action checks that either
modperl
orperl-script
is set as handler for the request. If not,modperl
is set.string
is evaluated as Perl code. The result is expected to be a package name or a fully qualified function name. If a package name is given::handler
is appended to build a fully qualified function name.The action checks if the function is defined. If not, it tries to load the appropriate module.
The function is the used as
PerlResponseHandler
.Further, a
PerlMapToStorageHandler
is installed that skips the handling ofDirectory
containers and.htaccess
files. If not set, this handler also setspath_info
. Assumed,#uri blk ord action /some/path 0 0 PerlHandler: ...
and a request comes in for
/some/path/foo/bar
. Thenpath_info
is set to/foo/bar
.
Convenience Variables and Data Structures
These variables are tied to elements of the current request ($r
) or the current context hash ($ctx
). Reading them returns the current value, setting changes it.
- $URI =
$r->uri
- $REAL_URI =
$r->unparsed_uri
- $METHOD =
$r->method
- $QUERY_STRING =
$r->args
- $FILENAME =
$r->filename
- $DOCROOT =
$r->document_root
- $HOSTNAME =
$r->hostname
- $PATH_INFO =
$r->path_info
-
for more information see Apache2::RequestRec.
- $MATCHED_URI =
$ctx->{' uri'}
- $MATCHED_PATH_INFO =
$ctx->{' pathinfo'}
-
While in
PROC
state the incoming uri is split in 2 parts. The first part is matching theuri
field of a database record. The second part is the rest. They can be accessed as$MATCHED_URI
and$MATCHED_PATH_INFO
. - $KEY =
$ctx->{' key'}
-
the current key.
- $STATE =
$ctx->{' state'}
-
the current processing state.
- $RC =
$ctx->{' rc'}
-
Normally,
Apache2::Translation
checks at the end if$r->filename
is set. If so, it returnsApache2::Const::OK
to its caller. If not,Apache2::Const::DECLINED
is returned. The first alternative signals that the Uri Translation Phase is done and no further handlers are called in this phase. The second alternative signals that subsequent handlers are to be called. Thus,mod_alias
or even the core translation handler see the request.Setting
$RC
your action decide what is returned.$RC
is also set by thePerlHandler
action. Modperl generated responses are normally not associated with a single file on disk. - $DEBUG =
$ctx->{' debug'}
-
If set to
1
or2
debugging output is sent to theerror_log
.
APACHE CONFIGURATION DIRECTIVES
After installed and loaded by
PerlLoadModule Apache2::Translation
in your httpd.conf
Apache2::Translation
is configured with the following directives:
- TranslationProvider class param1 param2 ...
-
Currently there is only one provider class implemented,
Apache2::Translation::DB
. Hence,class
is alwaysDB
orApache2::Translation::DB
.Each parameter is expected to be a string formatted as
NAME=VALUE
There must be no spaces around the equal sign. The list is passed to the constructor of the provider class as named parameters:
$class->new( NAME1=>VALUE1, NAME2=>VALUE2, ... );
The following parameters are expected by the
DB
provider:- database=DSN
-
a string describing a DBI database
- user=NAME
- password=PW
-
the user and password to use
- table=NAME
-
names the translation table.
- key=NAME
- uri=NAME
- block=NAME
- order=NAME
- action=NAME
-
name the columns of the translation table to use.
- cachetbl=NAME
- cachecol=NAME
-
name the cache table and its column
- cachesize=NUMBER|infinite
-
sets the maximum number of cached block lists, default is 1000.
If set to
infinite
the cache has no limits.A Tie::Cache::LRU cache is used.
Apache2::Translation::DB
caches database entries as lists of blocks. Each list of blocks consumes one cache entry.For each request first the following lookup is done:
SELECT MAX($cachecol) FROM $cachetbl
The resulting value is then compared with the previous read value. If it has changed, it means the cache is invalid. If not, the cache is valid and if all information is found in the cache, no further database lookups are needed.
- singleton=BOOLEAN
-
Normally,
Apache2::Translation
tries to connect to the database at server startup. Then it inspects the database handle to see ifApache::DBI
orApache::DBI::Cache
are loaded. If so, it will connect and disconnect for each translation phase / request, thus, put back the connection to the connection pool.If neither of them is loaded the DB connection is used as a singleton. It is connected once at server startup and then held open (and reconnected if dropped by the database server).
With the optional
singleton
parameter you can decide to use a singleton connection even if a connection pool is in effect. If no connection pool is loaded, then of course settingsingleton
to false has no effect.
- TranslationKey initial-key
-
This sets the initial value for the key. Default is the empty string.
- TranslationEvalCache number
-
Apache2::Translation
compiles all code snippets into functions and caches these functions. Normally, an ordinary hash is used for this. Strictly speaking this is a memory hole if your translation table changes. I think that can be ignored, if the number of requests per worker is limited, seeMaxRequestsPerChild
. If you think this is too lax, put a number here.If set the cache is tied to Tie::Cache::LRU. The number of cached code snippets will then be limited by
number
.
PROVIDER INTERFACE
A provider must support the following methods:
- new( NAME=>VALUE, ... )
-
the constructor. It is called once from the master Apache during its configuration.
- child_init
-
This method is optional. If defined it is called from a
PerlChildInitHandler
and can be used to do some initializations. TheDB
provider connects here to the database and decides to use a singleton or not. - start
-
This method is called at start of each uri translation. The DB provider checks the cache here.
- stop
-
is called after each uri translation.
- fetch( $key, $uri )
-
is called to fetch a list of blocks. The result is a list of arrays:
([block, order, action], [block, order, action], ...)
SEE ALSO
AUTHOR
Torsten Foertsch, <torsten.foertsch@gmx.net>
SPONSORING
Sincere thanks to Arvato Direct Services (http://www.arvato.com/) for sponsoring this module.
COPYRIGHT AND LICENSE
Copyright (C) 2005, 2006 by Torsten Foertsch
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.