[普通]apache挂钩

作者(passion) 阅读(1024次) 评论(0) 分类( apache)

在Apache中挂钩hook共可分为三类:自定义挂钩、标准挂钩和可选挂钩,下面逐一介绍下这三类挂钩的使用方法。

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,可结合这两个进行分析。

« 上一篇:wifi共享上网(至尊版wifi)
« 下一篇:ASP.NET附加数据库文件的方式,如何发布到IIS7而不导致SQLServer出错
在这里写下您精彩的评论
  • 微信

  • QQ

  • 支付宝

返回首页
返回首页 img
返回顶部~
返回顶部 img