2006年05月09日 (火)

homepage.mac.com が URL エンコードに関して仕様変更の模様

.Mac の homepage のサーバ homepage.mac.com が URL エンコード(%-escape)が付いている URL の解釈について仕様変更されたようです。これに伴い .Mac スペースに設置されている日本語など非 Ascii 文字を扱う全ての bookmarklet に修正が必要になりました。キーワードは "SC_INTERNAL_SERVER_ERROR" です。

.Mac と関係ない方にも忠告しておきます。URI の文字列を作るために JavaScript の escape 関数を単純に用いるのは厳密には間違いです。

  1. 最もスマートな対応方法を追記

仕様変更の背景

URI の %-escape の仕様

URL というか URI の中で日本語や記号などの特殊な文字を使用する場合、その文字の各バイトを %-escape といって % で始まる 16 進数の文字コードで表現する必要があります。例えば "a/あ" という UTF-8 の文字列は "a%2F%E3%81%82" と表現しなければなりません。これは RFC 3986 で規定されていて、それによると

pct-encoded = "%" HEXDIG HEXDIG

2006年5月9日の RFC 3986 から引用

と %-escape は "%" に続いて二つの HEXDIG というものから構成されています。HEXDIGRFC 2234 で規定されていて数字または "A" から "F" までのアルファベットです。

JavaScript の仕様

ECMA-262 の「B 互換性 (Compatibility)」によると

置き換えられる文字のコードポイント値が 0xFF を超える場合、 %uxxxx 形式の 4 桁のエスケープシーケンスが用いられる。

2006年5月9日の B 互換性 (Compatibility) から引用

とあります。つまり日本語などのマルチバイト文字は %uxxxx という形式にエスケープされます。

二つの仕様の問題点

従来 JavaScript の escape 関数は URI の %-escape と同じような結果をもたらすために URI に記載する文字を形成するための道具として用いられてきました。例えば URI の中に埋め込む文字列 "a/あ" を escape 関数に通して用いたりしました。その変換は ECMA-262 の規定に基づいて行われます。日本語の "あ" は "%u3042" となります。

しかし、RFC の URI の規定では "%" の後に続くのは HEXDIG です。"u" は数字でもなければ "A" から "F" までのアルファベットでもありません。したがって URI に埋め込む文字列を作成するために JavaScript の escape 関数を一回だけそのまま適用するのは厳密には間違いなのでした。

私も .Mac の今回の仕様変更がなされるまでこのことに気付いていませんでした。

仕様変更の内容

.Mac のサーバ homepage.mac.com は URI の %-escape について厳密にチェックするようになりました。そして %-escape に合わない文字列が含まれているとこれをエラーとして "SC_INTERNAL_SERVER_ERROR" とだけ書かれたテキストを返すようになりました。このテキストを返す場合の HTTP ステータスコードは 500 Internal Server Error です。

これはエラーの表現としてはいささか不適切ではあります。エラーなのはサーバではなくリクエストの方なので 400 Bad Request を用いるべきでしょう。

Bookmaklet への影響

今回の仕様変更に伴い、JavaScript の escape 関数を単純に用いて URI やその中の QueryString を構成している Bookmarklet を設置している人はその Bookmarklet にマルチバイト文字を与えた場合 "SC_INTERNAL_SERVER_ERROR" となってうまく動作しなくなってしまいます。

Bookmarklet 作者へのアドバイス

しかしご安心ください。あなたの Bookmarklet は(ほとんどの場合)少しの修正で元のように動作するようになります。次の手順で修正してください。

  1. Bookmarklet が開く HTML で用いられる JavaScript の修正。

    その JavaScript の中で URI あるいはその中の文字列を unescape しているところがあるはずです。仮に unescape(title) というコードだとしたら、ここを unescape(unescape(title)) と unescape を二回行うように変えます。古いブラウザを切り捨ててもよいのでしたら decodeURIComponent(title) とするのがベストです。

    もちろん修正した JavaScript はアップロードしておきます。

  2. Bookmark 中の JavaScript の修正。

    Bookmarklet の中の JavaScript は逆に escape を二回行うようにします。例えば escape(document.title) としているところを escape(escape(document.title)) とします。前のステップで decodeURIComponent を用いたのならばencodeURIComponent(document.title) としてください。

  3. JavaScript を含むファイルをリロードする。

    ブラウザのキャッシュが効いているので、Bookmarklet が開く HTML に直接 JavaScript が書いてある場合はその HTML を、その HTML から js ファイルをロードしている場合はその js ファイルをリロードします。これによってキャッシュが修正済みのものに置き換えられます。

おまけの効果

この修正により Safari にある Bookmarklet 中での escape 関数の変な動作を回避することができます。

今回の修正を自分でも行って Safari でテストしてみたところ、Safari は Bookmarklet 中にある escape 関数がそのような動作をしているのではなく、escape 関数の結果を URI に埋め込む際に間違った URI にならないように修正していると解釈できる動作になっていました。escape を二回施せば "%" の後に "u" が続く RFC での URI の規定に反するものにならないので、したがって Safari での変な動作は回避されます。

また、encodeURIComponent と decodeURIComponent を用いると ECMA-262 でも定義されていて RFC 3986 および 2234 にも適合した仕様的に完全なソリューションになります。ただし古いブラウザはサポートしていないものと思われます。

つまり Safari は推定無罪ということです。Ssafari 君ごめんなさい。<(_ _)>


Posted: 16:11    | Comment | Trackback


以下、類似エントリです。