apacheのモジュールを作ってみる

**2010/2/5の過去ブログの転載です。内容が古い可能性がありますのでご注意ください。**

apacheのモジュールの作り方について調べてみたので、とりあえずのまとめです。

参考にしたサイト。
* [module.jp](http://module.jp/)
* [抜きん出て日記](http://nukin.jugem.jp/?cid=1)
* [daily dayflower](http://d.hatena.ne.jp/dayflower/searchdiary?word=apache)
* [DSAS開発者の部屋](http://dsas.blog.klab.org/)

使用した環境は、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`のところで条件判定することでアクセス制御が出来ます。ただし、この例ではサイト全体が共通のアクセス制御になってしまいます。<Directory>などで一部だけアクセス制御するための方法は、また今度調べます。

# 色々な情報表示
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で確認しましたが、うまく初回だけページが表示できました。