« [Ajax tips] XMLHttpRequest と If-Modified-Since | メイン | [RNA] windows+IISで不具合報告 »

September 19, 2005

[RNA] HTTPコード301 によるRSS移転に対応

(かなり)久しぶりにRNA Nightly版をupdate。話題としては少し古いが、HTTPステータス「301 Moved Permanently」への対応。

【内容】
SiteListに登録されたRSSをGETする際、レスポンスが「301 Moved Permanently」であった場合には、SiteListに登録されたURLを書き換えるようにした。

【効果】
ブログなどのサイト運営者側がRSSの移転(=URLの変更)を行う際に、「301」で新URLにリダイレクトしていれば、RNA側が自動的にSiteListを新URLに書き換える。これにより、ユーザーが意図することなくSiteListの更新が行われ、(RSS業界ではよく問題になる)無駄なトラフィックおよびWebサーバー負荷が抑えられ、旧環境を早期に撤去(ホスティング費や運営費の問題に貢献)することができる。

背景と実装方法について以下にメモ。

【背景】
RSS移転時の措置についての議論より。
「BloglinesのForumをタラタラと ・・・ フィードのURLを変更した時に301 Moved Permanently(301とは書いてないけど多分)でリダイレクトしてやると、自動的に新しいURLに変更される」
「Bloglines では、301 の場合データベースの URL も変更。以降は新しい URL のみにアクセスします。302 の場合、データベースは更新せず、古いほうに継続してアクセスします」
「301 Moved Permanentlyを出すようにすれば、一部のRSSリーダでは自動的に購読を切り替えてくれる」
「古いRSSへのアクセスに301を返しているのだけど、自動的に移転処理をしてくれないRSSリーダーが結構ある」

【実装方法】
RNAでは、RSSの取得にLWP::UserAgentを使用している。LWP::UserAgentのrequest()では、「301」や「302」が返されると自動的にリダイレクトの処理をしてくれる。

LWP::UserAgent使用時に、「301」リダイレクト元を取得する方法としては次の2つが考えられる。
a) リクエストにはsimple_request()を使用し、1回ずつレスポンスを見て適切な処理(リダイレクト処理、SiteList更新、その他)を行う。
b) リクエストにはrequest()を使用する。得られたレスポンスから、HTTP::Responseモジュールのprevious()メソッドでレスポンスチェーンをさかのぼり、コードが「301」のレスポンスがある場合にSiteList更新を行う。

RNAでは、既存プログラムでrequest()を使用しているため、b)を採用。
初回のレスポンスが301の場合に、連続した301レスポンスの最後のLocationヘッダに記述されたURLを、真のURLとしてSiteListに登録している。

request() や previous()の動きは以下のようなプログラムを用いて簡単に検証することが出来る。

===================================
プログラムソース

[http://example/301_1.cgi]
-----------------------------------------------
#!/usr/bin/perl

print "Status: 301 Moved Permanently\n";
print "Location: http://example/301_2.cgi\n\n";
-----------------------------------------------

[http://example/301_2.cgi]
-----------------------------------------------
#!/usr/bin/perl

print "Status: 301 Moved Permanently\n";
print "Location: http://example/302_1.cgi\n\n";
-----------------------------------------------

[http://example/302_1.cgi]
-----------------------------------------------
#!/usr/bin/perl

print "Location: http://example/index.html\n\n";
-----------------------------------------------

[301test.pl]
-----------------------------------------------
#!/usr/bin/perl

use LWP::UserAgent;

my $uri = 'http://example/301_1.cgi';

my $req = HTTP::Request->new(GET => $uri);
my $ua = LWP::UserAgent->new;
$ua->parse_head(0);
my $res = $ua->request($req);

my @a = ();
while ($res) {
unshift(@a, $res);
$res = $res->previous();
}

print "Response chain:\n";
foreach my $r (@a) {
print "$uri\n";

if($r->code == 301) {
$uri = $r->header('Location');
}
else {
last;
}
}

print "\nTrue Location: $uri\n";
-----------------------------------------------

===================================
実行例

$ perl ./301test.pl
Response chain:
http://example/301_1.cgi
http://example/301_2.cgi
http://example/302_1.cgi

True Location: http://example/302_1.cgi
===================================


【余談】
RFC2616では、Locationヘッダに記述されるのは absoluteURI とある。しかし、UserAgent.pmを見ると「Some servers erroneously return a relative URL for redirects」ということもあるらしい。

投稿者 msano : September 19, 2005 12:01 AM

トラックバック

このエントリーのトラックバックURL:
http://www.semblog.org/mt3/mt-tb.cgi/297

コメント

おお,活動されている.
どんなに間があいてもすぐトラックできる(されてしまう)のが
RSSのすばらしいところですなあ.

投稿者 i2k : September 19, 2005 06:30 PM

逆にRSSが無くなると、ネットから消えてしまったかのように扱われる、ようになるかもしれません。

投稿者 msano : October 2, 2005 11:54 PM

いやもうそこまで来てるでしょう.

投稿者 i2k : October 3, 2005 04:37 AM

コメントしてください




保存しますか?