mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Parse url-encoded and multipart requests ourselves instead of delegating to CGI.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6764 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
b6541b8dcc
commit
d2ed32d592
14 changed files with 784 additions and 818 deletions
|
@ -1,5 +1,7 @@
|
||||||
*SVN*
|
*SVN*
|
||||||
|
|
||||||
|
* Parse url-encoded and multipart requests ourselves instead of delegating to CGI. [Jeremy Kemper]
|
||||||
|
|
||||||
* select :include_blank option can be set to a string instead of true, which just uses an empty string. #7664 [Wizard]
|
* select :include_blank option can be set to a string instead of true, which just uses an empty string. #7664 [Wizard]
|
||||||
|
|
||||||
* Added url_for usage on render :location, which allows for record identification [DHH]. Example:
|
* Added url_for usage on render :location, which allows for record identification [DHH]. Example:
|
||||||
|
|
|
@ -271,7 +271,9 @@ module ActionController #:nodoc:
|
||||||
# A YAML parser is also available and can be turned on with:
|
# A YAML parser is also available and can be turned on with:
|
||||||
#
|
#
|
||||||
# ActionController::Base.param_parsers[Mime::YAML] = :yaml
|
# ActionController::Base.param_parsers[Mime::YAML] = :yaml
|
||||||
@@param_parsers = { Mime::XML => :xml_simple }
|
@@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
|
||||||
|
Mime::URL_ENCODED_FORM => :url_encoded_form,
|
||||||
|
Mime::XML => :xml_simple }
|
||||||
cattr_accessor :param_parsers
|
cattr_accessor :param_parsers
|
||||||
|
|
||||||
# Controls the default charset for all renders.
|
# Controls the default charset for all renders.
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
require 'action_controller/cgi_ext/stdinput'
|
require 'action_controller/cgi_ext/stdinput'
|
||||||
require 'action_controller/cgi_ext/parameters'
|
|
||||||
require 'action_controller/cgi_ext/query_extension'
|
require 'action_controller/cgi_ext/query_extension'
|
||||||
require 'action_controller/cgi_ext/cookie'
|
require 'action_controller/cgi_ext/cookie'
|
||||||
require 'action_controller/cgi_ext/session'
|
require 'action_controller/cgi_ext/session'
|
||||||
|
|
||||||
class CGI #:nodoc:
|
class CGI #:nodoc:
|
||||||
include ActionController::CgiExt::Stdinput
|
include ActionController::CgiExt::Stdinput
|
||||||
include ActionController::CgiExt::Parameters
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
alias :escapeHTML_fail_on_nil :escapeHTML
|
alias :escapeHTML_fail_on_nil :escapeHTML
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
require 'cgi'
|
|
||||||
require 'strscan'
|
|
||||||
|
|
||||||
module ActionController
|
|
||||||
module CgiExt
|
|
||||||
module Parameters
|
|
||||||
def self.included(base)
|
|
||||||
base.extend ClassMethods
|
|
||||||
end
|
|
||||||
|
|
||||||
# Merge POST and GET parameters from the request body and query string,
|
|
||||||
# with GET parameters taking precedence.
|
|
||||||
def parameters
|
|
||||||
request_parameters.update(query_parameters)
|
|
||||||
end
|
|
||||||
|
|
||||||
def query_parameters
|
|
||||||
self.class.parse_query_parameters(query_string)
|
|
||||||
end
|
|
||||||
|
|
||||||
def request_parameters
|
|
||||||
self.class.parse_request_parameters(params, env_table)
|
|
||||||
end
|
|
||||||
|
|
||||||
module ClassMethods
|
|
||||||
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
|
|
||||||
|
|
||||||
private
|
|
||||||
def get_typed_value(value)
|
|
||||||
case value
|
|
||||||
when String
|
|
||||||
value
|
|
||||||
when NilClass
|
|
||||||
''
|
|
||||||
when Array
|
|
||||||
value.map { |v| get_typed_value(v) }
|
|
||||||
else
|
|
||||||
# Uploaded file provides content type and filename.
|
|
||||||
if value.respond_to?(:content_type) &&
|
|
||||||
!value.content_type.blank? &&
|
|
||||||
!value.original_filename.blank?
|
|
||||||
unless value.respond_to?(:full_original_filename)
|
|
||||||
class << value
|
|
||||||
alias_method :full_original_filename, :original_filename
|
|
||||||
|
|
||||||
# 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
|
|
||||||
if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
|
|
||||||
md.captures.first
|
|
||||||
else
|
|
||||||
File.basename full_original_filename
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the same value after overriding original_filename.
|
|
||||||
value
|
|
||||||
|
|
||||||
# Multipart values may have content type, but no filename.
|
|
||||||
elsif value.respond_to?(:read)
|
|
||||||
result = value.read
|
|
||||||
value.rewind
|
|
||||||
result
|
|
||||||
|
|
||||||
# Unknown value, neither string nor multipart.
|
|
||||||
else
|
|
||||||
raise "Unknown form value: #{value.inspect}"
|
|
||||||
end
|
|
||||||
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
|
|
||||||
end
|
|
||||||
else
|
|
||||||
top << value
|
|
||||||
end
|
|
||||||
elsif top.is_a? Hash
|
|
||||||
key = CGI.unescape(key)
|
|
||||||
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
|
|
||||||
return top[key] ||= value
|
|
||||||
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."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -5,92 +5,18 @@ class CGI #:nodoc:
|
||||||
# Remove the old initialize_query method before redefining it.
|
# Remove the old initialize_query method before redefining it.
|
||||||
remove_method :initialize_query
|
remove_method :initialize_query
|
||||||
|
|
||||||
# Initialize the data from the query.
|
# Neuter CGI parameter parsing.
|
||||||
#
|
|
||||||
# Handles multipart forms (in particular, forms that involve file uploads).
|
|
||||||
# Reads query parameters in the @params field, and cookies into @cookies.
|
|
||||||
def initialize_query
|
def initialize_query
|
||||||
@cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
|
|
||||||
|
|
||||||
# Fix some strange request environments.
|
# Fix some strange request environments.
|
||||||
if method = env_table['REQUEST_METHOD']
|
env_table['REQUEST_METHOD'] ||= 'GET'
|
||||||
method = method.to_s.downcase.intern
|
|
||||||
else
|
|
||||||
method = :get
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST assumes missing Content-Type is application/x-www-form-urlencoded.
|
# POST assumes missing Content-Type is application/x-www-form-urlencoded.
|
||||||
content_type = env_table['CONTENT_TYPE']
|
if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST'
|
||||||
if content_type.blank? && method == :post
|
env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
|
||||||
content_type = 'application/x-www-form-urlencoded'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Force content length to zero if missing.
|
@cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
|
||||||
content_length = env_table['CONTENT_LENGTH'].to_i
|
@params = {}
|
||||||
|
|
||||||
# Set multipart to false by default.
|
|
||||||
@multipart = false
|
|
||||||
|
|
||||||
# POST and PUT may have params in entity body. If content type is missing
|
|
||||||
# or non-urlencoded, don't read the body or parse parameters: assume it's
|
|
||||||
# binary data.
|
|
||||||
if method == :post || method == :put
|
|
||||||
if boundary = extract_multipart_form_boundary(content_type)
|
|
||||||
@multipart = true
|
|
||||||
@params = read_multipart(boundary, content_length)
|
|
||||||
elsif content_type.blank? || content_type !~ %r{application/x-www-form-urlencoded}i
|
|
||||||
@params = {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@params ||= CGI.parse(read_params(method, content_length))
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
private
|
|
||||||
unless defined?(MULTIPART_FORM_BOUNDARY_RE)
|
|
||||||
MULTIPART_FORM_BOUNDARY_RE = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n #"
|
|
||||||
end
|
|
||||||
|
|
||||||
def extract_multipart_form_boundary(content_type)
|
|
||||||
MULTIPART_FORM_BOUNDARY_RE.match(content_type).to_a.pop
|
|
||||||
end
|
|
||||||
|
|
||||||
if defined? MOD_RUBY
|
|
||||||
def read_query
|
|
||||||
Apache::request.args || ''
|
|
||||||
end
|
|
||||||
else
|
|
||||||
def read_query
|
|
||||||
# fixes CGI querystring parsing for lighttpd
|
|
||||||
env_qs = env_table['QUERY_STRING']
|
|
||||||
if env_qs.blank? && !(uri = env_table['REQUEST_URI']).blank?
|
|
||||||
uri.split('?', 2)[1] || ''
|
|
||||||
else
|
|
||||||
env_qs || ''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_body(content_length)
|
|
||||||
stdinput.binmode if stdinput.respond_to?(:binmode)
|
|
||||||
content = stdinput.read(content_length) || ''
|
|
||||||
# Fix for Safari Ajax postings that always append \000
|
|
||||||
content.chop! if content[-1] == 0
|
|
||||||
content.gsub!(/&_=$/, '')
|
|
||||||
env_table['RAW_POST_DATA'] = content.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_params(method, content_length)
|
|
||||||
case method
|
|
||||||
when :get
|
|
||||||
read_query
|
|
||||||
when :post, :put
|
|
||||||
read_body(content_length)
|
|
||||||
when :cmd
|
|
||||||
read_from_cmdline
|
|
||||||
else # :head, :delete, :options, :trace, :connect
|
|
||||||
read_query
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end # module QueryExtension
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,12 +47,11 @@ module ActionController #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def query_string
|
def query_string
|
||||||
if (qs = @cgi.query_string) && !qs.empty?
|
qs = @cgi.query_string
|
||||||
|
if !qs.blank?
|
||||||
qs
|
qs
|
||||||
elsif uri = @env['REQUEST_URI']
|
elsif uri = @env['REQUEST_URI']
|
||||||
parts = uri.split('?')
|
uri.split('?', 2).last
|
||||||
parts.shift
|
|
||||||
parts.join('?')
|
|
||||||
else
|
else
|
||||||
@env['QUERY_STRING'] || ''
|
@env['QUERY_STRING'] || ''
|
||||||
end
|
end
|
||||||
|
@ -69,16 +68,11 @@ module ActionController #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def query_parameters
|
def query_parameters
|
||||||
@query_parameters ||= CGI.parse_query_parameters(query_string)
|
@query_parameters ||= self.class.parse_query_parameters(query_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_parameters
|
def request_parameters
|
||||||
@request_parameters ||=
|
@request_parameters ||= self.class.parse_formatted_request_parameters(body, content_type_with_parameters, content_length, env)
|
||||||
if ActionController::Base.param_parsers.has_key?(content_type)
|
|
||||||
self.class.parse_formatted_request_parameters(content_type, body.read)
|
|
||||||
else
|
|
||||||
CGI.parse_request_parameters(@cgi.params)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cookies
|
def cookies
|
||||||
|
|
|
@ -306,7 +306,7 @@ module ActionController
|
||||||
"REQUEST_URI" => "/",
|
"REQUEST_URI" => "/",
|
||||||
"HTTP_HOST" => host,
|
"HTTP_HOST" => host,
|
||||||
"SERVER_PORT" => https? ? "443" : "80",
|
"SERVER_PORT" => https? ? "443" : "80",
|
||||||
"HTTPS" => https? ? "on" : "off")
|
"HTTPS" => https? ? "on" : "off")
|
||||||
ActionController::UrlRewriter.new(ActionController::CgiRequest.new(cgi), {})
|
ActionController::UrlRewriter.new(ActionController::CgiRequest.new(cgi), {})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
require 'tempfile'
|
||||||
|
require 'stringio'
|
||||||
|
require 'strscan'
|
||||||
|
|
||||||
module ActionController
|
module ActionController
|
||||||
# CgiRequest and TestRequest provide concrete implementations.
|
# CgiRequest and TestRequest provide concrete implementations.
|
||||||
class AbstractRequest
|
class AbstractRequest
|
||||||
|
@ -55,6 +59,14 @@ module ActionController
|
||||||
@env
|
@env
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def content_length
|
||||||
|
@content_length ||= env['CONTENT_LENGTH'].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def content_type_with_parameters
|
||||||
|
@content_type_with_parameters ||= env['CONTENT_TYPE'].to_s
|
||||||
|
end
|
||||||
|
|
||||||
# Determine whether the body of a HTTP call is URL-encoded (default)
|
# Determine whether the body of a HTTP call is URL-encoded (default)
|
||||||
# or matches one of the registered param_parsers.
|
# or matches one of the registered param_parsers.
|
||||||
#
|
#
|
||||||
|
@ -64,7 +76,7 @@ module ActionController
|
||||||
@content_type ||=
|
@content_type ||=
|
||||||
begin
|
begin
|
||||||
# Receive header sans any charset information.
|
# Receive header sans any charset information.
|
||||||
content_type = @env['CONTENT_TYPE'].to_s.sub(/\s*\;.*$/, '').strip.downcase
|
content_type = content_type_with_parameters.sub(/\s*\;.*$/, '').strip.downcase
|
||||||
|
|
||||||
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
|
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
|
||||||
case x_post_format.to_s.downcase
|
case x_post_format.to_s.downcase
|
||||||
|
@ -297,20 +309,350 @@ module ActionController
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def self.parse_formatted_request_parameters(mime_type, body)
|
class << self
|
||||||
case strategy = ActionController::Base.param_parsers[mime_type]
|
def parse_formatted_request_parameters(body, content_type, content_length, env = {})
|
||||||
when Proc
|
content_length = content_length.to_i
|
||||||
strategy.call(body)
|
return {} if content_length.zero?
|
||||||
when :xml_simple, :xml_node
|
|
||||||
body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
|
content_type, boundary = extract_multipart_boundary(content_type.to_s)
|
||||||
when :yaml
|
return {} if content_type.blank?
|
||||||
YAML.load(body)
|
|
||||||
else
|
mime_type = Mime::Type.lookup(content_type)
|
||||||
{}
|
strategy = ActionController::Base.param_parsers[mime_type]
|
||||||
|
|
||||||
|
raise [content_type, content_length, mime_type, ActionController::Base.param_parsers].inspect unless strategy
|
||||||
|
|
||||||
|
# Only multipart form parsing expects a stream.
|
||||||
|
if strategy && strategy != :multipart_form
|
||||||
|
body = body.read(content_length)
|
||||||
|
end
|
||||||
|
|
||||||
|
case strategy
|
||||||
|
when Proc
|
||||||
|
strategy.call(body)
|
||||||
|
when :url_encoded_form
|
||||||
|
clean_up_ajax_request_body! body
|
||||||
|
parse_query_parameters(body)
|
||||||
|
when :multipart_form
|
||||||
|
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)
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
rescue Exception => e # YAML, XML or Ruby code block errors
|
||||||
|
raise
|
||||||
|
{ "body" => body,
|
||||||
|
"content_type" => content_type,
|
||||||
|
"content_length" => content_length,
|
||||||
|
"exception" => "#{e.message} (#{e.class})",
|
||||||
|
"backtrace" => e.backtrace }
|
||||||
end
|
end
|
||||||
rescue Exception => e # YAML, XML or Ruby code block errors
|
|
||||||
{ "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace,
|
def parse_query_parameters(query_string)
|
||||||
"body" => body, "format" => mime_type }
|
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, content_length, env)
|
||||||
|
parse_request_parameters(read_multipart(body, boundary, content_length, env))
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def get_typed_value(value)
|
||||||
|
case value
|
||||||
|
when String
|
||||||
|
value
|
||||||
|
when NilClass
|
||||||
|
''
|
||||||
|
when Array
|
||||||
|
value.map { |v| get_typed_value(v) }
|
||||||
|
else
|
||||||
|
# Uploaded file provides content type and filename.
|
||||||
|
if value.respond_to?(:content_type) &&
|
||||||
|
!value.content_type.blank? &&
|
||||||
|
!value.original_filename.blank?
|
||||||
|
unless value.respond_to?(:full_original_filename)
|
||||||
|
class << value
|
||||||
|
alias_method :full_original_filename, :original_filename
|
||||||
|
|
||||||
|
# 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
|
||||||
|
if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
|
||||||
|
md.captures.first
|
||||||
|
else
|
||||||
|
File.basename full_original_filename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the same value after overriding original_filename.
|
||||||
|
value
|
||||||
|
|
||||||
|
# Multipart values may have content type, but no filename.
|
||||||
|
elsif value.respond_to?(:read)
|
||||||
|
result = value.read
|
||||||
|
value.rewind
|
||||||
|
result
|
||||||
|
|
||||||
|
# Unknown value, neither string nor multipart.
|
||||||
|
else
|
||||||
|
raise "Unknown form value: #{value.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
|
||||||
|
|
||||||
|
def extract_multipart_boundary(content_type)
|
||||||
|
if content_type =~ MULTIPART_BOUNDARY
|
||||||
|
['multipart/form-data', $1.dup]
|
||||||
|
else
|
||||||
|
content_type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def clean_up_ajax_request_body!(body)
|
||||||
|
body.chop! if body[-1] == 0
|
||||||
|
body.gsub!(/&_=$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
EOL = "\015\012"
|
||||||
|
|
||||||
|
def read_multipart(body, boundary, content_length, env)
|
||||||
|
params = Hash.new([])
|
||||||
|
boundary = "--" + boundary
|
||||||
|
quoted_boundary = Regexp.quote(boundary, "n")
|
||||||
|
buf = ""
|
||||||
|
bufsize = 10 * 1024
|
||||||
|
boundary_end=""
|
||||||
|
|
||||||
|
# start multipart/form-data
|
||||||
|
body.binmode if defined? body.binmode
|
||||||
|
boundary_size = boundary.size + EOL.size
|
||||||
|
content_length -= 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 < content_length
|
||||||
|
Tempfile.new("CGI")
|
||||||
|
else
|
||||||
|
StringIO.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 < content_length
|
||||||
|
body.read(bufsize)
|
||||||
|
else
|
||||||
|
body.read(content_length)
|
||||||
|
end
|
||||||
|
if c.nil? || c.empty?
|
||||||
|
raise EOFError, "bad content body"
|
||||||
|
end
|
||||||
|
buf.concat(c)
|
||||||
|
content_length -= 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
|
||||||
|
content_length = -1
|
||||||
|
end
|
||||||
|
boundary_end = $2.dup
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
content.rewind
|
||||||
|
|
||||||
|
/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni.match(head)
|
||||||
|
filename = ($1 or $2 or "")
|
||||||
|
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-Type: (.*)/ni.match(head)
|
||||||
|
content_type = ($1 or "")
|
||||||
|
|
||||||
|
(class << content; self; end).class_eval do
|
||||||
|
alias local_path path
|
||||||
|
define_method(:original_filename) {filename.dup.taint}
|
||||||
|
define_method(:content_type) {content_type.dup.taint}
|
||||||
|
end
|
||||||
|
|
||||||
|
/Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
|
||||||
|
name = $1.dup
|
||||||
|
|
||||||
|
if params.has_key?(name)
|
||||||
|
params[name].push(content)
|
||||||
|
else
|
||||||
|
params[name] = [content]
|
||||||
|
end
|
||||||
|
break if buf.size == 0
|
||||||
|
break if content_length == -1
|
||||||
|
end
|
||||||
|
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
|
||||||
|
|
||||||
|
params
|
||||||
|
end
|
||||||
end
|
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
|
||||||
|
end
|
||||||
|
else
|
||||||
|
top << value
|
||||||
|
end
|
||||||
|
elsif top.is_a? Hash
|
||||||
|
key = CGI.unescape(key)
|
||||||
|
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
|
||||||
|
return top[key] ||= value
|
||||||
|
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."
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,373 +1,7 @@
|
||||||
require File.dirname(__FILE__) + '/../abstract_unit'
|
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||||
require 'action_controller/cgi_process'
|
require 'action_controller/cgi_process'
|
||||||
|
|
||||||
class CGITest < Test::Unit::TestCase
|
class CgiRequestTest < Test::Unit::TestCase
|
||||||
def setup
|
|
||||||
@query_string = "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1"
|
|
||||||
@query_string_with_empty = "action=create_customer&full_name="
|
|
||||||
@query_string_with_array = "action=create_customer&selected[]=1&selected[]=2&selected[]=3"
|
|
||||||
@query_string_with_amps = "action=create_customer&name=Don%27t+%26+Does"
|
|
||||||
@query_string_with_multiple_of_same_name =
|
|
||||||
"action=update_order&full_name=Lau%20Taarnskov&products=4&products=2&products=3"
|
|
||||||
@query_string_with_many_equal = "action=create_customer&full_name=abc=def=ghi"
|
|
||||||
@query_string_without_equal = "action"
|
|
||||||
@query_string_with_many_ampersands =
|
|
||||||
"&action=create_customer&&&full_name=David%20Heinemeier%20Hansson"
|
|
||||||
@query_string_with_empty_key = "action=create_customer&full_name=David%20Heinemeier%20Hansson&=Save"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_query_string
|
|
||||||
assert_equal(
|
|
||||||
{ "action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1"},
|
|
||||||
CGI.parse_query_parameters(@query_string)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_deep_query_string
|
|
||||||
expected = {'x' => {'y' => {'z' => '10'}}}
|
|
||||||
assert_equal(expected, CGI.parse_query_parameters('x[y][z]=10'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_deep_query_string_with_array
|
|
||||||
assert_equal({'x' => {'y' => {'z' => ['10']}}}, CGI.parse_query_parameters('x[y][z][]=10'))
|
|
||||||
assert_equal({'x' => {'y' => {'z' => ['10', '5']}}}, CGI.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'}]}}, CGI.parse_query_parameters('x[y][][z]=10'))
|
|
||||||
assert_equal({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, CGI.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'}]}}, CGI.parse_query_parameters('x[y][][z]=10&x[y][][z]=20'))
|
|
||||||
assert_equal("10", CGI.parse_query_parameters('x[y][][z]=10&x[y][][z]=20')["x"]["y"].first["z"])
|
|
||||||
assert_equal("10", CGI.parse_query_parameters('x[y][][z]=10&x[y][][z]=20').with_indifferent_access[:x][:y].first[:z])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_request_hash_parsing
|
|
||||||
query = {
|
|
||||||
"note[viewers][viewer][][type]" => ["User", "Group"],
|
|
||||||
"note[viewers][viewer][][id]" => ["1", "2"]
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } }
|
|
||||||
|
|
||||||
assert_equal(expected, CGI.parse_request_parameters(query))
|
|
||||||
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'}]}},
|
|
||||||
CGI.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" => ''},
|
|
||||||
CGI.parse_query_parameters(@query_string_with_empty)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_query_string_with_array
|
|
||||||
assert_equal(
|
|
||||||
{ "action" => "create_customer", "selected" => ["1", "2", "3"]},
|
|
||||||
CGI.parse_query_parameters(@query_string_with_array)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_query_string_with_amps
|
|
||||||
assert_equal(
|
|
||||||
{ "action" => "create_customer", "name" => "Don't & Does"},
|
|
||||||
CGI.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"},
|
|
||||||
CGI.parse_query_parameters(@query_string_with_many_equal)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_query_string_without_equal
|
|
||||||
assert_equal(
|
|
||||||
{ "action" => nil },
|
|
||||||
CGI.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" },
|
|
||||||
CGI.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"},
|
|
||||||
CGI.parse_query_parameters(@query_string_with_many_ampersands)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_parse_params
|
|
||||||
input = {
|
|
||||||
"customers[boston][first][name]" => [ "David" ],
|
|
||||||
"customers[boston][first][url]" => [ "http://David" ],
|
|
||||||
"customers[boston][second][name]" => [ "Allan" ],
|
|
||||||
"customers[boston][second][url]" => [ "http://Allan" ],
|
|
||||||
"something_else" => [ "blah" ],
|
|
||||||
"something_nil" => [ nil ],
|
|
||||||
"something_empty" => [ "" ],
|
|
||||||
"products[first]" => [ "Apple Computer" ],
|
|
||||||
"products[second]" => [ "Pc" ],
|
|
||||||
"" => [ 'Save' ]
|
|
||||||
}
|
|
||||||
|
|
||||||
expected_output = {
|
|
||||||
"customers" => {
|
|
||||||
"boston" => {
|
|
||||||
"first" => {
|
|
||||||
"name" => "David",
|
|
||||||
"url" => "http://David"
|
|
||||||
},
|
|
||||||
"second" => {
|
|
||||||
"name" => "Allan",
|
|
||||||
"url" => "http://Allan"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"something_else" => "blah",
|
|
||||||
"something_empty" => "",
|
|
||||||
"something_nil" => "",
|
|
||||||
"products" => {
|
|
||||||
"first" => "Apple Computer",
|
|
||||||
"second" => "Pc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal expected_output, CGI.parse_request_parameters(input)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_parse_params_from_multipart_upload
|
|
||||||
mockup = Struct.new(:content_type, :original_filename, :read, :rewind)
|
|
||||||
file = mockup.new('img/jpeg', 'foo.jpg')
|
|
||||||
ie_file = mockup.new('img/jpeg', 'c:\\Documents and Settings\\foo\\Desktop\\bar.jpg')
|
|
||||||
non_file_text_part = mockup.new('text/plain', '', 'abc')
|
|
||||||
|
|
||||||
input = {
|
|
||||||
"something" => [ StringIO.new("") ],
|
|
||||||
"array_of_stringios" => [[ StringIO.new("One"), StringIO.new("Two") ]],
|
|
||||||
"mixed_types_array" => [[ StringIO.new("Three"), "NotStringIO" ]],
|
|
||||||
"mixed_types_as_checkboxes[strings][nested]" => [[ file, "String", StringIO.new("StringIO")]],
|
|
||||||
"ie_mixed_types_as_checkboxes[strings][nested]" => [[ ie_file, "String", StringIO.new("StringIO")]],
|
|
||||||
"products[string]" => [ StringIO.new("Apple Computer") ],
|
|
||||||
"products[file]" => [ file ],
|
|
||||||
"ie_products[string]" => [ StringIO.new("Microsoft") ],
|
|
||||||
"ie_products[file]" => [ ie_file ],
|
|
||||||
"text_part" => [non_file_text_part]
|
|
||||||
}
|
|
||||||
|
|
||||||
expected_output = {
|
|
||||||
"something" => "",
|
|
||||||
"array_of_stringios" => ["One", "Two"],
|
|
||||||
"mixed_types_array" => [ "Three", "NotStringIO" ],
|
|
||||||
"mixed_types_as_checkboxes" => {
|
|
||||||
"strings" => {
|
|
||||||
"nested" => [ file, "String", "StringIO" ]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"ie_mixed_types_as_checkboxes" => {
|
|
||||||
"strings" => {
|
|
||||||
"nested" => [ ie_file, "String", "StringIO" ]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"products" => {
|
|
||||||
"string" => "Apple Computer",
|
|
||||||
"file" => file
|
|
||||||
},
|
|
||||||
"ie_products" => {
|
|
||||||
"string" => "Microsoft",
|
|
||||||
"file" => ie_file
|
|
||||||
},
|
|
||||||
"text_part" => "abc"
|
|
||||||
}
|
|
||||||
|
|
||||||
params = CGI.parse_request_parameters(input)
|
|
||||||
assert_equal expected_output, params
|
|
||||||
|
|
||||||
# Lone filenames are preserved.
|
|
||||||
assert_equal 'foo.jpg', params['mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
|
|
||||||
assert_equal 'foo.jpg', params['products']['file'].original_filename
|
|
||||||
|
|
||||||
# But full Windows paths are reduced to their basename.
|
|
||||||
assert_equal 'bar.jpg', params['ie_mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
|
|
||||||
assert_equal 'bar.jpg', params['ie_products']['file'].original_filename
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_parse_params_with_file
|
|
||||||
input = {
|
|
||||||
"customers[boston][first][name]" => [ "David" ],
|
|
||||||
"something_else" => [ "blah" ],
|
|
||||||
"logo" => [ File.new(File.dirname(__FILE__) + "/cgi_test.rb").path ]
|
|
||||||
}
|
|
||||||
|
|
||||||
expected_output = {
|
|
||||||
"customers" => {
|
|
||||||
"boston" => {
|
|
||||||
"first" => {
|
|
||||||
"name" => "David"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"something_else" => "blah",
|
|
||||||
"logo" => File.new(File.dirname(__FILE__) + "/cgi_test.rb").path,
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal expected_output, CGI.parse_request_parameters(input)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_parse_params_with_array
|
|
||||||
input = { "selected[]" => [ "1", "2", "3" ] }
|
|
||||||
|
|
||||||
expected_output = { "selected" => [ "1", "2", "3" ] }
|
|
||||||
|
|
||||||
assert_equal expected_output, CGI.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, CGI.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, CGI.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, CGI.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 , CGI.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, CGI.parse_request_parameters(input)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_parse_params_with_nil_key
|
|
||||||
input = { nil => nil, "test2" => %w(value1) }
|
|
||||||
expected = { "test2" => "value1" }
|
|
||||||
assert_equal expected, CGI.parse_request_parameters(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
class MultipartCGITest < Test::Unit::TestCase
|
|
||||||
FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart'
|
|
||||||
|
|
||||||
def setup
|
|
||||||
ENV['REQUEST_METHOD'] = 'POST'
|
|
||||||
ENV['CONTENT_LENGTH'] = '0'
|
|
||||||
ENV['CONTENT_TYPE'] = 'multipart/form-data, boundary=AaB03x'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_single_parameter
|
|
||||||
params = process('single_parameter')
|
|
||||||
assert_equal({ 'foo' => 'bar' }, params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_text_file
|
|
||||||
params = process('text_file')
|
|
||||||
assert_equal %w(file foo), params.keys.sort
|
|
||||||
assert_equal 'bar', params['foo']
|
|
||||||
|
|
||||||
file = params['file']
|
|
||||||
assert_kind_of StringIO, file
|
|
||||||
assert_equal 'file.txt', file.original_filename
|
|
||||||
assert_equal "text/plain\r", file.content_type
|
|
||||||
assert_equal 'contents', file.read
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_large_text_file
|
|
||||||
params = process('large_text_file')
|
|
||||||
assert_equal %w(file foo), params.keys.sort
|
|
||||||
assert_equal 'bar', params['foo']
|
|
||||||
|
|
||||||
file = params['file']
|
|
||||||
assert_kind_of Tempfile, file
|
|
||||||
assert_equal 'file.txt', file.original_filename
|
|
||||||
assert_equal "text/plain\r", file.content_type
|
|
||||||
assert ('a' * 20480) == file.read
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_binary_file
|
|
||||||
params = process('binary_file')
|
|
||||||
assert_equal %w(file flowers foo), params.keys.sort
|
|
||||||
assert_equal 'bar', params['foo']
|
|
||||||
|
|
||||||
file = params['file']
|
|
||||||
assert_kind_of StringIO, file
|
|
||||||
assert_equal 'file.txt', file.original_filename
|
|
||||||
assert_equal "text/plain\r", file.content_type
|
|
||||||
assert_equal 'contents', file.read
|
|
||||||
|
|
||||||
file = params['flowers']
|
|
||||||
assert_kind_of StringIO, file
|
|
||||||
assert_equal 'flowers.jpg', file.original_filename
|
|
||||||
assert_equal "image/jpeg\r", file.content_type
|
|
||||||
assert_equal 19512, file.size
|
|
||||||
#assert_equal File.read(File.dirname(__FILE__) + '/../../../activerecord/test/fixtures/flowers.jpg'), file.read
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_mixed_files
|
|
||||||
params = process('mixed_files')
|
|
||||||
assert_equal %w(files foo), params.keys.sort
|
|
||||||
assert_equal 'bar', params['foo']
|
|
||||||
|
|
||||||
# Ruby CGI doesn't handle multipart/mixed for us.
|
|
||||||
assert_kind_of String, params['files']
|
|
||||||
assert_equal 19756, params['files'].size
|
|
||||||
end
|
|
||||||
|
|
||||||
# Rewind readable cgi params so others may reread them (such as CGI::Session
|
|
||||||
# when passing the session id in a multipart form).
|
|
||||||
def test_multipart_param_rewound
|
|
||||||
params = process('text_file')
|
|
||||||
assert_equal 'bar', @cgi.params['foo'][0].read
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def process(name)
|
|
||||||
File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
|
|
||||||
ENV['CONTENT_LENGTH'] = file.stat.size.to_s
|
|
||||||
@cgi = CGI.new('query', file)
|
|
||||||
CGI.parse_request_parameters @cgi.params
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ensures that PUT works with multipart as well as POST.
|
|
||||||
class PutMultipartCGITest < MultipartCGITest
|
|
||||||
def setup
|
|
||||||
super
|
|
||||||
ENV['REQUEST_METHOD'] = 'PUT'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
class CGIRequestTest < Test::Unit::TestCase
|
|
||||||
def setup
|
def setup
|
||||||
@request_hash = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"}
|
@request_hash = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"}
|
||||||
# cookie as returned by some Nokia phone browsers (no space after semicolon separator)
|
# cookie as returned by some Nokia phone browsers (no space after semicolon separator)
|
||||||
|
@ -375,20 +9,20 @@ class CGIRequestTest < Test::Unit::TestCase
|
||||||
@fake_cgi = Struct.new(:env_table).new(@request_hash)
|
@fake_cgi = Struct.new(:env_table).new(@request_hash)
|
||||||
@request = ActionController::CgiRequest.new(@fake_cgi)
|
@request = ActionController::CgiRequest.new(@fake_cgi)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_proxy_request
|
def test_proxy_request
|
||||||
assert_equal 'glu.ttono.us', @request.host_with_port
|
assert_equal 'glu.ttono.us', @request.host_with_port
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_http_host
|
def test_http_host
|
||||||
@request_hash.delete "HTTP_X_FORWARDED_HOST"
|
@request_hash.delete "HTTP_X_FORWARDED_HOST"
|
||||||
@request_hash['HTTP_HOST'] = "rubyonrails.org:8080"
|
@request_hash['HTTP_HOST'] = "rubyonrails.org:8080"
|
||||||
assert_equal "rubyonrails.org:8080", @request.host_with_port
|
assert_equal "rubyonrails.org:8080", @request.host_with_port
|
||||||
|
|
||||||
@request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
|
@request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
|
||||||
assert_equal "www.secondhost.org", @request.host
|
assert_equal "www.secondhost.org", @request.host
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_http_host_with_default_port_overrides_server_port
|
def test_http_host_with_default_port_overrides_server_port
|
||||||
@request_hash.delete "HTTP_X_FORWARDED_HOST"
|
@request_hash.delete "HTTP_X_FORWARDED_HOST"
|
||||||
@request_hash['HTTP_HOST'] = "rubyonrails.org"
|
@request_hash['HTTP_HOST'] = "rubyonrails.org"
|
||||||
|
@ -412,21 +46,9 @@ class CGIRequestTest < Test::Unit::TestCase
|
||||||
cookies = CGI::Cookie::parse(@request_hash["HTTP_COOKIE"]);
|
cookies = CGI::Cookie::parse(@request_hash["HTTP_COOKIE"]);
|
||||||
assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"]
|
assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"]
|
||||||
assert_equal ["yes"], cookies["is_admin"]
|
assert_equal ["yes"], cookies["is_admin"]
|
||||||
|
|
||||||
alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
|
alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
|
||||||
assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"]
|
assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"]
|
||||||
assert_equal ["yes"], alt_cookies["is_admin"]
|
assert_equal ["yes"], alt_cookies["is_admin"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_unbalanced_query_string_with_array
|
|
||||||
assert_equal(
|
|
||||||
{'location' => ["1", "2"], 'age_group' => ["2"]},
|
|
||||||
CGI.parse_query_parameters("location[]=1&location[]=2&age_group[]=2")
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
{'location' => ["1", "2"], 'age_group' => ["2"]},
|
|
||||||
CGI.parse_request_parameters({'location[]' => ["1", "2"],
|
|
||||||
'age_group[]' => ["2"]})
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -232,11 +232,13 @@ class MimeControllerTest < Test::Unit::TestCase
|
||||||
assert_equal "<p>Hello world!</p>\n", @response.body
|
assert_equal "<p>Hello world!</p>\n", @response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_with_content_type
|
def test_with_atom_content_type
|
||||||
@request.env["CONTENT_TYPE"] = "application/atom+xml"
|
@request.env["CONTENT_TYPE"] = "application/atom+xml"
|
||||||
get :made_for_content_type
|
get :made_for_content_type
|
||||||
assert_equal "ATOM", @response.body
|
assert_equal "ATOM", @response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_with_rss_content_type
|
||||||
@request.env["CONTENT_TYPE"] = "application/rss+xml"
|
@request.env["CONTENT_TYPE"] = "application/rss+xml"
|
||||||
get :made_for_content_type
|
get :made_for_content_type
|
||||||
assert_equal "RSS", @response.body
|
assert_equal "RSS", @response.body
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
require "#{File.dirname(__FILE__)}/../abstract_unit"
|
|
||||||
|
|
||||||
class RawPostDataTest < Test::Unit::TestCase
|
|
||||||
def setup
|
|
||||||
ENV.delete('RAW_POST_DATA')
|
|
||||||
@request_body = 'a=1'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_post_with_urlencoded_body
|
|
||||||
ENV['REQUEST_METHOD'] = 'POST'
|
|
||||||
ENV['CONTENT_TYPE'] = ' apPlication/x-Www-form-urlEncoded; charset=utf-8'
|
|
||||||
assert_equal ['1'], cgi.params['a']
|
|
||||||
assert_raw_post_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_post_with_empty_content_type_treated_as_urlencoded
|
|
||||||
ENV['REQUEST_METHOD'] = 'POST'
|
|
||||||
ENV['CONTENT_TYPE'] = ''
|
|
||||||
assert_equal ['1'], cgi.params['a']
|
|
||||||
assert_raw_post_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_post_with_unrecognized_content_type_ignores_body
|
|
||||||
ENV['REQUEST_METHOD'] = 'POST'
|
|
||||||
ENV['CONTENT_TYPE'] = 'foo/bar'
|
|
||||||
assert cgi.params.empty?
|
|
||||||
assert_no_raw_post_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_put_with_urlencoded_body
|
|
||||||
ENV['REQUEST_METHOD'] = 'PUT'
|
|
||||||
ENV['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
|
|
||||||
assert_equal ['1'], cgi.params['a']
|
|
||||||
assert_raw_post_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_put_with_empty_content_type_ignores_body
|
|
||||||
ENV['REQUEST_METHOD'] = 'PUT'
|
|
||||||
ENV['CONTENT_TYPE'] = ''
|
|
||||||
assert cgi.params.empty?
|
|
||||||
assert_no_raw_post_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_put_with_unrecognized_content_type_ignores_body
|
|
||||||
ENV['REQUEST_METHOD'] = 'PUT'
|
|
||||||
ENV['CONTENT_TYPE'] = 'foo/bar'
|
|
||||||
assert cgi.params.empty?
|
|
||||||
assert_no_raw_post_data
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def cgi
|
|
||||||
unless defined? @cgi
|
|
||||||
ENV['CONTENT_LENGTH'] = @request_body.size.to_s
|
|
||||||
@cgi = CGI.new('query', StringIO.new(@request_body.dup))
|
|
||||||
end
|
|
||||||
|
|
||||||
@cgi
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_raw_post_data
|
|
||||||
assert_not_nil ENV['RAW_POST_DATA']
|
|
||||||
assert ENV['RAW_POST_DATA'].frozen?
|
|
||||||
assert_equal @request_body, ENV['RAW_POST_DATA']
|
|
||||||
|
|
||||||
assert_equal '', cgi.stdinput.read
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_no_raw_post_data
|
|
||||||
assert_nil ENV['RAW_POST_DATA']
|
|
||||||
|
|
||||||
assert_equal @request_body, cgi.stdinput.read
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -44,7 +44,7 @@ class RequestTest < Test::Unit::TestCase
|
||||||
|
|
||||||
@request.host = "www.rubyonrails.co.uk"
|
@request.host = "www.rubyonrails.co.uk"
|
||||||
assert_equal "rubyonrails.co.uk", @request.domain(2)
|
assert_equal "rubyonrails.co.uk", @request.domain(2)
|
||||||
|
|
||||||
@request.host = "192.168.1.200"
|
@request.host = "192.168.1.200"
|
||||||
assert_nil @request.domain
|
assert_nil @request.domain
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.host = nil
|
@request.host = nil
|
||||||
assert_equal [], @request.subdomains
|
assert_equal [], @request.subdomains
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_port_string
|
def test_port_string
|
||||||
@request.port = 80
|
@request.port = 80
|
||||||
assert_equal "", @request.port_string
|
assert_equal "", @request.port_string
|
||||||
|
@ -76,14 +76,14 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.port = 8080
|
@request.port = 8080
|
||||||
assert_equal ":8080", @request.port_string
|
assert_equal ":8080", @request.port_string
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_relative_url_root
|
def test_relative_url_root
|
||||||
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
||||||
@request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
|
@request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
|
||||||
assert_equal '', @request.relative_url_root, "relative_url_root should be disabled on lighttpd"
|
assert_equal '', @request.relative_url_root, "relative_url_root should be disabled on lighttpd"
|
||||||
|
|
||||||
@request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text'
|
@request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text'
|
||||||
|
|
||||||
@request.env['SCRIPT_NAME'] = nil
|
@request.env['SCRIPT_NAME'] = nil
|
||||||
assert_equal "", @request.relative_url_root
|
assert_equal "", @request.relative_url_root
|
||||||
|
|
||||||
|
@ -99,19 +99,19 @@ class RequestTest < Test::Unit::TestCase
|
||||||
|
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
|
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
|
||||||
assert_equal "/collaboration/hieraki", @request.relative_url_root
|
assert_equal "/collaboration/hieraki", @request.relative_url_root
|
||||||
|
|
||||||
# apache/scgi case
|
# apache/scgi case
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki"
|
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki"
|
||||||
assert_equal "/collaboration/hieraki", @request.relative_url_root
|
assert_equal "/collaboration/hieraki", @request.relative_url_root
|
||||||
|
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
||||||
@request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
|
@request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3'
|
||||||
@request.env['RAILS_RELATIVE_URL_ROOT'] = "/hieraki"
|
@request.env['RAILS_RELATIVE_URL_ROOT'] = "/hieraki"
|
||||||
assert_equal "/hieraki", @request.relative_url_root
|
assert_equal "/hieraki", @request.relative_url_root
|
||||||
|
|
||||||
# @env overrides path guess
|
# @env overrides path guess
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
||||||
|
@ -119,15 +119,15 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.env['RAILS_RELATIVE_URL_ROOT'] = "/real_url"
|
@request.env['RAILS_RELATIVE_URL_ROOT'] = "/real_url"
|
||||||
assert_equal "/real_url", @request.relative_url_root
|
assert_equal "/real_url", @request.relative_url_root
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_request_uri
|
def test_request_uri
|
||||||
@request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432'
|
@request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432'
|
||||||
|
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1"
|
@request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1"
|
||||||
assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
|
assert_equal "/path/of/some/uri?mapped=1", @request.request_uri
|
||||||
assert_equal "/path/of/some/uri", @request.path
|
assert_equal "/path/of/some/uri", @request.path
|
||||||
|
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri"
|
@request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri"
|
||||||
assert_equal "/path/of/some/uri", @request.request_uri
|
assert_equal "/path/of/some/uri", @request.request_uri
|
||||||
|
@ -147,25 +147,25 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.set_REQUEST_URI "/?m=b"
|
@request.set_REQUEST_URI "/?m=b"
|
||||||
assert_equal "/?m=b", @request.request_uri
|
assert_equal "/?m=b", @request.request_uri
|
||||||
assert_equal "/", @request.path
|
assert_equal "/", @request.path
|
||||||
|
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.set_REQUEST_URI "/"
|
@request.set_REQUEST_URI "/"
|
||||||
@request.env['SCRIPT_NAME'] = "/dispatch.cgi"
|
@request.env['SCRIPT_NAME'] = "/dispatch.cgi"
|
||||||
assert_equal "/", @request.request_uri
|
assert_equal "/", @request.request_uri
|
||||||
assert_equal "/", @request.path
|
assert_equal "/", @request.path
|
||||||
|
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.set_REQUEST_URI "/hieraki/"
|
@request.set_REQUEST_URI "/hieraki/"
|
||||||
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
@request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi"
|
||||||
assert_equal "/hieraki/", @request.request_uri
|
assert_equal "/hieraki/", @request.request_uri
|
||||||
assert_equal "/", @request.path
|
assert_equal "/", @request.path
|
||||||
|
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.set_REQUEST_URI "/collaboration/hieraki/books/edit/2"
|
@request.set_REQUEST_URI "/collaboration/hieraki/books/edit/2"
|
||||||
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
|
@request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi"
|
||||||
assert_equal "/collaboration/hieraki/books/edit/2", @request.request_uri
|
assert_equal "/collaboration/hieraki/books/edit/2", @request.request_uri
|
||||||
assert_equal "/books/edit/2", @request.path
|
assert_equal "/books/edit/2", @request.path
|
||||||
|
|
||||||
# The following tests are for when REQUEST_URI is not supplied (as in IIS)
|
# The following tests are for when REQUEST_URI is not supplied (as in IIS)
|
||||||
@request.relative_url_root = nil
|
@request.relative_url_root = nil
|
||||||
@request.set_REQUEST_URI nil
|
@request.set_REQUEST_URI nil
|
||||||
|
@ -238,30 +238,30 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.host = "rubyonrails.org"
|
@request.host = "rubyonrails.org"
|
||||||
@request.port = 80
|
@request.port = 80
|
||||||
assert_equal "rubyonrails.org", @request.host_with_port
|
assert_equal "rubyonrails.org", @request.host_with_port
|
||||||
|
|
||||||
@request.host = "rubyonrails.org"
|
@request.host = "rubyonrails.org"
|
||||||
@request.port = 81
|
@request.port = 81
|
||||||
assert_equal "rubyonrails.org:81", @request.host_with_port
|
assert_equal "rubyonrails.org:81", @request.host_with_port
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_server_software
|
def test_server_software
|
||||||
assert_equal nil, @request.server_software
|
assert_equal nil, @request.server_software
|
||||||
|
|
||||||
@request.env['SERVER_SOFTWARE'] = 'Apache3.422'
|
@request.env['SERVER_SOFTWARE'] = 'Apache3.422'
|
||||||
assert_equal 'apache', @request.server_software
|
assert_equal 'apache', @request.server_software
|
||||||
|
|
||||||
@request.env['SERVER_SOFTWARE'] = 'lighttpd(1.1.4)'
|
@request.env['SERVER_SOFTWARE'] = 'lighttpd(1.1.4)'
|
||||||
assert_equal 'lighttpd', @request.server_software
|
assert_equal 'lighttpd', @request.server_software
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_xml_http_request
|
def test_xml_http_request
|
||||||
assert !@request.xml_http_request?
|
assert !@request.xml_http_request?
|
||||||
assert !@request.xhr?
|
assert !@request.xhr?
|
||||||
|
|
||||||
@request.env['HTTP_X_REQUESTED_WITH'] = "DefinitelyNotAjax1.0"
|
@request.env['HTTP_X_REQUESTED_WITH'] = "DefinitelyNotAjax1.0"
|
||||||
assert !@request.xml_http_request?
|
assert !@request.xml_http_request?
|
||||||
assert !@request.xhr?
|
assert !@request.xhr?
|
||||||
|
|
||||||
@request.env['HTTP_X_REQUESTED_WITH'] = "XMLHttpRequest"
|
@request.env['HTTP_X_REQUESTED_WITH'] = "XMLHttpRequest"
|
||||||
assert @request.xml_http_request?
|
assert @request.xml_http_request?
|
||||||
assert @request.xhr?
|
assert @request.xhr?
|
||||||
|
@ -301,7 +301,7 @@ class RequestTest < Test::Unit::TestCase
|
||||||
assert_equal method, @request.method
|
assert_equal method, @request.method
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_head_masquarading_as_get
|
def test_head_masquarading_as_get
|
||||||
set_request_method_to :head
|
set_request_method_to :head
|
||||||
assert_equal :get, @request.method
|
assert_equal :get, @request.method
|
||||||
|
@ -313,12 +313,12 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.instance_eval { @parameters = { :format => 'xml' } }
|
@request.instance_eval { @parameters = { :format => 'xml' } }
|
||||||
assert_equal Mime::XML, @request.format
|
assert_equal Mime::XML, @request.format
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_xhtml_format
|
def test_xhtml_format
|
||||||
@request.instance_eval { @parameters = { :format => 'xhtml' } }
|
@request.instance_eval { @parameters = { :format => 'xhtml' } }
|
||||||
assert_equal Mime::HTML, @request.format
|
assert_equal Mime::HTML, @request.format
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_txt_format
|
def test_txt_format
|
||||||
@request.instance_eval { @parameters = { :format => 'txt' } }
|
@request.instance_eval { @parameters = { :format => 'txt' } }
|
||||||
assert_equal Mime::TEXT, @request.format
|
assert_equal Mime::TEXT, @request.format
|
||||||
|
@ -329,7 +329,7 @@ class RequestTest < Test::Unit::TestCase
|
||||||
@request.env["HTTP_ACCEPT"] = "text/javascript"
|
@request.env["HTTP_ACCEPT"] = "text/javascript"
|
||||||
assert_equal Mime::JS, @request.format
|
assert_equal Mime::JS, @request.format
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_content_type
|
def test_content_type
|
||||||
@request.env["CONTENT_TYPE"] = "text/html"
|
@request.env["CONTENT_TYPE"] = "text/html"
|
||||||
assert_equal Mime::HTML, @request.content_type
|
assert_equal Mime::HTML, @request.content_type
|
||||||
|
@ -338,7 +338,7 @@ class RequestTest < Test::Unit::TestCase
|
||||||
def test_content_no_type
|
def test_content_no_type
|
||||||
assert_equal nil, @request.content_type
|
assert_equal nil, @request.content_type
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_content_type_xml
|
def test_content_type_xml
|
||||||
@request.env["CONTENT_TYPE"] = "application/xml"
|
@request.env["CONTENT_TYPE"] = "application/xml"
|
||||||
assert_equal Mime::XML, @request.content_type
|
assert_equal Mime::XML, @request.content_type
|
||||||
|
@ -357,18 +357,377 @@ class RequestTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
class RequestParameterParsingTest < Test::Unit::TestCase
|
class UrlEncodedRequestParameterParsingTest < Test::Unit::TestCase
|
||||||
def test_xml_with_single_file
|
def setup
|
||||||
|
@query_string = "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1"
|
||||||
|
@query_string_with_empty = "action=create_customer&full_name="
|
||||||
|
@query_string_with_array = "action=create_customer&selected[]=1&selected[]=2&selected[]=3"
|
||||||
|
@query_string_with_amps = "action=create_customer&name=Don%27t+%26+Does"
|
||||||
|
@query_string_with_multiple_of_same_name =
|
||||||
|
"action=update_order&full_name=Lau%20Taarnskov&products=4&products=2&products=3"
|
||||||
|
@query_string_with_many_equal = "action=create_customer&full_name=abc=def=ghi"
|
||||||
|
@query_string_without_equal = "action"
|
||||||
|
@query_string_with_many_ampersands =
|
||||||
|
"&action=create_customer&&&full_name=David%20Heinemeier%20Hansson"
|
||||||
|
@query_string_with_empty_key = "action=create_customer&full_name=David%20Heinemeier%20Hansson&=Save"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_query_string
|
||||||
|
assert_equal(
|
||||||
|
{ "action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1"},
|
||||||
|
ActionController::AbstractRequest.parse_query_parameters(@query_string)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_deep_query_string
|
||||||
|
expected = {'x' => {'y' => {'z' => '10'}}}
|
||||||
|
assert_equal(expected, ActionController::AbstractRequest.parse_query_parameters('x[y][z]=10'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_deep_query_string_with_array
|
||||||
|
assert_equal({'x' => {'y' => {'z' => ['10']}}}, ActionController::AbstractRequest.parse_query_parameters('x[y][z][]=10'))
|
||||||
|
assert_equal({'x' => {'y' => {'z' => ['10', '5']}}}, ActionController::AbstractRequest.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::AbstractRequest.parse_query_parameters('x[y][][z]=10'))
|
||||||
|
assert_equal({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, ActionController::AbstractRequest.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::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20'))
|
||||||
|
assert_equal("10", ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20')["x"]["y"].first["z"])
|
||||||
|
assert_equal("10", ActionController::AbstractRequest.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::AbstractRequest.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::AbstractRequest.parse_query_parameters(@query_string_with_empty)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_query_string_with_array
|
||||||
|
assert_equal(
|
||||||
|
{ "action" => "create_customer", "selected" => ["1", "2", "3"]},
|
||||||
|
ActionController::AbstractRequest.parse_query_parameters(@query_string_with_array)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_query_string_with_amps
|
||||||
|
assert_equal(
|
||||||
|
{ "action" => "create_customer", "name" => "Don't & Does"},
|
||||||
|
ActionController::AbstractRequest.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::AbstractRequest.parse_query_parameters(@query_string_with_many_equal)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_query_string_without_equal
|
||||||
|
assert_equal(
|
||||||
|
{ "action" => nil },
|
||||||
|
ActionController::AbstractRequest.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::AbstractRequest.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::AbstractRequest.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::AbstractRequest.parse_query_parameters("location[]=1&location[]=2&age_group[]=2")
|
||||||
|
)
|
||||||
|
assert_equal(
|
||||||
|
{'location' => ["1", "2"], 'age_group' => ["2"]},
|
||||||
|
ActionController::AbstractRequest.parse_request_parameters({'location[]' => ["1", "2"],
|
||||||
|
'age_group[]' => ["2"]})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_request_hash_parsing
|
||||||
|
query = {
|
||||||
|
"note[viewers][viewer][][type]" => ["User", "Group"],
|
||||||
|
"note[viewers][viewer][][id]" => ["1", "2"]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } }
|
||||||
|
|
||||||
|
assert_equal(expected, ActionController::AbstractRequest.parse_request_parameters(query))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_params
|
||||||
|
input = {
|
||||||
|
"customers[boston][first][name]" => [ "David" ],
|
||||||
|
"customers[boston][first][url]" => [ "http://David" ],
|
||||||
|
"customers[boston][second][name]" => [ "Allan" ],
|
||||||
|
"customers[boston][second][url]" => [ "http://Allan" ],
|
||||||
|
"something_else" => [ "blah" ],
|
||||||
|
"something_nil" => [ nil ],
|
||||||
|
"something_empty" => [ "" ],
|
||||||
|
"products[first]" => [ "Apple Computer" ],
|
||||||
|
"products[second]" => [ "Pc" ],
|
||||||
|
"" => [ 'Save' ]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_output = {
|
||||||
|
"customers" => {
|
||||||
|
"boston" => {
|
||||||
|
"first" => {
|
||||||
|
"name" => "David",
|
||||||
|
"url" => "http://David"
|
||||||
|
},
|
||||||
|
"second" => {
|
||||||
|
"name" => "Allan",
|
||||||
|
"url" => "http://Allan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"something_else" => "blah",
|
||||||
|
"something_empty" => "",
|
||||||
|
"something_nil" => "",
|
||||||
|
"products" => {
|
||||||
|
"first" => "Apple Computer",
|
||||||
|
"second" => "Pc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_parse_params_from_multipart_upload
|
||||||
|
mockup = Struct.new(:content_type, :original_filename, :read, :rewind)
|
||||||
|
file = mockup.new('img/jpeg', 'foo.jpg')
|
||||||
|
ie_file = mockup.new('img/jpeg', 'c:\\Documents and Settings\\foo\\Desktop\\bar.jpg')
|
||||||
|
non_file_text_part = mockup.new('text/plain', '', 'abc')
|
||||||
|
|
||||||
|
input = {
|
||||||
|
"something" => [ StringIO.new("") ],
|
||||||
|
"array_of_stringios" => [[ StringIO.new("One"), StringIO.new("Two") ]],
|
||||||
|
"mixed_types_array" => [[ StringIO.new("Three"), "NotStringIO" ]],
|
||||||
|
"mixed_types_as_checkboxes[strings][nested]" => [[ file, "String", StringIO.new("StringIO")]],
|
||||||
|
"ie_mixed_types_as_checkboxes[strings][nested]" => [[ ie_file, "String", StringIO.new("StringIO")]],
|
||||||
|
"products[string]" => [ StringIO.new("Apple Computer") ],
|
||||||
|
"products[file]" => [ file ],
|
||||||
|
"ie_products[string]" => [ StringIO.new("Microsoft") ],
|
||||||
|
"ie_products[file]" => [ ie_file ],
|
||||||
|
"text_part" => [non_file_text_part]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_output = {
|
||||||
|
"something" => "",
|
||||||
|
"array_of_stringios" => ["One", "Two"],
|
||||||
|
"mixed_types_array" => [ "Three", "NotStringIO" ],
|
||||||
|
"mixed_types_as_checkboxes" => {
|
||||||
|
"strings" => {
|
||||||
|
"nested" => [ file, "String", "StringIO" ]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ie_mixed_types_as_checkboxes" => {
|
||||||
|
"strings" => {
|
||||||
|
"nested" => [ ie_file, "String", "StringIO" ]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"products" => {
|
||||||
|
"string" => "Apple Computer",
|
||||||
|
"file" => file
|
||||||
|
},
|
||||||
|
"ie_products" => {
|
||||||
|
"string" => "Microsoft",
|
||||||
|
"file" => ie_file
|
||||||
|
},
|
||||||
|
"text_part" => "abc"
|
||||||
|
}
|
||||||
|
|
||||||
|
params = ActionController::AbstractRequest.parse_request_parameters(input)
|
||||||
|
assert_equal expected_output, params
|
||||||
|
|
||||||
|
# Lone filenames are preserved.
|
||||||
|
assert_equal 'foo.jpg', params['mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
|
||||||
|
assert_equal 'foo.jpg', params['products']['file'].original_filename
|
||||||
|
|
||||||
|
# But full Windows paths are reduced to their basename.
|
||||||
|
assert_equal 'bar.jpg', params['ie_mixed_types_as_checkboxes']['strings']['nested'].first.original_filename
|
||||||
|
assert_equal 'bar.jpg', params['ie_products']['file'].original_filename
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_parse_params_with_file
|
||||||
|
input = {
|
||||||
|
"customers[boston][first][name]" => [ "David" ],
|
||||||
|
"something_else" => [ "blah" ],
|
||||||
|
"logo" => [ File.new(File.dirname(__FILE__) + "/cgi_test.rb").path ]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_output = {
|
||||||
|
"customers" => {
|
||||||
|
"boston" => {
|
||||||
|
"first" => {
|
||||||
|
"name" => "David"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"something_else" => "blah",
|
||||||
|
"logo" => File.new(File.dirname(__FILE__) + "/cgi_test.rb").path,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_parse_params_with_array
|
||||||
|
input = { "selected[]" => [ "1", "2", "3" ] }
|
||||||
|
|
||||||
|
expected_output = { "selected" => [ "1", "2", "3" ] }
|
||||||
|
|
||||||
|
assert_equal expected_output, ActionController::AbstractRequest.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::AbstractRequest.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::AbstractRequest.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::AbstractRequest.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::AbstractRequest.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::AbstractRequest.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::AbstractRequest.parse_request_parameters(input)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class MultipartRequestParameterParsingTest < Test::Unit::TestCase
|
||||||
|
FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart'
|
||||||
|
|
||||||
|
def test_single_parameter
|
||||||
|
params = process('single_parameter')
|
||||||
|
assert_equal({ 'foo' => 'bar' }, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_text_file
|
||||||
|
params = process('text_file')
|
||||||
|
assert_equal %w(file foo), params.keys.sort
|
||||||
|
assert_equal 'bar', params['foo']
|
||||||
|
|
||||||
|
file = params['file']
|
||||||
|
assert_kind_of StringIO, file
|
||||||
|
assert_equal 'file.txt', file.original_filename
|
||||||
|
assert_equal "text/plain\r", file.content_type
|
||||||
|
assert_equal 'contents', file.read
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_large_text_file
|
||||||
|
params = process('large_text_file')
|
||||||
|
assert_equal %w(file foo), params.keys.sort
|
||||||
|
assert_equal 'bar', params['foo']
|
||||||
|
|
||||||
|
file = params['file']
|
||||||
|
assert_kind_of Tempfile, file
|
||||||
|
assert_equal 'file.txt', file.original_filename
|
||||||
|
assert_equal "text/plain\r", file.content_type
|
||||||
|
assert ('a' * 20480) == file.read
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_binary_file
|
||||||
|
params = process('binary_file')
|
||||||
|
assert_equal %w(file flowers foo), params.keys.sort
|
||||||
|
assert_equal 'bar', params['foo']
|
||||||
|
|
||||||
|
file = params['file']
|
||||||
|
assert_kind_of StringIO, file
|
||||||
|
assert_equal 'file.txt', file.original_filename
|
||||||
|
assert_equal "text/plain\r", file.content_type
|
||||||
|
assert_equal 'contents', file.read
|
||||||
|
|
||||||
|
file = params['flowers']
|
||||||
|
assert_kind_of StringIO, file
|
||||||
|
assert_equal 'flowers.jpg', file.original_filename
|
||||||
|
assert_equal "image/jpeg\r", file.content_type
|
||||||
|
assert_equal 19512, file.size
|
||||||
|
#assert_equal File.read(File.dirname(__FILE__) + '/../../../activerecord/test/fixtures/flowers.jpg'), file.read
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_mixed_files
|
||||||
|
params = process('mixed_files')
|
||||||
|
assert_equal %w(files foo), params.keys.sort
|
||||||
|
assert_equal 'bar', params['foo']
|
||||||
|
|
||||||
|
# Ruby CGI doesn't handle multipart/mixed for us.
|
||||||
|
assert_kind_of String, params['files']
|
||||||
|
assert_equal 19756, params['files'].size
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def process(name)
|
||||||
|
File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
|
||||||
|
content_length = file.stat.size.to_s
|
||||||
|
content_type = 'multipart/form-data, boundary=AaB03x'
|
||||||
|
ActionController::AbstractRequest.parse_formatted_request_parameters(file, content_type, content_length)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class XmlParamsParsingTest < Test::Unit::TestCase
|
||||||
|
def test_single_file
|
||||||
body = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{Base64.encode64('ABC')}</avatar></person>"
|
body = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{Base64.encode64('ABC')}</avatar></person>"
|
||||||
|
|
||||||
person = ActionController::AbstractRequest.parse_formatted_request_parameters(Mime::XML, body)
|
person = ActionController::AbstractRequest.parse_formatted_request_parameters(StringIO.new(body), 'application/xml', body.size)
|
||||||
|
|
||||||
assert_equal "image/jpg", person['person']['avatar'].content_type
|
assert_equal "image/jpg", person['person']['avatar'].content_type
|
||||||
assert_equal "me.jpg", person['person']['avatar'].original_filename
|
assert_equal "me.jpg", person['person']['avatar'].original_filename
|
||||||
assert_equal "ABC", person['person']['avatar'].read
|
assert_equal "ABC", person['person']['avatar'].read
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_xml_with_multiple_files
|
def test_multiple_files
|
||||||
body = <<-end_body
|
body = <<-end_body
|
||||||
<person>
|
<person>
|
||||||
<name>David</name>
|
<name>David</name>
|
||||||
|
@ -379,7 +738,7 @@ class RequestParameterParsingTest < Test::Unit::TestCase
|
||||||
</person>
|
</person>
|
||||||
end_body
|
end_body
|
||||||
|
|
||||||
person = ActionController::AbstractRequest.parse_formatted_request_parameters(Mime::XML, body)
|
person = ActionController::AbstractRequest.parse_formatted_request_parameters(StringIO.new(body), 'application/xml', body.size)
|
||||||
|
|
||||||
assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type
|
assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type
|
||||||
assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename
|
assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename
|
||||||
|
@ -390,4 +749,3 @@ class RequestParameterParsingTest < Test::Unit::TestCase
|
||||||
assert_equal "DEF", person['person']['avatars']['avatar'].last.read
|
assert_equal "DEF", person['person']['avatars']['avatar'].last.read
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -199,10 +199,9 @@ class CookieStoreTest < Test::Unit::TestCase
|
||||||
ENV['HTTP_HOST'] = 'example.com'
|
ENV['HTTP_HOST'] = 'example.com'
|
||||||
ENV['QUERY_STRING'] = ''
|
ENV['QUERY_STRING'] = ''
|
||||||
|
|
||||||
$stdin, old_stdin = StringIO.new(''), $stdin
|
cgi = CGI.new('query', StringIO.new(''))
|
||||||
yield CGI.new
|
yield cgi if block_given?
|
||||||
ensure
|
cgi
|
||||||
$stdin = old_stdin
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,13 @@ class WebServiceTest < Test::Unit::TestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@controller = TestController.new
|
@controller = TestController.new
|
||||||
ActionController::Base.param_parsers.clear
|
@default_param_parsers = ActionController::Base.param_parsers.dup
|
||||||
ActionController::Base.param_parsers[Mime::XML] = :xml_simple
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def teardown
|
||||||
|
ActionController::Base.param_parsers = @default_param_parsers
|
||||||
|
end
|
||||||
|
|
||||||
def test_check_parameters
|
def test_check_parameters
|
||||||
process('GET')
|
process('GET')
|
||||||
assert_equal '', @controller.response.body
|
assert_equal '', @controller.response.body
|
||||||
|
|
Loading…
Reference in a new issue