[DRE-commits] [SCM] ruby-rack.git branch, upstream, updated. debian/1.4.0-1-5-g065db2a
Youhei SASAKI
uwabami at gfd-dennou.org
Tue Mar 6 16:12:31 UTC 2012
The following commit has been merged in the upstream branch:
commit c1dbf280518e82936f97674a5351a669d18240b2
Author: Youhei SASAKI <uwabami at gfd-dennou.org>
Date: Wed Mar 7 00:43:46 2012 +0900
Imported Upstream version 1.4.1
diff --git a/README.rdoc b/README.rdoc
index 664f021..f31506e 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,4 +1,4 @@
-= Rack, a modular Ruby webserver interface
+= Rack, a modular Ruby webserver interface {<img src="https://secure.travis-ci.org/rack/rack.png" alt="Build Status" />}[http://travis-ci.org/rack/rack] {<img src="https://gemnasium.com/rack/rack.png" alt="Dependency Status" />}[https://gemnasium.com/rack/rack]
Rack provides a minimal, modular and adaptable interface for developing
web applications in Ruby. By wrapping HTTP requests and responses in
@@ -392,6 +392,22 @@ run on port 11211) and memcache-client installed.
* Support added for HTTP_X_FORWARDED_SCHEME
* Numerous bug fixes, including many fixes for new and alternate rubies
+* January 22nd, 2012: Twenty fifth public release 1.4.1
+ * Alter the keyspace limit calculations to reduce issues with nested params
+ * Add a workaround for multipart parsing where files contian unescaped "%"
+ * Added Rack::Response::Helpers#method_not_allowed? (code 405)
+ * Rack::File now returns 404's for illegal directory traversals
+ * Rack::File now returns 405's for illegal methods (non HEAD/GET)
+ * Rack::Cascade now catches 405 by default, as well as 404
+ * Cookies missing '--' no longer cause an exception to be raised
+ * Various style changes and documentation spelling errors
+ * Rack::BodyProxy always ensures to execute it's block
+ * Additional test coverage around cookies and secrets
+ * Rack::Session::Cookie can now be supplied either secret or old_secret
+ * Tests are no longer dependent on set order
+ * Rack::Static no longer defaults to serving index files
+ * Rack.release was fixed
+
== Contact
Please post bugs, suggestions and patches to
diff --git a/lib/rack.rb b/lib/rack.rb
index 4306d41..acfcb5a 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -20,7 +20,7 @@ module Rack
# Return the Rack release as a dotted string.
def self.release
- "1.3"
+ "1.4"
end
autoload :Builder, "rack/builder"
diff --git a/lib/rack/body_proxy.rb b/lib/rack/body_proxy.rb
index 6362c5f..64984e6 100644
--- a/lib/rack/body_proxy.rb
+++ b/lib/rack/body_proxy.rb
@@ -11,8 +11,11 @@ module Rack
def close
return if @closed
@closed = true
- @body.close if @body.respond_to? :close
- @block.call
+ begin
+ @body.close if @body.respond_to? :close
+ ensure
+ @block.call
+ end
end
def closed?
diff --git a/lib/rack/cascade.rb b/lib/rack/cascade.rb
index 4b9ba35..77c9724 100644
--- a/lib/rack/cascade.rb
+++ b/lib/rack/cascade.rb
@@ -8,7 +8,7 @@ module Rack
attr_reader :apps
- def initialize(apps, catch=404)
+ def initialize(apps, catch=[404, 405])
@apps = []; @has_app = {}
apps.each { |app| add app }
diff --git a/lib/rack/file.rb b/lib/rack/file.rb
index 8973195..2bb31b9 100644
--- a/lib/rack/file.rb
+++ b/lib/rack/file.rb
@@ -34,7 +34,7 @@ module Rack
def _call(env)
unless ALLOWED_VERBS.include? env["REQUEST_METHOD"]
- return fail(403, "Forbidden")
+ return fail(405, "Method Not Allowed")
end
@path_info = Utils.unescape(env["PATH_INFO"])
@@ -45,7 +45,7 @@ module Rack
when '', '.'
depth
when '..'
- return fail(403, "Forbidden") if depth - 1 < 0
+ return fail(404, "Not Found") if depth - 1 < 0
depth - 1
else
depth + 1
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb
index 2b55cf9..98eceaa 100644
--- a/lib/rack/multipart/parser.rb
+++ b/lib/rack/multipart/parser.rb
@@ -14,9 +14,6 @@ module Rack
fast_forward_to_first_boundary
- max_key_space = Utils.key_space_limit
- bytes = 0
-
loop do
head, filename, content_type, name, body =
get_current_head_and_filename_and_content_type_and_name_and_body
@@ -31,13 +28,6 @@ module Rack
filename, data = get_data(filename, body, content_type, name, head)
- if name
- bytes += name.size
- if bytes > max_key_space
- raise RangeError, "exceeded available parameter key space"
- end
- end
-
Utils.normalize_params(@params, name, data) unless data.nil?
# break if we're at the end of a buffer, but not if it is the end of a field
@@ -46,7 +36,7 @@ module Rack
@io.rewind
- @params
+ @params.to_params_hash
end
private
@@ -56,7 +46,7 @@ module Rack
@boundary = "--#{$1}"
@buf = ""
- @params = {}
+ @params = Utils::KeySpaceConstrainedParams.new
@content_length = @env['CONTENT_LENGTH'].to_i
@io = @env['rack.input']
@@ -135,8 +125,11 @@ module Rack
filename = $1
end
+ if filename && filename.scan(/%.?.?/).all? { |s| s =~ /%[0-9a-fA-F]{2}/ }
+ filename = Utils.unescape(filename)
+ end
if filename && filename !~ /\\[^\\"]/
- filename = Utils.unescape(filename).gsub(/\\(.)/, '\1')
+ filename = filename.gsub(/\\(.)/, '\1')
end
filename
end
diff --git a/lib/rack/request.rb b/lib/rack/request.rb
index fbbf00b..6daedca 100644
--- a/lib/rack/request.rb
+++ b/lib/rack/request.rb
@@ -177,7 +177,7 @@ module Rack
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
end
- # Returns the data recieved in the query string.
+ # Returns the data received in the query string.
def GET
if @env["rack.request.query_string"] == query_string
@env["rack.request.query_hash"]
@@ -187,7 +187,7 @@ module Rack
end
end
- # Returns the data recieved in the request body.
+ # Returns the data received in the request body.
#
# This method support both application/x-www-form-urlencoded and
# multipart/form-data.
diff --git a/lib/rack/response.rb b/lib/rack/response.rb
index 12180d8..a9973f4 100644
--- a/lib/rack/response.rb
+++ b/lib/rack/response.rb
@@ -12,7 +12,7 @@ module Rack
# You can use Response#write to iteratively generate your response,
# but note that this is buffered by Rack::Response until you call
# +finish+. +finish+ however can take a block inside which calls to
- # +write+ are syncronous with the Rack response.
+ # +write+ are synchronous with the Rack response.
#
# Your application's +call+ should end returning Response#finish.
@@ -112,21 +112,22 @@ module Rack
alias headers header
module Helpers
- def invalid?; status < 100 || status >= 600; end
-
- def informational?; status >= 100 && status < 200; end
- def successful?; status >= 200 && status < 300; end
- def redirection?; status >= 300 && status < 400; end
- def client_error?; status >= 400 && status < 500; end
- def server_error?; status >= 500 && status < 600; end
-
- def ok?; status == 200; end
- def bad_request?; status == 400; end
- def forbidden?; status == 403; end
- def not_found?; status == 404; end
- def unprocessable?; status == 422; end
-
- def redirect?; [301, 302, 303, 307].include? status; end
+ def invalid?; status < 100 || status >= 600; end
+
+ def informational?; status >= 100 && status < 200; end
+ def successful?; status >= 200 && status < 300; end
+ def redirection?; status >= 300 && status < 400; end
+ def client_error?; status >= 400 && status < 500; end
+ def server_error?; status >= 500 && status < 600; end
+
+ def ok?; status == 200; end
+ def bad_request?; status == 400; end
+ def forbidden?; status == 403; end
+ def not_found?; status == 404; end
+ def method_not_allowed?; status == 405; end
+ def unprocessable?; status == 422; end
+
+ def redirect?; [301, 302, 303, 307].include? status; end
# Headers
attr_reader :headers, :original_headers
diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb
index 641e3c0..9e19d1d 100644
--- a/lib/rack/session/abstract/id.rb
+++ b/lib/rack/session/abstract/id.rb
@@ -36,7 +36,7 @@ module Rack
private
def session_id_not_loaded?
- !key?(:id) && !@session_id_loaded
+ !(@session_id_loaded || key?(:id))
end
def load_session_id!
@@ -183,7 +183,7 @@ module Rack
:renew => false,
:sidbits => 128,
:cookie_only => true,
- :secure_random => begin ::SecureRandom rescue false end
+ :secure_random => (::SecureRandom rescue false)
}
attr_reader :key, :default_options
@@ -191,7 +191,7 @@ module Rack
def initialize(app, options={})
@app = app
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
- @key = options[:key] || "rack.session"
+ @key = @default_options.delete(:key)
@cookie_only = @default_options.delete(:cookie_only)
initialize_sid
end
diff --git a/lib/rack/session/cookie.rb b/lib/rack/session/cookie.rb
index 1d87384..ed9ee58 100644
--- a/lib/rack/session/cookie.rb
+++ b/lib/rack/session/cookie.rb
@@ -81,8 +81,7 @@ module Rack
attr_reader :coder
def initialize(app, options={})
- @secret = options[:secret]
- @old_secret = options[:old_secret]
+ @secrets = options.values_at(:secret, :old_secret).compact
@coder = options[:coder] ||= Base64::Marshal.new
super(app, options.merge!(:cookie_only => true))
end
@@ -104,11 +103,16 @@ module Rack
request = Rack::Request.new(env)
session_data = request.cookies[@key]
- if (@secret || @old_secret) && session_data
+ if @secrets.size > 0 && session_data
session_data, digest = session_data.split("--")
- if (digest != generate_hmac(session_data, @secret)) && (digest != generate_hmac(session_data, @old_secret))
- session_data = nil
+
+ if session_data && digest
+ ok = @secrets.any? do |secret|
+ secret && digest == generate_hmac(session_data, secret)
+ end
end
+
+ session_data = nil unless ok
end
coder.decode(session_data) || {}
@@ -131,8 +135,8 @@ module Rack
session = session.merge("session_id" => session_id)
session_data = coder.encode(session)
- if @secret
- session_data = "#{session_data}--#{generate_hmac(session_data, @secret)}"
+ if @secrets.first
+ session_data = "#{session_data}--#{generate_hmac(session_data, @secrets.first)}"
end
if session_data.size > (4096 - @key.size)
diff --git a/lib/rack/showstatus.rb b/lib/rack/showstatus.rb
index e1fff99..5a9506f 100644
--- a/lib/rack/showstatus.rb
+++ b/lib/rack/showstatus.rb
@@ -3,8 +3,8 @@ require 'rack/request'
require 'rack/utils'
module Rack
- # Rack::ShowStatus catches all empty responses the app it wraps and
- # replaces them with a site explaining the error.
+ # Rack::ShowStatus catches all empty responses and replaces them
+ # with a site explaining the error.
#
# Additional details can be put into <tt>rack.showstatus.detail</tt>
# and will be shown as HTML. If such details exist, the error page
diff --git a/lib/rack/static.rb b/lib/rack/static.rb
index 1b843a0..727ecbb 100644
--- a/lib/rack/static.rb
+++ b/lib/rack/static.rb
@@ -38,7 +38,7 @@ module Rack
def initialize(app, options={})
@app = app
@urls = options[:urls] || ["/favicon.ico"]
- @index = options[:index] || "index.html"
+ @index = options[:index]
root = options[:root] || Dir.pwd
cache_control = options[:cache_control]
@file_server = Rack::File.new(root, cache_control)
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 7bceb45..b706279 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -61,21 +61,11 @@ module Rack
# cookies by changing the characters used in the second
# parameter (which defaults to '&;').
def parse_query(qs, d = nil)
- params = {}
-
- max_key_space = Utils.key_space_limit
- bytes = 0
+ params = KeySpaceConstrainedParams.new
(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
@@ -87,30 +77,20 @@ module Rack
end
end
- return params
+ return params.to_params_hash
end
module_function :parse_query
def parse_nested_query(qs, d = nil)
- params = {}
-
- max_key_space = Utils.key_space_limit
- bytes = 0
+ params = KeySpaceConstrainedParams.new
(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
@@ -131,14 +111,14 @@ module Rack
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
@@ -146,6 +126,11 @@ module Rack
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
@@ -445,6 +430,41 @@ module Rack
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:
# curl -s http://www.iana.org/assignments/http-status-codes | \
diff --git a/metadata.yml b/metadata.yml
index 3234b9b..15a43bd 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,13 +1,13 @@
--- !ruby/object:Gem::Specification
name: rack
version: !ruby/object:Gem::Version
- hash: 7
+ hash: 5
prerelease:
segments:
- 1
- 4
- - 0
- version: 1.4.0
+ - 1
+ version: 1.4.1
platform: ruby
authors:
- Christian Neukirchen
@@ -15,7 +15,7 @@ autorequire:
bindir: bin
cert_chain: []
-date: 2011-12-28 00:00:00 Z
+date: 2012-01-23 00:00:00 Z
dependencies:
- !ruby/object:Gem::Dependency
name: bacon
@@ -213,6 +213,9 @@ files:
- test/multipart/filename_with_escaped_quotes
- test/multipart/filename_with_escaped_quotes_and_modification_param
- test/multipart/filename_with_percent_escaped_quotes
+- test/multipart/filename_with_unescaped_percentages
+- test/multipart/filename_with_unescaped_percentages2
+- test/multipart/filename_with_unescaped_percentages3
- test/multipart/filename_with_unescaped_quotes
- test/multipart/ie
- test/multipart/mixed_files
diff --git a/rack.gemspec b/rack.gemspec
index 0d6fa07..988edee 100644
--- a/rack.gemspec
+++ b/rack.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "rack"
- s.version = "1.4.0"
+ s.version = "1.4.1"
s.platform = Gem::Platform::RUBY
s.summary = "a modular Ruby webserver interface"
diff --git a/test/multipart/filename_with_unescaped_percentages b/test/multipart/filename_with_unescaped_percentages
new file mode 100644
index 0000000..f63dd22
--- /dev/null
+++ b/test/multipart/filename_with_unescaped_percentages
@@ -0,0 +1,6 @@
+------WebKitFormBoundary2NHc7OhsgU68l3Al
+Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"
+Content-Type: image/jpeg
+
+contents
+------WebKitFormBoundary2NHc7OhsgU68l3Al--
diff --git a/test/multipart/filename_with_unescaped_percentages2 b/test/multipart/filename_with_unescaped_percentages2
new file mode 100644
index 0000000..83eac36
--- /dev/null
+++ b/test/multipart/filename_with_unescaped_percentages2
@@ -0,0 +1,6 @@
+------WebKitFormBoundary2NHc7OhsgU68l3Al
+Content-Disposition: form-data; name="document[attachment]"; filename="100%a"
+Content-Type: image/jpeg
+
+contents
+------WebKitFormBoundary2NHc7OhsgU68l3Al--
diff --git a/test/multipart/filename_with_unescaped_percentages3 b/test/multipart/filename_with_unescaped_percentages3
new file mode 100644
index 0000000..4dba3c8
--- /dev/null
+++ b/test/multipart/filename_with_unescaped_percentages3
@@ -0,0 +1,6 @@
+------WebKitFormBoundary2NHc7OhsgU68l3Al
+Content-Disposition: form-data; name="document[attachment]"; filename="100%"
+Content-Type: image/jpeg
+
+contents
+------WebKitFormBoundary2NHc7OhsgU68l3Al--
diff --git a/test/spec_body_proxy.rb b/test/spec_body_proxy.rb
index 64bd65f..2f9f040 100644
--- a/test/spec_body_proxy.rb
+++ b/test/spec_body_proxy.rb
@@ -1,4 +1,5 @@
require 'rack/body_proxy'
+require 'stringio'
describe Rack::BodyProxy do
should 'call each on the wrapped body' do
@@ -32,6 +33,22 @@ describe Rack::BodyProxy do
called.should.equal true
end
+ should 'call the passed block on close even if there is an exception' do
+ object = Object.new
+ def object.close() raise "No!" end
+ called = false
+
+ begin
+ proxy = Rack::BodyProxy.new(object) { called = true }
+ called.should.equal false
+ proxy.close
+ rescue RuntimeError => e
+ end
+
+ raise "Expected exception to have been raised" unless e
+ called.should.equal true
+ end
+
should 'not close more than one time' do
count = 0
proxy = Rack::BodyProxy.new([]) { count += 1; raise "Block invoked more than 1 time!" if count > 1 }
diff --git a/test/spec_cascade.rb b/test/spec_cascade.rb
index cfd1164..8db570d 100644
--- a/test/spec_cascade.rb
+++ b/test/spec_cascade.rb
@@ -17,12 +17,15 @@ describe Rack::Cascade do
app3 = Rack::URLMap.new("/foo" => lambda { |env|
[200, { "Content-Type" => "text/plain"}, [""]]})
- should "dispatch onward on 404 by default" do
+ should "dispatch onward on 404 and 405 by default" do
cascade = cascade([app1, app2, app3])
Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
Rack::MockRequest.new(cascade).get("/foo").should.be.ok
Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found
- Rack::MockRequest.new(cascade).get("/cgi/../..").should.be.forbidden
+ Rack::MockRequest.new(cascade).get("/cgi/../..").should.be.client_error
+
+ # Put is not allowed by Rack::File so it'll 405.
+ Rack::MockRequest.new(cascade).put("/foo").should.be.ok
end
should "dispatch onward on whatever is passed" do
@@ -42,7 +45,7 @@ describe Rack::Cascade do
Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found
cascade << app1
Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok
- Rack::MockRequest.new(cascade).get('/cgi/../..').should.be.forbidden
+ Rack::MockRequest.new(cascade).get('/cgi/../..').should.be.client_error
Rack::MockRequest.new(cascade).get('/foo').should.be.not_found
cascade << app3
Rack::MockRequest.new(cascade).get('/foo').should.be.ok
diff --git a/test/spec_file.rb b/test/spec_file.rb
index 2a4d214..2c90732 100644
--- a/test/spec_file.rb
+++ b/test/spec_file.rb
@@ -64,13 +64,15 @@ describe Rack::File do
req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
res = req.get("/../README")
- res.should.be.forbidden
+ res.should.be.client_error
res = req.get("../test")
- res.should.be.forbidden
+ res.should.be.client_error
res = req.get("..")
- res.should.be.forbidden
+ res.should.be.client_error
+
+ res.should.be.not_found
end
should "allow files with .. in their name" do
@@ -89,7 +91,8 @@ describe Rack::File do
res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
get("/%2E%2E/README")
- res.should.be.forbidden
+ res.should.be.client_error?
+ res.should.be.not_found
end
should "allow safe directory traversal with encoded periods" do
@@ -159,7 +162,8 @@ describe Rack::File do
forbidden.each do |method|
res = req.send(method, "/cgi/test")
- res.should.be.forbidden
+ res.should.be.client_error
+ res.should.be.method_not_allowed
end
allowed = %w[get head]
@@ -169,4 +173,11 @@ describe Rack::File do
end
end
+ should "set Content-Length correctly for HEAD requests" do
+ req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
+ res = req.head "/cgi/test"
+ res.should.be.successful
+ res['Content-Length'].should.equal "193"
+ end
+
end
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 1dc2f4d..b0bf57c 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -2,11 +2,11 @@ require 'rack/utils'
require 'rack/mock'
describe Rack::Multipart do
- def multipart_fixture(name)
+ def multipart_fixture(name, boundary = "AaB03x")
file = multipart_file(name)
data = File.open(file, 'rb') { |io| io.read }
- type = "multipart/form-data; boundary=AaB03x"
+ type = "multipart/form-data; boundary=#{boundary}"
length = data.respond_to?(:bytesize) ? data.bytesize : data.size
{ "CONTENT_TYPE" => type,
@@ -211,6 +211,51 @@ describe Rack::Multipart do
params["files"][:tempfile].read.should.equal "contents"
end
+ should "parse filename with unescaped percentage characters" do
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
+ params = Rack::Multipart.parse_multipart(env)
+ files = params["document"]["attachment"]
+ files[:type].should.equal "image/jpeg"
+ files[:filename].should.equal "100% of a photo.jpeg"
+ files[:head].should.equal <<-MULTIPART
+Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r
+Content-Type: image/jpeg\r
+ MULTIPART
+
+ files[:name].should.equal "document[attachment]"
+ files[:tempfile].read.should.equal "contents"
+ end
+
+ should "parse filename with unescaped percentage characters that look like partial hex escapes" do
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
+ params = Rack::Multipart.parse_multipart(env)
+ files = params["document"]["attachment"]
+ files[:type].should.equal "image/jpeg"
+ files[:filename].should.equal "100%a"
+ files[:head].should.equal <<-MULTIPART
+Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r
+Content-Type: image/jpeg\r
+ MULTIPART
+
+ files[:name].should.equal "document[attachment]"
+ files[:tempfile].read.should.equal "contents"
+ end
+
+ should "parse filename with unescaped percentage characters that look like partial hex escapes" do
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
+ params = Rack::Multipart.parse_multipart(env)
+ files = params["document"]["attachment"]
+ files[:type].should.equal "image/jpeg"
+ files[:filename].should.equal "100%"
+ files[:head].should.equal <<-MULTIPART
+Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r
+Content-Type: image/jpeg\r
+ MULTIPART
+
+ files[:name].should.equal "document[attachment]"
+ files[:tempfile].read.should.equal "contents"
+ end
+
it "rewinds input after parsing upload" do
options = multipart_fixture(:text)
input = options[:input]
diff --git a/test/spec_request.rb b/test/spec_request.rb
index d20585c..57d34ab 100644
--- a/test/spec_request.rb
+++ b/test/spec_request.rb
@@ -137,6 +137,19 @@ describe Rack::Request do
end
end
+ should "limit the key size per nested params hash" do
+ nested_query = Rack::MockRequest.env_for("/?foo[bar][baz][qux]=1")
+ plain_query = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1")
+
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3
+ begin
+ lambda { Rack::Request.new(nested_query).GET }.should.not.raise(RangeError)
+ lambda { Rack::Request.new(plain_query).GET }.should.raise(RangeError)
+ ensure
+ Rack::Utils.key_space_limit = old
+ end
+ end
+
should "not unify GET and POST when calling params" do
mr = Rack::MockRequest.env_for("/?foo=quux",
"REQUEST_METHOD" => 'POST',
diff --git a/test/spec_response.rb b/test/spec_response.rb
index 07dd012..395bf91 100644
--- a/test/spec_response.rb
+++ b/test/spec_response.rb
@@ -1,4 +1,3 @@
-require 'set'
require 'rack/response'
require 'stringio'
@@ -125,7 +124,6 @@ describe Rack::Response do
response = Rack::Response.new
response.redirect "/foo"
status, header, body = response.finish
-
status.should.equal 302
header["Location"].should.equal "/foo"
@@ -147,7 +145,12 @@ describe Rack::Response do
str = ""; body.each { |part| str << part }
str.should.equal "foobar"
- r = Rack::Response.new(["foo", "bar"].to_set)
+ object_with_each = Object.new
+ def object_with_each.each
+ yield "foo"
+ yield "bar"
+ end
+ r = Rack::Response.new(object_with_each)
r.write "foo"
status, header, body = r.finish
str = ""; body.each { |part| str << part }
@@ -218,6 +221,11 @@ describe Rack::Response do
res.should.be.client_error
res.should.be.not_found
+ res.status = 405
+ res.should.not.be.successful
+ res.should.be.client_error
+ res.should.be.method_not_allowed
+
res.status = 422
res.should.not.be.successful
res.should.be.client_error
diff --git a/test/spec_session_cookie.rb b/test/spec_session_cookie.rb
index 514bb3b..ab7d518 100644
--- a/test/spec_session_cookie.rb
+++ b/test/spec_session_cookie.rb
@@ -123,6 +123,10 @@ describe Rack::Session::Cookie do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).
get("/", "HTTP_COOKIE" => "rack.session=blarghfasel")
res.body.should.equal '{"counter"=>1}'
+
+ app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
+ res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => "rack.session=")
+ res.body.should.equal '{"counter"=>1}'
end
bigcookie = lambda do |env|
@@ -137,7 +141,7 @@ describe Rack::Session::Cookie do
}.should.raise(Rack::MockRequest::FatalWarning)
end
- it "loads from a cookie wih integrity hash" do
+ it "loads from a cookie with integrity hash" do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
cookie = res["Set-Cookie"]
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).
@@ -147,6 +151,9 @@ describe Rack::Session::Cookie do
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).
get("/", "HTTP_COOKIE" => cookie)
res.body.should.equal '{"counter"=>3}'
+ res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'other')).
+ get("/", "HTTP_COOKIE" => cookie)
+ res.body.should.equal '{"counter"=>1}'
end
it "loads from a cookie wih accept-only integrity hash for graceful key rotation" do
@@ -165,16 +172,31 @@ describe Rack::Session::Cookie do
app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
response1 = Rack::MockRequest.new(app).get("/")
response1.body.should.equal '{"counter"=>1}'
+ response1 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => response1["Set-Cookie"])
+ response1.body.should.equal '{"counter"=>2}'
_, digest = response1["Set-Cookie"].split("--")
tampered_with_cookie = "hackerman-was-here" + "--" + digest
response2 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" =>
tampered_with_cookie)
- # Tampared cookie was ignored. Counter is back to 1.
+ # Tampered cookie was ignored. Counter is back to 1.
response2.body.should.equal '{"counter"=>1}'
end
+ it "supports either of secret or old_secret" do
+ app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
+ res = Rack::MockRequest.new(app).get("/")
+ res.body.should.equal '{"counter"=>1}'
+ res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"])
+ res.body.should.equal '{"counter"=>2}'
+ app = Rack::Session::Cookie.new(incrementor, :old_secret => 'test')
+ res = Rack::MockRequest.new(app).get("/")
+ res.body.should.equal '{"counter"=>1}'
+ res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"])
+ res.body.should.equal '{"counter"=>2}'
+ end
+
describe "1.9 bugs relating to inspecting yet-to-be-loaded from cookie data: Rack::Session::Abstract::SessionHash" do
it "can handle Rack::Lint middleware" do
@@ -225,6 +247,7 @@ describe Rack::Session::Cookie do
res = Rack::MockRequest.new(app).get("/", "HTTPS" => "on")
res["Set-Cookie"].should.not.be.nil
+ res["Set-Cookie"].should.match(/secure/)
end
it "does not return a cookie if cookie was not read/written" do
diff --git a/test/spec_static.rb b/test/spec_static.rb
index 21b2b6a..1860d13 100644
--- a/test/spec_static.rb
+++ b/test/spec_static.rb
@@ -40,6 +40,11 @@ describe Rack::Static do
res.should.be.ok
res.body.should =~ /index!/
end
+
+ it "doesn't call index file if :index option was omitted" do
+ res = @request.get("/")
+ res.body.should == "Hello World"
+ end
it "serves hidden files" do
res = @hash_request.get("/cgi/sekret")
diff --git a/test/spec_utils.rb b/test/spec_utils.rb
index a787763..32d0a6f 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -3,6 +3,15 @@ require 'rack/utils'
require 'rack/mock'
describe Rack::Utils do
+
+ # A helper method which checks
+ # if certain query parameters
+ # are equal.
+ def equal_query_to(query)
+ parts = query.split('&')
+ lambda{|other| (parts & other.split('&')) == parts }
+ end
+
def kcodeu
one8 = RUBY_VERSION.to_f < 1.9
default_kcode, $KCODE = $KCODE, 'U' if one8
@@ -179,7 +188,7 @@ describe Rack::Utils do
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
should.raise(TypeError).
- message.should.equal "expected Array (got Hash) for param `x'"
+ message.should.match /expected Array \(got [^)]*\) for param `x'/
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }.
should.raise(TypeError).
@@ -187,13 +196,13 @@ describe Rack::Utils do
end
should "build query strings correctly" do
- Rack::Utils.build_query("foo" => "bar").should.equal "foo=bar"
+ Rack::Utils.build_query("foo" => "bar").should.be equal_query_to("foo=bar")
Rack::Utils.build_query("foo" => ["bar", "quux"]).
- should.equal "foo=bar&foo=quux"
+ should.be equal_query_to("foo=bar&foo=quux")
Rack::Utils.build_query("foo" => "1", "bar" => "2").
- should.equal "foo=1&bar=2"
+ should.be equal_query_to("foo=1&bar=2")
Rack::Utils.build_query("my weird field" => "q1!2\"'w$5&7/z8)?").
- should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
+ should.be equal_query_to("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F")
end
should "build nested query strings correctly" do
@@ -202,9 +211,9 @@ describe Rack::Utils do
Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar"
Rack::Utils.build_nested_query("foo" => "1", "bar" => "2").
- should.equal "foo=1&bar=2"
+ should.be equal_query_to("foo=1&bar=2")
Rack::Utils.build_nested_query("my weird field" => "q1!2\"'w$5&7/z8)?").
- should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
+ should.be equal_query_to("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F")
Rack::Utils.build_nested_query("foo" => [nil]).
should.equal "foo[]"
--
ruby-rack.git
More information about the Pkg-ruby-extras-commits
mailing list