[apache] 挂钩说明(2)
核心层实现了一个处理HTTP请求的基本框架,每个挂钩就对应着一次HTTP请求的一个处理阶段,核心层按照某个顺序运行每个挂钩具体实现中的挂钩调用函数。
底层挂钩的代码被封装在apr-util库apr-util\include\apr_hooks.h,以宏的形式来声明。
1. 挂钩声明
在Apache中声明一个挂钩,总是通过如下的宏来实现的。挂钩只能被声明一次。
例如:header_parser挂钩
AP_DECLARE_HOOK(int,header_parser,(request_rec *r))
2. 挂钩结构/挂钩数组的声明
每一个挂钩,都有一个apr_array_header_t数组来保存它的相关内容。APACHE2中不能直接访问挂钩数组,引入了APR_HOOK_STRUCT宏,所有对数组的操作都只能通过该宏来实现。
该宏定义了一个限于模块内使用的结构_hooks,该模块内声明的所有挂钩对应的数组都保存为_hooks的成员,如server\config.c声明了含有9个挂钩的结构。
67 APR_HOOK_STRUCT( 68 APR_HOOK_LINK(header_parser) 69 APR_HOOK_LINK(pre_config) 70 APR_HOOK_LINK(post_config) 71 APR_HOOK_LINK(open_logs) 72 APR_HOOK_LINK(child_init) 73 APR_HOOK_LINK(handler) 74 APR_HOOK_LINK(quick_handler) 75 APR_HOOK_LINK(optional_fn_retrieve) 76 APR_HOOK_LINK(test_config) 77 )
其中APR_HOOK_STRUCT和APR_HOOK_LINK的定义在apr-util\include\apr_hooks.h。
134 /** macro to declare the hook structure */ 135 define APR_HOOK_STRUCT(members) \ 136 static struct { members } _hooks; 137 138 /** macro to link the hook structure */ 139 define APR_HOOK_LINK(name) \ 140 apr_array_header_t *link_##name;
可以看出_hooks结构的定义为static,该结构是模块内的私有机构,外部模块无法直接访问_hooks变量,并且只要声明了挂钩,就应该有一个对应的_hooks变量。
当某个模块想使用某个挂钩,它既不能直接访问该挂钩的挂钩数组,也不能访问被屏蔽的模块内的_hooks变量,它只能使用该挂钩的注册函数ap_<hook name>。
ap_<hook name>函数所做的事情是访问_hooks结构中的某个数组,然后在数组中添加挂钩处理函数。
例如把挂钩处理函数match_headers添加到header_parser挂钩数组中,就要用ap_hook_header_parser这个注册函数。
modules\metadata\mod_setenvif.c:
573 ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
3. 挂钩注册
如果模块对某个挂钩感兴趣,它就需要注册对应挂钩的处理函数。模块对挂钩函数的注册,通常是在模块结构中的register_hooks函数中调用对应挂钩的挂钩注册函数ap_<hook name>来实现的。
modules\metadata\mod_setenvif.c:
571 static void register_hooks(apr_pool_t *p) 572 { 573 ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE); 574 ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE); 575 }
查遍APACHE的所有文件,也不能找到ap_hook_header_parser和ap_hook_post_read_request等函数声明和实现,这是因为挂钩注册函数是通过宏AP_IMPLEMENT_HOOK_VOID/AP_IMPLEMENT_HOOK_RUN_ALL/AP_IMPLEMENT_HOOK_RUN_FIRST来实现的。
server\config.c
79 AP_IMPLEMENT_HOOK_RUN_ALL(int, header_parser, 80 (request_rec *r), (r), OK, DECLINED) 81 82 AP_IMPLEMENT_HOOK_RUN_ALL(int, pre_config, 83 (apr_pool_t *pconf, apr_pool_t *plog, 84 apr_pool_t *ptemp), 85 (pconf, plog, ptemp), OK, DECLINED) 86 87 AP_IMPLEMENT_HOOK_VOID(test_config, 88 (apr_pool_t *pconf, server_rec *s), 89 (pconf, s)) 90 91 AP_IMPLEMENT_HOOK_RUN_ALL(int, post_config, 92 (apr_pool_t *pconf, apr_pool_t *plog, 93 apr_pool_t *ptemp, server_rec *s), 94 (pconf, plog, ptemp, s), OK, DECLINED) (略) 148 AP_IMPLEMENT_HOOK_RUN_ALL(int, open_logs, 149 (apr_pool_t *pconf, apr_pool_t *plog, 150 apr_pool_t *ptemp, server_rec *s), 151 (pconf, plog, ptemp, s), OK, DECLINED) 152 153 AP_IMPLEMENT_HOOK_VOID(child_init, 154 (apr_pool_t *pchild, server_rec *s), 155 (pchild, s)) 156 157 AP_IMPLEMENT_HOOK_RUN_FIRST(int, handler, (request_rec *r), 158 (r), DECLINED) 159 160 AP_IMPLEMENT_HOOK_RUN_FIRST(int, quick_handler, (request_rec *r, int lookup), 161 (r, lookup), DECLINED) 162 163 AP_IMPLEMENT_HOOK_VOID(optional_fn_retrieve, (void , ()) 164
AP_IMPLEMENT_HOOK_VOID/AP_IMPLEMENT_HOOK_RUN_ALL/AP_IMPLEMENT_HOOK_RUN_FIRST这3个宏的定义中引用了APR_IMPLEMENT_XXX开头的宏。
#define AP_IMPLEMENT_HOOK_VOID(name,args_decl,args_use) \ APR_IMPLEMENT_EXTERNAL_HOOK_VOID(ap,AP,name,args_decl,args_use) #define AP_IMPLEMENT_HOOK_RUN_ALL(ret,name,args_decl,args_use,ok,decline) \ APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap,AP,ret,name,args_decl, \ args_use,ok,decline) #define AP_IMPLEMENT_HOOK_RUN_FIRST(ret,name,args_decl,args_use,decline) \ APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap,AP,ret,name,args_decl, \ args_use,decline)
APR_IMPLEMENT_XXX开头的宏才是关注的地方,他们都要用到APR_IMPLEMENT_EXTERNAL_HOOK_BASE这个宏。
下面以header_parser挂钩来展开说明。
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL宏的定义如下:
#define APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ns,link,ret,name,args_decl,args_use,ok,decline) \ APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \ link##_DECLARE(ret) ns##_run_##name args_decl \ { \ ns##_LINK_##name##_t *pHook; \ int n; \ ret rv = ok; \ APR_HOOK_INT_DCL_UD; \ \ APR_HOOK_PROBE_ENTRY(ud, ns, name, args_use); \ \ if(_hooks.link_##name) \ { \ pHook=(ns##_LINK_##name##_t *)_hooks.link_##name->elts; \ for(n=0 ; n < _hooks.link_##name->nelts ; ++n) \ { \ APR_HOOK_PROBE_INVOKE(ud, ns, name, (char *)pHook[n].szName, args_use); \ rv=pHook[n].pFunc args_use; \ APR_HOOK_PROBE_COMPLETE(ud, ns, name, (char *)pHook[n].szName, rv, args_use); \ if(rv != ok && rv != decline) \ break; \ rv = ok; \ } \ } \ \ APR_HOOK_PROBE_RETURN(ud, ns, name, rv, args_use); \ \ return rv; \ }
其中APR_IMPLEMENT_EXTERNAL_HOOK_BASE的宏定义:
#define APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \ link##_DECLARE(void) ns##_hook_##name(ns##_HOOK_##name##_t *pf,const char * const *aszPre, \ const char * const *aszSucc,int nOrder) \ { \ ns##_LINK_##name##_t *pHook; \ if(!_hooks.link_##name) \ { \ _hooks.link_##name=apr_array_make(apr_hook_global_pool,1,sizeof(ns##_LINK_##name##_t)); \ apr_hook_sort_register(#name,&_hooks.link_##name); \ } \ pHook=apr_array_push(_hooks.link_##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(#name,aszPre,aszSucc); \ } \ APR_IMPLEMENT_HOOK_GET_PROTO(ns,link,name) \ { \ return _hooks.link_##name; \ }
对APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL/APR_IMPLEMENT_EXTERNAL_HOOK_BASE宏进行展开:
void ap_hook_header_parser(ap_HOOK_header_parser_t *pf,const char * const *aszPre, const char * const *aszSucc,int nOrder) { ap_LINK_header_parser_t *pHook; if(!_hooks.link_header_parser) { _hooks.link_header_parser=apr_array_make(apr_hook_global_pool,1,sizeof(ap_LINK_header_parser_t)); apr_hook_sort_register("header_parser",&_hooks.link_header_parser); } pHook=apr_array_push(_hooks.link_header_parser); 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("header_parser",aszPre,aszSucc); } apr_array_header_t *ap_hook_get_header_parser(void) { return _hooks.link_header_parser; } int ap_run_header_parser (request_rec *r) { ap_LINK_header_parser_t *pHook; int n; int rv = OK; if(_hooks.link_header_parser) { pHook=(ap_LINK_header_parser_t *)_hooks.link_header_parser->elts; for(n=0 ; n < _hooks.link_header_parser->nelts ; ++n) { rv=pHook[n].pFunc (r); if(rv != OK && rv != DECLINED) break; rv = OK; } } return rv; }
可以看到上面定义了ap_hook_header_parser、ap_run_header_parser和ap_hook_get_header_parser这3个函数,分别是注册函数、调用函数、获取挂钩数组函数。
4. 挂钩调用
所有的挂钩对外提供的调用形式都是一样的ap_run_<hook name>。
挂钩的类型的类型用三个宏:AP_IMPLEMENT_HOOK_VOID、AP_IMPLEMENT_HOOK_RUN_FIRST及AP_IMPLEMENT_HOOK_RUN_ALL来区分。
三者的内部实现不尽相同, 具体差别见[apache] 挂钩说明(1)