技术宅和经济盲

Thursday, February 10, 2011

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