2014年6月26日木曜日

ハンドラ(handler) フック関数の例: default_handler

フック関数の一覧の続きだ。
フックポイントには多くの種類があって、どういったタイミングで呼ばれるのか、私にはよく分からないものも多い。
ハンドラフック関数は、ハンドラ処理のフック関数だ。
ハンドラは、ドキュメントには「ファイルが呼ばれたときに実行される動作の Apache における内部表現」とある。
参考: http://httpd.apache.org/docs/2.4/ja/handler.html

default_handler

ふつうApacheはHTMLファイルをリクエストされれば、ファイルシステム上のそのHTMLファイルを読み込んでレスポンスとして返す。
その処理を行うのが、default_handler ハンドラだ。

(1)フック関数の登録


フック関数 default_handler を登録する処理は次のようになっている

(httpd-2.4.9/server/core.c)


4885 ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST);

APR_HOOK_REALLY_LASTは、実行順序の最後を意味する。
(apr-util-1.5.3/include/apr_hooks.h より)
マクロ 備考
APR_HOOK_REALLY_FIRST -10 どれよりも先に実行したい
APR_HOOK_FIRST 0 先頭グループ
APR_HOOK_MIDDLE 10 中間グループ
APR_HOOK_LAST 20 最終グループ
APR_HOOK_REALLY_LAST 30 どれよりも後に実行したい

値が小さいものが先に実行される。
別のハンドラがハンドラフックポイントに登録されている場合、そのハンドラを先に実行してから、default_handlerは実行される。
ハンドラフック関数は、RUN_FIRSTタイプなので、フック関数の戻り値がDECLINE以外の場合に処理を終了する。
つまり、OKや、エラーステータスが返されると処理は終了する。
終了するということは、登録された次のフック関数は処理されないということだ。
要するに、default_handlerは他のハンドラで処理されなかったリクエストを処理するハンドラ、ということになる。

(2)ハンドラフック関数の処理

実行される処理は次の通り。

(httpd-2.4.9/server/core.c)

   4248 static int default_handler(request_rec *r)
   4249 {
   4250     conn_rec *c = r->connection;
   4251     apr_bucket_brigade *bb;
   4252     apr_bucket *e;
    :

   4277     if ((errstatus = ap_discard_request_body(r)) != OK) {
KeepAliveな接続の場合は、入力フィルタからbucket brigadeを吸い上げ、 Content-Length分のボディ部分を読み捨てるなどの処理を実行している
   4278         return errstatus;
   4279     }
    :

   4281     if (r->method_number == M_GET || r->method_number == M_POST) {
    :

   4330         if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY
   4331 #if APR_HAS_SENDFILE
   4332                             | AP_SENDFILE_ENABLED(d->enable_sendfile)
   4333 #endif
   4334                                     , 0, r->pool)) != APR_SUCCESS) {

r->filenameがリクエストされたファイルを指している オープンできない場合には、403 Forbiddenを返す 
   4335             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00132)
   4336                           "file permissions deny server access: %s", r->filename);
   4337             return HTTP_FORBIDDEN;
   4338         }
   4339
   4340         ap_update_mtime(r, r->finfo.mtime);

リソースの最終更新日時の情報(r->mtime)を更新している
   4341         ap_set_last_modified(r);

Last-Modifiedヘッダをレスポンス情報にセットしている。 値は、ap_update_mtime()で更新された値
   4342         ap_set_etag(r);

ETagヘッダをレスポンス情報にセットしている。
   4343         ap_set_accept_ranges(r);

Accept-Rangesヘッダをレスポンス情報にセットしている。 MaxRangesディレクティブの指定が"none"の場合は、"none"。それ以外は、"bytes"
   4344         ap_set_content_length(r, r->finfo.size);

Content-Lengthヘッダをレスポンス情報にセットしている。 値は、r->finfo.size。
   4345         if (bld_content_md5) {
   4346             apr_table_setn(r->headers_out, "Content-MD5",
   4347                            ap_md5digest(r->pool, fd));

ContentDigestディレクティブに指定に応じて、 Content-MD5ヘッダをレスポンス情報にセットしている
   4348         }
   4349
   4350         bb = apr_brigade_create(r->pool, c->bucket_alloc);

ここでbucket brigade bbを作成している
   4351
   4352         if ((errstatus = ap_meets_conditions(r)) != OK) {
   4353             apr_file_close(fd);
   4354             r->status = errstatus;
   4355         }
   4356         else {
   4357             e = apr_brigade_insert_file(bb, fd, 0, r->finfo.size, r->pool);

ここで、ターゲットのファイルのファイルbucket を作成し bucket brigade bbに挿入している
   4358
   4359 #if APR_HAS_MMAP
   4360             if (d->enable_mmap == ENABLE_MMAP_OFF) {
   4361                 (void)apr_bucket_file_enable_mmap(e, 0);
   4362             }
   4363 #endif
   4364         }
   4365
   4366         e = apr_bucket_eos_create(c->bucket_alloc);
   4367         APR_BRIGADE_INSERT_TAIL(bb, e);

EOSメタデータbucketを作成し、 bucket brigade bb に追加した。 ここまでで、bb には ファイルbucketとEOSバケットが格納されている
   4368
   4369         status = ap_pass_brigade(r->output_filters, bb);

ここで、bucket brigade bb を出力フィルタに引き渡す。
   4370         if (status == APR_SUCCESS
   4371             || r->status != HTTP_OK
   4372             || c->aborted) {
   4373             return OK;
   4374         }
   4375         else {
   4376             /* no way to know what type of error occurred */
   4377             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00133)
   4378                           "default_handler: ap_pass_brigade returned %i",
   4379                           status);
   4380             return HTTP_INTERNAL_SERVER_ERROR;
   4381         }
    :

   4406 }

ひとまず、もう一つくらい、handlerの例を書く。

2014年6月16日月曜日

フック関数の一覧

続いて、フック関数の種類について書く。

フックポイントの一覧

Apache httpdで用意されているフックポイントにはどんなものがあるか。
あるフック関数の実行関数 ap_run_hogehoge()の実行箇所は一か所とは限らず、また必ず実行されるわけでもないので、どういった条件で実行されるかを明確にするのは難しいケースがある。

ひとまずは、httpd-2.4.9でAP_IMPLEMENT_HOOK_RUN_ALL/RUN_FIRST/VOID で定義されているフックポイントの一覧を作成した。
比較として、httpd-2.2.27からも同じマクロで定義されているフックポイントを並べている。
減ったフックポイントは見当たらない。追加されたポイントが、17あった。
分類は、下記で説明するmod_infoでの分類を書いた。が、もう少し細かく分類してもいいと思う。

seq フックポイント 処理タイプ フックポイント(2.2.27) 分類(mod_info)
1 access_checker RUN_ALL access_checker リクエスト処理系
2 access_checker_ex RUN_FIRST
リクエスト処理系
3 auth_checker RUN_FIRST auth_checker リクエスト処理系
4 check_config RUN_ALL
起動処理系
5 check_user_id RUN_FIRST check_user_id リクエスト処理系
6 child_init VOID child_init 起動処理系
7 child_status VOID
その他
8 create_connection RUN_FIRST create_connection リクエスト処理系
9 create_request RUN_ALL create_request リクエスト処理系
10 default_port RUN_FIRST default_port リクエスト処理系
11 dirwalk_stat RUN_FIRST
(なし)
12 drop_privileges RUN_ALL
起動処理系
13 end_generation VOID
その他
14 error_log VOID error_log その他
15 expr_lookup RUN_FIRST
その他
16 fatal_exception RUN_ALL fatal_exception その他
17 fixups RUN_ALL fixups リクエスト処理系
18 generate_log_id RUN_FIRST
リクエスト処理系
19 get_mgmt_items RUN_FIRST get_mgmt_items その他
20 get_suexec_identity RUN_FIRST get_suexec_identity (なし)
21 handler RUN_FIRST handler リクエスト処理系
22 header_parser RUN_ALL header_parser リクエスト処理系
23 http_scheme RUN_FIRST http_scheme, リクエスト処理系
24 insert_error_filter VOID insert_error_filter リクエスト処理系
25 insert_filter VOID insert_filter リクエスト処理系
26 insert_network_bucket RUN_FIRST
(なし)
27 log_transaction RUN_ALL log_transaction リクエスト処理系
28 map_to_storage RUN_FIRST map_to_storage リクエスト処理系
29 monitor RUN_ALL monitor その他
30 mpm RUN_FIRST
起動処理系
31 mpm_get_name RUN_FIRST
その他
32 mpm_query RUN_FIRST
その他
33 mpm_register_timed_callback RUN_FIRST
その他
34 note_auth_failure RUN_FIRST
リクエスト処理系
35 open_htaccess RUN_FIRST
(なし)
36 open_logs RUN_ALL open_logs 起動処理系
37 optional_fn_retrieve VOID optional_fn_retrieve 起動処理系
38 post_config RUN_ALL post_config 起動処理系
39 post_perdir_config RUN_ALL
(なし)
40 post_read_request RUN_ALL post_read_request リクエスト処理系
41 pre_config RUN_ALL pre_config 起動処理系
42 pre_connection RUN_ALL pre_connection リクエスト処理系
43 pre_mpm RUN_ALL pre_mpm 起動処理系
44 pre_read_request VOID
リクエスト処理系
45 process_connection RUN_FIRST process_connection リクエスト処理系
46 quick_handler RUN_FIRST quick_handler リクエスト処理系
47 test_config VOID test_config 起動処理系
48 translate_name RUN_FIRST translate_name リクエスト処理系
49 type_checker RUN_FIRST type_checker リクエスト処理系

mod_infoで見るフック関数情報

mod_infoで各フックポイントに登録されているフック関数の情報を一覧することができる。
http://localhost/server-info?hooks
ただ、調べてみると、出力されるフックポイントは一部欠けている(上の表で「(なし)」と書いたのが欠けているフックポイント)。
関数も関数名が一覧されるわけではなく、その関数を登録したファイル名が出力されている。

処理を見ると、ap_hook_get_hogehoge()でhogehogeフックポイントの登録情報(apr_array_header_t情報)を取得する。
apr_array_header_t情報は、配列情報の管理情報で、内部にメモリプールの情報なども持っているが、基本的には、apr_array_header_t.nelts 個の有効な要素を持つ配列 apr_array_header_t.elts の格納庫だ。
このapr_array_header_t構造体自体は汎用で フック関数の登録先に利用する場合にはapr_array_header_t.eltsには、各フック関数用の構造体 ap_LINK_hogehoge_t が格納される。

mod_infoで出力されるのは、ap_LINK_hogehoge_t.szNameで、ここにはモジュールのCファイル名が入っている。
各モジュールごとにフック関数の型が異なるので、この構造体の個々の定義は異なっているが、その辺気にしなければ(実際に関数をを呼び出すのでなければ)、ダミーの構造体で処理可能らしい。mod_infoでは、処理用の構造体を用意してこのモジュールCファイル名と、実行時の順序を制御するための値 ap_LINK_hogehoge_t.nOrder を出力している。



引き続き、主なフック関数を確認すれば、フック関数の全体のイメージが把握できるのではないか。

2014年6月9日月曜日

フック関数の概要

フック関数について書く
フック関数の処理はマクロ化されているので、まず、そのあたりをまとめる。
概要は、2.4系も2.2系も異ならないのではないか。


http://httpd.apache.org/docs/2.4/en/developer/hooks.html

Apache httpdのライフサイクルには、起動時/再起動時の設定ファイルの読込みから、リクエスト処理のための子プロセス/スレッドの生成、接続の受付け、リクエストの受付け、内容を解析し、処理し、レスポンスを返すという、リクエスト処理のサイクルを通して、多数のフックポイントが用意されている。
例えば、hogehogeというフックポイントがあって、そこでフック関数を呼び出すために行われる処理は、 ap_run_hogehoge() という名前の関数だ。
このap_run_hogehoge()関数は、hogehogeフック関数の登録リストに登録されたフック関数を順に実行する。
このフック関数実行関数ap_run_hogehoge()には、登録されたフック関数をすべて無条件に実行するものと、いずれかの関数が所定の戻り値を返すと処理を終了するものが存在する。
そして、ap_run_hogehoge() で実行されるフック関数を登録するのが、ap_hook_hogehoge()という名前の関数だ。

ソースコードをgrepなどで検索すると、ap_run_hogehogeやap_hook_hogehogeという関数が使われる個所は多数見つかるが、実際にその関数が定義されている個所はない。これは、ap_run_hogehogeとap_hook_hogehogeがマクロで定義されているためだ。

と書きながら、実際にgrepしてみたところ、 post_configフック関数が検索で見つかる(server/config.c)。
マクロを展開したコードがコメントとして残されているようだ。

2014年6月2日月曜日

入力フィルタの概要

入力フィルタについて書くが、出力フィルタの概要の記述を前提とする。

出力フィルタは、メイン処理が送り出したデータを加工、処理し、ネットワークへの出力までを行った。
入力フィルタは、ネットワークから受け取ったデータを加工、処理してメイン処理に引き渡す。
データの流れる方向は、当たり前だが出力フィルタとは逆向きになる。
出力フィルタは、ap_pass_brigade()関数によって、bucket brigadeをフィルタの階層の下流に向かって送り出していた。
入力フィルタもまた、階層をなしている。
メイン処理は、入力フィルタの階層を通して、bucket brigadeを吸い上げる処理を行う。
吸い上げると言うのは、相変わらず下にネットワークがあって、メイン処理が上にあるというイメージからの表現だ。
各入力フィルタは、下流のフィルタから吸い上げた bucket brigadeを処理し、それを上流のフィルタに引き渡している。