フックから Apache の全体像を追う
DSAS開発者の部屋:[補足記事]Apache 2.0 の hook 一覧(apache module 開発事初め その3-3) はモジュールを書く際にどこにフックをしかけるかという点で非常に参考になります。
が,いまだにいまいち Apache からどのように呼び出されるか実感がわきません。ひょっとしてフックの呼び出し方を調べると Apache の処理フローを追っていけるんじゃないか,という無謀な挑戦をしてみました。
対象
なお,以下に記したソースは,実際のソースの引用ではなく,おもにフックを呼び出している部分のみの抜粋となります。またロジックを損なわない程度に書き換えた部分もあります。さらには引数や戻り値についてもほぼ無視しています。つまり字面としてまったく違う(準擬似)コードになってしまったのですが,流れを追う上でかえってすっきりしたのではないかと思います。
フックの種類について(読み飛ばし可)
VOID
タイプのフック
フックに登録されている callback がすべて呼び出されます。そもそもこのタイプのフックの callback は戻り値型が void
なのでステータスを返しようがないのですが。
たとえば ap_hook_child_init
などのように,(原則エラーの発生しない)各モジュールの初期化を行うような場合に使われます。
RUN_FIRST
タイプのフック
サーバはフックを順番に呼び出していきますが,基本的に DECLINE
以外のステータス(=成功 or エラー)を返した場合にそこで呼び出しチェーンがストップするタイプのフックです。成功の場合もそれ以降のフックが呼ばれないところが特殊です。
つまり。Web サーバが「何かを知りたい」「何かをやってほしい」ときに,誰か一人がやってくれればそれでよい場合に使われるフックです。
たとえば,
誰か蕎麦作って!
- ピザ屋
- いや俺無理 (DECLINED)
- 中華料理屋
- わたしも無理アルよ (DECLINED)
- そば屋
- できますよー (OK)
- うどん屋
- (聞かれてないし)
- ラーメン屋
- (聞かれてないし)
のような感じです。
みなさんお馴染みの ap_hook_handler
フックのように,誰か一人がコンテンツを返してくれればよい場合に使われるフックです。逆に全モジュールが返してきたらおかしいですよね。
実際に自分が処理するわけではないんだけど,その時点にフックを仕込みたい,という場合,フック順序に APR_HOOK_FIRST
(より極端には APR_HOOK_REALLY_FIRST
)を指定してフックする必要があります。またその場合 DECLINED
を返すべきです。
「基本的に」と書きましたが,DECLINED
な値タイプはフックの仕様設計者が任意に定めることができます。たとえば ap_hook_create_connection
フックの場合,callback の戻り値型は conn_rec *
型であり,declined は NULL
になっています。いずれかのモジュールが NULL
以外の conn_rec *
型の値を返したらオッケー,ということになります。
RUN_ALL
タイプのフック
サーバはフックを順番に呼び出していきますが,基本的に OK
や DECLINED
以外のステータス(=エラー)を返した場合にそこで呼び出しチェーンがストップするタイプのフックです。
たとえば ap_hook_pre_config
のように,各モジュールの設定を行うけれども設定にエラーがある場合は httpd の起動を阻止したい,などの場合に使われるフックです。エラーがない限り全モジュールに対して呼び出す必要があるので RUN_FIRST
ではだめですよね。
無理にたとえると
みんな自分の店掃除しといてね by フロアマネージャ
- ピザ屋
- 掃除したよ (OK)
- 中華料理屋
- 本日休業アル (DECLINED)
- そば屋
- 掃除したよ (OK)
- うどん屋
- すいませんゴキブリがでました (ERROR)
……レストランフロア営業停止……
みたいな感じでしょうか。
「基本的に」と書きましたが,RUN_FIRST
と同様,OK
や DECLINED
とする値は仕様設計者が任意に定めることができます。とはいうものの,Apache で使われる RUN_ALL
なフックは,ほぼすべて int
型の定数値 OK
,DECLINED
になっています。
凡例
いままで ap_hook_child_init
のようにフックについて書いてきましたが,これはフックを登録する関数の名称です。
そこで以降では,ap_HOOK_child_init
のように hook
を大文字にすることで,フックそれ自身(の型)を示すことにしました。
また,フック群を呼び出す場合には ap_run_child_init()
のような関数を呼ぶことになるのですが,他の関数との違いが少なく目立ちづらいので,下記では ap_RUN_child_init()
のようにあえて run
の部分を大文字にしてあります。実際のソースを読むときには気をつけてください。
まずはメインルーチン(server/main.c
)
/* server/main.c:: */ main() { ap_RUN_pre_config(); // RUN_ALL if (configtestonly) ap_RUN_test_config(); // VOID ap_RUN_open_logs(); // RUN_ALL ap_RUN_post_config(); // RUN_ALL for (;;) { ap_RUN_pre_config(); // RUN_ALL ap_RUN_open_logs(); // RUN_ALL ap_RUN_post_config(); // RUN_ALL ap_RUN_optional_fn_retrieve(); // VOID if (ap_mpm_run()) break; } }
設定やログまわりのフックが呼び出されています。これらのフックはマスタプロセスにおいてのみ呼び出されています。各フックの詳細や使用例についてここでは説明しません。ap_run_pre_config()
等が2回行われている理由は mod_perl 2.0 の Server Life Cycle - daily dayflower あたりを参照してください。
最終的に各 MPM の ap_mpm_run()
が呼び出されます。エラーが発生したり shutdown 指示がだされたりした場合に ap_mpm_run()
が 0 以外で戻ってきますので,結果的に while
ループを抜け main()
が終了します。restart などの際には ap_mpm_run()
が 0 を返すので,再度 while
ループが実行されます(よって再度セッティング処理が行われ ap_run_pre_config()
等が呼び出される)。
各 MPM における ap_mpm_run()
(Prefork の場合)
Prefork MPM での ap_mpm_run()
の実装をみてみます。
/* server/mpm/prefork/prefork.c:: */ ap_mpm_run() { ap_RUN_pre_mpm(); // RUN_ALL startup_children(); while (! restart && ! shutdown) { ap_wait_or_timeout(); ap_process_child_status(); if (needed) { make_child(); } perform_idle_server_maintenance(); } } startup_children() { for (childrens) { make_child(); } } perform_idle_server_maintenance() { if (too_many_free_children) { kill_child; } if (needed) { make_child(); } }
全体的な流れは,
- まず
startup_children()
で子プロセスを用意し - restart や shutdown しない限り以下を繰り返し行う
ap_wait_or_timeout()
(後述) で子プロセス終了を待つ- 終了した子プロセスのマネージメントを行い(
ap_process_child_status()
) - 必要に応じて子プロセスを作成し(
make_child()
) - 暇なら
perform_idle_server_maintenance()
で子プロセス数を調整する
となります。Prefork の挙動をご存知の方なら「ああなるほど」と思われるのではないでしょうか。
ということで make_child()
が子プロセス生成に関わっており重要っぽいので引き続き読み進めていきます。
/* server/mpm/prefork/prefork.c:: */ make_child() { if (fork() == 0) { /* I am child */ child_main(); } } child_main() { ap_RUN_child_init(p, s); // VOID while (! die_now) { listener->accept_func(); ap_RUN_create_connection(p, s, ...); // RUN_FIRST ap_process_connection(c, ...); } }
fork()
して自身が子プロセスの場合 child_main()
に移行します。
child_main()
ではまず ap_run_child_init()
で ap_HOOK_child_init
フックを呼び出します。各子プロセス生成後に一度だけ呼び出されるフックであり,数多くのモジュールで内部変数の初期化などに使われています。
親プロセスから「終了してね」といわれない限り
accept()
してap_HOOK_create_connection
フックを呼び出しap_process_connection()
でコネクションを使った処理を行う
ということを繰り返します。
なお,server/core.c
において ap_HOOK_create_connection
フックとして core_create_conn()
という関数を登録しています。他のモジュールによってフック・処理されていない限り,ここで conn_rec
構造体のセットアップをすることになります。
余談: accept()
はいいとして listen()
はいつ呼び出されているの?
Prefork MPM の場合,ap_HOOK_open_logs
フックにおいて ap_setup_listeners()
を呼び出しています(名前からすると,なぜそこで,と直感に反しますが)。つまりマスタープロセスにおいてのみリスナをセットアップしています。ap_setup_listener()
の実装じたいは server/listen.c
に存在します。この文書では触れません。
各コネクションに対する処理(server/connection.c
)
さきほどの処理ループの最後にでてきた ap_process_connection()
という関数は server/connection.c
にあります。
/* server/connection.c:: */ ap_process_connection(c, ...) { ap_RUN_pre_connection(c, ...); // RUN_ALL ap_RUN_process_connection(c); // RUN_FIRST }
ap_HOOK_pre_connection
フックを呼び出しap_HOOK_process_connection
フックを呼び出す
という単純な関数です。
ap_HOOK_process_connection
フックは RUN_FIRST
フックですので,「誰かこの connection を処理してお願い」ということになるかと思います。トップダウンに読み込んでくると,ここで次にどこにいけばいいのかわからなくなります。
実は modules/http/http_core.c
において
ap_hook_process_connection(ap_process_http_connection, NULL, NULL, APR_HOOK_REALLY_LAST);
のように ap_HOOK_process_connection
フックを登録しています。よって他のモジュールによってフック・処理されていない限り,ap_process_http_connection()
でコネクションごとの処理が行われることになります。
HTTP コネクションの処理(modules/http/http_core.c
)
では ap_process_http_connection()
の実装をみてみましょう。
/* modules/http/http_core.c:: */ ap_process_http_connection(c) { while (ap_read_request(c)) { if (HTTP_OK) ap_process_request(r); } }
ということをリクエストが続く限り行います。
いうなれば,この関数で単一コネクションから各リクエストを分離していることになります。もちろん旧来の HTTP であれば 1 コネクションにつき 1 リクエストなのですが,Keep-Alive の場合 1 コネクションに複数のリクエストが到達するのでこのようになっています。
まずはリクエストを読む(server/protocol.c
)
まずは ap_read_request()
のほうからみていきます。
/* server/protocol.c:: */ ap_read_request(c) { ap_RUN_create_request(r); // RUN_ALL try { /* 実際にリクエストを読み込んだり */ ap_RUN_post_read_request(r); // RUN_ALL } catch (any_error) { ap_RUN_log_transaction(r); // RUN_ALL } }
最初に ap_HOOK_create_request
フックが呼び出されます。リクエストごとに一番最初に呼び出されるのでリクエストごとにモジュールのステートを(request_rec->notes
などを使って)初期化したい場合,このフックをもちいるとよいでしょう。
次にリクエストを実際に読んでから ap_HOOK_post_read_request
フックを呼び出します。
もしこれらの一連の流れでエラーが発生した場合,ap_HOOK_log_transaction
フックを呼び出して上位にエラーとして戻ります。つまり,一旦リクエストの処理が始まれば,エラーがおきようと最後に必ず ap_HOOK_log_transaction
フックが呼び出されることになります。リクエストごとのファイナライズ処理を行いたい場合(名前と裏腹ですが)このフックを利用することができます。
おっと,もしこれらのシーケンスが成功裏に終わった場合 ap_HOOK_log_transaction
が呼び出されない感じがします。が,その場合でもちゃんと呼び出されます(後述)。
次にリクエストを処理する(modules/http/http_request.c
)
ap_read_request()
でリクエストを読んだ後(そして成功した場合)は,ap_process_request()
でリクエストを処理します。処理しますというより,HTTP リクエスト処理の全体的なフレームワークなのですが。
/* modules/http/http_request.c:: */ ap_process_request(r) { ap_RUN_quick_handler(r, ...); // RUN_FIRST if (DECLINED) { ap_process_request_internal(r); if (OK) ap_invoke_handler(r); } if (OK) { ap_finalize_request_protocol(r); } else { ap_die(status, r); } ap_RUN_log_transaction(r); // RUN_ALL }
まずは ap_HOOK_quick_handler
フックを呼び出します。いずれかのモジュールがこのフックで処理を行った場合は,即終了となります。
ap_HOOK_quick_handlder
フックが何の処理も行わなかった場合,ap_process_request_internal()
関数でリクエストの処理(リクエストの加工やアクセス制限やハンドラの決定など)を行います。ここがうまくいった場合,ap_invoke_handler()
でほんとのほんとに実際の処理を行います。
処理するのが quick だろうが internal process だろうが,最終的にはさきほど述べた ap_HOOK_log_transaction
フックをキッチリ呼び出します。
2008-11-05 追記: ap_finalize_request_protocol()
等を追加しました。あんまり重要じゃなかったですけど,自分用。
余談: ap_HOOK_quick_handler
フックについて
文字通り quick にレスポンスを返すことのできる場合に使うべきフックです。たとえば DB サーバに強く依存しているウェブアプリサーバの場合,DB サーバが落ちていたときにすぐさま Status 500 系エラーを返す,などのモジュールが考えられます*2。このようなモジュールを作ると Apache 自体の負荷を軽くすることができます。
ただし,後述するアクセス制限(AAA)などが行われないことに注意してください。実際,付属モジュールで唯一 ap_HOOK_quick_handler
を利用している [http://httpd.apache.org/docs/2.2/mod/mod_cache.html:title=mod_cache]
のドキュメントには以下のような注意書きがあります。
使用方法については注意する必要があり、 Allow や Deny ディレクティブを迂回する設定もできてしまいます。 ホスト名やアドレスや環境変数に基づいてクライアントからの アクセスを制限したい場合は、キャッシュ機能を有効にすべきでは ありません。
mod_cache - Apache HTTP Server Version 2.2
quick じゃないリクエスト処理の端緒(server/request.c
)
リクエスト処理で一番大掛かりな関数である ap_process_request_internal()
を見ていきましょう。対象リソースの決定やアクセス制限など,多くの処理が詰まっています。
/* server/request.c:: */ ap_process_request_internal(r) { if (! file_req) { ap_RUN_translate_name(r); // RUN_FIRST } ap_RUN_map_to_storage(r); // RUN_FIRST if (i_am_main_request) { ap_RUN_header_parser(r); // RUN_ALL } if (not_authed_yet_in_any_ancestor_requests) { if (Satisfy in [ ALL, NOSPEC ]) { ap_RUN_access_checker(r); // RUN_ALL if (some_auth_required) { ap_RUN_check_user_id(r); // RUN_FIRST ap_RUN_auth_checker(r); // RUN_FIRST } } else if (Satisfy == ANY) { ap_RUN_access_checker(r); // RUN_ALL if (FAILED) { if (some_auth_required) { ap_RUN_check_user_id(r); // RUN_FIRST ap_RUN_auth_checker(r); // RUN_FIRST } } } } ap_RUN_type_checker(r); // RUN_FIRST ap_RUN_fixups(r); // RUN_ALL }
まず
ap_HOOK_translate_name
ap_HOOK_map_to_storage
ap_HOOK_header_parser
を呼び出しています。
ap_HOOK_translate_name
フックは,URI からリソースのファイル名を決定するフックで,たとえば [http://httpd.apache.org/docs/2.2/mod/mod_userdir.html:title=mod_userdir]
で用いられています([]http://example.com/~foo/bar.html[]
→ /home/foo/public_html/bar.html
)。つまり request_rec->filename
を設定するフックです。
ap_HOOK_map_to_storage
フックはあまり利用されていません。core
モジュールではリソースの実在(たとえばさきほど設定した request_rec->filename
)を確認したりしています(存在しない場合にエラーを返すなど)。
ap_HOOK_header_parser
フックはリクエストヘッダを使った処理を行いたい場合に使うフックです。といっても [http://httpd.apache.org/docs/2.2/mod/mod_setenvif.html:title=mod_setenvif]
くらいでしか使われていません。もちろんこのステージじゃなくてもヘッダ情報を取得できるのでここに仕掛ける必要はないのかもしれませんが,逆に「ここのステージに至った時点でリクエストヘッダを利用できるよ」と考えればよいでしょう。
アクセス制限処理については次のセクションで説明します。
アクセス制限処理(AAA; 認証,承認,アクセス制御 - Authentication and Authorization - Apache HTTP Server Version 2.2)
アクセス制限処理では以下の3つのフックを使用しています。
ap_HOOK_access_checker
ap_HOOK_check_user_id
ap_HOOK_auth_checker
ap_HOOK_access_checker
フックは,アクセス制御(Access Control)処理を行うフックです。アクセス元(ドメインや IP アドレス)に基づく制御([http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#allow:title=Allow]
,[http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#deny:title=Deny]
)が一番有名なアクセス制御です。
ap_HOOK_check_user_id
フックは,認証(Authentication)処理を行うフックです。「認証」とは「あなたは誰?」ということを決定することです。「あなたは誰だかわからないからアクセスさせない」という意味でアクセス制限がおこなわれる可能性もあります。たとえば [http://httpd.apache.org/docs/2.2/mod/mod_auth_basic.html:title=mod_auth_basic]
がこのフックを利用しています。
ap_HOOK_auth_checker
フックは,承認(Authorization)処理を行うフックです。「承認」とは「あなたは誰それだからアクセスしてよい/よくない」ということを決定することです。たとえば [http://httpd.apache.org/docs/2.2/mod/mod_authz_user.html:title=mod_authz_user]
によってアクセス可能なユーザや valid-user
を指定することができます。またファイルの owner に基づいた制限を行う [http://httpd.apache.org/docs/2.2/mod/mod_authz_owner.html:title=mod_authz_owner]
もこのフェーズでフックを行っています。
Satisfy ディレクティブに対応するため,フックの呼び出しがちょっとややこしくなっています。この部分を再掲します(ロジックをなるべく保ったまま元のコードから書き換えてあります)。
if (Satisfy in [ ALL, NOSPEC ]) { ap_RUN_access_checker(r); // RUN_ALL if (some_auth_required) { ap_RUN_check_user_id(r); // RUN_FIRST ap_RUN_auth_checker(r); // RUN_FIRST } } else if (Satisfy == ANY) { ap_RUN_access_checker(r); // RUN_ALL if (FAILED) { if (some_auth_required) { ap_RUN_check_user_id(r); // RUN_FIRST ap_RUN_auth_checker(r); // RUN_FIRST } } }
Satisfy が All
か指定されていない場合,AAA の3つのフックが呼び出されます。ただし失敗した時点で呼び出し元に帰るので,たとえば ap_RUN_access_checker()
が「拒絶」した場合 ap_RUN_check_user_id()
以降は呼び出されません。Satisfy が Any
の場合は,ap_RUN_access_checker()
が成功した場合にはそのまま次のステートに以降しますが,失敗した場合に ap_RUN_check_user_id(r)
等を実行してアクセス可否を決定します。
最後に私感ですが。認証と承認という2つのフェーズに分割されているため結果的にごちゃごちゃした構成になっているように感じます。さらに [http://httpd.apache.org/docs/2.2/mod/mod_auth_basic.html:title=mod_auth_basic]
の場合,[http://httpd.apache.org/docs/2.2/mod/mod_auth_basic.html#authbasicprovider:title=AuthBasicProvider]
ディレクティブによって認証 DB バックエンドに処理が流れるため,処理を追うのが大変です。もっとシンプルな枠組みだったらよかったのにと思います。
2008-11-05 追記: https://www.codeblog.org/blog/inoue/20060112.html あたりを参照するとそうそう枠組みを変えられないかなぁとも思いました。
リクエストの最終準備
アクセス制限処理が無事おわった場合(アクセス許可の場合),以下の2つのフックが呼び出されます。
ap_HOOK_type_checker
ap_HOOK_fixups
ap_HOOK_type_checker
フックは,リクエストの内容やリソースの種別に応じてレスポンスを変えたい場合に用います。たとえば [http://httpd.apache.org/docs/2.2/mod/mod_mime.html:title=mod_mime]
はコンテンツの種別に応じて Content-Type
レスポンスヘッダを調整しますし,[http://httpd.apache.org/docs/2.2/mod/mod_negotiation.html:title=mod_negotiation]
はリクエストヘッダで指定された言語に応じて返すべきコンテンツを決定します(コンテントネゴシエーション)。この2つの例はまったく異なる方向性のモジュールですが,このことからわかるようにだいたい「リクエストやリソースの種類」にまつわることならなんでもいいみたいです(そのわりに RUN_FIRST
なんだよなぁ)。
ap_HOOK_fixups
フックは,まさにリクエスト構造体の最終調整を行うフェーズです。[http://httpd.apache.org/docs/2.2/mod/mod_dir.html:title=mod_dir]
((URL の末尾の /
が欠損したディレクトリへのリクエストの場合,/
をつけた URL にリダイレクトする。)) や [http://httpd.apache.org/docs/2.2/mod/mod_speling.html:title=mod_spelling]
*3 などはいかにもここでやるのにふさわしい作業ですが,リクエスト準備の最終段階で呼び出されることから,それ以外にも数多くの標準モジュールで利用されています。
セクション冒頭でも書きましたが,これらのフックはアクセス制限フェーズを無事通過しないと呼ばれません。つまりファイナライズのためにこれらのフックを利用することはできないということです((前述しましたが ap_HOOK_log_transaction
フックを利用します。))。
コンテンツハンドラを呼び出す(server/config.c
)
いよいよコンテンツに応じたハンドラを呼び出すフェーズです。
/* server/config.c:: */ ap_invoke_handler() { ap_RUN_insert_filter(r); // VOID ap_invoke_filter_init(input_filters); ap_invoke_filter_init(output_filters); ap_RUN_handler(r); // RUN_FIRST }
まず ap_HOOK_insert_filter
フックを呼び出し,そののちに input_filter
と output_filter
の初期化を行います。リクエストに応じたカスタムフィルタを挿入するにはわりと適したフックのように感じますが,一般的にフィルタは [http://httpd.apache.org/docs/2.2/mod/core.html#setinputfilter:title=SetInputFilter]
や [http://httpd.apache.org/docs/2.2/mod/core.html#setoutputfilter:title=SetOutputFilter]
ディレクティブによって明示的に組み込まれることと,モジュール内部でプライベートフィルタを挿入する場合でもより以前のフェーズで組み込まれることが多いことから,このフックはあまり使われていません。
ap_HOOK_handler
フックについてはもはや説明の必要はないでしょう。モジュールを作ったことのある方なら,ほぼ間違いなく ap_hook_handler()
でハンドラを登録したことがあるはずです。
落ち穂拾い
ここまでのステージに出現しなかったフックについて説明します。
/* modules/http/http_protocol.c:: */ ap_send_error_response() { ap_RUN_insert_error_filter(r); // VOID /* エラー用コンテンツの生成と送信 */ }
ap_send_error_response()
関数はエラーが発生した時にエラー用コンテンツ(404 などみなさんお馴染みのあの画面)を生成するために Apache が呼び出す関数です。ap_HOOK_insert_error_filter
フックはこの最初の段階で呼び出されます。
ap_HOOK_insert_filter
フックの内部エラー画面版という位置づけであり,あまり使われていません。[http://httpd.apache.org/docs/2.2/mod/mod_headers.html:title=mod_headers]
や [http://httpd.apache.org/docs/2.2/mod/mod_expires.html:title=mod_expires]
で使われていますが,ap_hook_insert_filter()
するからおまけにこいつもやっとくか,みたいな感じです。
/* server/log.c:: */ log_error_core() { /* ... */ ap_RUN_error_log(...); // VOID }
ap_HOOK_error_log
フックはロギング処理共通関数において最後に呼び出されます。つまりすでに stderr や syslog やログファイルに出力された後に呼び出されるので,ログ出力内容を加工するなどの用途に用いることはできません。
/* server/mpm_common.c:: */ sig_coredump() { #if AP_ENABLE_EXCEPTION_HOOK run_fatal_exception_hook(); #endif } run_fatal_exception_hook() { ap_RUN_fatal_exception(...); // RUN_ALL }
ap_HOOK_fatal_exception
フックは coredump を吐くような致命的エラーが発生した場合に呼び出されます。が,--enable-exception-hook
で configure
してビルドした場合のみ利用されるようです。まぁデバッグ時の最終手段ですよねー。
/* server/mpm_common.c:: */ ap_wait_or_timeout() { if (some_interval) { ap_RUN_monitor(p); // RUN_ALL } /* タイムアウトつき子プロセス終了まち */ }
ap_wait_or_timeout()
関数は ap_mpm_run()
関数の while
ループで呼び出されていました。そのコードと上記コードを読むとおわかりのとおり,ap_HOOK_monitor
フックはマスタープロセスにおいて,あるていどの時間間隔で呼び出されるフックです。リソース監視などにどうぞ。
/* include/httpd.h:: */ #define ap_default_port(r) ap_run_default_port(r) #define ap_http_scheme(r) ap_run_http_scheme(r)
ap_HOOK_default_port
フックと ap_HOOK_http_scheme
*4 フックは,上記のように #define
になってます。つまりサーバのライフサイクルのどこかのフェーズで呼び出されるというより,コード内で ap_default_port()
を呼び出すたびにフックが走って default port を算出する,というイメージです。
フックのまとめ
順を追って説明してきたので見通しが悪くなりました。ハンドラに絞って擬似コードでまとめておきます。
main() { ap_RUN_pre_config(); // RUN_ALL ap_RUN_test_config(); // VOID ap_RUN_open_logs(); // RUN_ALL ap_RUN_post_config(); // RUN_ALL for (;;) { ap_RUN_pre_config(); // RUN_ALL ap_RUN_open_logs(); // RUN_ALL ap_RUN_post_config(); // RUN_ALL ap_RUN_optional_fn_retrieve(); // VOID ap_mpm_run() { startup_children() { make_child(child_main); } ap_RUN_pre_mpm(); ap_wait_or_timeout() { ap_RUN_monitor(); // RUN_ALL } make_child(child_main); } } } child_main() { ap_RUN_child_init(); // VOID ap_RUN_create_connection(); // RUN_FIRST ap_process_connection() { ap_RUN_pre_connection(); // RUN_ALL ap_RUN_process_connection(); // RUN_FIRST { ap_process_http_connection() { ap_read_request() { ap_RUN_create_request(); // RUN_ALL ap_RUN_post_read_request(); // RUN_ALL } ap_process_request() { ap_RUN_quick_handler(); // RUN_FIRST ap_process_request_internal() { ap_RUN_translate_name(); // RUN_FIRST ap_RUN_map_to_storage(); // RUN_FIRST ap_RUN_header_parser(); // RUN_ALL ap_RUN_access_checker(); // RUN_ALL ap_RUN_check_user_id(); // RUN_FIRST ap_RUN_auth_checker(); // RUN_FIRST ap_RUN_type_checker(); // RUN_FIRST ap_RUN_fixups(); // RUN_ALL } ap_invoke_handler() { ap_RUN_insert_filter(); // VOID ap_RUN_handler(); // RUN_FIRST } } ap_RUN_log_transaction(); // RUN_ALL } } } }