From e00ab5701f413e37b0848a7dca8c7ad3d3a92a84 Mon Sep 17 00:00:00 2001 From: Serge Balyuk Date: Wed, 23 Jun 2010 23:38:40 +0300 Subject: [PATCH] Added handling for encoding of form POST data (ruby 1.9) --- lib/rack/request.rb | 10 +++++++- lib/rack/utils.rb | 8 +++++- test/spec_request.rb | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/spec_utils.rb | 8 +++++++ 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/lib/rack/request.rb b/lib/rack/request.rb index 673caa6..1feda3e 100644 --- a/lib/rack/request.rb +++ b/lib/rack/request.rb @@ -141,12 +141,14 @@ module Rack def POST if @env["rack.input"].nil? raise "Missing rack.input" - elsif @env["rack.request.form_input"].eql? @env["rack.input"] + elsif @env["rack.request.form_input"].eql?(@env["rack.input"]) && @env["rack.request.form_input_encoding"] == input_encoding @env["rack.request.form_hash"] elsif form_data? || parseable_data? @env["rack.request.form_input"] = @env["rack.input"] + @env["rack.request.form_input_encoding"] = input_encoding unless @env["rack.request.form_hash"] = parse_multipart(env) form_vars = @env["rack.input"].read + form_vars.force_encoding(input_encoding) if input_encoding # Fix for Safari Ajax postings that always append \0 form_vars.sub!(/\0\z/, '') @@ -267,7 +269,11 @@ module Rack end def parse_multipart(env) - Utils::Multipart.parse_multipart(env) + Utils::Multipart.parse_multipart(env, input_encoding) + end + + def input_encoding + Encoding.find(@env['rack.force_content_charset'] || content_charset) rescue nil end end end diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index bc60cd1..7741ed0 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -22,7 +22,9 @@ module Rack # Unescapes a URI escaped string. (Stolen from Camping). def unescape(s) s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ - [$1.delete('%')].pack('H*') + decoded = [$1.delete('%')].pack('H*') + decoded.force_encoding(s.encoding) if decoded.respond_to?(:force_encoding) + decoded } end module_function :unescape @@ -471,7 +473,7 @@ module Rack EOL = "\r\n" MULTIPART_BOUNDARY = "AaB03x" - def self.parse_multipart(env) + def self.parse_multipart(env, encoding = nil) unless env['CONTENT_TYPE'] =~ %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n nil @@ -524,6 +526,7 @@ module Rack end if filename && filename !~ /\\[^\\"]/ + filename.force_encoding(encoding) if encoding filename = Utils.unescape(filename).gsub(/\\(.)/, '\1') end @@ -579,6 +582,7 @@ module Rack :name => name, :tempfile => body, :head => head} else data = body + data.force_encoding(encoding) if encoding end Utils.normalize_params(params, name, data) unless data.nil? diff --git a/test/spec_request.rb b/test/spec_request.rb index 914ce35..83022f1 100644 --- a/test/spec_request.rb +++ b/test/spec_request.rb @@ -164,6 +164,57 @@ describe Rack::Request do req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der'] end + if '1.9'.respond_to?(:force_encoding) + should "set params encoding according to content charset" do + req = Rack::Request.new \ + Rack::MockRequest.env_for("/", + "REQUEST_METHOD" => 'POST', + "CONTENT_TYPE" => 'application/x-www-form-urlencoded;charset=utf-8', + :input => 'foo=bar') + req.params['foo'].encoding.should.equal Encoding::UTF_8 + end + + should "set params encoding to explcitly specified value" do + req = Rack::Request.new \ + Rack::MockRequest.env_for("/", + "REQUEST_METHOD" => 'POST', + "CONTENT_TYPE" => 'application/x-www-form-urlencoded;charset=utf-8', + :input => 'foo=bar', 'rack.force_content_charset' => 'koi8-r') + req.params['foo'].encoding.should.equal Encoding::KOI8_R + end + + should "set params encoding even if params where already cached with different encoding" do + req = Rack::Request.new \ + Rack::MockRequest.env_for("/", + "REQUEST_METHOD" => 'POST', + "CONTENT_TYPE" => 'application/x-www-form-urlencoded;charset=utf-8', + :input => 'foo=bar') + req.params['foo'].encoding.should.equal Encoding::UTF_8 + new_req = Rack::Request.new(req.env.merge('rack.force_content_charset' => 'koi8-r')) + new_req.params['foo'].encoding.should.equal Encoding::KOI8_R + end + + should "set params encoding for multipart form values" do + input = < "multipart/form-data, boundary=---------------------------6569381329457552971782805232", + "CONTENT_LENGTH" => input.size, :input => input, 'rack.force_content_charset' => 'utf-8') + req.POST['asset']['title'].encoding.should.equal Encoding::UTF_8 + req.POST['asset']['asset'][:filename].encoding.should.equal Encoding::UTF_8 + end + end + should "extract referrer correctly" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path") diff --git a/test/spec_utils.rb b/test/spec_utils.rb index 822058b..93ec257 100644 --- a/test/spec_utils.rb +++ b/test/spec_utils.rb @@ -25,6 +25,14 @@ describe Rack::Utils do Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C"). should.equal "q1!2\"'w$5&7/z8)?\\" end + + if '1.9'.respond_to?(:force_encoding) + should "preserve string ecoding when unescaping" do + s = "%E2%80%A2Describe" + s.force_encoding(Encoding::UTF_8) + Rack::Utils.unescape(s).encoding.should.equal Encoding::UTF_8 + end + end should "parse query strings correctly" do Rack::Utils.parse_query("foo=bar"). -- 1.7.1