Yesterday at work someone was trying to pass traditional Apache SSI directives through an XSL transformation on a Google search appliance. Long story short, they vanished: HTML comments don’t make it out of that device.
Anyway…I had a simple solution. Since we were pumping the Google results through a Perl CGI anyway, there was no reason we couldn’t just output a fake HTML tag which the CGI would then turn into an SSI comment for Apache. This was born <ssi virtual="/foo/bar.html" />
.
Then a simple s/<ssi (virtual=\"[^\"]+\")><\/ssi>/<!--#include $1 -->/;
in Perl will give something Apache can understand.
That solved the immediate problem, but got me thinking about emulating the full Apache mod_include set of SSI directives using the <ssi>
tag. I’m thinking of something like this:
<ssi element="include" virtual="/foo/bar.html" />
<ssi element="set" var="FOO" value="BAR" />
<ssi element="if">
<ssi_if expr="test_condition">YES!</ssi_if>
<ssi_elif expr="test_condition">MAYBE!</ssi_elif>
<ssi_else>NO!</ssi_else>
</ssi>
And with that format, the original version still works if you assume a missing “element” attribute implies element="include"
. The <!--#if -->
block isn’t quite satisfying here — any text nodes inside the <ssi>
block but outside the <ssi_(if|elif|else)>
blocks would be ignored, but that’s no different than odd content in, say, a <table>
that doesn’t actually fall into a cell.
I don’t actually have the Perl that would do the transformation, but it wouldn’t be hard. I’ll wait until someone actually needs it.
mod_perl 2 has an annoying…feature. Because the system environ
struct is not thread safe, mod_perl’s perl-script
handler unties the %ENV
hash from the actual environment. That means, anything that uses the C getenv
/setenv
/unsetenv
functions to read the environment will not see changes that were made to %ENV
.
An obvious example is Perl’s localtime
function. It actually calls the system localtime
function, which uses the C getenv
to check the current value of the timezone environment variable TZ
. If you try to change the timezone in a mod_perl2 program by assigning to $ENV{TZ}
, localtime
won’t know it.
The solution is to use the Env::C
module and it’s getenv
/setenv
/unsetenv
wrappers. It works fine, but it’s a bit cumbersome. But a simple module, loaded at server-startup time, can wrap the system localtime
in a function that takes care of the environment.
package Apache2::Localtime;
use Env::C;
use Exporter;
use strict;
our @ISA = qw(Exporter);
our @EXPORT = qw(localtime);
sub import {
my $class = shift;
$class->export('CORE::GLOBAL', 'localtime');
}
sub localtime {
my $time = shift || time;
return localtime($time) unless $ENV{TZ};
my $orig_tz = Env::C::getenv('TZ');
Env::C::setenv('TZ', $ENV{TZ}, 1);
my(@ret, $ret);
if(wantarray) {
@ret = CORE::localtime($time);
} else {
$ret = CORE::localtime($time);
}
if(defined $orig_tz) {
Env::C::setenv('TZ', $orig_tz, 1);
} else {
Env::C::unsetenv('TZ');
}
return wantarray ? @ret : $ret;
}
1;
Put that in your @INC
path at Apache2/Localtime.pm
and then add use Apache2::Localtime
to a PerlRequire .../initialize.pl
script or something similar. The new function should override the built-in localtime
and keep your timeonzes in sync.
The code was mostly taken from here.