On this page
过滤器如何在 Apache 2.0 中工作
Warning
这是电子邮件中的剪切粘贴作业(<022501c1c529 $ f63a9550 $ 7f00000a @ KOJ>),仅重新格式化以提高可读性。它不是最新的,但可能是进一步研究的良好开端。
Filter Types
共有三种基本的过滤器类型(每种类型实际上都分为两类,但稍后介绍)。
CONNECTION
- 此类型的过滤器在此连接的生存期内有效。 (
AP_FTYPE_CONNECTION
,AP_FTYPE_NETWORK
)
- 此类型的过滤器在此连接的生存期内有效。 (
PROTOCOL
- 从 Client 端的角度来看,这种类型的过滤器在此请求的生存期内有效,这意味着该请求从发送请求之时到收到响应之时才有效。 (
AP_FTYPE_PROTOCOL
,AP_FTYPE_TRANSCODE
)
- 从 Client 端的角度来看,这种类型的过滤器在此请求的生存期内有效,这意味着该请求从发送请求之时到收到响应之时才有效。 (
RESOURCE
- 此类型的过滤器在此内容用于满足请求时有效。对于简单的请求,这与
PROTOCOL
相同,但是内部重定向和子请求可以更改内容而不会结束请求。 (AP_FTYPE_RESOURCE
,AP_FTYPE_CONTENT_SET
)
- 此类型的过滤器在此内容用于满足请求时有效。对于简单的请求,这与
区分协议和资源过滤器很重要。资源过滤器绑定到特定资源,它也可能绑定到 Headers 信息,但主要绑定是绑定到资源。如果您正在编写过滤器,并且想知道它是资源还是协议,则要问的正确问题是:“如果将请求重定向到另一个资源,可以删除此过滤器吗?”如果答案是肯定的,则它是资源过滤器。如果为否,则很可能是协议或连接筛选器。我不会介绍连接过滤器,因为它们似乎已被很好地理解。使用此定义,一些示例可能会有所帮助:
Byterange
- 我们已将其编码为可插入所有请求,如果未使用,则将其删除。由于此过滤器在所有请求的开始时都处于活动状态,因此如果重定向,则无法将其删除,因此这是一个协议过滤器。
http_header
- 该过滤器实际上将 Headers 写入网络。显然,这是必需的过滤器(特殊情况除外,这将在下面处理),因此它是协议过滤器。
Deflate
- Management 员根据请求的文件配置此过滤器。如果我们执行从自动索引页面到 index.html 页面的内部重定向,则可以基于 config 添加或删除 deflate 过滤器,因此这是资源过滤器。
每个类别进一步细分为另外两种过滤器类型严格用于 Order。我们可以删除它,只允许使用一种过滤器类型,但是 Sequences 往往是错误的,并且我们需要破解一些东西才能使其工作。当前,RESOURCE
过滤器只有一种过滤器类型,但是应该改变。
如何插入过滤器?
从理论上讲,这实际上很简单,但是代码很复杂。首先,重要的一点是每个人都必须意识到每个请求都有三个过滤器列表,但是它们都被串联在一起:
r->output_filters
(对应于 RESOURCE)r->proto_output_filters
(对应于 PROTOCOL)r->connection->output_filters
(对应于 CONNECTION)
以前的问题是,我们使用一个单链表创建了过滤器堆栈,然后从“正确”的位置开始。这意味着,如果我在堆栈上具有RESOURCE
过滤器,并添加了CONNECTION
过滤器,则CONNECTION
过滤器将被忽略。这应该是有道理的,因为我们会将连接过滤器插入到c->output_filters
列表的顶部,但是r->output_filters
的末尾指向曾经位于c->output_filters
前面的过滤器。这显然是错误的。新的插入代码使用双向链表。这样的好处是我们永远不会丢失已插入的过滤器。不幸的是,它带来了另外一系列麻烦。
问题在于,使用子请求时,我们有两种不同的情况。首先是在响应中插入更多数据。第二个是用内部重定向替换现有响应。这是两种不同的情况,需要照此处理。
在第一种情况下,我们从处理程序或过滤器中创建子请求。这意味着应该将下一个过滤器传递给make_sub_request
函数,并且子请求中的最后一个资源过滤器将指向主请求中的下一个过滤器。这是有道理的,因为子请求的数据需要流经与主请求相同的一组过滤器。图形表示可能会帮助:
Default_handler --> includes_filter --> byterange --> ...
如果 include 过滤器创建了一个子请求,则我们不希望该子请求中的数据通过 include 过滤器,因为它可能不是 SSI 数据。因此,子请求添加了以下内容:
Default_handler --> includes_filter -/-> byterange --> ...
/
Default_handler --> sub_request_core
如果子请求是 SSI 数据会怎样?好吧,这很容易,includes_filter
是资源过滤器,因此它将被添加到Default_handler
和sub_request_core
过滤器之间的子请求中。
子请求的第二种情况是一个子请求将成为真实请求。每当在处理程序或过滤器外部创建子请求并将 NULL 作为下一个过滤器传递给make_sub_request
函数时,就会发生这种情况。
在这种情况下,资源过滤器对于新请求不再有意义,因为资源已更改。因此,我们无需从头开始,只需将子请求的资源过滤器的前端指向旧请求的协议过滤器的前端。这意味着我们不会丢失任何协议过滤器,也不会尝试通过不应该看到的过滤器发送此数据。
问题在于我们现在正在为过滤器堆栈使用双向链接列表。但是,您应该注意到,两个列表在此模型中可能相交。那么,您处理上一个指针了吗?这是一个很难回答的问题,因为没有“正确”的答案,任何一种方法都同样有效。我研究了为什么我们使用以前的指针。这样做的唯一原因是允许更轻松地添加新服务器。话虽这么说,我选择的解决方案是使先前的指针始终停留在原始请求上。
这会导致一些更复杂的逻辑,但是它适用于所有情况。我担心将其移到子请求中是,对于更常见的情况(使用子请求将数据添加到响应中),主过滤器链将是错误的。在我看来,这似乎不是一个好主意。
Asis
最后一个 Topic。 :-) Mod_Asis 有点破烂,但是处理程序需要删除除连接过滤器之外的所有过滤器,然后发送数据。如果您使用的是mod_asis,那么所有其他投注都将关闭。
Explanations
绝对的最后一点是,该代码之所以难以正确编写,是因为我们已经破解了太多代码以迫使其工作。我最初写了大多数 hack,所以我要怪很多。但是,既然代码正确,我已经开始删除一些技巧。大多数人应该已经看到reset_filters
和add_required_filters
功能已消失。那些插入的针对错误条件的协议级过滤器,实际上,两个功能都做同样的事情,一个接一个,真的很奇怪。因为我们不再为错误情况丢失协议过滤器,所以这些黑客消失了。 HTTP_HEADER
,Content-length
和Byterange
过滤器都已添加到insert_filters
阶段,因为如果较早添加它们,我们将进行一些有趣的交互。现在,所有这些都可以移动以使用HTTP_IN
,CORE
和CORE_IN
过滤器插入。这将使代码更易于遵循。