lib/rack/utils.rb in rack-1.4.0 vs lib/rack/utils.rb in rack-1.4.1
- old
+ new
@@ -59,25 +59,15 @@
# Parses a query string by breaking it up at the '&'
# and ';' characters. You can also use this to parse
# cookies by changing the characters used in the second
# parameter (which defaults to '&;').
def parse_query(qs, d = nil)
- params = {}
+ params = KeySpaceConstrainedParams.new
- max_key_space = Utils.key_space_limit
- bytes = 0
-
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
k, v = p.split('=', 2).map { |x| unescape(x) }
- if k
- bytes += k.size
- if bytes > max_key_space
- raise RangeError, "exceeded available parameter key space"
- end
- end
-
if cur = params[k]
if cur.class == Array
params[k] << v
else
params[k] = [cur, v]
@@ -85,34 +75,24 @@
else
params[k] = v
end
end
- return params
+ return params.to_params_hash
end
module_function :parse_query
def parse_nested_query(qs, d = nil)
- params = {}
+ params = KeySpaceConstrainedParams.new
- max_key_space = Utils.key_space_limit
- bytes = 0
-
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
k, v = p.split('=', 2).map { |s| unescape(s) }
- if k
- bytes += k.size
- if bytes > max_key_space
- raise RangeError, "exceeded available parameter key space"
- end
- end
-
normalize_params(params, k, v)
end
- return params
+ return params.to_params_hash
end
module_function :parse_nested_query
def normalize_params(params, name, v = nil)
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
@@ -129,25 +109,30 @@
params[k] << v
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
child_key = $1
params[k] ||= []
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
- if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
+ if params_hash_type?(params[k].last) && !params[k].last.key?(child_key)
normalize_params(params[k].last, child_key, v)
else
- params[k] << normalize_params({}, child_key, v)
+ params[k] << normalize_params(params.class.new, child_key, v)
end
else
- params[k] ||= {}
- raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
+ params[k] ||= params.class.new
+ raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
params[k] = normalize_params(params[k], after, v)
end
return params
end
module_function :normalize_params
+ def params_hash_type?(obj)
+ obj.kind_of?(KeySpaceConstrainedParams) || obj.kind_of?(Hash)
+ end
+ module_function :params_hash_type?
+
def build_query(params)
params.map { |k, v|
if v.class == Array
build_query(v.map { |x| [k, x] })
else
@@ -440,9 +425,44 @@
def replace(other)
clear
other.each { |k, v| self[k] = v }
self
+ end
+ end
+
+ class KeySpaceConstrainedParams
+ def initialize(limit = Utils.key_space_limit)
+ @limit = limit
+ @size = 0
+ @params = {}
+ end
+
+ def [](key)
+ @params[key]
+ end
+
+ def []=(key, value)
+ @size += key.size unless @params.key?(key)
+ raise RangeError, 'exceeded available parameter key space' if @size > @limit
+ @params[key] = value
+ end
+
+ def key?(key)
+ @params.key?(key)
+ end
+
+ def to_params_hash
+ hash = @params
+ hash.keys.each do |key|
+ value = hash[key]
+ if value.kind_of?(self.class)
+ hash[key] = value.to_params_hash
+ elsif value.kind_of?(Array)
+ value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x}
+ end
+ end
+ hash
end
end
# Every standard HTTP code mapped to the appropriate message.
# Generated with: