Apache::OutputChain - chain stacked Perl handlers
You need reasonably new version of Apache httpd, compiled with mod_perl with PERL_STACKED_HANDLERS enabled. In the conf/access.conf file of your Apache installation, add lines like
<Files *.html> SetHandler perl-script PerlHandler Apache::OutputChain Apache::SSIChain Apache::PassHtml </Files>
Also, this module can be used as a base for other Apache::*Chain handlers -- inherit from it to get the chaining features.
This module allows chaining perl of handlers in Apache, which allows you to make filter modules that take output from previous handlers, make some modifications, and pass the output to the next handler or out to browser.
Normal handler modules specify functions handler that do the job -- authenticate, log, or send back the response. With chaining modules, the handler function only registers the handler into chain. It is done by tie of STDOUT. The module then catches output of other handlers in its function PRINT, that gets called whenever something is printed to tied handle, can modify or otherwise process the output and send it further on.
PerlHandler above shows the typical configuration: first, Apache::OutputChain starts the chaining feature. Then, there is a list of chaining modules, in reverse order. Here shown is Apache::SSIChain; if you would also want to gzip the output, you would write something like
Apache::OutputChain Apache::GzipChain Apache::SSIChain
Note that you probably want to do SSI first and gzip the result, that's why the modules are written in that (reverse) order in the configuration file.
As the last in the chaining chain, there should be some module that actually produces the data: Apache::PassHtml, Apache::PassFile, Apache::PassExec, or even Apache::Registry.
I will try to explain how this feature is achieved, because I hope you could help me to make it better and mature.
When the handler function is called, it checks if it gets a reference to a class. If this is true, then this function was called from some other handler that wants to be put into the chain. If not, it's probably an initialization (first call) of this package (Apache::OutputChain) and we will supply name of this package. Note that other chaining modules should call inherited handler from their own handlers.
Now we check, where is STDOUT tied. If it is Apache, we are the first one trying to be put into the chain. If it is not, there is somebody in the chain already. We call tie on the STDOUT, steal it from anybody who had it before -- either Apache or the other class.
When later anybody prints into STDOUT, it will call function PRINT of the first class in the chain (the last one that registered). If there is not other class behind, the print method of Apache will be called. If this is not the last user defined handler in the chain, we will call PRINT method of the next class.
(c) 1997--2002 Jan Pazdziora, email@example.com, http://www.fi.muni.cz/~adelton/ at Faculty of Informatics, Masaryk University, Brno, Czech Republic
Apache::GzipChain(3) by Andreas Koenig for solution that gzips the output on the fly; Apache::SSIChain(3) for SSI parsing module; mod_perl(1) by Doug MacEachern for the great Perl in Apache project.
Apache::MakeCapital(3) for a simple example of inheriting from this module.