一.挂钩使用
1.自定义挂钩的使用步骤:
1、声明
#define AP_DECLARE_HOOK(ret,name,args) /
APR_DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args)
即,使用宏AP_DECLARE_HOOK或APR_DECLARE_EXTERNAL_HOOK来声明一个挂钩,该声明宏展开后是五个函数:挂钩的执行函数、挂钩的注册函数、挂钩的调用函数、返回挂钩函数链的函数以及函数链节点结构。
以post_config挂钩为例,展开后的五个函数分别为:
执行函数:ap_HOOK_post_config_t
注册函数:ap_hook_post_config
调用函数:ap_run_post_config
获取函数链:AP_DECLARE (apr_array_header_t *) ap_hook_get_post_config(void)
typedef struct ap_LINK_post_config_t
{
ap_HOOK_post_config_t *pFunc;
const char *szName;
const char * const *aszPredecessors;
const char * const *aszSuccessors;
int nOrder;
} ap_LINK_post_config_t;
2、添加
挂钩结构体APR_HOOK_STRUCT中包含了当前模块的所有挂钩,因此,我们需要将自己定义的挂钩添加到该结构体中。比如在第一步中我们声明了如下的挂钩AP_DECLARE_HOOK(int,some_hook,(request_rec* r,int n)),则
APR_HOOK_STRUCT(
APR_HOOK_LINK(some_hook)
……
)
3、实现
在上面的第一步中声明了五个函数,在这一步中要实现其中的两个函数:注册函数和调用函数,其中
宏APR_IMPLEMENT_EXTERNAL_HOOK_BASE实现了挂钩的注册函数,以post_config挂钩为例,该宏展开的结果如下:
AP_DECLARE(int) ap_hook_post_config(ap_HOOK_post_config_t *pf,
const char * const *aszPre,
const char * const *aszSucc,
int nOrder)
{
ap_LINK_post_config_t *pHook;
if (!_hooks.link_post_config) {
_hooks.link_post_config = apr_array_make(apr_hook_global_pool,1,sizeof(ap_LINK_post_config_t));
apr_hook_sort_register("post_config", &_hooks.link_post_config);
}
pHook = apr_array_push(_hooks.link_post_config);
pHook->pFunc = pf;
pHook->aszPredecessors = aszPre;
pHook->aszSuccessors = aszSucc;
pHook->nOrder = nOrder;
pHook->szName = apr_hook_debug_current;
if (apr_hook_debug_enabled)
apr_hook_debug_show("post_config", aszPre, aszSucc);
}
AP_DECLARE(apr_array_header_t *) ap_hook_get_post_config(void) {
return _hooks.link_post_config;
}
从上面的展开结果中我们可以很清楚的看出APR_IMPLEMENT_EXTERNAL_HOOK_BASE宏实现了我们所需要的挂钩注册函数以及挂钩信息获取函数。
下面三个宏AP_IMPLEMENT_HOOK_VOID、AP_IMPLEMENT_HOOK_RUN_FIRST以及 AP_IMPLEMENT_HOOK_RUN_ALL实现了挂钩的调用函数,以AP_IMPLEMENT_HOOK_VOID为例展开:
#define APR_IMPLEMENT_EXTERNAL_HOOK_VOID(ns,link,name,args_decl,args_use) /
APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) /
link##_DECLARE(void) ns##_run_##name args_decl /
{ /
ns##_LINK_##name##_t *pHook; /
int n; /
if(!_hooks.link_##name) /
return; /
pHook=(ns##_LINK_##name##_t *)_hooks.link_##name->elts; /
for(n=0 ; n < _hooks.link_##name->nelts ; ++n) /
pHook[n].pFunc args_use; /
}
从上面我们可以看到,在APR_IMPLEMENT_EXTERNAL_HOOK_VOID宏中已经包含了宏APR_IMPLEMENT_EXTERNAL_HOOK_BASE,即该宏同时实现了挂钩的注册函数和调用函数。
4、关联
将挂钩函数与挂钩通过挂钩的注册函数ap_hook_test_hook(test_hook_func,NULL,NULL,APR_HOOK_MIDDLE);
[其中,test_hook挂钩的名字,test_hook_func该挂钩的挂钩函数]进行关联并通过模块的注册函数register_hooks(apr_pool_t *p)注册到模块内。
5、调用
一旦各个模块在挂钩数组中注册了自己感兴趣的挂钩,那么剩下的事情无非就是调用这些挂钩,实际上也就是最终调用挂钩对应的函数。Apache中的挂钩调用函数形式通常如ap_run_HOOKNAME所 示,比如ap_run_post_config就是调用post_config挂钩。
2.标准挂钩的使用步骤:
Apache本身实现了部分挂钩,这些由apache自身实现的挂钩在调用时直接在module结构体的注册挂钩函数中调用挂钩的注册函数即可,以mod_status模块中的标准挂钩handler和post_config为例:
static void register_hooks(apr_pool_t *p)
{
/* handler与post_config是apache定义的标准挂钩;test_hook是用户自定义的普通挂钩;optional_hook是用户自定义的可选挂钩 */
ap_hook_handler(status_handler, NULL, NULL, APR_HOOK_MIDDLE);
/* status_init是post_config挂钩的挂钩函数(挂钩的执行函数),ap_hook_post_config是挂钩的注册函数 */
ap_hook_post_config(status_init, NULL, NULL, APR_HOOK_MIDDLE);
/* Optional hook 将可选挂钩添加到apache内核的全局可选挂钩哈希表中 */
APR_OPTIONAL_HOOK(ap, optional_hook,optional_hook_func,NULL,NULL,APR_HOOK_MIDDLE);
/* No-optional hook 普通挂钩的注册函数 */
ap_hook_test_hook(test_hook_func,NULL,NULL,APR_HOOK_MIDDLE);
#ifdef HAVE_TIMES
ap_hook_child_init(status_child_init, NULL, NULL, APR_HOOK_MIDDLE);
#endif
}
module AP_MODULE_DECLARE_DATA status_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
status_module_cmds, /* command table */
register_hooks /* register_hooks */
};
3.可选挂钩的使用步骤:
1、声明
可选挂钩的声明方法与标准挂钩声明没有任何区别,都是通过AP_DECLARE_HOOK进行的,比如下面的语句声明一个可选挂钩:
AP_DECLARE_HOOK(int , optional_hook , (request_rec *r , int n))
与标准挂钩相比,可选挂钩没有内部私有的数据结构。在标准挂钩中,为了保存各个模块对声明的挂钩的使用情况,通过声明AP_HOOK_STRUCT结构来实现。这种做法实际上是由挂钩实现者自行进行维护;而对于可选挂钩,模块编写者可以不需要维护该AP_HOOK_STRUCT结构了,该结构则转交内核维护。
2、添加
对于可选挂钩,由于不存在AP_HOOK_STRUCT宏,因此也就不存在挂钩数组了。在前面我们提到过,可选挂钩的保存是由Apache内核维护的,在Apache内部,内核定义了两个全局哈希表s_phOptionalHooks和s_phOptionalFunctions分别保存所有的可选挂钩以及对应的挂钩处理句柄。S_phOptionalHooks哈希表中,可选挂钩名称用来作为哈希表Key键,而挂钩对应的挂钩数组则作为哈希表的Value值。我们展开宏APR_OPTIONAL_HOOK就知道了。
APR_OPTIONAL_HOOK宏定义在ap_optional_hooks.h中:
#define APR_OPTIONAL_HOOK(ns,name,pfn,aszPre,aszSucc,nOrder) do { \
ns##_HOOK_##name##_t *apu__hook = pfn; \
apr_optional_hook_add(#name,(void (*)(void))apu__hook,aszPre, aszSucc, nOrder); \
} while (0)
由该宏我们可以得知,该宏将可选挂钩添加到了内核的可选挂钩哈希表中。
3、实现
在实现上,可选挂钩的声明从标准挂钩的AP_IMPLEMENT_HOOK_RUN_ALL形式转变为AP_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL,而且没有AP_IMPLEMENT_OPTIONAL_HOOK_RUN_VOID和AP_IMPLEMENT_OPTIONAL_HOOK_RUN_FIRST这两个宏。同时要注意,与非可选挂钩的AP_IMPLEMENT_HOOK_RUN_ALL相比,可选挂钩的AP_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL仅仅实现了挂钩的调用函数而没有同时实现挂钩的注册函数。
4、关联
对于可选挂钩,其关联同上面第二步的添加操作所用的是同一个宏,即:将挂钩函数与挂钩通过APR_OPTIONAL_HOOK(ap, optional_hook,optional_hook_func,NULL,NULL,APR_HOOK_MIDDLE);
[其中,optional_hook挂钩的名字,optional_hook_func该挂钩的挂钩函数]进行关联并通过模块的注册函数register_hooks(apr_pool_t *p)注册到apache的内核中。
5、调用
调用同标准挂钩,调用ap_run_HOOKNAME即可。
在apache源码中D:\httpd-2.4.9\modules\test目录下有mod_optional_hook_export.c和mod_optional_hook_import.c两个关于可选挂钩的Demo,可结合这两个进行分析。
说明:在第四步中实现了可选挂钩的函数并将其添加到了apache的内核可选挂钩哈希表中,在第五步中调用可选挂钩函数时会触发可选挂钩的实现函数,如果在一个模块内不需要调用可选挂钩函数,则第四、五两步可以省略不实现,比如apache自带的mod_status模块仅仅有前三步骤,这样的模块只是定义了一个可选挂钩,当其他的模块若需要该可选挂钩的时候再来实现第四和五两步即可。
附:可选挂钩与普通(非可选)挂钩在使用上的区别:
对比上面一和三,我们可以发现,他们两者的区别主要在第二步添加上,对于用户自定义添加的普通挂钩,其所在模块必有一个挂钩结构体APR_HOOK_STRUCT来保存当前模块中的所有挂钩,挂钩注册的对象是当前模块的挂钩结构体(即:向当前模块的挂钩结构体内添加该挂钩的挂钩函数);而可选挂钩由apache内核维护,具有全局可见性,因此在声明可选挂钩的模块内无需在挂钩结构体内在包含该挂钩了。
二.可选函数的使用
1、声明
#include <apr_optional.h>
/* Optional function */
APR_DECLARE_OPTIONAL_FN(int, optional_func, (request_rec*, int flags));
2、实现
static int optional_func(request_rec *r, int flags)
{
ap_rputs("<h4 style=\"text-align: center\"> Apache Optional Function </h4>\n", r);
return 0;
}
3、注册
在模块register_hooks函数内注册可选函数,将可选函数添加到apache内核维护的全局可选函数哈希表中。
/* Optional Function 将可选函数注册到apache内核的全局可选函数哈希表中 */
APR_REGISTER_OPTIONAL_FN(optional_func);
4、获取
以上三步均在可选函数的实现模块中,下面两步在调用模块中:
static APR_OPTIONAL_FN_TYPE(optional_func) *get_optional_func = 0;
get_optional_func = APR_RETRIEVE_OPTIONAL_FN(optional_func);
5、调用
if (get_optional_func){
get_optional_func(r,1);
}
在apache源码中D:\httpd-2.4.9\modules\test目录下有mod_optional_fn_export.c和mod_optional_fn_import.c两个关于可选函数的Demo,可结合这两个进行分析。
三.过滤器的使用
如果说挂钩是横向对输入数据进行处理的话,那么过滤器则是纵向对输入数据进行处理,过滤器分输入过滤器和输出过滤器,两者在使用上的步骤如下。
1.输入过滤器:
1、注册
在挂钩注册函数内进行注册过滤器,ap_register_input_filter(s_szCaseFilterName, CaseFilterInFilter, NULL,AP_FTYPE_RESOURCE);
其中,s_szCaseFilterName为过滤器的名称, CaseFilterInFilter为过滤器的实现函数。
2、调用
对过滤器的调用有两种方法,其一在配置文件内使用配置指令
AddOutputFilter filterName .html
其二,在程序中调用ap_add_input_filter(s_szCaseFilterName,NULL,r,r->connection);
其中,s_szCaseFilterName为过滤器的名称。
2.输出过滤器:
1、注册
在挂钩注册函数内进行注册过滤器,ap_register_out_filter(s_szCaseFilterName, CaseFilterInFilter, NULL,AP_FTYPE_RESOURCE);
其中,s_szCaseFilterName为过滤器的名称, CaseFilterInFilter为过滤器的实现函数。
2、调用
对过滤器的调用有两种方法,其一在配置文件内使用配置指令
AddOutputFilter filterName .html
其二,在程序中调用ap_add_out_filter(s_szCaseFilterName,NULL,r,r->connection);
其中,s_szCaseFilterName为过滤器的名称。
在apache源码中D:\httpd-2.4.9\modules\examples目录下有mod_case_filter.c和mod_case_filter_in.c两个关于过滤器的Demo,可结合这两个进行分析。