尝试制作Apache模块

以下内容是转载自2010年2月5日的过去博客。请注意该内容可能已经过时。

我调查了有关Apache模块的制作方法,并总结了一下。

以下是参考的网站:

* module.jp
* 抜きん出て日记
* daily dayflower
* DSAS开发者的房间

我使用的环境是FreeBSD 7.2-RELEASE和Apache/2.2.11。

制作原型

当使用apxs命令并指定-g选项时,它会为模块生成一个大致的模板。通过-n选项可以指定模块的名称。

apxs -g -n sample

生成后的源代码如下。由于太长,因此已删除部分注释。

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

static int sample_handler(request_rec *r)
{
    if (strcmp(r->handler, "sample")) {
        return DECLINED;
    }
    r->content_type = "text/html";

    if (!r->header_only)
        ap_rputs("The sample page from mod_sample.c\n", r);
    return OK;
}

static void sample_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(sample_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA sample_module = {
    STANDARD20_MODULE_STUFF,
    NULL,                  /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    NULL,                  /* table of config file commands       */
    sample_register_hooks  /* register hooks                      */
};

编译和安装

如果使用apxs也会很方便。使用-c进行编译,-i进行安装,-a会为httpd.conf中写入LoadModule。虽然有Makefile,可以使用make && make install,但不知为何会出错。

apxs -c -i -a mod_print.c

在httpd.conf文件中,还需要添加以下行:

<Location "/foo/">
   SetHandler sample
</Location>

重启Apache并进行操作确认。

$ w3m -dump_both http://127.0.0.1/foo/
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2010 22:48:00 GMT
Server: Apache/2.2.11 (FreeBSD) mod_ssl/2.2.11 OpenSSL/0.9.8e DAV/2
Content-Length: 34
Connection: close
Content-Type: text/html

The sample page from mod_sample.c

在访问控制的情况下

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

static int myauth_handler(request_rec *r)
{
    if( 1 )
        return OK;
    else
        return HTTP_FORBIDDEN;
}

static void myauth_register_hooks(apr_pool_t *p)
{
    ap_hook_access_checker(myauth_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA myauth_module = {
    STANDARD20_MODULE_STUFF,
    NULL,                  /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    NULL,                  /* table of config file commands       */
    myauth_register_hooks  /* register hooks                      */
};

通过在if语句中进行条件判断,可以实现访问控制。然而,在这个例子中,整个网站都成为了共同的访问控制。关于仅对部分进行访问控制的方法,我将在以后进行调查。

显示各种信息 (Xianshi ge zhong xinxi)

在request_rec或request_rec.headers_in中包含了各种信息。

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

static int print_handler(request_rec *r)
{
    if (strcmp(r->handler, "print")) {
        return DECLINED;
    }
    r->content_type = "text/plain";

    if (!r->header_only) {
        ap_rprintf(r,"method = %s\n",r->method);
        ap_rprintf(r,"uri = %s\n",r->uri);
        ap_rprintf(r,"filename = %s\n",r->filename);
        ap_rprintf(r,"user-agent = %s\n",apr_table_get(r->headers_in, "User-Agen
t"));
    }

    return OK;
}

static void print_register_hooks(apr_pool_t *p)
{
    ap_hook_handler(print_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA print_module = {
    STANDARD20_MODULE_STUFF,
    NULL,                  /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    NULL,                  /* table of config file commands       */
    print_register_hooks  /* register hooks                      */
};

执行后会得到如下结果。

$ w3m -dump_both http://127.0.0.1/foo/
HTTP/1.1 200 OK
Date: Fri, 05 Feb 2010 22:50:40 GMT
Server: Apache/2.2.11 (FreeBSD) mod_ssl/2.2.11 OpenSSL/0.9.8e DAV/2
Content-Length: 92
Connection: close
Content-Type: text/plain

method = GET
uri = /foo/
filename = /usr/local/www/apache22/data/foo
user-agent = w3m/0.5.2

使用Cookie进行身份验证

我将之前的信息总结起来,给cookie设置了一个访问标志,并创建了一个模块,只有第一次访问时才可以访问。

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

#include <string.h>

static int cookieauth_handler(request_rec *r)
{
    const char *cookie;

    apr_table_set(r->headers_out, "Set-Cookie","visited=true;");

    cookie = apr_table_get(r->headers_in, "Cookie");
    if(cookie!=NULL && strcmp(cookie, "visited=true")==0) {
        return HTTP_FORBIDDEN;
    }
    else {
        return OK;
    }
}

static void cookieauth_register_hooks(apr_pool_t *p)
{
    ap_hook_access_checker(cookieauth_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA cookieauth_module = {
    STANDARD20_MODULE_STUFF,
    NULL,                  /* create per-dir    config structures */
    NULL,                  /* merge  per-dir    config structures */
    NULL,                  /* create per-server config structures */
    NULL,                  /* merge  per-server config structures */
    NULL,                  /* table of config file commands       */
    cookieauth_register_hooks  /* register hooks                      */
};

出于某种原因,w3m无法正确处理Cookie,因此我尝试使用Firefox进行确认,结果只有初次加载页面才能成功显示。

bannerAds