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の例を書く。

0 件のコメント:

コメントを投稿