apache 2.0用のモジュール作成
自分向けの備忘録。
プロジェクトの作成
apxsというツールを利用してひな形を作成することができる。次の例は、mod_helloworldを作成する。
apxs -g -n helloworld
ビルド
apxsの作成したMakefileを利用する場合、ビルドは簡単である。
make CC=gcc CXX=c++
ただし、実際には、g++を利用した場合、リンク時の問題を避けるために、
make CC=g++ CXX=g++
とした方が良いと考えられる。
インストール
特に難しいことに気を遣う必要がなければ、apxsの作成したMakefileで行うことができる。
make install
ただし、設定ファイルは自分で作成する必要がある。CentOSの場合には、/etc/httpd/conf.dの下に、helloworld.confのような名前で次のようなファイルを作成する。
LoadModule helloworld_module modules/mod_helloworld.so <Location /helloworld> SetHandler helloworld SetMessage Hello, everyone! </Location>
LoadModuleの第一パラメータはモジュール名だが、これについては、次のコーディングを参照のこと。
コーディング
モジュール名
LoadModuleの第一パラメータ、モジュール名は、ソースコード中で、
/* Dispatch list for API hooks */ module AP_MODULE_DECLARE_DATA helloworld_module = { STANDARD20_MODULE_STUFF, helloworld_create_per_dir_config, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ NULL, /* creSeate per-server config structures */ NULL, /* merge per-server config structures */ helloworld_commands, /* table of config file commands */ helloworld_register_hooks /* register hooks */ };
のように定義されている変数の名前である。
helloworld_create_per_dir_config関数
この関数は、ディレクトリごとの設定、つまり、Locationあるいは、Directoryによって設定された設定を保存する領域を初期化する関数である。構造体の構造(設計)および、そのインスタンス作成は完全にユーザーに委ねられている。
helloworldでは、表示メッセージを保持するために次のような構造体を定義している。
typedef struct _helloworld_per_dir_config { const char* message; // 表示するメッセージ } helloworld_per_dir_config;
そして、helloworld_create_per_dir_config関数は、この構造体のためのメモリ確保、初期化を行う。実際の実装は次のようになる。
static void* helloworld_create_per_dir_config(apr_pool_t* p, char* arg) { helloworld_per_dir_config* conf; conf = (helloworld_per_dir_config*)apr_palloc(p, sizeof(helloworld_per_dir_config)); conf->message = ""; // 無難な初期値 }
ここでは、mallocなどではなく、aprが提供するapr_pallocを利用する。この場合、エラー復帰時などにメモリを解放し忘れた場合にも、apacheがプール(apr_pool_tによって管理されている)解放時に自動的に解放を行ってくれるため、メモリリークを防ぐことができる。
設定コマンド
また、Locationブロックの中で利用される設定コマンドは、上記のmodule構造体に指定されているhelloworld_commandsによって定義されている。
static const command_rec helloworld_commands[] = { { "SetMessage", set_message, // コマンドを処理する関数 NULL, OR_ALL, TAKE1, // 一つのパラメータをとる "Specify message!" }, {NULL} };
set_message関数
set_message関数の定義は次のようになる
static const char* set_message(cmd_parms* parms, void* pthis, char* arg) { helloworld_per_dir_config* conf = (helloworld_per_dir_config*)pthis; conf->message = apr_pstrdup(parms->pool, arg; }
ここで利用しているhelloworld_per_dir_configは、上で定義したものである。
また、apr_pstrdupは、strdupと同等の処理を行うが、プールからのメモリ割り当てを行う。
helloworld_register_hooks関数
helloworld_register_hooks関数は、実際の処理を行う関数群(フック)を登録する関数である。実際の実装は次のようになる。
static void helloworld_register_hooks(apr_pool_t *p) { ap_hook_handler(helloworld_handler, NULL, NULL, APR_HOOK_MIDDLE); }
helloworld_handler関数
helloworld_register_hooks関数中で、APR_HOOK_MIDDLEに対して関連づけられているhelloworld_handler関数は、実際のリクエストに対するレスポンス出力処理を行う。実際の実装は次のようになる。
static int helloworld_handler(request_rec *r) { if (strcmp(r->handler, "helloworld")) { return DECLINED; } r->content_type = "text/html"; if (r->header_only) // HEAD処理の場合には本文は送らない return OK; // 設定の構造体を取得 helloworld_config* conf = (helloworld_config*)get_module_config(p_rec->per_dir_config, &helloworld); // 出力 apr_rprintf(r, "<h1>%s</h1>\n", conf->message); return OK; }