Category: mod_perl Keywords: modperl filter
我写过 Template builtin Filters 和 Template customized Filters,这是 TT 的过滤器。modperl 也有它自己的过滤器。过滤器的含义都是一样的,接受一些数据,改变它,然后返回这些数据。
这在你不能对原始数据做一些改变时特别有用。
我的打算是先介绍 modperl 的内置案例,然后自己写几个 filter.首先 modperl 的 filter 分为两种:PerlInputFilterHandler 和 PerlOutputFilterHandler 。
我先用官方文档的 Input and Output Filters 来解释一下 filter 的一般写法。
#file:MyApache2/FilterObfuscate.pm这个过滤器是将文件里的 \r\n 换行去掉。实际上一般你要去掉 \r\n 的时候要注意如 pre 这样的标签时必须将 \r\n 转为 br 或者不去掉。这里我们只是简单的写一下,没什么实际意义。
#--------------------------------
package MyApache2::FilterObfuscate;use strict;
use warnings;
use Apache2::Filter ();
use Apache2::RequestRec ();
use APR::Table ();
use Apache2::Const -compile => qw(OK);use constant BUFF_LEN => 1024;sub handler {
my $f = shift;
unless ($f->ctx) {
$f->r->headers_out->unset('Content-Length');
$f->ctx(1);
}
while ($f->read(my $buffer, BUFF_LEN)) {
$buffer =~ s/[\r\n]//g;
$f->print($buffer);
}
return Apache2::Const::OK;
}
1;
应用的话:
<Files ~ "\.html">对于所有的 html 使用此过滤器。
PerlOutputFilterHandler MyApache2::FilterObfuscate
</Files>
- 下面解释下:
- my $f = shift; 这里是用 $f 而不是 $r 是因为这里传进来的是 filter 对象而不是 request 对象。
- $f->ctx 用于设置 filter 的上下文变量。它在每一个请求时初始化,请求结束时 undef 掉。如果有多个 filter 的话,它可以在这几个 filter 共享一些变量。
unless ($f->ctx) {第一行测试是否已经设置了 ctx, 如果没有设置的话,说明是第一次访问,因为我们将在接下来对内容做一些缩水(去掉了换行),所以必须重新设置 Content-Length, 否则的话浏览器将期待更多的内容,这样会出错。当 unset Content-Length 后,我们将 ctx 设置为 1. 这样下次访问就不需要重新 unset Content-Length 了。
$f->r->headers_out->unset('Content-Length');
$f->ctx(1);
}
关于 $f->ctx 的更多解释,查看 Apache2::Filter - while ($f->read(my $buffer, BUFF_LEN)) {
read 用于读进 BUFF_LEN 长度的内容。设置 BUFF_LEN 是怕内容太大,一次读入可能会死机。这里的 BUFF_LEN 已经用
use constant BUFF_LEN => 1024; 设置过了。
这个和处理 CGI 上传文件是相似的。 - $buffer =~ s/[\r\n]//g;$f->print($buffer); 做一些改变,然后输出它。
- return Apache2::Const::OK; 返回值。这里的返回值可以是 Apache2::Const::OK 或者是 Apache2::Const::DECLINED。二者的区别可以查看 modperl 服务器的运行阶段和句柄
一次请求中调用了多次 filter 句柄
在继续之前,我们得学习下 Apache 处理输出的内在性质。Apache 并不是将所有的内容存在一个块中,而是将内容分为很多个 buckets,然后用一个所谓的 Bucket Brigades 连起来。详细的查看官方文档,Bucket Brigades. 我也不是很清楚。
诸位只需要知道在一次请求中,我们会调用多次同一个 filter 句柄就可以了。
这就是为什么我们要设置 $f->ctx(1); 的原因。
如果你想知道到底调用了多少次该句柄的话,可以这么写 handler:
sub handler {
my $f = shift; my $ctx = $f->ctx;
$ctx->{invoked}++;
$f->ctx($ctx);
warn "filter was invoked $ctx->{invoked} times\n";
return Apache2::Const::DECLINED;
}$f->ctx->{invoked} 来纪录调用了多少次,每调用一次增加一。先读取 my $ctx = $f->ctx; 再增加 $ctx->{invoked}++; 最后赋值纪录回去 $f->ctx($ctx);
这就是 $f->ctx 的一般用法。
$f->seen_eos
我们可以用 $f->ctx 来测试是否是第一次访问,同时我们可以用 $f->seen_eos 来测试是否是最后一次访问该过滤器句柄。可能的代码如下:if ($f->seen_eos) {
finalize($f);
}自定义子过程 finalize 用于处理一些结尾工作。