岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

Rack::Request#[]でUTF-8文字列が返ってくる件

$ ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-linux]
$ gem list | grep rack
rack (1.3.0)

という環境で:

require 'rack'

class MyApp
  def call(env)
    [200, {"Content-Type" => "text/plain"}, [Rack::Request.new(env)['hoge'].encoding.to_s]]
  end
end

Rack::Handler::WEBrick.run(MyApp.new, :Port => 3000)

を実行し、http://localhost:3000/?hoge=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A (クエリストリングは「あいうえお」をUTF-8エンコードしたもの)にアクセスしたら「UTF-8」と表示された。

「あれ、自動判別してるのか」と思い、http://localhost:3000/?hoge=%A4%A2%A4%A4%A4%A6%A4%A8%A4%AA (同じくEUC-JPでエンコード)にアクセスしても、やっぱり「UTF-8」。ためしに「Encoding.default_external = 'EUC-JP'」としても変わらなかった。

Rack::Utils#unescape が原因

解せないので Rack::Request のコードを追いかけたところ、Rack::Utils#unescape が原因と分かった。

Rack::Utils#unescape では、下記のように、URI.decode_www_form_component が第2引数なしで呼ばれている。

def unescape(s)
  URI.decode_www_form_component(s)
end

このように第2引数を省略すると、デフォルトの「Encoding::UTF_8」がデコードに使われ、デコードされた文字列のエンコーディングも「Encoding::UTF_8」と設定される。これが原因。

これ以上は追いかけていないが、この挙動は意図せざるものだと思う。思いたい。

Rack::Request#[] の戻り値のエンコーディングを仮定してはならない

挙動がどうであれ、Rackアプリケーションの作者は、Rack::Request#[] の戻り値のエンコーディングを仮定してはならない。現時点では仕様が明文化されていないからだ。

仮定せず、アプリケーション側で扱いたいエンコーディングに変換し、妥当性をチェックする必要がある。この辺は次の日記に書くつもり。