lib/rack/utils.rb in rack-1.4.7 vs lib/rack/utils.rb in rack-1.5.0.beta.1
- old
+ new
@@ -49,27 +49,16 @@
DEFAULT_SEP = /[&;] */n
class << self
attr_accessor :key_space_limit
- attr_accessor :param_depth_limit
- attr_accessor :multipart_part_limit
end
# The default number of bytes to allow parameter keys to take up.
# This helps prevent a rogue client from flooding a Request.
self.key_space_limit = 65536
- # Default depth at which the parameter parser will raise an exception for
- # being too deep. This helps prevent SystemStackErrors
- self.param_depth_limit = 100
- #
- # The maximum number of parts a request can contain. Accepting to many part
- # can lead to the server running out of file handles.
- # Set to `0` for no limit.
- self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
-
# Stolen from Mongrel, with some small modifications:
# 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 '&;').
@@ -109,13 +98,11 @@
return params.to_params_hash
end
module_function :parse_nested_query
- def normalize_params(params, name, v = nil, depth = Utils.param_depth_limit)
- raise RangeError if depth <= 0
-
+ def normalize_params(params, name, v = nil)
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
k = $1 || ''
after = $' || ''
return if k.empty?
@@ -129,18 +116,18 @@
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_hash_type?(params[k].last) && !params[k].last.key?(child_key)
- normalize_params(params[k].last, child_key, v, depth - 1)
+ normalize_params(params[k].last, child_key, v)
else
- params[k] << normalize_params(params.class.new, child_key, v, depth - 1)
+ params[k] << normalize_params(params.class.new, child_key, v)
end
else
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, depth - 1)
+ params[k] = normalize_params(params[k], after, v)
end
return params
end
module_function :normalize_params
@@ -178,10 +165,35 @@
prefix
end
end
module_function :build_nested_query
+ def q_values(q_value_header)
+ q_value_header.to_s.split(/\s*,\s*/).map do |part|
+ value, parameters = part.split(/\s*;\s*/, 2)
+ quality = 1.0
+ if md = /\Aq=([\d.]+)/.match(parameters)
+ quality = md[1].to_f
+ end
+ [value, quality]
+ end
+ end
+ module_function :q_values
+
+ def best_q_match(q_value_header, available_mimes)
+ values = q_values(q_value_header)
+
+ values.map do |req_mime, quality|
+ match = available_mimes.first { |am| Rack::Mime.match?(am, req_mime) }
+ next unless match
+ [match, quality]
+ end.compact.sort_by do |match, quality|
+ (match.split('/', 2).count('*') * -10) + quality
+ end.last.first
+ end
+ module_function :best_q_match
+
ESCAPE_HTML = {
"&" => "&",
"<" => "<",
">" => ">",
"'" => "'",
@@ -235,10 +247,11 @@
def set_cookie_header!(header, key, value)
case value
when Hash
domain = "; domain=" + value[:domain] if value[:domain]
path = "; path=" + value[:path] if value[:path]
+ max_age = "; max-age=" + value[:max_age] if value[:max_age]
# According to RFC 2109, we need dashes here.
# N.B.: cgi.rb uses spaces...
expires = "; expires=" +
rfc2822(value[:expires].clone.gmtime) if value[:expires]
secure = "; secure" if value[:secure]
@@ -246,11 +259,11 @@
value = value[:value]
end
value = [value] unless Array === value
cookie = escape(key) + "=" +
value.map { |v| escape v }.join("&") +
- "#{domain}#{path}#{expires}#{secure}#{httponly}"
+ "#{domain}#{path}#{max_age}#{expires}#{secure}#{httponly}"
case header["Set-Cookie"]
when nil, ''
header["Set-Cookie"] = cookie
when String
@@ -285,10 +298,11 @@
header["Set-Cookie"] = cookies.join("\n")
set_cookie_header!(header, key,
{:value => '', :path => nil, :domain => nil,
+ :max_age => '0',
:expires => Time.at(0) }.merge(value))
nil
end
module_function :delete_cookie_header!
@@ -353,22 +367,10 @@
end
ranges
end
module_function :byte_ranges
- # Constant time string comparison.
- def secure_compare(a, b)
- return false unless bytesize(a) == bytesize(b)
-
- l = a.unpack("C*")
-
- r, i = 0, -1
- b.each_byte { |v| r |= v ^ l[i+=1] }
- r == 0
- end
- module_function :secure_compare
-
# Context allows the use of a compatible middleware at different points
# in a request handling stack. A compatible middleware must define
# #context which should take the arguments env and app. The first of which
# would be the request environment. The second of which would be the rack
# application that the request would be forwarded to.
@@ -496,65 +498,75 @@
end
end
# Every standard HTTP code mapped to the appropriate message.
# Generated with:
- # curl -s http://www.iana.org/assignments/http-status-codes | \
- # ruby -ane 'm = /^(\d{3}) +(\S[^\[(]+)/.match($_) and
- # puts " #{m[1]} => \x27#{m[2].strip}x27,"'
+ # irb -ropen-uri -rnokogiri
+ # > Nokogiri::XML(open("http://www.iana.org/assignments/http-status-codes/http-status-codes.xml")).css("record").each{|r|
+ # puts "#{r.css('value').text} => '#{r.css('description').text}'"}
HTTP_STATUS_CODES = {
- 100 => 'Continue',
- 101 => 'Switching Protocols',
- 102 => 'Processing',
- 200 => 'OK',
- 201 => 'Created',
- 202 => 'Accepted',
- 203 => 'Non-Authoritative Information',
- 204 => 'No Content',
- 205 => 'Reset Content',
- 206 => 'Partial Content',
- 207 => 'Multi-Status',
- 226 => 'IM Used',
- 300 => 'Multiple Choices',
- 301 => 'Moved Permanently',
- 302 => 'Found',
- 303 => 'See Other',
- 304 => 'Not Modified',
- 305 => 'Use Proxy',
- 306 => 'Reserved',
- 307 => 'Temporary Redirect',
- 400 => 'Bad Request',
- 401 => 'Unauthorized',
- 402 => 'Payment Required',
- 403 => 'Forbidden',
- 404 => 'Not Found',
- 405 => 'Method Not Allowed',
- 406 => 'Not Acceptable',
- 407 => 'Proxy Authentication Required',
- 408 => 'Request Timeout',
- 409 => 'Conflict',
- 410 => 'Gone',
- 411 => 'Length Required',
- 412 => 'Precondition Failed',
- 413 => 'Request Entity Too Large',
- 414 => 'Request-URI Too Long',
- 415 => 'Unsupported Media Type',
- 416 => 'Requested Range Not Satisfiable',
- 417 => 'Expectation Failed',
- 418 => "I'm a Teapot",
- 422 => 'Unprocessable Entity',
- 423 => 'Locked',
- 424 => 'Failed Dependency',
- 426 => 'Upgrade Required',
- 500 => 'Internal Server Error',
- 501 => 'Not Implemented',
- 502 => 'Bad Gateway',
- 503 => 'Service Unavailable',
- 504 => 'Gateway Timeout',
- 505 => 'HTTP Version Not Supported',
- 506 => 'Variant Also Negotiates',
- 507 => 'Insufficient Storage',
- 510 => 'Not Extended',
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status',
+ 208 => 'Already Reported',
+ 226 => 'IM Used',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 306 => 'Reserved',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 422 => 'Unprocessable Entity',
+ 423 => 'Locked',
+ 424 => 'Failed Dependency',
+ 425 => 'Reserved for WebDAV advanced collections expired proposal',
+ 426 => 'Upgrade Required',
+ 427 => 'Unassigned',
+ 428 => 'Precondition Required',
+ 429 => 'Too Many Requests',
+ 430 => 'Unassigned',
+ 431 => 'Request Header Fields Too Large',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 506 => 'Variant Also Negotiates (Experimental)',
+ 507 => 'Insufficient Storage',
+ 508 => 'Loop Detected',
+ 509 => 'Unassigned',
+ 510 => 'Not Extended',
+ 511 => 'Network Authentication Required'
}
# Responses with HTTP status codes that should not have an entity body
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304)