[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)

 

 

posted on 2013-03-06 23:45  facome  阅读(577)  评论(0编辑  收藏  举报

导航