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: