<-
Apache > HTTP 服务器 > 文档 > 版本 2.4 > 开发者文档

Apache 2.0 中的过滤器工作原理

可用语言:  en 

警告

这是一封电子邮件 (<022501c1c529$f63a9550$7f00000a@KOJ>) 的剪切粘贴内容,仅为了更好的可读性而重新格式化。它不是最新的,但可能是进一步研究的良好起点。

Support Apache!

另请参阅

top

过滤器类型

有三种基本的过滤器类型(实际上,每种类型都细分为两个类别,但这将在后面讨论)。

连接
此类型的过滤器在整个连接的生命周期内有效。(AP_FTYPE_CONNECTIONAP_FTYPE_NETWORK
协议
此类型的过滤器在从客户端的角度来看,整个请求的生命周期内有效,这意味着请求从发送请求的时间开始到接收响应的时间结束都是有效的。(AP_FTYPE_PROTOCOLAP_FTYPE_TRANSCODE
资源
此类型的过滤器在使用此内容来满足请求的时间内有效。对于简单的请求,这与PROTOCOL相同,但内部重定向和子请求可以在不结束请求的情况下更改内容。(AP_FTYPE_RESOURCEAP_FTYPE_CONTENT_SET

区分协议过滤器和资源过滤器非常重要。资源过滤器与特定资源绑定,它也可能与标头信息绑定,但主要绑定是与资源。如果你正在编写过滤器,并且想知道它是资源过滤器还是协议过滤器,正确的提问方式是:“如果请求重定向到不同的资源,是否可以删除此过滤器?”如果答案是肯定的,那么它就是资源过滤器。如果答案是否定的,那么它很可能是一个协议过滤器或连接过滤器。我不会讨论连接过滤器,因为它们似乎已经被很好地理解了。根据这个定义,一些例子可能会有所帮助

字节范围
我们已将其编码为插入所有请求,如果未使用则将其删除。由于此过滤器在所有请求开始时处于活动状态,因此如果重定向,它无法删除,因此它是一个协议过滤器。
http_header
此过滤器实际上将标头写入网络。这显然是一个必需的过滤器(除了 asis 情况,它很特殊,将在下面处理),因此它是一个协议过滤器。
压缩
管理员根据请求的文件配置此过滤器。如果我们从自动索引页面到 index.html 页面的内部重定向,则根据配置,压缩过滤器可能会添加或删除,因此它是一个资源过滤器。

对每个类别进行进一步细分以获得另外两种过滤器类型,纯粹是为了排序。我们可以删除它,只允许一种过滤器类型,但顺序往往会出错,我们需要进行一些修改才能使其正常工作。目前,RESOURCE 过滤器只有一种过滤器类型,但这应该会改变。

top

过滤器是如何插入的?

从理论上讲,这实际上很简单,但代码很复杂。首先,重要的是每个人都要意识到每个请求都有三个过滤器列表,但它们都是连接在一起的

之前的问题是,我们使用单链表来创建过滤器堆栈,并且我们从“正确”的位置开始。这意味着,如果我在堆栈上有一个RESOURCE 过滤器,并且我添加了一个CONNECTION 过滤器,那么CONNECTION 过滤器将被忽略。这应该是有道理的,因为我们会在c->output_filters 列表的顶部插入连接过滤器,但r->output_filters 的末尾指向曾经位于c->output_filters 前面的过滤器。这显然是错误的。新的插入代码使用双链表。这具有我们永远不会丢失已插入过滤器的优点。不幸的是,它也带来了一系列不同的问题。

问题是我们有两个不同的情况,我们使用子请求。第一个是将更多数据插入响应。第二个是用内部重定向替换现有响应。这两个是不同的情况,需要分别对待。

在第一种情况下,我们是在处理程序或过滤器中创建子请求。这意味着下一个过滤器应该传递给make_sub_request 函数,并且子请求中的最后一个资源过滤器将指向主请求中的下一个过滤器。这是有道理的,因为子请求的数据需要通过与主请求相同的过滤器集。图形表示可能会有所帮助

Default_handler --> includes_filter --> byterange --> ...

如果 includes 过滤器创建子请求,那么我们不希望来自该子请求的数据通过 includes 过滤器,因为它可能不是 SSI 数据。因此,子请求添加以下内容

Default_handler --> includes_filter -/-> byterange --> ...
                                    /
Default_handler --> sub_request_core

如果子请求是 SSI 数据会发生什么?这很简单,includes_filter 是一个资源过滤器,因此它将在Default_handlersub_request_core 过滤器之间添加到子请求中。

子请求的第二种情况是,当一个子请求将成为真正的请求时。每当在处理程序或过滤器之外创建子请求,并且将 NULL 作为下一个过滤器传递给make_sub_request 函数时,就会发生这种情况。

在这种情况下,资源过滤器对新请求不再有意义,因为资源已更改。因此,我们不是从头开始,而是简单地将子请求的资源过滤器的开头指向旧请求的协议过滤器的开头。这意味着我们不会丢失任何协议过滤器,也不会尝试通过不应该看到它的过滤器发送此数据。

问题是我们现在正在使用双链表作为我们的过滤器堆栈。但是,你应该注意到,在这个模型中,两个列表可能相交。那么,你如何处理上一个指针?这是一个非常难以回答的问题,因为没有“正确”的答案,两种方法都同样有效。我查看了我们使用上一个指针的原因。唯一的原因是允许更轻松地添加新服务器。话虽如此,我选择的解决方案是让上一个指针始终停留在原始请求上。

这会导致一些更复杂的逻辑,但它适用于所有情况。我担心将其移动到子请求,是因为对于更常见的情况(其中子请求用于将数据添加到响应),主过滤器链将是错误的。在我看来,这似乎不是一个好主意。

top

Asis

最后一个主题。:-) Mod_Asis 有点像黑客,但处理程序需要删除除连接过滤器之外的所有过滤器,并发送数据。如果你正在使用 mod_asis,那么所有其他赌注都无效。

top

解释

绝对最后一点是,这段代码之所以如此难以正确编写,是因为我们进行了太多修改来强迫它工作。我最初编写了大部分修改,因此我负有很大责任。但是,现在代码已经正确了,我开始删除一些修改。大多数人应该已经看到reset_filtersadd_required_filters 函数消失了。这些函数为错误条件插入了协议级过滤器,实际上,这两个函数都做了同样的事情,一个接一个,这真的很奇怪。因为我们不再为错误情况丢失协议过滤器,所以这些修改消失了。HTTP_HEADERContent-lengthByterange 过滤器都在insert_filters 阶段添加,因为如果它们早些添加,我们就会有一些有趣的交互。现在,它们都可以移动到与HTTP_INCORECORE_IN 过滤器一起插入。这将使代码更容易理解。

可用语言:  en 

top

评论

注意
这不是问答部分。此处放置的评论应针对改进文档或服务器的建议,如果它们被实施或被认为无效/不相关,我们的版主可能会将其删除。有关如何管理 Apache HTTP Server 的问题,应直接发送到我们的 IRC 频道 #httpd(在 Libera.chat 上),或发送到我们的 邮件列表