apache / 2.4 / reference / developer-hooks.html

Apache HTTP Server 2.x 中的钩子函数

Warning

该文档仍在开发中,可能已过时。

通常,钩子函数是 Apache HTTP Server 在处理请求期间的某个 Moment 调用的函数。模块可以提供被调用的功能,并指定与其他模块相比它们何时被调用。

Core Hooks

httpd 的核心模块提供了在标准request processing阶段使用的钩子的 sched 义列表。创建一个新的钩子将公开一个实现它的函数(请参阅以下部分),但必须了解您不会扩展 httpd 的核心钩子,这一点很重要。它们在请求处理中的存在和 Sequences 实际上是在server/request.c中对其进行调用的结果(请查看this section以获取概述)。核心钩子在doxygen documentation中列出。

强烈建议您先阅读开发模块指南request processing,然后再 continue。

创建钩子函数

为了创建一个新的钩子,需要完成四件事:

声明钩子功能

使用AP_DECLARE_HOOK宏,需要为其指定钩子函数的返回类型,钩子的名称和参数。例如,如果该钩子返回int并接受request_rec *int并被称为do_something,则可以这样声明:

AP_DECLARE_HOOK(int, do_something, (request_rec *r, int n))

如果模块要使用该钩子,则应将其包含在 Headers 中。

创建钩子结构

每个导出钩子的源文件都有一个私有结构,用于记录使用该钩子的模块功能。声明如下:

APR_HOOK_STRUCT(
  APR_HOOK_LINK(do_something)
  ...
)

实现钩子调用方

导出钩子的源文件必须实现一个将调用该钩子的函数。当前有三种可能的方法可以做到这一点。在所有情况下,调用函数都称为ap_run_hookname()

Void hooks

如果一个钩子的返回值为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)

同样,OKDECLINED是传统值。您可以使用所需的内容。

呼叫挂机呼叫者

在代码中的适当 Moment,调用钩子调用程序,如下所示:

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 */
};

控制钩子调用 Sequences

在上面的示例中,我们没有在钩子注册函数中使用三个参数来控制在钩子中注册的所有函数的调用 Sequences。有两种执行此操作的机制。第一种方法(不是很简单)允许我们大致指定钩子相对于其他模块的运行位置。最后一个参数控制这个。有三个可能的值:APR_HOOK_FIRSTAPR_HOOK_MIDDLEAPR_HOOK_LAST

使用任何特定值的所有模块可以相对于彼此以任何 Sequences 运行,但是,当然,使用APR_HOOK_FIRST的所有模块都将在APR_HOOK_MIDDLE之前运行,而APR_HOOK_LAST之前。不在乎何时运行的模块应使用APR_HOOK_MIDDLE这些值是隔开的,因此像APR_HOOK_FIRST-2这样的位置可能比其他函数更早地挂起.

请注意,还有两个值APR_HOOK_REALLY_FIRSTAPR_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设置的排序。