summary refs log tree commit
diff options
context:
space:
mode:
-rw-r--r--lib/rack.rb2
-rw-r--r--lib/rack/auth/abstract/request.rb6
-rw-r--r--lib/rack/handler/webrick.rb8
-rw-r--r--lib/rack/multipart/parser.rb2
-rw-r--r--lib/rack/query_parser.rb15
-rw-r--r--lib/rack/session/abstract/id.rb14
-rw-r--r--rack.gemspec6
-rw-r--r--test/multipart/filename_with_null_byte7
-rw-r--r--test/spec_auth_basic.rb7
-rw-r--r--test/spec_multipart.rb6
-rw-r--r--test/spec_session_abstract_session_hash.rb17
-rw-r--r--test/spec_utils.rb12
-rw-r--r--test/spec_webrick.rb2
13 files changed, 88 insertions, 16 deletions
diff --git a/lib/rack.rb b/lib/rack.rb
index 00621178..f1417d2d 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -18,7 +18,7 @@ module Rack
     VERSION.join(".")
   end
 
-  RELEASE = "2.0.0.rc1"
+  RELEASE = "2.0.1"
 
   # Return the Rack release as a dotted string.
   def self.release
diff --git a/lib/rack/auth/abstract/request.rb b/lib/rack/auth/abstract/request.rb
index 80d1c272..b738cc98 100644
--- a/lib/rack/auth/abstract/request.rb
+++ b/lib/rack/auth/abstract/request.rb
@@ -13,7 +13,11 @@ module Rack
       end
 
       def provided?
-        !authorization_key.nil?
+        !authorization_key.nil? && valid?
+      end
+
+      def valid?
+        !@env[authorization_key].nil?
       end
 
       def parts
diff --git a/lib/rack/handler/webrick.rb b/lib/rack/handler/webrick.rb
index 95aa8927..d0fcd213 100644
--- a/lib/rack/handler/webrick.rb
+++ b/lib/rack/handler/webrick.rb
@@ -86,10 +86,11 @@ module Rack
         status, headers, body = @app.call(env)
         begin
           res.status = status.to_i
+          io_lambda = nil
           headers.each { |k, vs|
-            next if k.downcase == RACK_HIJACK
-
-            if k.downcase == "set-cookie"
+            if k == RACK_HIJACK
+              io_lambda = vs
+            elsif k.downcase == "set-cookie"
               res.cookies.concat vs.split("\n")
             else
               # Since WEBrick won't accept repeated headers,
@@ -98,7 +99,6 @@ module Rack
             end
           }
 
-          io_lambda = headers[RACK_HIJACK]
           if io_lambda
             rd, wr = IO.pipe
             res.body = rd
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb
index 74a7ee67..d8cb3670 100644
--- a/lib/rack/multipart/parser.rb
+++ b/lib/rack/multipart/parser.rb
@@ -8,7 +8,7 @@ module Rack
       BUFSIZE = 16384
       TEXT_PLAIN = "text/plain"
       TEMPFILE_FACTORY = lambda { |filename, content_type|
-        Tempfile.new(["RackMultipart", ::File.extname(filename)])
+        Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0".freeze, '%00'.freeze))])
       }
 
       class BoundedIO # :nodoc:
diff --git a/lib/rack/query_parser.rb b/lib/rack/query_parser.rb
index 17b77128..be74bc06 100644
--- a/lib/rack/query_parser.rb
+++ b/lib/rack/query_parser.rb
@@ -102,8 +102,7 @@ module Rack
         child_key = $1
         params[k] ||= []
         raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-        first_key = child_key.gsub(/[\[\]]/, ' ').split.first
-        if params_hash_type?(params[k].last) && !params[k].last.key?(first_key)
+        if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
           normalize_params(params[k].last, child_key, v, depth - 1)
         else
           params[k] << normalize_params(make_params, child_key, v, depth - 1)
@@ -135,6 +134,18 @@ module Rack
       obj.kind_of?(@params_class)
     end
 
+    def params_hash_has_key?(hash, key)
+      return false if key =~ /\[\]/
+
+      key.split(/[\[\]]+/).inject(hash) do |h, part|
+        next h if part == ''
+        return false unless params_hash_type?(h) && h.key?(part)
+        h[part]
+      end
+
+      true
+    end
+
     def unescape(s)
       Utils.unescape(s)
     end
diff --git a/lib/rack/session/abstract/id.rb b/lib/rack/session/abstract/id.rb
index 204bdb34..ca1a2628 100644
--- a/lib/rack/session/abstract/id.rb
+++ b/lib/rack/session/abstract/id.rb
@@ -18,6 +18,8 @@ module Rack
         include Enumerable
         attr_writer :id
 
+        Unspecified = Object.new
+
         def self.find(req)
           req.get_header RACK_SESSION
         end
@@ -54,7 +56,15 @@ module Rack
           load_for_read!
           @data[key.to_s]
         end
-        alias :fetch :[]
+
+        def fetch(key, default=Unspecified, &block)
+          load_for_read!
+          if default == Unspecified
+            @data.fetch(key.to_s, &block)
+          else
+            @data.fetch(key.to_s, default, &block)
+          end
+        end
 
         def has_key?(key)
           load_for_read!
@@ -200,7 +210,7 @@ module Rack
           :sidbits =>       128,
           :cookie_only =>   true,
           :secure_random => ::SecureRandom
-        }
+        }.freeze
 
         attr_reader :key, :default_options, :sid_secure
 
diff --git a/rack.gemspec b/rack.gemspec
index 1b412793..259ae3ab 100644
--- a/rack.gemspec
+++ b/rack.gemspec
@@ -23,13 +23,11 @@ EOF
   s.extra_rdoc_files = ['README.rdoc', 'HISTORY.md']
   s.test_files      = Dir['test/spec_*.rb']
 
-  s.author          = 'Christian Neukirchen'
-  s.email           = 'chneukirchen@gmail.com'
+  s.author          = 'Aaron Patterson'
+  s.email           = 'tenderlove@ruby-lang.org'
   s.homepage        = 'http://rack.github.io/'
   s.required_ruby_version = '>= 2.2.2'
 
-  s.add_dependency 'json'
-
   s.add_development_dependency 'minitest', "~> 5.0"
   s.add_development_dependency 'minitest-sprint'
   s.add_development_dependency 'concurrent-ruby'
diff --git a/test/multipart/filename_with_null_byte b/test/multipart/filename_with_null_byte
new file mode 100644
index 00000000..961d44c4
--- /dev/null
+++ b/test/multipart/filename_with_null_byte
@@ -0,0 +1,7 @@
+--AaB03x
+Content-Type: image/jpeg
+Content-Disposition: attachment; name="files"; filename="flowers.exe%00.jpg"
+Content-Description: a complete map of the human genome
+
+contents
+--AaB03x--
diff --git a/test/spec_auth_basic.rb b/test/spec_auth_basic.rb
index 1e19bf66..45d28576 100644
--- a/test/spec_auth_basic.rb
+++ b/test/spec_auth_basic.rb
@@ -75,6 +75,13 @@ describe Rack::Auth::Basic do
     end
   end
 
+  it 'return 401 Bad Request for a nil authorization header' do
+    request 'HTTP_AUTHORIZATION' => nil do |response|
+      response.must_be :client_error?
+      response.status.must_equal 401
+    end
+  end
+
   it 'takes realm as optional constructor arg' do
     app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
     realm.must_equal app.realm
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 80e49ccb..02b86bed 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -305,6 +305,12 @@ describe Rack::Multipart do
     params["files"][:filename].must_equal "bob's flowers.jpg"
   end
 
+  it "parse multipart form with a null byte in the filename" do
+    env = Rack::MockRequest.env_for '/', multipart_fixture(:filename_with_null_byte)
+    params = Rack::Multipart.parse_multipart(env)
+    params["files"][:filename].must_equal "flowers.exe\u0000.jpg"
+  end
+
   it "not include file params if no file was selected" do
     env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
     params = Rack::Multipart.parse_multipart(env)
diff --git a/test/spec_session_abstract_session_hash.rb b/test/spec_session_abstract_session_hash.rb
index 6d73a80a..76b34a01 100644
--- a/test/spec_session_abstract_session_hash.rb
+++ b/test/spec_session_abstract_session_hash.rb
@@ -25,4 +25,21 @@ describe Rack::Session::Abstract::SessionHash do
     assert_equal [:bar, :qux], hash.values
   end
 
+  describe "#fetch" do
+    it "returns value for a matching key" do
+      assert_equal :bar, hash.fetch(:foo)
+    end
+
+    it "works with a default value" do
+      assert_equal :default, hash.fetch(:unknown, :default)
+    end
+
+    it "works with a block" do
+      assert_equal :default, hash.fetch(:unkown) { :default }
+    end
+
+    it "it raises when fetching unknown keys without defaults" do
+      lambda { hash.fetch(:unknown) }.must_raise KeyError
+    end
+  end
 end
diff --git a/test/spec_utils.rb b/test/spec_utils.rb
index b24762c9..e5d4d244 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -231,6 +231,18 @@ describe Rack::Utils do
       message.must_equal "invalid byte sequence in UTF-8"
   end
 
+  it "only moves to a new array when the full key has been seen" do
+    Rack::Utils.parse_nested_query("x[][y][][z]=1&x[][y][][w]=2").
+      must_equal "x" => [{"y" => [{"z" => "1", "w" => "2"}]}]
+
+    Rack::Utils.parse_nested_query(
+      "x[][id]=1&x[][y][a]=5&x[][y][b]=7&x[][z][id]=3&x[][z][w]=0&x[][id]=2&x[][y][a]=6&x[][y][b]=8&x[][z][id]=4&x[][z][w]=0"
+    ).must_equal "x" => [
+        {"id" => "1", "y" => {"a" => "5", "b" => "7"}, "z" => {"id" => "3", "w" => "0"}},
+        {"id" => "2", "y" => {"a" => "6", "b" => "8"}, "z" => {"id" => "4", "w" => "0"}},
+      ]
+  end
+
   it "allow setting the params hash class to use for parsing query strings" do
     begin
       default_parser = Rack::Utils.default_query_parser
diff --git a/test/spec_webrick.rb b/test/spec_webrick.rb
index 9ae6103d..4a10c1ca 100644
--- a/test/spec_webrick.rb
+++ b/test/spec_webrick.rb
@@ -171,7 +171,7 @@ describe Rack::Handler::WEBrick do
     Rack::Lint.new(lambda{ |req|
       [
         200,
-        {"rack.hijack" => io_lambda},
+        [ [ "rack.hijack", io_lambda ] ],
         [""]
       ]
     })