ActionWebServiceの引数にXML特殊文字を2個続けて渡すとエラーが発生する問題、の続き
の続き。原因がわかった。AWSのバージョンは1.1.6。ActionPackのバージョンは1.12.5。
エラーが発生している箇所は、ActionPack内のcgi_methods.rbで定義されている CGIMethods.parse_request_parameters(params) メソッド。以下がそのソース。
# Returns the request (POST/GET) parameters in a parsed form where pairs such as "customer[address][street]" / # "Somewhere cool!" are translated into a full hash hierarchy, like # { "customer" => { "address" => { "street" => "Somewhere cool!" } } } def CGIMethods.parse_request_parameters(params) parsed_params = {} for key, value in params value = [value] if key =~ /.*\[\]$/ unless key.include?('[') # ← ★★★★ここでエラー! key==nil ★★★★ # much faster to test for the most common case first (GET) # and avoid the call to build_deep_hash parsed_params[key] = get_typed_value(value[0]) else build_deep_hash(get_typed_value(value[0]), parsed_params, get_levels(key)) end end parsed_params end
これを呼んでいるのは、cgi_process.rbのrequest_parameterメソッドで、
CGIMethods.parse_request_parameters(@cgi.params)
@cgi.paramsが返すハッシュは、CGI::parseによって構築される。
# Parse an HTTP query string into a hash of key=>value pairs. # # params = CGI::parse("query_string") # # {"name1" => ["value1", "value2", ...], # # "name2" => ["value1", "value2", ...], ... } # def CGI::parse(query) params = Hash.new([].freeze) query.split(/[&;]/n).each do |pairs| # ← ココ。&か;でsplitする。 key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) } if params.has_key?(key) params[key].push(value) else params[key] = [value] end end params end
CGI::parseは、POSTのbodyに文字実体参照を2個連続で含むXMLが渡されると、以下のようにパースする。
$ irb >> CGI::parse("<?xml version=\"1.0\"><body><<</body>") => {"<?xml version"=>[""1.0"><body>"], "</body>"=>[nil], nil=>[nil], "lt"=>[nil, nil]}
最初に&と;でsplitするので、
<< ↓ nil=>[nil], "lt"=>[nil, nil]
とパースされてしまう。「;&」で、キーも値もnilの要素ができる。
これがkey.include?がエラーになる原因。試しにSOAPの引数に、「;<」のような「;+特殊文字」を渡しても、エラーになることが確認できた。
とりあえず安直に、
for key, value in params next if key.nil? # ← この行を追加 value = [value] if key =~ /.*\[\]$/ unless key.include?('[') : :
とすると、エラーは出なくなった。
でも、そもそもSOAPリクエストに対して、parse_request_parametersみたいな処理は不要なはず。最新版で修正されてそうな予感がする。
(追記) rails-1.2.3で試したところ、エラーは発生しなくなっているようでした。