Apache中的挂钩剖析(1)[1]

[入库:2006年2月23日] [更新:2007年3月24日]

本文简介:

5.5 挂钩(HOOK)
5.5.1 为什么引入挂钩
Apache1.3版本中,对HTTP请求的处理包括若干个固定阶段,比如地址转换阶段、身份确认阶段、身份认证阶段、权限确认阶段、MIME类型识别阶段等等,这也意味着Apache1.3中的挂钩数目是有限的,固定的。这个反映在模块结构中就是针对每个HOOK都对应一个函数指针。比如如果需要检查用户的身份是否合法则只需要调用ap_check_user_i;如果需要核查用户的权限是否满足则只需要调用auth_checker;而如果需要记录日志,则只需要调用logger函数即可。这种对请求的处理策略非常清晰明了。不过这种方法也有明显的局限性,首先就是所有的模块都维护所有的挂钩,但对于某个模块而言只有很少的几个挂钩会被使用,比如ap_check_user_id挂钩可能只有安全模块才用到,模块中不使用的挂钩都被置为NULL;最重要的问题是很难增加新的挂钩。如果需要增加一个新的挂钩,就必须修改module结构,这涉及到所有的模块的修改,工作量很大,而且需要重新进行编译,因此这种策略使得模块的扩展性能极差。
为了使得Apache 2.0版本为了能够更具模块化,更具扩展性,apache采取了更加灵活的处理策略,即“模块自行管理”策略,即挂钩(Hooks)的概念,Hooks的使用使得函数从静态的变为动态的,模块编写者可以通过Hooks自行增加处理句柄,而不需要所有的模块都千篇一律。因此每次增加新函数,唯一必须修改的就是增加函数的模块而已。
为了对挂钩有大体的了解,我们首先来看一下Apache2.0的HTTP请求处理流程。
从大的方面来看,Apache对HTTP的请求可以分为连接、处理和断开连接三个阶段。从小的方面而言,每个阶段又可以分为更多的子阶段。比如对HTTP的请求,我们可以进一步划分为客户身份验证、客户权限认证、请求校验、URL重定向等阶段,每一个阶段调用相应的函数进行处理。在Apache中,这些子阶段可以用术语“挂钩(HOOK)”来描述。Apache中对请求的处理过程实质上就是依次调用一系列挂钩的过程,不过由于HTTP请求类型的不同,它们所对应的挂钩数目和类型也不尽相同。对于典型的HTTP请求,有一个默认的挂钩调用顺序,你可以按照这个默认的顺序进行调用,也可以不遵守这个顺序,你可以根据自己的情况调整调用顺序。整个HTTP的请求可以用下图来描述:

 
在上面的图示中,存在六种不同的挂钩,对于请求a,其指需要调用Hook2.、1;而对于请求b,则需要调用1、6、5;请求c则需要调用1、4、3、6四个挂钩。
在Apache中,挂钩总是和挂钩函数联系在一起的。挂钩是用来表示在处理HTTP请求中一组类似的操作,与之对应,挂钩函数就是操作函数。不过即使挂钩相同,对应的挂钩函数也未必相同。举个简单的例子,配置文件模块和虚拟主机模块都需要身份验证挂钩来确认访问者能否访问相应的资源,但两个模块的验证方法则差别很大。因此一个挂钩同时是和一组挂钩函数联系在一起的。因此请求处理过程中,调用挂钩的时候实际上就是调用挂钩函数组。调用过程可以用下图示意。Apache对指定挂钩的挂钩函数调用有两种,一种就是将挂钩函数组中的每个函数都调用一次,比如图中的挂钩4;另一种就是从前往后调用,一旦找到合适的就停止继续调用,图中1、2、3就是这种情况。
通过挂钩机制,你可以自行修改服务器的行为,比如修改挂钩函数或者增加挂钩函数,甚至增加挂钩。不过增加挂钩只是2.0才提供的功能。

5.5.2 声明挂钩
Apache中关于挂钩的实现大部分是通过宏来实现的,而且这些宏大多数非常复杂。挂钩的实现主要定义在文件apr_hook.h和apr_hook.c中,另外在config.c中也有部分定义。
Apache中对挂钩的使用总是从定义一个挂钩开始的,在Apache中声明一个挂钩,总是通过宏
#define AP_DECLARE_HOOK(ret,name,args) \
    APR_DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args)
来实现的。在该宏中,ret是定义的挂钩的返回类型;而name则是定义的挂钩的名称;args则是挂钩函数需要的额外参数,通常是以bracket形式出现的。例如下面的语句就声明了一个返回类型为整数,函数名称为do_something,函数参数为(request_rec *r,int n)的挂钩:
AP_DECLARE_HOOK(int , do_something , (request_rec *r , int n))
不过AP_DECLARE_HOOK内部则是调用的APR_DECLARE_EXTERNAL_HOOK宏。该宏定义如下:
#define APR_DECLARE_EXTERNAL_HOOK(ns,link,ret,name,args) \
typedef ret ns##_HOOK_##name##_t args; \
link##_DECLARE(void) ns##_hook_##name(ns##_HOOK_##name##_t *pf, \
                                      const char * const *aszPre, \
                                      const char * const *aszSucc, int nOrder); \

本文关键:Apache中的挂钩剖析(1)
  相关方案
Google
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top