Apache は HTTP/1.1 の規格に記述されているコンテントネゴシエーションを サポートしています。 ブラウザにより提供されたメディアタイプ、 言語、文字セット、エンコーディングの優先傾向に基づいて、 最適なリソースの表現を選択できます。 また、不完全なネゴシエーション情報を送ってくるブラウザからのリクエストを もっと賢く取り扱えるよう、いくつか機能も実装してあります。
コンテントネゴシエーションは
リソースは、幾つか異なった表現で利用できる場合があります。 例えば、異なる言語や異なるメディアタイプ、 またはそれらの組み合わせで利用できるかも知れません。 もっとも適した選択をする方法の一つには、インデックスページを ユーザに見せて、ユーザに選んでもらう方法があります。 しかし、サーバが自動的に選ぶことができる場合が多くあります。 これは、ブラウザがリクエスト情報毎の情報の一部に、 どの表現を嗜好するかを送ることで動作しています。 例えばブラウザは、可能ならフランス語で情報を見たい、 不可能ならその代わりに英語でもよいと、 自分の嗜好を知らせることができます。 ブラウザはリクエストのヘッダで自分の優先傾向を知らせます。 フランス語のみの表現を要求する場合は、ブラウザは次を送ります。
この優先傾向は、選択可能な表現が存在して、 言語によって様々な表現がある場合にのみ適用される ということに注意してください。
もっと複雑なリクエストの例を挙げましょう。 このブラウザはフランス語と英語を受け付ける、しかしフランス語を好む、 そして様々なメディアタイプを受け付けるが、 プレインテキストや他のタイプよりは HTML を好む、 他のメディアタイプよりは GIF や JPEG を好む、しかし最終手段として 他のメディアタイプも受け付ける、と設定されています。
Apache は HTTP/1.1 規格で定義されている 'server driven' コンテントネゴシエーションをサポートしています。 Accept, Accept-Language, Accept-Charset, Accept-Encoding リクエストヘッダを完全にサポートしています。Apache は 'transparent' コンテントネゴシエーションもサポートしていますが、 これは RFC 2295 と RFC 2296 で定義されている試験的な ネゴシエーションプロトコルです。 これらの RFCで定義されている 'feature negotiation' はサポートしていません。
リソースとは URI で特定される概念上のもののことです (RFC 2396)。 Apache のような HTTP サーバは、その名前空間の中での リソースの表現へのアクセスを提供します。 それぞれの表現は 定義されたメディアタイプ、文字セット、エンコーディング等の 付属した、バイト列の形式です。 それぞれのリソースはある時点で 0 個、1 個、それ以上の表現と 関連付けられる可能性があります。複数の表現が利用できる場合は、 リソースはネゴシエーション可能であるとされ、 個々の表現は variant と呼ばれます。 ネゴシエーション可能なリソースの variant が異なる、 その状態を指して、 ネゴシエーションの次元と呼びます。
リソースをネゴシエーションするためには、 サーバは variant それぞれについての情報を知っておく必要があります。 これは以下の二つの方法のどちらかで行われます。
*.var
ファイル)
を使う方法。 これは variant
を明示的に挙げているファイルを指定します。タイプマップは type-map
ハンドラ
(もしくは、古い Apache
の設定と下位互換である mime タイプ
application/x-type-map
)
に関連付けられたドキュメントです。
この機能を使うためには、あるファイルの拡張子を
type-map
として定義するようなハンドラを、
設定ファイル中に置く必要があることに注意してください。
これは
をサーバ設定ファイル中に書くことが一番良い方法です。
タイプマップファイルは記述するリソースと同じ名前を持っていて、
利用可能な variant それぞれのエントリを持っている必要があります。
そして、このエントリは連続した HTTP のヘッダ行で構成されます。
異なる variant のためのエントリは空行で区切られています。
エントリ中に空行が複数あってはいけません。
習慣的には、マップファイルは全体を結合したもののエントリから始まります
(しかしこれは必須ではなく、あったとしても無視されるものです)。
次に例を示します。このファイルはリソース foo
を記述しているので、foo.var
という名前になります。
たとえ MultiViews を使用するようになっていたとしても、 ファイル名の拡張子よりタイプマップの方が優先権を持つということにも 注意してください。 variant の品質が違うときは、この画像のように (JPEG, GIF, ASCII アートがあります) メディアタイプの "qs" パラメータで指定されます。
qs 値の範囲は 0.000 から 1.000 です。qs 値が 0.000 の variant は決して 選択されないことに注意してください。'qs' 値のない variant は qs 値 1.0 を 与えられます。qs パラメータはクライアントの能力に関係無く、他の variant と 比較したときの variant の相対的な「品質」を示します。 例えば、写真を表現しようとしているときは JPEG ファイルの方が普通は ASCII ファイルよりも高い品質になります。しかし、リソースが元々 ASCII アートで表現されているときは、ASCII ファイルの 方が JPEG ファイルよりも高い品質になります。このように、qs は 表現されるリソースの性質によって variant 毎に特有の値を取ります。
認識されるヘッダの一覧は mod_negotiation ドキュメントにあります。
MultiViews
はディレクトリ毎のオプションで、
httpd.conf
ファイルの
.htaccess
ファイルで Options All
は
MultiViews
をセットしないことに注意してください。明示的に
その名前を書く必要があります。
MultiViews
の効果は以下のようになります:
サーバが /some/dir/foo
へのリクエストを受け取り、/some/dir
で
MultiViews
が有効であって、
/some/dir/foo
が存在しない場合、
サーバはディレクトリを読んで foo.*
にあてはまる全てのファイルを探し、
事実上それらのファイルをマップするタイプマップを作ります。
そのとき、メディアタイプとコンテントエンコーディングは、そのファイル名を
直接指定したときと同じものが割り当てられます。
それからクライアントの要求に一番合うものを選びます。
サーバがディレクトリの索引を作ろうとしている場合、
MultiViews
は
が書かれていて、index.html
と
index.html3
が
両方存在していると、サーバはその中からどちらかを適当に選びます。
もしその両方が存在せずに index.cgi
が存在していると、 サーバはそれを実行します。
もしディレクトリを読んでいる際に、
文字セット、コンテントタイプ、言語、エンコーディングを
指定するための mod_mime
で認識できる拡張子を持たないファイルが見つかると、結果は
Apache はリソースの variant の一覧を、タイプマップファイルか ディレクトリ内のファイル名からかで取得した後、 「最適な」 variant を決定するために二つの方法の どちらかを起動します。 Apache のコンテントネゴシエーションの機能を使うために、 どのようにしてこの調停が行われるか詳細を知る必要はありません。 しかしながら、この文書の残りでは関心のある人のために、 使用されている方法について説明しています。
ネゴシエーション方法は二つあります。
次元 | 説明 |
---|---|
メディアタイプ | ブラウザは Accept ヘッダフィールドで優先傾向を指定します。 アイテムそれぞれは、関連した品質数値を持つことができます。 variant の説明も品質数値を持つことができます ("qs" パラメータをご覧下さい)。 |
言語 | ブラウザは Accept-Language ヘッダフィールドで優先傾向を指定します。 要素それぞれに品質数値を持たせることができます。 variants は 0 か 1 つかそれ以上の言語と 関連づけることができます。 |
エンコーディング | ブラウザは Accept-Encoding ヘッダフィールドで優先傾向を指定します。 要素それぞれに品質数値を持たせることができます。 |
文字セット | ブラウザは Accept-Charset ヘッダフィールドで優先傾向を指定します。 要素それぞれに品質数値を持たせることができます。 variant はメディアタイプのパラメータとして文字セットを 指定することもできます。 |
ブラウザに返す「最適な」variant を (もしあれば) 選択するように Apache は次のアルゴリズムを使うことができます。 このアルゴリズムを設定により変更することはできません。 次のように動作します:
text/*
メディアタイプであるけれども
特定の文字セットに明示的に関連づけられているわけではない
variant は ISO-8859-1 であると仮定されます。上記の Apaceh ネゴシエーションアルゴリズムの厳格な解釈で 得られるであろう値から、Apache は品質数値を時々変えます。 これは、このアルゴリズムで完全ではない、あるいは正確でない情報を送る ブラウザ向けによりよい結果を得るために行われます。 かなりポピュラーなブラウザで、もしないと間違った variant を選択する結果になってしまうような Accept ヘッダ情報を送るものもあります。 ブラウザが完全で正しい情報を送っていれば、 この数値変化は適用されません。
Accept: リクエストヘッダはメディアタイプの優先傾向を指定します。 これはまた、"image/*" や "*/*" といった「ワイルドカード」メディアタイプを含むことができます。 ここで * は任意の文字列にマッチします。 ですから、次の:
を含むリクエストは、"image/" ではじまるタイプ全てが許容できる、 そして他のどんなタイプも許容できる (この場合はじめの "image/*" は冗長になります) ことを示します。 扱うことのできる明示的なタイプに加えて、機械的に ワイルドカードを送るブラウザもあります。例えば:
こうすることの狙いは、明示的にリストしているタイプが優先されるけれども、 異なる表現が利用可能であればそれでも良い、ということです。 しかしながら、上の基本的なアルゴリズムでは、 */* ワイルドカードは他の全てのタイプと全く同等なので優先されません。 ブラウザは */* にもっと低い品質 (優先) 値を付けてリクエストを送るべきなのです。例えば:
明示的なタイプには品質数値が付けられていませんので、 デフォルトの 1.0 (最高値) の優先になります。 ワイルドカード */* は低い優先度 0.01 を与えられているので、 明示的にリストされているタイプに合致する variant がない場合にのみ、 他のタイプが返されます。
もし Accept: ヘッダが q 値を全く含んでいなければ、 望みの挙動をするために、 Apache は "*/*" があれば 0.01 の q 値を設定します。 また、"type/*" の形のワイルドカードには 0.02 の q 値を設定します (ですからこれらは "*/*" のマッチよりも優先されます)。 もし Accept: ヘッダ中のメディアタイプのどれかが q 値を含んでいれば、これらの特殊な値は適応されず、 正しい情報を送るブラウザからのリクエストは期待通りに 動作するようになります。
Apache 2.0 では新たに、言語ネゴシエーションが適合するものを 見つけるのに失敗した時に、優雅にフォールバックできるような ネゴシエーションアルゴリズムが幾つか追加されました。
サーバのページをクライアントがリクエストしたけれども、
ブラウザの送ってきた Accept-Language に合致するページが一つも
見つからなかった場合に、サーバは "No Acceptable Variant"
か "Multiple Choices" レスポンスをクライアントに返します。
これらのエラーメッセージを返さないように、
このような場合には Apache が Accept-Language を無視して、
クライアントのリクエストに明示的には合致しないドキュメントを
提供するように設定できます。
サーバは他に適合するものが見つからなければ、
言語サブセットで適合するものを試そうともします。
例えばクライアントが英国英語である en-GB
言語で
ドキュメントをリクエストした場合、サーバは HTTP/1.1
規格では、単に en
とマークされているドキュメントを
マッチするものとすることは通常は許されていません。
(英国英語は理解できるけど一般的な英語は理解できないという読み手は
考えられないので、Accept-Language ヘッダで en-GB
を含んで en
を含まないのはほぼ確実に設定の間違いである、
ということに注意してください。
ですが不幸なことに、多くのクライアントではデフォルトで
このような設定になっています。)
しかしながら、他の言語にはマッチせず、"No Acceptable Variants"
エラーを返したり、
en-GB
を en
にマッチします。
Apache はクライアントの許容言語リストに暗黙に
非常に低い品質値の親言語を加えることになります。
しかし、クライアントが "en-GB; qs=0.9, fr; qs=0.8" とリクエストして、
サーバが "en" と "fr" と設計されたドキュメントを持っている場合は、
"fr" ドキュメントが返されることに注意してください。
このような処理は、HTTP 1.1 規格との整合性を維持して、
適切に設定されたクライアントともきちんと動作するために
必要です。
Apache は transparent content negotiation プロトコル
(RFC 2295) を次のように拡張しています。
特定のコンテントエンコーディングのみが利用可能である variant
に印を付けるために、新たに {encoding ..}
要素を variant リスト中に使っています。
リスト中のエンコードされた variant を認識し、
Accept-Encoding リクエストヘッダに従って許容される
エンコードをもった variant は、どれでも候補 variant
として使用するように、
RVSA/1.0 アルゴリズム (RFC 2296) の実装が拡張されました。
RVSA/1.0 の実装では、最適な variant が見つかるまで、
計算した品質数値は小数点以下 5 桁まで丸めません。
言語ネゴシエーションを使っている場合は、 ファイルが一つ以上の拡張子を持てて、 拡張子の順番は通常は考慮されない (詳細は mod_mime を参照) ので、 幾つかの異なる名前の変換を選べることになります。
典型的なファイルでは、MIME タイプ拡張子 (例えば
html
) を持っていて、エンコーディング拡張子
(例えば gz
) を持っているかもしれなくて、
このファイルに異なる言語 variant を用意していれば、
もちろん言語拡張子 (例えば en
)
を持っているでしょう。
例:
ファイル名と、それに対して使えるリンクと使えないリンクの例です:
ファイル名 | 使えるリンク | 使えないリンク |
---|---|---|
foo.html.en | foo foo.html |
- |
foo.en.html | foo | foo.html |
foo.html.en.gz | foo foo.html |
foo.gz foo.html.gz |
foo.en.html.gz | foo | foo.html foo.html.gz foo.gz |
foo.gz.html.en | foo foo.gz foo.gz.html |
foo.html |
foo.html.gz.en | foo foo.html foo.html.gz |
foo.gz |
上の表を見て、拡張子なしのリンク (例えば foo
)
がいつでも使えることに気が付くでしょう。
この利点は、ドキュメントとして応答するファイルの
実際のファイルタイプを隠蔽して、リンクの参照を変更することなく
後からファイルを変更できる、
例えば html
から shtml
に、あるいは cgi
に変更できる点です。
リンクに MIME タイプを使い続けたい (例えば
foo.html
)時は、言語拡張子は
(エンコーディング拡張子もあればそれも含めて)
MIME タイプ拡張子の右側になければなりません
(例えば foo.html.en
)。
キャッシュが一つの表現を保存しているときは、 リクエスト URL と関連づけられています。 次にその URL がリクエストされた時に、キャッシュは 保存されている表現を使用できます。しかし、 リソースがサーバでネゴシエーション可能であれば、 最初のリクエストでキャッシュされて続くキャッシュヒットでは 間違った応答を返してしまうということになりかねません。 これを防ぐために、Apache はコンテントネゴシエーションの 後に返された応答全てに、HTTP/1.0 クライアントでは キャッシュ不可能の印をつけます。 また、ネゴシエーションされた応答のキャッシュを可能にする HTTP/1.1 プロトコルの機能も Apache はサポートします。
HTTP/1.0 準拠のクライアントからのリクエストに対しては、
(ブラウザであろうとキャッシュであろうと)
ネゴシエーションを受けた応答のキャッシュを許すために、
コンテントネゴシエーションに関する追加情報は、 Alan J. Flavell さんのLanguage Negotiation Notes をご覧下さい。ですが、 Apache 2.0 での変更点を含むためには更新されていないかもしれない ということに注意してください。