mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Move request parsing related code to ActionController::RequestParser
This commit is contained in:
parent
9c1e48eaea
commit
e898f82a74
7 changed files with 512 additions and 460 deletions
|
@ -60,6 +60,10 @@ module ActionController
|
|||
autoload :MimeResponds, 'action_controller/mime_responds'
|
||||
autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes'
|
||||
autoload :Request, 'action_controller/request'
|
||||
autoload :RequestParser, 'action_controller/request_parser'
|
||||
autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser'
|
||||
autoload :UploadedStringIO, 'action_controller/uploaded_file'
|
||||
autoload :UploadedTempfile, 'action_controller/uploaded_file'
|
||||
autoload :RecordIdentifier, 'action_controller/record_identifier'
|
||||
autoload :Response, 'action_controller/response'
|
||||
autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection'
|
||||
|
|
|
@ -101,7 +101,7 @@ module ActionController
|
|||
# For backward compatibility, the post \format is extracted from the
|
||||
# X-Post-Data-Format HTTP header if present.
|
||||
def content_type
|
||||
Mime::Type.lookup(content_type_without_parameters)
|
||||
Mime::Type.lookup(parser.content_type_without_parameters)
|
||||
end
|
||||
memoize :content_type
|
||||
|
||||
|
@ -389,11 +389,7 @@ EOM
|
|||
# Read the request \body. This is useful for web services that need to
|
||||
# work with raw requests directly.
|
||||
def raw_post
|
||||
unless env.include? 'RAW_POST_DATA'
|
||||
env['RAW_POST_DATA'] = body.read(content_length)
|
||||
body.rewind if body.respond_to?(:rewind)
|
||||
end
|
||||
env['RAW_POST_DATA']
|
||||
parser.raw_post
|
||||
end
|
||||
|
||||
# Returns both GET and POST \parameters in a single hash.
|
||||
|
@ -421,15 +417,8 @@ EOM
|
|||
@path_parameters ||= {}
|
||||
end
|
||||
|
||||
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
||||
# variable is already set, wrap it in a StringIO.
|
||||
def body
|
||||
if raw_post = env['RAW_POST_DATA']
|
||||
raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
|
||||
StringIO.new(raw_post)
|
||||
else
|
||||
body_stream
|
||||
end
|
||||
parser.body
|
||||
end
|
||||
|
||||
def remote_addr
|
||||
|
@ -442,11 +431,11 @@ EOM
|
|||
alias referer referrer
|
||||
|
||||
def query_parameters
|
||||
@query_parameters ||= self.class.parse_query_parameters(query_string)
|
||||
@query_parameters ||= parser.query_parameters
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
@request_parameters ||= parse_formatted_request_parameters
|
||||
@request_parameters ||= parser.request_parameters
|
||||
end
|
||||
|
||||
def body_stream #:nodoc:
|
||||
|
@ -481,411 +470,13 @@ EOM
|
|||
@env['SERVER_PORT'].to_i
|
||||
end
|
||||
|
||||
protected
|
||||
# The raw content type string. Use when you need parameters such as
|
||||
# charset or boundary which aren't included in the content_type MIME type.
|
||||
# Overridden by the X-POST_DATA_FORMAT header for backward compatibility.
|
||||
def content_type_with_parameters
|
||||
content_type_from_legacy_post_data_format_header ||
|
||||
env['CONTENT_TYPE'].to_s
|
||||
end
|
||||
|
||||
# The raw content type string with its parameters stripped off.
|
||||
def content_type_without_parameters
|
||||
self.class.extract_content_type_without_parameters(content_type_with_parameters)
|
||||
end
|
||||
memoize :content_type_without_parameters
|
||||
|
||||
private
|
||||
def content_type_from_legacy_post_data_format_header
|
||||
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
|
||||
case x_post_format.to_s.downcase
|
||||
when 'yaml'; 'application/x-yaml'
|
||||
when 'xml'; 'application/xml'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_formatted_request_parameters
|
||||
return {} if content_length.zero?
|
||||
|
||||
content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters)
|
||||
|
||||
# Don't parse params for unknown requests.
|
||||
return {} if content_type.blank?
|
||||
|
||||
mime_type = Mime::Type.lookup(content_type)
|
||||
strategy = ActionController::Base.param_parsers[mime_type]
|
||||
|
||||
# Only multipart form parsing expects a stream.
|
||||
body = (strategy && strategy != :multipart_form) ? raw_post : self.body
|
||||
|
||||
case strategy
|
||||
when Proc
|
||||
strategy.call(body)
|
||||
when :url_encoded_form
|
||||
self.class.clean_up_ajax_request_body! body
|
||||
self.class.parse_query_parameters(body)
|
||||
when :multipart_form
|
||||
self.class.parse_multipart_form_parameters(body, boundary, content_length, env)
|
||||
when :xml_simple, :xml_node
|
||||
body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
|
||||
when :yaml
|
||||
YAML.load(body)
|
||||
when :json
|
||||
if body.blank?
|
||||
{}
|
||||
else
|
||||
data = ActiveSupport::JSON.decode(body)
|
||||
data = {:_json => data} unless data.is_a?(Hash)
|
||||
data.with_indifferent_access
|
||||
end
|
||||
else
|
||||
{}
|
||||
end
|
||||
rescue Exception => e # YAML, XML or Ruby code block errors
|
||||
raise
|
||||
{ "body" => body,
|
||||
"content_type" => content_type_with_parameters,
|
||||
"content_length" => content_length,
|
||||
"exception" => "#{e.message} (#{e.class})",
|
||||
"backtrace" => e.backtrace }
|
||||
end
|
||||
|
||||
def named_host?(host)
|
||||
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
|
||||
end
|
||||
|
||||
class << self
|
||||
def parse_query_parameters(query_string)
|
||||
return {} if query_string.blank?
|
||||
|
||||
pairs = query_string.split('&').collect do |chunk|
|
||||
next if chunk.empty?
|
||||
key, value = chunk.split('=', 2)
|
||||
next if key.empty?
|
||||
value = value.nil? ? nil : CGI.unescape(value)
|
||||
[ CGI.unescape(key), value ]
|
||||
end.compact
|
||||
|
||||
UrlEncodedPairParser.new(pairs).result
|
||||
def parser
|
||||
@parser ||= ActionController::RequestParser.new(@env)
|
||||
end
|
||||
|
||||
def parse_request_parameters(params)
|
||||
parser = UrlEncodedPairParser.new
|
||||
|
||||
params = params.dup
|
||||
until params.empty?
|
||||
for key, value in params
|
||||
if key.blank?
|
||||
params.delete key
|
||||
elsif !key.include?('[')
|
||||
# much faster to test for the most common case first (GET)
|
||||
# and avoid the call to build_deep_hash
|
||||
parser.result[key] = get_typed_value(value[0])
|
||||
params.delete key
|
||||
elsif value.is_a?(Array)
|
||||
parser.parse(key, get_typed_value(value.shift))
|
||||
params.delete key if value.empty?
|
||||
else
|
||||
raise TypeError, "Expected array, found #{value.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parser.result
|
||||
end
|
||||
|
||||
def parse_multipart_form_parameters(body, boundary, body_size, env)
|
||||
parse_request_parameters(read_multipart(body, boundary, body_size, env))
|
||||
end
|
||||
|
||||
def extract_multipart_boundary(content_type_with_parameters)
|
||||
if content_type_with_parameters =~ MULTIPART_BOUNDARY
|
||||
['multipart/form-data', $1.dup]
|
||||
else
|
||||
extract_content_type_without_parameters(content_type_with_parameters)
|
||||
end
|
||||
end
|
||||
|
||||
def extract_content_type_without_parameters(content_type_with_parameters)
|
||||
$1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/
|
||||
end
|
||||
|
||||
def clean_up_ajax_request_body!(body)
|
||||
body.chop! if body[-1] == 0
|
||||
body.gsub!(/&_=$/, '')
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def get_typed_value(value)
|
||||
case value
|
||||
when String
|
||||
value
|
||||
when NilClass
|
||||
''
|
||||
when Array
|
||||
value.map { |v| get_typed_value(v) }
|
||||
else
|
||||
if value.respond_to? :original_filename
|
||||
# Uploaded file
|
||||
if value.original_filename
|
||||
value
|
||||
# Multipart param
|
||||
else
|
||||
result = value.read
|
||||
value.rewind
|
||||
result
|
||||
end
|
||||
# Unknown value, neither string nor multipart.
|
||||
else
|
||||
raise "Unknown form value: #{value.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
|
||||
|
||||
EOL = "\015\012"
|
||||
|
||||
def read_multipart(body, boundary, body_size, env)
|
||||
params = Hash.new([])
|
||||
boundary = "--" + boundary
|
||||
quoted_boundary = Regexp.quote(boundary)
|
||||
buf = ""
|
||||
bufsize = 10 * 1024
|
||||
boundary_end=""
|
||||
|
||||
# start multipart/form-data
|
||||
body.binmode if defined? body.binmode
|
||||
case body
|
||||
when File
|
||||
body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
|
||||
when StringIO
|
||||
body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
|
||||
end
|
||||
boundary_size = boundary.size + EOL.size
|
||||
body_size -= boundary_size
|
||||
status = body.read(boundary_size)
|
||||
if nil == status
|
||||
raise EOFError, "no content body"
|
||||
elsif boundary + EOL != status
|
||||
raise EOFError, "bad content body"
|
||||
end
|
||||
|
||||
loop do
|
||||
head = nil
|
||||
content =
|
||||
if 10240 < body_size
|
||||
UploadedTempfile.new("CGI")
|
||||
else
|
||||
UploadedStringIO.new
|
||||
end
|
||||
content.binmode if defined? content.binmode
|
||||
|
||||
until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
|
||||
|
||||
if (not head) and /#{EOL}#{EOL}/n.match(buf)
|
||||
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
|
||||
head = $1.dup
|
||||
""
|
||||
end
|
||||
next
|
||||
end
|
||||
|
||||
if head and ( (EOL + boundary + EOL).size < buf.size )
|
||||
content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
|
||||
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
|
||||
end
|
||||
|
||||
c = if bufsize < body_size
|
||||
body.read(bufsize)
|
||||
else
|
||||
body.read(body_size)
|
||||
end
|
||||
if c.nil? || c.empty?
|
||||
raise EOFError, "bad content body"
|
||||
end
|
||||
buf.concat(c)
|
||||
body_size -= c.size
|
||||
end
|
||||
|
||||
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
|
||||
content.print $1
|
||||
if "--" == $2
|
||||
body_size = -1
|
||||
end
|
||||
boundary_end = $2.dup
|
||||
""
|
||||
end
|
||||
|
||||
content.rewind
|
||||
|
||||
head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni
|
||||
if filename = $1 || $2
|
||||
if /Mac/ni.match(env['HTTP_USER_AGENT']) and
|
||||
/Mozilla/ni.match(env['HTTP_USER_AGENT']) and
|
||||
(not /MSIE/ni.match(env['HTTP_USER_AGENT']))
|
||||
filename = CGI.unescape(filename)
|
||||
end
|
||||
content.original_path = filename.dup
|
||||
end
|
||||
|
||||
head =~ /Content-Type: ([^\r]*)/ni
|
||||
content.content_type = $1.dup if $1
|
||||
|
||||
head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni
|
||||
name = $1.dup if $1
|
||||
|
||||
if params.has_key?(name)
|
||||
params[name].push(content)
|
||||
else
|
||||
params[name] = [content]
|
||||
end
|
||||
break if body_size == -1
|
||||
end
|
||||
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
|
||||
|
||||
begin
|
||||
body.rewind if body.respond_to?(:rewind)
|
||||
rescue Errno::ESPIPE
|
||||
# Handles exceptions raised by input streams that cannot be rewound
|
||||
# such as when using plain CGI under Apache
|
||||
end
|
||||
|
||||
params
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class UrlEncodedPairParser < StringScanner #:nodoc:
|
||||
attr_reader :top, :parent, :result
|
||||
|
||||
def initialize(pairs = [])
|
||||
super('')
|
||||
@result = {}
|
||||
pairs.each { |key, value| parse(key, value) }
|
||||
end
|
||||
|
||||
KEY_REGEXP = %r{([^\[\]=&]+)}
|
||||
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
|
||||
|
||||
# Parse the query string
|
||||
def parse(key, value)
|
||||
self.string = key
|
||||
@top, @parent = result, nil
|
||||
|
||||
# First scan the bare key
|
||||
key = scan(KEY_REGEXP) or return
|
||||
key = post_key_check(key)
|
||||
|
||||
# Then scan as many nestings as present
|
||||
until eos?
|
||||
r = scan(BRACKETED_KEY_REGEXP) or return
|
||||
key = self[1]
|
||||
key = post_key_check(key)
|
||||
end
|
||||
|
||||
bind(key, value)
|
||||
end
|
||||
|
||||
private
|
||||
# After we see a key, we must look ahead to determine our next action. Cases:
|
||||
#
|
||||
# [] follows the key. Then the value must be an array.
|
||||
# = follows the key. (A value comes next)
|
||||
# & or the end of string follows the key. Then the key is a flag.
|
||||
# otherwise, a hash follows the key.
|
||||
def post_key_check(key)
|
||||
if scan(/\[\]/) # a[b][] indicates that b is an array
|
||||
container(key, Array)
|
||||
nil
|
||||
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
|
||||
container(key, Hash)
|
||||
nil
|
||||
else # End of key? We do nothing.
|
||||
key
|
||||
end
|
||||
end
|
||||
|
||||
# Add a container to the stack.
|
||||
def container(key, klass)
|
||||
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
|
||||
value = bind(key, klass.new)
|
||||
type_conflict! klass, value unless value.is_a?(klass)
|
||||
push(value)
|
||||
end
|
||||
|
||||
# Push a value onto the 'stack', which is actually only the top 2 items.
|
||||
def push(value)
|
||||
@parent, @top = @top, value
|
||||
end
|
||||
|
||||
# Bind a key (which may be nil for items in an array) to the provided value.
|
||||
def bind(key, value)
|
||||
if top.is_a? Array
|
||||
if key
|
||||
if top[-1].is_a?(Hash) && ! top[-1].key?(key)
|
||||
top[-1][key] = value
|
||||
else
|
||||
top << {key => value}.with_indifferent_access
|
||||
push top.last
|
||||
value = top[key]
|
||||
end
|
||||
else
|
||||
top << value
|
||||
end
|
||||
elsif top.is_a? Hash
|
||||
key = CGI.unescape(key)
|
||||
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
|
||||
top[key] ||= value
|
||||
return top[key]
|
||||
else
|
||||
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
def type_conflict!(klass, value)
|
||||
raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
|
||||
end
|
||||
end
|
||||
|
||||
module UploadedFile
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
attr_accessor :original_path, :content_type
|
||||
alias_method :local_path, :path
|
||||
end
|
||||
end
|
||||
|
||||
# Take the basename of the upload's original filename.
|
||||
# This handles the full Windows paths given by Internet Explorer
|
||||
# (and perhaps other broken user agents) without affecting
|
||||
# those which give the lone filename.
|
||||
# The Windows regexp is adapted from Perl's File::Basename.
|
||||
def original_filename
|
||||
unless defined? @original_filename
|
||||
@original_filename =
|
||||
unless original_path.blank?
|
||||
if original_path =~ /^(?:.*[:\\\/])?(.*)/m
|
||||
$1
|
||||
else
|
||||
File.basename original_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@original_filename
|
||||
end
|
||||
end
|
||||
|
||||
class UploadedStringIO < StringIO
|
||||
include UploadedFile
|
||||
end
|
||||
|
||||
class UploadedTempfile < Tempfile
|
||||
include UploadedFile
|
||||
end
|
||||
end
|
||||
|
|
314
actionpack/lib/action_controller/request_parser.rb
Normal file
314
actionpack/lib/action_controller/request_parser.rb
Normal file
|
@ -0,0 +1,314 @@
|
|||
module ActionController
|
||||
class RequestParser
|
||||
def initialize(env)
|
||||
@env = env
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
@request_parameters ||= parse_formatted_request_parameters
|
||||
end
|
||||
|
||||
def query_parameters
|
||||
@query_parameters ||= self.class.parse_query_parameters(query_string)
|
||||
end
|
||||
|
||||
# Returns the query string, accounting for server idiosyncrasies.
|
||||
def query_string
|
||||
@env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '')
|
||||
end
|
||||
|
||||
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
||||
# variable is already set, wrap it in a StringIO.
|
||||
def body
|
||||
if raw_post = @env['RAW_POST_DATA']
|
||||
raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
|
||||
StringIO.new(raw_post)
|
||||
else
|
||||
@env['rack.input']
|
||||
end
|
||||
end
|
||||
|
||||
# The raw content type string with its parameters stripped off.
|
||||
def content_type_without_parameters
|
||||
self.class.extract_content_type_without_parameters(content_type_with_parameters)
|
||||
end
|
||||
|
||||
def raw_post
|
||||
unless @env.include? 'RAW_POST_DATA'
|
||||
@env['RAW_POST_DATA'] = body.read(content_length)
|
||||
body.rewind if body.respond_to?(:rewind)
|
||||
end
|
||||
@env['RAW_POST_DATA']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_formatted_request_parameters
|
||||
return {} if content_length.zero?
|
||||
|
||||
content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters)
|
||||
|
||||
# Don't parse params for unknown requests.
|
||||
return {} if content_type.blank?
|
||||
|
||||
mime_type = Mime::Type.lookup(content_type)
|
||||
strategy = ActionController::Base.param_parsers[mime_type]
|
||||
|
||||
# Only multipart form parsing expects a stream.
|
||||
body = (strategy && strategy != :multipart_form) ? raw_post : self.body
|
||||
|
||||
case strategy
|
||||
when Proc
|
||||
strategy.call(body)
|
||||
when :url_encoded_form
|
||||
self.class.clean_up_ajax_request_body! body
|
||||
self.class.parse_query_parameters(body)
|
||||
when :multipart_form
|
||||
self.class.parse_multipart_form_parameters(body, boundary, content_length, @env)
|
||||
when :xml_simple, :xml_node
|
||||
body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
|
||||
when :yaml
|
||||
YAML.load(body)
|
||||
when :json
|
||||
if body.blank?
|
||||
{}
|
||||
else
|
||||
data = ActiveSupport::JSON.decode(body)
|
||||
data = {:_json => data} unless data.is_a?(Hash)
|
||||
data.with_indifferent_access
|
||||
end
|
||||
else
|
||||
{}
|
||||
end
|
||||
rescue Exception => e # YAML, XML or Ruby code block errors
|
||||
raise
|
||||
{ "body" => body,
|
||||
"content_type" => content_type_with_parameters,
|
||||
"content_length" => content_length,
|
||||
"exception" => "#{e.message} (#{e.class})",
|
||||
"backtrace" => e.backtrace }
|
||||
end
|
||||
|
||||
def content_length
|
||||
@content_length ||= @env['CONTENT_LENGTH'].to_i
|
||||
end
|
||||
|
||||
# The raw content type string. Use when you need parameters such as
|
||||
# charset or boundary which aren't included in the content_type MIME type.
|
||||
# Overridden by the X-POST_DATA_FORMAT header for backward compatibility.
|
||||
def content_type_with_parameters
|
||||
content_type_from_legacy_post_data_format_header || @env['CONTENT_TYPE'].to_s
|
||||
end
|
||||
|
||||
def content_type_from_legacy_post_data_format_header
|
||||
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
|
||||
case x_post_format.to_s.downcase
|
||||
when 'yaml'; 'application/x-yaml'
|
||||
when 'xml'; 'application/xml'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
def parse_query_parameters(query_string)
|
||||
return {} if query_string.blank?
|
||||
|
||||
pairs = query_string.split('&').collect do |chunk|
|
||||
next if chunk.empty?
|
||||
key, value = chunk.split('=', 2)
|
||||
next if key.empty?
|
||||
value = value.nil? ? nil : CGI.unescape(value)
|
||||
[ CGI.unescape(key), value ]
|
||||
end.compact
|
||||
|
||||
UrlEncodedPairParser.new(pairs).result
|
||||
end
|
||||
|
||||
def parse_request_parameters(params)
|
||||
parser = UrlEncodedPairParser.new
|
||||
|
||||
params = params.dup
|
||||
until params.empty?
|
||||
for key, value in params
|
||||
if key.blank?
|
||||
params.delete key
|
||||
elsif !key.include?('[')
|
||||
# much faster to test for the most common case first (GET)
|
||||
# and avoid the call to build_deep_hash
|
||||
parser.result[key] = get_typed_value(value[0])
|
||||
params.delete key
|
||||
elsif value.is_a?(Array)
|
||||
parser.parse(key, get_typed_value(value.shift))
|
||||
params.delete key if value.empty?
|
||||
else
|
||||
raise TypeError, "Expected array, found #{value.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parser.result
|
||||
end
|
||||
|
||||
def parse_multipart_form_parameters(body, boundary, body_size, env)
|
||||
parse_request_parameters(read_multipart(body, boundary, body_size, env))
|
||||
end
|
||||
|
||||
def extract_multipart_boundary(content_type_with_parameters)
|
||||
if content_type_with_parameters =~ MULTIPART_BOUNDARY
|
||||
['multipart/form-data', $1.dup]
|
||||
else
|
||||
extract_content_type_without_parameters(content_type_with_parameters)
|
||||
end
|
||||
end
|
||||
|
||||
def extract_content_type_without_parameters(content_type_with_parameters)
|
||||
$1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/
|
||||
end
|
||||
|
||||
def clean_up_ajax_request_body!(body)
|
||||
body.chop! if body[-1] == 0
|
||||
body.gsub!(/&_=$/, '')
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def get_typed_value(value)
|
||||
case value
|
||||
when String
|
||||
value
|
||||
when NilClass
|
||||
''
|
||||
when Array
|
||||
value.map { |v| get_typed_value(v) }
|
||||
else
|
||||
if value.respond_to? :original_filename
|
||||
# Uploaded file
|
||||
if value.original_filename
|
||||
value
|
||||
# Multipart param
|
||||
else
|
||||
result = value.read
|
||||
value.rewind
|
||||
result
|
||||
end
|
||||
# Unknown value, neither string nor multipart.
|
||||
else
|
||||
raise "Unknown form value: #{value.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
|
||||
|
||||
EOL = "\015\012"
|
||||
|
||||
def read_multipart(body, boundary, body_size, env)
|
||||
params = Hash.new([])
|
||||
boundary = "--" + boundary
|
||||
quoted_boundary = Regexp.quote(boundary)
|
||||
buf = ""
|
||||
bufsize = 10 * 1024
|
||||
boundary_end=""
|
||||
|
||||
# start multipart/form-data
|
||||
body.binmode if defined? body.binmode
|
||||
case body
|
||||
when File
|
||||
body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
|
||||
when StringIO
|
||||
body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
|
||||
end
|
||||
boundary_size = boundary.size + EOL.size
|
||||
body_size -= boundary_size
|
||||
status = body.read(boundary_size)
|
||||
if nil == status
|
||||
raise EOFError, "no content body"
|
||||
elsif boundary + EOL != status
|
||||
raise EOFError, "bad content body"
|
||||
end
|
||||
|
||||
loop do
|
||||
head = nil
|
||||
content =
|
||||
if 10240 < body_size
|
||||
UploadedTempfile.new("CGI")
|
||||
else
|
||||
UploadedStringIO.new
|
||||
end
|
||||
content.binmode if defined? content.binmode
|
||||
|
||||
until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
|
||||
|
||||
if (not head) and /#{EOL}#{EOL}/n.match(buf)
|
||||
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
|
||||
head = $1.dup
|
||||
""
|
||||
end
|
||||
next
|
||||
end
|
||||
|
||||
if head and ( (EOL + boundary + EOL).size < buf.size )
|
||||
content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
|
||||
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
|
||||
end
|
||||
|
||||
c = if bufsize < body_size
|
||||
body.read(bufsize)
|
||||
else
|
||||
body.read(body_size)
|
||||
end
|
||||
if c.nil? || c.empty?
|
||||
raise EOFError, "bad content body"
|
||||
end
|
||||
buf.concat(c)
|
||||
body_size -= c.size
|
||||
end
|
||||
|
||||
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
|
||||
content.print $1
|
||||
if "--" == $2
|
||||
body_size = -1
|
||||
end
|
||||
boundary_end = $2.dup
|
||||
""
|
||||
end
|
||||
|
||||
content.rewind
|
||||
|
||||
head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni
|
||||
if filename = $1 || $2
|
||||
if /Mac/ni.match(env['HTTP_USER_AGENT']) and
|
||||
/Mozilla/ni.match(env['HTTP_USER_AGENT']) and
|
||||
(not /MSIE/ni.match(env['HTTP_USER_AGENT']))
|
||||
filename = CGI.unescape(filename)
|
||||
end
|
||||
content.original_path = filename.dup
|
||||
end
|
||||
|
||||
head =~ /Content-Type: ([^\r]*)/ni
|
||||
content.content_type = $1.dup if $1
|
||||
|
||||
head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni
|
||||
name = $1.dup if $1
|
||||
|
||||
if params.has_key?(name)
|
||||
params[name].push(content)
|
||||
else
|
||||
params[name] = [content]
|
||||
end
|
||||
break if body_size == -1
|
||||
end
|
||||
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
|
||||
|
||||
begin
|
||||
body.rewind if body.respond_to?(:rewind)
|
||||
rescue Errno::ESPIPE
|
||||
# Handles exceptions raised by input streams that cannot be rewound
|
||||
# such as when using plain CGI under Apache
|
||||
end
|
||||
|
||||
params
|
||||
end
|
||||
end # class << self
|
||||
end
|
||||
end
|
|
@ -29,18 +29,21 @@ module ActionController #:nodoc:
|
|||
|
||||
class TestRequest < Request #:nodoc:
|
||||
attr_accessor :cookies, :session_options
|
||||
attr_accessor :query_parameters, :request_parameters, :path, :session
|
||||
attr_accessor :host, :user_agent
|
||||
attr_accessor :query_parameters, :path, :session
|
||||
attr_accessor :host
|
||||
|
||||
def initialize
|
||||
super(Rack::MockRequest.env_for('/'))
|
||||
env = Rack::MockRequest.env_for("/")
|
||||
|
||||
# TODO: Fix Request to assume env['SERVER_ADDR'] doesn't contain port number
|
||||
env['SERVER_ADDR'] = env.delete("SERVER_NAME")
|
||||
super(env)
|
||||
|
||||
@query_parameters = {}
|
||||
@request_parameters = {}
|
||||
@session = TestSession.new
|
||||
|
||||
initialize_containers
|
||||
initialize_default_values
|
||||
initialize_containers
|
||||
end
|
||||
|
||||
def reset_session
|
||||
|
@ -55,7 +58,11 @@ module ActionController #:nodoc:
|
|||
# Either the RAW_POST_DATA environment variable or the URL-encoded request
|
||||
# parameters.
|
||||
def raw_post
|
||||
env['RAW_POST_DATA'] ||= returning(url_encoded_request_parameters) { |b| b.force_encoding(Encoding::BINARY) if b.respond_to?(:force_encoding) }
|
||||
@env['RAW_POST_DATA'] ||= begin
|
||||
data = url_encoded_request_parameters
|
||||
data.force_encoding(Encoding::BINARY) if data.respond_to?(:force_encoding)
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
def port=(number)
|
||||
|
@ -125,26 +132,30 @@ module ActionController #:nodoc:
|
|||
path_parameters[key.to_s] = value
|
||||
end
|
||||
end
|
||||
raw_post # populate env['RAW_POST_DATA']
|
||||
@parameters = nil # reset TestRequest#parameters to use the new path_parameters
|
||||
end
|
||||
|
||||
def recycle!
|
||||
self.request_parameters = {}
|
||||
self.query_parameters = {}
|
||||
self.path_parameters = {}
|
||||
unmemoize_all
|
||||
end
|
||||
|
||||
def user_agent=(user_agent)
|
||||
@env['HTTP_USER_AGENT'] = user_agent
|
||||
end
|
||||
|
||||
private
|
||||
def initialize_containers
|
||||
@env, @cookies = {}, {}
|
||||
@cookies = {}
|
||||
end
|
||||
|
||||
def initialize_default_values
|
||||
@host = "test.host"
|
||||
@request_uri = "/"
|
||||
@user_agent = "Rails Testing"
|
||||
self.remote_addr = "0.0.0.0"
|
||||
@env['HTTP_USER_AGENT'] = "Rails Testing"
|
||||
@env['REMOTE_ADDR'] = "0.0.0.0"
|
||||
@env["SERVER_PORT"] = 80
|
||||
@env['REQUEST_METHOD'] = "GET"
|
||||
end
|
||||
|
|
37
actionpack/lib/action_controller/uploaded_file.rb
Normal file
37
actionpack/lib/action_controller/uploaded_file.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
module ActionController
|
||||
module UploadedFile
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
attr_accessor :original_path, :content_type
|
||||
alias_method :local_path, :path
|
||||
end
|
||||
end
|
||||
|
||||
# Take the basename of the upload's original filename.
|
||||
# This handles the full Windows paths given by Internet Explorer
|
||||
# (and perhaps other broken user agents) without affecting
|
||||
# those which give the lone filename.
|
||||
# The Windows regexp is adapted from Perl's File::Basename.
|
||||
def original_filename
|
||||
unless defined? @original_filename
|
||||
@original_filename =
|
||||
unless original_path.blank?
|
||||
if original_path =~ /^(?:.*[:\\\/])?(.*)/m
|
||||
$1
|
||||
else
|
||||
File.basename original_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@original_filename
|
||||
end
|
||||
end
|
||||
|
||||
class UploadedStringIO < StringIO
|
||||
include UploadedFile
|
||||
end
|
||||
|
||||
class UploadedTempfile < Tempfile
|
||||
include UploadedFile
|
||||
end
|
||||
end
|
95
actionpack/lib/action_controller/url_encoded_pair_parser.rb
Normal file
95
actionpack/lib/action_controller/url_encoded_pair_parser.rb
Normal file
|
@ -0,0 +1,95 @@
|
|||
module ActionController
|
||||
class UrlEncodedPairParser < StringScanner #:nodoc:
|
||||
attr_reader :top, :parent, :result
|
||||
|
||||
def initialize(pairs = [])
|
||||
super('')
|
||||
@result = {}
|
||||
pairs.each { |key, value| parse(key, value) }
|
||||
end
|
||||
|
||||
KEY_REGEXP = %r{([^\[\]=&]+)}
|
||||
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
|
||||
|
||||
# Parse the query string
|
||||
def parse(key, value)
|
||||
self.string = key
|
||||
@top, @parent = result, nil
|
||||
|
||||
# First scan the bare key
|
||||
key = scan(KEY_REGEXP) or return
|
||||
key = post_key_check(key)
|
||||
|
||||
# Then scan as many nestings as present
|
||||
until eos?
|
||||
r = scan(BRACKETED_KEY_REGEXP) or return
|
||||
key = self[1]
|
||||
key = post_key_check(key)
|
||||
end
|
||||
|
||||
bind(key, value)
|
||||
end
|
||||
|
||||
private
|
||||
# After we see a key, we must look ahead to determine our next action. Cases:
|
||||
#
|
||||
# [] follows the key. Then the value must be an array.
|
||||
# = follows the key. (A value comes next)
|
||||
# & or the end of string follows the key. Then the key is a flag.
|
||||
# otherwise, a hash follows the key.
|
||||
def post_key_check(key)
|
||||
if scan(/\[\]/) # a[b][] indicates that b is an array
|
||||
container(key, Array)
|
||||
nil
|
||||
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
|
||||
container(key, Hash)
|
||||
nil
|
||||
else # End of key? We do nothing.
|
||||
key
|
||||
end
|
||||
end
|
||||
|
||||
# Add a container to the stack.
|
||||
def container(key, klass)
|
||||
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
|
||||
value = bind(key, klass.new)
|
||||
type_conflict! klass, value unless value.is_a?(klass)
|
||||
push(value)
|
||||
end
|
||||
|
||||
# Push a value onto the 'stack', which is actually only the top 2 items.
|
||||
def push(value)
|
||||
@parent, @top = @top, value
|
||||
end
|
||||
|
||||
# Bind a key (which may be nil for items in an array) to the provided value.
|
||||
def bind(key, value)
|
||||
if top.is_a? Array
|
||||
if key
|
||||
if top[-1].is_a?(Hash) && ! top[-1].key?(key)
|
||||
top[-1][key] = value
|
||||
else
|
||||
top << {key => value}.with_indifferent_access
|
||||
push top.last
|
||||
value = top[key]
|
||||
end
|
||||
else
|
||||
top << value
|
||||
end
|
||||
elsif top.is_a? Hash
|
||||
key = CGI.unescape(key)
|
||||
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
|
||||
top[key] ||= value
|
||||
return top[key]
|
||||
else
|
||||
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
def type_conflict!(klass, value)
|
||||
raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -424,95 +424,95 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
def test_query_string
|
||||
assert_equal(
|
||||
{ "action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1"},
|
||||
ActionController::Request.parse_query_parameters(@query_string)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string)
|
||||
)
|
||||
end
|
||||
|
||||
def test_deep_query_string
|
||||
expected = {'x' => {'y' => {'z' => '10'}}}
|
||||
assert_equal(expected, ActionController::Request.parse_query_parameters('x[y][z]=10'))
|
||||
assert_equal(expected, ActionController::RequestParser.parse_query_parameters('x[y][z]=10'))
|
||||
end
|
||||
|
||||
def test_deep_query_string_with_array
|
||||
assert_equal({'x' => {'y' => {'z' => ['10']}}}, ActionController::Request.parse_query_parameters('x[y][z][]=10'))
|
||||
assert_equal({'x' => {'y' => {'z' => ['10', '5']}}}, ActionController::Request.parse_query_parameters('x[y][z][]=10&x[y][z][]=5'))
|
||||
assert_equal({'x' => {'y' => {'z' => ['10']}}}, ActionController::RequestParser.parse_query_parameters('x[y][z][]=10'))
|
||||
assert_equal({'x' => {'y' => {'z' => ['10', '5']}}}, ActionController::RequestParser.parse_query_parameters('x[y][z][]=10&x[y][z][]=5'))
|
||||
end
|
||||
|
||||
def test_deep_query_string_with_array_of_hash
|
||||
assert_equal({'x' => {'y' => [{'z' => '10'}]}}, ActionController::Request.parse_query_parameters('x[y][][z]=10'))
|
||||
assert_equal({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, ActionController::Request.parse_query_parameters('x[y][][z]=10&x[y][][w]=10'))
|
||||
assert_equal({'x' => {'y' => [{'z' => '10'}]}}, ActionController::RequestParser.parse_query_parameters('x[y][][z]=10'))
|
||||
assert_equal({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, ActionController::RequestParser.parse_query_parameters('x[y][][z]=10&x[y][][w]=10'))
|
||||
end
|
||||
|
||||
def test_deep_query_string_with_array_of_hashes_with_one_pair
|
||||
assert_equal({'x' => {'y' => [{'z' => '10'}, {'z' => '20'}]}}, ActionController::Request.parse_query_parameters('x[y][][z]=10&x[y][][z]=20'))
|
||||
assert_equal("10", ActionController::Request.parse_query_parameters('x[y][][z]=10&x[y][][z]=20')["x"]["y"].first["z"])
|
||||
assert_equal("10", ActionController::Request.parse_query_parameters('x[y][][z]=10&x[y][][z]=20').with_indifferent_access[:x][:y].first[:z])
|
||||
assert_equal({'x' => {'y' => [{'z' => '10'}, {'z' => '20'}]}}, ActionController::RequestParser.parse_query_parameters('x[y][][z]=10&x[y][][z]=20'))
|
||||
assert_equal("10", ActionController::RequestParser.parse_query_parameters('x[y][][z]=10&x[y][][z]=20')["x"]["y"].first["z"])
|
||||
assert_equal("10", ActionController::RequestParser.parse_query_parameters('x[y][][z]=10&x[y][][z]=20').with_indifferent_access[:x][:y].first[:z])
|
||||
end
|
||||
|
||||
def test_deep_query_string_with_array_of_hashes_with_multiple_pairs
|
||||
assert_equal(
|
||||
{'x' => {'y' => [{'z' => '10', 'w' => 'a'}, {'z' => '20', 'w' => 'b'}]}},
|
||||
ActionController::Request.parse_query_parameters('x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b')
|
||||
ActionController::RequestParser.parse_query_parameters('x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b')
|
||||
)
|
||||
end
|
||||
|
||||
def test_query_string_with_nil
|
||||
assert_equal(
|
||||
{ "action" => "create_customer", "full_name" => ''},
|
||||
ActionController::Request.parse_query_parameters(@query_string_with_empty)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string_with_empty)
|
||||
)
|
||||
end
|
||||
|
||||
def test_query_string_with_array
|
||||
assert_equal(
|
||||
{ "action" => "create_customer", "selected" => ["1", "2", "3"]},
|
||||
ActionController::Request.parse_query_parameters(@query_string_with_array)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string_with_array)
|
||||
)
|
||||
end
|
||||
|
||||
def test_query_string_with_amps
|
||||
assert_equal(
|
||||
{ "action" => "create_customer", "name" => "Don't & Does"},
|
||||
ActionController::Request.parse_query_parameters(@query_string_with_amps)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string_with_amps)
|
||||
)
|
||||
end
|
||||
|
||||
def test_query_string_with_many_equal
|
||||
assert_equal(
|
||||
{ "action" => "create_customer", "full_name" => "abc=def=ghi"},
|
||||
ActionController::Request.parse_query_parameters(@query_string_with_many_equal)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string_with_many_equal)
|
||||
)
|
||||
end
|
||||
|
||||
def test_query_string_without_equal
|
||||
assert_equal(
|
||||
{ "action" => nil },
|
||||
ActionController::Request.parse_query_parameters(@query_string_without_equal)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string_without_equal)
|
||||
)
|
||||
end
|
||||
|
||||
def test_query_string_with_empty_key
|
||||
assert_equal(
|
||||
{ "action" => "create_customer", "full_name" => "David Heinemeier Hansson" },
|
||||
ActionController::Request.parse_query_parameters(@query_string_with_empty_key)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string_with_empty_key)
|
||||
)
|
||||
end
|
||||
|
||||
def test_query_string_with_many_ampersands
|
||||
assert_equal(
|
||||
{ "action" => "create_customer", "full_name" => "David Heinemeier Hansson"},
|
||||
ActionController::Request.parse_query_parameters(@query_string_with_many_ampersands)
|
||||
ActionController::RequestParser.parse_query_parameters(@query_string_with_many_ampersands)
|
||||
)
|
||||
end
|
||||
|
||||
def test_unbalanced_query_string_with_array
|
||||
assert_equal(
|
||||
{'location' => ["1", "2"], 'age_group' => ["2"]},
|
||||
ActionController::Request.parse_query_parameters("location[]=1&location[]=2&age_group[]=2")
|
||||
ActionController::RequestParser.parse_query_parameters("location[]=1&location[]=2&age_group[]=2")
|
||||
)
|
||||
assert_equal(
|
||||
{'location' => ["1", "2"], 'age_group' => ["2"]},
|
||||
ActionController::Request.parse_request_parameters({'location[]' => ["1", "2"],
|
||||
ActionController::RequestParser.parse_request_parameters({'location[]' => ["1", "2"],
|
||||
'age_group[]' => ["2"]})
|
||||
)
|
||||
end
|
||||
|
@ -525,7 +525,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
|
||||
expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } }
|
||||
|
||||
assert_equal(expected, ActionController::Request.parse_request_parameters(query))
|
||||
assert_equal(expected, ActionController::RequestParser.parse_request_parameters(query))
|
||||
end
|
||||
|
||||
def test_parse_params
|
||||
|
@ -564,7 +564,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
}
|
||||
}
|
||||
|
||||
assert_equal expected_output, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected_output, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
UploadedStringIO = ActionController::UploadedStringIO
|
||||
|
@ -619,7 +619,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
"text_part" => "abc"
|
||||
}
|
||||
|
||||
params = ActionController::Request.parse_request_parameters(input)
|
||||
params = ActionController::RequestParser.parse_request_parameters(input)
|
||||
assert_equal expected_output, params
|
||||
|
||||
# Lone filenames are preserved.
|
||||
|
@ -650,7 +650,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
"logo" => File.new(File.dirname(__FILE__) + "/rack_test.rb").path,
|
||||
}
|
||||
|
||||
assert_equal expected_output, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected_output, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_array
|
||||
|
@ -658,55 +658,55 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
|
||||
expected_output = { "selected" => [ "1", "2", "3" ] }
|
||||
|
||||
assert_equal expected_output, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected_output, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_non_alphanumeric_name
|
||||
input = { "a/b[c]" => %w(d) }
|
||||
expected = { "a/b" => { "c" => "d" }}
|
||||
assert_equal expected, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_single_brackets_in_middle
|
||||
input = { "a/b[c]d" => %w(e) }
|
||||
expected = { "a/b" => {} }
|
||||
assert_equal expected, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_separated_brackets
|
||||
input = { "a/b@[c]d[e]" => %w(f) }
|
||||
expected = { "a/b@" => { }}
|
||||
assert_equal expected, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_separated_brackets_and_array
|
||||
input = { "a/b@[c]d[e][]" => %w(f) }
|
||||
expected = { "a/b@" => { }}
|
||||
assert_equal expected , ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected , ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_unmatched_brackets_and_array
|
||||
input = { "a/b@[c][d[e][]" => %w(f) }
|
||||
expected = { "a/b@" => { "c" => { }}}
|
||||
assert_equal expected, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_nil_key
|
||||
input = { nil => nil, "test2" => %w(value1) }
|
||||
expected = { "test2" => "value1" }
|
||||
assert_equal expected, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_array_prefix_and_hashes
|
||||
input = { "a[][b][c]" => %w(d) }
|
||||
expected = {"a" => [{"b" => {"c" => "d"}}]}
|
||||
assert_equal expected, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
|
||||
def test_parse_params_with_complex_nesting
|
||||
input = { "a[][b][c][][d][]" => %w(e) }
|
||||
expected = {"a" => [{"b" => {"c" => [{"d" => ["e"]}]}}]}
|
||||
assert_equal expected, ActionController::Request.parse_request_parameters(input)
|
||||
assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -768,7 +768,7 @@ class MultipartRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
# Ensures that parse_multipart_form_parameters works with streams that cannot be rewound
|
||||
file = File.open(File.join(FIXTURE_PATH, 'large_text_file'), 'rb')
|
||||
file.expects(:rewind).raises(Errno::ESPIPE)
|
||||
params = ActionController::Request.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
|
||||
params = ActionController::RequestParser.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
|
||||
assert_not_equal 0, file.pos # file was not rewound after reading
|
||||
end
|
||||
end
|
||||
|
@ -807,7 +807,7 @@ class MultipartRequestParameterParsingTest < ActiveSupport::TestCase
|
|||
private
|
||||
def parse_multipart(name)
|
||||
File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
|
||||
params = ActionController::Request.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
|
||||
params = ActionController::RequestParser.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
|
||||
assert_equal 0, file.pos # file was rewound after reading
|
||||
params
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue