Apache HTTP Server 版本 2.4
可用语言: en
本文档仍在开发中,可能部分过时。
一般来说,钩子函数是在 Apache HTTP Server 处理请求过程中的某个时间点调用的函数。模块可以提供被调用的函数,并指定它们相对于其他模块的调用时间。
httpd 的核心模块提供了一个预定义的钩子列表,这些钩子在标准的 请求处理 阶段使用。创建新的钩子将公开实现它的函数(参见下面的部分),但必须了解,您不会扩展 httpd 的核心钩子。它们在请求处理中的存在和顺序实际上是它们在 server/request.c
中调用方式的结果(有关概述,请查看 本节)。核心钩子在 doxygen 文档 中列出。
为了创建一个新的钩子,需要做四件事
使用 AP_DECLARE_HOOK
宏,该宏需要提供钩子函数的返回类型、钩子的名称和参数。例如,如果钩子返回一个 int
,并接受一个 request_rec *
和一个 int
,并且被称为 do_something
,那么像这样声明它
AP_DECLARE_HOOK(int, do_something, (request_rec *r, int n))
这应该放在一个头文件中,如果模块想要使用钩子,它们会包含这个头文件。
每个导出钩子的源文件都有一个私有结构,用于记录使用钩子的模块函数。它被声明如下
APR_HOOK_STRUCT( APR_HOOK_LINK(do_something) ... )
导出钩子的源文件必须实现一个函数来调用钩子。目前有三种可能的方法可以做到这一点。在所有情况下,调用函数被称为 ap_run_hookname()
。
如果钩子的返回值是 void
,那么所有钩子都会被调用,调用器像这样实现
AP_IMPLEMENT_HOOK_VOID(do_something, (request_rec *r, int n), (r, n))
第二个和第三个参数是虚拟参数声明和虚拟参数,它们将在调用钩子时使用。换句话说,这个宏展开成类似于这样的东西
void ap_run_do_something(request_rec *r, int n) { ... do_something(r, n); }
如果钩子返回一个值,那么它可以一直运行到第一个执行有趣操作的钩子,就像这样
AP_IMPLEMENT_HOOK_RUN_FIRST(int, do_something, (request_rec *r, int n), (r, n), DECLINED)
第一个不返回 DECLINED
的钩子停止循环,它的返回值从钩子调用器返回。请注意,DECLINED
是传统的钩子返回值,表示“我没有做任何事”,但它可以是任何适合你的值。
或者,所有钩子都可以一直运行到发生错误为止。这归结为允许两个返回值,其中一个表示“我做了一些事情,并且一切正常”,另一个表示“我没有做任何事情”。第一个返回除这两个值之外的值的函数停止循环,它的返回值就是返回值。像这样声明它们
AP_IMPLEMENT_HOOK_RUN_ALL(int, do_something, (request_rec *r, int n), (r, n), OK, DECLINED)
同样,OK
和 DECLINED
是传统的值。你可以使用你想要的任何值。
在代码中的适当位置,调用钩子调用器,就像这样
int n, ret; request_rec *r; ret=ap_run_do_something(r, n);
想要调用钩子的模块需要做两件事。
包含适当的头文件,并定义一个正确类型的静态函数
static int my_something_doer(request_rec *r, int n) { ... return OK; }
在初始化期间,服务器将调用每个模块的钩子注册函数,该函数包含在模块结构中
static void my_register_hooks() { ap_hook_do_something(my_something_doer, NULL, NULL, APR_HOOK_MIDDLE); } mode MODULE_VAR_EXPORT my_module = { ... my_register_hooks /* register hooks */ };
在上面的例子中,我们没有使用钩子注册函数中的三个参数来控制在钩子中注册的所有函数的调用顺序。有两种机制可以做到这一点。第一种方法比较粗糙,允许我们大致指定钩子相对于其他模块的运行位置。最后一个参数控制这一点。有三个可能的值:APR_HOOK_FIRST
、APR_HOOK_MIDDLE
和 APR_HOOK_LAST
。
所有使用任何特定值的模块都可以在彼此之间以任何顺序运行,但当然,所有使用 APR_HOOK_FIRST
的模块将在 APR_HOOK_MIDDLE
之前运行,而 APR_HOOK_MIDDLE
又在 APR_HOOK_LAST
之前运行。不关心何时运行的模块应该使用 APR_HOOK_MIDDLE
。这些值是间隔开的,因此像 APR_HOOK_FIRST-2
这样的位置可以比其他函数稍微早一些挂钩。
请注意,还有两个值,APR_HOOK_REALLY_FIRST
和 APR_HOOK_REALLY_LAST
。这些值只应该由钩子导出器使用。
另一种方法允许更精细的控制。当一个模块知道它必须在其他一些模块之前(或之后)运行时,它可以按名称指定它们。第二个(第三个)参数是一个以 NULL 结尾的字符串数组,包含必须在当前模块之前(之后)运行的模块的名称。例如,假设我们希望“mod_xyz.c”和“mod_abc.c”在我们之前运行,那么我们将像这样挂钩
static void register_hooks() { static const char * const aszPre[] = { "mod_xyz.c", "mod_abc.c", NULL }; ap_hook_do_something(my_something_doer, aszPre, NULL, APR_HOOK_MIDDLE); }
请注意,用于实现此目的的排序是稳定的,因此由 APR_HOOK_ORDER
设置的排序将尽可能保留。
可用语言: en