はじめに
前回の記事:Apacheのmod_proxyのコードを読んでみる(2)
前回はmod_proxyの概要についての記事を書きました。
今回はmod_proxyの実装の詳細として、各handler関数(register_hooksで登録される各hook関数の第一引数)の理解内容を記事にします。
呼び出されるフェーズ順に説明を記載します。
これまでと同様に、我流で読み進めているので、理解内容に間違いなどありましたらご指摘ください。
リクエスト実行の前処理のhandler
child_init
ap_hook_child_initのhandlerです。
構造体メンバの初期化ぐらいしかしていません。
proxy_detect
ap_hook_post_read_requestのhandlerです。
URIを見てproxy対象かを判断します。
- virtual hostへのアクセス
- HTTPメソッドがCONNECTの場合
上記の場合にrequest_rec構造体に値を設定します。
r->proxyreq = PROXYREQ_PROXY;
r->uri = r->unparsed_uri;
r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server";
handlerやfilenameの値は、handler呼び出し時に処理対象か判定するためにも使われます。
proxy_pre_config
ap_hook_pre_configのhandlerです。
ここでmod_proxy_balancerで使われるworkerのカウントをリセットしています。
proxy_post_config
ap_hook_post_configのhandlerです。
SSLなどの設定内容を変数に格納しています。
proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
proxy_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
proxy_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
ap_proxy_strmatch_path = apr_strmatch_precompile(pconf, "path=", 0);
ap_proxy_strmatch_domain = apr_strmatch_precompile(pconf, "domain=", 0);
proxy_trans
ap_hook_translage_nameのhandlerです。
条件を満たした場合にfilenameに”proxy:”というprefixを付けたURLを設定します。
if (r->proxyreq) {
/* someone has already set up the proxy, it was possibly ourselves
* in proxy_detect
*/
return OK;
}
コメントの通りですが、すでに別のhandlerなどでproxyreqにNONE以外が設定されていたら、proxy対象として検知できているため、処理正常終了(OK)します。
ここでreturnしている場合はおそらくforward proxyと判定されていると思います。
次の条件文はDECLINEDを返すので説明を省略し、その次の条件文ではreverse proxyの判定を行っています。
dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
/* short way - this location is reverse proxied? */
if (dconf->alias) {
int rv = ap_proxy_trans_match(r, dconf->alias, dconf);
if (DONE != rv) {
return rv;
}
}
conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config,
&proxy_module);
/* long way - walk the list of aliases, find a match */
if (conf->aliases->nelts) {
ent = (struct proxy_alias *) conf->aliases->elts;
for (i = 0; i < conf->aliases->nelts; i++) {
int rv = ap_proxy_trans_match(r, &ent[i], dconf);
if (DONE != rv) {
return rv;
}
}
}
ディレクトリごとの設定(dconf変数)やサーバごとの設定(conf変数)の値を基にap_proxy_trans_match関数を呼び出し、エラーリターンもしくはreverse proxyと判定された時、DONE以外が返ってきて、returnします。
proxy_map_location(あまり読めていないです)
ap_hook_map_to_storageのhandlerです。
私のコードリーディングの目的にはあまり関係ないと思い、あまりよく読めていないので説明を省略します。
proxy_fixup
ap_hook_fixupsのhandlerです。
リクエストの最終準備フェーズに該当し、リクエスト構造体の最終調整を行っています。
mod_proxyで処理すべきリクエストであった場合、mod_proxyの後段モジュール(mod_proxy_wstunnelなど)のcanonicalize用関数(proxy_hook_canon_handler)をフックし処理を委譲します。
/* canonicalise each specific scheme */
if ((access_status = proxy_run_canon_handler(r, url))) {
return access_status;
}
リクエスト実行のhandler
proxy_handler
ap_hook_handlerのhandlerです。
do-while文よりも前
次のような処理を行っていますが、詳細な説明は省略します。
- 処理対象の判別
- Proxyのリクエストの有無チェック
- NoProxyのホストのチェック
- エラーチェック
do-while文の処理内容
/* Try to obtain the most suitable worker */
access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
do-while文の序盤でmod_proxyの後段モジュール(mod_proxy_balancerなど)のrequest前処理用関数(proxy_hook_pre_request)をフックし適切なworkerを取得しています。
balancerが登録してある場合には初期化など必要な処理をし、
NoProxyが指定されていない場合はproxyであると仮定して処理を進めます。
/* handle the scheme */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01142)
"Trying to run scheme_handler against proxy");
access_status = proxy_run_scheme_handler(r, worker,
conf, url,
ents[i].hostname,
ents[i].port);
proxy_hook_scheme_handlerをフックしschemeに応じた処理を後段へ委譲します。
後段からDECLINED以外のリターンが返って来た場合は、cleanupなどで後処理をして終了です。
NoProxyの場合でもproxy_hook_scheme_handlerをフックしschemeに応じた処理を後段へ委譲するのは同様です。
/* Try again if the worker is unusable and the service is
* unavailable.
*/
} while (!PROXY_WORKER_IS_USABLE(worker) &&
max_attempts > attempts++);
whileループの条件文です。
workerがusableでない、かつ試行回数の設定値(max attempts)を超えたか
を判定しています。
statusがDECLINEDだった場合はHTTP_INTERNAL_SERVER_ERRORを設定してcleanupで後処理をして終了です。
cleanup
エラーの場合や正常終了で後処理をする場合の処理を実施するためのgoto分のラベルです。
リクエスト構造体のstatusの設定や後段で実装されているproxy_hook_post_requestやproxy_hook_request_statusをフックし、最終的なstatusをreturnします。
おわりに
不要なところを飛ばしながら読んだため、理解できていないところもあります。
また、理解内容に間違いがあるかもしれませんが、mod_proxyの主要な関数を一通り読んでみました。
備忘録も兼ねていますが、参考になる方がいれば幸いです。
本記事でmod_proxy本体の内容は終わりです。
時間があったら、mod_proxy_balanacerやmod_proxy_wstunnelの実装についても記事を書いてみようと思っています。