本文可以任意进行转摘,但是必须保留以下内容:
1)、本文原始出处为Aapche中国, http://www.cn-apache.com
2)、HESEE悦色尚品,100%正版原装进口美妆商城,假一赔三,满200货到付款,网址:http://www.hesee.cn
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
10.1 请求处理模型
10.1.1 请求处理概述
从第一卷的分析中,我们知道Apache中对于请求的处理实际只有两个函数:ap_read_request()和ap_process_request(),一旦通过ap_read_request()读取了HTTP连接上的某个请求之后,Apache将调用函数ap_process_request对其进行处理。在整个Apache的核心内部,大部分读者最关心的就是Apache中是如何处理客户端的请求的。由于HTTP是基于TCP的应用层的协议,而TCP则是面向连接的传输层的协议,因此通信的双方在传输数据之前必须建立起网络连接。因此,通信双发将首先建立TCP连接,然后接受HTTP数据,最后再关闭连接。
Apache中的HTTP请求是与前面所介绍的挂钩紧紧关联在一起的。从大的方面来看,Apache对HTTP的请求可以分为连接、处理和断开连接三个阶段。从小的方面而言,每个阶段又可以分为更多的子阶段。比如对HTTP的请求,我们可以进一步划分为客户身份验证、客户权限认证、请求校验、URL重定向等阶段,每一个阶段调用相应的函数进行处理。在Apache中,这些子阶段可以用术语“挂钩(HOOK)”来描述。Apache中对请求的处理过程实质上就是依次调用一系列挂钩的过程,不过由于HTTP请求类型的不同,它们所对应的挂钩数目和类型也不尽相同。对于典型的HTTP请求,有一个默认的挂钩调用顺序,你可以按照这个默认的顺序进行调用,也可以不遵遁这个顺序,而根据自己的情况调整调用顺序。整个HTTP的请求可以用图10-1来描述:
图10-1 HTTP请求模型
在图10-1中,存在6种不同的挂钩,对于请求a,只需要调用Hook2、Hook1;对于请求b,则需要调用Hook1、Hook6、Hook5;请求c需要依次调用Hook1、Hook4、Hook3、Hook6四个挂钩。
在Apache中,挂钩总是和挂钩函数联系在一起的。挂钩是用来表示在处理HTTP请求中一组类似的操作,与之对应,挂钩函数就是操作函数。不过即使挂钩相同,对应的挂钩函数也未必相同,如图10-2所示。举个简单的例子,配置文件模块和虚拟主机模块都需要身份验证挂钩来确认访问者能否访问相应的资源,但两个模块的验证方法则差别很大。因此,一个挂钩同时是和一组挂钩函数联系在一起的。在请求处理过程中,调用挂钩的时候实际上就是调用挂钩函数组中的挂钩函数。调用过程可以用图10-2所示。Apache对指定挂钩的挂钩函数调用有两种方式,一种就是将挂钩函数组中的每个函数都调用一次,比如图10-2中的挂钩4;另一种就是从前往后调用,一旦找到合适的就停止继续调用,图10-2中的挂钩1、挂钩2、挂钩3就是这种情况。
通过挂钩机制,你可以自行修改服务器的行为,比如修改挂钩函数或增加挂钩函数,甚至增加挂钩。不过增加挂钩只是Apache 2.0才提供的功能。
图10-2 请求挂钩和挂钩函数
由于所有的挂钩函数最终都是定义在模块之中,各个模块能够支持一定数目和一定类型的挂钩,因此,如果我们将挂钩、模块和整个请求进行整体分析,则如图10-3所示:
从图10-3中我们可以看到整个请求的处理流程。
所有的模块A、B、C、D、E组成模块链表,而每个模块分别支持不同的挂钩。某一个请求与4种类型的挂钩HOOK type1、HOOK type2、HOOK type3及HOOK type4关联,当请求执行的时候,各个模块中的对应的挂钩函数将依次被调用,调用的顺序很简单,先调用挂钩类型,后调用模块类型。因此,从图10-3中可以看出,当一个请求处理完毕,7个函数将被调用。
10.1.2 处理阶段划分
当HTTP头读取完毕后,服务器的状态被修改为“busy_write”,与此同时,服务器将对该请求进行响应处理。整个请求阶段可以被划分为多个阶段,各个阶段可以用图10-4进行描述。Apache 2.0版本中的请求处理流程与早期的Apache 1.3版本的处理流程几乎相同。唯一的区别在于,在过滤器的概念中,Apache 2.0以上的版本中尽管只有一个内容处理器被使用,但是可能由多个模块同时负责生成响应。
图10-4 请求处理中的各个阶段
整个请求处理可以被划分为多个子阶段,这些子阶段分别是:
n Url预处理阶段
通常情况下,浏览器会自动地将请求的地址栏中的一些特殊字符(比如将空格转换为%)和十六进制进行组合,比如空格就是“%20”,因此对于服务器而言,其需要将“%xx”格式的字符串重新还原为原来的字符串。同时,Apache有必要剔除URI中一些冗余的字符,比如“./xxx”、“xxx/../”或“.//xxx”等。在进行进一步的处理之前,有必要对这些URI进行规则处理。具体的处理由ap_unescape_url ()和ap_getparents()完成,函数的细节我们在后面的部分将深入了解。
n 请求配置信息查找
在前面我们曾经描述过Apache中的配置信息。配置信息可以分为多个种类:针对整个服务器的全局配置信息、针对某个虚拟主机的配置信息、针对某个连接的配置信息,以及针对某个请求的配置信息。在请求的处理过程中,很多的信息需要依赖这些配置信息去处理,比如授权信息。为此在进行继续处理之前,服务器必须能够得到所有的与该请求关联的配置信息。
Apache中与特定请求关联的配置信息总是用<Location>…</Location>配置段进行处理,因此,对于请求的URI,Apache需要查找当前的配置信息中是否有针对该URI的配置。配置查找通过location_walk进行。这项工作必须在模块转换URI之前进行,因为它可能会影响Apache对URI的转换。
n URL重写(translate_name)
一些模块会对URL进行重写,通过对URL重写,可以在请求文件的保存路径发生变化的时候避免对外提供的URL被修改。比如mod_alias会将一个符合条件的URL用另一个别名URL进行替换,而mod_rewrite则会对给定的URL进行更加复杂的替换和修改。
在这个阶段,如果某个模块返回DECLINE,那么Apache将会返回一个500的错误码给浏览器,这意味着通知浏览器“当前请求的URI无法进行转换”,同时该错误被记入日志。
n 再次获取与该请求相关的配置信息
转换前的URI可能与转换后的URI不相同,因此一旦URL经过转换,Apache将有必要再调用location_walk获取转换后的URI所对应的配置信息。同时,核心将调用挂钩map_to_storage的处理句柄core_map_to_storage()。一旦URI转换之后,该URI所对应的Location、目录及文件都将确定下来,此时,有必要获取每一部分的配置信息。ap_directory_walk()和ap_file_walk()分别用来实现遍历指定的目录和文件的配置,这些配置与获取的location配置信息进行合并,从而得到最终的完整的针对当前请求的URI的配置信息。
n 资源映射(map_to_storag)
该阶段的目的仅仅有一个,那就是让模块确定特定的资源是否可以在磁盘上找到。使用这个挂钩的原因是,如果数据要在磁盘上找到,而不是由模块生成的,那么服务器就必须执行更加严格的安全检查。在这种情况下,服务器所做的检查越严格,就越有可能避免错误的发生。如果数据位于磁盘上,那么服务器就必须确保可以提供请求的文件。这个听起来很简单,其实并非如此。一些系统支持符号连接,因此,如果在路径中有符号连接,那么服务器就可能无法理解符号。这种情况必须能够在map_to_storage中处理。
如果请求的内容不是磁盘上的文件,而是动态生成的,那么这种检查就没有必要了。
n 头信息解析(header_parser)
该阶段的主要目的在于检验HTTP请求的报文头,并根据请求头信息做一些必要的处理。这是一个一般性目的的挂钩,在配置完全有效且进一步特化之前启用。在这一阶段,你可以根据请求头中的信息进行相关的设置,比如mod_setenvif就会根据特定的请求头信息设置一些环境变量。
另外,如果你需要实现Apache中不支持的一些HTTP方法,比如HTTP PATCH那么你也可以在该挂钩阶段实现。
n 访问检查(access_checker)
access_checker挂钩被设计用于让模块根据与模块的特定基础限制对资源进行访问。这很重要,因为它可以让系统管理员根据任意数量的特性阻止用户访问它们的站点。例如,mod_access就可以让管理员根据客户的IP地址来限制访问,这就可以让相同的Web服务器同时为公共的Web站点和私有的Web站点服务。通过access_checker模块做一些简单的配置就可以减少相当一部分的无效的攻击。
n 用户检查(check_user_id)
该挂钩可以让模块认证用户的名称和密码。对于大多数的身份验证模块来讲,这意味着如果URI有所要求,就要确保随请求发送口令信息,还要确保所提供的口令对确定用户有效。服务器会为这个挂钩运行所有的经过注册的函数,直到有函数返回DECLINED之外的内容。大多数实现check_user_id的函数模块都会使用下面的4个有效的返回值:
(1)、OK用户名和用户密码已经成功的通过认证;
(2)、HTTP_UNAUTHORIED没有找到用户或该用户提供的密码不正确;
(3)、HTTP_INTERNAL_SERVER_ERROR在认证这个用户的时候出现了严重的问题,服务器不能继续为这个用户请求服务器;
(4)、DECLINED这个模块不能支持用户认证。
n 用户授权权限检查(auth_checker)
该模块用以确定通过认证的用户是否有权利查看请求的资源。这个挂钩要依赖由核心实现的require指令。通过使用require指令,管理员就能将资源的访问权限制到一组已经配置过的用户组,或者由服务器认可的用户。这个挂钩在处理返回代码的方式上与其余的挂钩有所不同,如果针对这个挂钩的所有的模块都返回DECLINED,并且这个请求已经启用了认证,那么服务器就会使用错误消息终止当前的请求。
access_checker、check_user_id和auth_checker三个挂钩属于安全管理的内容,我们在安全管理章节会详细描述它们。
n 资源类型检查(type_checker)
该挂钩会为模块提供一个机会来确定或修改文档类型的特性。例如,mod_mime会利用该挂钩来设置语言、编码及请求类型。在这个阶段,你还可以修改被请求的资源所调用的处理器函数。
n fixups
请求处理中调用的最后一个挂钩就是fixups。这是一个一般性的挂钩,没有特定的执行意义。该挂钩被设计用来让模块在向客户端发送响应之前确定响应头。这是在服务器开始生成数据之前,模块影响响应数据的最后机会。如果在服务器开始处理器阶段之前能找到任何错误,那么模块都应该利用所有的机会来进行这样的工作。如果服务器开始为无效的请求生成响应,那么你就会在不能发送请求的页面上浪费时间和资源。
整个请求的概要流程可以用图10-5描述:
在ap_process_request中,各个阶段依次按序执行完成,但是完整的HTTP请求至少还要包括下面三个处理阶段:
n insert_filter
在该阶段,内容过滤器将被插入到请求中,他们用于对处理器生成的需要返回给客户端的数据进行过滤处理。
n handler
该阶段被称为内容生成器,主要的任务就是生成客户端所请求的响应。
n logger
该阶段用于记录请求中的对话日志。
所有的挂钩组合起来可以概括为4个不同的阶段:
n post_read_request阶段
该阶段标志着从协议到请求处理的过渡,在该挂钩中主要的任务就是设置request_rec中需要被设置的内容。虽然request_rec已经建立起来,但还是有很多的内容尚未被建立。
n 从translate_name到fixups阶段:
该阶段通常被称为“请求准备阶段”,主要对请求做必要的准备
n 从insert_filter到handler阶段
insert_filter和handler组合在一起构成了Apache中的内容生成器,该阶段属于内容生成阶段,生成的内容最终将返回给客户端。
n logger阶段
这是最后的阶段,与请求处理本身的关系不是非常大,主要用于日志记录。
在下面的部分,我们将描述请求处理中的URI处理,配置数据处理。部分阶段的详细处理我们会在后面的章节中给出,还有一部分属于模块的内容如安全处理等,我们会将其放到第三卷中进行更深入的讨论。