Apache hooks简介
项目需要,而且网上的中英文材料都语焉不详,所以看了看apache hooks的代码。
我们的代码是:
static void register_hooks(apr_pool_t *pool) {
...
ap_hook_translate_name(set_cookie, NULL, NULL, APR_HOOK_FIRST);
ap_hook_handler(process_request, NULL, NULL, APR_HOOK_FIRST);
}
module another_client = {
...;
register_hooks;
};
我想搞明白来一个request,到底cookie是怎么handle的,所以需要知道到底set_cookie和process_request的调用的order到底是怎样的。好了,然后这个问题看了我快一天,这代码写的。
首先是ap_hook_translate_name(...)。这个的函数是有宏写的定义,方便扩展到自定义的hooks上。(我不是很确定这是一种很现代化的实现,不过在那个用C来写可扩展程序的年代,貌似也是一种唯一的办法了)下面是我手动扩展出来的定义代码:(你也可以用-E的选项生成语编译代码来看看是否是你需要的东西,定义在include/http_request.h,实现在server/request.c)
// Declaration
typedef int ap_HOOK_translate_name_t (request_rec *r);
void ap_hook_translate_name(ap_HOOK_translate_name_t *pf,
const char * const *aszPre,
const char * const *aszSucc, int nOrder);
int ap_run_translate_name (request_rec *r);
apr_array_header_t * ap_hook_get_translate_name(void)
typedef struct ap_LINK_translate_name_t {
ap_HOOK_translate_name_t *pFunc;
const char *szName;
const char * const *aszPredecessors;
const char * const *aszSuccessors;
int nOrder;
} ap_LINK_translate_name_t;
// Implementation
void ap_hook_translate_name(
ap_HOOK_translate_name_t *pf,
const char * const *aszPre,
const char * const *aszSucc,
int nOrder)
{
ap_LINK_translate_name_t *pHook;
if(!_hooks.link_translate_name)
{
_hooks.link_translate_name = apr_array_make(
apr_hook_global_pool,1,sizeof(ap_LINK_translate_name_t));
apr_hook_sort_register(translate_name,
&_hooks.link_translate_name);
}
pHook=apr_array_push(_hooks.link_translate_name);
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(translate_name, aszPre, aszSucc);
}
apr_array_header_t * ap_hook_get_translate_name(void)
{
return _hooks.link_translate_name;
}
int ap_run_translate_name (request_rec *r)
{
ap_LINK_translate_name_t *pHook;
int n;
int rv;
if(!_hooks.link_translate_name)
return DECLINED;
pHook=(ap_LINK_translate_name_t *)_hooks.link_translate_name->elts;
for(n=0 ; n < _hooks.link_translate_name->nelts ; ++n)
{
rv=pHook[n].pFunc (r);
if(rv != DECLINED)
return rv;
}
return DECLINED;
}
可以看出来,调用translate_name的hook的入口是ap_run_translate_name。然后我们就可以关注到底那里调用了这个东西,以及另外ap_run_handler的位置在哪里了。这个任务相对比较简单,它们的位置分别是modules/http/http_request.c中的ap_process_request(...)和server/request.c中的ap_process_request_internal(...),下面是我根据这两个函数summarize出来的我所知道的所有hooks的order:
// in ap_process_request
ap_run_quick_handler
ap_process_request_internal
ap_run_translate_name
ap_run_map_to_storage
ap_run_header_parser
ap_run_access_checker
ap_run_check_user_id
ap_run_auth_check
ap_run_type_checker
ap_run_fixups
ap_invoke_handler
ap_run_handler
// hook create_request 比较复杂,本文不述。而且,因为它跟我没关系,所以我没准备去看它的代码,有兴趣的同志自己看看,然后发个贴吧。
另外一个要注意的是:同一个hook可能注册很多的handler。看我们上面translate_name的代码,可以知道他们会排序(看多点代码可以知道就是按APR_HOOK_REALLY_FIRST/APR_HOOK_FIRST/APR_HOOK_MIDDLE/APR_HOOK_LAST/APR_HOOK_REALLY_LAST排序),同样次序的不保证执行的先后,最后执行的一个就是返回非DECLINED结果的那个。所以,在注册handler的时候,记得不要注册重复优先级的handler到同一个hook上,否则你会搞不清楚到底执行了那个的。
最后,说说观感吧。对于程序员来说,如果Project因为历史原因用了apache的话,慢慢refactoring,然后逃离吧。这样的代码讲究效率,但是用起来太累了。如果刚过开始选择的话,我建议app engine自己写吧。做点request handling其实并不是那么难的事情对吧?
至于一般用户,apache还是蛮方便的,不过用用它default的功能就行,用太多module的话,如果里面有冲突,或者有先后次序的关系的话,光看配置文件是没法搞定的,所以要小心啊。可以的话,学着写两行程序吧。
0 Comments:
Post a Comment
<< Home