|
少々ここで一般的なpedagogicalなスタイルに触れています。簡潔にするために、ここでは全ての構成通知は不完全です --- 実際には触れていないより多くのスロットがあります。これらはほとんどの部分についてサーバの中心部と他の部分に用意されており、使用する上で注意が必要なモジュールによって変更されるでしょう。しかしながらいくつかの場合には、まだ本当に手付かずのものがあります。最前線へようこそ。
最後になりますがここにあるのは概要であり、何が起こって何を指図するかということについての基本的な概念を提供するものです:
SetEnvのような拡張できるフックとして示されています。
OKの返送によってそのように行なうことを示しています。
DECLINEDの返送によってDeclineします。この場合、サーバは全てに関してハンドラーが単にそこにいなかったかのようにふるまいます。
*/*(すなわちワイルドカードのMIMEタイプ特定化)を与えることによって、なんらかの要求をハンドルすることができる反応段階のハンドラーを明示しているのかもしれません。しかしながら、もしサーバがすでにより多くの要求された物のMIMEタイプについての特定の反応ハンドラーを捜そうとして失敗すれば、ワイルドカードハンドラーが利用されます。
ハンドラー自身は、上記のようにintegerを転送する一つの引数(a request_rec structure. vide infra)の関数です。
ScriptAliasコンフィグファイルコマンドの両方をハンドルしています。実際、ほとんどのモジュールよりも非常に複雑になっていますが、しかしもし一つの例だけを取り上げれば、それはあらゆる場所でのそのフィンガーに関する一つかもしれません。
ハンドラーにおついて始めましょう。CGIスクリプトをハンドルする代わりに、モジュールはそれらの反応ハンドラーを明示します。ScriptAliasのせいで、それは名前の翻訳段階(ScriptAliased URIsの認識)、タイプチェック段階(なんらかのScriptAliased要求がCGIスクリプトとしてタイプされています)を持っています。
モジュールはいくつかの(仮想)サーバ情報、すなわち実際にはScriptAliasesを維持する必要があります;それ故、モジュール構造はこれらの構造を構成している関数と、その二つを組み合わせているべつの物に対するポインタを含んでいます(メインサーバと仮想サーバの両方が明示されたScriptAliasesを持っている場合)。
最後に、このモジュールはScriptAliasコマンド自身をハンドルするコードを含んでいます。この特定のモジュールは一つのコマンドだけを明示しますが、更にそのようなモジュールは、どんなコマンドを明示したコマンドテーブル、それらがどこで許可され、どのように利用されるかについての記述を含んでいます。
最終的ないくつかのこれらのコマンドの明示された引数タイプについての注釈: poolはresource pool構造に対するポインタです;これらは割り当てられたメモリートラック、開かれたファイル等を維持して、特定の要求を修理するか、コンフィギュアする処理自身をハンドルするサーバによって使われます。That way, 要求が終了(またはコンフィグレーションプールについて、サーバが再起動する時)する時、それらを全てつきとめる明白なコードを書き、それらを処分することなしでメモリーがfreedされ、ファイルが閉じるen masseがその方法です。また、cmd_parms構造は読まれるコンフィグファイルについての多様な情報と、コンフィグファイルコマンド(ScriptAliasのようなもの)を処理する関数を時々使う他のステータス情報を含んでいます。
モジュール自身に問題ありません:
/* Declarations of handlers. */
int translate_scriptalias (request_rec *);
int type_scriptalias (request_rec *);
int cgi_handler (request_rec *);
/* Subsidiary dispatch table for response-phase handlers, by MIME type */
handler_rec cgi_handlers[] = {
{ "application/x-httpd-cgi", cgi_handler },
{ NULL }
};
/* Declarations of routines to manipulate the module's configuration
* info. Note that these are returned, and passed in, as void *'s;
* the server core keeps track of them, but it doesn't, and can't,
* know their internal structure.
*/
void *make_cgi_server_config (pool *);
void *merge_cgi_server_config (pool *, void *, void *);
/* Declarations of routines to handle config-file commands */
extern char *script_alias(cmd_parms *, void *per_dir_config, char *fake,
char *real);
command_rec cgi_cmds[] = {
{ "ScriptAlias", script_alias, NULL, RSRC_CONF, TAKE2,
"a fakename and a realname"},
{ NULL }
};
module cgi_module = {
STANDARD_MODULE_STUFF,
NULL, /* initializer */
NULL, /* dir config creator */
NULL, /* dir merger --- default is to override */
make_cgi_server_config, /* server config */
merge_cgi_server_config, /* merge server config */
cgi_cmds, /* command table */
cgi_handlers, /* handlers */
translate_scriptalias, /* filename translation */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
type_scriptalias, /* type_checker */
NULL, /* fixups */
NULL /* logger */
};
request_rec構造です。この構造はクライアント代わってサーバに作られた特定の要求を記述しています。ほとんどの場合、それぞれのクライアントに対する接続は唯一request_rec構造だけを生じます。
request_recrequest_recはサーバが要求のハンドリングを終了した時に消去されるリソースプールへのポインタを含んでいます; サーバ毎と、接続毎の情報、そして最も重要な要求自体の情報を含んでいる構造に対して。最も重要なそのような情報は、そのURI、ファイル名、コンテントタイプとコンテントエンコーディング(これらはそれぞれ要求をハンドルするタイプチェックハンドラーと翻訳によって最新の情報を与えられる)を含んで要求された物の属性を記述しているキャラクタストリングの小さなセットです。
他に一般的に使われるデータ項目は、クライアント自身の要求のMIMEヘッダと、レスポンス(意のままにモジュールを追加できる)と共に返送されるMIMEヘッダ、要求をサービスする間に生じるなんらかの二次処理についての環境変数を与えているテーブルです。これらのテーブルはtable_getとtable_setルーチンを使うことによって扱われています。
最後にモジュールごとのコンフィグレーション構成に対して順番にポイントする二つのデータ構成に対するポインターがあります。特に、これらは要求を受け取っている間に構築されるプライベートデータについて、(一つの段階についてのそのようなモジュールハンドラーは、他の段階についてのハンドラーに対して`notes'をパスすることができます)モジュールが与えられたディレクトリ(.htaccessファイルや<Directory>セクションによって)で作動するためにコンフィギュアする方法を記述するために作るデータ構成に対するポインターを維持します。(仮想)サーバコンフィギュレーションのデータごとに含まれるrequest_recによってポイントされるserver_recデータ構成には別のそのようなコンフィギュレーションベクトルがあります。
ここにあるのは簡易記述で、最も頻繁に使われる領域に与えられています:
struct request_rec {
pool *pool;
conn_rec *connection;
server_rec *server;
/* どんな物が要求されているか */
char *uri;
char *filename;
char *path_info;
char *args; /* QUERY_ARGS, if any */
struct stat finfo; /* Set by server core;
* st_mode set to zero if no such file */
char *content_type;
char *content_encoding;
/* MIMEヘッダー環境は出たり入ったりしています。また、配列はサブプロ
* セスにパスされる環境変数を含んでいるので、その環境に追加するため
* にモジュールを書くことができます。headers_outとerr_headers_outと
* の違いは、後者がエラーでさえプリントし、内部リダイレクトの横断を
* 続けることです(それで、ErrorDocumentのヘッダーにプリントされたヘ
* ッダーはそれらを持っています)。
*/
table *headers_in;
table *headers_out;
table *err_headers_out;
table *subprocess_env;
/* 要求自身についての情報 ... */
int header_only; /* HEAD request, as opposed to GET */
char *protocol; /* Protocol, as given to us, or HTTP/0.9 */
char *method; /* GET, HEAD, POST, etc. */
int method_number; /* M_GET, M_POST, etc. */
/* ロギングについての情報 */
char *the_request;
int bytes_sent;
/* 返送されたデータが変わりやすいことを示すためにモジュールがセット
* できるフラッグとクライアントは、それをキャッシュしないことを言う
* べきである。
*/
int no_cache;
/* .htaccessファイルと共に変わるかもしれない多様な他のコンフィグ情
* 報これらはそれぞれのモジュールにとって一つのvoid*ポインタが付い
* たコンフィグベクトルです(モジュールの仕事にするためにポイントさ
* れたもの)。
*/
void *per_dir_config; /* Options set in config files, etc. */
void *request_config; /* Notes on *this* request */
};
request_rec構成は、クライアントからのHTTP要求を読んだり、フィールドに注入することにより構築されます。 しかし、2、3の例外があります:
*.varファイル)やローカル`Location:'から返送されたCGIスクリプトであれば、ユーザが要求したリソースはクライアントが独自に供給するのとは違ったいくつかのURIによって最終的に場所を示されるでしょう。この場合、サーバは、internal redirectと新しいURIのための新しいrequest_recの構築、そしてクライアントが新しいURIディレクトリを要求したかのようにそれをほぼ正確に処理します。
ErrorDocumentがスコープにあれば、同じ内部リダイレクト機構が行われます
そのようなハンドラーはsub_req_lookup_fileとsub_req_lookup_uriの機能を使って、sub-requestを構築します; これは予想通り達しますが、実際に反応を送信するポイントを含まないように新しいrequest_rec構造を構築し、それを処理します。 (これらの機能は、もしサブ要求が独自の要求のように同じディレクトリにあるファイルのためであるなら、アクセスチェックをスキップオーバーします。).
(サーバ側はrun_sub_request機能によって、サブ要求の構築とそれからそれらについての反応ハンドラーを実際に利用することによる仕事を含んでいます)
request_recをハンドルするために利用された時、それぞれのハンドラーは何が起こったのかを指し示すintをリターンしなければなりません。以下のいずれかが可能です。
REDIRECTであれば、モジュールはクライアントがどこに向けてリダイレクトすべきなのかを指し示す、要求のheaders_outの中にあるLocationを付けるべきであることに注意してください。
request_rec構造(または、アクセスチェッカーの場合には単に正確なエラーコードをリターンすることによります)にある2、3の領域を単にセッティングすることによって仕事をします。しかしながら、反応ハンドラーは実際にクライアントに要求を返送しなければなりません。
それらはHTTP反応ヘッダを送信し、send_http_header機能を使うことによって始まらなければなりません(HTTP/0.9要求について、ヘッダの送信をスキップするために特別なことをしてはいけません; その機能は何もすべきではないそれ自身を見分けます)。もし要求がマークされたheader_onlyであるなら、それが行なうべき全てです; それらは更なるアウトプットを試みることなしに、その後にリターンすべきです。
そうでなければ、それらは適切にクライアントに反応する要求本体を生じるべきです。この初期は内部的に生じるrputcとrprintf、いくつかのFILE *の内容をまっすぐにクライアントにコピーするためのsend_fdです。
この点で、特定のハンドラーを持たないGET要求をハンドルするハンドラーの以下のコード片を多少は理解すべきです; それはまたどのように条件付きのGETがハンドルされたかを示しており、もし特定の反応ハンドラーの中でそうすることが望ましいなら --- set_last_modifiedはクライアントによって供給されたIf-modified-sinceの値をチェックして適切なコードをリターンします(もしゼロでなければUSE_LOCAL_COPYです)。類似の理由がset_content_lengthに適用されることはありませんが、それは釣り合いのエラーコードをリターンします。
int default_handler (request_rec *r)
{
int errstatus;
FILE *f;
if (r->method_number != M_GET) return DECLINED;
if (r->finfo.st_mode == 0) return NOT_FOUND;
if ((errstatus = set_content_length (r, r->finfo.st_size))
|| (errstatus = set_last_modified (r, r->finfo.st_mtime)))
return errstatus;
f = fopen (r->filename, "r");
if (f == NULL) {
log_reason("file permissions deny server access",
r->filename, r);
return FORBIDDEN;
}
register_timeout ("send", r);
send_http_header (r);
if (!r->header_only) send_fd (f, r);
pfclose (r->pool, f);
return OK;
}
最後に、この全てが手に負えない試みであるなら、2、3の解決法があります。まず上に示したように、まだなんらかのアウトプットを生じていない反応ハンドラーは、サーバが自動的にエラー反応を生じた場合に単にエラーコードをリターンすることができます。二番目に、上記の内部リダイレクト機構がどのように利用されているのかというinternal_redirectを利用することによっていくつかの他のハンドラーをパントすることができます。内部的にリダイレクトした反応ハンドラーはいつもOKをリターンすべきです。
(反応ハンドラーではないハンドラーからinternal_redirect を利用することは、深刻な混乱を招くでしょう)
auth_typeとauth_name、requiresが付属します。
connection->user構造領域と返送する適当なWWW-Authenticate:ヘッダをアレンジするnote_basic_auth_failureをセットするget_basic_auth_pw)
r->prevとr->nextポインタを通じてスレッドするrequest_rec構造のリストにリダイレクトの完全なチェーンをバンドルすることによってこれをハンドルします。そのような場合にハンドラーのロギングをパスするrequest_recは、クライアントからのイニシャル要求を独自に構築するものの一つです; bytes_sentフィールドはチェーンの最後の要求の中でのみ正確になることに注意して下さい(反応が実際に送信されたうちの一つ).
これが作動する方法は以下の通りです: 特定の要求を扱うための開かれたファイル等に割り当てられたメモリは、要求に割り当てられたresource poolに制限されます。プールは問題のリソースをトラックするデータ構造です。
要求が処理されると、プールは消去されます。その点、それに関連付けられた全てのメモリはreuseに開放され、全ての関連付けられたファイルは閉じられます。そしてプールと関連付けられたなんらかの消去機能が作動します。これが終了すると、プールに制限されていた全てのリソースが開放され、リークされたものはないことがわかります。
サーバは再起動して、メモリの割り当てと、サーバ毎のコンフィギュレーションのリソースが同じようにしてハンドルされます。サーバのコンフィギュレーションファイルを読み込んで、その中にコマンドをハンドルしている間に、割り当てられたリソースのトラックを維持するconfiguration poolがあります (例として、開かれた、外部へのサーバ毎のモジュールコンフィギュレーションとログファイル、その他のファイルに割り当てられたメモリ等)。サーバが再起動してコンフィギュレーションファイルを再び読み込むと、コンフィギュレーションプールは消去され、それらを最後に読み込むことにより始められるメモリとファイルの書き込みはreuseの役に立つようになります。
プール機構の使用は普通、義務的なものではありません。ただし、サーバが再起動する時にログファイルが閉じることを確認するために、本当にcleanupsを記録する必要のあるハンドラーのロギングのような場合や、(これはCGIスクリプトのような、なんらかの子プロセスが実行される前に閉じられる内在するファイルの書き込みをアレンジするpfopen機能を使うことによって最も簡単に行われます)タイムアウト機構を使っている場合を除きます(ここではまだ記述されていません)。
しかし、それを使うことには二つ利点があります: 決してリークしないプールに割り当てられたリソース (スクラッチストリングを割り当てて、それについて忘れてしまう時でさえも); また、メモリの割り当てにとってpallocは普通mallocよりも速いです。
ここでは、メモリがプールにどのように割り当てられたかを説明して、それから他のリソースがリソースプール機構によってどのようにトラックされているかを説明します。
pallocと呼ばれる機能によってプールに割り当てられます。pallocはリソースプール構造へのポインターであり、割り当てるメモリと等しくなっています(in chars)。要求をハンドリングするハンドラーの内部で、リソースプール構造を得る最も一般的な方法はrequest_rec関連のpoolスロットを見ることです; それ故、モジュールコードの以下の慣用法が繰り替えされます:
int my_handler(request_rec *r)
{
struct my_structure *foo;
...
foo = (foo *)palloc (r->pool, sizeof(my_structure));
}
pfreeがないことに注意してください ---
pallocされたメモリは、関連したリソースプールが消去されると開放されます。これはpallocがmalloc()と同じようにアカウントしてはいけないことを意味しています; 典型的な場合に行われることは、サイズを統一してポインタをバンプし、レンジをチェックすることです。
(pallocの頻繁な使用はまた、過度に大きくなるサーバプロセスを引き起こす可能性があります。以下にあるこれの扱いには二つの方法があります; 要するに、mallocを使って全てのメモリがはっきりと開放されていることを確認しようとするか、あるいはメインプールのサブプールを割り当てて、サブプールにメモリを割り当て、定期的にそれを消去することができます。後者の方法はサブプール以下のセクションで説明されており、数千のファイルのあるディレクトリをリストに書き込む時、過度の記憶装置の割り当てを避けるためにディレクトリインデックスコードで使われます)。
pcalloc機能はpallocと同じインターフェースを持っていますが、それをリターンする前に割り当てているメモリを消去します。pstrdup機能はリソースプールと引数としてchar *を取り、ポインタがポイントするストリングのコピーのために、コピーに対するポインタをリターンするメモリを割り当てます。最後にpstrcatはリソースプールに対するポインタを取るvarargs-styleの機能で、少なくとも二つのchar *引数を取り、最後にNULLにならなければなりません。ユニットとして、それぞれのストリングのコピーに合致するのに十分なメモリを割り当てます; 例えば:
pstrcat (r->pool, "foo", "/", "bar", NULL);
"foo/bar"に初期化された、メモリの8バイトに相当するポインタをリターンします。
pfopenです; ストリングはfopenに対する典型的な引数と同じで、例えば、
...
FILE *f = pfopen (r->pool, r->filename, "r");
if (f == NULL) { ... } else { ... }
システムが呼び出す低レベルのopenと平行するpopenfルーチンもあります。問題のリソースプールがはっきりすると、両方のこれらのルーチンは閉じられたファイルをアレンジします。
メモリの異なる場合、pfopenやpopenfで割り当てられたファイルを閉じるpfcloseやpclosefという機能があります。(これは多くのシステムで、一つのプロセスが開くことのできるファイルの数が完全に限られているせいです)。pfopenやpopenfで割り当てられたファイルを閉じる機能を使うことが重要なのは、別のやり方では同じFILE*が一度以上閉じられると不都合な反応を示すLinuxのようなシステムで、致命的なエラーを引き起こすことがあるからです。
(close機能を使うことが強制でないのは、ファイルが結局不注意に閉じられるからですが、モジュールがたくさんのファイルを開いているか、開くことのできる場合には考慮すべきです)
spawn_processについても同様です。
palloc()と、関連したprimitivesの過度の使用は望ましくないリソースの割り当てをもたらすかもしれません。sub-poolを作成してメインプールよりもむしろサブプールの内部に割り当てて、それに関連するリソースを放出するサブプールを消去するか壊すことによってそのようなケースを扱うことができます。(これは本当にめったにないことです; 標準モジュールセットに発生する唯一のケースは、リスティングしているディレクトリに非常に大きなディレクトリがある場合だけです。 ここで書かれたprimitivesの無益な使用は、ほとんど得るものもなく相当コードをヘアアップすることになります).
サブプールを作成するためのprimitiveは、引数として他のプール(親プール)を取るmake_sub_poolです。メインプールが消去されると、サブプールは壊されます。サブプールはまた、clear_poolとdestroy_poolの機能それぞれに呼ぶことによって、時々消去されたり壊されたりするかもしれません。(相違点はclear_poolがプールに関連したリソースを開放することですが、 destroy_poolもまたプール自身の割り当てをなくします。前者の場合にはプールに新しいリソースを割り当てて、それを再び消去すること等ができます; 後者の場合では単になくなってしまいます)
最終的な注意点 --- サブ要求はメインの要求にとってのリソースプールであるサブプールを自身のリソースプールとして持っています。割り当てていた(sub_req_lookup_...機能を使って)サブ要求に関連したリソースを改善する正確な方法は、リソースプールを開放するdestroy_sub_requestです。この機能を呼び出す前に、ほとんど変わらないでどこかにある、サブリクエストのリソースに割り当てられているかもしれないことが気になるものをコピーしていることを確認してください。(例えばそのrequest_rec構造にあるファイル名)
(また一方で、ほとんどの環境下ではこの機能を仕方なく呼んでいると感じるべきではありません; たった2K程度のメモリが典型的なサブ要求に割り当てられていて、メイン要求のプールが消去されると開放されるでしょう。それは、深刻にdestroy...機能を考慮すべき単一のメイン要求にたくさんのサブ要求を割り当てている時だけです)
しかし、ただモジュールコマンドテーブルを与えることは、サーバコアから完全に引き離すには不十分です。サーバは後にそれを作動させるために、コマンドを記憶しています。それはサーバ毎かディレクトリ毎かのどちらかになるモジュールに対する秘密のデータを維持することを意味しています。ほとんどはディレクトリ毎であり、特定のアクセスコントロールと認証情報に含まれていますが、AddTypeとDefaultType命令等によって修正される拡張子からどのようにファイルタイプを決定するかについての情報です。一般には統制する原理は、ディレクトリであるべきことによってコンフィギュアが可能にされるものです; サーバ毎の情報は普通、要求が内在するファイルシステムにある特定の場所に制限される前に作動するようになるAliasesとRedirectsのような情報を基に標準セットのモジュールで使われます。
NCSAサーバをエミュレートするための他の要求は、一般的に.htaccessファイルと呼ばれるディレクトリ毎のコンフィギュレーションファイルをハンドルすることを可能にしますが、NCSAサーバの中でさえ、アクセスコントロールを全く行なわない命令を含んでいます。それ故、URI -> ファイル名翻訳の後、他の段階を実行する前に 提示されたかもしれないなんらかの.htaccessファイルを読み込むために、次の翻訳されたパス名の内在するファイルシステムのディレクトリ階層構造をサーバが降りていきます。その時読み込まれている情報は、サーバ自身からのコンフィグファイルからの適用できる情報でマージされなければなりません。(実際に<Directory />のようにほぼ正確にほとんどの目的について作動するaccess.confにある<Directory>セクションか、srm.confにあるデフォルトからかのどちらか).
最後に、.htaccessファイルの読み込みを必要とする要求を受け取った後、それらをハンドリングするために割り当てられた記憶装置を放棄する必要があります。トランザクション毎のリソースプールに対してそれらの構造を制限することにより、どこで類似する問題が起こっても同じ方法で解決されます。
mod_mime.cで、これの全てがどのように動くか気を付けてください。ここで見るであろうものは、AddTypeとAddEncodingコマンドを実行するコードです。これらのコマンドは.htaccessファイルに現れるので、実際にMIMEタイプとエンコーディング情報についての二つに分かれたtablesを構成している、モジュールの秘密のディレクトリ毎のデータにハンドルされなければならず、以下のように示されます:
typedef struct {
table *forced_types; /* Additional AddTyped stuff */
table *encoding_types; /* Added with AddEncoding... */
} mime_dir_config;
サーバがMIMEモジュールコマンドの一つを含むコンフィギュレーションファイルや<Directory>セクションを読み込んでいる時、mime_dir_config構造を作成する必要があるのは、それらのコマンドが作動するためのものを持っているからです。二つの引数のあるモジュールが作成するディレクトリ毎のコンフィグスロットに見られる機能を利用することによってこれを行ないます: このコンフィギュレーション情報が適用するディレクトリ名と割り当てが起こるべきリソースプールに対するポインタ
(もし.htaccessファイルを読み込むと、リソースプールは要求についての要求毎のリソースプールになります; 一方、コンフィギュレーションデータに使われるのはリソースデータで、再起動をクリアします。どちらかの方法で、もし必要ならプールのクリーンアップを記録することによって、プールがクリアされる時消去することが構築された構造にとって重要です。)
MIMEモジュールにとって、ディレクトリ毎のコンフィグ作成はちょうど上記のようなpallocs構造を構築し、それを満たす2、3のテーブルを作成します。それはこのように見えます:
void *create_mime_dir_config (pool *p, char *dummy)
{
mime_dir_config *new =
(mime_dir_config *) palloc (p, sizeof(mime_dir_config));
new->forced_types = make_table (p, 4);
new->encoding_types = make_table (p, 4);
return new;
}
今、.htaccessファイルの中でちょうど読み込んでいるとします。すでに階層構造の一つ上にあるディレクトリ毎のコンフィギュレーション構造を持っています。ちょうど読み込んでいる.htaccessファイルはAddTypeやAddEncodingコマンドを持っておらず、MIMEモジュールのディレクトリ毎のコンフィグ構造はまだ有効で、それを使うことが出来るだけです。一方、なんらかの方法で二つの構造をマージする必要があります。それを行なうためには、もし一つ存在するならサーバはモジュールのディレクトリ毎のコンフィグマージ機能を利用します。その機能は三つの項から成ります:マージされた二つの構造と結果を割り当てるリソースプール。MIMEモジュールにとって行われるべきことは、新しいディレクトリ毎のコンフィグ構造からのテーブルに、親からのテーブルをオーバーレイすることだけです:
void *merge_mime_dir_configs (pool *p, void *parent_dirv, void *subdirv)
{
mime_dir_config *parent_dir = (mime_dir_config *)parent_dirv;
mime_dir_config *subdir = (mime_dir_config *)subdirv;
mime_dir_config *new =
(mime_dir_config *)palloc (p, sizeof(mime_dir_config));
new->forced_types = overlay_tables (p, subdir->forced_types,
parent_dir->forced_types);
new->encoding_types = overlay_tables (p, subdir->encoding_types,
parent_dir->encoding_types);
return new;
}
As a note --- もしディレクトリ毎のマージ機能が存在しなくても、サーバはサブディレクトリのコンフィギュレーション情報を使い、親のものを無視します。いくつかのモジュールについて、それがうまく作動して(例えばディレクトリ毎のコンフィギュレーション情報がXBITHACKの状態を単に構成している含んでいるモジュールについて)そしてそれらのモジュールについてちょうど一つを断言することはできず、モジュール自身のNULLの中に対応する構造スロットを残します。
AddTypeとAddEncodingコマンドを処理する必要があります。コマンドを捜すために、サーバはモジュールのコマンドテーブルの中を見ます。そのテーブルはコマンドがどのくらいたくさんの引数を取るのか、フォーマットは何か、どこで許可されているのか等の情報を含んでいます。その情報は先に解析された引数についてのほとんどのコマンドハンドリング機能を利用するサーバを許可するのに十分です。更なる苦労なしで、このようなAddTypeコマンドハンドラーを見ましょう(AddEncodingコマンドは基本的に同じように見え、ここには示されていません):
char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext)
{
if (*ext == '.') ++ext;
table_set (m->forced_types, ext, ct);
return NULL;
}
このコマンドハンドラーは非常にシンプルです。見ればわかるように、4つの引数を取り、2つは先に解析された引数で、3つ目は問題のモジュールについてのディレクトリ毎のコンフィギュレーション構造で、4つ目はcmd_parms構造に対するポインタとなっています。その構造はリソースプール(メモリが割り当てられることができることから、クリーンアップが制限されるべきです)とモジュールのサーバ毎のコンフィギュレーションデータがもし必要なら得られることからコンフィギュアされる(仮想)サーバを含んでいる頻繁に使われるいくつかのコマンドの引数の束を含んでいます。
この特定のコマンドハンドラーが非常に単純であることの他の点は、遭遇するエラーコンディションがないことです。もしあるなら、NULLの代わりにエラーメッセージを返送できます; もしメインのコンフィグファイルの中にそれがあると、これはquick exitに続くサーバのstderrのプリントアウトに対するエラーを引き起こします; .htaccessファイルについて、シンタックスエラーはサーバエラーログにログされ(それが何処から来るのかの表示に従って)、要求はサーバエラーレスポンスに跳ね返されます(HTTP error status, code 500)。
MIMEモジュールのコマンドテーブルは、このようなこれらのコマンドのエントリーを持っています:
command_rec mime_cmds[] = {
{ "AddType", add_type, NULL, OR_FILEINFO, TAKE2,
"a mime type followed by a file extension" },
{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, TAKE2,
"an encoding (e.g., gzip), followed by a file extension" },
{ NULL }
};
これらのテーブルにあるエントリーは:
cmd_parmsでパスする(void *)ポインタ --- これは同じ機能によって多くの類似のコマンドがハンドルされる場合に有効です。
AllowOverrideオプションに対応するマスクビットと、コマンドがサーバ自身のコンフィグファイルの中に現れるかもしれないが、.htaccessファイルの中には現れないことを示す追加のマスクビットとRSRC_CONFがあります。
TAKE2は二つの前解析された引数を示しています。他のオプションは一つの前解析された引数を指し示すTAKE1、OnかOffにされるべき引数を指し示すFLAG、そしてブールフラッグとしてパスし、生の解析されていない引数(全てですが、コマンド名自身)をコマンドの与えることをサーバに行なわせるRAW_ARGSです。TAKE1と同じようなハンドラーを意味するITERATEもありますが、もし倍数の引数が存在するなら、倍数回呼び出されて最後にTAKE2に似たコマンドハンドラーを指し示すITERATE2が呼び出されるべきです。そこでより多くの引数が存在するなら、その時は倍数回呼び出されて最初の引数を一定に維持します。
NULLを残すことができます)。
get_module_config機能を使うことによってrequest_recのディレクトリ毎のコンフィギュレーションベクトルから引き出されます。
int find_ct(request_rec *r)
{
int i;
char *fn = pstrdup (r->pool, r->filename);
mime_dir_config *conf = (mime_dir_config *)
get_module_config(r->per_dir_config, &mime_module);
char *type;
if (S_ISDIR(r->finfo.st_mode)) {
r->content_type = DIR_MAGIC_TYPE;
return OK;
}
if((i=rind(fn,'.')) < 0) return DECLINED;
++i;
if ((type = table_get (conf->encoding_types, &fn[i])))
{
r->content_encoding = type;
/* go back to previous extension to try to use it as a type */
fn[i-1] = '\0';
if((i=rind(fn,'.')) < 0) return OK;
++i;
}
if ((type = table_get (conf->forced_types, &fn[i])))
{
r->content_type = type;
}
return OK;
}
ただ実質的な違いは、コマンドがサーバ毎のプライベートなモジュールデータをコンフィギュアするために必要とする時、それを入手するためにcmd_parmsデータへ行く必要があることです。シンタックスエラーの返送がどのようにして可能になるのかを示しているアライアスモジュールからの例がここにあります (コマンドハンドラーに対するディレクトリ毎のコンフィギュレーション引数がダミーとして申告されるのは、モジュールが実際にはディレクトリ毎のコンフィグデータを持っていないからです):
char *add_redirect(cmd_parms *cmd, void *dummy, char *f, char *url)
{
server_rec *s = cmd->server;
alias_server_conf *conf = (alias_server_conf *)
get_module_config(s->module_config,&alias_module);
alias_entry *new = push_array (conf->redirects);
if (!is_url (url)) return "Redirect to non-URL";
new->fake = f; new->real = url;
return NULL;
}
このページの情報に関わる、ご質問、お問い合わせは、 japache@infoscience.co.jpまで。