全 UserAgent とサーバの全ディレクトリに対し、ロボットのアクセスを拒否する場合の意思表示をするための robots.txt の例。
2003-06-08 の「ロボット避け meta 要素 <meta name="robots" content="noindex,nofollow">」では html の meta 要素によるロボット避けのみしか書かなかったのでメモ。
大文字小文字は関係なし。改行文字は CRLF でも LF でもどちらでも良い。これをサーバのルートに置く。http://sonic64.com/ だったら http://sonic64.com/robots.txt だ。
2003-06-08 の「ロボット避け meta 要素 <meta name="robots" content="noindex,nofollow">」では html の meta 要素によるロボット避けのみしか書かなかったのでメモ。
- robots.txt の例
robots.txtUser-agent: *
Disallow: /
大文字小文字は関係なし。改行文字は CRLF でも LF でもどちらでも良い。これをサーバのルートに置く。http://sonic64.com/ だったら http://sonic64.com/robots.txt だ。
Squid が付与する HTTP レスポンスヘッダの X-Cache や X-Squid-Error を消す方法。
squid.confg に以下を追加する。squid/2.5.STABLE13 ではこれで当該ヘッダの出力を抑制することができた。
Squid をリバースプロキシとして使用していて、内部のコンテンツサーバのホスト名などがクライアントに返ってしまうのを避けたい場合に使える。別に消す必要もないかなあと思ったが、サービスに直接影響のない情報は外部に出さないのが基本だし、とりあえず設定しておくことにした。でも、それでいて X-Powered-By: ASP.NET とか X-AspNet-Version: 2.0.50727 などといったヘッダは平気で出力してたりする。一貫性のない私。
squid.confg に以下を追加する。squid/2.5.STABLE13 ではこれで当該ヘッダの出力を抑制することができた。
header_access X-Cache deny all
header_access X-Squid-Error deny all
Squid をリバースプロキシとして使用していて、内部のコンテンツサーバのホスト名などがクライアントに返ってしまうのを避けたい場合に使える。別に消す必要もないかなあと思ったが、サービスに直接影響のない情報は外部に出さないのが基本だし、とりあえず設定しておくことにした。でも、それでいて X-Powered-By: ASP.NET とか X-AspNet-Version: 2.0.50727 などといったヘッダは平気で出力してたりする。一貫性のない私。
* Accept-Encoding に gzip を付けてないクライアントをリダイレクト
この記事の直リンクURL: Permlink | この記事が属するカテゴリ: [Apache] [http] [sonic64.com]
アクセスログを見ていたら、503 Service Temporarily Unavailable が出ていることに気づいた。503 はサーバ側の都合でサービスができないことを意味する HTTP ステータスコード。要するに、503 が出ている間は当サイトにアクセスできなかったということだ。
ログによると、最近アクセスが多かったようで一日20GB を超える転送量が続いていた模様。中でも全記事全文入りの RSS である cl-full.xml の転送量が90%を占めていた。おそらくこれのせいでさくらインターネットの転送量制限を超えてしまい、503 となっていたのだろう。
cl-full.xml は過去の全記事全文入りで 4MB を超えるサイズだから、サイト全体の転送量が増えても仕方がないかもしれないけど、ちょっと多い感じがする。だれかの役に立つかもしれないから公開しているのでどんどん使ってもらって構わないのだが、サイトのサービスの妨げになるのは困る。仕方がないので制限をかけることにした。
リクエストに Accept-Encoding: gzip がある場合は今まで通り gzip 圧縮した cl-full.xml を返す。
リダイレクトせずに cl.xml の中身を返すようにすることもできるけど、「君はこっちのコンテンツを使ってね」というリダイレクトの意図が伝わりにくいので使わない。でも、それだったら Vary を付ける方がいいかなあ。
以下のように、 Accept-Encoding: gzip つきならそのままアクセス許可。
以下のように、Accept-Encoding に gzip を付けてないクライアントは 302 を返して cl.xml へリダイレクト。
まず、Accept-Encoding に gzip なしのクライアントがリクエストしてくるとする。
302 を返して cl.xml へリダイレクト。
302 を受けたクライアントは cl.xml にリクエスト。
もちろん Accept-Encoding: gzip などはリクエストにないが、cl.xml はそういったリクエストの場合は圧縮していないコンテンツを返すだけなので、無事レスポンスが返される。
これでよしと。
ログによると、最近アクセスが多かったようで一日20GB を超える転送量が続いていた模様。中でも全記事全文入りの RSS である cl-full.xml の転送量が90%を占めていた。おそらくこれのせいでさくらインターネットの転送量制限を超えてしまい、503 となっていたのだろう。
cl-full.xml は過去の全記事全文入りで 4MB を超えるサイズだから、サイト全体の転送量が増えても仕方がないかもしれないけど、ちょっと多い感じがする。だれかの役に立つかもしれないから公開しているのでどんどん使ってもらって構わないのだが、サイトのサービスの妨げになるのは困る。仕方がないので制限をかけることにした。
- 制限をかける
2005-09-15 の「mod_rewrite でリクエストに応じて gzip 圧縮ファイルを返す」では、mod_gzip を 使えない当サイトの環境でも Accept-Encoding: gzip を送ってきているクライアントには gzip 圧縮したデータを返すようにした。今回はそれを一歩進めて、リクエストされたファイルが cl-full.xml でかつ Accept-Encoding: gzip がない場合、HTTP レスポンスコード 302 Moved Temporarily を返して、数十キロバイト程度でサイズの小さい cl.xml へリダイレクトする。リクエストに Accept-Encoding: gzip がある場合は今まで通り gzip 圧縮した cl-full.xml を返す。
- Accept-Encoding に gzip を付けてないクライアントをリダイレクトする mod_rewrite の RewiteRule
単純にリダイレクトしてるだけ。RewriteEngine on
RewriteCond %{REQUEST_FILENAME} cl-full\.xml$
RewriteCond %{HTTP:Accept-Encoding} !gzip
RewriteRule .+ http://sonic64.com/cl.xml [L,R]
リダイレクトせずに cl.xml の中身を返すようにすることもできるけど、「君はこっちのコンテンツを使ってね」というリダイレクトの意図が伝わりにくいので使わない。でも、それだったら Vary を付ける方がいいかなあ。
- HTTP トランザクションの中身を見て確認
2005-04-16 で書いた「Live Http headers - HTTP ヘッダ表示ツール」で HTTP トランザクションの中身を表示して確認する。以下のように、 Accept-Encoding: gzip つきならそのままアクセス許可。
http://sonic64.com/cl-full.xml
GET /cl-full.xml HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.12) Gecko/20050919 Firefox/1.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html; q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://sonic64.com/
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.x 200 OK
Date: Mon, 20 Feb 2006 04:43:35 GMT
Server: Apache/1.3.34 (Unix)
Last-Modified: Mon, 20 Feb 2006 02:33:38 GMT
Etag: "339758-fab0f-43ebfb82"
Accept-Ranges: bytes
Content-Length: 1026831
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Content-Type: application/xml
Content-Encoding: gzip
以下のように、Accept-Encoding に gzip を付けてないクライアントは 302 を返して cl.xml へリダイレクト。
まず、Accept-Encoding に gzip なしのクライアントがリクエストしてくるとする。
GET /cl-full.xml HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.12) Gecko/20050919 Firefox/1.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html; q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://sonic64.com/
Pragma: no-cache
Cache-Control: no-cache
302 を返して cl.xml へリダイレクト。
HTTP/1.x 302 Found
Date: Mon, 20 Feb 2006 04:46:02 GMT
Server: Apache/1.3.34 (Unix)
Location: http://sonic64.com/cl.xml
Keep-Alive: timeout=3, max=7
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-1
302 を受けたクライアントは cl.xml にリクエスト。
GET /cl.xml HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.12) Gecko/20050919 Firefox/1.0.7
Accept: text/xml,application/xml,application/xhtml+xml,text/html; q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
If-Modified-Since: Mon, 20 Feb 2006 02:33:30 GMT
If-None-Match: "339172-24c4-43ebfb7a"
もちろん Accept-Encoding: gzip などはリクエストにないが、cl.xml はそういったリクエストの場合は圧縮していないコンテンツを返すだけなので、無事レスポンスが返される。
HTTP/1.x 304 Not Modified
Date: Mon, 20 Feb 2006 04:46:02 GMT
Server: Apache/1.3.34 (Unix)
Connection: Keep-Alive, Keep-Alive
Keep-Alive: timeout=3, max=6
Etag: "339172-24c4-43ebfb7a"
これでよしと。
http リクエストの処理にかかった時間をロギングする方法のメモ。
集計や分析、パフォーマンス劣化の監視などで活用するため、http サーバ側でリクエストを処理したあとレスポンスを返すまでどれだけ時間がかかったかを記録したい。
所要時間などの値は http サーバ上で動くアプリケーション側でロギングする仕組みを作るのが普通。ただ、とりあえず記録さえできればいいときは、http サーバが提供するロギング機能を使ってアクセスログに書かせるのが手っ取り早い。
mod_log_config - Apache HTTP サーバ
http://httpd.apache.org/docs/2.2/ja/mod/mod_log_config.html# ...
Internet Information Service (IIS) 6.0 ヘルプ
以下は出力されるログのサンプル。
末尾の 40 が time-taken の値。単位はミリ秒。
IIS 5.0 マニュアル
これだけ。
集計や分析、パフォーマンス劣化の監視などで活用するため、http サーバ側でリクエストを処理したあとレスポンスを返すまでどれだけ時間がかかったかを記録したい。
所要時間などの値は http サーバ上で動くアプリケーション側でロギングする仕組みを作るのが普通。ただ、とりあえず記録さえできればいいときは、http サーバが提供するロギング機能を使ってアクセスログに書かせるのが手っ取り早い。
- Apache 2系
LogFormat ディレクティブ、CustomLog ディレクティブの書式指定文字列で %D を使う。mod_log_config - Apache HTTP サーバ
http://httpd.apache.org/docs/2.2/ja/mod/mod_log_config.html# ...
%D リクエストを処理するのにかかった時間、ミリ秒単位
- Apache 1.3系
標準では出力できない? モジュールなどが必要? 今回は Apache 1.3系は調査対象外なので詳細は調べていない。%T で秒単位ながらもロギングできるらしいが未確認。もっとも、秒単位だとあまりうれしくない。何もないよりはいいけど。- Internet Information Service (IIS) 6.0
インターネット インフォメーション サービス (IIS) マネージャ の「Web サイトのプロパティ」の「Web サイト」タブの「ログの記録を有効にする」の「詳細設定」タブの「所要時間 ( time-taken )」チェックボックスをオンにする。Internet Information Service (IIS) 6.0 ヘルプ
[所要時間]
処理が完了するまでに要した時間を記録するときに選択します。ODBC ログを除くすべてのログ形式では、次の手順に従い、所要時間がミリ秒単位でログが記録されます。HTTP.sys (カーネルモード ドライバ) が、要求の解析を開始する前に最初のバイトを受信すると、クライアント要求のタイムスタンプが初期化されます。IIS で最後の送信が完了すると、クライアント要求のタイムスタンプが停止します。ネットワーク上での時間は所要時間に反映されません。また、サイトへの最初の要求は、他の類似した要求、または同一の要求に比べて、所要時間がわずかに長くなります。これは、HTTP.sys により最初の要求時にログ ファイルが開かれるからです。
以下は出力されるログのサンプル。
#Fields: date time s-sitename s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs-version cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken
2005-12-27 09:14:59 W3SVC1 127.0.0.1 GET /iisstart.htm - 80 - 127.0.0.1 HTTP/1.1 Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.2;+SV1;+.NET+CLR+1.1.4322) - 200 0 0 1842 251 40
末尾の 40 が time-taken の値。単位はミリ秒。
- Internet Information Service (IIS) 5.0
IIS 5 でも IIS 6 と同じ手順で設定可能。ただ、マニュアルの記述が非常に少ない。IIS 5.0 マニュアル
[所要時間] アクションに要した時間。
これだけ。
リクエストを受けた後のサーバの処理時間が長く、処理が終わってないのに経路上の proxy などが接続を切ってしまうので、なんとかならないかという相談を受けた。
断続的にデータが流れてればいいので、HTTP Status 102 Processing や 202 Accepted を返すとか?
Transfer Encoding: Chunked で小出しにダミーデータを返すとか?
どちらにせよ、普段使わない特殊なステータスコードやダミーデータをうまく取り扱えるようにクライアントを修正する必要がある。
そもそも HTTP はそういう長い処理の実行を待つという方式には向いていなんいだから、設計からやり直した方がいい。なんらかの非同期な仕組みを織り込んだ方が信頼性も確保できるし、ネットワークやサーバの資源も節約できるはず。
どうしても設計を変えられないんだったら、経路上の proxy やファイアウォール系の設定を全部変更して、データが流れていない場合の HTTP トランザクション切断までのタイムアウト時間を長くするとか。
または、接続が切れた後もサーバ上では処理を継続させるようにし、かつ処理結果をキャッシングする。クライアントがリトライしてきたらキャッシュしておいた処理結果を返すようにすれば、タイムアウトの発生確率を下げられる。
あとは、処理を分割して、レスポンスを返すまでの時間を短くするとか。DB の ACID を要求されないような処理じゃないと難しいけど。
断続的にデータが流れてればいいので、HTTP Status 102 Processing や 202 Accepted を返すとか?
Transfer Encoding: Chunked で小出しにダミーデータを返すとか?
どちらにせよ、普段使わない特殊なステータスコードやダミーデータをうまく取り扱えるようにクライアントを修正する必要がある。
そもそも HTTP はそういう長い処理の実行を待つという方式には向いていなんいだから、設計からやり直した方がいい。なんらかの非同期な仕組みを織り込んだ方が信頼性も確保できるし、ネットワークやサーバの資源も節約できるはず。
どうしても設計を変えられないんだったら、経路上の proxy やファイアウォール系の設定を全部変更して、データが流れていない場合の HTTP トランザクション切断までのタイムアウト時間を長くするとか。
または、接続が切れた後もサーバ上では処理を継続させるようにし、かつ処理結果をキャッシングする。クライアントがリトライしてきたらキャッシュしておいた処理結果を返すようにすれば、タイムアウトの発生確率を下げられる。
あとは、処理を分割して、レスポンスを返すまでの時間を短くするとか。DB の ACID を要求されないような処理じゃないと難しいけど。
2005-09-14 の「Content Negotiation でリクエストに応じて gzip 圧縮ファイルを返す」の続き。
cl.xml にリクエストされたときは Content Negotiation が働かず、拡張子が一切ない cl にアクセスされたときは Content Negotiation が働くという挙動だった。これでは使いにくいので、mod_rewrite を使って圧縮ファイルを返すようにする。
RewriteRule の各行の説明。
%{HTTP:Accept-Encoding} gzip は、クライアントの HTTP リクエストヘッダ中に Accept-Encoding が存在し、かつ文字列 gzip が含まれているかどうかをチェックしている。単なる正規表現によるチェックなので sonic64gzip などという文字列でもマッチしちゃうけど、まあいいでしょ。ちなみに、Accpet-Encodind: sonic4gzip で実際にリクエストして RewriteLog を見たら matched になってた。
RewriteCond %{REQUEST_FILENAME} !\.gz$ は、最初から .gz ファイルをアクセスされた場合は Rewrite しないための判定。これをやらないと、file.html.gz にアクセスがあった場合に file.html.gz.gz を探しに行ってしまう。もっとも、次の RewriteCond %{REQUEST_FILENAME}\.gz -s での gz ファイル存在チェックで弾かれるから、やらなくてもとりあえずは動く。あ、でもこれだと png や .jpg もチェック対象になるよなあ。やっぱり .html や .xml だけを対象として明示した方がいいかなあ。
RewriteCond %{REQUEST_FILENAME}\.gz -s は、.gz ファイルが存在するかどうかをチェックしている。.gz ファイルが存在しない場合は Rewrite せずにもとのリクエストのファイルを返す。
RewriteLog の出力内容。
mod_rewrite で任意の HTTP レスポンスヘッダを生成させることってできるのかな? 環境変数の値を設定することはできるけど、直接ヘッダに追加する方法は見つけられなかった。あ、HEADER ディレクティブを使えばいいのか。以下のような感じかな?
ただ、これだといつも Vary を送ってしまうので、レスポンスに Content-Encoding: gzip があるときだけ Vary を返すようにしたいけど、環境変数の値に応じて条件分岐できるのは Apache 2.0 系だけ? ドキュメントを読んでみる。
mod_headers - Apache HTTP サーバ Header ディレクティブ
http://httpd.apache.org/docs/2.1/mod/mod_headers.html#header
Apache module mod_headers
http://httpd.apache.org/docs/1.3/mod/mod_headers.html
1.3 系のドキュメントには env= という構文が使えるとは書いてないもんなあ。どちらにせよ、mod_header のモジュールがステータス: Extension じゃあ使えない可能性が高い。
そもそも Vary ヘッダの付加って 必須? 推奨? HTTP1.1 を規定している RFC 2616 ではどうなってる?
ハイパーテキスト転送プロトコル -- HTTP/1.1
http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec14.44
「含まなければならない」ではなく、「含むべきである」なので必須ではないということか。じゃあ、とりあえずは Vary なしでいいか。
追記。
Internet Explorer がクライアントとき、HTTP レスポンスで Vary ヘッダを返してしまうと、IE は HTTP リクエストヘッダ If-Modified-Since を送信してこないという問題があるようだ。その結果、あらゆるレスポンスを 200 OK で返さざるを得なくなり、304 Not Modified によるレスポンスを返せなくなる。Vary を送らなければこの問題は回避できるようなので、Content-Encoding しか返さない方が総合的に見れば問題が少ないかも。
cl.xml にリクエストがあったとき、Accept-Encoding に gzip があり、かつ cl.xml.gz がサーバに存在していたら Content-Encoding: gzip で cl.xml.gz の中身を返すようにしたい
cl.xml にリクエストされたときは Content Negotiation が働かず、拡張子が一切ない cl にアクセスされたときは Content Negotiation が働くという挙動だった。これでは使いにくいので、mod_rewrite を使って圧縮ファイルを返すようにする。
- mod_rewrite を利用した Content Negotioation の代替の .htaccess サンプル
以下の RewriteRule を書いた。とりあえず希望の動作は実現できた。RewriteEngine on
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME} !\.gz$
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule .+ %{REQUEST_URI}.gz
RewriteRule の各行の説明。
%{HTTP:Accept-Encoding} gzip は、クライアントの HTTP リクエストヘッダ中に Accept-Encoding が存在し、かつ文字列 gzip が含まれているかどうかをチェックしている。単なる正規表現によるチェックなので sonic64gzip などという文字列でもマッチしちゃうけど、まあいいでしょ。ちなみに、Accpet-Encodind: sonic4gzip で実際にリクエストして RewriteLog を見たら matched になってた。
(4) RewriteCond: input='sonic64gzip' pattern='gzip' => matched
RewriteCond %{REQUEST_FILENAME} !\.gz$ は、最初から .gz ファイルをアクセスされた場合は Rewrite しないための判定。これをやらないと、file.html.gz にアクセスがあった場合に file.html.gz.gz を探しに行ってしまう。もっとも、次の RewriteCond %{REQUEST_FILENAME}\.gz -s での gz ファイル存在チェックで弾かれるから、やらなくてもとりあえずは動く。あ、でもこれだと png や .jpg もチェック対象になるよなあ。やっぱり .html や .xml だけを対象として明示した方がいいかなあ。
RewriteCond %{REQUEST_FILENAME}\.gz -s は、.gz ファイルが存在するかどうかをチェックしている。.gz ファイルが存在しない場合は Rewrite せずにもとのリクエストのファイルを返す。
- HTTP トランザクションの中身
ブラウザでリクエストした結果。http://sonic64.com/cl.xml
GET /cl.xml HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.10) Gecko/20050717 Firefox/1.0.6
Accept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.x 200 OK
Date: Wed, 14 Sep 2005 22:51:21 GMT
Server: Apache/1.3.33 (Unix)
Last-Modified: Tue, 13 Sep 2005 23:08:14 GMT
Etag: "3391fb-2ecc-43275bde"
Accept-Ranges: bytes
Content-Length: 11980
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Content-Type: application/xml
Content-Encoding: gzip
RewriteLog の出力内容。
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (3) [per-dir /home/hiroaki/public_html/test/] strip per-dir prefix: /home/hiroaki/public_html/test/cl.xml -> cl.xml
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (3) [per-dir /home/hiroaki/public_html/test/] applying pattern '.+' to uri 'cl.xml'
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (4) RewriteCond: input='gzip' pattern='gzip' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (4) RewriteCond: input='/home/hiroaki/public_html/test/cl.xml' pattern='!\.gz$' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (4) RewriteCond: input='/home/hiroaki/public_html/test/cl.xml.gz' pattern='-s' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (2) [per-dir /home/hiroaki/public_html/test/] rewrite cl.xml -> /~hiroaki/test/cl.xml.gz
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (1) [per-dir /home/hiroaki/public_html/test/] internal redirect with /~hiroaki/test/cl.xml.gz [INTERNAL REDIRECT]
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (3) [per-dir /home/hiroaki/public_html/test/] strip per-dir prefix: /home/hiroaki/public_html/test/cl.xml.gz -> cl.xml.gz
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (3) [per-dir /home/hiroaki/public_html/test/] applying pattern '.+' to uri 'cl.xml.gz'
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (4) RewriteCond: input='gzip' pattern='gzip' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (4) RewriteCond: input='/home/hiroaki/public_html/test/cl.xml.gz' pattern='!\.gz$' => not-matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (1) [per-dir /home/hiroaki/public_html/test/] pass through /home/hiroaki/public_html/test/cl.xml.gz
- Vary ヘッダを出力した方がより明示的
HTTP レスポンスヘッダに Vary: Accept-Encoding を追加するとより理想的な動きになる。Vary ヘッダは 2004-02-06 「http の Vary レスポンスヘッダの意味と使用例」で書いた。mod_rewrite で任意の HTTP レスポンスヘッダを生成させることってできるのかな? 環境変数の値を設定することはできるけど、直接ヘッダに追加する方法は見つけられなかった。あ、HEADER ディレクティブを使えばいいのか。以下のような感じかな?
Header append Vary Accept-Encoding
ただ、これだといつも Vary を送ってしまうので、レスポンスに Content-Encoding: gzip があるときだけ Vary を返すようにしたいけど、環境変数の値に応じて条件分岐できるのは Apache 2.0 系だけ? ドキュメントを読んでみる。
mod_headers - Apache HTTP サーバ Header ディレクティブ
http://httpd.apache.org/docs/2.1/mod/mod_headers.html#header
構文: Header [condition] set|append|add|unset|echo header [value] [early|env=[!]variable]
Apache module mod_headers
http://httpd.apache.org/docs/1.3/mod/mod_headers.html
Syntax: Header set|append|add header value
1.3 系のドキュメントには env= という構文が使えるとは書いてないもんなあ。どちらにせよ、mod_header のモジュールがステータス: Extension じゃあ使えない可能性が高い。
そもそも Vary ヘッダの付加って 必須? 推奨? HTTP1.1 を規定している RFC 2616 ではどうなってる?
ハイパーテキスト転送プロトコル -- HTTP/1.1
http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec14.44
HTTP/1.1 サーバは、サーバ駆動型ネゴシエーションを受けるあらゆるキャッシュ可能なレスポンスに Vary ヘッダフィールド値を含むべきである。そうする事で、キャッシュはそのリソースへの将来のリクエストを適切に解釈する事ができ、ユーザエージェントにそのリソースへのネゴシエーションの存在について知らせる事ができる。サーバは、サーバ駆動型ネゴシエーションを受けるキャッシュ不可能なレスポンスにも、ユーザエージェントにそのレスポンス時には変化してしまうレスポンスのについての有益な情報を提供するであろうから、Vary ヘッダフィールド値を含む事ができる。
「含まなければならない」ではなく、「含むべきである」なので必須ではないということか。じゃあ、とりあえずは Vary なしでいいか。
追記。
Internet Explorer がクライアントとき、HTTP レスポンスで Vary ヘッダを返してしまうと、IE は HTTP リクエストヘッダ If-Modified-Since を送信してこないという問題があるようだ。その結果、あらゆるレスポンスを 200 OK で返さざるを得なくなり、304 Not Modified によるレスポンスを返せなくなる。Vary を送らなければこの問題は回避できるようなので、Content-Encoding しか返さない方が総合的に見れば問題が少ないかも。
* Content Negotiation でリクエストに応じて gzip 圧縮ファイルを返す
HTTP の Content Negotiation 機能を使うと、ユーザの環境に応じて適切なファイルを返すことができる。
これを利用すると、以下を実現できる。
HTTP リクエストヘッダ Accept-Encoding: gzip を送ってきているクライアントにはあらかじめ gzip で圧縮しておいたファイルを返し、Accept-Encoding: gzip を送ってきていないクライアントには gzip 圧縮していない生のファイルを返す。
動的に生成するページを圧縮したいなら、mod_gzip や mod_deflate のフィルタを使うのがよいだろう。しかし、静的ページならば、あらかじめ圧縮したファイルを返す方式でも良い。毎回圧縮をしないので、CPU 資源を節約することにもつながる。
また、mod_gzip や mod_deflate が使えないサーバでも圧縮を利用できるというメリットがある。現在 sonic64.com はさくらインターネットのホスティングサービスを利用している。残念なことに私の利用しているコースでは mod_gzip が使えない。Content Negotiation 以外の方法でコンテンツを圧縮して返そうと思ったら、すべてを CGI にしてコード中で圧縮するか、proxy を挟むくらいしか方法がない。
コンテントネゴシエーション - Apache HTTP サーバ
http://httpd.apache.org/docs/2.1/content-negotiation.html
MultiViews が有効になると、クライアントのリクエストに応じてサーバが最適なファイルを選択して返すようになる。
私の使っているサーバは httpd.conf で MuitiViews が有効になっているようなので試してみる。
まず、生のファイルと、圧縮して拡張子 .gz を付加したファイルを用意する。
ブラウザからリクエスト。
あれ? うまく Content-Negotiation されてないな。なんで? リクエストするファイル名を変えてみるか。cl.xml じゃなくて、cl をリクエストしてみよう。
今度はうまくいった。Content-Location: cl.xml.gz と Vary: negotiate,accept-encoding が返されている。cl でリクエストすると Content-Negotiation が働き、cl.xml でリクエストしたときは Content-Negotiation がなされない。なんでだろう?
コンテントネゴシエーション - Apache HTTP サーバ
http://httpd.apache.org/docs/2.1/ja/content-negotiation.html
上記ページの説明を見る限り、foo.MIME-TYPE.ENCODING のファイルは foo.MIME-TYPE でアクセスできるはずなんだけどなあ。 とりあえずなんで cl でないとダメなのかは後で調べてみよう。
これはとくに Content-Negtiation とは関係ない。以下のように、cl.xml.gz を直接リクエストしたときも Content-Encoding: gzip がサーバから返されている。
Content-Encoding: gzip を返しているのは mod_mime だ。Apache のデフォルトでこの機能が働くように httpd.conf が記述されている。httpd.conf の以下の部分が該当箇所。もし設定されていなければ、.htaccess で設定する。
ん? 今気づいたけど、拡張子 tgz も AddEncodding してるの? それだと tgz という拡張子を付けたファイルの場合、クライアント側で gzip を展開してしまうので gzip されたファイルを保存したいとユーザが考えたときなどに問題になるんじゃないのかな? Google で AddEncoding x-gzip gz tgz を検索するとヒット。
Re: .tar.gz -> .tgz
http://search.fml.org/mlarchives/fml-help/200107/msg397.html
あ、やっぱり。.tar.gz という拡張子なら Content-Encoding が付かないので問題ないが、.tgz にすると Content-Encoding が付いてしまうので展開さてしまうと。まあ tgz なんて拡張子を使わないようにするか、AddEncoding から tgz を消せば良いだけだから実害は少ないかな。
cl.xml にリクエストがあったとき、Accept-Encoding に gzip があり、かつ cl.xml.gz がサーバに存在していたら Content-Encoding: gzip で cl.xml.gz の中身を返すようにしたいだけなんだけどなあ。cl.xml を CGI にでもすれば簡単にできるけど、それじゃあサーバに余計な負荷をかけてしまう。しょうがない、mod_rewrite を使うか。明日やってみよう。
これを利用すると、以下を実現できる。
HTTP リクエストヘッダ Accept-Encoding: gzip を送ってきているクライアントにはあらかじめ gzip で圧縮しておいたファイルを返し、Accept-Encoding: gzip を送ってきていないクライアントには gzip 圧縮していない生のファイルを返す。
- Content Negotiation を利用して圧縮ファイルを返す手法と mod_gzip/mod_deflate の違い
クライアントが送ってくるリクエストヘッダに応じて、圧縮したデータまたは生のデータを返す。一見 mod_gzip や mod_deflate がやっていることと同じように見えるが、違う。最大の違いは動的にファイルを圧縮するのではなく、最初から圧縮済みのファイルを置いておくことにある。動的に生成するページを圧縮したいなら、mod_gzip や mod_deflate のフィルタを使うのがよいだろう。しかし、静的ページならば、あらかじめ圧縮したファイルを返す方式でも良い。毎回圧縮をしないので、CPU 資源を節約することにもつながる。
また、mod_gzip や mod_deflate が使えないサーバでも圧縮を利用できるというメリットがある。現在 sonic64.com はさくらインターネットのホスティングサービスを利用している。残念なことに私の利用しているコースでは mod_gzip が使えない。Content Negotiation 以外の方法でコンテンツを圧縮して返そうと思ったら、すべてを CGI にしてコード中で圧縮するか、proxy を挟むくらいしか方法がない。
- Apache で Content Negotiation
Apache だと MultiViews を使う。コンテントネゴシエーション - Apache HTTP サーバ
http://httpd.apache.org/docs/2.1/content-negotiation.html
Multiviews
MultiViews はディレクトリ毎のオプションで、 httpd.confファイルの <Directory>, <Location>, <Files> セクション中や、(AllowOverride が適切な値に 設定されていると) .htaccess ファイルで Options ディレクティブによって設定することができます。 Options All は MultiViews をセットしないことに注意してください。明示的に その名前を書く必要があります。
MultiViews が有効になると、クライアントのリクエストに応じてサーバが最適なファイルを選択して返すようになる。
私の使っているサーバは httpd.conf で MuitiViews が有効になっているようなので試してみる。
まず、生のファイルと、圧縮して拡張子 .gz を付加したファイルを用意する。
[21:27:32][hiroaki@sonic64 ~/public_html/test]$ ls -l
-rw----r-- 1 hiroaki users 39751 Sep 13 21:18 cl.xml
-rw----r-- 1 hiroaki users 9785 Sep 13 21:18 cl.xml.gz
ブラウザからリクエスト。
http://sonic64.com/test/cl.xml
GET /test/cl.xml HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.10) Gecko/20050717 Firefox/1.0.6
Accept: text/xml, application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.x 200 OK
Date: Tue, 13 Sep 2005 12:29:40 GMT
Server: Apache/1.3.33 (Unix)
Last-Modified: Tue, 13 Sep 2005 12:18:00 GMT
Etag: "3449c4-9b47-4326c378"
Accept-Ranges: bytes
Content-Length: 39751
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Content-Type: application/xml
あれ? うまく Content-Negotiation されてないな。なんで? リクエストするファイル名を変えてみるか。cl.xml じゃなくて、cl をリクエストしてみよう。
http://sonic64.com/test/cl
GET /test/cl HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.10) Gecko/20050717 Firefox/1.0.6
Accept: text/xml, application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.x 200 OK
Date: Tue, 13 Sep 2005 12:35:24 GMT
Server: Apache/1.3.33 (Unix)
Content-Location: cl.xml.gz
Vary: negotiate,accept-encoding
TCN: choice
Last-Modified: Tue, 13 Sep 2005 12:18:02 GMT
Etag: "3449c8-2639-4326c37a;4326c74b"
Accept-Ranges: bytes
Content-Length: 9785
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Content-Type: application/xml
Content-Encoding: gzip
今度はうまくいった。Content-Location: cl.xml.gz と Vary: negotiate,accept-encoding が返されている。cl でリクエストすると Content-Negotiation が働き、cl.xml でリクエストしたときは Content-Negotiation がなされない。なんでだろう?
コンテントネゴシエーション - Apache HTTP サーバ
http://httpd.apache.org/docs/2.1/ja/content-negotiation.html
リンクに MIME タイプを使い続けたい (例えば foo.html)時は、言語拡張子は (エンコーディング拡張子もあればそれも含めて) MIME タイプ拡張子の右側になければなりません (例えば foo.html.en)。
上記ページの説明を見る限り、foo.MIME-TYPE.ENCODING のファイルは foo.MIME-TYPE でアクセスできるはずなんだけどなあ。 とりあえずなんで cl でないとダメなのかは後で調べてみよう。
- mod_mime が 拡張子 gz のファイルを Content-Encoding: gzip にしている
うまく Content-Negotiation がなされて gz ファイルが返されたとき、Content-Encoding: gzip も同時にサーバから返されていて、いわゆる「透過的な圧縮」になっている。mod_gzip や mod_deflate を使ったときと同じような、HTTP 転送データそのものを圧縮している状態だ。これはとくに Content-Negtiation とは関係ない。以下のように、cl.xml.gz を直接リクエストしたときも Content-Encoding: gzip がサーバから返されている。
http://sonic64.com/test/cl.xml.gz
GET /test/cl.xml.gz HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.10) Gecko/20050717 Firefox/1.0.6
Accept: text/xml, application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
HTTP/1.x 200 OK
Date: Tue, 13 Sep 2005 12:42:35 GMT
Server: Apache/1.3.33 (Unix)
Last-Modified: Tue, 13 Sep 2005 12:18:02 GMT
Etag: "3449c8-2639-4326c37a"
Accept-Ranges: bytes
Content-Length: 9785
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Content-Type: application/xml
Content-Encoding: gzip
Content-Encoding: gzip を返しているのは mod_mime だ。Apache のデフォルトでこの機能が働くように httpd.conf が記述されている。httpd.conf の以下の部分が該当箇所。もし設定されていなければ、.htaccess で設定する。
<IfModule mod_mime.c>
#
# AddEncoding allows you to have certain browsers (Mosaic/X 2.1+) uncompress
# information on the fly. Note: Not all browsers support this.
# Despite the name similarity, the following Add* directives have nothing
# to do with the FancyIndexing customization directives above.
#
AddEncoding x-compress Z
AddEncoding x-gzip gz tgz
ん? 今気づいたけど、拡張子 tgz も AddEncodding してるの? それだと tgz という拡張子を付けたファイルの場合、クライアント側で gzip を展開してしまうので gzip されたファイルを保存したいとユーザが考えたときなどに問題になるんじゃないのかな? Google で AddEncoding x-gzip gz tgz を検索するとヒット。
Re: .tar.gz -> .tgz
http://search.fml.org/mlarchives/fml-help/200107/msg397.html
gz が展開されてしまう、という問題ですよね?
あ、やっぱり。.tar.gz という拡張子なら Content-Encoding が付かないので問題ないが、.tgz にすると Content-Encoding が付いてしまうので展開さてしまうと。まあ tgz なんて拡張子を使わないようにするか、AddEncoding から tgz を消せば良いだけだから実害は少ないかな。
- cl.xml へのリクエストを cl.xml.gz にネゴシエーションしたい
うーん、なんだか今日はやりたいことができなかったなあ。cl.xml にリクエストがあったとき、Accept-Encoding に gzip があり、かつ cl.xml.gz がサーバに存在していたら Content-Encoding: gzip で cl.xml.gz の中身を返すようにしたいだけなんだけどなあ。cl.xml を CGI にでもすれば簡単にできるけど、それじゃあサーバに余計な負荷をかけてしまう。しょうがない、mod_rewrite を使うか。明日やってみよう。
Web サービスにリクエストを投げたときに、それがタイムアウトするまでの時間を延ばしたい。
データストアとして、データベース。たとえば、MS SQL Server。
データベースに接続しクエリを投げるためのデータプロバイダ。.NET で MS SQL Server 相手だったら System.Data.SqlClient。
データストアから取得したデータを加工するビジネスロジック。
ビジネスロジックを載せるための Web アプリケーションフレームワーク。たとえば、ASP.NET。
REST や SOAP のリクエストを受けるための HTTP サーバ。たとえば、IIS (Internet Infomation Server)。
REST や SOAP のリクエストを投げるための HTTP クライアント。.NET だったら System.Web.Services.Protocols.SoapHttpClientProtocol あたりがいろいろやってくれる。
これらの全てのタイムアウトを延ばさないと、Web サービスのタイムアウトを延ばすという目標は達成できない。以下、それぞれのタイムアウト設定方法のメモ。
http://www.microsoft.com/japan/msdn/library/ja/cpref/html/fr ...
今回設定が必要なもののうち、この SqlCommand.CommandTimeout が一番短い。短いといっても、30秒もあるわけだけど。
接続のタイムアウトを設定する
http://www.microsoft.com/resources/documentation/windowsserv ...
http://www.microsoft.com/japan/msdn/library/ja/cpref/html/fr ...
- 大人の事情
Web サービスのタイムアウトの時間を延ばすなんて、なんでそんなことする必要があるの? と思うかもしれない。でも、大人にはいろいろ事情がある。とにかく Web サービスがタイムアウトすることだけは避けなければならない。それが今回の課題。たとえクライアントからリクエストを投げた後、レスポンスが返ってくるまで2分とかかかったとしても。- Web サービスの背後で動いてるもの
Web サービスは単純だ。SOAP なり REST なりでリクエストを投げ、レスポンスが返ってくる。ただそれだけ。でも、普通はこれを実現するためにいくつかのミドルウェアが背後で動いている。データストアとして、データベース。たとえば、MS SQL Server。
データベースに接続しクエリを投げるためのデータプロバイダ。.NET で MS SQL Server 相手だったら System.Data.SqlClient。
データストアから取得したデータを加工するビジネスロジック。
ビジネスロジックを載せるための Web アプリケーションフレームワーク。たとえば、ASP.NET。
REST や SOAP のリクエストを受けるための HTTP サーバ。たとえば、IIS (Internet Infomation Server)。
REST や SOAP のリクエストを投げるための HTTP クライアント。.NET だったら System.Web.Services.Protocols.SoapHttpClientProtocol あたりがいろいろやってくれる。
これらの全てのタイムアウトを延ばさないと、Web サービスのタイムアウトを延ばすという目標は達成できない。以下、それぞれのタイムアウト設定方法のメモ。
- SQL Server のクエリ実行時間タイムアウト
SqlCommand.CommandTimeout プロパティ System.Data.SqlClient 名前空間http://www.microsoft.com/japan/msdn/library/ja/cpref/html/fr ...
プロパティ値
コマンド実行の待機時間 (秒)。既定値は 30 秒です。
解説
0 は、制限なしを示します。ただし、コマンド実行の試行が永久的に待機されるため、 CommandTimeout では使用しないでください。
今回設定が必要なもののうち、この SqlCommand.CommandTimeout が一番短い。短いといっても、30秒もあるわけだけど。
- IIS の HTTP レスポンス生成時のタイムアウト
IIS5 の場合。インターネット サービス マネージャを開き、Web サイトのプロパティを表示させる。「既定の Web サイト」しかなかったら、それを右クリックしてプロパティを表示させる。「Web サイト」タブの「接続」の「接続のタイムアウト」に秒単位で設定できる。デフォルトは900秒。接続のタイムアウトを設定する
http://www.microsoft.com/resources/documentation/windowsserv ...
- SOAP クライアントのタイムアウト
WebClientProtocol.Timeout プロパティhttp://www.microsoft.com/japan/msdn/library/ja/cpref/html/fr ...
プロパティ値
XML Web サービスを同期的に呼び出すときのタイムアウト (ミリ秒単位)。既定値は 100000 ミリ秒です。
解説
Timeout プロパティを Timeout.Infinite に設定すると、要求がタイムアウトしないことを示します。XML Web サービス クライアントが Timeout プロパティをタイムアウトなしに設定しても、Web サーバーが要求をサーバー側でタイムアウトにする場合があります。
Delphi5 + Indy 9.0.0.10 で HTTP Cookie を扱う方法。
2003-12-16 の「indy で HTTP の拡張ヘッダを扱う」とやってることは同じ。HTTP ヘッダを直読み・直書きしてるだけ。CookieManager クラスを使う方法もあるが、上記の方法が一番シンプルで簡単。ただし、上記方法は Cookie の有効期限やドメイン、path は一切無視しているので注意。それらを扱いたければ CookieManager を使う。
Google を検索したら ExtraHeaders を使う方法が見つかったが、Indy のバージョンが異なるのかプロパティが見つからなかった。
Indy Tips
http://www.monazilla.org/document/indytips.html
http://66.102.7.104/search?q=cache:aXMyyoN584QJ:www.studying ...
なぜか 403 Forbidden になってしまうので Google キャッシュ。
HTTP := TIdHTTP.Create(nil);
HTTP.Request.CustomHeaders.Add('Cookie: ' + cookie_string); // Set Cookie
cookie_string := HTTP.Response.RawHeaders.Values['Cookie']; // Get Cookie
2003-12-16 の「indy で HTTP の拡張ヘッダを扱う」とやってることは同じ。HTTP ヘッダを直読み・直書きしてるだけ。CookieManager クラスを使う方法もあるが、上記の方法が一番シンプルで簡単。ただし、上記方法は Cookie の有効期限やドメイン、path は一切無視しているので注意。それらを扱いたければ CookieManager を使う。
Google を検索したら ExtraHeaders を使う方法が見つかったが、Indy のバージョンが異なるのかプロパティが見つからなかった。
Indy Tips
http://www.monazilla.org/document/indytips.html
クッキーを添えて送信する
FIdHttp.Request.ExtraHeaders.Add('Cookie:NAME=HOGEHOGE'); // こんなかんじ
FIdHttp.Post(Url, Source, Res);
- Cookie の仕様
HTTP Cookieshttp://66.102.7.104/search?q=cache:aXMyyoN584QJ:www.studying ...
なぜか 403 Forbidden になってしまうので Google キャッシュ。
ieHTTPHeaders は HTTP リクエストとレスポンスを表示してくれる IE 用プラグイン。
2005-04-16 に書いた「Live Http headers - HTTP ヘッダ表示ツール」は Mozilla / Firefox 用だったが、こちらは IE 用。Firefox を使えない環境で役立つ。
blunck.info
http://www.blunck.info/iehttpheaders.html
Download ieHTTPHeaders - Just download and execute the installer
http://www.blunck.se/iehttpheaders/ieHTTPHeadersSetup.exe
すると IE のウインドウ下部に HTTP リクエストとレスポンスを表示するウインドウが出現する。
あとはブラウズすればリクエストとレスポンスが随時表示される。
HTTP リクエスト/レスポンス表示ウインドウから文字列をコピーすることもできる。CTRL + C を押すだけ。メニューには無いけどね。
表示サンプル。
Misc の Scroll during Capture にはチェックを入れておこう。チェックを入れておくと、スクロールバーを自動で一番下まで移動してくれる。unix の tail コマンドのように、常に最新の情報を表示し続けることができる。
ieHTTPHeaders は Live Http headers に比べると機能が少ない。正規表現でキャプチャ対象 URL を絞り込んだりできないし、別ウインドウで表示させることもできない。一度発行したリクエストを修正して再度リクエストし直すこともできない。あくまで簡易なキャプチャ用ツールと割り切って使うのが良いだろう。
2005-04-16 に書いた「Live Http headers - HTTP ヘッダ表示ツール」は Mozilla / Firefox 用だったが、こちらは IE 用。Firefox を使えない環境で役立つ。
- ieHTTPHeaders のダウンロードとインストール
オフィシャルサイトから setup.exe をダウンロードして実行するだけ。最新版は Version 1.6。インストールする際は IE をすべて閉じておいた方が無難。blunck.info
http://www.blunck.info/iehttpheaders.html
Download ieHTTPHeaders - Just download and execute the installer
http://www.blunck.se/iehttpheaders/ieHTTPHeadersSetup.exe
- ieHTTPHeaders の使い方
IE の上部メニューから、表示 (V) -> エクスプローラーバー (E) -> ieHTTPHeaders を選択する。すると IE のウインドウ下部に HTTP リクエストとレスポンスを表示するウインドウが出現する。
あとはブラウズすればリクエストとレスポンスが随時表示される。
HTTP リクエスト/レスポンス表示ウインドウから文字列をコピーすることもできる。CTRL + C を押すだけ。メニューには無いけどね。
表示サンプル。
GET http: //sonic64.hp.infoseek.co.jp/ HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/x-shockwave-flash, */*
Accept-Language: ja
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)
Host: sonic64.hp.infoseek.co.jp
Proxy-Connection: Keep-Alive
Cookie: ******=**********************; ***=************
HTTP/1.1 200 OK
Date: Wed, 27 Apr 2005 00:37:17 GMT
Server: Apache
Content-Length: 70461
Content-Type: text/html
X-Cache: MISS from ****************
Age: 0
Proxy-Connection: close
Via: HTTP/1.0 ********** (**********************)
- ieHTTPHeaders のオプション設定
HTTP リクエスト/レスポンス表示ウインドウで右クリックして Settings を選択すると設定画面に入れる。Misc の Scroll during Capture にはチェックを入れておこう。チェックを入れておくと、スクロールバーを自動で一番下まで移動してくれる。unix の tail コマンドのように、常に最新の情報を表示し続けることができる。
ieHTTPHeaders は Live Http headers に比べると機能が少ない。正規表現でキャプチャ対象 URL を絞り込んだりできないし、別ウインドウで表示させることもできない。一度発行したリクエストを修正して再度リクエストし直すこともできない。あくまで簡易なキャプチャ用ツールと割り切って使うのが良いだろう。
HTTP で xls や doc ファイルをダウンロードさせたとき、対応するアプリケーションがインストールされたマシンではブラウザ内で開いてしまう。そうさせたくない場合は、サーバで HTTP レスポンスヘッダ Content-disposition: を送出してやればよい。
既知の MIME タイプに対し [ファイルのダウンロード] ダイアログ ボックスを開く JP260519
http://support.microsoft.com/default.aspx?scid=kb;JA;260519
Content-disposition: はよく使うけど、IE に対してこんな効果があるとは知らなかった。
・・・と思ったけど、できない。html や png へのリンクを Shift を押しながらクリックしてもブラウザで普通に開いてしまう。昔 Shift キー押しながら大量のリンクを一気にクリックしてファイルを保存した記憶があるんだけどなあ。
普段メールクライアントとしてしか使っていない Netscape Communicator 4.8 を立ち上げて、Navigator で http://www.google.co.jp/ を Shift + クリックすると・・・ Save as ダイアログが出たー! これと勘違いしてたんだ。ちなみに Mozilla Firefox 0.8 ではできなかった。
既知の MIME タイプに対し [ファイルのダウンロード] ダイアログ ボックスを開く JP260519
http://support.microsoft.com/default.aspx?scid=kb;JA;260519
Web サーバーからドキュメントを提供する際、ユーザーが、そのドキュメントをブラウザで開かずに直接ディスクに保存できるよう、即座にプロンプトを表示したい場合があります。しかし Microsoft Word ("application/ms-word") などの MIME (Multipurpose Internet Mail Extensions) タイプに対する既定の動作は、Internet Explorer でドキュメントを開くようになっています。
content-disposition ヘッダを使って、この既定の動作を無効にすることができます。そのフォーマットは次のとおりです。
Content-disposition: attachment; filename=fname.ext
Content-disposition: はよく使うけど、IE に対してこんな効果があるとは知らなかった。
- クライアント側では Shift 押しながらリンククリック ・・・してもできません
クライアント側では Shift キーを押しながら該当ファイルへのリンクをクリックすれば良い。・・・と思ったけど、できない。html や png へのリンクを Shift を押しながらクリックしてもブラウザで普通に開いてしまう。昔 Shift キー押しながら大量のリンクを一気にクリックしてファイルを保存した記憶があるんだけどなあ。
- Netscape Navigator なら Shift + クリックが有効
しかし、Shift キーを押しながらクリックって、どのブラウザなら有効なんだろう? 大量のリンクをブラウザでクリックしまくって保存するなんて、右も左もわからなかった頃の所業だ。普通 wget なり Iria や Irvine 使って遊んでる間にダウンロードさせるよね。となると、20世紀の Netscape 全盛だった頃かな?普段メールクライアントとしてしか使っていない Netscape Communicator 4.8 を立ち上げて、Navigator で http://www.google.co.jp/ を Shift + クリックすると・・・ Save as ダイアログが出たー! これと勘違いしてたんだ。ちなみに Mozilla Firefox 0.8 ではできなかった。
- IE のクライアント側での対処
以下の方法ならクライアント側、というか IE 側で対処可能。でも、私の扱う要件ではそれができるクライアントや環境だけじゃないから、やっぱりサーバ側での対処が必要。なお、サーバー側から Content-Disposition ヘッダーを送信せずに、クライアント側の設定によりダイアログを表示させる場合には、以下の手順を実行します。
クライアント側のフォルダオプションで設定する方法:1. [マイ コンピュータ] をダブルクリックします。
2. [ツール] メニューの [フォルダ オプション] をクリックします。
3. [ファイル タイプ] タブをクリックします。
4. [登録されているファイル タイプ] の下から、ダイアログを表示させたいファイルの種類を選択して、[詳細設定] ボタンをクリックします。
5. [ダウンロード後に開く確認をする] (または [ダウンロード後に常に開くように設定する]) のチェックボックスをオフにし、[OK] をクリックします。
6. [登録されているファイル タイプ] の下からファイルの種類を選択して、[詳細設定] ボタンをクリックします。
※ ただし、ファイルの種類によっては上記の手順で設定できないものもあります。
Port80 Software: Fortune 1000 HTTP Compression Survey
http://www.port80software.com/surveys/top1000compression/#ch ...
以下をチェックしてくれる。
・指定した url のサイトでは http 圧縮がされているかどうか
・圧縮されているならフォーマットは何か。deflate なのか gzip なのか。
・圧縮されてないときは、仮に圧縮したらどの程度のサイズになり、どれくらい高速化できるか
Port 80 に telnet して Accept-encoding: gzip 付きのリクエストを送ってレスポンスを見ればいいんだけど、フォームから確認できるのは簡単でいい。
Webビジネスコンサルタントのネタ帳「パケット料金が気になるなら圧縮転送を試すべき」から。
http://neta.ywcafe.net/000107.html
知らなかった。Content-Encoding に対応してるなんて、今の i-mode って高機能なんだなあ。
追記。i-mode が Content-Encoding 対応というのは誤報だった模様。
http://www.port80software.com/tools/compresscheck.asp?url=ht ...
http://www.port80software.com/surveys/top1000compression/#ch ...
以下をチェックしてくれる。
・指定した url のサイトでは http 圧縮がされているかどうか
・圧縮されているならフォーマットは何か。deflate なのか gzip なのか。
・圧縮されてないときは、仮に圧縮したらどの程度のサイズになり、どれくらい高速化できるか
Port 80 に telnet して Accept-encoding: gzip 付きのリクエストを送ってレスポンスを見ればいいんだけど、フォームから確認できるのは簡単でいい。
Webビジネスコンサルタントのネタ帳「パケット料金が気になるなら圧縮転送を試すべき」から。
http://neta.ywcafe.net/000107.html
そしてiモードなどの携帯電話用ブラウザも、とっくの昔から対応している。
知らなかった。Content-Encoding に対応してるなんて、今の i-mode って高機能なんだなあ。
追記。i-mode が Content-Encoding 対応というのは誤報だった模様。
- 2ちゃんねるを救ったのも gzip だったなあ
そういえば、2001年8月の 2ch.net の閉鎖の危機、いわゆる「8月危機」を救ったのも gzip ということになってたなあ。あのときの2ちゃんねるは確か160Mbps というすさまじい転送量に悩んでいた。なんとかして転送量を減らしたいが、ホスティング先との契約だか手間だかの問題で、mod_gzip なんてインストールできないとのことだった。結局、read.cgi 改良チームは 直接 gzip コマンドを呼び出して圧縮させるというすごい手法を選んだ。そのときの2ちゃんねるは転送量に悩んではいたが CPU は余ってたのでこの方法でも劇的な効果を上げていた。- Landscape は gzip 転送されてるのかな
やってみよう。http://www.port80software.com/tools/compresscheck.asp?url=ht ...
Compression status: Uncompressedあ、生なんだ。infoseek は CPU 負荷を気にしてるのかな。
http の Vary レスポンスヘッダはコンテント・ネゴシエーションの基準を UserAgent に通知するために使う。
たとえば、クライアントが送信する User-Agent リクエストヘッダによって返す内容を変えているサーバがあるとする。この場合、サーバから返されたデータを proxy が素直にキャッシュしてしまうと、別の User-Agent が proxy にアクセスしたときにサーバが意図しないデータがクライアントに返されてしまう。
レスポンスヘッダに Vary: User-Agent が指定されていれば、proxy は User-Agent によって内容が変わることを知ることができるので、キャッシュをしないとか、User-Agent 毎に異なるキャッシュを保持する、といった対応ができる。
RFC2616 HTTP/1.1: ヘッダフィールド定義 13.6 ネゴシエートされたレスポンスのキャッシング
http://www.studyinghttp.net/rfc_ja/2616/sec13.html#sec13.6
RFC2616 HTTP/1.1: ヘッダフィールド定義 14.44 Vary
http://www.studyinghttp.net/rfc_ja/2616/sec14.html#sec14.44
たとえば、クライアントが送信する User-Agent リクエストヘッダによって返す内容を変えているサーバがあるとする。この場合、サーバから返されたデータを proxy が素直にキャッシュしてしまうと、別の User-Agent が proxy にアクセスしたときにサーバが意図しないデータがクライアントに返されてしまう。
レスポンスヘッダに Vary: User-Agent が指定されていれば、proxy は User-Agent によって内容が変わることを知ることができるので、キャッシュをしないとか、User-Agent 毎に異なるキャッシュを保持する、といった対応ができる。
RFC2616 HTTP/1.1: ヘッダフィールド定義 13.6 ネゴシエートされたレスポンスのキャッシング
http://www.studyinghttp.net/rfc_ja/2616/sec13.html#sec13.6
RFC2616 HTTP/1.1: ヘッダフィールド定義 14.44 Vary
http://www.studyinghttp.net/rfc_ja/2616/sec14.html#sec14.44
Vary フィールド値は、そのレスポンスが新鮮である{fresh} 間、キャッシュが再検証無しにそれに続くリクエストに対するレスポンスとして使ってよいかどうかを、完全に決定するためのリクエストヘッダフィールドのセットを示す。キャッシュできない、あるいは新鮮でなくなった{stale} レスポンスの場合、Vary フィールド値はユーザエージェントにその表現を選択するために使われた基準{criteria} について通知するために使われる。 "*" という Vary フィールド値は、キャッシュはこのレスポンスが適切な表現であるかどうかをそれに続くのリクエストのリクエストヘッダからは決定できない、という事を意味する。キャッシュにおける Vary ヘッダフィールドの使い方については section 13.6 参照。
CGI からファイルをダウンロードさせたい。これだけなら非常に単純で簡単なのだが、今回は一度に複数のファイルをレスポンスとして返したい。複数のファイルを tar などでまとめるという方法は使えない。
単数のファイルだったら適切な Content-Type ヘッダを出力して、CRLF を2つ出力した後、ボディ部分を出力してやればいい。実に簡単だ。でも、返すデータが複数の場合はどんな http response を返せばいいのか調べてみた。
すぐに思いついた先行事例は、フォームによるファイルのアップロードだ。フォームによるファイルのアップロードでは、複数ファイル同時送信なんて当たり前だ。たとえば、Yahoo Mail などで添付ファイルを付けるとき、一気に付けることができる。
そのときの Content-Type って何を使っているのかを調べればいい。Google で http ファイル アップロード content-type で検索。すると Content-type: multipart/form-data というものを使っていることが判明。なるほど、フォームから送るときはこうするんだな。
次に、思いついた先行事例はメールだ。メールで複数の添付ファイルを付けたときって、どんな Content-Type なのかを調べてみた。Netscape Messanger の sent ディレクトリから、過去に自分が送ったメールのうち、複数の添付ファイルをがあるものを探してヘッダを見てみた。
http://www.atmarkit.co.jp/fnetwork/rensai/netpro04/netpro01. ...
RFC 2616によると、以下のように定義されていた。multipart/mixed はかなりローレベルな型なんだな。
http://www.studyinghttp.net/rfc_ja/2616/sec3.html
単数のファイルだったら適切な Content-Type ヘッダを出力して、CRLF を2つ出力した後、ボディ部分を出力してやればいい。実に簡単だ。でも、返すデータが複数の場合はどんな http response を返せばいいのか調べてみた。
- 複数ファイルを返すための Content-Type
結局 Content-Type 次第だと思う。適切な Content-Type とそれを理解する UserAgent であれば、複数ファイルだろうがなんだろうが返してあげられるはず。複数ファイルのまとめ送りに適した Content-Type がきっとあるはずだ。それを調べてみる。すぐに思いついた先行事例は、フォームによるファイルのアップロードだ。フォームによるファイルのアップロードでは、複数ファイル同時送信なんて当たり前だ。たとえば、Yahoo Mail などで添付ファイルを付けるとき、一気に付けることができる。
そのときの Content-Type って何を使っているのかを調べればいい。Google で http ファイル アップロード content-type で検索。すると Content-type: multipart/form-data というものを使っていることが判明。なるほど、フォームから送るときはこうするんだな。
次に、思いついた先行事例はメールだ。メールで複数の添付ファイルを付けたときって、どんな Content-Type なのかを調べてみた。Netscape Messanger の sent ディレクトリから、過去に自分が送ったメールのうち、複数の添付ファイルをがあるものを探してヘッダを見てみた。
Content-Type: multipart/mixed; boundary="------------546DD04699AFAF892A7D6D3F"と書いてある。なるほど、multipart/mixed でいいんだな。boundary はデリミタだな。
- 結論 HTTP レスポンスで複数ファイルを返すには multipart/form-data か multipart/mixed
サーバは Content-type: multipart/form-data か Content-Type: multipart/mixed で送信する。Content-Type: multipart/mixed が汎用的でいいだろう。で、それを理解するクライアントで受信させる。Internet Explorer や Mozilla などでは理解してくれないかもしれないが、今回は専用のクライアントを使用可能なので問題ない。- 参考
連載:インターネット・プロトコル詳説(4)MIME(Multipurpose Internet Mail Extensions)〜後編http://www.atmarkit.co.jp/fnetwork/rensai/netpro04/netpro01. ...
RFC 2616によると、以下のように定義されていた。multipart/mixed はかなりローレベルな型なんだな。
http://www.studyinghttp.net/rfc_ja/2616/sec3.html
3.7.2 マルチパートタイプ
MIME は、一つのメッセージボディの中に複数のエンティティをカプセル化する "multipart" タイプをいくつか供給する。すべてのマルチパートタイプは RFC 2046 [40] の section 5.1.1 で定義されているように、共通のシンタックスを共有し、メディアタイプ値の一部として境界パラメータ{boundary parameter} を含めなければならない。メッセージボディは、それ自身プロトコル要素の一部であり、それゆえに、body-parts 間の行末を表すためには CRLF のみを使用しなければならない。 RFC 2046 と異なり、どのマルチパートメッセージのエピローグ{epilogue} も空でなければならない。そのため、HTTP アプリケーションは (たとえ元のマルチパートがエピローグを含んでいても) エピローグを転送してはならない。これらの制限は、最後のマルチパートの境界線によってメッセージボディの "終端" を示せるように、マルチパートのメッセージボディに自己限界性質{the self-delimiting nature} を持たせるために存在する。
一般的に、HTTP はマルチパートメッセージボディを他のメディアタイプとは区別無く、すなわち単なる付加物{payload} として扱う。ただ一つ例外は、206 (Partial Content) レスポンス中に現れる時の "multipart/byteranges" タイプ (appendix 19.2) であり、その場合 section 13.5.4 や section 14.16 で表されるような、いくつかの HTTP キャッシュメカニズムによって解釈されるだろう。その他のすべての場合では、HTTP ユーザエージェントは、MIME ユーザエージェントがマルチパートタイプの受けとる時と同じ、または似たような振る舞いをすべきである。マルチパートメッセージボディの各々のボディ部分中の MIME ヘッダフィールドは、HTTP ではそれらの MIME セマンティクスによる定義以上にどんな意味も持たない。
一般的には、HTTP ユーザエージェントは、MIME ユーザエージェントがマルチパートタイプの受けとる時と同じ、または似たような振る舞いをすべきである。アプリケーションが認識できないマルチパートサブタイプを受け取った場合は、それを "multipart/mixed" に相当するものとして扱わなければならない。
注意: "multipart/form-data" タイプは、RFC 1867 [15] で表されるように、特に POST リクエストメソッド経由で処理するのに合ったフォームデータを転送するために特別に定義されている。
Set-Cookie2 の方が指定できるパラメータが多いようだ。
Set-Cookie と Set-Cookie2 のどちらを使ったらよいかだが、現在のところは Set-Cookie でいいと思う。
2003年5月1日(木曜日) のえび日記 > 「Set-Cookie2」
http://altba.com/bakera/hatomaru.aspx/ebi/topic/555
連載:インターネット・プロトコル詳説(2)HTTP(Hyper Text Transfer Protocol)〜後編
http://www.atmarkit.co.jp/fnetwork/rensai/netpro02/netpro01. ...
Set-Cookie と Set-Cookie2 のどちらを使ったらよいかだが、現在のところは Set-Cookie でいいと思う。
2003年5月1日(木曜日) のえび日記 > 「Set-Cookie2」
http://altba.com/bakera/hatomaru.aspx/ebi/topic/555
連載:インターネット・プロトコル詳説(2)HTTP(Hyper Text Transfer Protocol)〜後編
http://www.atmarkit.co.jp/fnetwork/rensai/netpro02/netpro01. ...
http クライアントや proxy にキャッシングしないように指示するには、http レスポンスヘッダに以下を組み込む。
proxy を経由した場合に情報が失われるおそれがあるので、html にも以下の情報を入れておくのが望ましい。Pragma が HTTP 1.0 用、Cache-Control が HTTP 1.1 用。両方入れておけば完璧。
HTTP/1.1: ヘッダフィールド定義
http://www.studyinghttp.net/rfc_ja/2616/sec14.html#sec14.9
Cache-Control: no-cache
Pragma: no-cache
proxy を経由した場合に情報が失われるおそれがあるので、html にも以下の情報を入れておくのが望ましい。Pragma が HTTP 1.0 用、Cache-Control が HTTP 1.1 用。両方入れておけば完璧。
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
HTTP/1.1: ヘッダフィールド定義
http://www.studyinghttp.net/rfc_ja/2616/sec14.html#sec14.9
14.9 Cache-Control
Cache-Control 一般ヘッダフィールドは、リクエスト/レスポンス連鎖上のすべてのキャッシングメカニズムが従わなければならない指示を記述するために使用される。キャッシュがリクエストやレスポンスに不利になるように干渉させないような振る舞いを指定する。これらの指示は、常にデフォルトのキャッシングアルゴリズムを上書きするものである。
CONNECT メソッドを使った外部とのトンネリング。
- 172.16.4.1 の場合
$ telnet 172.16.4.1 17216
Trying 172.16.4.1...
Connected to 172.16.4.1.
Escape character is '^]'.
CONNECT www.google.com:443 HTTP/1.1
Host: www.google.com
HTTP/1.0 200 Connection established
Connection closed by foreign host.
- 172.16.4.2 の場合
$ telnet 172.16.4.2 17242
Trying 172.16.4.2...
Connected to 10.2.4.2.
Escape character is '^]'.
CONNECT www.google.com:443 HTTP/1.1
Host: www.google.com
HTTP/1.1 200 Connection established
Date: Tue, 15 Apr 2003 11:10:45 GMT
Age: 0
Connection: close
Via: HTTP/1.0 hostname (Traffic-Server/4.0.19 [c sSf ])
Connection closed by foreign host.
OPTIONS を使うと、そのサーバで使用可能なメソッドを取得できる。
$ telnet 10.3.31.90 80
Trying 10.3.31.90...
Connected to 10.3.31.90.
Escape character is '^]'.
OPTIONS * HTTP/1.1
Host: localhost
HTTP/1.1 200 OK
Date: Wed, 16 Apr 2003 02:35:15 GMT
Server: Apache/1.3.26 (Unix) PHP/4.2.1
Content-Length: 0
Allow: GET, HEAD, OPTIONS, TRACE
Connection closed by foreign host.
Basic 認証の入力画面を表示させたくないが、Basic 認証を使いたい。どうすればいいか。という相談を受ける。
Basic 認証は、BASE 64エンコードされた認証情報を http ヘッダに埋め込んでいるだけ。
最初から Basic 認証用のヘッダを http ヘッダに記述しておけば、401 を返されることなくコンテンツにアクセスできるはず。以下、調査資料。
RFC2671
ftp://ftp.isi.edu/in-notes/rfc2617.txt
インターネット興隆の立役者「HTTP〜後編」
http://www.atmarkit.co.jp/fnetwork/rensai/netpro02/netpro01. ...
Google で rfc basic 認証 http ヘッダを検索したらヒット。
CGI 中で Realm を定義する方法
http://mtlab.ecn.fpu.ac.jp/WSM_1997/970427212810.html
問題は、ShellExecute などでアプリケーションから IE を起動するとき、どうやって http ヘッダを指定すればよいか、というところ。起動時引数などでリクエストヘッダを指定できれば楽なのだが。IE のバージョンにも依存しそうな予感。
・・・と思ったけど、http://id:password@host/ という形式でリクエストすればいいんじゃないか、と思った。試したらできた。セキュリティ的にはあまり良くないが、簡単ではある。まあ Basic 認証なんて通信内容見られたら全部まるわかりなんだし、使う場所によってはこれで十分でしょう。
Basic 認証は、BASE 64エンコードされた認証情報を http ヘッダに埋め込んでいるだけ。
最初から Basic 認証用のヘッダを http ヘッダに記述しておけば、401 を返されることなくコンテンツにアクセスできるはず。以下、調査資料。
RFC2671
ftp://ftp.isi.edu/in-notes/rfc2617.txt
インターネット興隆の立役者「HTTP〜後編」
http://www.atmarkit.co.jp/fnetwork/rensai/netpro02/netpro01. ...
Google で rfc basic 認証 http ヘッダを検索したらヒット。
CGI 中で Realm を定義する方法
http://mtlab.ecn.fpu.ac.jp/WSM_1997/970427212810.html
問題は、ShellExecute などでアプリケーションから IE を起動するとき、どうやって http ヘッダを指定すればよいか、というところ。起動時引数などでリクエストヘッダを指定できれば楽なのだが。IE のバージョンにも依存しそうな予感。
・・・と思ったけど、http://id:password@host/ という形式でリクエストすればいいんじゃないか、と思った。試したらできた。セキュリティ的にはあまり良くないが、簡単ではある。まあ Basic 認証なんて通信内容見られたら全部まるわかりなんだし、使う場所によってはこれで十分でしょう。