18 December 2005
This post may be outdated due to it was written on 2005. The links may be broken. The code may be not working anymore. Leave comments if needed.
我写过 Template builtin FiltersTemplate customized Filters,这是 TT 的过滤器。
modperl 也有它自己的过滤器。过滤器的含义都是一样的,接受一些数据,改变它,然后返回这些数据。
这在你不能对原始数据做一些改变时特别有用。
我的打算是先介绍 modperl 的内置案例,然后自己写几个 filter.

首先 modperl 的 filter 分为两种:PerlInputFilterHandler 和 PerlOutputFilterHandler 。
我先用官方文档的 Input and Output Filters 来解释一下 filter 的一般写法。

#file:MyApache2/FilterObfuscate.pm
#--------------------------------
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;

这个过滤器是将文件里的 \r\n 换行去掉。实际上一般你要去掉 \r\n 的时候要注意如 pre 这样的标签时必须将 \r\n 转为 br 或者不去掉。这里我们只是简单的写一下,没什么实际意义。
应用的话:
<Files ~ "\.html">
   PerlOutputFilterHandler MyApache2::FilterObfuscate
</Files>
对于所有的 html 使用此过滤器。
    下面解释下:
  • my $f = shift; 这里是用 $f 而不是 $r 是因为这里传进来的是 filter 对象而不是 request 对象。
  • $f->ctx 用于设置 filter 的上下文变量。它在每一个请求时初始化,请求结束时 undef 掉。如果有多个 filter 的话,它可以在这几个 filter 共享一些变量。
    unless ($f->ctx) {
       $f->r->headers_out->unset('Content-Length');
       $f->ctx(1);
    }
    第一行测试是否已经设置了 ctx, 如果没有设置的话,说明是第一次访问,因为我们将在接下来对内容做一些缩水(去掉了换行),所以必须重新设置 Content-Length, 否则的话浏览器将期待更多的内容,这样会出错。当 unset Content-Length 后,我们将 ctx 设置为 1. 这样下次访问就不需要重新 unset Content-Length 了。
    关于 $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 用于处理一些结尾工作。

Stay stun

See u next time!


blog comments powered by Disqus